icecat: add release icecat-140.10.1-1gnu1 for ecne

This commit is contained in:
Ark74 2026-05-04 16:58:41 -06:00
parent a5f93cb214
commit ff85d7c623
1256 changed files with 63469 additions and 24141 deletions

View file

@ -85,9 +85,9 @@ git = "https://github.com/mozilla/audioipc"
rev = "e6f44a2bd1e57d11dfc737632a9e849077632330"
replace-with = "vendored-sources"
[source."git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=579b75af21c040700eee6a1d8520e222699fe4cd"]
[source."git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=bebaa23317332c95734df76e25193c24a83a6840"]
git = "https://github.com/mozilla/cubeb-coreaudio-rs"
rev = "579b75af21c040700eee6a1d8520e222699fe4cd"
rev = "bebaa23317332c95734df76e25193c24a83a6840"
replace-with = "vendored-sources"
[source."git+https://github.com/mozilla/cubeb-pulse-rs?rev=8678dcab1c287de79c4c184ccc2e065bc62b70e2"]

3
icecat/.gitignore vendored
View file

@ -373,3 +373,6 @@ toolkit/crashreporter/minidump-analyzer/analyzer-test/target/
# Ignore mozperftest artifacts folder
/artifacts/
# Ignore personal preferences files
CLAUDE.local.md

View file

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Merge day clobber 2026-02-23
Merge day clobber 2026-03-23

4
icecat/Cargo.lock generated
View file

@ -1086,7 +1086,7 @@ dependencies = [
[[package]]
name = "coreaudio-sys-utils"
version = "0.1.0"
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=579b75af21c040700eee6a1d8520e222699fe4cd#579b75af21c040700eee6a1d8520e222699fe4cd"
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=bebaa23317332c95734df76e25193c24a83a6840#bebaa23317332c95734df76e25193c24a83a6840"
dependencies = [
"core-foundation-sys",
"coreaudio-sys",
@ -1398,7 +1398,7 @@ dependencies = [
[[package]]
name = "cubeb-coreaudio"
version = "0.1.0"
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=579b75af21c040700eee6a1d8520e222699fe4cd#579b75af21c040700eee6a1d8520e222699fe4cd"
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=bebaa23317332c95734df76e25193c24a83a6840#bebaa23317332c95734df76e25193c24a83a6840"
dependencies = [
"atomic",
"audio-mixer",

View file

@ -23,7 +23,7 @@ using namespace mozilla::a11y;
// AccIterator
////////////////////////////////////////////////////////////////////////////////
AccIterator::AccIterator(const LocalAccessible* aAccessible,
AccIterator::AccIterator(LocalAccessible* aAccessible,
filters::FilterFuncPtr aFilterFunc)
: mFilterFunc(aFilterFunc) {
mState = new IteratorState(aAccessible);
@ -63,7 +63,7 @@ LocalAccessible* AccIterator::Next() {
////////////////////////////////////////////////////////////////////////////////
// nsAccIterator::IteratorState
AccIterator::IteratorState::IteratorState(const LocalAccessible* aParent,
AccIterator::IteratorState::IteratorState(LocalAccessible* aParent,
IteratorState* mParentState)
: mParent(aParent), mIndex(0), mParentState(mParentState) {}

View file

@ -42,7 +42,7 @@ class AccIterable {
*/
class AccIterator : public AccIterable {
public:
AccIterator(const LocalAccessible* aRoot, filters::FilterFuncPtr aFilterFunc);
AccIterator(LocalAccessible* aRoot, filters::FilterFuncPtr aFilterFunc);
virtual ~AccIterator();
/**
@ -57,10 +57,10 @@ class AccIterator : public AccIterable {
AccIterator& operator=(const AccIterator&);
struct IteratorState {
explicit IteratorState(const LocalAccessible* aParent,
explicit IteratorState(LocalAccessible* aParent,
IteratorState* mParentState = nullptr);
const LocalAccessible* mParent;
RefPtr<LocalAccessible> mParent;
int32_t mIndex;
IteratorState* mParentState;
};

View file

@ -71,10 +71,14 @@ void CachedTableAccessible::Invalidate(Accessible* aAcc) {
return;
}
if (Accessible* table = nsAccUtils::TableFor(aAcc)) {
Accessible* table = nsAccUtils::TableFor(aAcc);
while (table && table->IsTable()) {
// Destroy the instance (if any). We'll create a new one the next time it
// is requested.
// is requested. Climb up the heirarcy to invalidate parent tables as well.
sCachedTables->Remove(table);
// The table may be a direct child of another table, invalidate that one as
// well.
table = table->Parent();
}
}

View file

@ -536,7 +536,7 @@ static dom::Selection* GetDOMSelection(const nsIContent* aStartContent,
return startFrameSel ? &startFrameSel->NormalSelection() : nullptr;
}
std::pair<nsIContent*, uint32_t> TextLeafPoint::ToDOMPoint(
std::pair<RefPtr<nsIContent>, uint32_t> TextLeafPoint::ToDOMPoint(
bool aIncludeGenerated) const {
if (!(*this) || !mAcc->IsLocal()) {
MOZ_ASSERT_UNREACHABLE("Invalid point");

View file

@ -188,7 +188,7 @@ class TextLeafPoint final {
/**
* Translate given TextLeafPoint into a DOM point.
*/
MOZ_CAN_RUN_SCRIPT std::pair<nsIContent*, uint32_t> ToDOMPoint(
MOZ_CAN_RUN_SCRIPT std::pair<RefPtr<nsIContent>, uint32_t> ToDOMPoint(
bool aIncludeGenerated = true) const;
private:

View file

@ -859,16 +859,15 @@ void HyperTextAccessible::ReplaceText(const nsAString& aText) {
return;
}
RefPtr<EditorBase> editorBase = GetEditor();
SetSelectionBoundsAt(TextLeafRange::kRemoveAllExistingSelectedRanges, 0,
CharacterCount());
RefPtr<EditorBase> editorBase = GetEditor();
if (!editorBase) {
return;
if (editorBase) {
DebugOnly<nsresult> rv = editorBase->InsertTextAsAction(aText);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to insert the new text");
}
DebugOnly<nsresult> rv = editorBase->InsertTextAsAction(aText);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to insert the new text");
}
void HyperTextAccessible::InsertText(const nsAString& aText,

View file

@ -118,6 +118,11 @@ mozilla::ipc::IPCResult DocAccessibleParent::ProcessShowEvent(
return IPC_OK();
#endif
}
if (parent->IsOuterDoc()) {
return IPC_FAIL(this, "Cannot attach non-doc to OuterDoc");
}
lastParent = parent;
lastParentID = accData.ParentID();
@ -223,6 +228,11 @@ mozilla::ipc::IPCResult DocAccessibleParent::ProcessShowEvent(
RemoteAccessible* DocAccessibleParent::CreateAcc(
const AccessibleData& aAccData) {
if (aAccData.ID() == 0) {
MOZ_ASSERT_UNREACHABLE("An ID of 0 is reserved for the document itself");
return nullptr;
}
RemoteAccessible* newProxy;
if ((newProxy = GetAccessible(aAccData.ID()))) {
// This is a move. Reuse the Accessible; don't destroy it.
@ -906,7 +916,10 @@ mozilla::ipc::IPCResult DocAccessibleParent::RecvBindChildDoc(
MOZ_ASSERT(CheckDocTree());
auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc.get());
childDoc->Unbind();
if (childDoc->IsShutdown()) {
return IPC_FAIL(this, "Attempt to bind a shutdown child doc");
}
ipc::IPCResult result = AddChildDoc(childDoc, aID, false);
MOZ_ASSERT(result);
MOZ_ASSERT(CheckDocTree());
@ -929,6 +942,10 @@ ipc::IPCResult DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
"Attempt to add child doc which already has a parent");
}
if (aChildDoc->IsShutdown()) {
return IPC_FAIL(this, "Attempt to add a shutdown child doc");
}
// We do not use GetAccessible here because we want to be sure to not get the
// document it self.
ProxyEntry* e = mAccessibles.GetEntry(aParentID);
@ -1031,6 +1048,9 @@ void DocAccessibleParent::Destroy() {
// If we are already shutdown that is because our containing tab parent is
// shutting down in which case we don't need to do anything.
if (mShutdown) {
// Just in case there is a cycle in the document heirarchy.
mParent = nullptr;
mIndexInParent = -1;
return;
}
@ -1068,6 +1088,8 @@ void DocAccessibleParent::Destroy() {
RemoteAccessible* acc = iter.Get()->mProxy;
MOZ_ASSERT(acc != this);
if (acc->IsTable()) {
// Prevents the invalidation code from trying to walk up the tree.
acc->SetParent(nullptr);
CachedTableAccessible::Invalidate(acc);
}
ProxyDestroyed(acc);
@ -1113,6 +1135,9 @@ void DocAccessibleParent::ActorDestroy(ActorDestroyReason aWhy) {
if (!mShutdown) {
ACQUIRE_ANDROID_LOCK
Destroy();
} else if (RemoteParent()) {
ACQUIRE_ANDROID_LOCK
Unbind();
}
}

View file

@ -16,25 +16,9 @@ function getSiteBlockedErrorDetails(docShell) {
Ci.nsIClassifiedChannel
);
if (classifiedChannel) {
let httpChannel = docShell.failedChannel.QueryInterface(
Ci.nsIHttpChannel
);
let reportUri = httpChannel.URI;
// Remove the query to avoid leaking sensitive data
if (reportUri instanceof Ci.nsIURL) {
reportUri = reportUri.mutate().setQuery("").finalize();
}
let triggeringPrincipal = docShell.failedChannel.loadInfo
? docShell.failedChannel.loadInfo.triggeringPrincipal
: null;
blockedInfo = {
list: classifiedChannel.matchedList,
triggeringPrincipal,
provider: classifiedChannel.matchedProvider,
uri: reportUri.asciiSpec,
};
}
}

View file

@ -177,8 +177,11 @@ export class BlockedSiteParent extends EscapablePageParent {
// site, so that they don't lose track after, e.g., tab switching.
// We can't use browser.contentPrincipal which is principal of about:blocked
// Create one from uri with current principal origin attributes
// Remove the query to avoid leaking sensitive data
let uri = browsingContext.currentURI.mutate().setQuery("").finalize();
let principal = Services.scriptSecurityManager.createContentPrincipal(
Services.io.newURI(blockedInfo.uri),
uri,
browsingContext.currentWindowGlobal.documentPrincipal.originAttributes
);
Services.perms.addFromPrincipal(
@ -206,10 +209,10 @@ export class BlockedSiteParent extends EscapablePageParent {
let title;
let chromeWin = browsingContext.topChromeWindow;
if (reason === "malware") {
let reportUrl = lazy.SafeBrowsing.getReportURL(
"MalwareMistake",
blockedInfo
);
let reportUrl = lazy.SafeBrowsing.getReportURL("MalwareMistake", {
...blockedInfo,
uri: uri.asciiSpec,
});
title = lazy.browserBundle.GetStringFromName(
"safebrowsing.reportedAttackSite"
);
@ -233,10 +236,10 @@ export class BlockedSiteParent extends EscapablePageParent {
};
}
} else if (reason === "phishing") {
let reportUrl = lazy.SafeBrowsing.getReportURL(
"PhishMistake",
blockedInfo
);
let reportUrl = lazy.SafeBrowsing.getReportURL("PhishMistake", {
...blockedInfo,
uri: uri.asciiSpec,
});
title = lazy.browserBundle.GetStringFromName(
"safebrowsing.deceptiveSite"
);
@ -281,16 +284,15 @@ export class BlockedSiteParent extends EscapablePageParent {
buttons
);
// Allow users to override and continue through to the site.
// Note that we have to use the passed URI info and can't just
// rely on the document URI, because the latter contains
// additional query parameters that should be stripped.
let triggeringPrincipal =
blockedInfo.triggeringPrincipal ||
Services.scriptSecurityManager.createNullPrincipal({});
let activeSHEntry = browsingContext.activeSessionHistoryEntry;
if (!activeSHEntry) {
console.error("No active session history entry found");
return;
}
browsingContext.fixupAndLoadURIString(blockedInfo.uri, {
triggeringPrincipal,
// Allow users to override and continue through to the site.
browsingContext.loadURI(uri, {
triggeringPrincipal: activeSHEntry.triggeringPrincipal,
loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
});
}

View file

@ -142,9 +142,7 @@ export class DOMFullscreenParent extends JSWindowActorParent {
case "DOMFullscreen:NewOrigin": {
// Don't show the warning if we've already exited fullscreen.
if (window.document.fullscreen) {
window.PointerlockFsWarning.showFullScreen(
aMessage.data.originNoSuffix
);
window.PointerlockFsWarning.showFullScreen(topBrowsingContext);
}
this.updateFullscreenWindowReference(window);
break;
@ -222,7 +220,7 @@ export class DOMFullscreenParent extends JSWindowActorParent {
if (!this.hasBeenDestroyed() && this.requestOrigin) {
window.PointerlockFsWarning.showFullScreen(
this.requestOrigin.manager.documentPrincipal.originNoSuffix
this.requestOrigin.browsingContext
);
}
break;

View file

@ -49,10 +49,16 @@ var PointerlockFsWarning = {
}
},
showFullScreen(aOrigin) {
// Show info that top level has entered fullscreen. Ultimately, it is always
// ancestors who are in control and can with various means make the user believe
// a site has entered fullscreen while displaying it's own content.
// We try to make it clear to the user that it's the top level that is actually in fullscreen
showFullScreen(browsingContext) {
const origin =
browsingContext.top.currentWindowGlobal.documentPrincipal.originNoSuffix;
let timeout = Services.prefs.getIntPref("full-screen-api.warning.timeout");
let delay = Services.prefs.getIntPref("full-screen-api.warning.delay");
this.show(aOrigin, "fullscreen-warning", timeout, delay);
this.show(origin, "fullscreen-warning", timeout, delay);
},
// Shows a warning that the site has entered fullscreen or

View file

@ -60,6 +60,9 @@ skip-if = [
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && swgl", # Bug 1949995
]
["browser_fullscreen_toplevel_warning.js"]
support-files = ["fullscreen.html"]
["browser_fullscreen_warning.js"]
support-files = ["fullscreen.html"]
skip-if = [

View file

@ -0,0 +1,184 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const FULLSCREEN_PATH =
"/browser/browser/base/content/test/fullscreen/fullscreen.html";
function getWarningDomain(warning) {
let textElem = warning.querySelector(".pointerlockfswarning-domain-text");
if (textElem.hidden) {
return null;
}
let args = textElem.getAttribute("data-l10n-args");
return args ? JSON.parse(args).domain : null;
}
async function waitForWarningState(aWarningElement, aExpectedState) {
await BrowserTestUtils.waitForAttribute(aExpectedState, aWarningElement, "");
}
add_setup(async function init() {
await SpecialPowers.pushPrefEnv({
set: [
["test.wait300msAfterTabSwitch", true],
["full-screen-api.enabled", true],
["full-screen-api.allow-trusted-requests-only", false],
],
});
});
// Bug 2021080 - Verify the fullscreen warning always displays the top-level domain,
// not the origin of the cross-origin frame that requested fullscreen.
add_task(async function test_fullscreen_warning_cross_origin_shows_toplevel() {
await BrowserTestUtils.withNewTab("https://example.com", async browser => {
let warning = document.getElementById("fullscreen-warning");
await SpecialPowers.spawn(browser, [FULLSCREEN_PATH], async path => {
let iframe = content.document.createElement("iframe");
iframe.allow = "fullscreen";
iframe.src = `https://example.org${path}`;
let loaded = new Promise(r =>
iframe.addEventListener("load", r, { once: true })
);
content.document.body.appendChild(iframe);
await loaded;
});
let warningShown = waitForWarningState(warning, "onscreen");
await SpecialPowers.spawn(browser, [], async () => {
let frame = content.document.querySelector("iframe");
frame.focus();
await SpecialPowers.spawn(frame, [], () => {
content.document.getElementById("request").click();
});
});
await warningShown;
let activeOrigin = await SpecialPowers.spawn(browser, [], async () => {
let frame = content.document.querySelector("iframe");
return SpecialPowers.spawn(frame, [], () => content.location.hostname);
});
is(
activeOrigin,
"example.org",
"Cross-origin frame (example.org) is the active fullscreen document"
);
is(
getWarningDomain(warning),
"example.com",
"Warning shows top-level domain, not the active fullscreen frame's domain"
);
let warningHidden = waitForWarningState(warning, "hidden");
let exitPromise = BrowserTestUtils.waitForEvent(
document,
"fullscreenchange",
false,
() => !document.fullscreenElement
);
document.getElementById("fullscreen-exit-button").click();
await Promise.all([exitPromise, warningHidden]);
});
});
// Bug 2021080 - Verify the fullscreen warning shows the top-level domain when each
// of three nested cross-origin frames (top, middle, inner) requests fullscreen.
add_task(async function test_fullscreen_warning_three_nested_origins() {
await BrowserTestUtils.withNewTab("https://example.com", async browser => {
let warning = document.getElementById("fullscreen-warning");
// Build a 3-level nested structure:
// example.com (div > iframe[example.org (div > iframe[example.net])])
await SpecialPowers.spawn(browser, [FULLSCREEN_PATH], async path => {
let topDiv = content.document.createElement("div");
content.document.body.appendChild(topDiv);
let middleFrame = content.document.createElement("iframe");
middleFrame.allow = "fullscreen";
middleFrame.src = `https://example.org${path}`;
let loaded = new Promise(r =>
middleFrame.addEventListener("load", r, { once: true })
);
topDiv.appendChild(middleFrame);
await loaded;
await SpecialPowers.spawn(middleFrame, [path], async innerPath => {
let middleDiv = content.document.createElement("div");
content.document.body.appendChild(middleDiv);
let innerFrame = content.document.createElement("iframe");
innerFrame.allow = "fullscreen";
innerFrame.src = `https://example.net${innerPath}`;
let innerLoaded = new Promise(r =>
innerFrame.addEventListener("load", r, { once: true })
);
middleDiv.appendChild(innerFrame);
await innerLoaded;
});
});
async function exitFullscreen() {
let warningHidden = waitForWarningState(warning, "hidden");
let exitPromise = BrowserTestUtils.waitForEvent(
document,
"fullscreenchange",
false,
() => !document.fullscreenElement
);
document.getElementById("fullscreen-exit-button").click();
await Promise.all([exitPromise, warningHidden]);
}
// Step 1: Top-level (example.com) requests fullscreen on its div.
let warningShown = waitForWarningState(warning, "onscreen");
await SpecialPowers.spawn(browser, [], () => {
content.document.querySelector("div").requestFullscreen();
});
await warningShown;
is(
getWarningDomain(warning),
"example.com",
"Top-level fullscreen: warning shows top-level domain"
);
await exitFullscreen();
// Step 2: Middle frame (example.org) requests fullscreen on its div.
warningShown = waitForWarningState(warning, "onscreen");
await SpecialPowers.spawn(browser, [], async () => {
let middleFrame = content.document.querySelector("iframe");
middleFrame.focus();
await SpecialPowers.spawn(middleFrame, [], () => {
content.document.querySelector("div").requestFullscreen();
});
});
await warningShown;
is(
getWarningDomain(warning),
"example.com",
"Middle frame fullscreen: warning shows top-level domain"
);
await exitFullscreen();
// Step 3: Inner frame (example.net) requests fullscreen on an element.
warningShown = waitForWarningState(warning, "onscreen");
await SpecialPowers.spawn(browser, [], async () => {
let middleFrame = content.document.querySelector("iframe");
await SpecialPowers.spawn(middleFrame, [], async () => {
let innerFrame = content.document.querySelector("iframe");
innerFrame.focus();
await SpecialPowers.spawn(innerFrame, [], () => {
content.document.getElementById("request").click();
});
});
});
await warningShown;
is(
getWarningDomain(warning),
"example.com",
"Inner frame fullscreen: warning shows top-level domain"
);
await exitFullscreen();
});
});

View file

@ -551,11 +551,8 @@ async function getAutofillRecords(data) {
// JSActors, but that would import a lot of code for a targeting attribute.
return 0;
}
let records = await actor?.receiveMessage({
name: "FormAutofill:GetRecords",
data,
});
return records?.records?.length ?? 0;
let records = await actor?.getRecords(data);
return records?.length ?? 0;
}
// Attribution data can be encoded multiple times so we need this function to

View file

@ -1418,15 +1418,10 @@ add_task(async function test_creditCardsSaved() {
gBrowser.selectedBrowser.browsingContext.currentWindowGlobal.getActor(
"FormAutofill"
),
"receiveMessage"
"getRecords"
)
.withArgs(
sandbox.match({
name: "FormAutofill:GetRecords",
data: { collectionName: "creditCards" },
})
)
.resolves({ records: [creditcard] })
.withArgs(sandbox.match({ collectionName: "creditCards" }))
.resolves([creditcard])
.callThrough();
is(
@ -1435,8 +1430,8 @@ add_task(async function test_creditCardsSaved() {
"Should return 1 when 1 credit card is saved"
);
ok(
stub.calledWithMatch({ name: "FormAutofill:GetRecords" }),
"Targeting called FormAutofill:GetRecords"
stub.calledWithMatch({ collectionName: "creditCards" }),
"Targeting called getRecords"
);
sandbox.restore();

View file

@ -60,5 +60,5 @@
}
}
},
"required": ["providerId", "searchPageRegexp", "includeParams"]
"required": ["providerId", "searchPageRegexp"]
}

View file

@ -1 +1 @@
140.9.0
140.10.1

View file

@ -1 +1 @@
140.9.0esr
140.10.1esr

View file

@ -13,5 +13,5 @@ MOZ_BRANDING_DIRECTORY=browser/branding/unofficial
MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/branding/official
# IceCat settings
MOZ_APP_BASENAME=IceCat
MOZ_APP_VERSION=140.9.0
MOZ_APP_VERSION=140.10.1
MOZ_DATA_REPORTING=0

View file

@ -10,4 +10,4 @@
# hardcoded milestones in the tree from these two files.
#--------------------------------------------------------
140.9.0
140.10.1

View file

@ -1,3 +1,15 @@
icecat (140.10.1-1gnu1+build1-0.12.0) ecne; urgency=medium
* New upstream stable release (icecat-140.10.1-1gnu1)
-- Capitulo Mexicano de Software Libre <devel@cmxsl.org> Mon, 04 May 2026 16:44:54 -0600
icecat (140.10.0-1gnu1+build1-0.12.0) ecne; urgency=medium
* New upstream stable release (icecat-140.10.0-1gnu1)
-- Capitulo Mexicano de Software Libre <devel@cmxsl.org> Tue, 28 Apr 2026 03:08:30 -0600
icecat (140.9.0-1gnu1+build1-0.12.0) ecne; urgency=medium
* New upstream stable release (icecat-140.9.0-1gnu1)

View file

@ -190,6 +190,7 @@ fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and
["browser_aboutdebugging_serviceworker_start.js"]
skip-if = [
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && tsan", # Bug 1947358, Bug 2030884
"os == 'linux' && os_version == '24.04' && processor == 'x86_64' && display == 'x11' && tsan", # Bug 1947358
]

View file

@ -96,8 +96,11 @@ fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and
["browser_application_panel_start-service-worker.js"]
fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
skip-if = [
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && tsan", # Bug 1608640
"os == 'linux' && os_version == '24.04' && processor == 'x86_64' && display == 'x11' && tsan", # Bug 1608640
"os == 'linux' && processor == 'x86_64' && tsan", # Bug 1608640, Bug 2030884
"os == 'linux' && processor == 'x86_64' && asan", # Bug 1781479, Bug 2030884
"os == 'mac' && os_version == '14.70' && processor == 'x86_64'", # Bug 1980084, Bug 2030884
"os == 'win' && os_version == '11.26100' && processor == 'x86_64' && asan", # Bug 1781479, Bug 2030884
"os == 'win' && os_version == '11.26200' && processor == 'x86_64' && asan", # Bug 1781479, Bug 2030884
]
["browser_application_panel_target-switching.js"]

View file

@ -47,6 +47,8 @@ export default [
process: true,
global: true,
L10N: true,
// TODO: Add this to the main ESlint globals Bug 2025542
Sanitizer: true,
},
},
rules: {

View file

@ -2,11 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
import React, { Component } from "devtools/client/shared/vendor/react";
import { div } from "devtools/client/shared/vendor/react-dom-factories";
import React, {
Component,
createRef,
} from "devtools/client/shared/vendor/react";
import PropTypes from "devtools/client/shared/vendor/react-prop-types";
import { connect } from "devtools/client/shared/vendor/react-redux";
import { basename } from "../utils/path";
import { createLocation } from "../utils/location";
const fuzzyAldrin = require("resource://devtools/client/shared/vendor/fuzzaldrin-plus.js");
@ -59,6 +60,7 @@ export class QuickOpenModal extends Component {
constructor(props) {
super(props);
this.state = { results: null, selectedIndex: 0 };
this.resultListRef = createRef();
}
static get propTypes() {
@ -223,20 +225,14 @@ export class QuickOpenModal extends Component {
if (query == "" && !this.isShortcutQuery()) {
this.showTopSources();
return;
}
if (this.isSymbolSearch()) {
} else if (this.isSymbolSearch()) {
await this.searchSymbols(query);
return;
}
if (this.isShortcutQuery()) {
} else if (this.isShortcutQuery()) {
this.searchShortcuts(query);
return;
} else {
this.searchSources(query);
}
this.searchSources(query);
this.highlightQueryMatches(this.props.query);
} catch (e) {
// Due to throttling this might get scheduled after the component and the
// toolbox are destroyed.
@ -394,23 +390,35 @@ export class QuickOpenModal extends Component {
isSourcesQuery = () => this.props.searchType === "sources";
isSourceSearch = () => this.isSourcesQuery() || this.isGotoSourceQuery();
/* eslint-disable react/no-danger */
renderHighlight(candidateString, query) {
highlightQueryMatches(query) {
const options = {
wrap: {
tagOpen: '<mark class="highlight">',
tagClose: "</mark>",
},
};
const html = fuzzyAldrin.wrap(candidateString, query, options);
return div({
dangerouslySetInnerHTML: {
__html: html,
},
});
if (this.resultListRef.current) {
const domEl = this.resultListRef.current.ref.current;
for (const titleNode of domEl.querySelectorAll(".title")) {
const htmlString = fuzzyAldrin.wrap(
titleNode.innerText,
query,
options
);
// Sanitizer API not supported in ESR 140
// Should remove at ESR 153
if ("Sanitizer" in window) {
const sanitizer = new Sanitizer({
elements: ["mark"],
attributes: ["class"],
});
titleNode.setHTML(htmlString, { sanitizer });
}
}
}
}
highlightMatching = (query, results) => {
renderResults = (query, results) => {
let newQuery = query;
if (newQuery === "") {
return results;
@ -421,11 +429,7 @@ export class QuickOpenModal extends Component {
if (typeof result.title == "string") {
return {
...result,
title: this.renderHighlight(
result.title,
basename(newQuery),
"title"
),
title: result.title,
};
}
return result;
@ -454,7 +458,7 @@ export class QuickOpenModal extends Component {
const { query } = this.props;
const { selectedIndex, results } = this.state;
const items = this.highlightMatching(query, results || []);
const items = this.renderResults(query, results || []);
const expanded = !!items && !!items.length;
return React.createElement(
Modal,
@ -487,7 +491,7 @@ export class QuickOpenModal extends Component {
items,
selected: selectedIndex,
selectItem: this.selectResultItem,
ref: "resultList",
ref: this.resultListRef,
expanded,
...(this.isSourceSearch() ? SIZE_BIG : SIZE_DEFAULT),
})

View file

@ -115,6 +115,9 @@ skip-if = [
["browser_dbg-call-stack.js"]
["browser_dbg-chrome-create.js"]
skip-if = [
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && asan", # Bug 2030884
]
["browser_dbg-console-async.js"]
@ -286,6 +289,7 @@ skip-if = [
["browser_dbg-javascript-tracer-values-preview.js"]
skip-if = [
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && opt && a11y_checks", # Bug The tracer tree isn't yet accessible
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && tsan", # Bug 1959018, Bug 2030884
"os == 'linux' && os_version == '24.04' && processor == 'x86_64' && display == 'x11' && opt && a11y_checks", # Bug The tracer tree isn't yet accessible
]

View file

@ -94,6 +94,18 @@ add_task(async function () {
pressKey(dbg, "Escape");
assertQuickOpenDisabled(dbg);
info("Test that the highlighted result matches match the query");
await quickOpen(dbg, "sw");
await waitForResults(dbg, [
"script-switching-01.js",
"script-switching-02.js",
]);
await assertHighlightMatches(dbg, 1, "sw");
await assertHighlightMatches(dbg, 2, "sw");
EventUtils.sendString("i");
await assertHighlightMatches(dbg, 1, "swi");
pressKey(dbg, "Escape");
info("Testing goto line:column");
assertLine(dbg, 0);
assertColumn(dbg, 1);
@ -170,3 +182,19 @@ async function assertResultIsTab(dbg, index) {
"Result should be a tab"
);
}
async function assertHighlightMatches(dbg, resultIndex, expectedMatchText) {
// Sanitizer API not supported in ESR 140
// Should remove at ESR 153
if ("Sanitizer" in dbg.win) {
const el = await findResultEl(dbg, resultIndex);
const highlight = await waitForElementWithSelector(dbg, "mark.highlight");
ok(el && !!highlight, "The query match is highlighted");
await waitUntil(
() => el.querySelector("mark.highlight").innerText == expectedMatchText
);
ok(true, "The highlighted text matches the query text");
} else {
ok(true, "The text is not highlighted");
}
}

View file

@ -112,6 +112,12 @@ skip-if = [
["browser_tab_commands_factory.js"]
["browser_tab_descriptor_fission.js"]
skip-if = [
"os == 'linux' && processor == 'x86_64' && asan", # Bug 1966872, Bug 2030884
"os == 'linux' && processor == 'x86_64' && tsan", # Bug 1966872, Bug 2030884
"os == 'win' && os_version == '11.26100' && processor == 'x86_64' && asan", # Bug 1966872, Bug 2030884
"os == 'win' && os_version == '11.26200' && processor == 'x86_64' && asan", # Bug 1966872, Bug 2030884
]
["browser_target_cached-front.js"]
@ -291,6 +297,9 @@ fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and
["browser_toolbox_watchedByDevTools.js"]
["browser_toolbox_window_global_debugging.js"]
skip-if = [
"os == 'linux' && processor == 'x86_64' && tsan", # Bug 1950845, Bug 2030884
]
["browser_toolbox_window_reload_target.js"]

View file

@ -150,8 +150,11 @@ fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and
["browser_animation_logic_mutations_fast.js"]
skip-if = [
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && debug", # Bug 1567800
"os == 'linux' && os_version == '24.04' && processor == 'x86_64' && display == 'x11' && debug", # Bug 1567800
"os == 'linux' && processor == 'x86_64' && asan", # Bug 1980142, Bug 2030884
"os == 'linux' && processor == 'x86_64' && debug", # Bug 1567800, Bug 2030884
"os == 'linux' && processor == 'x86_64' && tsan", # Bug 1980142, Bug 2030884
"os == 'win' && os_version == '11.26100' && processor == 'x86_64' && debug", # Bug 1980142, Bug 2030884
"os == 'win' && os_version == '11.26200' && processor == 'x86_64' && debug", # Bug 1980142, Bug 2030884
]
["browser_animation_logic_mutations_properties.js"]

View file

@ -587,7 +587,9 @@ support-files = ["browser_webconsole_object_inspector_entries.snapshot.mjs"]
https_first_disabled = true # JS HttpServer doesn't support https
skip-if = [
"http3", # JS HttpServer doesn't support http3
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && opt", # Bug 1965340, Bug 2030884
"os == 'linux' && os_version == '24.04' && processor == 'x86_64' && display == 'x11' && opt", # Bug 1965340
"os == 'mac' && os_version == '14.70' && processor == 'x86_64'", # Bug 1965340, Bug 2030884
]
["browser_webconsole_object_inspector_getters.js"]

View file

@ -179,17 +179,17 @@ function getCleanedPacket(key, packet) {
res.startedDateTime = existingPacket.startedDateTime;
}
if (res.totalTime && existingPacket.totalTime) {
res.totalTime = existingPacket.totalTime;
}
if (res.securityState && existingPacket.securityState) {
res.securityState = existingPacket.securityState;
}
// waitingTime can be very small and rounded to 0. However this is still a
// valid waiting time, so check isNaN instead of a simple truthy check.
if (!isNaN(res.waitingTime) && existingPacket.waitingTime) {
// totalTime and waitingTime can be very small and rounded to 0. However this
// is still a valid time value, so check isNaN instead of a simple truthy check.
if (!isNaN(res.totalTime) && !isNaN(existingPacket.totalTime)) {
res.totalTime = existingPacket.totalTime;
}
if (!isNaN(res.waitingTime) && !isNaN(existingPacket.waitingTime)) {
res.waitingTime = existingPacket.waitingTime;
}

View file

@ -1937,6 +1937,19 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(BrowsingContext)
return IsCertainlyAliveForCC(tmp);
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
/* static */
void BrowsingContext::SweepWindowProxies(JSTracer* aTrc) {
if (!sBrowsingContexts) {
return;
}
for (BrowsingContext* bc : sBrowsingContexts->Values()) {
if (bc->mWindowProxy) {
JS_UpdateWeakPointerAfterGC(aTrc, &bc->mWindowProxy);
}
}
}
class RemoteLocationProxy
: public RemoteObjectProxy<BrowsingContext::LocationProxy,
Location_Binding::sCrossOriginProperties> {

View file

@ -734,6 +734,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
mWindowProxy = aWindowProxy;
}
// Since mWindowProxy is a weak pointer it has to be updated during sweeping.
static void SweepWindowProxies(JSTracer* aTrc);
Nullable<WindowProxyHolder> GetWindow();
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@ -1378,10 +1381,8 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
RefPtr<nsGeolocationService> mGeolocationServiceOverride;
// This is not a strong reference, but using a JS::Heap for that should be
// fine. The JSObject stored in here should be a proxy with a
// nsOuterWindowProxy handler, which will update the pointer from its
// objectMoved hook and clear it from its finalize hook.
// This is a weak reference. It will be updated automatically during sweeping
// by SweepWindowProxies.
JS::Heap<JSObject*> mWindowProxy;
LocationProxy mLocation;

View file

@ -38,6 +38,11 @@ void AbortSignalImpl::GetReason(JSContext* aCx,
}
MaybeAssignAbortError(aCx);
aReason.set(mReason);
if (NS_WARN_IF(!JS_WrapValue(aCx, aReason))) {
aReason.setUndefined();
// TODO(Bug 2026137) - AbortSignalImpl::GetReason should be made fallible
JS_ClearPendingException(aCx);
}
}
JS::Value AbortSignalImpl::RawReason() const { return mReason.get(); }
@ -75,7 +80,7 @@ void AbortSignalImpl::RunAbortSteps() {
// https://dom.spec.whatwg.org/#abortsignal-remove could be invoked in an
// earlier algorithm to remove a later algorithm, so |mFollowers| must be a
// |nsTObserverArray| to defend against mutation.
for (RefPtr<AbortFollower>& follower : mFollowers.ForwardRange()) {
for (RefPtr<AbortFollower> follower : mFollowers.ForwardRange()) {
MOZ_ASSERT(follower->mFollowingSignal == this);
follower->RunAbortAlgorithm();
}

View file

@ -386,11 +386,9 @@ void Animation::SetStartTime(const Nullable<TimeDuration>& aNewStartTime) {
}
CancelPendingTasks();
if (mReady) {
// We may have already resolved mReady, but in that case calling
// MaybeResolve is a no-op, so that's okay.
mReady->MaybeResolve(this);
}
// We may have already resolved mReady, but in that case calling
// MaybeResolve is a no-op, so that's okay.
MaybeResolvePromiseWithThis(mReady);
UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Async);
if (IsRelevant()) {
@ -449,9 +447,7 @@ void Animation::SetCurrentTimeNoUpdate(const TimeDuration& aSeekTime) {
ApplyPendingPlaybackRate();
mStartTime.SetNull();
if (mReady) {
mReady->MaybeResolve(this);
}
MaybeResolvePromiseWithThis(mReady);
CancelPendingTasks();
}
@ -608,11 +604,25 @@ Promise* Animation::GetReady(ErrorResult& aRv) {
return nullptr;
}
if (!Pending()) {
mReady->MaybeResolve(this);
MaybeResolvePromiseWithThis(mReady);
}
return mReady;
}
void Animation::MaybeResolvePromiseWithThis(Promise* aPromise) {
if (!aPromise) {
return;
}
if (!nsContentUtils::IsSafeToRunScript()) {
nsContentUtils::AddScriptRunner(NewRunnableMethod<RefPtr<Promise>>(
"MaybeResolvePromiseWithThis", this,
&Animation::MaybeResolvePromiseWithThis, aPromise));
return;
}
RefPtr promise = aPromise;
promise->MaybeResolve(this);
}
Promise* Animation::GetFinished(ErrorResult& aRv) {
nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
if (!mFinished && global) {
@ -714,9 +724,7 @@ void Animation::Finish(ErrorResult& aRv) {
}
CancelPendingTasks();
didChange = true;
if (mReady) {
mReady->MaybeResolve(this);
}
MaybeResolvePromiseWithThis(mReady);
}
UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Sync);
if (didChange && IsRelevant()) {
@ -1607,9 +1615,7 @@ void Animation::ResumeAt(const TimeDuration& aReadyTime) {
MutationObservers::NotifyAnimationChanged(this);
}
if (mReady) {
mReady->MaybeResolve(this);
}
MaybeResolvePromiseWithThis(mReady);
}
void Animation::PauseAt(const TimeDuration& aReadyTime) {
@ -1626,9 +1632,7 @@ void Animation::PauseAt(const TimeDuration& aReadyTime) {
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
if (mReady) {
mReady->MaybeResolve(this);
}
MaybeResolvePromiseWithThis(mReady);
}
void Animation::UpdateTiming(SeekFlag aSeekFlag,
@ -1877,10 +1881,8 @@ void Animation::ResetFinishedPromise() {
}
void Animation::MaybeResolveFinishedPromise() {
if (mFinished) {
mFinished->MaybeResolve(this);
}
mFinishedIsResolved = true;
MaybeResolvePromiseWithThis(mFinished);
}
void Animation::DoFinishNotificationImmediately(MicroTaskRunnable* aAsync) {

View file

@ -466,6 +466,7 @@ class Animation : public DOMEventTargetHelper,
friend class AsyncFinishNotification;
void DoFinishNotificationImmediately(MicroTaskRunnable* aAsync = nullptr);
void QueuePlaybackEvent(nsAtom* aOnEvent, TimeStamp&& aScheduledEventTime);
void MaybeResolvePromiseWithThis(Promise*);
/**
* Remove this animation from the pending animation tracker and reset

View file

@ -10664,23 +10664,25 @@ void nsDOMAttributeMap::BlastSubtreeToPieces(nsINode* aNode) {
mozilla::DebugOnly<nsresult> rv =
element->UnsetAttr(attr->NodeInfo()->NamespaceID(),
attr->NodeInfo()->NameAtom(), false);
attr->NodeInfo()->NameAtom(), true);
// XXX Should we abort here?
NS_ASSERTION(NS_SUCCEEDED(rv), "Uh-oh, UnsetAttr shouldn't fail!");
}
}
if (mozilla::dom::ShadowRoot* shadow = element->GetShadowRoot()) {
// Hold the strong reference to be sure, since we may notify
if (RefPtr<mozilla::dom::ShadowRoot> shadow = element->GetShadowRoot()) {
BlastSubtreeToPieces(shadow);
element->UnattachShadow();
}
}
while (aNode->HasChildren()) {
nsIContent* node = aNode->GetFirstChild();
// Hold the strong reference to be sure, since we are notifying.
nsCOMPtr<nsIContent> node = aNode->GetFirstChild();
BlastSubtreeToPieces(node);
aNode->RemoveChildNode(node, false);
aNode->RemoveChildNode(node, true);
}
}
@ -16275,11 +16277,16 @@ void Document::RequestFullscreenInParentProcess(
/* static */
bool Document::HandlePendingFullscreenRequests(Document* aDoc) {
AutoTArray<UniquePtr<FullscreenRequest>, 1> requests;
{
PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
aDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
while (!iter.AtEnd()) {
requests.AppendElement(iter.TakeAndNext());
}
}
bool handled = false;
PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
aDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
while (!iter.AtEnd()) {
UniquePtr<FullscreenRequest> request = iter.TakeAndNext();
for (UniquePtr<FullscreenRequest>& request : requests) {
Document* doc = request->Document();
if (doc->ApplyFullscreen(std::move(request))) {
handled = true;

View file

@ -5732,7 +5732,7 @@ class MOZ_RAII IgnoreOpensDuringUnload final {
}
private:
Document* mDoc;
RefPtr<Document> mDoc;
};
bool IsInFocusedTab(Document* aDoc);

View file

@ -279,8 +279,9 @@ nsIFrame* nsIContent::GetPrimaryFrame(mozilla::FlushType aType) {
return nullptr;
}
RefPtr<mozilla::PresShell> presShell = frame->PresShell();
if (aType == mozilla::FlushType::Layout) {
frame->PresShell()->EnsureReflowIfFrameHasHiddenContent(frame);
presShell->EnsureReflowIfFrameHasHiddenContent(frame);
frame = GetPrimaryFrame();
}
@ -2807,6 +2808,7 @@ bool Element::OnlyNotifySameValueSet(int32_t aNamespaceID, nsAtom* aName,
}
nsAutoScriptBlocker scriptBlocker;
OnAttrSetButNotChanged(aNamespaceID, aName, aValue, aNotify);
MutationObservers::NotifyAttributeSetToCurrentValue(this, aNamespaceID,
aName);
return true;
@ -2856,7 +2858,6 @@ nsresult Element::SetAttr(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix,
if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
oldValue, &modType, &hasListeners,
&oldValueSet)) {
OnAttrSetButNotChanged(aNamespaceID, aName, value, aNotify);
return NS_OK;
}
}
@ -2906,7 +2907,6 @@ nsresult Element::SetParsedAttr(int32_t aNamespaceID, nsAtom* aName,
if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
oldValue, &modType, &hasListeners,
&oldValueSet)) {
OnAttrSetButNotChanged(aNamespaceID, aName, value, aNotify);
return NS_OK;
}
}

View file

@ -341,8 +341,8 @@ nsresult ImageEncoder::ExtractDataInternal(
return NS_ERROR_INVALID_ARG;
}
auto size = data->GetSize();
rv = aEncoder->InitFromData(map.mData, size.width * size.height * 4,
size.width, size.height, size.width * 4,
rv = aEncoder->InitFromData(map.mData, map.mStride * size.height,
size.width, size.height, map.mStride,
imgIEncoder::INPUT_FORMAT_HOSTARGB, aOptions);
data->Unmap();
}
@ -374,8 +374,8 @@ nsresult ImageEncoder::ExtractDataInternal(
}
rv = aEncoder->InitFromData(data.Elements(),
aSize.width * aSize.height * 4, aSize.width,
aSize.height, aSize.width * 4,
length, aSize.width,
aSize.height, stride,
imgIEncoder::INPUT_FORMAT_HOSTARGB, aOptions);
} else {
if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) {
@ -391,8 +391,8 @@ nsresult ImageEncoder::ExtractDataInternal(
return NS_ERROR_INVALID_ARG;
}
auto size = dataSurface->GetSize();
rv = aEncoder->InitFromData(map.mData, size.width * size.height * 4,
size.width, size.height, size.width * 4,
rv = aEncoder->InitFromData(map.mData, map.mStride * size.height,
size.width, size.height, map.mStride,
imgIEncoder::INPUT_FORMAT_HOSTARGB, aOptions);
dataSurface->Unmap();
}
@ -421,13 +421,13 @@ nsresult ImageEncoder::ExtractDataInternal(
if (!emptyCanvas->Map(DataSourceSurface::MapType::WRITE, &map)) {
return NS_ERROR_INVALID_ARG;
}
auto size = map.mStride * aSize.height;
if (aUsePlaceholder) {
auto size = 4 * aSize.width * aSize.height;
auto* data = map.mData;
GeneratePlaceholderCanvasData(size, data);
}
rv = aEncoder->InitFromData(map.mData, aSize.width * aSize.height * 4,
aSize.width, aSize.height, aSize.width * 4,
rv = aEncoder->InitFromData(map.mData, size, aSize.width, aSize.height,
map.mStride,
imgIEncoder::INPUT_FORMAT_HOSTARGB, aOptions);
emptyCanvas->Unmap();
if (NS_SUCCEEDED(rv)) {

View file

@ -70,15 +70,24 @@ ScreenOrientation::ScreenOrientation(nsPIDOMWindowInner* aWindow,
: DOMEventTargetHelper(aWindow), mScreen(aScreen) {
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aScreen);
}
mAngle = aScreen->GetOrientationAngle();
mType = InternalOrientationToType(aScreen->GetOrientationType());
/* static */ already_AddRefed<ScreenOrientation> ScreenOrientation::Create(
nsPIDOMWindowInner* aWindow, nsScreen* aScreen) {
RefPtr screenOrientation = new ScreenOrientation(aWindow, aScreen);
Document* doc = GetResponsibleDocument();
screenOrientation->mAngle = aScreen->GetOrientationAngle();
screenOrientation->mType =
InternalOrientationToType(aScreen->GetOrientationType());
Document* doc = screenOrientation->GetResponsibleDocument();
BrowsingContext* bc = doc ? doc->GetBrowsingContext() : nullptr;
if (bc && !bc->IsDiscarded() && !bc->InRDMPane()) {
MOZ_ALWAYS_SUCCEEDS(bc->SetCurrentOrientation(mType, mAngle));
MOZ_ALWAYS_SUCCEEDS(bc->SetCurrentOrientation(screenOrientation->mType,
screenOrientation->mAngle));
}
return screenOrientation.forget();
}
ScreenOrientation::~ScreenOrientation() {

View file

@ -33,8 +33,13 @@ class ScreenOrientation final : public DOMEventTargetHelper {
// Called when the orientation may have changed.
void MaybeChanged();
private:
ScreenOrientation(nsPIDOMWindowInner* aWindow, nsScreen* aScreen);
public:
static already_AddRefed<ScreenOrientation> Create(nsPIDOMWindowInner* aWindow,
nsScreen* aScreen);
already_AddRefed<Promise> Lock(OrientationLockType aOrientation,
ErrorResult& aRv);
@ -107,8 +112,8 @@ class ScreenOrientation final : public DOMEventTargetHelper {
RefPtr<nsScreen> mScreen;
RefPtr<FullscreenEventListener> mFullscreenListener;
RefPtr<VisibleEventListener> mVisibleListener;
OrientationType mType;
uint16_t mAngle;
OrientationType mType{};
uint16_t mAngle{};
// Whether we've tried to call into hal to lock the device orientation. This
// is needed because you don't want calling UnlockDeviceOrientation() during
// shutdown to initialize PHal if it hasn't been initialized earlier. Also,

View file

@ -213,7 +213,7 @@ void ShadowRoot::Unattach() {
void ShadowRoot::InvalidateStyleAndLayoutOnSubtree(Element* aElement) {
MOZ_ASSERT(aElement);
Document* doc = GetComposedDoc();
Document* doc = aElement->GetComposedDoc();
if (!doc) {
return;
}

View file

@ -136,7 +136,7 @@ class ShadowRoot final : public DocumentFragment, public DocumentOrShadowRoot {
* It is important that this runs _before_ actually shuffling the flat tree
* around, so that layout knows the actual tree that it needs to invalidate.
*/
void InvalidateStyleAndLayoutOnSubtree(Element*);
static void InvalidateStyleAndLayoutOnSubtree(Element*);
private:
void InsertSheetIntoAuthorData(size_t aIndex, StyleSheet&,

View file

@ -15,6 +15,7 @@
#include "js/Wrapper.h"
#include "jsapi.h"
#include "mozilla/Assertions.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/Maybe.h"
#include "mozilla/Span.h"
@ -214,8 +215,13 @@ bool StructuredCloneBlob::WriteStructuredClone(JSContext* aCx,
bool StructuredCloneBlob::Holder::WriteStructuredClone(
JSContext* aCx, JSStructuredCloneWriter* aWriter,
StructuredCloneHolder* aHolder) {
auto& data = mBuffer->data();
if (!JS_WriteUint32Pair(aWriter, data.Size(), JS_STRUCTURED_CLONE_VERSION) ||
const auto& data = mBuffer->data();
CheckedUint32 dataSize(data.Size());
if (!dataSize.isValid()) {
return false;
}
if (!JS_WriteUint32Pair(aWriter, dataSize.value(),
JS_STRUCTURED_CLONE_VERSION) ||
!JS_WriteUint32Pair(aWriter, aHolder->BlobImpls().Length(),
BlobImpls().Length())) {
return false;

View file

@ -938,9 +938,8 @@ TextInputProcessor::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
NS_IMETHODIMP_(IMENotificationRequests)
TextInputProcessor::GetIMENotificationRequests() {
// TextInputProcessor should support all change notifications.
return IMENotificationRequests(
IMENotificationRequests::NOTIFY_TEXT_CHANGE |
IMENotificationRequests::NOTIFY_POSITION_CHANGE);
return {IMENotificationRequest::TextChange,
IMENotificationRequest::PositionChange};
}
NS_IMETHODIMP_(void)

View file

@ -27,9 +27,10 @@ class KeyboardEvent;
class TextInputProcessor final : public nsITextInputProcessor,
public widget::TextEventDispatcherListener {
typedef mozilla::widget::IMENotification IMENotification;
typedef mozilla::widget::IMENotificationRequests IMENotificationRequests;
typedef mozilla::widget::TextEventDispatcher TextEventDispatcher;
using IMENotification = mozilla::widget::IMENotification;
using IMENotificationRequest = mozilla::widget::IMENotificationRequest;
using IMENotificationRequests = mozilla::widget::IMENotificationRequests;
using TextEventDispatcher = mozilla::widget::TextEventDispatcher;
public:
TextInputProcessor();

View file

@ -1333,7 +1333,11 @@ void nsAttrValue::ParseAtom(const nsAString& aValue) {
void nsAttrValue::ParseAtomArray(nsAtom* aValue) {
if (MiscContainer* cont = AtomArrayCache::Lookup(aValue)) {
// Set our MiscContainer to the cached one.
// AddRef must happen before ResetIfSet: the cache does not hold a strong
// reference, and ResetIfSet could release the last reference to cont if
// this nsAttrValue is already holding it.
NS_ADDREF(cont);
ResetIfSet();
SetPtrValueAndType(cont, eOtherBase);
return;
}
@ -1933,7 +1937,11 @@ bool nsAttrValue::ParseStyleAttribute(const nsAString& aString,
if (cachingAllowed) {
if (MiscContainer* cont = attrStyles->LookupStyleAttr(aString)) {
// Set our MiscContainer to the cached one.
// AddRef must happen before ResetIfSet: the cache does not hold a strong
// reference, and ResetIfSet could release the last reference to cont if
// this nsAttrValue is already holding it.
NS_ADDREF(cont);
ResetIfSet();
SetPtrValueAndType(cont, eOtherBase);
return true;
}

View file

@ -592,7 +592,8 @@ Element* nsContentList::NamedItem(const nsAString& aName, bool aDoFlush) {
return mNamedItemsCache->Get(name);
}
void nsContentList::GetSupportedNames(nsTArray<nsString>& aNames) {
void nsContentList::GetSupportedNames(nsTArray<nsString>& aNames,
FilterElementWithName aFilter) {
BringSelfUpToDate(true);
AutoTArray<nsAtom*, 8> atoms;
@ -606,14 +607,14 @@ void nsContentList::GetSupportedNames(nsTArray<nsString>& aNames) {
}
}
nsGenericHTMLElement* el = nsGenericHTMLElement::FromNode(content);
if (el) {
if (nsGenericHTMLElement* el = nsGenericHTMLElement::FromNode(content)) {
// XXXbz should we be checking for particular tags here? How
// stable is this part of the spec?
// Note: nsINode::HasName means the name is exposed on the document,
// which is false for options, so we don't check it here.
const nsAttrValue* val = el->GetParsedAttr(nsGkAtoms::name);
if (val && val->Type() == nsAttrValue::eAtom) {
if (val && val->Type() == nsAttrValue::eAtom &&
(!aFilter || aFilter(el))) {
nsAtom* name = val->GetAtomValue();
MOZ_ASSERT(name != nsGkAtoms::_empty, "Empty names don't get atomized");
if (!atoms.Contains(name)) {

View file

@ -296,13 +296,21 @@ class nsContentList : public nsBaseContentList,
aFound = !!item;
return item;
}
void GetSupportedNames(nsTArray<nsString>& aNames) override;
void GetSupportedNames(nsTArray<nsString>& aNames) override {
GetSupportedNames(aNames, nullptr);
}
// nsContentList public methods
uint32_t Length(bool aDoFlush);
nsIContent* Item(uint32_t aIndex, bool aDoFlush);
Element* NamedItem(const nsAString& aName, bool aDoFlush);
// Used by HTMLAllCollection to limit the elements whose name attribute is
// considered. The filter MUST NOT cause any flushes.
using FilterElementWithName = bool (*)(nsIContent*);
void GetSupportedNames(nsTArray<nsString>& aNames,
FilterElementWithName aFilter);
// nsIMutationObserver
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED

View file

@ -4533,10 +4533,9 @@ nsDOMWindowUtils::WrCapture() {
}
NS_IMETHODIMP
nsDOMWindowUtils::WrStartCaptureSequence(const nsACString& aPath,
uint32_t aFlags) {
nsDOMWindowUtils::WrStartCaptureSequence(uint32_t aFlags) {
if (WebRenderBridgeChild* wrbc = GetWebRenderBridge()) {
wrbc->StartCaptureSequence(nsCString(aPath), aFlags);
wrbc->StartCaptureSequence(aFlags);
}
return NS_OK;
}

View file

@ -105,7 +105,7 @@ void nsFrameLoaderOwner::ChangeRemotenessCommon(
// no other blockers. Since we're going to be adding a new blocker as soon as
// we recreate the frame loader, this is not what we want, so add our own
// blocker until the process is complete.
Document* doc = owner->OwnerDoc();
RefPtr<Document> doc = owner->OwnerDoc();
doc->BlockOnload();
auto cleanup = MakeScopeExit([&]() { doc->UnblockOnload(false); });

View file

@ -2459,7 +2459,7 @@ VisualViewport* nsGlobalWindowInner::VisualViewport() {
nsScreen* nsGlobalWindowInner::Screen() {
if (!mScreen) {
mScreen = new nsScreen(this);
mScreen = nsScreen::Create(this);
}
return mScreen;
}
@ -2535,6 +2535,10 @@ Maybe<ServiceWorkerDescriptor> nsPIDOMWindowInner::GetController() const {
return nsGlobalWindowInner::Cast(this)->GetController();
}
ClientSource* nsPIDOMWindowInner::GetClientSource() const {
return nsGlobalWindowInner::Cast(this)->GetClientSource();
}
void nsPIDOMWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
return nsGlobalWindowInner::Cast(this)->SetCsp(aCsp);
}

View file

@ -1286,6 +1286,10 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
void SetCurrentPasteDataTransfer(mozilla::dom::DataTransfer* aDataTransfer);
mozilla::dom::DataTransfer* GetCurrentPasteDataTransfer() const;
mozilla::dom::ClientSource* GetClientSource() const {
return mClientSource.get();
}
private:
RefPtr<mozilla::dom::ContentMediaController> mContentMediaController;

View file

@ -1105,11 +1105,7 @@ void nsINode::Normalize() {
const nsTextFragment* text = node->GetText();
if (text->GetLength()) {
nsIContent* target = node->GetPreviousSibling();
NS_ASSERTION(
(target && target->NodeType() == TEXT_NODE) || hasRemoveListeners,
"Should always have a previous text sibling unless "
"mutation events messed us up");
if (!hasRemoveListeners || (target && target->NodeType() == TEXT_NODE)) {
if (target && target->NodeType() == TEXT_NODE) {
nsTextNode* t = static_cast<nsTextNode*>(target);
if (text->Is2b()) {
t->AppendTextForNormalize(text->Get2b(), text->GetLength(), true,
@ -3721,8 +3717,10 @@ already_AddRefed<nsINode> nsINode::CloneAndAdopt(
JSAutoRealm ar(cx, wrapper);
UpdateReflectorGlobal(cx, wrapper, aError);
if (aError.Failed()) {
bool needsRollBack = false;
if (wasRegistered) {
newDoc->UnregisterActivityObserver(aNode->AsElement());
needsRollBack =
newDoc->UnregisterActivityObserver(aNode->AsElement());
}
if (hadProperties) {
// NOTE: When it fails it removes all properties for the node
@ -3732,7 +3730,7 @@ already_AddRefed<nsINode> nsINode::CloneAndAdopt(
}
aNode->mNodeInfo.swap(newNodeInfo);
aNode->NodeInfoChanged(newDoc);
if (wasRegistered) {
if (needsRollBack) {
oldDoc->RegisterActivityObserver(aNode->AsElement());
}
return nullptr;

View file

@ -214,8 +214,8 @@ already_AddRefed<nsIDocShell> nsObjectLoadingContent::SetupDocShell(
}
if (!docShell) {
mFrameLoader->Destroy();
mFrameLoader = nullptr;
RefPtr<nsFrameLoader> loader = std::move(mFrameLoader);
loader->Destroy();
return nullptr;
}
@ -1284,8 +1284,8 @@ nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad,
nsCOMPtr<nsIURILoader> uriLoader(components::URILoader::Service());
if (NS_WARN_IF(!uriLoader)) {
MOZ_ASSERT_UNREACHABLE("Failed to get uriLoader service");
mFrameLoader->Destroy();
mFrameLoader = nullptr;
RefPtr<nsFrameLoader> loader = std::move(mFrameLoader);
loader->Destroy();
break;
}
@ -1591,11 +1591,6 @@ uint32_t nsObjectLoadingContent::GetCapabilities() const {
}
void nsObjectLoadingContent::Destroy() {
if (mFrameLoader) {
mFrameLoader->Destroy();
mFrameLoader = nullptr;
}
// Reset state so that if the element is re-appended to tree again (e.g.
// adopting to another document), it will reload resource again.
UnloadObject();
@ -1619,8 +1614,8 @@ void nsObjectLoadingContent::Unlink(nsObjectLoadingContent* tmp) {
void nsObjectLoadingContent::UnloadObject(bool aResetState) {
if (mFrameLoader) {
mFrameLoader->Destroy();
mFrameLoader = nullptr;
RefPtr<nsFrameLoader> loader = std::move(mFrameLoader);
loader->Destroy();
}
if (aResetState) {
@ -1718,23 +1713,30 @@ void nsObjectLoadingContent::TriggerInnerFallbackLoads() {
}
// Do a depth-first traverse of node tree with the current element as root,
// looking for non-<param> elements. If we find some then we have an HTML
// fallback for this element.
// fallback for this element
AutoTArray<RefPtr<nsIContent>, 4> targets;
for (nsIContent* child = el->GetFirstChild(); child;) {
// <object> and <embed> elements in the fallback need to StartObjectLoad.
// Their children should be ignored since they are part of those element's
// fallback.
if (auto* embed = HTMLEmbedElement::FromNode(child)) {
embed->StartObjectLoad(true, true);
// Skip the children
child = child->GetNextNonChildNode(el);
} else if (auto* object = HTMLObjectElement::FromNode(child)) {
object->StartObjectLoad(true, true);
// Skip the children
if (child->IsAnyOfHTMLElements(nsGkAtoms::embed, nsGkAtoms::object)) {
targets.AppendElement(child);
child = child->GetNextNonChildNode(el);
} else {
child = child->GetNextNode(el);
}
}
for (RefPtr<nsIContent>& target : targets) {
if (!target->IsInclusiveDescendantOf(el)) {
continue;
}
if (auto* embed = HTMLEmbedElement::FromNode(target)) {
embed->StartObjectLoad(true, true);
} else if (auto* object = HTMLObjectElement::FromNode(target)) {
object->StartObjectLoad(true, true);
}
}
}
NS_IMETHODIMP

View file

@ -366,6 +366,7 @@ class nsPIDOMWindowInner : public mozIDOMWindow {
mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const;
mozilla::dom::ClientSource* GetClientSource() const;
void SetCsp(nsIContentSecurityPolicy* aCsp);
void SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp);

View file

@ -2829,20 +2829,6 @@ static nsTextFrame* GetTextFrameForContent(nsIContent* aContent,
return nullptr;
}
// Try to un-suppress whitespace if needed, but only if we'll be able to flush
// to immediately see the results of the un-suppression. If we can't flush
// here, then calling EnsureFrameForTextNodeIsCreatedAfterFlush would be
// pointless anyway.
if (aFlushLayout) {
const bool frameWillBeUnsuppressed =
presShell->FrameConstructor()
->EnsureFrameForTextNodeIsCreatedAfterFlush(
static_cast<CharacterData*>(aContent));
if (frameWillBeUnsuppressed) {
doc->FlushPendingNotifications(FlushType::Layout);
}
}
nsIFrame* frame = aContent->GetPrimaryFrame();
if (!frame || !frame->IsTextFrame()) {
return nullptr;

View file

@ -23,8 +23,14 @@ using namespace mozilla;
using namespace mozilla::dom;
nsScreen::nsScreen(nsPIDOMWindowInner* aWindow)
: DOMEventTargetHelper(aWindow),
mScreenOrientation(new ScreenOrientation(aWindow, this)) {}
: DOMEventTargetHelper(aWindow) {}
/* static */ already_AddRefed<nsScreen> nsScreen::Create(
nsPIDOMWindowInner* aWindow) {
RefPtr screen = new nsScreen(aWindow);
screen->mScreenOrientation = ScreenOrientation::Create(aWindow, screen);
return screen.forget();
}
nsScreen::~nsScreen() = default;

View file

@ -21,9 +21,12 @@ enum class RFPTarget : uint64_t;
// Script "screen" object
class nsScreen : public mozilla::DOMEventTargetHelper {
public:
private:
explicit nsScreen(nsPIDOMWindowInner* aWindow);
public:
static already_AddRefed<nsScreen> Create(nsPIDOMWindowInner* aWindow);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsScreen,
mozilla::DOMEventTargetHelper)

View file

@ -47,6 +47,13 @@ void nsWrapperCache::SetWrapperJSObject(JSObject* aNewWrapper) {
}
}
void nsWrapperCache::ClearWrapperOnWrapFailure() {
if (IsNurseryWrapper(mWrapper)) {
CycleCollectedJSRuntime::Get()->NurseryWrapperRemovedSlow(this);
}
ClearWrapper();
}
void nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder) {
// If the behavior here changes in a substantive way, you may need
// to update css::Rule::UnlinkDeclarationWrapper as well.

View file

@ -170,6 +170,8 @@ class JS_HAZ_ROOTED nsWrapperCache {
}
}
void ClearWrapperOnWrapFailure();
/**
* Update the wrapper when the object moves between globals.
*/

View file

@ -86,12 +86,18 @@ inline void nsWrapperCache::UpdateWrapperForNewGlobal(T* aScriptObjectHolder,
SetPreservingWrapper(false);
}
JSObject* oldWrapper = mWrapper;
SetWrapper(aNewWrapper);
if (zoneChanged) {
PreserveWrapper(aScriptObjectHolder);
} else if (preserving) {
SetPreservingWrapper(true);
if (!JS::ObjectIsTenured(mWrapper)) {
// SetWrapper doesn't fire a write barrier; add one so minor GC can
// update mWrapper if the new wrapper is tenured.
JS::HeapObjectPostWriteBarrier(&mWrapper, oldWrapper, mWrapper);
}
}
}

View file

@ -1,3 +1,5 @@
[DEFAULT]
["test_bug_2027541.html"]
["test_nested_modules.html"]

View file

@ -0,0 +1,47 @@
<!DOCTYPE html>
<head>
<meta charset=utf-8>
<title>Call import.meta.resolve after iframe removal</title>
</head>
<body>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script>
SimpleTest.waitForExplicitFinish();
window.stolenResolve = null;
async function runTest() {
const iframe = document.createElement("iframe");
iframe.srcdoc = `<!DOCTYPE html><html><body>
<script type="module">
window.parent.stolenResolve = import.meta.resolve;
window.parent.postMessage("ready", "*");
<\/script>
</body></html>`;
const ready = new Promise(resolve => {
window.addEventListener("message", () => resolve(), { once: true });
});
document.body.appendChild(iframe);
await ready;
ok(typeof window.stolenResolve === "function",
"Got import.meta.resolve from inline iframe module");
iframe.remove();
SpecialPowers.forceGC();
SpecialPowers.forceCC();
await new Promise(r => requestAnimationFrame(r));
let result = window.stolenResolve("https://example.com/");
is(result, "https://example.com/",
"import.meta.resolve returns correct result after iframe removal and GC");
window.stolenResolve = null;
SimpleTest.finish();
}
runTest();
</script>
</body>

View file

@ -2473,8 +2473,7 @@ void UpdateReflectorGlobal(JSContext* aCx, JS::Handle<JSObject*> aObjArg,
}
// We've set up |newobj|, so we make it own the native by setting its reserved
// slot and nulling out the reserved slot of |obj|. Update the wrapper cache
// to keep everything consistent in case GC moves newobj.
// slot and nulling out the reserved slot of |obj|.
//
// NB: It's important to do this _after_ copying the properties to
// propertyHolder. Otherwise, an object with |foo.x === foo| will
@ -2483,18 +2482,37 @@ void UpdateReflectorGlobal(JSContext* aCx, JS::Handle<JSObject*> aObjArg,
JS::SetReservedSlot(newobj, DOM_OBJECT_SLOT,
JS::GetReservedSlot(aObj, DOM_OBJECT_SLOT));
JS::SetReservedSlot(aObj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
size_t nslots = JSCLASS_RESERVED_SLOTS(JS::GetClass(aObj));
for (size_t slot = DOM_INSTANCE_RESERVED_SLOTS; slot < nslots; ++slot) {
const JS::Value& slotValue = JS::GetReservedSlot(aObj, slot);
if (slotValue.isObject()) {
JSObject* slotObj = &slotValue.toObject();
if (IsObservableArrayProxy(slotObj)) {
JS::SetReservedSlot(newobj, slot, slotValue);
JS::SetReservedSlot(aObj, slot, JS::UndefinedValue());
}
}
}
nsWrapperCache* cache = nullptr;
CallQueryInterface(native, &cache);
cache->UpdateWrapperForNewGlobal(native, newobj);
// For preserved wrappers the store buffer keeps mWrapper consistent across
// the transplant. For non-preserved wrappers clear mWrapper so that
// JSObjectsTenured doesn't follow a stale pointer if nursery GC fires.
bool preserving = cache->PreservingWrapper();
if (preserving) {
cache->UpdateWrapperForNewGlobal(native, newobj);
} else {
cache->ClearWrapper();
}
aObj = xpc::TransplantObjectRetainingXrayExpandos(aCx, aObj, newobj);
if (!aObj) {
MOZ_CRASH();
}
// Update the wrapper cache again if transplanting didn't use newobj but
// returned some other object.
if (aObj != newobj) {
if (!preserving || aObj != newobj) {
MOZ_ASSERT(UnwrapDOMObjectToISupports(aObj) == native);
cache->UpdateWrapperForNewGlobal(native, aObj);
}
@ -3555,6 +3573,7 @@ static bool GetBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
? aObj
: js::UncheckedUnwrap(aObj,
/* stopAtWindowProxy = */ false);
MOZ_ASSERT(aSlotIndex < JSCLASS_RESERVED_SLOTS(JS::GetClass(reflector)));
// Retrieve the backing object from the reserved slot on the maplike/setlike
// object. If it doesn't exist yet, create it.

View file

@ -4683,7 +4683,7 @@ class CGWrapWithCacheMethod(CGAbstractMethod):
failureCode = dedent(
"""
aCache->ReleaseWrapper(aObject);
aCache->ClearWrapper();
aCache->ClearWrapperOnWrapFailure();
return false;
"""
)
@ -23213,7 +23213,9 @@ class CGIterableMethodGenerator(CGGeneric):
CGGeneric.__init__(self, createIterator)
def getObservableArrayBackingObject(descriptor, attr, errorReturn="return false;\n"):
def getObservableArrayBackingObject(
descriptor, attr, objName="obj", errorReturn="return false;\n"
):
"""
Generate code to get/create a JS backing list for an observableArray attribute
from the declaration slot.
@ -23228,7 +23230,7 @@ def getObservableArrayBackingObject(descriptor, attr, errorReturn="return false;
"""
JS::Rooted<JSObject*> backingObj(cx);
bool created = false;
if (!GetObservableArrayBackingObject(cx, obj, ${slot},
if (!GetObservableArrayBackingObject(cx, ${objName}, ${slot},
&backingObj, &created, ${namespace}::ObservableArrayProxyHandler::getInstance(),
self)) {
$*{errorReturn}
@ -23237,8 +23239,9 @@ def getObservableArrayBackingObject(descriptor, attr, errorReturn="return false;
PreserveWrapper(self);
}
""",
namespace=toBindingNamespace(MakeNativeName(attr.identifier.name)),
objName=objName,
slot=memberReservedSlot(attr, descriptor),
namespace=toBindingNamespace(MakeNativeName(attr.identifier.name)),
errorReturn=errorReturn,
selfType=descriptor.nativeType,
)
@ -23350,12 +23353,18 @@ class CGObservableArrayProxyHandler_callback(ClassMethod):
$*{convertType}
$*{preCallback}
JS::Value val = js::GetProxyReservedSlot(aProxy, OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT);
auto* interface = static_cast<${ifaceType}*>(val.toPrivate());
MOZ_ASSERT(interface);
const JS::Value& val = js::GetProxyReservedSlot(aProxy, OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT);
if (MOZ_LIKELY(!val.isUndefined())) {
auto* interface = static_cast<${ifaceType}*>(val.toPrivate());
MOZ_ASSERT(interface);
ErrorResult rv;
MOZ_KnownLive(interface)->${methodName}(${callbackArgs});
if (rv.MaybeSetPendingException(cx)) {
return false;
}
}
ErrorResult rv;
MOZ_KnownLive(interface)->${methodName}(${callbackArgs});
$*{postCallback}
""",
preConversion=self.preConversion(),
@ -23399,7 +23408,7 @@ class CGObservableArrayProxyHandler_OnDeleteItem(
def postCallback(self):
return dedent(
"""
return !rv.MaybeSetPendingException(cx);
return true;
"""
)
@ -23464,10 +23473,6 @@ class CGObservableArrayProxyHandler_SetIndexedValue(
def postCallback(self):
return dedent(
"""
if (rv.MaybeSetPendingException(cx)) {
return false;
}
if (!JS_SetElement(aCx, aBackingList, aIndex, aValue)) {
return false;
}
@ -23530,7 +23535,9 @@ class CGObservableArraySetterGenerator(CGGeneric):
def __init__(self, descriptor, attr):
assert attr.isAttr()
assert attr.type.isObservableArray()
getBackingObject = getObservableArrayBackingObject(descriptor, attr)
getBackingObject = getObservableArrayBackingObject(
descriptor, attr, objName="unwrappedObj"
)
setElement = dedent(
"""
if (!JS_SetElement(cx, backingObj, i, val)) {
@ -23557,15 +23564,22 @@ class CGObservableArraySetterGenerator(CGGeneric):
return false;
}
${getBackingObject}
const ObservableArrayProxyHandler* handler = GetObservableArrayProxyHandler(backingObj);
if (!handler->SetLength(cx, backingObj, 0)) {
return false;
}
JS::Rooted<JSObject*> unwrappedObj(cx, js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false));
MOZ_ASSERT(IsDOMObject(unwrappedObj));
{
JSAutoRealm ar(cx, unwrappedObj);
JS::Rooted<JS::Value> val(cx);
for (size_t i = 0; i < arg0.Length(); i++) {
$*{conversion}
$*{getBackingObject}
const ObservableArrayProxyHandler* handler = GetObservableArrayProxyHandler(backingObj);
if (!handler->SetLength(cx, backingObj, 0)) {
return false;
}
JS::Rooted<JS::Value> val(cx);
for (size_t i = 0; i < arg0.Length(); i++) {
$*{conversion}
}
}
""",
conversion=conversion,
@ -23611,7 +23625,7 @@ class CGObservableArrayHelperFunctionGenerator(CGHelperFunctionGenerator):
getObservableArrayBackingObject(
descriptor,
attr,
dedent(
errorReturn=dedent(
"""
aRv.Throw(NS_ERROR_UNEXPECTED);
return%s;

View file

@ -215,6 +215,7 @@ class TErrorResult {
// informative message and calling the relevant Throw*Error.
void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG Throw(nsresult rv) {
MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success");
ClearUnionData();
AssignErrorCode(rv);
}
@ -431,7 +432,10 @@ class TErrorResult {
// Backwards-compat to make conversion simpler. We don't call
// Throw() here because people can easily pass success codes to
// this. This operator is deprecated and ideally shouldn't be used.
void operator=(nsresult rv) { AssignErrorCode(rv); }
void operator=(nsresult rv) {
ClearUnionData();
AssignErrorCode(rv);
}
bool Failed() const { return NS_FAILED(mResult); }
@ -542,6 +546,7 @@ class TErrorResult {
}
void AssignErrorCode(nsresult aRv) {
MOZ_ASSERT(mUnionState == HasNothing);
MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR,
"Use ThrowTypeError()");
MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR,

View file

@ -658,7 +658,8 @@ class Manager::CacheMatchAction final : public Manager::BaseAction {
// If we entered shutdown on the main thread while we were doing IO,
// bail out now.
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) {
if (IsCanceled() ||
AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) {
if (stream) {
stream->Close();
}
@ -734,7 +735,8 @@ class Manager::CacheMatchAllAction final : public Manager::BaseAction {
// If we entered shutdown on the main thread while we were doing IO,
// bail out now.
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) {
if (IsCanceled() ||
AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) {
if (stream) {
stream->Close();
}
@ -1289,7 +1291,8 @@ class Manager::CacheKeysAction final : public Manager::BaseAction {
// If we entered shutdown on the main thread while we were doing IO,
// bail out now.
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) {
if (IsCanceled() ||
AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) {
if (stream) {
stream->Close();
}
@ -1368,7 +1371,8 @@ class Manager::StorageMatchAction final : public Manager::BaseAction {
// If we entered shutdown on the main thread while we were doing IO,
// bail out now.
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) {
if (IsCanceled() ||
AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) {
if (stream) {
stream->Close();
}

View file

@ -30,7 +30,6 @@ StreamList::StreamList(SafeRefPtr<Manager> aManager,
mStreamControl(nullptr),
mActivated(false) {
MOZ_DIAGNOSTIC_ASSERT(mManager);
mContext->AddActivity(*this);
}
Manager& StreamList::GetManager() const {
@ -73,6 +72,9 @@ void StreamList::Activate(CacheId aCacheId) {
MOZ_DIAGNOSTIC_ASSERT(mCacheId == INVALID_CACHE_ID);
mActivated = true;
mCacheId = aCacheId;
mContext->AddActivity(*this);
mManager->AddRefCacheId(mCacheId);
mManager->AddStreamList(*this);
@ -134,6 +136,7 @@ void StreamList::NoteClosedAll() {
void StreamList::CloseAll() {
NS_ASSERT_OWNINGTHREAD(StreamList);
SafeRefPtr<StreamList> kungFuDeathGrip = SafeRefPtrFromThis();
if (mStreamControl && mStreamControl->CanSend()) {
// CloseAll will kick off everything needed for shutdown.
@ -192,13 +195,13 @@ StreamList::~StreamList() {
NS_ASSERT_OWNINGTHREAD(StreamList);
MOZ_DIAGNOSTIC_ASSERT(!mStreamControl);
if (mActivated) {
mContext->RemoveActivity(*this);
mManager->RemoveStreamList(*this);
for (uint32_t i = 0; i < mList.Length(); ++i) {
mManager->ReleaseBodyId(mList[i].mId);
}
mManager->ReleaseCacheId(mCacheId);
}
mContext->RemoveActivity(*this);
}
} // namespace mozilla::dom::cache

View file

@ -880,6 +880,8 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasRenderingContext2D)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(CanvasRenderingContext2D)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D)
tmp->RemoveShutdownObserver();
tmp->OnShutdown();
// Make sure we remove ourselves from the list of demotable contexts (raw
// pointers), since we're logically destructed at this point.
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCanvasElement)
@ -2888,8 +2890,12 @@ void CanvasRenderingContext2D::GetLetterSpacing(nsACString& aLetterSpacing) {
void CanvasRenderingContext2D::SetLetterSpacing(
const nsACString& aLetterSpacing) {
ParseSpacing(aLetterSpacing, &CurrentState().letterSpacing,
CurrentState().letterSpacingStr);
nsAutoCString normalized;
Maybe<float> value = ParseSpacing(aLetterSpacing, normalized);
if (value) {
CurrentState().letterSpacing = *value;
CurrentState().letterSpacingStr = normalized;
}
}
void CanvasRenderingContext2D::GetWordSpacing(nsACString& aWordSpacing) {
@ -2901,8 +2907,12 @@ void CanvasRenderingContext2D::GetWordSpacing(nsACString& aWordSpacing) {
}
void CanvasRenderingContext2D::SetWordSpacing(const nsACString& aWordSpacing) {
ParseSpacing(aWordSpacing, &CurrentState().wordSpacing,
CurrentState().wordSpacingStr);
nsAutoCString normalized;
Maybe<float> value = ParseSpacing(aWordSpacing, normalized);
if (value) {
CurrentState().wordSpacing = *value;
CurrentState().wordSpacingStr = normalized;
}
}
static GeckoFontMetrics GetFontMetricsFromCanvas(void* aContext) {
@ -2933,9 +2943,8 @@ static GeckoFontMetrics GetFontMetricsFromCanvas(void* aContext) {
0.0f};
}
void CanvasRenderingContext2D::ParseSpacing(const nsACString& aSpacing,
float* aValue,
nsACString& aNormalized) {
Maybe<float> CanvasRenderingContext2D::ParseSpacing(const nsACString& aSpacing,
nsACString& aNormalized) {
// Normalize whitespace in the string before trying to parse it, as we want
// to store it in normalized form, and this allows a simple check against the
// 'normal' keyword, which is not accepted.
@ -2943,28 +2952,28 @@ void CanvasRenderingContext2D::ParseSpacing(const nsACString& aSpacing,
normalized.CompressWhitespace(true, true);
ToLowerCase(normalized);
if (normalized.EqualsLiteral("normal")) {
return;
return Nothing();
}
float value;
if (!Servo_ParseLengthWithoutStyleContext(&normalized, &value,
GetFontMetricsFromCanvas, this)) {
if (!GetPresShell()) {
return;
return Nothing();
}
// This will parse aSpacing as a <length-percentage>...
RefPtr<const ComputedStyle> style =
ResolveStyleForProperty(eCSSProperty_letter_spacing, aSpacing);
if (!style) {
return;
return Nothing();
}
// ...but only <length> is allowed according to the canvas spec.
if (!style->StyleText()->mLetterSpacing.IsLength()) {
return;
return Nothing();
}
value = style->StyleText()->mLetterSpacing.AsLength().ToCSSPixels();
}
aNormalized = normalized;
*aValue = value;
return Some(value);
}
class CanvasUserSpaceMetrics final : public UserSpaceMetricsWithSize {
@ -3434,6 +3443,11 @@ void CanvasRenderingContext2D::StrokeImpl(const gfx::Path& aPath) {
return;
}
const bool needBounds = NeedToCalculateBounds();
if (!IsTargetValid()) {
return;
}
const ContextState* state = &CurrentState();
StrokeOptions strokeOptions(state->lineWidth, CanvasToGfx(state->lineJoin),
CanvasToGfx(state->lineCap), state->miterLimit,
@ -3441,10 +3455,6 @@ void CanvasRenderingContext2D::StrokeImpl(const gfx::Path& aPath) {
state->dashOffset);
state = nullptr;
const bool needBounds = NeedToCalculateBounds();
if (!IsTargetValid()) {
return;
}
gfx::Rect bounds;
if (needBounds) {
bounds = aPath.GetStrokedBounds(strokeOptions, mTarget->GetTransform());
@ -4695,6 +4705,7 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor final
}
mCtx->EnsureTarget();
const bool needBounds = mCtx->NeedToCalculateBounds();
if (!mCtx->IsTargetValid()) {
return;
}
@ -4709,7 +4720,7 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor final
const ContextState& state = mCtx->CurrentState();
gfx::Rect bounds;
if (mCtx->NeedToCalculateBounds()) {
if (needBounds) {
bounds = ToRect(mBoundingBox);
bounds.MoveBy(mPt / mAppUnitsPerDevPixel);
if (style == Style::STROKE) {
@ -4857,6 +4868,9 @@ UniquePtr<TextMetrics> CanvasRenderingContext2D::DrawOrMeasureText(
canvasStyle = nsComputedDOMStyle::GetComputedStyle(mCanvasElement);
}
// This is only needed to know if we can know the drawing bounding box easily.
const bool doCalculateBounds = NeedToCalculateBounds();
// Get text direction, either from the property or inherited from context.
const ContextState& state = CurrentState();
bool isRTL;
@ -4882,8 +4896,6 @@ UniquePtr<TextMetrics> CanvasRenderingContext2D::DrawOrMeasureText(
MOZ_CRASH("unknown direction!");
}
// This is only needed to know if we can know the drawing bounding box easily.
const bool doCalculateBounds = NeedToCalculateBounds();
if (presShell && presShell->IsDestroying()) {
aError = NS_ERROR_FAILURE;
return nullptr;
@ -5163,50 +5175,54 @@ gfxFontGroup* CanvasRenderingContext2D::GetCurrentFontStyle() {
nsPresContext* presContext =
presShell ? presShell->GetPresContext() : nullptr;
// If we have a cached fontGroup, check that it is valid for the current
// prescontext; if not, we need to discard and re-create it.
RefPtr<gfxFontGroup>& fontGroup = CurrentState().fontGroup;
if (fontGroup) {
if (fontGroup->GetPresContext() != presContext) {
fontGroup = nullptr;
}
}
if (!fontGroup) {
ErrorResult err;
constexpr auto kDefaultFontStyle = "10px sans-serif"_ns;
const float kDefaultFontSize = 10.0;
// If the font has already been set, we're re-creating the fontGroup
// and should re-use the existing font attribute; if not, we initialize
// it to the canvas default.
const nsCString& currentFont = CurrentState().font;
bool fontUpdated = SetFontInternal(
currentFont.IsEmpty() ? kDefaultFontStyle : currentFont, err);
if (err.Failed() || !fontUpdated) {
err.SuppressException();
// XXX Should we get a default lang from the prescontext or something?
nsAtom* language = nsGkAtoms::x_western;
bool explicitLanguage = false;
gfxFontStyle style;
style.size = kDefaultFontSize;
int32_t perDevPixel, perCSSPixel;
GetAppUnitsValues(&perDevPixel, &perCSSPixel);
gfxFloat devToCssSize = gfxFloat(perDevPixel) / gfxFloat(perCSSPixel);
const auto* sans =
Servo_FontFamily_Generic(StyleGenericFontFamily::SansSerif);
fontGroup = new gfxFontGroup(
presContext, sans->families, &style, language, explicitLanguage,
presContext ? presContext->GetTextPerfMetrics() : nullptr, nullptr,
devToCssSize, StyleFontVariantEmoji::Normal);
if (fontGroup) {
CurrentState().font = kDefaultFontStyle;
{
// If we have a cached fontGroup, check that it is valid for the current
// prescontext; if not, we need to discard and re-create it.
RefPtr<gfxFontGroup>& fontGroup = CurrentState().fontGroup;
if (fontGroup) {
if (fontGroup->GetPresContext() != presContext) {
fontGroup = nullptr;
} else {
NS_ERROR("Default canvas font is invalid");
return fontGroup;
}
}
}
return fontGroup;
ErrorResult err;
constexpr auto kDefaultFontStyle = "10px sans-serif"_ns;
const float kDefaultFontSize = 10.0;
// If the font has already been set, we're re-creating the fontGroup
// and should re-use the existing font attribute; if not, we initialize
// it to the canvas default.
nsAutoCString currentFont(CurrentState().font);
if (currentFont.IsEmpty()) {
currentFont = kDefaultFontStyle;
}
bool fontUpdated = SetFontInternal(currentFont, err);
if (err.Failed() || !fontUpdated) {
err.SuppressException();
// XXX Should we get a default lang from the prescontext or something?
nsAtom* language = nsGkAtoms::x_western;
bool explicitLanguage = false;
gfxFontStyle style;
style.size = kDefaultFontSize;
int32_t perDevPixel, perCSSPixel;
GetAppUnitsValues(&perDevPixel, &perCSSPixel);
gfxFloat devToCssSize = gfxFloat(perDevPixel) / gfxFloat(perCSSPixel);
const auto* sans =
Servo_FontFamily_Generic(StyleGenericFontFamily::SansSerif);
CurrentState().fontGroup = new gfxFontGroup(
presContext, sans->families, &style, language, explicitLanguage,
presContext ? presContext->GetTextPerfMetrics() : nullptr, nullptr,
devToCssSize, StyleFontVariantEmoji::Normal);
if (CurrentState().fontGroup) {
CurrentState().font = kDefaultFontStyle;
} else {
NS_ERROR("Default canvas font is invalid");
}
}
return CurrentState().fontGroup;
}
//
@ -5711,7 +5727,7 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
HTMLVideoElement* video = HTMLVideoElement::FromNodeOrNull(element);
if (video && mBufferProvider->IsAccelerated() &&
mTarget->IsRecording() &&
!(!NeedToApplyFilter() && NeedToDrawShadow())) {
!(NeedToApplyFilter() || NeedToDrawShadow())) {
res = nsLayoutUtils::SurfaceFromElement(
video, sfeFlags, mTarget, /* aOptimizeSourceSurface */ false);
surfaceDescriptor = MaybeGetSurfaceDescriptorForRemoteCanvas(res);

View file

@ -613,11 +613,13 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
protected:
/**
* Helper to parse a value for the letterSpacing or wordSpacing attribute.
* If successful, returns the result in aValue, and the whitespace-normalized
* value string in aNormalized; if unsuccessful these are left untouched.
* If the string can be parsed, returns Some(value) and sets aNormalized to
* the normalized form of the specified string. If it cannot be parsed as a
* spacing value, returns Nothing, and aNormalized is untouched.
* Note that ParseSpacing may flush style (to resolve font-relative units).
*/
void ParseSpacing(const nsACString& aSpacing, float* aValue,
nsACString& aNormalized);
mozilla::Maybe<float> ParseSpacing(const nsACString& aSpacing,
nsACString& aNormalized);
already_AddRefed<const ComputedStyle> ResolveStyleForProperty(
nsCSSPropertyID aProperty, const nsACString& aValue);
@ -1000,10 +1002,14 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
* last call to UpdateFilter and now.
*/
const gfx::FilterDescription& EnsureUpdatedFilter() {
bool isWriteOnly = mCanvasElement && mCanvasElement->IsWriteOnly();
bool isWriteOnly = IsWriteOnly() ||
(mCanvasElement && mCanvasElement->IsWriteOnly()) ||
(mOffscreenCanvas && mOffscreenCanvas->IsWriteOnly());
if (CurrentState().filterSourceGraphicTainted != isWriteOnly) {
UpdateFilter(/* aFlushIfNeeded = */ true);
EnsureTarget();
// Do not flush here: this runs inside drawing operations that hold raw
// references to mPath/state, and a flush can run script that resets the
// context, leading to UAF. Flush already happened at SetFilter() time.
UpdateFilter(/* aFlushIfNeeded = */ false);
}
MOZ_ASSERT(CurrentState().filterSourceGraphicTainted == isWriteOnly);
return CurrentState().filter;

View file

@ -198,7 +198,18 @@ ClientWebGLContext::ClientWebGLContext(const bool webgl2)
: mIsWebGL2(webgl2),
mExtLoseContext(new ClientWebGLExtensionLoseContext(*this)) {}
ClientWebGLContext::~ClientWebGLContext() { RemovePostRefreshObserver(); }
static inline void SafeReleaseNotLostData(std::shared_ptr<webgl::NotLostData>& notLost) {
if (notLost) {
const auto keepAlive = std::move(notLost);
keepAlive->extensions = {};
keepAlive->state = {};
}
}
ClientWebGLContext::~ClientWebGLContext() {
RemovePostRefreshObserver();
SafeReleaseNotLostData(mNotLost);
}
void ClientWebGLContext::JsWarning(const std::string& utf8) const {
nsIGlobalObject* global = nullptr;
@ -4511,6 +4522,14 @@ void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget,
std::string{"gpuProcessTextureId works only in GPU process."});
}
} break;
case layers::SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr: {
MOZ_ASSERT(desc->image);
keepAliveImage = desc->image;
} break;
case layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: {
MOZ_ASSERT(desc->image);
keepAliveImage = desc->image;
} break;
case layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo: {
const auto& inProcess = mNotLost->inProcess;
MOZ_ASSERT(desc->image);
@ -7004,11 +7023,7 @@ void ImplCycleCollectionTraverse(
}
void ImplCycleCollectionUnlink(std::shared_ptr<webgl::NotLostData>& field) {
if (!field) return;
const auto keepAlive = field;
keepAlive->extensions = {};
keepAlive->state = {};
field = nullptr;
SafeReleaseNotLostData(field);
}
// -----------------------------------------------------

View file

@ -21,7 +21,6 @@
#include "mozilla/gfx/Swizzle.h"
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/layers/RemoteTextureMap.h"
#include "mozilla/widget/ScreenManager.h"
#include "skia/include/core/SkPixmap.h"
#include "nsContentUtils.h"
#include "nsIMemoryReporter.h"
@ -817,7 +816,7 @@ bool DrawTargetWebgl::GenerateComplexClipMask() {
return !!data;
}
bool DrawTargetWebgl::SetSimpleClipRect() {
Maybe<Rect> DrawTargetWebgl::ComputeSimpleClipRect() const {
// Determine whether the clipping rectangle is simple enough to accelerate.
// Check if there is a device space clip rectangle available from the Skia
// target.
@ -829,9 +828,7 @@ bool DrawTargetWebgl::SetSimpleClipRect() {
if (!clip->IsEmpty() && clip->Contains(GetRect())) {
clip = Some(GetRect());
}
mSharedContext->SetClipRect(*clip);
mSharedContext->SetNoClipMask();
return true;
return Some(Rect(*clip));
}
// There was no pixel-aligned clip rect available, so check the clip stack to
@ -842,15 +839,22 @@ bool DrawTargetWebgl::SetSimpleClipRect() {
// complex.
if (clipStack.mPath ||
!clipStack.mTransform.PreservesAxisAlignedRectangles()) {
return false;
return Nothing();
}
// Transform the rect and intersect it with the current clip.
rect =
clipStack.mTransform.TransformBounds(clipStack.mRect).Intersect(rect);
}
mSharedContext->SetClipRect(rect);
mSharedContext->SetNoClipMask();
return true;
return Some(rect);
}
bool DrawTargetWebgl::SetSimpleClipRect() {
if (Maybe<Rect> rect = ComputeSimpleClipRect()) {
mSharedContext->SetClipRect(*rect);
mSharedContext->SetNoClipMask();
return true;
}
return false;
}
// Installs the Skia clip rectangle, if applicable, onto the shared WebGL
@ -874,6 +878,21 @@ bool DrawTargetWebgl::PrepareContext(bool aClipped) {
return mSharedContext->SetTarget(this);
}
// Whether clipping may be necessary for the operation. This tries to avoid
// generating a complex clip mask in case the current target is not active
// or not using WebGL. If there is only a simple clip mask and its bounds
// encompass the viewport, then no clipping is required.
bool DrawTargetWebgl::ShouldClip() {
if (mSharedContext->IsCurrentTarget(this) && !mRefreshClipState) {
return mSharedContext->HasClipMask() ||
!mSharedContext->mClipAARect.Contains(Rect(GetRect()));
}
if (Maybe<Rect> rect = ComputeSimpleClipRect()) {
return !rect->Contains(Rect(GetRect()));
}
return true;
}
bool SharedContextWebgl::IsContextLost() const {
return !mWebgl || mWebgl->IsContextLost();
}
@ -905,29 +924,12 @@ bool DrawTargetWebgl::CanCreate(const IntSize& aSize, SurfaceFormat aFormat) {
return false;
}
// Maximum pref allows 3 different options:
// 0 means unlimited size,
// Maximum pref allows 2 different options:
// <= 0 means unlimited size,
// > 0 means use value as an absolute threshold,
// < 0 means use the number of screen pixels as a threshold.
int32_t maxSize = StaticPrefs::gfx_canvas_accelerated_max_size();
if (maxSize > 0) {
if (std::max(aSize.width, aSize.height) > maxSize) {
return false;
}
} else if (maxSize < 0) {
// Default to historical mobile screen size of 980x480, like FishIEtank.
// In addition, allow acceleration up to this size even if the screen is
// smaller. A lot content expects this size to work well. See Bug 999841
static const int32_t kScreenPixels = 980 * 480;
if (RefPtr<widget::Screen> screen =
widget::ScreenManager::GetSingleton().GetPrimaryScreen()) {
LayoutDeviceIntSize screenSize = screen->GetRect().Size();
if (aSize.width * aSize.height >
std::max(screenSize.width * screenSize.height, kScreenPixels)) {
return false;
}
}
if (maxSize > 0 && std::max(aSize.width, aSize.height) > maxSize) {
return false;
}
return true;
@ -1645,9 +1647,7 @@ void DrawTargetWebgl::ClearRect(const Rect& aRect) {
// If the clear rectangle encompasses the entire viewport and is not clipped,
// then mark the target as entirely clear.
if (containsViewport && mSharedContext->IsCurrentTarget(this) &&
!mSharedContext->HasClipMask() &&
mSharedContext->mClipAARect.Contains(Rect(GetRect()))) {
if (containsViewport && !ShouldClip()) {
mIsClear = true;
}
}
@ -2038,6 +2038,7 @@ bool SharedContextWebgl::UploadSurface(DataSourceSurface* aData,
if (srcRect.IsEmpty()) {
return true;
}
Maybe<DataSourceSurface::ScopedMap> map;
if (aData) {
// If the source rect could not possibly overlap the surface, then it is
// effectively empty with nothing to upload.
@ -2058,15 +2059,15 @@ bool SharedContextWebgl::UploadSurface(DataSourceSurface* aData,
// The surface needs to be uploaded to its backing texture either to
// initialize or update the texture handle contents. Map the data
// contents of the surface so it can be read.
DataSourceSurface::ScopedMap map(aData, DataSourceSurface::READ);
if (!map.IsMapped()) {
map.emplace(aData, DataSourceSurface::READ);
if (!map->IsMapped()) {
return false;
}
int32_t stride = map.GetStride();
int32_t stride = map->GetStride();
// Get the data pointer range considering the sampling rect offset and
// size.
Span<const uint8_t> range(
map.GetData() + srcRect.y * size_t(stride) + srcRect.x * bpp,
map->GetData() + srcRect.y * size_t(stride) + srcRect.x * bpp,
std::max(srcRect.height - 1, 0) * size_t(stride) + srcRect.width * bpp);
texDesc.cpuData = Some(range);
// If the stride happens to be 4 byte aligned, assume that is the

View file

@ -617,9 +617,11 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
return mSharedContext->SupportsPattern(aPattern);
}
Maybe<Rect> ComputeSimpleClipRect() const;
bool SetSimpleClipRect();
bool GenerateComplexClipMask();
bool PrepareContext(bool aClipped = true);
bool ShouldClip();
void DrawRectFallback(const Rect& aRect, const Pattern& aPattern,
const DrawOptions& aOptions,

View file

@ -146,10 +146,29 @@ already_AddRefed<ImageData> ImageData::ReadStructuredClone(
!JS_ReadTypedArray(aReader, &dataArray)) {
return nullptr;
}
MOZ_ASSERT(dataArray.isObject());
JS::Rooted<JSObject*> arrayObj(aCx, &dataArray.toObject());
RefPtr<ImageData> imageData = new ImageData(aGlobal, width, height, arrayObj);
JS::Rooted<JSObject*> dataObj(aCx, &dataArray.toObject());
RootedSpiderMonkeyInterface<Uint8ClampedArray> data(aCx);
if (!data.Init(dataObj)) {
return nullptr;
}
Maybe<size_t> maybeLength = data.ProcessData(
[&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&& nogc) {
return Some(aData.Length());
});
if (maybeLength.isNothing()) {
return nullptr;
}
CheckedInt<uint32_t> calculatedLength =
CheckedInt<uint32_t>(width) * height * 4;
if (!calculatedLength.isValid() ||
size_t(calculatedLength.value()) != maybeLength.value()) {
return nullptr;
}
RefPtr<ImageData> imageData = new ImageData(aGlobal, width, height, dataObj);
return imageData.forget();
}

View file

@ -391,8 +391,8 @@ static bool HasColorAndAlpha(const WebGLTexelFormat format) {
}
bool TexUnpackBlob::ConvertIfNeeded(
const WebGLContext* const webgl, const uint32_t rowLength,
const uint32_t rowCount, WebGLTexelFormat srcFormat,
const WebGLContext* const webgl, const size_t rowLength,
const size_t rowCount, WebGLTexelFormat srcFormat,
const uint8_t* const srcBegin, const ptrdiff_t srcStride,
WebGLTexelFormat dstFormat, const ptrdiff_t dstStride,
const uint8_t** const out_begin,
@ -469,7 +469,7 @@ bool TexUnpackBlob::ConvertIfNeeded(
////
const auto dstTotalBytes = CheckedUint32(rowCount) * dstStride;
const auto dstTotalBytes = CheckedInt<size_t>(rowCount) * dstStride;
if (!dstTotalBytes.isValid()) {
webgl->ErrorOutOfMemory("Calculation failed.");
return false;
@ -855,9 +855,14 @@ bool TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec,
: dom::PredefinedColorSpace::Srgb;
bool sameColorSpace = (srcColorSpace == dstColorSpace);
const auto reason = BlitPreventReason(
Maybe<std::string> reason;
if (!webgl->IsUploadableSdType(sd)) {
reason = Some(std::string("Unsupported surface descriptor type"));
} else {
reason = BlitPreventReason(
level, {xOffset, yOffset, zOffset}, dui->internalFormat, pi, mDesc,
webgl->mOptionalRenderableFormatBits, sameColorSpace);
}
if (reason) {
webgl->GeneratePerfWarning(
"Failed to hit GPU-copy fast-path."

View file

@ -65,8 +65,8 @@ class TexUnpackBlob {
virtual ~TexUnpackBlob() = default;
protected:
bool ConvertIfNeeded(const WebGLContext*, const uint32_t rowLength,
const uint32_t rowCount, WebGLTexelFormat srcFormat,
bool ConvertIfNeeded(const WebGLContext*, const size_t rowLength,
const size_t rowCount, WebGLTexelFormat srcFormat,
const uint8_t* const srcBegin, const ptrdiff_t srcStride,
WebGLTexelFormat dstFormat, const ptrdiff_t dstStride,

View file

@ -630,50 +630,6 @@ RefPtr<WebGLContext> WebGLContext::Create(HostWebGLContext* host,
// -
const auto UploadableSdTypes = [&]() {
webgl::EnumMask<layers::SurfaceDescriptor::Type> types;
types[layers::SurfaceDescriptor::TSurfaceDescriptorBuffer] = true;
// Only support canvas surface interchange if using AC2D. This guarantees
// that WebGL and AC2D commands are sequenced and processed on the same
// thread, so that there is no mal-ordering between AC2D and WebGL
// processing. We can flush out AC2D commands to produce a surface in time
// for WebGL to use without requiring any blocking to occur.
types[layers::SurfaceDescriptor::TSurfaceDescriptorCanvasSurface] =
gfx::gfxVars::UseAcceleratedCanvas2D();
// This is conditional on not using the Compositor thread because we may
// need to synchronize with the RDD process over the PVideoBridge protocol
// to wait for the texture to be available in the compositor process. We
// cannot block on the Compositor thread, so in that configuration, we would
// prefer to do the readback from the RDD which is guaranteed to work, and
// only block the owning thread for WebGL.
const bool offCompositorThread = gfx::gfxVars::UseCanvasRenderThread() ||
!gfx::gfxVars::SupportsThreadsafeGL();
types[layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo] =
offCompositorThread;
// Similarly to the PVideoBridge protocol, we may need to synchronize with
// the content process over the PCompositorManager protocol to wait for the
// shared surface to be available in the compositor process, and we cannot
// block on the Compositor thread.
types[layers::SurfaceDescriptor::TSurfaceDescriptorExternalImage] =
offCompositorThread;
if (webgl->gl->IsANGLE()) {
types[layers::SurfaceDescriptor::TSurfaceDescriptorD3D10] = true;
types[layers::SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr] = true;
}
if (kIsMacOS) {
types[layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface] = true;
}
if (kIsAndroid) {
types[layers::SurfaceDescriptor::TSurfaceTextureDescriptor] = true;
}
if (kIsLinux) {
types[layers::SurfaceDescriptor::TSurfaceDescriptorDMABuf] = true;
}
return types;
};
// -
constexpr GLenum SHADER_TYPES[] = {
LOCAL_GL_VERTEX_SHADER,
LOCAL_GL_FRAGMENT_SHADER,
@ -709,7 +665,7 @@ RefPtr<WebGLContext> WebGLContext::Create(HostWebGLContext* host,
out->options = webgl->mOptions;
out->limits = *webgl->mLimits;
out->uploadableSdTypes = UploadableSdTypes();
out->uploadableSdTypes = webgl->mUploadableSdTypes;
out->vendor = webgl->gl->Vendor();
out->optionalRenderableFormatBits = webgl->mOptionalRenderableFormatBits;
@ -811,6 +767,58 @@ void WebGLContext::FinishInit() {
gl->ResetSyncCallCount("WebGLContext Initialization");
LoseLruContextIfLimitExceeded();
InitUploadableSdTypes();
}
void WebGLContext::InitUploadableSdTypes() {
webgl::EnumMask<layers::SurfaceDescriptor::Type> types;
types[layers::SurfaceDescriptor::TSurfaceDescriptorBuffer] = true;
// Only support canvas surface interchange if using AC2D. This guarantees
// that WebGL and AC2D commands are sequenced and processed on the same
// thread, so that there is no mal-ordering between AC2D and WebGL
// processing. We can flush out AC2D commands to produce a surface in time
// for WebGL to use without requiring any blocking to occur.
types[layers::SurfaceDescriptor::TSurfaceDescriptorCanvasSurface] =
gfx::gfxVars::UseAcceleratedCanvas2D();
// This is conditional on not using the Compositor thread because we may
// need to synchronize with the RDD process over the PVideoBridge protocol
// to wait for the texture to be available in the compositor process. We
// cannot block on the Compositor thread, so in that configuration, we would
// prefer to do the readback from the RDD which is guaranteed to work, and
// only block the owning thread for WebGL.
const bool offCompositorThread = gfx::gfxVars::UseCanvasRenderThread() ||
!gfx::gfxVars::SupportsThreadsafeGL();
types[layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo] =
offCompositorThread;
// Similarly to the PVideoBridge protocol, we may need to synchronize with
// the content process over the PCompositorManager protocol to wait for the
// shared surface to be available in the compositor process, and we cannot
// block on the Compositor thread.
types[layers::SurfaceDescriptor::TSurfaceDescriptorExternalImage] =
offCompositorThread;
if (gl->IsANGLE()) {
types[layers::SurfaceDescriptor::TSurfaceDescriptorD3D10] = true;
types[layers::SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr] = true;
}
if (kIsMacOS) {
types[layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface] = true;
}
if (kIsAndroid) {
types[layers::SurfaceDescriptor::TSurfaceTextureDescriptor] = true;
}
if (kIsLinux) {
types[layers::SurfaceDescriptor::TSurfaceDescriptorDMABuf] = true;
}
mUploadableSdTypes = types;
}
bool WebGLContext::IsUploadableSdType(
const layers::SurfaceDescriptor& sd) const {
// If the WebGLContext is remote, then validate that the SD is an allowed
// type.
return !bool(mHost) || mUploadableSdTypes[sd.type()];
}
void WebGLContext::SetCompositableHost(
@ -2787,6 +2795,23 @@ webgl::ExplicitPixelPackingState::ForUseWith(
const Maybe<size_t> bytesPerRowStrideOverride) {
auto state = stateOrZero;
// Enforce the GLES alignmentInTypeElems invariant. ElemsPerRowStride below
// assumes a in {1,2,4,8}. Callers at IPC entry points validate this but
// alignmentInTypeElems is deserialized from IPC, so guard it here too.
switch (state.alignmentInTypeElems) {
case 1:
case 2:
case 4:
case 8:
break;
default: {
const auto text = nsPrintfCString(
"PACK/UNPACK_ALIGNMENT must be one of [1,2,4,8], was %u.",
state.alignmentInTypeElems);
return Err(mozilla::ToString(text));
}
}
if (!IsTexTarget3D(target)) {
state.skipImages = 0;
state.imageHeight = 0;
@ -2892,7 +2917,9 @@ webgl::ExplicitPixelPackingState::ForUseWith(
const auto elemsPerRowStride = ElemsPerRowStride();
const auto bytesPerRowStride = pii.bytesPerElement * elemsPerRowStride;
if (!bytesPerRowStride.isValid()) {
const auto maxBytesPerRow = StaticPrefs::webgl_max_bytes_per_row();
if (!bytesPerRowStride.isValid() ||
(maxBytesPerRow > 0 && bytesPerRowStride.value() > maxBytesPerRow)) {
return Err("ROW_LENGTH or width too large for packing.");
}
metrics.bytesPerRowStride = bytesPerRowStride.value();

View file

@ -292,6 +292,7 @@ class WebGLContext : public VRefCounted, public SupportsWeakPtr {
WebGLContextOptions mOptions;
const uint32_t mPrincipalKey;
Maybe<webgl::Limits> mLimits;
webgl::EnumMask<layers::SurfaceDescriptor::Type> mUploadableSdTypes;
const uint32_t mMaxVertIdsPerDraw =
StaticPrefs::webgl_max_vert_ids_per_draw();
@ -340,6 +341,7 @@ class WebGLContext : public VRefCounted, public SupportsWeakPtr {
webgl::OptionalRenderableFormatBits mOptionalRenderableFormatBits =
webgl::OptionalRenderableFormatBits{0};
void FinishInit();
void InitUploadableSdTypes();
protected:
WebGLContext(HostWebGLContext*, const webgl::InitContextDesc&);
@ -995,6 +997,8 @@ class WebGLContext : public VRefCounted, public SupportsWeakPtr {
bool IsFormatValidForFB(TexInternalFormat format) const;
bool IsUploadableSdType(const layers::SurfaceDescriptor& sd) const;
protected:
// -------------------------------------------------------------------------
// WebGL extensions (implemented in WebGLContextExtensions.cpp)

View file

@ -1133,6 +1133,18 @@ webgl::ReadPixelsResult WebGLContext::ReadPixelsImpl(
//////
// Reject invalid pack alignment.
switch (desc.packState.alignmentInTypeElems) {
case 1:
case 2:
case 4:
case 8:
break; // all good
default:
ErrorInvalidValue("pack alignment must be 1, 2, 4, or 8.");
return {};
}
const auto& srcOffset = desc.srcOffset;
const auto& size = desc.size;

View file

@ -487,23 +487,28 @@ bool ShaderValidatorResults::CanLinkTo(const ShaderValidatorResults& vert,
size_t ShaderValidatorResults::SizeOfIncludingThis(
const MallocSizeOf fnSizeOf) const {
auto ret = fnSizeOf(this);
ret += mInfoLog.size();
ret += mObjectCode.size();
for (const auto& cur : mAttributes) {
ret += fnSizeOf(&cur);
// std::string heap allocations are not measured here because:
// 1. Small String Optimization (SSO) means data() may point to inline
// storage within the std::string object (already counted in
// fnSizeOf(this))
// 2. There's no standard way to distinguish SSO from heap-allocated strings
// 3. Calling fnSizeOf on a pointer to inline storage is inappropriate
if (!mAttributes.empty()) {
ret += fnSizeOf(mAttributes.data());
}
for (const auto& cur : mInterfaceBlocks) {
ret += fnSizeOf(&cur);
if (!mInterfaceBlocks.empty()) {
ret += fnSizeOf(mInterfaceBlocks.data());
}
for (const auto& cur : mOutputVariables) {
ret += fnSizeOf(&cur);
if (!mOutputVariables.empty()) {
ret += fnSizeOf(mOutputVariables.data());
}
for (const auto& cur : mUniforms) {
ret += fnSizeOf(&cur);
if (!mUniforms.empty()) {
ret += fnSizeOf(mUniforms.data());
}
for (const auto& cur : mVaryings) {
ret += fnSizeOf(&cur);
if (!mVaryings.empty()) {
ret += fnSizeOf(mVaryings.data());
}
return ret;

View file

@ -55,8 +55,9 @@ bool ClientManagerChild::DeallocPClientNavigateOpChild(
mozilla::ipc::IPCResult ClientManagerChild::RecvPClientNavigateOpConstructor(
PClientNavigateOpChild* aActor,
const ClientNavigateOpConstructorArgs& aArgs) {
auto actor = static_cast<ClientNavigateOpChild*>(aActor);
actor->Init(aArgs);
RefPtr<mozilla::ipc::ActorLifecycleProxy> proxy = aActor->GetLifecycleProxy();
auto* actor = static_cast<ClientNavigateOpChild*>(aActor);
actor->Init(aArgs, proxy);
return IPC_OK();
}

View file

@ -152,7 +152,8 @@ NS_IMPL_ISUPPORTS(NavigateLoadListener, nsIWebProgressListener,
} // anonymous namespace
RefPtr<ClientOpPromise> ClientNavigateOpChild::DoNavigate(
const ClientNavigateOpConstructorArgs& aArgs) {
const ClientNavigateOpConstructorArgs& aArgs,
mozilla::ipc::ActorLifecycleProxy* aProxy) {
nsCOMPtr<nsPIDOMWindowInner> window;
// Navigating the target client window will result in the original
@ -278,6 +279,12 @@ RefPtr<ClientOpPromise> ClientNavigateOpChild::DoNavigate(
return ClientOpPromise::CreateAndReject(result, __func__);
}
if (!aProxy->Get() || !CanSend()) {
CopyableErrorResult result;
result.ThrowInvalidStateError("Unknown Client");
return ClientOpPromise::CreateAndReject(result, __func__);
}
RefPtr<ClientOpPromise::Private> promise =
new ClientOpPromise::Private(__func__);
@ -305,8 +312,12 @@ void ClientNavigateOpChild::ActorDestroy(ActorDestroyReason aReason) {
mPromiseRequestHolder.DisconnectIfExists();
}
void ClientNavigateOpChild::Init(const ClientNavigateOpConstructorArgs& aArgs) {
RefPtr<ClientOpPromise> promise = DoNavigate(aArgs);
void ClientNavigateOpChild::Init(const ClientNavigateOpConstructorArgs& aArgs,
mozilla::ipc::ActorLifecycleProxy* aProxy) {
RefPtr<ClientOpPromise> promise = DoNavigate(aArgs, aProxy);
if (!aProxy->Get() || !CanSend()) {
return;
}
// Normally we get the event target from the window in DoNavigate(). If a
// failure occurred, though, we may need to fall back to the current thread

View file

@ -16,7 +16,8 @@ class ClientNavigateOpChild final : public PClientNavigateOpChild {
nsCOMPtr<nsISerialEventTarget> mSerialEventTarget;
[[nodiscard]] RefPtr<ClientOpPromise> DoNavigate(
const ClientNavigateOpConstructorArgs& aArgs);
const ClientNavigateOpConstructorArgs& aArgs,
mozilla::ipc::ActorLifecycleProxy* aProxy);
// PClientNavigateOpChild interface
void ActorDestroy(ActorDestroyReason aReason) override;
@ -25,7 +26,8 @@ class ClientNavigateOpChild final : public PClientNavigateOpChild {
ClientNavigateOpChild() = default;
~ClientNavigateOpChild() = default;
void Init(const ClientNavigateOpConstructorArgs& aArgs);
void Init(const ClientNavigateOpConstructorArgs& aArgs,
mozilla::ipc::ActorLifecycleProxy* aProxy);
};
} // namespace mozilla::dom

View file

@ -520,11 +520,12 @@ RefPtr<ClientOpPromise> ClientSource::Focus(const ClientFocusArgs& aArgs) {
return ClientOpPromise::CreateAndReject(rv, __func__);
}
nsCOMPtr<nsPIDOMWindowOuter> outer;
nsPIDOMWindowInner* inner = GetInnerWindow();
nsCOMPtr<nsPIDOMWindowInner> inner = GetInnerWindow();
nsIDocShell* docshell = nullptr;
if (inner) {
outer = inner->GetOuterWindow();
} else {
nsIDocShell* docshell = GetDocShell();
docshell = GetDocShell();
if (docshell) {
outer = docshell->GetWindow();
}
@ -537,9 +538,48 @@ RefPtr<ClientOpPromise> ClientSource::Focus(const ClientFocusArgs& aArgs) {
}
MOZ_ASSERT(NS_IsMainThread());
// Inlined from `ClientSource::SnapshotWindowState()`:
// Should not be necessary after bug 543435. Clean this up in bug 2025284.
if (docshell) {
// Force the creation of the initial document if it does not yet exist.
if (!docshell->GetDocument()) {
CopyableErrorResult rv;
rv.ThrowInvalidStateError("No document available.");
return ClientOpPromise::CreateAndReject(rv, __func__);
}
inner = GetInnerWindow();
}
nsFocusManager::FocusWindow(outer, aArgs.callerType());
Result<ClientState, ErrorResult> state = SnapshotState();
Result<ClientState, ErrorResult> state =
[&]() -> Result<ClientState, ErrorResult> {
if (!inner) {
// Inlined from `ClientSource::SnapshotWindowState()`:
return ClientState(ClientWindowState(VisibilityState::Hidden, TimeStamp(),
StorageAccess::eDeny, false));
}
if (inner->GetClientSource() == this) {
// The pointer comparison assumes that an inner window
// cannot gain a new ClientSource other than this same
// `ClientSource` having moved from a docshell owner to an
// inner window owner gained via `outer`, so we don't need to worry
// about a newly-allocated ClientSource occupying the same
// memory as the one pointed to by `this`. That is, in the case
// of the pointers being unequal, `inner->GetClientSource()`
// returns `nullptr` and `this` is an invalid pointer.
// Per [expr.eq], it's not UB to compare a pointer to a deleted
// object, since no pointer comparisons are UB anymore. The
// case about pointer-past-end for a different object being
// _unspecified_ behavior does not apply here.
return SnapshotState();
}
ErrorResult rv;
rv.ThrowInvalidStateError("Client destroyed during focus");
return Err(std::move(rv));
}();
if (state.isErr()) {
return ClientOpPromise::CreateAndReject(
CopyableErrorResult(state.unwrapErr()), __func__);

View file

@ -424,6 +424,7 @@ class ConsoleRunnable : public StructuredCloneHolderBase {
}
Sequence<JS::Value> arguments;
SequenceRooter<JS::Value> rooter(aCx, &arguments);
for (uint32_t i = 0; i < length; ++i) {
JS::Rooted<JS::Value> value(aCx);

View file

@ -196,10 +196,17 @@ void CookieStoreNotifier::DispatchEvent(const CookieListItem& aItem,
void CookieStoreNotifier::FireDelayedDOMEvents() {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<CookieStoreNotifier> kungFuDeathGrip(this);
nsTArray<RefPtr<Event>> delayedDOMEvents;
delayedDOMEvents.SwapElements(mDelayedDOMEvents);
for (Event* event : delayedDOMEvents) {
// mCookieStore is a raw pointer cleared by Disentangle().
if (!mCookieStore) {
break;
}
mCookieStore->DispatchEvent(*event);
}
}

View file

@ -2134,9 +2134,13 @@ const TypedEventHandler* EventListenerManager::GetTypedEventHandler(
}
JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
Maybe<RefPtr<JSEventHandler>> pin;
if (listener->mHandlerIsString) {
CompileEventHandlerInternal(listener, aEventName, nullptr, nullptr);
pin.emplace(jsEventHandler);
if (NS_FAILED(CompileEventHandlerInternal(listener, aEventName, nullptr,
nullptr))) {
listener = nullptr;
}
}
const TypedEventHandler& typedHandler =

View file

@ -788,7 +788,8 @@ nsresult IMEContentObserver::MaybeHandleSelectionEvent(
bool IMEContentObserver::OnMouseButtonEvent(nsPresContext& aPresContext,
WidgetMouseEvent& aMouseEvent) {
if (!mIMENotificationRequests ||
!mIMENotificationRequests->WantMouseButtonEventOnChar()) {
!mIMENotificationRequests->contains(
IMENotificationRequest::MouseEventOnChar)) {
return false;
}
if (!aMouseEvent.IsTrusted() || aMouseEvent.DefaultPrevented() ||

View file

@ -47,6 +47,7 @@ class IMEContentObserver final : public nsStubMutationObserver,
using SelectionChangeData = widget::IMENotification::SelectionChangeData;
using TextChangeData = widget::IMENotification::TextChangeData;
using TextChangeDataBase = widget::IMENotification::TextChangeDataBase;
using IMENotificationRequest = widget::IMENotificationRequest;
using IMENotificationRequests = widget::IMENotificationRequests;
using IMEMessage = widget::IMEMessage;
enum class ForRemoval : bool { No, Yes };
@ -163,7 +164,8 @@ class IMEContentObserver final : public nsStubMutationObserver,
bool IsEditorHandlingEventForComposition() const;
bool KeepAliveDuringDeactive() const {
return mIMENotificationRequests &&
mIMENotificationRequests->WantDuringDeactive();
mIMENotificationRequests->contains(
IMENotificationRequest::NotifyDuringInactive);
}
[[nodiscard]] bool EditorIsTextEditor() const {
return mEditorBase && mEditorBase->IsTextEditor();
@ -328,12 +330,13 @@ class IMEContentObserver final : public nsStubMutationObserver,
void UnregisterObservers();
void FlushMergeableNotifications();
bool NeedsTextChangeNotification() const {
return mIMENotificationRequests &&
mIMENotificationRequests->WantTextChange();
return mIMENotificationRequests && mIMENotificationRequests->contains(
IMENotificationRequest::TextChange);
}
bool NeedsPositionChangeNotification() const {
return mIMENotificationRequests &&
mIMENotificationRequests->WantPositionChanged();
mIMENotificationRequests->contains(
IMENotificationRequest::PositionChange);
}
void ClearPendingNotifications() {
mNeedsToNotifyIMEOfFocusSet = false;

View file

@ -198,13 +198,12 @@ void IMEStateManager::OnFocusMovedBetweenBrowsers(BrowserParent* aBlur,
RefPtr<TextComposition> composition =
sTextCompositions->GetCompositionFor(oldWidget);
if (composition) {
MOZ_LOG(
sISMLog, LogLevel::Debug,
(" OnFocusMovedBetweenBrowsers(), requesting to commit "
"composition to "
"the (previous) focused widget (would request=%s)",
GetBoolName(
!oldWidget->IMENotificationRequestsRef().WantDuringDeactive())));
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnFocusMovedBetweenBrowsers(), requesting to commit "
"composition to "
"the (previous) focused widget (would request=%s)",
GetBoolName(!oldWidget->IMENotificationRequestsRef().contains(
IMENotificationRequest::NotifyDuringInactive))));
NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
composition->GetBrowserParent());
}
@ -727,8 +726,8 @@ nsresult IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
// such case, sFocusedIMEWidget is perhaps nullptr). For example, IME
// may receive only blur notification but still has composition.
// We need to clean up only the oldWidget's composition state here.
if (aPresContext ||
!oldWidget->IMENotificationRequestsRef().WantDuringDeactive()) {
if (aPresContext || !oldWidget->IMENotificationRequestsRef().contains(
IMENotificationRequest::NotifyDuringInactive)) {
MOZ_LOG(
sISMLog, LogLevel::Info,
(" OnChangeFocusInternal(), requesting to commit composition to "

View file

@ -66,21 +66,17 @@ namespace {
// https://fetch.spec.whatwg.org/#concept-http-network-fetch
// If stream is readable, then error stream with ...
void AbortStream(JSContext* aCx, ReadableStream* aReadableStream,
ErrorResult& aRv, JS::Handle<JS::Value> aReasonDetails) {
AbortSignalImpl* aSignal, ErrorResult& aRv) {
MOZ_ASSERT(aSignal->Aborted());
if (aReadableStream->State() != ReadableStream::ReaderState::Readable) {
return;
}
JS::Rooted<JS::Value> value(aCx, aReasonDetails);
JS::Rooted<JS::Value> reason(aCx);
aSignal->GetReason(aCx, &reason);
if (aReasonDetails.isUndefined()) {
RefPtr<DOMException> e = DOMException::Create(NS_ERROR_DOM_ABORT_ERR);
if (!GetOrCreateDOMReflector(aCx, e, &value)) {
return;
}
}
aReadableStream->ErrorNative(aCx, value, aRv);
aReadableStream->ErrorNative(aCx, reason, aRv);
}
} // namespace
@ -533,12 +529,8 @@ already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
if (signalImpl && signalImpl->Aborted()) {
// Already aborted signal rejects immediately.
JS::Rooted<JS::Value> reason(cx, signalImpl->RawReason());
if (reason.get().isUndefined()) {
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
return nullptr;
}
JS::Rooted<JS::Value> reason(cx);
signalImpl->GetReason(cx, &reason);
p->MaybeReject(reason);
return p.forget();
}
@ -1422,16 +1414,15 @@ already_AddRefed<Promise> FetchBody<Derived>::ConsumeBody(
DerivedClass()->GetSignalImplToConsumeBody();
if (signalImpl && signalImpl->Aborted()) {
JS::Rooted<JS::Value> abortReason(aCx, signalImpl->RawReason());
JS::Rooted<JS::Value> abortReason(aCx);
signalImpl->GetReason(aCx, &abortReason);
if (abortReason.get().isUndefined()) {
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
nsCOMPtr<nsIGlobalObject> global = DerivedClass()->GetParentObject();
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
nsCOMPtr<nsIGlobalObject> go = DerivedClass()->GetParentObject();
RefPtr<Promise> promise = Promise::Create(go, aRv);
promise->MaybeReject(abortReason);
return promise.forget();
}
@ -1572,11 +1563,9 @@ void FetchBody<Derived>::SetReadableStreamBody(JSContext* aCx,
return;
}
bool aborted = signalImpl->Aborted();
if (aborted) {
if (signalImpl->Aborted()) {
IgnoredErrorResult result;
JS::Rooted<JS::Value> abortReason(aCx, signalImpl->RawReason());
AbortStream(aCx, mReadableStreamBody, result, abortReason);
AbortStream(aCx, mReadableStreamBody, signalImpl, result);
if (NS_WARN_IF(result.Failed())) {
return;
}
@ -1632,8 +1621,7 @@ already_AddRefed<ReadableStream> FetchBody<Derived>::GetBody(JSContext* aCx,
RefPtr<AbortSignalImpl> signalImpl = DerivedClass()->GetSignalImpl();
if (signalImpl) {
if (signalImpl->Aborted()) {
JS::Rooted<JS::Value> abortReason(aCx, signalImpl->RawReason());
AbortStream(aCx, body, aRv, abortReason);
AbortStream(aCx, body, signalImpl, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
@ -1733,16 +1721,7 @@ void FetchBody<Derived>::RunAbortAlgorithm() {
JSContext* cx = jsapi.cx();
RefPtr<ReadableStream> body(mReadableStreamBody);
IgnoredErrorResult result;
JS::Rooted<JS::Value> abortReason(cx);
AbortSignalImpl* signalImpl = Signal();
if (signalImpl) {
abortReason.set(signalImpl->RawReason());
}
AbortStream(cx, body, result, abortReason);
AbortStream(cx, body, Signal(), IgnoredErrorResult());
}
template void FetchBody<Request>::RunAbortAlgorithm();

View file

@ -89,6 +89,9 @@ IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
FETCH_LOG(("FetchParent::RecvFetchOp [%p]", this));
AssertIsOnBackgroundThread();
if (mReceivedFetchOp.exchange(true)) {
return IPC_FAIL(this, "FetchOp received more than once on this actor");
}
MOZ_ASSERT(!mIsDone);
if (mActorDestroyed) {
return IPC_OK();

View file

@ -103,6 +103,7 @@ class FetchParent final : public PFetchParent {
Atomic<bool> mIsDone{false};
Atomic<bool> mActorDestroyed{false};
Atomic<bool> mReceivedFetchOp{false};
nsCOMPtr<nsISerialEventTarget> mBackgroundEventTarget;
};

View file

@ -123,6 +123,19 @@ template <typename T>
InternalResponse::~InternalResponse() = default;
void InternalResponse::SnapshotUnfilteredHeaders() {
auto snapshot = [](InternalHeaders* aHeaders) {
nsTArray<InternalHeaders::Entry> entries;
aHeaders->GetEntries(entries);
return MakeRefPtr<InternalHeaders>(std::move(entries), aHeaders->Guard());
};
if (mWrappedResponse) {
mWrappedResponse->mHeaders = snapshot(mWrappedResponse->mHeaders);
} else {
mHeaders = snapshot(mHeaders);
}
}
InternalResponseMetadata InternalResponse::GetMetadata() {
nsTArray<HeadersEntry> headers;
HeadersGuardEnum headersGuard;

Some files were not shown because too many files have changed in this diff Show more