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" rev = "e6f44a2bd1e57d11dfc737632a9e849077632330"
replace-with = "vendored-sources" 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" git = "https://github.com/mozilla/cubeb-coreaudio-rs"
rev = "579b75af21c040700eee6a1d8520e222699fe4cd" rev = "bebaa23317332c95734df76e25193c24a83a6840"
replace-with = "vendored-sources" replace-with = "vendored-sources"
[source."git+https://github.com/mozilla/cubeb-pulse-rs?rev=8678dcab1c287de79c4c184ccc2e065bc62b70e2"] [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 # Ignore mozperftest artifacts folder
/artifacts/ /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 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more. # 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]] [[package]]
name = "coreaudio-sys-utils" name = "coreaudio-sys-utils"
version = "0.1.0" 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 = [ dependencies = [
"core-foundation-sys", "core-foundation-sys",
"coreaudio-sys", "coreaudio-sys",
@ -1398,7 +1398,7 @@ dependencies = [
[[package]] [[package]]
name = "cubeb-coreaudio" name = "cubeb-coreaudio"
version = "0.1.0" 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 = [ dependencies = [
"atomic", "atomic",
"audio-mixer", "audio-mixer",

View file

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

View file

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

View file

@ -71,10 +71,14 @@ void CachedTableAccessible::Invalidate(Accessible* aAcc) {
return; 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 // 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); 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; return startFrameSel ? &startFrameSel->NormalSelection() : nullptr;
} }
std::pair<nsIContent*, uint32_t> TextLeafPoint::ToDOMPoint( std::pair<RefPtr<nsIContent>, uint32_t> TextLeafPoint::ToDOMPoint(
bool aIncludeGenerated) const { bool aIncludeGenerated) const {
if (!(*this) || !mAcc->IsLocal()) { if (!(*this) || !mAcc->IsLocal()) {
MOZ_ASSERT_UNREACHABLE("Invalid point"); MOZ_ASSERT_UNREACHABLE("Invalid point");

View file

@ -188,7 +188,7 @@ class TextLeafPoint final {
/** /**
* Translate given TextLeafPoint into a DOM point. * 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; bool aIncludeGenerated = true) const;
private: private:

View file

@ -859,16 +859,15 @@ void HyperTextAccessible::ReplaceText(const nsAString& aText) {
return; return;
} }
RefPtr<EditorBase> editorBase = GetEditor();
SetSelectionBoundsAt(TextLeafRange::kRemoveAllExistingSelectedRanges, 0, SetSelectionBoundsAt(TextLeafRange::kRemoveAllExistingSelectedRanges, 0,
CharacterCount()); CharacterCount());
RefPtr<EditorBase> editorBase = GetEditor(); if (editorBase) {
if (!editorBase) { DebugOnly<nsresult> rv = editorBase->InsertTextAsAction(aText);
return; 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, void HyperTextAccessible::InsertText(const nsAString& aText,

View file

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

View file

@ -16,25 +16,9 @@ function getSiteBlockedErrorDetails(docShell) {
Ci.nsIClassifiedChannel Ci.nsIClassifiedChannel
); );
if (classifiedChannel) { 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 = { blockedInfo = {
list: classifiedChannel.matchedList, list: classifiedChannel.matchedList,
triggeringPrincipal,
provider: classifiedChannel.matchedProvider, 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. // 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 // We can't use browser.contentPrincipal which is principal of about:blocked
// Create one from uri with current principal origin attributes // 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( let principal = Services.scriptSecurityManager.createContentPrincipal(
Services.io.newURI(blockedInfo.uri), uri,
browsingContext.currentWindowGlobal.documentPrincipal.originAttributes browsingContext.currentWindowGlobal.documentPrincipal.originAttributes
); );
Services.perms.addFromPrincipal( Services.perms.addFromPrincipal(
@ -206,10 +209,10 @@ export class BlockedSiteParent extends EscapablePageParent {
let title; let title;
let chromeWin = browsingContext.topChromeWindow; let chromeWin = browsingContext.topChromeWindow;
if (reason === "malware") { if (reason === "malware") {
let reportUrl = lazy.SafeBrowsing.getReportURL( let reportUrl = lazy.SafeBrowsing.getReportURL("MalwareMistake", {
"MalwareMistake", ...blockedInfo,
blockedInfo uri: uri.asciiSpec,
); });
title = lazy.browserBundle.GetStringFromName( title = lazy.browserBundle.GetStringFromName(
"safebrowsing.reportedAttackSite" "safebrowsing.reportedAttackSite"
); );
@ -233,10 +236,10 @@ export class BlockedSiteParent extends EscapablePageParent {
}; };
} }
} else if (reason === "phishing") { } else if (reason === "phishing") {
let reportUrl = lazy.SafeBrowsing.getReportURL( let reportUrl = lazy.SafeBrowsing.getReportURL("PhishMistake", {
"PhishMistake", ...blockedInfo,
blockedInfo uri: uri.asciiSpec,
); });
title = lazy.browserBundle.GetStringFromName( title = lazy.browserBundle.GetStringFromName(
"safebrowsing.deceptiveSite" "safebrowsing.deceptiveSite"
); );
@ -281,16 +284,15 @@ export class BlockedSiteParent extends EscapablePageParent {
buttons buttons
); );
// Allow users to override and continue through to the site. let activeSHEntry = browsingContext.activeSessionHistoryEntry;
// Note that we have to use the passed URI info and can't just if (!activeSHEntry) {
// rely on the document URI, because the latter contains console.error("No active session history entry found");
// additional query parameters that should be stripped. return;
let triggeringPrincipal = }
blockedInfo.triggeringPrincipal ||
Services.scriptSecurityManager.createNullPrincipal({});
browsingContext.fixupAndLoadURIString(blockedInfo.uri, { // Allow users to override and continue through to the site.
triggeringPrincipal, browsingContext.loadURI(uri, {
triggeringPrincipal: activeSHEntry.triggeringPrincipal,
loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER, loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
}); });
} }

View file

@ -142,9 +142,7 @@ export class DOMFullscreenParent extends JSWindowActorParent {
case "DOMFullscreen:NewOrigin": { case "DOMFullscreen:NewOrigin": {
// Don't show the warning if we've already exited fullscreen. // Don't show the warning if we've already exited fullscreen.
if (window.document.fullscreen) { if (window.document.fullscreen) {
window.PointerlockFsWarning.showFullScreen( window.PointerlockFsWarning.showFullScreen(topBrowsingContext);
aMessage.data.originNoSuffix
);
} }
this.updateFullscreenWindowReference(window); this.updateFullscreenWindowReference(window);
break; break;
@ -222,7 +220,7 @@ export class DOMFullscreenParent extends JSWindowActorParent {
if (!this.hasBeenDestroyed() && this.requestOrigin) { if (!this.hasBeenDestroyed() && this.requestOrigin) {
window.PointerlockFsWarning.showFullScreen( window.PointerlockFsWarning.showFullScreen(
this.requestOrigin.manager.documentPrincipal.originNoSuffix this.requestOrigin.browsingContext
); );
} }
break; 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 timeout = Services.prefs.getIntPref("full-screen-api.warning.timeout");
let delay = Services.prefs.getIntPref("full-screen-api.warning.delay"); 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 // 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 "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"] ["browser_fullscreen_warning.js"]
support-files = ["fullscreen.html"] support-files = ["fullscreen.html"]
skip-if = [ 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. // JSActors, but that would import a lot of code for a targeting attribute.
return 0; return 0;
} }
let records = await actor?.receiveMessage({ let records = await actor?.getRecords(data);
name: "FormAutofill:GetRecords", return records?.length ?? 0;
data,
});
return records?.records?.length ?? 0;
} }
// Attribution data can be encoded multiple times so we need this function to // 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( gBrowser.selectedBrowser.browsingContext.currentWindowGlobal.getActor(
"FormAutofill" "FormAutofill"
), ),
"receiveMessage" "getRecords"
) )
.withArgs( .withArgs(sandbox.match({ collectionName: "creditCards" }))
sandbox.match({ .resolves([creditcard])
name: "FormAutofill:GetRecords",
data: { collectionName: "creditCards" },
})
)
.resolves({ records: [creditcard] })
.callThrough(); .callThrough();
is( is(
@ -1435,8 +1430,8 @@ add_task(async function test_creditCardsSaved() {
"Should return 1 when 1 credit card is saved" "Should return 1 when 1 credit card is saved"
); );
ok( ok(
stub.calledWithMatch({ name: "FormAutofill:GetRecords" }), stub.calledWithMatch({ collectionName: "creditCards" }),
"Targeting called FormAutofill:GetRecords" "Targeting called getRecords"
); );
sandbox.restore(); 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 MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/branding/official
# IceCat settings # IceCat settings
MOZ_APP_BASENAME=IceCat MOZ_APP_BASENAME=IceCat
MOZ_APP_VERSION=140.9.0 MOZ_APP_VERSION=140.10.1
MOZ_DATA_REPORTING=0 MOZ_DATA_REPORTING=0

View file

@ -10,4 +10,4 @@
# hardcoded milestones in the tree from these two files. # 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 icecat (140.9.0-1gnu1+build1-0.12.0) ecne; urgency=medium
* New upstream stable release (icecat-140.9.0-1gnu1) * 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"] ["browser_aboutdebugging_serviceworker_start.js"]
skip-if = [ 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 "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"] ["browser_application_panel_start-service-worker.js"]
fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
skip-if = [ skip-if = [
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && tsan", # Bug 1608640 "os == 'linux' && processor == 'x86_64' && tsan", # Bug 1608640, Bug 2030884
"os == 'linux' && os_version == '24.04' && processor == 'x86_64' && display == 'x11' && tsan", # Bug 1608640 "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"] ["browser_application_panel_target-switching.js"]

View file

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

View file

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

View file

@ -115,6 +115,9 @@ skip-if = [
["browser_dbg-call-stack.js"] ["browser_dbg-call-stack.js"]
["browser_dbg-chrome-create.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"] ["browser_dbg-console-async.js"]
@ -286,6 +289,7 @@ skip-if = [
["browser_dbg-javascript-tracer-values-preview.js"] ["browser_dbg-javascript-tracer-values-preview.js"]
skip-if = [ 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' && 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 "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"); pressKey(dbg, "Escape");
assertQuickOpenDisabled(dbg); 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"); info("Testing goto line:column");
assertLine(dbg, 0); assertLine(dbg, 0);
assertColumn(dbg, 1); assertColumn(dbg, 1);
@ -170,3 +182,19 @@ async function assertResultIsTab(dbg, index) {
"Result should be a tab" "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_commands_factory.js"]
["browser_tab_descriptor_fission.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"] ["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_watchedByDevTools.js"]
["browser_toolbox_window_global_debugging.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"] ["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"] ["browser_animation_logic_mutations_fast.js"]
skip-if = [ skip-if = [
"os == 'linux' && os_version == '18.04' && processor == 'x86_64' && debug", # Bug 1567800 "os == 'linux' && processor == 'x86_64' && asan", # Bug 1980142, Bug 2030884
"os == 'linux' && os_version == '24.04' && processor == 'x86_64' && display == 'x11' && debug", # Bug 1567800 "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"] ["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 https_first_disabled = true # JS HttpServer doesn't support https
skip-if = [ skip-if = [
"http3", # JS HttpServer doesn't support http3 "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 == '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"] ["browser_webconsole_object_inspector_getters.js"]

View file

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

View file

@ -1937,6 +1937,19 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(BrowsingContext)
return IsCertainlyAliveForCC(tmp); return IsCertainlyAliveForCC(tmp);
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END 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 class RemoteLocationProxy
: public RemoteObjectProxy<BrowsingContext::LocationProxy, : public RemoteObjectProxy<BrowsingContext::LocationProxy,
Location_Binding::sCrossOriginProperties> { Location_Binding::sCrossOriginProperties> {

View file

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

View file

@ -38,6 +38,11 @@ void AbortSignalImpl::GetReason(JSContext* aCx,
} }
MaybeAssignAbortError(aCx); MaybeAssignAbortError(aCx);
aReason.set(mReason); 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(); } 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 // https://dom.spec.whatwg.org/#abortsignal-remove could be invoked in an
// earlier algorithm to remove a later algorithm, so |mFollowers| must be a // earlier algorithm to remove a later algorithm, so |mFollowers| must be a
// |nsTObserverArray| to defend against mutation. // |nsTObserverArray| to defend against mutation.
for (RefPtr<AbortFollower>& follower : mFollowers.ForwardRange()) { for (RefPtr<AbortFollower> follower : mFollowers.ForwardRange()) {
MOZ_ASSERT(follower->mFollowingSignal == this); MOZ_ASSERT(follower->mFollowingSignal == this);
follower->RunAbortAlgorithm(); follower->RunAbortAlgorithm();
} }

View file

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

View file

@ -466,6 +466,7 @@ class Animation : public DOMEventTargetHelper,
friend class AsyncFinishNotification; friend class AsyncFinishNotification;
void DoFinishNotificationImmediately(MicroTaskRunnable* aAsync = nullptr); void DoFinishNotificationImmediately(MicroTaskRunnable* aAsync = nullptr);
void QueuePlaybackEvent(nsAtom* aOnEvent, TimeStamp&& aScheduledEventTime); void QueuePlaybackEvent(nsAtom* aOnEvent, TimeStamp&& aScheduledEventTime);
void MaybeResolvePromiseWithThis(Promise*);
/** /**
* Remove this animation from the pending animation tracker and reset * 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 = mozilla::DebugOnly<nsresult> rv =
element->UnsetAttr(attr->NodeInfo()->NamespaceID(), element->UnsetAttr(attr->NodeInfo()->NamespaceID(),
attr->NodeInfo()->NameAtom(), false); attr->NodeInfo()->NameAtom(), true);
// XXX Should we abort here? // XXX Should we abort here?
NS_ASSERTION(NS_SUCCEEDED(rv), "Uh-oh, UnsetAttr shouldn't fail!"); 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); BlastSubtreeToPieces(shadow);
element->UnattachShadow(); element->UnattachShadow();
} }
} }
while (aNode->HasChildren()) { 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); BlastSubtreeToPieces(node);
aNode->RemoveChildNode(node, false); aNode->RemoveChildNode(node, true);
} }
} }
@ -16275,11 +16277,16 @@ void Document::RequestFullscreenInParentProcess(
/* static */ /* static */
bool Document::HandlePendingFullscreenRequests(Document* aDoc) { 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; bool handled = false;
PendingFullscreenChangeList::Iterator<FullscreenRequest> iter( for (UniquePtr<FullscreenRequest>& request : requests) {
aDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
while (!iter.AtEnd()) {
UniquePtr<FullscreenRequest> request = iter.TakeAndNext();
Document* doc = request->Document(); Document* doc = request->Document();
if (doc->ApplyFullscreen(std::move(request))) { if (doc->ApplyFullscreen(std::move(request))) {
handled = true; handled = true;

View file

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

View file

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

View file

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

View file

@ -70,15 +70,24 @@ ScreenOrientation::ScreenOrientation(nsPIDOMWindowInner* aWindow,
: DOMEventTargetHelper(aWindow), mScreen(aScreen) { : DOMEventTargetHelper(aWindow), mScreen(aScreen) {
MOZ_ASSERT(aWindow); MOZ_ASSERT(aWindow);
MOZ_ASSERT(aScreen); MOZ_ASSERT(aScreen);
}
mAngle = aScreen->GetOrientationAngle(); /* static */ already_AddRefed<ScreenOrientation> ScreenOrientation::Create(
mType = InternalOrientationToType(aScreen->GetOrientationType()); 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; BrowsingContext* bc = doc ? doc->GetBrowsingContext() : nullptr;
if (bc && !bc->IsDiscarded() && !bc->InRDMPane()) { 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() { ScreenOrientation::~ScreenOrientation() {

View file

@ -33,8 +33,13 @@ class ScreenOrientation final : public DOMEventTargetHelper {
// Called when the orientation may have changed. // Called when the orientation may have changed.
void MaybeChanged(); void MaybeChanged();
private:
ScreenOrientation(nsPIDOMWindowInner* aWindow, nsScreen* aScreen); ScreenOrientation(nsPIDOMWindowInner* aWindow, nsScreen* aScreen);
public:
static already_AddRefed<ScreenOrientation> Create(nsPIDOMWindowInner* aWindow,
nsScreen* aScreen);
already_AddRefed<Promise> Lock(OrientationLockType aOrientation, already_AddRefed<Promise> Lock(OrientationLockType aOrientation,
ErrorResult& aRv); ErrorResult& aRv);
@ -107,8 +112,8 @@ class ScreenOrientation final : public DOMEventTargetHelper {
RefPtr<nsScreen> mScreen; RefPtr<nsScreen> mScreen;
RefPtr<FullscreenEventListener> mFullscreenListener; RefPtr<FullscreenEventListener> mFullscreenListener;
RefPtr<VisibleEventListener> mVisibleListener; RefPtr<VisibleEventListener> mVisibleListener;
OrientationType mType; OrientationType mType{};
uint16_t mAngle; uint16_t mAngle{};
// Whether we've tried to call into hal to lock the device orientation. This // Whether we've tried to call into hal to lock the device orientation. This
// is needed because you don't want calling UnlockDeviceOrientation() during // is needed because you don't want calling UnlockDeviceOrientation() during
// shutdown to initialize PHal if it hasn't been initialized earlier. Also, // 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) { void ShadowRoot::InvalidateStyleAndLayoutOnSubtree(Element* aElement) {
MOZ_ASSERT(aElement); MOZ_ASSERT(aElement);
Document* doc = GetComposedDoc(); Document* doc = aElement->GetComposedDoc();
if (!doc) { if (!doc) {
return; 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 * 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. * around, so that layout knows the actual tree that it needs to invalidate.
*/ */
void InvalidateStyleAndLayoutOnSubtree(Element*); static void InvalidateStyleAndLayoutOnSubtree(Element*);
private: private:
void InsertSheetIntoAuthorData(size_t aIndex, StyleSheet&, void InsertSheetIntoAuthorData(size_t aIndex, StyleSheet&,

View file

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

View file

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

View file

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

View file

@ -1333,7 +1333,11 @@ void nsAttrValue::ParseAtom(const nsAString& aValue) {
void nsAttrValue::ParseAtomArray(nsAtom* aValue) { void nsAttrValue::ParseAtomArray(nsAtom* aValue) {
if (MiscContainer* cont = AtomArrayCache::Lookup(aValue)) { if (MiscContainer* cont = AtomArrayCache::Lookup(aValue)) {
// Set our MiscContainer to the cached one. // 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); NS_ADDREF(cont);
ResetIfSet();
SetPtrValueAndType(cont, eOtherBase); SetPtrValueAndType(cont, eOtherBase);
return; return;
} }
@ -1933,7 +1937,11 @@ bool nsAttrValue::ParseStyleAttribute(const nsAString& aString,
if (cachingAllowed) { if (cachingAllowed) {
if (MiscContainer* cont = attrStyles->LookupStyleAttr(aString)) { if (MiscContainer* cont = attrStyles->LookupStyleAttr(aString)) {
// Set our MiscContainer to the cached one. // 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); NS_ADDREF(cont);
ResetIfSet();
SetPtrValueAndType(cont, eOtherBase); SetPtrValueAndType(cont, eOtherBase);
return true; return true;
} }

View file

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

View file

@ -296,13 +296,21 @@ class nsContentList : public nsBaseContentList,
aFound = !!item; aFound = !!item;
return item; return item;
} }
void GetSupportedNames(nsTArray<nsString>& aNames) override; void GetSupportedNames(nsTArray<nsString>& aNames) override {
GetSupportedNames(aNames, nullptr);
}
// nsContentList public methods // nsContentList public methods
uint32_t Length(bool aDoFlush); uint32_t Length(bool aDoFlush);
nsIContent* Item(uint32_t aIndex, bool aDoFlush); nsIContent* Item(uint32_t aIndex, bool aDoFlush);
Element* NamedItem(const nsAString& aName, 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 // nsIMutationObserver
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED

View file

@ -4533,10 +4533,9 @@ nsDOMWindowUtils::WrCapture() {
} }
NS_IMETHODIMP NS_IMETHODIMP
nsDOMWindowUtils::WrStartCaptureSequence(const nsACString& aPath, nsDOMWindowUtils::WrStartCaptureSequence(uint32_t aFlags) {
uint32_t aFlags) {
if (WebRenderBridgeChild* wrbc = GetWebRenderBridge()) { if (WebRenderBridgeChild* wrbc = GetWebRenderBridge()) {
wrbc->StartCaptureSequence(nsCString(aPath), aFlags); wrbc->StartCaptureSequence(aFlags);
} }
return NS_OK; 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 // 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 // we recreate the frame loader, this is not what we want, so add our own
// blocker until the process is complete. // blocker until the process is complete.
Document* doc = owner->OwnerDoc(); RefPtr<Document> doc = owner->OwnerDoc();
doc->BlockOnload(); doc->BlockOnload();
auto cleanup = MakeScopeExit([&]() { doc->UnblockOnload(false); }); auto cleanup = MakeScopeExit([&]() { doc->UnblockOnload(false); });

View file

@ -2459,7 +2459,7 @@ VisualViewport* nsGlobalWindowInner::VisualViewport() {
nsScreen* nsGlobalWindowInner::Screen() { nsScreen* nsGlobalWindowInner::Screen() {
if (!mScreen) { if (!mScreen) {
mScreen = new nsScreen(this); mScreen = nsScreen::Create(this);
} }
return mScreen; return mScreen;
} }
@ -2535,6 +2535,10 @@ Maybe<ServiceWorkerDescriptor> nsPIDOMWindowInner::GetController() const {
return nsGlobalWindowInner::Cast(this)->GetController(); return nsGlobalWindowInner::Cast(this)->GetController();
} }
ClientSource* nsPIDOMWindowInner::GetClientSource() const {
return nsGlobalWindowInner::Cast(this)->GetClientSource();
}
void nsPIDOMWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) { void nsPIDOMWindowInner::SetCsp(nsIContentSecurityPolicy* aCsp) {
return nsGlobalWindowInner::Cast(this)->SetCsp(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); void SetCurrentPasteDataTransfer(mozilla::dom::DataTransfer* aDataTransfer);
mozilla::dom::DataTransfer* GetCurrentPasteDataTransfer() const; mozilla::dom::DataTransfer* GetCurrentPasteDataTransfer() const;
mozilla::dom::ClientSource* GetClientSource() const {
return mClientSource.get();
}
private: private:
RefPtr<mozilla::dom::ContentMediaController> mContentMediaController; RefPtr<mozilla::dom::ContentMediaController> mContentMediaController;

View file

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

View file

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

View file

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

View file

@ -2829,20 +2829,6 @@ static nsTextFrame* GetTextFrameForContent(nsIContent* aContent,
return nullptr; 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(); nsIFrame* frame = aContent->GetPrimaryFrame();
if (!frame || !frame->IsTextFrame()) { if (!frame || !frame->IsTextFrame()) {
return nullptr; return nullptr;

View file

@ -23,8 +23,14 @@ using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;
nsScreen::nsScreen(nsPIDOMWindowInner* aWindow) nsScreen::nsScreen(nsPIDOMWindowInner* aWindow)
: DOMEventTargetHelper(aWindow), : DOMEventTargetHelper(aWindow) {}
mScreenOrientation(new ScreenOrientation(aWindow, this)) {}
/* 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; nsScreen::~nsScreen() = default;

View file

@ -21,9 +21,12 @@ enum class RFPTarget : uint64_t;
// Script "screen" object // Script "screen" object
class nsScreen : public mozilla::DOMEventTargetHelper { class nsScreen : public mozilla::DOMEventTargetHelper {
public: private:
explicit nsScreen(nsPIDOMWindowInner* aWindow); explicit nsScreen(nsPIDOMWindowInner* aWindow);
public:
static already_AddRefed<nsScreen> Create(nsPIDOMWindowInner* aWindow);
NS_DECL_ISUPPORTS_INHERITED NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsScreen, NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsScreen,
mozilla::DOMEventTargetHelper) 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) { void nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder) {
// If the behavior here changes in a substantive way, you may need // If the behavior here changes in a substantive way, you may need
// to update css::Rule::UnlinkDeclarationWrapper as well. // 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. * Update the wrapper when the object moves between globals.
*/ */

View file

@ -86,12 +86,18 @@ inline void nsWrapperCache::UpdateWrapperForNewGlobal(T* aScriptObjectHolder,
SetPreservingWrapper(false); SetPreservingWrapper(false);
} }
JSObject* oldWrapper = mWrapper;
SetWrapper(aNewWrapper); SetWrapper(aNewWrapper);
if (zoneChanged) { if (zoneChanged) {
PreserveWrapper(aScriptObjectHolder); PreserveWrapper(aScriptObjectHolder);
} else if (preserving) { } else if (preserving) {
SetPreservingWrapper(true); 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] [DEFAULT]
["test_bug_2027541.html"]
["test_nested_modules.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 // 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 // slot and nulling out the reserved slot of |obj|.
// to keep everything consistent in case GC moves newobj.
// //
// NB: It's important to do this _after_ copying the properties to // NB: It's important to do this _after_ copying the properties to
// propertyHolder. Otherwise, an object with |foo.x === foo| will // 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::SetReservedSlot(newobj, DOM_OBJECT_SLOT,
JS::GetReservedSlot(aObj, DOM_OBJECT_SLOT)); JS::GetReservedSlot(aObj, DOM_OBJECT_SLOT));
JS::SetReservedSlot(aObj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr)); 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; nsWrapperCache* cache = nullptr;
CallQueryInterface(native, &cache); 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); aObj = xpc::TransplantObjectRetainingXrayExpandos(aCx, aObj, newobj);
if (!aObj) { if (!aObj) {
MOZ_CRASH(); MOZ_CRASH();
} }
// Update the wrapper cache again if transplanting didn't use newobj but if (!preserving || aObj != newobj) {
// returned some other object.
if (aObj != newobj) {
MOZ_ASSERT(UnwrapDOMObjectToISupports(aObj) == native); MOZ_ASSERT(UnwrapDOMObjectToISupports(aObj) == native);
cache->UpdateWrapperForNewGlobal(native, aObj); cache->UpdateWrapperForNewGlobal(native, aObj);
} }
@ -3555,6 +3573,7 @@ static bool GetBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
? aObj ? aObj
: js::UncheckedUnwrap(aObj, : js::UncheckedUnwrap(aObj,
/* stopAtWindowProxy = */ false); /* stopAtWindowProxy = */ false);
MOZ_ASSERT(aSlotIndex < JSCLASS_RESERVED_SLOTS(JS::GetClass(reflector)));
// Retrieve the backing object from the reserved slot on the maplike/setlike // Retrieve the backing object from the reserved slot on the maplike/setlike
// object. If it doesn't exist yet, create it. // object. If it doesn't exist yet, create it.

View file

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

View file

@ -215,6 +215,7 @@ class TErrorResult {
// informative message and calling the relevant Throw*Error. // informative message and calling the relevant Throw*Error.
void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG Throw(nsresult rv) { void MOZ_MUST_RETURN_FROM_CALLER_IF_THIS_IS_ARG Throw(nsresult rv) {
MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success"); MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success");
ClearUnionData();
AssignErrorCode(rv); AssignErrorCode(rv);
} }
@ -431,7 +432,10 @@ class TErrorResult {
// Backwards-compat to make conversion simpler. We don't call // Backwards-compat to make conversion simpler. We don't call
// Throw() here because people can easily pass success codes to // Throw() here because people can easily pass success codes to
// this. This operator is deprecated and ideally shouldn't be used. // 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); } bool Failed() const { return NS_FAILED(mResult); }
@ -542,6 +546,7 @@ class TErrorResult {
} }
void AssignErrorCode(nsresult aRv) { void AssignErrorCode(nsresult aRv) {
MOZ_ASSERT(mUnionState == HasNothing);
MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR, MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR,
"Use ThrowTypeError()"); "Use ThrowTypeError()");
MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR, 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, // If we entered shutdown on the main thread while we were doing IO,
// bail out now. // bail out now.
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) { if (IsCanceled() ||
AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) {
if (stream) { if (stream) {
stream->Close(); 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, // If we entered shutdown on the main thread while we were doing IO,
// bail out now. // bail out now.
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) { if (IsCanceled() ||
AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) {
if (stream) { if (stream) {
stream->Close(); 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, // If we entered shutdown on the main thread while we were doing IO,
// bail out now. // bail out now.
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) { if (IsCanceled() ||
AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) {
if (stream) { if (stream) {
stream->Close(); 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, // If we entered shutdown on the main thread while we were doing IO,
// bail out now. // bail out now.
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) { if (IsCanceled() ||
AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownQM)) {
if (stream) { if (stream) {
stream->Close(); stream->Close();
} }

View file

@ -30,7 +30,6 @@ StreamList::StreamList(SafeRefPtr<Manager> aManager,
mStreamControl(nullptr), mStreamControl(nullptr),
mActivated(false) { mActivated(false) {
MOZ_DIAGNOSTIC_ASSERT(mManager); MOZ_DIAGNOSTIC_ASSERT(mManager);
mContext->AddActivity(*this);
} }
Manager& StreamList::GetManager() const { Manager& StreamList::GetManager() const {
@ -73,6 +72,9 @@ void StreamList::Activate(CacheId aCacheId) {
MOZ_DIAGNOSTIC_ASSERT(mCacheId == INVALID_CACHE_ID); MOZ_DIAGNOSTIC_ASSERT(mCacheId == INVALID_CACHE_ID);
mActivated = true; mActivated = true;
mCacheId = aCacheId; mCacheId = aCacheId;
mContext->AddActivity(*this);
mManager->AddRefCacheId(mCacheId); mManager->AddRefCacheId(mCacheId);
mManager->AddStreamList(*this); mManager->AddStreamList(*this);
@ -134,6 +136,7 @@ void StreamList::NoteClosedAll() {
void StreamList::CloseAll() { void StreamList::CloseAll() {
NS_ASSERT_OWNINGTHREAD(StreamList); NS_ASSERT_OWNINGTHREAD(StreamList);
SafeRefPtr<StreamList> kungFuDeathGrip = SafeRefPtrFromThis();
if (mStreamControl && mStreamControl->CanSend()) { if (mStreamControl && mStreamControl->CanSend()) {
// CloseAll will kick off everything needed for shutdown. // CloseAll will kick off everything needed for shutdown.
@ -192,13 +195,13 @@ StreamList::~StreamList() {
NS_ASSERT_OWNINGTHREAD(StreamList); NS_ASSERT_OWNINGTHREAD(StreamList);
MOZ_DIAGNOSTIC_ASSERT(!mStreamControl); MOZ_DIAGNOSTIC_ASSERT(!mStreamControl);
if (mActivated) { if (mActivated) {
mContext->RemoveActivity(*this);
mManager->RemoveStreamList(*this); mManager->RemoveStreamList(*this);
for (uint32_t i = 0; i < mList.Length(); ++i) { for (uint32_t i = 0; i < mList.Length(); ++i) {
mManager->ReleaseBodyId(mList[i].mId); mManager->ReleaseBodyId(mList[i].mId);
} }
mManager->ReleaseCacheId(mCacheId); mManager->ReleaseCacheId(mCacheId);
} }
mContext->RemoveActivity(*this);
} }
} // namespace mozilla::dom::cache } // 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_WRAPPERCACHE_CLASS(CanvasRenderingContext2D)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D)
tmp->RemoveShutdownObserver();
tmp->OnShutdown();
// Make sure we remove ourselves from the list of demotable contexts (raw // Make sure we remove ourselves from the list of demotable contexts (raw
// pointers), since we're logically destructed at this point. // pointers), since we're logically destructed at this point.
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCanvasElement) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCanvasElement)
@ -2888,8 +2890,12 @@ void CanvasRenderingContext2D::GetLetterSpacing(nsACString& aLetterSpacing) {
void CanvasRenderingContext2D::SetLetterSpacing( void CanvasRenderingContext2D::SetLetterSpacing(
const nsACString& aLetterSpacing) { const nsACString& aLetterSpacing) {
ParseSpacing(aLetterSpacing, &CurrentState().letterSpacing, nsAutoCString normalized;
CurrentState().letterSpacingStr); Maybe<float> value = ParseSpacing(aLetterSpacing, normalized);
if (value) {
CurrentState().letterSpacing = *value;
CurrentState().letterSpacingStr = normalized;
}
} }
void CanvasRenderingContext2D::GetWordSpacing(nsACString& aWordSpacing) { void CanvasRenderingContext2D::GetWordSpacing(nsACString& aWordSpacing) {
@ -2901,8 +2907,12 @@ void CanvasRenderingContext2D::GetWordSpacing(nsACString& aWordSpacing) {
} }
void CanvasRenderingContext2D::SetWordSpacing(const nsACString& aWordSpacing) { void CanvasRenderingContext2D::SetWordSpacing(const nsACString& aWordSpacing) {
ParseSpacing(aWordSpacing, &CurrentState().wordSpacing, nsAutoCString normalized;
CurrentState().wordSpacingStr); Maybe<float> value = ParseSpacing(aWordSpacing, normalized);
if (value) {
CurrentState().wordSpacing = *value;
CurrentState().wordSpacingStr = normalized;
}
} }
static GeckoFontMetrics GetFontMetricsFromCanvas(void* aContext) { static GeckoFontMetrics GetFontMetricsFromCanvas(void* aContext) {
@ -2933,9 +2943,8 @@ static GeckoFontMetrics GetFontMetricsFromCanvas(void* aContext) {
0.0f}; 0.0f};
} }
void CanvasRenderingContext2D::ParseSpacing(const nsACString& aSpacing, Maybe<float> CanvasRenderingContext2D::ParseSpacing(const nsACString& aSpacing,
float* aValue, nsACString& aNormalized) {
nsACString& aNormalized) {
// Normalize whitespace in the string before trying to parse it, as we want // 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 // to store it in normalized form, and this allows a simple check against the
// 'normal' keyword, which is not accepted. // 'normal' keyword, which is not accepted.
@ -2943,28 +2952,28 @@ void CanvasRenderingContext2D::ParseSpacing(const nsACString& aSpacing,
normalized.CompressWhitespace(true, true); normalized.CompressWhitespace(true, true);
ToLowerCase(normalized); ToLowerCase(normalized);
if (normalized.EqualsLiteral("normal")) { if (normalized.EqualsLiteral("normal")) {
return; return Nothing();
} }
float value; float value;
if (!Servo_ParseLengthWithoutStyleContext(&normalized, &value, if (!Servo_ParseLengthWithoutStyleContext(&normalized, &value,
GetFontMetricsFromCanvas, this)) { GetFontMetricsFromCanvas, this)) {
if (!GetPresShell()) { if (!GetPresShell()) {
return; return Nothing();
} }
// This will parse aSpacing as a <length-percentage>... // This will parse aSpacing as a <length-percentage>...
RefPtr<const ComputedStyle> style = RefPtr<const ComputedStyle> style =
ResolveStyleForProperty(eCSSProperty_letter_spacing, aSpacing); ResolveStyleForProperty(eCSSProperty_letter_spacing, aSpacing);
if (!style) { if (!style) {
return; return Nothing();
} }
// ...but only <length> is allowed according to the canvas spec. // ...but only <length> is allowed according to the canvas spec.
if (!style->StyleText()->mLetterSpacing.IsLength()) { if (!style->StyleText()->mLetterSpacing.IsLength()) {
return; return Nothing();
} }
value = style->StyleText()->mLetterSpacing.AsLength().ToCSSPixels(); value = style->StyleText()->mLetterSpacing.AsLength().ToCSSPixels();
} }
aNormalized = normalized; aNormalized = normalized;
*aValue = value; return Some(value);
} }
class CanvasUserSpaceMetrics final : public UserSpaceMetricsWithSize { class CanvasUserSpaceMetrics final : public UserSpaceMetricsWithSize {
@ -3434,6 +3443,11 @@ void CanvasRenderingContext2D::StrokeImpl(const gfx::Path& aPath) {
return; return;
} }
const bool needBounds = NeedToCalculateBounds();
if (!IsTargetValid()) {
return;
}
const ContextState* state = &CurrentState(); const ContextState* state = &CurrentState();
StrokeOptions strokeOptions(state->lineWidth, CanvasToGfx(state->lineJoin), StrokeOptions strokeOptions(state->lineWidth, CanvasToGfx(state->lineJoin),
CanvasToGfx(state->lineCap), state->miterLimit, CanvasToGfx(state->lineCap), state->miterLimit,
@ -3441,10 +3455,6 @@ void CanvasRenderingContext2D::StrokeImpl(const gfx::Path& aPath) {
state->dashOffset); state->dashOffset);
state = nullptr; state = nullptr;
const bool needBounds = NeedToCalculateBounds();
if (!IsTargetValid()) {
return;
}
gfx::Rect bounds; gfx::Rect bounds;
if (needBounds) { if (needBounds) {
bounds = aPath.GetStrokedBounds(strokeOptions, mTarget->GetTransform()); bounds = aPath.GetStrokedBounds(strokeOptions, mTarget->GetTransform());
@ -4695,6 +4705,7 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor final
} }
mCtx->EnsureTarget(); mCtx->EnsureTarget();
const bool needBounds = mCtx->NeedToCalculateBounds();
if (!mCtx->IsTargetValid()) { if (!mCtx->IsTargetValid()) {
return; return;
} }
@ -4709,7 +4720,7 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor final
const ContextState& state = mCtx->CurrentState(); const ContextState& state = mCtx->CurrentState();
gfx::Rect bounds; gfx::Rect bounds;
if (mCtx->NeedToCalculateBounds()) { if (needBounds) {
bounds = ToRect(mBoundingBox); bounds = ToRect(mBoundingBox);
bounds.MoveBy(mPt / mAppUnitsPerDevPixel); bounds.MoveBy(mPt / mAppUnitsPerDevPixel);
if (style == Style::STROKE) { if (style == Style::STROKE) {
@ -4857,6 +4868,9 @@ UniquePtr<TextMetrics> CanvasRenderingContext2D::DrawOrMeasureText(
canvasStyle = nsComputedDOMStyle::GetComputedStyle(mCanvasElement); 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. // Get text direction, either from the property or inherited from context.
const ContextState& state = CurrentState(); const ContextState& state = CurrentState();
bool isRTL; bool isRTL;
@ -4882,8 +4896,6 @@ UniquePtr<TextMetrics> CanvasRenderingContext2D::DrawOrMeasureText(
MOZ_CRASH("unknown direction!"); 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()) { if (presShell && presShell->IsDestroying()) {
aError = NS_ERROR_FAILURE; aError = NS_ERROR_FAILURE;
return nullptr; return nullptr;
@ -5163,50 +5175,54 @@ gfxFontGroup* CanvasRenderingContext2D::GetCurrentFontStyle() {
nsPresContext* presContext = nsPresContext* presContext =
presShell ? presShell->GetPresContext() : nullptr; 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. // If we have a cached fontGroup, check that it is valid for the current
RefPtr<gfxFontGroup>& fontGroup = CurrentState().fontGroup; // prescontext; if not, we need to discard and re-create it.
if (fontGroup) { RefPtr<gfxFontGroup>& fontGroup = CurrentState().fontGroup;
if (fontGroup->GetPresContext() != presContext) { if (fontGroup) {
fontGroup = nullptr; 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;
} else { } 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); HTMLVideoElement* video = HTMLVideoElement::FromNodeOrNull(element);
if (video && mBufferProvider->IsAccelerated() && if (video && mBufferProvider->IsAccelerated() &&
mTarget->IsRecording() && mTarget->IsRecording() &&
!(!NeedToApplyFilter() && NeedToDrawShadow())) { !(NeedToApplyFilter() || NeedToDrawShadow())) {
res = nsLayoutUtils::SurfaceFromElement( res = nsLayoutUtils::SurfaceFromElement(
video, sfeFlags, mTarget, /* aOptimizeSourceSurface */ false); video, sfeFlags, mTarget, /* aOptimizeSourceSurface */ false);
surfaceDescriptor = MaybeGetSurfaceDescriptorForRemoteCanvas(res); surfaceDescriptor = MaybeGetSurfaceDescriptorForRemoteCanvas(res);

View file

@ -613,11 +613,13 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
protected: protected:
/** /**
* Helper to parse a value for the letterSpacing or wordSpacing attribute. * Helper to parse a value for the letterSpacing or wordSpacing attribute.
* If successful, returns the result in aValue, and the whitespace-normalized * If the string can be parsed, returns Some(value) and sets aNormalized to
* value string in aNormalized; if unsuccessful these are left untouched. * 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, mozilla::Maybe<float> ParseSpacing(const nsACString& aSpacing,
nsACString& aNormalized); nsACString& aNormalized);
already_AddRefed<const ComputedStyle> ResolveStyleForProperty( already_AddRefed<const ComputedStyle> ResolveStyleForProperty(
nsCSSPropertyID aProperty, const nsACString& aValue); nsCSSPropertyID aProperty, const nsACString& aValue);
@ -1000,10 +1002,14 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
* last call to UpdateFilter and now. * last call to UpdateFilter and now.
*/ */
const gfx::FilterDescription& EnsureUpdatedFilter() { const gfx::FilterDescription& EnsureUpdatedFilter() {
bool isWriteOnly = mCanvasElement && mCanvasElement->IsWriteOnly(); bool isWriteOnly = IsWriteOnly() ||
(mCanvasElement && mCanvasElement->IsWriteOnly()) ||
(mOffscreenCanvas && mOffscreenCanvas->IsWriteOnly());
if (CurrentState().filterSourceGraphicTainted != isWriteOnly) { if (CurrentState().filterSourceGraphicTainted != isWriteOnly) {
UpdateFilter(/* aFlushIfNeeded = */ true); // Do not flush here: this runs inside drawing operations that hold raw
EnsureTarget(); // 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); MOZ_ASSERT(CurrentState().filterSourceGraphicTainted == isWriteOnly);
return CurrentState().filter; return CurrentState().filter;

View file

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

View file

@ -21,7 +21,6 @@
#include "mozilla/gfx/Swizzle.h" #include "mozilla/gfx/Swizzle.h"
#include "mozilla/layers/ImageDataSerializer.h" #include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/layers/RemoteTextureMap.h" #include "mozilla/layers/RemoteTextureMap.h"
#include "mozilla/widget/ScreenManager.h"
#include "skia/include/core/SkPixmap.h" #include "skia/include/core/SkPixmap.h"
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsIMemoryReporter.h" #include "nsIMemoryReporter.h"
@ -817,7 +816,7 @@ bool DrawTargetWebgl::GenerateComplexClipMask() {
return !!data; return !!data;
} }
bool DrawTargetWebgl::SetSimpleClipRect() { Maybe<Rect> DrawTargetWebgl::ComputeSimpleClipRect() const {
// Determine whether the clipping rectangle is simple enough to accelerate. // Determine whether the clipping rectangle is simple enough to accelerate.
// Check if there is a device space clip rectangle available from the Skia // Check if there is a device space clip rectangle available from the Skia
// target. // target.
@ -829,9 +828,7 @@ bool DrawTargetWebgl::SetSimpleClipRect() {
if (!clip->IsEmpty() && clip->Contains(GetRect())) { if (!clip->IsEmpty() && clip->Contains(GetRect())) {
clip = Some(GetRect()); clip = Some(GetRect());
} }
mSharedContext->SetClipRect(*clip); return Some(Rect(*clip));
mSharedContext->SetNoClipMask();
return true;
} }
// There was no pixel-aligned clip rect available, so check the clip stack to // There was no pixel-aligned clip rect available, so check the clip stack to
@ -842,15 +839,22 @@ bool DrawTargetWebgl::SetSimpleClipRect() {
// complex. // complex.
if (clipStack.mPath || if (clipStack.mPath ||
!clipStack.mTransform.PreservesAxisAlignedRectangles()) { !clipStack.mTransform.PreservesAxisAlignedRectangles()) {
return false; return Nothing();
} }
// Transform the rect and intersect it with the current clip. // Transform the rect and intersect it with the current clip.
rect = rect =
clipStack.mTransform.TransformBounds(clipStack.mRect).Intersect(rect); clipStack.mTransform.TransformBounds(clipStack.mRect).Intersect(rect);
} }
mSharedContext->SetClipRect(rect); return Some(rect);
mSharedContext->SetNoClipMask(); }
return true;
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 // Installs the Skia clip rectangle, if applicable, onto the shared WebGL
@ -874,6 +878,21 @@ bool DrawTargetWebgl::PrepareContext(bool aClipped) {
return mSharedContext->SetTarget(this); 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 { bool SharedContextWebgl::IsContextLost() const {
return !mWebgl || mWebgl->IsContextLost(); return !mWebgl || mWebgl->IsContextLost();
} }
@ -905,29 +924,12 @@ bool DrawTargetWebgl::CanCreate(const IntSize& aSize, SurfaceFormat aFormat) {
return false; return false;
} }
// Maximum pref allows 3 different options: // Maximum pref allows 2 different options:
// 0 means unlimited size, // <= 0 means unlimited size,
// > 0 means use value as an absolute threshold, // > 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(); int32_t maxSize = StaticPrefs::gfx_canvas_accelerated_max_size();
if (maxSize > 0) { if (maxSize > 0 && std::max(aSize.width, aSize.height) > maxSize) {
if (std::max(aSize.width, aSize.height) > maxSize) { return false;
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;
}
}
} }
return true; return true;
@ -1645,9 +1647,7 @@ void DrawTargetWebgl::ClearRect(const Rect& aRect) {
// If the clear rectangle encompasses the entire viewport and is not clipped, // If the clear rectangle encompasses the entire viewport and is not clipped,
// then mark the target as entirely clear. // then mark the target as entirely clear.
if (containsViewport && mSharedContext->IsCurrentTarget(this) && if (containsViewport && !ShouldClip()) {
!mSharedContext->HasClipMask() &&
mSharedContext->mClipAARect.Contains(Rect(GetRect()))) {
mIsClear = true; mIsClear = true;
} }
} }
@ -2038,6 +2038,7 @@ bool SharedContextWebgl::UploadSurface(DataSourceSurface* aData,
if (srcRect.IsEmpty()) { if (srcRect.IsEmpty()) {
return true; return true;
} }
Maybe<DataSourceSurface::ScopedMap> map;
if (aData) { if (aData) {
// If the source rect could not possibly overlap the surface, then it is // If the source rect could not possibly overlap the surface, then it is
// effectively empty with nothing to upload. // 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 // The surface needs to be uploaded to its backing texture either to
// initialize or update the texture handle contents. Map the data // initialize or update the texture handle contents. Map the data
// contents of the surface so it can be read. // contents of the surface so it can be read.
DataSourceSurface::ScopedMap map(aData, DataSourceSurface::READ); map.emplace(aData, DataSourceSurface::READ);
if (!map.IsMapped()) { if (!map->IsMapped()) {
return false; return false;
} }
int32_t stride = map.GetStride(); int32_t stride = map->GetStride();
// Get the data pointer range considering the sampling rect offset and // Get the data pointer range considering the sampling rect offset and
// size. // size.
Span<const uint8_t> range( 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); std::max(srcRect.height - 1, 0) * size_t(stride) + srcRect.width * bpp);
texDesc.cpuData = Some(range); texDesc.cpuData = Some(range);
// If the stride happens to be 4 byte aligned, assume that is the // 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); return mSharedContext->SupportsPattern(aPattern);
} }
Maybe<Rect> ComputeSimpleClipRect() const;
bool SetSimpleClipRect(); bool SetSimpleClipRect();
bool GenerateComplexClipMask(); bool GenerateComplexClipMask();
bool PrepareContext(bool aClipped = true); bool PrepareContext(bool aClipped = true);
bool ShouldClip();
void DrawRectFallback(const Rect& aRect, const Pattern& aPattern, void DrawRectFallback(const Rect& aRect, const Pattern& aPattern,
const DrawOptions& aOptions, const DrawOptions& aOptions,

View file

@ -146,10 +146,29 @@ already_AddRefed<ImageData> ImageData::ReadStructuredClone(
!JS_ReadTypedArray(aReader, &dataArray)) { !JS_ReadTypedArray(aReader, &dataArray)) {
return nullptr; return nullptr;
} }
MOZ_ASSERT(dataArray.isObject());
JS::Rooted<JSObject*> arrayObj(aCx, &dataArray.toObject()); JS::Rooted<JSObject*> dataObj(aCx, &dataArray.toObject());
RefPtr<ImageData> imageData = new ImageData(aGlobal, width, height, arrayObj); 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(); return imageData.forget();
} }

View file

@ -391,8 +391,8 @@ static bool HasColorAndAlpha(const WebGLTexelFormat format) {
} }
bool TexUnpackBlob::ConvertIfNeeded( bool TexUnpackBlob::ConvertIfNeeded(
const WebGLContext* const webgl, const uint32_t rowLength, const WebGLContext* const webgl, const size_t rowLength,
const uint32_t rowCount, WebGLTexelFormat srcFormat, const size_t rowCount, WebGLTexelFormat srcFormat,
const uint8_t* const srcBegin, const ptrdiff_t srcStride, const uint8_t* const srcBegin, const ptrdiff_t srcStride,
WebGLTexelFormat dstFormat, const ptrdiff_t dstStride, WebGLTexelFormat dstFormat, const ptrdiff_t dstStride,
const uint8_t** const out_begin, 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()) { if (!dstTotalBytes.isValid()) {
webgl->ErrorOutOfMemory("Calculation failed."); webgl->ErrorOutOfMemory("Calculation failed.");
return false; return false;
@ -855,9 +855,14 @@ bool TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec,
: dom::PredefinedColorSpace::Srgb; : dom::PredefinedColorSpace::Srgb;
bool sameColorSpace = (srcColorSpace == dstColorSpace); 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, level, {xOffset, yOffset, zOffset}, dui->internalFormat, pi, mDesc,
webgl->mOptionalRenderableFormatBits, sameColorSpace); webgl->mOptionalRenderableFormatBits, sameColorSpace);
}
if (reason) { if (reason) {
webgl->GeneratePerfWarning( webgl->GeneratePerfWarning(
"Failed to hit GPU-copy fast-path." "Failed to hit GPU-copy fast-path."

View file

@ -65,8 +65,8 @@ class TexUnpackBlob {
virtual ~TexUnpackBlob() = default; virtual ~TexUnpackBlob() = default;
protected: protected:
bool ConvertIfNeeded(const WebGLContext*, const uint32_t rowLength, bool ConvertIfNeeded(const WebGLContext*, const size_t rowLength,
const uint32_t rowCount, WebGLTexelFormat srcFormat, const size_t rowCount, WebGLTexelFormat srcFormat,
const uint8_t* const srcBegin, const ptrdiff_t srcStride, const uint8_t* const srcBegin, const ptrdiff_t srcStride,
WebGLTexelFormat dstFormat, const ptrdiff_t dstStride, 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[] = { constexpr GLenum SHADER_TYPES[] = {
LOCAL_GL_VERTEX_SHADER, LOCAL_GL_VERTEX_SHADER,
LOCAL_GL_FRAGMENT_SHADER, LOCAL_GL_FRAGMENT_SHADER,
@ -709,7 +665,7 @@ RefPtr<WebGLContext> WebGLContext::Create(HostWebGLContext* host,
out->options = webgl->mOptions; out->options = webgl->mOptions;
out->limits = *webgl->mLimits; out->limits = *webgl->mLimits;
out->uploadableSdTypes = UploadableSdTypes(); out->uploadableSdTypes = webgl->mUploadableSdTypes;
out->vendor = webgl->gl->Vendor(); out->vendor = webgl->gl->Vendor();
out->optionalRenderableFormatBits = webgl->mOptionalRenderableFormatBits; out->optionalRenderableFormatBits = webgl->mOptionalRenderableFormatBits;
@ -811,6 +767,58 @@ void WebGLContext::FinishInit() {
gl->ResetSyncCallCount("WebGLContext Initialization"); gl->ResetSyncCallCount("WebGLContext Initialization");
LoseLruContextIfLimitExceeded(); 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( void WebGLContext::SetCompositableHost(
@ -2787,6 +2795,23 @@ webgl::ExplicitPixelPackingState::ForUseWith(
const Maybe<size_t> bytesPerRowStrideOverride) { const Maybe<size_t> bytesPerRowStrideOverride) {
auto state = stateOrZero; 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)) { if (!IsTexTarget3D(target)) {
state.skipImages = 0; state.skipImages = 0;
state.imageHeight = 0; state.imageHeight = 0;
@ -2892,7 +2917,9 @@ webgl::ExplicitPixelPackingState::ForUseWith(
const auto elemsPerRowStride = ElemsPerRowStride(); const auto elemsPerRowStride = ElemsPerRowStride();
const auto bytesPerRowStride = pii.bytesPerElement * 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."); return Err("ROW_LENGTH or width too large for packing.");
} }
metrics.bytesPerRowStride = bytesPerRowStride.value(); metrics.bytesPerRowStride = bytesPerRowStride.value();

View file

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

View file

@ -487,23 +487,28 @@ bool ShaderValidatorResults::CanLinkTo(const ShaderValidatorResults& vert,
size_t ShaderValidatorResults::SizeOfIncludingThis( size_t ShaderValidatorResults::SizeOfIncludingThis(
const MallocSizeOf fnSizeOf) const { const MallocSizeOf fnSizeOf) const {
auto ret = fnSizeOf(this); auto ret = fnSizeOf(this);
ret += mInfoLog.size();
ret += mObjectCode.size();
for (const auto& cur : mAttributes) { // std::string heap allocations are not measured here because:
ret += fnSizeOf(&cur); // 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) { if (!mInterfaceBlocks.empty()) {
ret += fnSizeOf(&cur); ret += fnSizeOf(mInterfaceBlocks.data());
} }
for (const auto& cur : mOutputVariables) { if (!mOutputVariables.empty()) {
ret += fnSizeOf(&cur); ret += fnSizeOf(mOutputVariables.data());
} }
for (const auto& cur : mUniforms) { if (!mUniforms.empty()) {
ret += fnSizeOf(&cur); ret += fnSizeOf(mUniforms.data());
} }
for (const auto& cur : mVaryings) { if (!mVaryings.empty()) {
ret += fnSizeOf(&cur); ret += fnSizeOf(mVaryings.data());
} }
return ret; return ret;

View file

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

View file

@ -152,7 +152,8 @@ NS_IMPL_ISUPPORTS(NavigateLoadListener, nsIWebProgressListener,
} // anonymous namespace } // anonymous namespace
RefPtr<ClientOpPromise> ClientNavigateOpChild::DoNavigate( RefPtr<ClientOpPromise> ClientNavigateOpChild::DoNavigate(
const ClientNavigateOpConstructorArgs& aArgs) { const ClientNavigateOpConstructorArgs& aArgs,
mozilla::ipc::ActorLifecycleProxy* aProxy) {
nsCOMPtr<nsPIDOMWindowInner> window; nsCOMPtr<nsPIDOMWindowInner> window;
// Navigating the target client window will result in the original // Navigating the target client window will result in the original
@ -278,6 +279,12 @@ RefPtr<ClientOpPromise> ClientNavigateOpChild::DoNavigate(
return ClientOpPromise::CreateAndReject(result, __func__); return ClientOpPromise::CreateAndReject(result, __func__);
} }
if (!aProxy->Get() || !CanSend()) {
CopyableErrorResult result;
result.ThrowInvalidStateError("Unknown Client");
return ClientOpPromise::CreateAndReject(result, __func__);
}
RefPtr<ClientOpPromise::Private> promise = RefPtr<ClientOpPromise::Private> promise =
new ClientOpPromise::Private(__func__); new ClientOpPromise::Private(__func__);
@ -305,8 +312,12 @@ void ClientNavigateOpChild::ActorDestroy(ActorDestroyReason aReason) {
mPromiseRequestHolder.DisconnectIfExists(); mPromiseRequestHolder.DisconnectIfExists();
} }
void ClientNavigateOpChild::Init(const ClientNavigateOpConstructorArgs& aArgs) { void ClientNavigateOpChild::Init(const ClientNavigateOpConstructorArgs& aArgs,
RefPtr<ClientOpPromise> promise = DoNavigate(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 // 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 // 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; nsCOMPtr<nsISerialEventTarget> mSerialEventTarget;
[[nodiscard]] RefPtr<ClientOpPromise> DoNavigate( [[nodiscard]] RefPtr<ClientOpPromise> DoNavigate(
const ClientNavigateOpConstructorArgs& aArgs); const ClientNavigateOpConstructorArgs& aArgs,
mozilla::ipc::ActorLifecycleProxy* aProxy);
// PClientNavigateOpChild interface // PClientNavigateOpChild interface
void ActorDestroy(ActorDestroyReason aReason) override; void ActorDestroy(ActorDestroyReason aReason) override;
@ -25,7 +26,8 @@ class ClientNavigateOpChild final : public PClientNavigateOpChild {
ClientNavigateOpChild() = default; ClientNavigateOpChild() = default;
~ClientNavigateOpChild() = default; ~ClientNavigateOpChild() = default;
void Init(const ClientNavigateOpConstructorArgs& aArgs); void Init(const ClientNavigateOpConstructorArgs& aArgs,
mozilla::ipc::ActorLifecycleProxy* aProxy);
}; };
} // namespace mozilla::dom } // namespace mozilla::dom

View file

@ -520,11 +520,12 @@ RefPtr<ClientOpPromise> ClientSource::Focus(const ClientFocusArgs& aArgs) {
return ClientOpPromise::CreateAndReject(rv, __func__); return ClientOpPromise::CreateAndReject(rv, __func__);
} }
nsCOMPtr<nsPIDOMWindowOuter> outer; nsCOMPtr<nsPIDOMWindowOuter> outer;
nsPIDOMWindowInner* inner = GetInnerWindow(); nsCOMPtr<nsPIDOMWindowInner> inner = GetInnerWindow();
nsIDocShell* docshell = nullptr;
if (inner) { if (inner) {
outer = inner->GetOuterWindow(); outer = inner->GetOuterWindow();
} else { } else {
nsIDocShell* docshell = GetDocShell(); docshell = GetDocShell();
if (docshell) { if (docshell) {
outer = docshell->GetWindow(); outer = docshell->GetWindow();
} }
@ -537,9 +538,48 @@ RefPtr<ClientOpPromise> ClientSource::Focus(const ClientFocusArgs& aArgs) {
} }
MOZ_ASSERT(NS_IsMainThread()); 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()); 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()) { if (state.isErr()) {
return ClientOpPromise::CreateAndReject( return ClientOpPromise::CreateAndReject(
CopyableErrorResult(state.unwrapErr()), __func__); CopyableErrorResult(state.unwrapErr()), __func__);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -123,6 +123,19 @@ template <typename T>
InternalResponse::~InternalResponse() = default; 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() { InternalResponseMetadata InternalResponse::GetMetadata() {
nsTArray<HeadersEntry> headers; nsTArray<HeadersEntry> headers;
HeadersGuardEnum headersGuard; HeadersGuardEnum headersGuard;

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