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

This commit is contained in:
Ark74 2026-03-28 14:10:24 -06:00
parent 8eb1f1732f
commit a5f93cb214
1197 changed files with 30593 additions and 15344 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=2407441a2f67341a0e13b4ba6547555e387c671c"] [source."git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=579b75af21c040700eee6a1d8520e222699fe4cd"]
git = "https://github.com/mozilla/cubeb-coreaudio-rs" git = "https://github.com/mozilla/cubeb-coreaudio-rs"
rev = "2407441a2f67341a0e13b4ba6547555e387c671c" rev = "579b75af21c040700eee6a1d8520e222699fe4cd"
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"]

View file

@ -263,8 +263,7 @@ jobs:
- mozilla-release - mozilla-release
- mozilla-esr140 - mozilla-esr140
when: when:
- {weekday: 'Monday', hour: 8, minute: 0} - {weekday: 'Monday', hour: 5, minute: 0}
- {weekday: 'Thursday', hour: 8, minute: 0}
- name: daily-beta-perf - name: daily-beta-perf
job: job:

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-01-12 Merge day clobber 2026-02-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=2407441a2f67341a0e13b4ba6547555e387c671c#2407441a2f67341a0e13b4ba6547555e387c671c" source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=579b75af21c040700eee6a1d8520e222699fe4cd#579b75af21c040700eee6a1d8520e222699fe4cd"
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=2407441a2f67341a0e13b4ba6547555e387c671c#2407441a2f67341a0e13b4ba6547555e387c671c" source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=579b75af21c040700eee6a1d8520e222699fe4cd#579b75af21c040700eee6a1d8520e222699fe4cd"
dependencies = [ dependencies = [
"atomic", "atomic",
"audio-mixer", "audio-mixer",

View file

@ -1768,11 +1768,12 @@ void DocAccessible::DoInitialUpdate() {
} }
#endif #endif
// Fire reorder event after the document tree is constructed. Note, since // Fire a reorder event on the OuterDocAccessible after the document tree is
// this reorder event is processed by parent document then events targeted to // constructed. Note that since this reorder event is processed by the parent
// this document may be fired prior to this reorder event. If this is // document, events targeted to this child document may be fired prior to this
// a problem then consider to keep event processing per tab document. // reorder event. We don't fire a reorder event for remote documents; the
if (!IsRoot()) { // parent process handles that.
if (!IPCDoc() && !IsRoot()) {
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(LocalParent()); RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(LocalParent());
ParentDocument()->FireDelayedEvent(reorderEvent); ParentDocument()->FireDelayedEvent(reorderEvent);
} }

View file

@ -50,6 +50,7 @@ DocAccessibleParent::DocAccessibleParent()
mTopLevel(false), mTopLevel(false),
mTopLevelInContentProcess(false), mTopLevelInContentProcess(false),
mShutdown(false), mShutdown(false),
mIsInitialTreeDone(false),
mFocus(0), mFocus(0),
mCaretId(0), mCaretId(0),
mCaretOffset(-1), mCaretOffset(-1),
@ -145,7 +146,9 @@ mozilla::ipc::IPCResult DocAccessibleParent::ProcessShowEvent(
// Otherwise, clients might crawl the incomplete subtree and they won't get // Otherwise, clients might crawl the incomplete subtree and they won't get
// mutation events for the remaining pieces. // mutation events for the remaining pieces.
if (aComplete || root != child) { if (aComplete || root != child) {
AttachChild(parent, childIdx, child); if (!AttachChild(parent, childIdx, child)) {
return IPC_FAIL(this, "failed to attach child");
}
} }
} }
@ -174,7 +177,9 @@ mozilla::ipc::IPCResult DocAccessibleParent::ProcessShowEvent(
MOZ_ASSERT(rootParent); MOZ_ASSERT(rootParent);
root = GetAccessible(mPendingShowChild); root = GetAccessible(mPendingShowChild);
MOZ_ASSERT(root); MOZ_ASSERT(root);
AttachChild(rootParent, mPendingShowIndex, root); if (!AttachChild(rootParent, mPendingShowIndex, root)) {
return IPC_FAIL(this, "failed to attach pending show child");
}
mPendingShowChild = 0; mPendingShowChild = 0;
mPendingShowParent = 0; mPendingShowParent = 0;
mPendingShowIndex = 0; mPendingShowIndex = 0;
@ -234,6 +239,11 @@ RemoteAccessible* DocAccessibleParent::CreateAcc(
return nullptr; return nullptr;
} }
if (aAccData.GenericTypes() & eDocument) {
MOZ_ASSERT_UNREACHABLE("Invalid acc type");
return nullptr;
}
newProxy = new RemoteAccessible(aAccData.ID(), this, aAccData.Role(), newProxy = new RemoteAccessible(aAccData.ID(), this, aAccData.Role(),
aAccData.Type(), aAccData.GenericTypes(), aAccData.Type(), aAccData.GenericTypes(),
aAccData.RoleMapEntryIndex()); aAccData.RoleMapEntryIndex());
@ -246,9 +256,20 @@ RemoteAccessible* DocAccessibleParent::CreateAcc(
return newProxy; return newProxy;
} }
void DocAccessibleParent::AttachChild(RemoteAccessible* aParent, bool DocAccessibleParent::AttachChild(RemoteAccessible* aParent,
uint32_t aIndex, uint32_t aIndex,
RemoteAccessible* aChild) { RemoteAccessible* aChild) {
if (aChild->RemoteParent()) {
MOZ_ASSERT_UNREACHABLE(
"Attempt to attach child which already has a parent!");
return false;
}
if (aParent == aChild) {
MOZ_ASSERT_UNREACHABLE("Attempt to make an accessible its own child!");
return false;
}
aParent->AddChildAt(aIndex, aChild); aParent->AddChildAt(aIndex, aChild);
aChild->SetParent(aParent); aChild->SetParent(aParent);
// ProxyCreated might have already been called if aChild is being moved. // ProxyCreated might have already been called if aChild is being moved.
@ -269,11 +290,16 @@ void DocAccessibleParent::AttachChild(RemoteAccessible* aParent,
} }
MOZ_ASSERT(bridge->GetEmbedderAccessibleDoc() == this); MOZ_ASSERT(bridge->GetEmbedderAccessibleDoc() == this);
if (DocAccessibleParent* childDoc = bridge->GetDocAccessibleParent()) { if (DocAccessibleParent* childDoc = bridge->GetDocAccessibleParent()) {
MOZ_DIAGNOSTIC_ASSERT(!childDoc->RemoteParent(),
"Pending OOP child doc shouldn't have parent "
"once new OuterDoc is attached");
AddChildDoc(childDoc, aChild->ID(), false); AddChildDoc(childDoc, aChild->ID(), false);
} }
return true; return true;
}); });
} }
return true;
} }
void DocAccessibleParent::ShutdownOrPrepareForMove(RemoteAccessible* aAcc) { void DocAccessibleParent::ShutdownOrPrepareForMove(RemoteAccessible* aAcc) {
@ -286,6 +312,10 @@ void DocAccessibleParent::ShutdownOrPrepareForMove(RemoteAccessible* aAcc) {
// the show event. For now, clear all of them by moving them to a temporary. // the show event. For now, clear all of them by moving them to a temporary.
auto children{std::move(aAcc->mChildren)}; auto children{std::move(aAcc->mChildren)};
for (RemoteAccessible* child : children) { for (RemoteAccessible* child : children) {
if (child == aAcc) {
MOZ_ASSERT_UNREACHABLE(
"Somehow an accessible got added as a child of itself!");
}
ShutdownOrPrepareForMove(child); ShutdownOrPrepareForMove(child);
} }
} }
@ -603,6 +633,18 @@ mozilla::ipc::IPCResult DocAccessibleParent::RecvMutationEvents(
mozilla::ipc::IPCResult DocAccessibleParent::RecvRequestAckMutationEvents() { mozilla::ipc::IPCResult DocAccessibleParent::RecvRequestAckMutationEvents() {
if (!mShutdown) { if (!mShutdown) {
if (!mIsInitialTreeDone) {
// This is the first request for an ACK, which means we now have the
// initial tree.
mIsInitialTreeDone = true;
// If this document is already bound to its embedder, fire a reorder event
// to notify the client that the embedded document is available. If not,
// this will be handled when this document is bound in AddChildDoc.
if (RemoteAccessible* parent = RemoteParent()) {
parent->Document()->FireEvent(parent,
nsIAccessibleEvent::EVENT_REORDER);
}
}
Unused << SendAckMutationEvents(); Unused << SendAckMutationEvents();
} }
return IPC_OK(); return IPC_OK();
@ -882,6 +924,11 @@ mozilla::ipc::IPCResult DocAccessibleParent::RecvBindChildDoc(
ipc::IPCResult DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc, ipc::IPCResult DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
uint64_t aParentID, uint64_t aParentID,
bool aCreating) { bool aCreating) {
if (aChildDoc->RemoteParent()) {
return IPC_FAIL(this,
"Attempt to add child doc which already has a parent");
}
// 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);
@ -930,11 +977,20 @@ ipc::IPCResult DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
aChildDoc->SetEmulatedWindowHandle(mEmulatedWindowHandle); aChildDoc->SetEmulatedWindowHandle(mEmulatedWindowHandle);
} }
#endif // defined(XP_WIN) #endif // defined(XP_WIN)
// We need to fire a reorder event on the outer doc accessible. }
// For same-process documents, this is fired by the content process, but // We need to fire a reorder event on the embedder. We do this here rather
// this isn't possible when the document is in a different process to its // than in the content process for two reasons:
// embedder. // 1. It isn't possible for the content process to fire a reorder event on the
// FireEvent fires both OS and XPCOM events. // embedder when the embedded document is in a different process to its
// embedder.
// 2. Doing it here ensures that the event is fired after the child document
// is bound. Otherwise, there could be a short period where the content
// process has fired the reorder event, but the child document isn't bound
// yet.
// However, if the initial tree hasn't been received yet, we don't want to
// fire the reorder event yet. That gets handled in
// RecvRequestAckMutationEvents.
if (aChildDoc->mIsInitialTreeDone) {
FireEvent(outerDoc, nsIAccessibleEvent::EVENT_REORDER); FireEvent(outerDoc, nsIAccessibleEvent::EVENT_REORDER);
} }

View file

@ -325,11 +325,14 @@ class DocAccessibleParent : public RemoteAccessible,
}; };
RemoteAccessible* CreateAcc(const AccessibleData& aAccData); RemoteAccessible* CreateAcc(const AccessibleData& aAccData);
void AttachChild(RemoteAccessible* aParent, uint32_t aIndex, bool AttachChild(RemoteAccessible* aParent, uint32_t aIndex,
RemoteAccessible* aChild); RemoteAccessible* aChild);
[[nodiscard]] bool CheckDocTree() const; [[nodiscard]] bool CheckDocTree() const;
xpcAccessibleGeneric* GetXPCAccessible(RemoteAccessible* aProxy); xpcAccessibleGeneric* GetXPCAccessible(RemoteAccessible* aProxy);
/**
* Fire an event to both OS and XPCOM consumers.
*/
void FireEvent(RemoteAccessible* aAcc, const uint32_t& aType); void FireEvent(RemoteAccessible* aAcc, const uint32_t& aType);
/** /**
@ -365,9 +368,10 @@ class DocAccessibleParent : public RemoteAccessible,
uint32_t mPendingShowIndex = 0; uint32_t mPendingShowIndex = 0;
nsTHashSet<uint64_t> mMovingIDs; nsTHashSet<uint64_t> mMovingIDs;
uint64_t mActorID; uint64_t mActorID;
bool mTopLevel; bool mTopLevel : 1;
bool mTopLevelInContentProcess; bool mTopLevelInContentProcess : 1;
bool mShutdown; bool mShutdown : 1;
bool mIsInitialTreeDone : 1;
RefPtr<dom::CanonicalBrowsingContext> mBrowsingContext; RefPtr<dom::CanonicalBrowsingContext> mBrowsingContext;
nsTHashSet<RefPtr<dom::BrowserBridgeParent>> mPendingOOPChildDocs; nsTHashSet<RefPtr<dom::BrowserBridgeParent>> mPendingOOPChildDocs;

View file

@ -11,6 +11,8 @@ support-files = [
["browser_hidden_iframe.js"] ["browser_hidden_iframe.js"]
https_first_disabled = true https_first_disabled = true
["browser_iframe_recreation.js"]
["browser_nested_iframe.js"] ["browser_nested_iframe.js"]
["browser_reframe_root.js"] ["browser_reframe_root.js"]

View file

@ -0,0 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
"use strict";
addAccessibleTask(
`test`,
async function testRecreation(browser, iframeDocAcc, topDocAcc) {
let iframe = findAccessibleChildByID(topDocAcc, DEFAULT_IFRAME_ID);
is(iframeDocAcc.parent, iframe, "iframe doc's parent is iframe");
// The ARIA role currently causes re-creation. If that ever changes, we'll
// need to switch to another technique here.
info("Change iframe's role to recreate it");
let shown = waitForEvent(EVENT_SHOW, DEFAULT_IFRAME_ID);
let reordered = waitForEvent(EVENT_REORDER, DEFAULT_IFRAME_ID);
await SpecialPowers.spawn(
topDocAcc.browsingContext,
[DEFAULT_IFRAME_ID],
id => {
content.document.getElementById(id).role = "foo";
}
);
iframe = (await shown).accessible;
await reordered;
is(iframeDocAcc.parent, iframe, "iframe doc's parent is iframe");
},
{ chrome: false, topLevel: false, iframe: true, remoteIframe: true }
);

View file

@ -1 +1 @@
140.8.0 140.9.0

View file

@ -1 +1 @@
140.8.0esr 140.9.0esr

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.8.0 MOZ_APP_VERSION=140.9.0
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.8.0 140.9.0

View file

@ -1,3 +1,9 @@
icecat (140.9.0-1gnu1+build1-0.12.0) ecne; urgency=medium
* New upstream stable release (icecat-140.9.0-1gnu1)
-- Capitulo Mexicano de Software Libre <devel@cmxsl.org> Sat, 28 Mar 2026 01:16:35 -0600
icecat (140.8.0-2gnu1+build1-0.12.0) ecne; urgency=medium icecat (140.8.0-2gnu1+build1-0.12.0) ecne; urgency=medium
* New upstream stable release (icecat-140.8.0-2gnu1) * New upstream stable release (icecat-140.8.0-2gnu1)

View file

@ -746,7 +746,7 @@ class RequestListContextMenu {
}; };
const options = JSON.stringify(fetchOptions, null, 4); const options = JSON.stringify(fetchOptions, null, 4);
const fetchString = `await fetch("${url}", ${options});`; const fetchString = `await fetch(${JSON.stringify(url)}, ${options});`;
return fetchString; return fetchString;
} }

View file

@ -50,10 +50,11 @@ add_task(async function () {
const types = ["end", "response", "duration", "latency"]; const types = ["end", "response", "duration", "latency"];
for (const t of types) { for (const t of types) {
info("Check the timing column for type: " + t);
await waitUntil(() => { await waitUntil(() => {
const node = item.querySelector(".requests-list-" + t + "-time"); const node = item.querySelector(".requests-list-" + t + "-time");
const value = parseInt(node.textContent, 10); const value = parseInt(node.textContent, 10);
return value > 0; return value >= 0;
}); });
} }

View file

@ -7,7 +7,7 @@
* Tests if Copy as Fetch works. * Tests if Copy as Fetch works.
*/ */
add_task(async function () { add_task(async function testBasicCopyAsFetch() {
const { tab, monitor } = await initNetMonitor(HTTPS_CURL_URL, { const { tab, monitor } = await initNetMonitor(HTTPS_CURL_URL, {
requestCount: 1, requestCount: 1,
}); });
@ -15,7 +15,9 @@ add_task(async function () {
// GET request, no cookies (first request) // GET request, no cookies (first request)
await performRequest("GET"); await performRequest("GET");
await testClipboardContent(`await fetch("https://example.com/browser/devtools/client/netmonitor/test/sjs_simple-test-server.sjs", { await testClipboardContent(
monitor,
`await fetch("https://example.com/browser/devtools/client/netmonitor/test/sjs_simple-test-server.sjs", {
"credentials": "omit", "credentials": "omit",
"headers": { "headers": {
"User-Agent": "${navigator.userAgent}", "User-Agent": "${navigator.userAgent}",
@ -33,7 +35,8 @@ add_task(async function () {
"referrer": "https://example.com/browser/devtools/client/netmonitor/test/html_copy-as-curl.html", "referrer": "https://example.com/browser/devtools/client/netmonitor/test/html_copy-as-curl.html",
"method": "GET", "method": "GET",
"mode": "cors" "mode": "cors"
});`); });`
);
await teardown(monitor); await teardown(monitor);
@ -54,39 +57,71 @@ add_task(async function () {
); );
await waitRequest; await waitRequest;
} }
async function testClipboardContent(expectedResult) {
const { document } = monitor.panelWin;
const items = document.querySelectorAll(".request-list-item");
EventUtils.sendMouseEvent({ type: "mousedown" }, items[items.length - 1]);
EventUtils.sendMouseEvent(
{ type: "contextmenu" },
document.querySelectorAll(".request-list-item")[0]
);
/* Ensure that the copy as fetch option is always visible */
is(
!!getContextMenuItem(monitor, "request-list-context-copy-as-fetch"),
true,
'The "Copy as Fetch" context menu item should not be hidden.'
);
await waitForClipboardPromise(
async function setup() {
await selectContextMenuItem(
monitor,
"request-list-context-copy-as-fetch"
);
},
function validate(result) {
if (typeof result !== "string") {
return false;
}
return expectedResult === result;
}
);
info("Clipboard contains a fetch command for item " + (items.length - 1));
}
}); });
/**
* Tests for Url escaping of copy as Fetch
*/
add_task(async function testUrlEscapeOfCopyAsFetch() {
const { monitor } = await initNetMonitor(HTTPS_CURL_URL, {
requestCount: 1,
});
info("Starting test... ");
const waitRequest = waitForNetworkEvents(monitor, 1);
await SpecialPowers.spawn(
gBrowser.selectedBrowser,
['data:text/html,"+alert(document.domain)+"'],
url => {
content.fetch(url);
}
);
await waitRequest;
await testClipboardContent(
monitor,
`await fetch("data:text/html,\\"+alert(document.domain)+\\"", {
"credentials": "omit",
"headers": {},
"method": "GET",
"mode": "cors"
});`
);
await teardown(monitor);
});
async function testClipboardContent(monitor, expectedResult) {
const { document } = monitor.panelWin;
const items = document.querySelectorAll(".request-list-item");
EventUtils.sendMouseEvent({ type: "mousedown" }, items[items.length - 1]);
EventUtils.sendMouseEvent(
{ type: "contextmenu" },
document.querySelectorAll(".request-list-item")[0]
);
/* Ensure that the copy as fetch option is always visible */
is(
!!getContextMenuItem(monitor, "request-list-context-copy-as-fetch"),
true,
'The "Copy as Fetch" context menu item should not be hidden.'
);
await waitForClipboardPromise(
async function setup() {
await selectContextMenuItem(
monitor,
"request-list-context-copy-as-fetch"
);
},
function validate(result) {
if (typeof result !== "string") {
return false;
}
return expectedResult === result;
}
);
info("Clipboard contains a fetch command for item " + (items.length - 1));
}

View file

@ -10,15 +10,13 @@ const DEFAULT_DPPX = window.devicePixelRatio;
/* eslint-disable max-len */ /* eslint-disable max-len */
const TEST_DEVICE = { const TEST_DEVICE = {
name: "iPhone 6/7/8", name: "iPhone 17 / 17 Pro",
width: 375, width: 402,
height: 667, height: 874,
pixelRatio: 2, pixelRatio: 3,
userAgent: userAgent:
"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1", "Mozilla/5.0 (iPhone; CPU iPhone OS 18_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1",
touch: true, touch: true,
icecatOS: false,
os: "iOS",
featured: true, featured: true,
}; };
/* eslint-enable max-len */ /* eslint-enable max-len */

View file

@ -2786,6 +2786,11 @@ void BrowsingContext::DidSet(FieldIndex<IDX_ExplicitActive>,
}); });
} }
bool BrowsingContext::CanSet(FieldIndex<IDX_InRDMPane>, const bool&,
ContentParent* aSource) {
return XRE_IsParentProcess() && IsTop() && !aSource;
}
void BrowsingContext::DidSet(FieldIndex<IDX_InRDMPane>, bool aOldValue) { void BrowsingContext::DidSet(FieldIndex<IDX_InRDMPane>, bool aOldValue) {
MOZ_ASSERT(IsTop(), MOZ_ASSERT(IsTop(),
"Should only set InRDMPane in the top-level browsing context"); "Should only set InRDMPane in the top-level browsing context");

View file

@ -1136,6 +1136,7 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
return IsTop(); return IsTop();
} }
bool CanSet(FieldIndex<IDX_InRDMPane>, const bool&, ContentParent* aSource);
void DidSet(FieldIndex<IDX_InRDMPane>, bool aOldValue); void DidSet(FieldIndex<IDX_InRDMPane>, bool aOldValue);
MOZ_CAN_RUN_SCRIPT_BOUNDARY void DidSet(FieldIndex<IDX_ForceDesktopViewport>, MOZ_CAN_RUN_SCRIPT_BOUNDARY void DidSet(FieldIndex<IDX_ForceDesktopViewport>,
bool aOldValue); bool aOldValue);

View file

@ -7,6 +7,7 @@
#include "DocumentOrShadowRoot.h" #include "DocumentOrShadowRoot.h"
#include "mozilla/AnimationComparator.h" #include "mozilla/AnimationComparator.h"
#include "mozilla/EventStateManager.h" #include "mozilla/EventStateManager.h"
#include "mozilla/Likely.h"
#include "mozilla/PointerLockManager.h" #include "mozilla/PointerLockManager.h"
#include "mozilla/PresShell.h" #include "mozilla/PresShell.h"
#include "mozilla/StyleSheet.h" #include "mozilla/StyleSheet.h"
@ -112,6 +113,10 @@ void DocumentOrShadowRoot::RemoveSheetFromStylesIfApplicable(
void DocumentOrShadowRoot::OnSetAdoptedStyleSheets(StyleSheet& aSheet, void DocumentOrShadowRoot::OnSetAdoptedStyleSheets(StyleSheet& aSheet,
uint32_t aIndex, uint32_t aIndex,
ErrorResult& aRv) { ErrorResult& aRv) {
if (MOZ_UNLIKELY(aIndex > mAdoptedStyleSheets.Length())) {
MOZ_ASSERT_UNREACHABLE("Out of sync proxy");
return;
}
Document& doc = *AsNode().OwnerDoc(); Document& doc = *AsNode().OwnerDoc();
// 1. If values constructed flag is not set, or its constructor document is // 1. If values constructed flag is not set, or its constructor document is
// not equal to this DocumentOrShadowRoot's node document, throw a // not equal to this DocumentOrShadowRoot's node document, throw a
@ -164,7 +169,10 @@ void DocumentOrShadowRoot::OnSetAdoptedStyleSheets(StyleSheet& aSheet,
void DocumentOrShadowRoot::OnDeleteAdoptedStyleSheets(StyleSheet& aSheet, void DocumentOrShadowRoot::OnDeleteAdoptedStyleSheets(StyleSheet& aSheet,
uint32_t aIndex, uint32_t aIndex,
ErrorResult&) { ErrorResult&) {
MOZ_ASSERT(mAdoptedStyleSheets.ElementAt(aIndex) == &aSheet); if (MOZ_UNLIKELY(mAdoptedStyleSheets.ElementAt(aIndex) != &aSheet)) {
MOZ_ASSERT_UNREACHABLE("Out of sync proxy");
return;
}
mAdoptedStyleSheets.RemoveElementAt(aIndex); mAdoptedStyleSheets.RemoveElementAt(aIndex);
auto existingIndex = mAdoptedStyleSheets.LastIndexOf(&aSheet); auto existingIndex = mAdoptedStyleSheets.LastIndexOf(&aSheet);
if (existingIndex != mAdoptedStyleSheets.NoIndex && existingIndex >= aIndex) { if (existingIndex != mAdoptedStyleSheets.NoIndex && existingIndex >= aIndex) {

View file

@ -271,6 +271,7 @@ template <typename char_type>
const nsTSubstring<char_type>& aMimeType, const nsTSubstring<char_type>& aMimeType,
nsTSubstring<char_type>& aOutEssence, nsTSubstring<char_type>& aOutEssence,
nsTSubstring<char_type>& aOutCharset) { nsTSubstring<char_type>& aOutCharset) {
// https://fetch.spec.whatwg.org/#concept-header-extract-mime-type
static char_type kCHARSET[] = {'c', 'h', 'a', 'r', 's', 'e', 't'}; static char_type kCHARSET[] = {'c', 'h', 'a', 'r', 's', 'e', 't'};
static nsTDependentSubstring<char_type> kCharset(kCHARSET, 7); static nsTDependentSubstring<char_type> kCharset(kCHARSET, 7);
@ -278,8 +279,8 @@ template <typename char_type>
nsTAutoString<char_type> prevContentType; nsTAutoString<char_type> prevContentType;
nsTAutoString<char_type> prevCharset; nsTAutoString<char_type> prevCharset;
prevContentType.Assign(aOutEssence); aOutEssence.Truncate();
prevCharset.Assign(aOutCharset); aOutCharset.Truncate();
nsTArray<nsTDependentSubstring<char_type>> mimeTypeParts = nsTArray<nsTDependentSubstring<char_type>> mimeTypeParts =
SplitMimetype(aMimeType); SplitMimetype(aMimeType);
@ -292,9 +293,7 @@ template <typename char_type>
parsed = Parse(mimeTypeString); parsed = Parse(mimeTypeString);
if (!parsed) { if (!parsed) {
aOutEssence.Truncate(); continue;
aOutCharset.Truncate();
return false;
} }
parsed->GetEssence(aOutEssence); parsed->GetEssence(aOutEssence);
@ -322,6 +321,10 @@ template <typename char_type>
} }
} }
if (aOutEssence.IsEmpty()) {
return false;
}
return true; return true;
} }

View file

@ -44,12 +44,15 @@ class ContentPermissionRequestParent : public PContentPermissionRequestParent {
// @param aIsRequestDelegatedToUnsafeThirdParty see // @param aIsRequestDelegatedToUnsafeThirdParty see
// mIsRequestDelegatedToUnsafeThirdParty. // mIsRequestDelegatedToUnsafeThirdParty.
ContentPermissionRequestParent( ContentPermissionRequestParent(
const nsTArray<PermissionRequest>& aRequests, Element* aElement, Element* aElement, nsIPrincipal* aPrincipal,
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal, nsIPrincipal* aTopLevelPrincipal,
const bool aHasValidTransientUserGestureActivation, const bool aHasValidTransientUserGestureActivation,
const bool aIsRequestDelegatedToUnsafeThirdParty); const bool aIsRequestDelegatedToUnsafeThirdParty);
virtual ~ContentPermissionRequestParent(); virtual ~ContentPermissionRequestParent();
MOZ_CAN_RUN_SCRIPT_BOUNDARY
void Init(nsTArray<PermissionRequest>&& aRequests);
bool IsBeingDestroyed(); bool IsBeingDestroyed();
nsCOMPtr<nsIPrincipal> mPrincipal; nsCOMPtr<nsIPrincipal> mPrincipal;
@ -64,16 +67,13 @@ class ContentPermissionRequestParent : public PContentPermissionRequestParent {
nsTArray<PermissionRequest> mRequests; nsTArray<PermissionRequest> mRequests;
private: private:
// Not MOZ_CAN_RUN_SCRIPT because we can't annotate the thing we override yet.
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual mozilla::ipc::IPCResult Recvprompt() override;
virtual mozilla::ipc::IPCResult RecvDestroy() override; virtual mozilla::ipc::IPCResult RecvDestroy() override;
virtual void ActorDestroy(ActorDestroyReason why) override; virtual void ActorDestroy(ActorDestroyReason why) override;
}; };
ContentPermissionRequestParent::ContentPermissionRequestParent( ContentPermissionRequestParent::ContentPermissionRequestParent(
const nsTArray<PermissionRequest>& aRequests, Element* aElement, Element* aElement, nsIPrincipal* aPrincipal,
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal, nsIPrincipal* aTopLevelPrincipal,
const bool aHasValidTransientUserGestureActivation, const bool aHasValidTransientUserGestureActivation,
const bool aIsRequestDelegatedToUnsafeThirdParty) { const bool aIsRequestDelegatedToUnsafeThirdParty) {
MOZ_COUNT_CTOR(ContentPermissionRequestParent); MOZ_COUNT_CTOR(ContentPermissionRequestParent);
@ -81,7 +81,6 @@ ContentPermissionRequestParent::ContentPermissionRequestParent(
mPrincipal = aPrincipal; mPrincipal = aPrincipal;
mTopLevelPrincipal = aTopLevelPrincipal; mTopLevelPrincipal = aTopLevelPrincipal;
mElement = aElement; mElement = aElement;
mRequests = aRequests.Clone();
mHasValidTransientUserGestureActivation = mHasValidTransientUserGestureActivation =
aHasValidTransientUserGestureActivation; aHasValidTransientUserGestureActivation;
mIsRequestDelegatedToUnsafeThirdParty = aIsRequestDelegatedToUnsafeThirdParty; mIsRequestDelegatedToUnsafeThirdParty = aIsRequestDelegatedToUnsafeThirdParty;
@ -91,13 +90,14 @@ ContentPermissionRequestParent::~ContentPermissionRequestParent() {
MOZ_COUNT_DTOR(ContentPermissionRequestParent); MOZ_COUNT_DTOR(ContentPermissionRequestParent);
} }
mozilla::ipc::IPCResult ContentPermissionRequestParent::Recvprompt() { void ContentPermissionRequestParent::Init(
nsTArray<PermissionRequest>&& aRequests) {
mRequests = std::move(aRequests);
mProxy = new nsContentPermissionRequestProxy(this); mProxy = new nsContentPermissionRequestProxy(this);
if (NS_FAILED(mProxy->Init(mRequests))) { if (NS_FAILED(mProxy->Init(mRequests))) {
RefPtr<nsContentPermissionRequestProxy> proxy(mProxy); RefPtr<nsContentPermissionRequestProxy> proxy(mProxy);
proxy->Cancel(); proxy->Cancel();
} }
return IPC_OK();
} }
mozilla::ipc::IPCResult ContentPermissionRequestParent::RecvDestroy() { mozilla::ipc::IPCResult ContentPermissionRequestParent::RecvDestroy() {
@ -239,12 +239,12 @@ nsresult nsContentPermissionUtils::CreatePermissionArray(
/* static */ /* static */
PContentPermissionRequestParent* PContentPermissionRequestParent*
nsContentPermissionUtils::CreateContentPermissionRequestParent( nsContentPermissionUtils::CreateContentPermissionRequestParent(
const nsTArray<PermissionRequest>& aRequests, Element* aElement, Element* aElement, nsIPrincipal* aPrincipal,
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal, nsIPrincipal* aTopLevelPrincipal,
const bool aHasValidTransientUserGestureActivation, const bool aHasValidTransientUserGestureActivation,
const bool aIsRequestDelegatedToUnsafeThirdParty, const TabId& aTabId) { const bool aIsRequestDelegatedToUnsafeThirdParty, const TabId& aTabId) {
PContentPermissionRequestParent* parent = new ContentPermissionRequestParent( PContentPermissionRequestParent* parent = new ContentPermissionRequestParent(
aRequests, aElement, aPrincipal, aTopLevelPrincipal, aElement, aPrincipal, aTopLevelPrincipal,
aHasValidTransientUserGestureActivation, aHasValidTransientUserGestureActivation,
aIsRequestDelegatedToUnsafeThirdParty); aIsRequestDelegatedToUnsafeThirdParty);
ContentPermissionRequestParentMap()[parent] = aTabId; ContentPermissionRequestParentMap()[parent] = aTabId;
@ -252,6 +252,14 @@ nsContentPermissionUtils::CreateContentPermissionRequestParent(
return parent; return parent;
} }
/* static */
void nsContentPermissionUtils::InitContentPermissionRequestParent(
PContentPermissionRequestParent* aActor,
nsTArray<PermissionRequest>&& aRequests) {
static_cast<ContentPermissionRequestParent*>(aActor)->Init(
std::move(aRequests));
}
/* static */ /* static */
nsresult nsContentPermissionUtils::AskPermission( nsresult nsContentPermissionUtils::AskPermission(
nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow) { nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow) {
@ -301,7 +309,6 @@ nsresult nsContentPermissionUtils::AskPermission(
} }
ContentPermissionRequestChildMap()[req.get()] = child->GetTabId(); ContentPermissionRequestChildMap()[req.get()] = child->GetTabId();
req->Sendprompt();
return NS_OK; return NS_OK;
} }

View file

@ -63,11 +63,15 @@ class nsContentPermissionUtils {
// @param aIsRequestDelegatedToUnsafeThirdParty see // @param aIsRequestDelegatedToUnsafeThirdParty see
// ContentPermissionRequestParent. // ContentPermissionRequestParent.
static PContentPermissionRequestParent* CreateContentPermissionRequestParent( static PContentPermissionRequestParent* CreateContentPermissionRequestParent(
const nsTArray<PermissionRequest>& aRequests, Element* aElement, Element* aElement, nsIPrincipal* aPrincipal,
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal, nsIPrincipal* aTopLevelPrincipal,
const bool aHasValidTransientUserGestureActivation, const bool aHasValidTransientUserGestureActivation,
const bool aIsRequestDelegatedToUnsafeThirdParty, const TabId& aTabId); const bool aIsRequestDelegatedToUnsafeThirdParty, const TabId& aTabId);
static void InitContentPermissionRequestParent(
PContentPermissionRequestParent* aActor,
nsTArray<PermissionRequest>&& aRequests);
static nsresult AskPermission(nsIContentPermissionRequest* aRequest, static nsresult AskPermission(nsIContentPermissionRequest* aRequest,
nsPIDOMWindowInner* aWindow); nsPIDOMWindowInner* aWindow);

View file

@ -821,8 +821,8 @@ TEST(MimeTypeParsing, contentTypes1)
bool parsed = CMimeType::Parse(val, contentType, contentCharset); bool parsed = CMimeType::Parse(val, contentType, contentCharset);
ASSERT_FALSE(parsed); ASSERT_TRUE(parsed);
ASSERT_TRUE(contentType.EqualsLiteral("")); ASSERT_TRUE(contentType.EqualsLiteral("text/plain"));
ASSERT_TRUE(contentCharset.EqualsLiteral("")); ASSERT_TRUE(contentCharset.EqualsLiteral(""));
} }
@ -1074,3 +1074,43 @@ TEST(MimeTypeParsing, contentTypes20)
ASSERT_TRUE(contentType.EqualsLiteral("text/plain")); ASSERT_TRUE(contentType.EqualsLiteral("text/plain"));
ASSERT_TRUE(contentCharset.EqualsLiteral("")); ASSERT_TRUE(contentCharset.EqualsLiteral(""));
} }
// U+002F(/) is not a valid HTTP token code point
// https://mimesniff.spec.whatwg.org/#http-token-code-point
TEST(MimeTypeParsing, invalidSubtype1)
{
const nsAutoCString val("text/json/");
RefPtr<CMimeType> parsed = CMimeType::Parse(val);
ASSERT_TRUE(!parsed);
}
TEST(MimeTypeParsing, invalidSubtype2)
{
const nsAutoCString val("text/json/bad");
RefPtr<CMimeType> parsed = CMimeType::Parse(val);
ASSERT_TRUE(!parsed);
}
TEST(MimeTypeParsing, EmptyParsing)
{
constexpr nsLiteralCString val("");
nsCString contentType;
nsCString contentCharset;
bool parsed = CMimeType::Parse(val, contentType, contentCharset);
ASSERT_FALSE(parsed);
ASSERT_TRUE(contentType.EqualsLiteral(""));
ASSERT_TRUE(contentCharset.EqualsLiteral(""));
}
TEST(MimeTypeParsing, EmptySubtype)
{
constexpr nsLiteralCString val("audio/");
nsCString contentType;
nsCString contentCharset;
bool parsed = CMimeType::Parse(val, contentType, contentCharset);
ASSERT_FALSE(parsed);
ASSERT_TRUE(contentType.EqualsLiteral(""));
ASSERT_TRUE(contentCharset.EqualsLiteral(""));
}

View file

@ -259,39 +259,6 @@ nsTArray<nsCString>& TErrorResult<CleanupPolicy>::CreateErrorMessageHelper(
return message->mArgs; return message->mArgs;
} }
template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::SerializeMessage(
IPC::MessageWriter* aWriter) const {
using namespace IPC;
AssertInOwningThread();
MOZ_ASSERT(mUnionState == HasMessage);
MOZ_ASSERT(mExtra.mMessage);
WriteParam(aWriter, mExtra.mMessage->mArgs);
WriteParam(aWriter, mExtra.mMessage->mErrorNumber);
}
template <typename CleanupPolicy>
bool TErrorResult<CleanupPolicy>::DeserializeMessage(
IPC::MessageReader* aReader) {
using namespace IPC;
AssertInOwningThread();
auto readMessage = MakeUnique<Message>();
if (!ReadParam(aReader, &readMessage->mArgs) ||
!ReadParam(aReader, &readMessage->mErrorNumber)) {
return false;
}
if (!readMessage->HasCorrectNumberOfArguments()) {
return false;
}
MOZ_ASSERT(mUnionState == HasNothing);
InitMessage(readMessage.release());
#ifdef DEBUG
mUnionState = HasMessage;
#endif // DEBUG
return true;
}
template <typename CleanupPolicy> template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::SetPendingExceptionWithMessage( void TErrorResult<CleanupPolicy>::SetPendingExceptionWithMessage(
JSContext* aCx, const char* context) { JSContext* aCx, const char* context) {
@ -401,34 +368,106 @@ struct TErrorResult<CleanupPolicy>::DOMExceptionInfo {
}; };
template <typename CleanupPolicy> template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::SerializeDOMExceptionInfo( void TErrorResult<CleanupPolicy>::SerializeErrorResult(
IPC::MessageWriter* aWriter) const { IPC::MessageWriter* aWriter) const {
using namespace IPC; using namespace IPC;
AssertInOwningThread(); AssertInOwningThread();
MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
MOZ_ASSERT(mExtra.mDOMExceptionInfo); // It should be the case that mMightHaveUnreportedJSException can only be
WriteParam(aWriter, mExtra.mDOMExceptionInfo->mMessage); // true when we're expecting a JS exception. We cannot send such messages
WriteParam(aWriter, mExtra.mDOMExceptionInfo->mRv); // over the IPC channel since there is no sane way of transferring the JS
// value over to the other side. Callers should never do that.
MOZ_ASSERT(!mMightHaveUnreportedJSException);
if (IsJSException() || IsJSContextException()) {
MOZ_CRASH(
"Cannot serialize an ErrorResult representing a Javascript exception");
}
WriteParam(aWriter, mResult);
if (IsErrorWithMessage()) {
MOZ_ASSERT(mResult == NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR ||
mResult == NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR);
MOZ_ASSERT(mUnionState == HasMessage);
MOZ_ASSERT(mExtra.mMessage);
WriteParam(aWriter, mExtra.mMessage->mArgs);
WriteParam(aWriter, mExtra.mMessage->mErrorNumber);
} else if (IsDOMException()) {
MOZ_ASSERT(mResult == NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION);
MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
MOZ_ASSERT(mExtra.mDOMExceptionInfo);
WriteParam(aWriter, mExtra.mDOMExceptionInfo->mMessage);
WriteParam(aWriter, mExtra.mDOMExceptionInfo->mRv);
} else {
MOZ_ASSERT(mUnionState == HasNothing);
}
} }
template <typename CleanupPolicy> template <typename CleanupPolicy>
bool TErrorResult<CleanupPolicy>::DeserializeDOMExceptionInfo( bool TErrorResult<CleanupPolicy>::DeserializeErrorResult(
IPC::MessageReader* aReader) { IPC::MessageReader* aReader) {
using namespace IPC; using namespace IPC;
AssertInOwningThread(); AssertInOwningThread();
nsCString message;
nsresult rv; nsresult result;
if (!ReadParam(aReader, &message) || !ReadParam(aReader, &rv)) { if (!ReadParam(aReader, &result)) {
return false; return false;
} }
MOZ_ASSERT(mUnionState == HasNothing); switch (result) {
MOZ_ASSERT(IsDOMException()); case NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION:
InitDOMExceptionInfo(new DOMExceptionInfo(rv, message)); case NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT:
// JS exceptions can not be serialized.
return false;
case NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR:
case NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR: {
nsTArray<nsCString> args;
dom::ErrNum errorNumber;
if (!ReadParam(aReader, &args) || !ReadParam(aReader, &errorNumber)) {
return false;
}
if (GetErrorArgCount(errorNumber) != args.Length()) {
return false;
}
for (nsCString& arg : args) {
if (Utf8ValidUpTo(arg) != arg.Length()) {
return false;
}
}
ClearUnionData();
nsTArray<nsCString>& messageArgsArray =
CreateErrorMessageHelper(errorNumber, result);
messageArgsArray = std::move(args);
MOZ_ASSERT(mExtra.mMessage->HasCorrectNumberOfArguments(),
"validated earlier");
#ifdef DEBUG #ifdef DEBUG
mUnionState = HasDOMExceptionInfo; mUnionState = HasMessage;
#endif // DEBUG #endif
return true; return true;
}
case NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION: {
nsCString message;
nsresult rv;
if (!ReadParam(aReader, &message) || !ReadParam(aReader, &rv)) {
return false;
}
ThrowDOMException(rv, message);
return true;
}
default:
ClearUnionData();
AssignErrorCode(result);
return true;
}
} }
template <typename CleanupPolicy> template <typename CleanupPolicy>

View file

@ -7,11 +7,8 @@
#ifndef IPC_ErrorIPCUtils_h #ifndef IPC_ErrorIPCUtils_h
#define IPC_ErrorIPCUtils_h #define IPC_ErrorIPCUtils_h
#include <utility>
#include "ipc/EnumSerializer.h" #include "ipc/EnumSerializer.h"
#include "ipc/IPCMessageUtils.h" #include "ipc/IPCMessageUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/ErrorResult.h" #include "mozilla/ErrorResult.h"
namespace IPC { namespace IPC {
@ -24,64 +21,18 @@ struct ParamTraits<mozilla::dom::ErrNum>
template <> template <>
struct ParamTraits<mozilla::ErrorResult> { struct ParamTraits<mozilla::ErrorResult> {
typedef mozilla::ErrorResult paramType; static void Write(MessageWriter* aWriter,
const mozilla::ErrorResult& aParam) {
static void Write(MessageWriter* aWriter, const paramType& aParam) { aParam.SerializeErrorResult(aWriter);
// It should be the case that mMightHaveUnreportedJSException can only be
// true when we're expecting a JS exception. We cannot send such messages
// over the IPC channel since there is no sane way of transferring the JS
// value over to the other side. Callers should never do that.
MOZ_ASSERT_IF(aParam.IsJSException(),
aParam.mMightHaveUnreportedJSException);
if (aParam.IsJSException()
#ifdef DEBUG
|| aParam.mMightHaveUnreportedJSException
#endif
) {
MOZ_CRASH(
"Cannot encode an ErrorResult representing a Javascript exception");
}
WriteParam(aWriter, aParam.mResult);
WriteParam(aWriter, aParam.IsErrorWithMessage());
WriteParam(aWriter, aParam.IsDOMException());
if (aParam.IsErrorWithMessage()) {
aParam.SerializeMessage(aWriter);
} else if (aParam.IsDOMException()) {
aParam.SerializeDOMExceptionInfo(aWriter);
}
} }
static void Write(MessageWriter* aWriter, paramType&& aParam) { static void Write(MessageWriter* aWriter, mozilla::ErrorResult&& aParam) {
Write(aWriter, static_cast<const paramType&>(aParam)); aParam.SerializeErrorResult(aWriter);
aParam.SuppressException(); aParam.SuppressException();
} }
static bool Read(MessageReader* aReader, paramType* aResult) { static bool Read(MessageReader* aReader, mozilla::ErrorResult* aResult) {
paramType readValue; return aResult->DeserializeErrorResult(aReader);
if (!ReadParam(aReader, &readValue.mResult)) {
return false;
}
bool hasMessage = false;
if (!ReadParam(aReader, &hasMessage)) {
return false;
}
bool hasDOMExceptionInfo = false;
if (!ReadParam(aReader, &hasDOMExceptionInfo)) {
return false;
}
if (hasMessage && hasDOMExceptionInfo) {
// Shouldn't have both!
return false;
}
if (hasMessage && !readValue.DeserializeMessage(aReader)) {
return false;
} else if (hasDOMExceptionInfo &&
!readValue.DeserializeDOMExceptionInfo(aReader)) {
return false;
}
*aResult = std::move(readValue);
return true;
} }
}; };
@ -90,14 +41,11 @@ struct ParamTraits<mozilla::CopyableErrorResult> {
typedef mozilla::CopyableErrorResult paramType; typedef mozilla::CopyableErrorResult paramType;
static void Write(MessageWriter* aWriter, const paramType& aParam) { static void Write(MessageWriter* aWriter, const paramType& aParam) {
ParamTraits<mozilla::ErrorResult>::Write(aWriter, aParam); aParam.SerializeErrorResult(aWriter);
} }
static bool Read(MessageReader* aReader, paramType* aResult) { static bool Read(MessageReader* aReader, paramType* aResult) {
// We can't cast *aResult to ErrorResult&, so cheat and just cast return aResult->DeserializeErrorResult(aReader);
// to ErrorResult*.
return ParamTraits<mozilla::ErrorResult>::Read(
aReader, reinterpret_cast<mozilla::ErrorResult*>(aResult));
} }
}; };

View file

@ -401,7 +401,7 @@ class TErrorResult {
// Check whether the TErrorResult says to just throw whatever is on // Check whether the TErrorResult says to just throw whatever is on
// the JSContext already. // the JSContext already.
bool IsJSContextException() { bool IsJSContextException() const {
return ErrorCode() == NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT; return ErrorCode() == NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT;
} }
@ -481,11 +481,10 @@ class TErrorResult {
friend struct IPC::ParamTraits<TErrorResult>; friend struct IPC::ParamTraits<TErrorResult>;
friend struct IPC::ParamTraits<ErrorResult>; friend struct IPC::ParamTraits<ErrorResult>;
void SerializeMessage(IPC::MessageWriter* aWriter) const; friend struct IPC::ParamTraits<CopyableErrorResult>;
bool DeserializeMessage(IPC::MessageReader* aReader);
void SerializeDOMExceptionInfo(IPC::MessageWriter* aWriter) const; void SerializeErrorResult(IPC::MessageWriter* aWriter) const;
bool DeserializeDOMExceptionInfo(IPC::MessageReader* aReader); bool DeserializeErrorResult(IPC::MessageReader* aReader);
// Helper method that creates a new Message for this TErrorResult, // Helper method that creates a new Message for this TErrorResult,
// and returns the arguments array from that Message. // and returns the arguments array from that Message.

View file

@ -277,6 +277,9 @@ bool ObservableArrayProxyHandler::GetBackingListObject(
if (NS_WARN_IF(!newBackingListObj)) { if (NS_WARN_IF(!newBackingListObj)) {
return false; return false;
} }
if (NS_WARN_IF(!JS_SetPrototype(aCx, newBackingListObj, nullptr))) {
return false;
}
slotValue = JS::ObjectValue(*newBackingListObj); slotValue = JS::ObjectValue(*newBackingListObj);
js::SetProxyReservedSlot(aProxy, OBSERVABLE_ARRAY_BACKING_LIST_OBJECT_SLOT, js::SetProxyReservedSlot(aProxy, OBSERVABLE_ARRAY_BACKING_LIST_OBJECT_SLOT,
slotValue); slotValue);

View file

@ -1110,10 +1110,6 @@ bool TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec,
//// ////
const auto surfSize = surf->GetSize(); const auto surfSize = surf->GetSize();
if (uint32_t(surfSize.width) < size.x || uint32_t(surfSize.height) < size.y) {
gfxCriticalError() << "Source surface size too small for upload.";
return false;
}
WebGLTexelFormat srcFormat; WebGLTexelFormat srcFormat;
uint8_t srcBPP; uint8_t srcBPP;
@ -1165,6 +1161,12 @@ bool TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec,
const auto& dstUnpacking = dstUnpackingRes.inspect(); const auto& dstUnpacking = dstUnpackingRes.inspect();
MOZ_ASSERT(dstUnpacking.metrics.bytesPerRowStride == dstStride); MOZ_ASSERT(dstUnpacking.metrics.bytesPerRowStride == dstStride);
if (uint32_t(surfSize.width) < dstUnpacking.metrics.usedPixelsPerRow ||
uint32_t(surfSize.height) < dstUnpacking.metrics.totalRows) {
gfxCriticalError() << "Source surface size too small for upload.";
return false;
}
// - // -
const uint8_t* dstBegin = srcBegin; const uint8_t* dstBegin = srcBegin;

View file

@ -418,6 +418,11 @@ GlobalKeyListener::WalkHandlersResult GlobalKeyListener::WalkHandlersAndExecute(
bool GlobalKeyListener::IsReservedKey(WidgetKeyboardEvent* aKeyEvent, bool GlobalKeyListener::IsReservedKey(WidgetKeyboardEvent* aKeyEvent,
KeyEventHandler* aHandler) { KeyEventHandler* aHandler) {
// If the event is a reply event, it means that we've already sent the event
// to the remote process because of not reserved.
if (aKeyEvent->IsHandledInRemoteProcess()) {
return false;
}
ReservedKey reserved = aHandler->GetIsReserved(); ReservedKey reserved = aHandler->GetIsReserved();
// reserved="true" means that the key is always reserved. reserved="false" // reserved="true" means that the key is always reserved. reserved="false"
// means that the key is never reserved. Otherwise, we check site-specific // means that the key is never reserved. Otherwise, we check site-specific

View file

@ -18723,6 +18723,14 @@ mozilla::ipc::IPCResult NormalTransactionOp::RecvContinue(
const PreprocessResponse& aResponse) { const PreprocessResponse& aResponse) {
AssertIsOnOwningThread(); AssertIsOnOwningThread();
// mWaitingForContinue is only touched on the owning thread. If it is not
// set, either we never sent Preprocess (child is misbehaving) or the op is
// still running on the connection thread. Calling NoteContinueReceived()
// in either case would race Cleanup() with DoDatabaseWork().
if (NS_WARN_IF(!IsWaitingForContinue())) {
return IPC_FAIL(this, "Continue received when not waiting for continue");
}
switch (aResponse.type()) { switch (aResponse.type()) {
case PreprocessResponse::Tnsresult: case PreprocessResponse::Tnsresult:
SetFailureCode(aResponse.get_nsresult()); SetFailureCode(aResponse.get_nsresult());

View file

@ -280,10 +280,13 @@ IPCResult BrowserBridgeParent::RecvSetEmbedderAccessible(
# if defined(ANDROID) # if defined(ANDROID)
MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor());
# endif # endif
MOZ_ASSERT(aDoc || mEmbedderAccessibleDoc, if (!aDoc && !mEmbedderAccessibleDoc) {
"Embedder doc shouldn't be cleared if it wasn't set"); return IPC_FAIL(this, "Embedder doc shouldn't be cleared if it wasn't set");
MOZ_ASSERT(!mEmbedderAccessibleDoc || !aDoc || mEmbedderAccessibleDoc == aDoc, }
"Embedder doc shouldn't change from one doc to another"); if (mEmbedderAccessibleDoc && aDoc && mEmbedderAccessibleDoc != aDoc) {
return IPC_FAIL(this,
"Embedder doc shouldn't change from one doc to another");
}
if (!aDoc && mEmbedderAccessibleDoc && if (!aDoc && mEmbedderAccessibleDoc &&
!mEmbedderAccessibleDoc->IsShutdown()) { !mEmbedderAccessibleDoc->IsShutdown()) {
// We're clearing the embedder doc, so remove the pending child doc addition // We're clearing the embedder doc, so remove the pending child doc addition
@ -293,14 +296,22 @@ IPCResult BrowserBridgeParent::RecvSetEmbedderAccessible(
mEmbedderAccessibleDoc = static_cast<a11y::DocAccessibleParent*>(aDoc); mEmbedderAccessibleDoc = static_cast<a11y::DocAccessibleParent*>(aDoc);
mEmbedderAccessibleID = aID; mEmbedderAccessibleID = aID;
if (!aDoc) { if (!aDoc) {
MOZ_ASSERT(!aID); if (aID) {
return IPC_FAIL(this, "Attempt to clear embedder but id given");
}
return IPC_OK(); return IPC_OK();
} }
MOZ_ASSERT(aID); if (!aID) {
return IPC_FAIL(this, "Attempt to set embedder without id");
}
if (GetDocAccessibleParent()) { if (GetDocAccessibleParent()) {
// The embedded DocAccessibleParent has already been created. This can // The embedded DocAccessibleParent has already been created. This can
// happen if, for example, an iframe is hidden and then shown or // happen if, for example, an iframe is hidden and then shown or an iframe
// an iframe is reflowed by layout. // Accessible is re-created. In the case of re-creation, the old iframe
// Accessible still exists at this point because this IPDL message is
// received *before* we receive the accessibility hide and show events. This
// is okay; DocAccessibleParent will store this as a pending OOP child
// document and add it when the new OuterDocAccessible arrives.
mEmbedderAccessibleDoc->AddChildDoc(this); mEmbedderAccessibleDoc->AddChildDoc(this);
} }
return IPC_OK(); return IPC_OK();

View file

@ -2440,6 +2440,10 @@ mozilla::ipc::IPCResult BrowserChild::RecvRealKeyEvent(
// we need to clear the flag explicitly here because ParamTraits should // we need to clear the flag explicitly here because ParamTraits should
// keep checking the flag for avoiding regression. // keep checking the flag for avoiding regression.
localEvent.mFlags.mNoRemoteProcessDispatch = false; localEvent.mFlags.mNoRemoteProcessDispatch = false;
// The parent process won't use the native key bindings of the reply event
// anymore. To save the IPC cost, let's clear the edit commands before sending
// the event back to the parent process.
localEvent.PreventNativeKeyBindings();
SendReplyKeyEvent(localEvent, aUUID); SendReplyKeyEvent(localEvent, aUUID);
return IPC_OK(); return IPC_OK();

View file

@ -1291,6 +1291,14 @@ mozilla::ipc::IPCResult BrowserParent::RecvPDocAccessibleConstructor(
return IPC_OK(); return IPC_OK();
} }
if (auto* prevTopLevel = GetTopLevelDocAccessible()) {
// Sometimes, we can get a new top level DocAccessibleParent before the
// old one gets destroyed. The old one will die pretty shortly anyway,
// so just destroy it now. If we don't do this, GetTopLevelDocAccessible()
// might return the wrong document for a short while.
prevTopLevel->Destroy();
}
if (aBrowsingContext) { if (aBrowsingContext) {
doc->SetBrowsingContext(aBrowsingContext.get_canonical()); doc->SetBrowsingContext(aBrowsingContext.get_canonical());
} }
@ -1325,13 +1333,6 @@ mozilla::ipc::IPCResult BrowserParent::RecvPDocAccessibleConstructor(
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
if (auto* prevTopLevel = GetTopLevelDocAccessible()) {
// Sometimes, we can get a new top level DocAccessibleParent before the
// old one gets destroyed. The old one will die pretty shortly anyway,
// so just destroy it now. If we don't do this, GetTopLevelDocAccessible()
// might return the wrong document for a short while.
prevTopLevel->Destroy();
}
doc->SetTopLevel(); doc->SetTopLevel();
a11y::DocManager::RemoteDocAdded(doc); a11y::DocManager::RemoteDocAdded(doc);
# ifdef XP_WIN # ifdef XP_WIN
@ -2782,7 +2783,10 @@ mozilla::ipc::IPCResult BrowserParent::RecvReplyKeyEvent(
NS_WARN_IF(data.mPseudoCharCode != aEvent.mPseudoCharCode) || NS_WARN_IF(data.mPseudoCharCode != aEvent.mPseudoCharCode) ||
NS_WARN_IF(data.mKeyNameIndex != aEvent.mKeyNameIndex) || NS_WARN_IF(data.mKeyNameIndex != aEvent.mKeyNameIndex) ||
NS_WARN_IF(data.mCodeNameIndex != aEvent.mCodeNameIndex) || NS_WARN_IF(data.mCodeNameIndex != aEvent.mCodeNameIndex) ||
NS_WARN_IF(data.mModifiers != aEvent.mModifiers)) { NS_WARN_IF(data.mModifiers != aEvent.mModifiers) ||
// The child process should've already cleared the editor commands
// because we don't use them.
NS_WARN_IF(aEvent.HasEditCommands())) {
// Got different event data from what we stored before dispatching an // Got different event data from what we stored before dispatching an
// event with the ID. // event with the ID.
return Nothing(); return Nothing();
@ -3909,13 +3913,18 @@ mozilla::ipc::IPCResult BrowserParent::RecvInvokeDragSession(
cookieJarSettings, aSourceWindowContext.GetMaybeDiscarded(), cookieJarSettings, aSourceWindowContext.GetMaybeDiscarded(),
aSourceTopWindowContext.GetMaybeDiscarded()); aSourceTopWindowContext.GetMaybeDiscarded());
if (aVisualDnDData) { if (aVisualDnDData && aDragRect.width >= 0 && aDragRect.height >= 0) {
const auto checkedSize = CheckedInt<size_t>(aDragRect.height) * aStride; const auto checkedSize = CheckedInt<int32_t>(aDragRect.height) * aStride;
if (checkedSize.isValid() && const auto computedStride =
aVisualDnDData->Size() >= checkedSize.value()) { CheckedInt<int32_t>(aDragRect.width) * gfx::BytesPerPixel(aFormat);
const auto checkedStride = CheckedInt<int32_t>(aStride);
if (checkedSize.isValid() && checkedSize.value() >= 0 &&
aVisualDnDData->Size() >= static_cast<size_t>(checkedSize.value()) &&
computedStride.isValid() && checkedStride.isValid() &&
computedStride.value() <= checkedStride.value()) {
dragStartData->SetVisualization(gfx::CreateDataSourceSurfaceFromData( dragStartData->SetVisualization(gfx::CreateDataSourceSurfaceFromData(
gfx::IntSize(aDragRect.width, aDragRect.height), aFormat, gfx::IntSize(aDragRect.width, aDragRect.height), aFormat,
aVisualDnDData->Data(), aStride)); aVisualDnDData->Data(), checkedStride.value()));
} }
} }

View file

@ -5130,8 +5130,18 @@ ContentParent::AllocPContentPermissionRequestParent(
topPrincipal = principal; topPrincipal = principal;
} }
return nsContentPermissionUtils::CreateContentPermissionRequestParent( return nsContentPermissionUtils::CreateContentPermissionRequestParent(
aRequests, tp->GetOwnerElement(), aPrincipal, topPrincipal, tp->GetOwnerElement(), aPrincipal, topPrincipal, aIsHandlingUserInput,
aIsHandlingUserInput, aMaybeUnsafePermissionDelegate, aTabId); aMaybeUnsafePermissionDelegate, aTabId);
}
mozilla::ipc::IPCResult ContentParent::RecvPContentPermissionRequestConstructor(
PContentPermissionRequestParent* aActor,
nsTArray<PermissionRequest>&& aRequests, nsIPrincipal* aPrincipal,
nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput,
const bool& aMaybeUnsafePermissionDelegate, const TabId& tabId) {
nsContentPermissionUtils::InitContentPermissionRequestParent(
aActor, std::move(aRequests));
return IPC_OK();
} }
bool ContentParent::DeallocPContentPermissionRequestParent( bool ContentParent::DeallocPContentPermissionRequestParent(

View file

@ -538,6 +538,12 @@ class ContentParent final : public PContentParent,
nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput, nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput,
const bool& aMaybeUnsafePermissionDelegate, const TabId& aTabId); const bool& aMaybeUnsafePermissionDelegate, const TabId& aTabId);
mozilla::ipc::IPCResult RecvPContentPermissionRequestConstructor(
PContentPermissionRequestParent* aActor,
nsTArray<PermissionRequest>&& aRequests, nsIPrincipal* aPrincipal,
nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput,
const bool& aMaybeUnsafePermissionDelegate, const TabId& tabId) override;
bool DeallocPContentPermissionRequestParent( bool DeallocPContentPermissionRequestParent(
PContentPermissionRequestParent* actor); PContentPermissionRequestParent* actor);

View file

@ -16,7 +16,6 @@ protocol PContentPermissionRequest
manager PContent; manager PContent;
parent: parent:
async prompt();
async Destroy(); async Destroy();
child: child:

View file

@ -6595,6 +6595,12 @@ mozilla::ipc::IPCResult LSRequestBase::RecvCancel() {
mozilla::ipc::IPCResult LSRequestBase::RecvFinish() { mozilla::ipc::IPCResult LSRequestBase::RecvFinish() {
AssertIsOnOwningThread(); AssertIsOnOwningThread();
// A well-behaved content process only sends Finish() after receiving Ready(),
// which transitions us to WaitingForFinish.
if (NS_WARN_IF(mState != State::WaitingForFinish)) {
return IPC_FAIL(this, "Finish received in unexpected state");
}
Finish(); Finish();
return IPC_OK(); return IPC_OK();

View file

@ -10,6 +10,7 @@
#include "MediaInfo.h" #include "MediaInfo.h"
#include "MediaResult.h" #include "MediaResult.h"
#include "PerformanceRecorder.h" #include "PerformanceRecorder.h"
#include "PlatformDecoderModule.h"
#include "VideoUtils.h" #include "VideoUtils.h"
#include "YCbCrUtils.h" #include "YCbCrUtils.h"
#include "mozilla/gfx/gfxVars.h" #include "mozilla/gfx/gfxVars.h"
@ -28,8 +29,12 @@
# include "mozilla/gfx/gfxVars.h" # include "mozilla/gfx/gfxVars.h"
#endif #endif
#define LOG(level, msg, ...) \
MOZ_LOG_FMT(sPDMLog, level, "%s: " msg, __func__, ##__VA_ARGS__)
namespace mozilla { namespace mozilla {
extern LazyLogModule sPDMLog;
using namespace mozilla::gfx; using namespace mozilla::gfx;
using layers::PlanarYCbCrData; using layers::PlanarYCbCrData;
using layers::PlanarYCbCrImage; using layers::PlanarYCbCrImage;
@ -186,6 +191,19 @@ static bool ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane) {
static MediaResult ValidateBufferAndPicture( static MediaResult ValidateBufferAndPicture(
const VideoData::YCbCrBuffer& aBuffer, const IntRect& aPicture) { const VideoData::YCbCrBuffer& aBuffer, const IntRect& aPicture) {
// mChromaSubsampling describes the relationship between plane sizes.
if (aBuffer.mChromaSubsampling == ChromaSubsampling::FULL) {
MOZ_ASSERT(aBuffer.mPlanes[1].mWidth == aBuffer.mPlanes[0].mWidth);
} else {
MOZ_ASSERT(aBuffer.mPlanes[1].mWidth ==
(aBuffer.mPlanes[0].mWidth + 1) / 2);
}
if (aBuffer.mChromaSubsampling == ChromaSubsampling::HALF_WIDTH_AND_HEIGHT) {
MOZ_ASSERT(aBuffer.mPlanes[1].mHeight ==
(aBuffer.mPlanes[0].mHeight + 1) / 2);
} else {
MOZ_ASSERT(aBuffer.mPlanes[1].mHeight == aBuffer.mPlanes[0].mHeight);
}
// The following situation should never happen unless there is a bug // The following situation should never happen unless there is a bug
// in the decoder // in the decoder
if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth || if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth ||
@ -193,7 +211,6 @@ static MediaResult ValidateBufferAndPicture(
return MediaResult(NS_ERROR_INVALID_ARG, return MediaResult(NS_ERROR_INVALID_ARG,
"Chroma planes with different sizes"); "Chroma planes with different sizes");
} }
// The following situations could be triggered by invalid input // The following situations could be triggered by invalid input
if (aPicture.width <= 0 || aPicture.height <= 0) { if (aPicture.width <= 0 || aPicture.height <= 0) {
return MediaResult(NS_ERROR_INVALID_ARG, "Empty picture rect"); return MediaResult(NS_ERROR_INVALID_ARG, "Empty picture rect");
@ -203,7 +220,12 @@ static MediaResult ValidateBufferAndPicture(
!ValidatePlane(aBuffer.mPlanes[2])) { !ValidatePlane(aBuffer.mPlanes[2])) {
return MediaResult(NS_ERROR_INVALID_ARG, "Invalid plane size"); return MediaResult(NS_ERROR_INVALID_ARG, "Invalid plane size");
} }
// ConstructPlanarYCbCrData() and ConvertI420AlphaToARGB() assume Chroma
// planes have equal strides.
if (aBuffer.mPlanes[1].mStride != aBuffer.mPlanes[2].mStride) {
return MediaResult(NS_ERROR_INVALID_ARG,
"Chroma planes with different strides");
}
// Ensure the picture size specified in the headers can be extracted out of // Ensure the picture size specified in the headers can be extracted out of
// the frame we've been supplied without indexing out of bounds. // the frame we've been supplied without indexing out of bounds.
CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width); CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
@ -291,6 +313,7 @@ PlanarYCbCrData ConstructPlanarYCbCrData(const VideoInfo& aInfo,
data.mYSkip = AssertedCast<int32_t>(Y.mSkip); data.mYSkip = AssertedCast<int32_t>(Y.mSkip);
data.mCbChannel = Cb.mData; data.mCbChannel = Cb.mData;
data.mCrChannel = Cr.mData; data.mCrChannel = Cr.mData;
MOZ_ASSERT(Cb.mStride == Cr.mStride);
data.mCbCrStride = AssertedCast<int32_t>(Cb.mStride); data.mCbCrStride = AssertedCast<int32_t>(Cb.mStride);
data.mCbSkip = AssertedCast<int32_t>(Cb.mSkip); data.mCbSkip = AssertedCast<int32_t>(Cb.mSkip);
data.mCrSkip = AssertedCast<int32_t>(Cr.mSkip); data.mCrSkip = AssertedCast<int32_t>(Cr.mSkip);
@ -409,6 +432,22 @@ already_AddRefed<VideoData> VideoData::CreateAndCopyData(
NS_ERROR(r.Message().get()); NS_ERROR(r.Message().get());
return nullptr; return nullptr;
} }
if (!ValidatePlane(aAlphaPlane)) {
MOZ_LOG_FMT(sPDMLog, LogLevel::Warning, "Invalid alpha plane");
return nullptr;
}
// The alpha plane is expected to be the same size as the luma plane.
// See Method 1 at https://wiki.webmproject.org/alpha-channel
if (aBuffer.mPlanes[0].mWidth != aAlphaPlane.mWidth ||
aBuffer.mPlanes[0].mHeight != aAlphaPlane.mHeight) {
MOZ_LOG_FMT(sPDMLog, LogLevel::Warning, "luma and alpha sizes differ");
return nullptr;
}
// ConvertI420AlphaToARGB() expects equal strides for luma and alpha
if (aBuffer.mPlanes[0].mStride != aAlphaPlane.mStride) {
MOZ_LOG_FMT(sPDMLog, LogLevel::Warning, "luma and alpha strides differ");
return nullptr;
}
RefPtr<VideoData> v(new VideoData(aOffset, aTime, aDuration, aKeyframe, RefPtr<VideoData> v(new VideoData(aOffset, aTime, aDuration, aKeyframe,
aTimecode, aInfo.mDisplay, 0)); aTimecode, aInfo.mDisplay, 0));
@ -635,3 +674,4 @@ CryptoScheme StringToCryptoScheme(const nsAString& aString) {
} }
} // namespace mozilla } // namespace mozilla
#undef LOG

View file

@ -129,6 +129,10 @@ class FakeVideoEncoder : public GMPVideoEncoder {
GMPVideoEncoderCallback* callback, int32_t numberOfCores, GMPVideoEncoderCallback* callback, int32_t numberOfCores,
uint32_t maxPayloadSize) override { uint32_t maxPayloadSize) override {
callback_ = callback; callback_ = callback;
constexpr uint16_t kFrameDropCadence = 5;
frame_drop_cadence_ =
codecSettings.mFrameDroppingOn ? kFrameDropCadence : 0;
num_frames_since_drop_ = 0;
frame_size_ = (maxPayloadSize > 0 && maxPayloadSize < BIG_FRAME) frame_size_ = (maxPayloadSize > 0 && maxPayloadSize < BIG_FRAME)
? maxPayloadSize ? maxPayloadSize
: BIG_FRAME; : BIG_FRAME;
@ -146,6 +150,18 @@ class FakeVideoEncoder : public GMPVideoEncoder {
void SendFrame(GMPVideoi420Frame* inputImage, GMPVideoFrameType frame_type, void SendFrame(GMPVideoi420Frame* inputImage, GMPVideoFrameType frame_type,
int nal_type) { int nal_type) {
if (frame_drop_cadence_ > 0) {
const auto frame_drop_id = ++num_frames_since_drop_;
num_frames_since_drop_ %= frame_drop_cadence_;
GMPLOG(GL_DEBUG,
"Frame dropping is on, id="
<< frame_drop_id << "/" << frame_drop_cadence_ << ". "
<< (frame_drop_id == frame_drop_cadence_ ? "Dropping"
: "Keeping"));
if (frame_drop_id == frame_drop_cadence_) {
return;
}
}
// Encode this in a frame that looks a little bit like H.264. // Encode this in a frame that looks a little bit like H.264.
// Send SPS/PPS/IDR to avoid confusing people // Send SPS/PPS/IDR to avoid confusing people
// Copy the data. This really should convert this to network byte order. // Copy the data. This really should convert this to network byte order.
@ -201,7 +217,7 @@ class FakeVideoEncoder : public GMPVideoEncoder {
f->SetCompleteFrame(true); f->SetCompleteFrame(true);
f->SetBufferType(GMP_BufferLength32); f->SetBufferType(GMP_BufferLength32);
GMPLOG(GL_DEBUG, "Encoding complete. type= " GMPLOG(GL_DEBUG, "Encoding complete. type="
<< f->FrameType() << f->FrameType()
<< " NAL_type=" << (int)eframe.idr_nalu.h264_compat_ << " NAL_type=" << (int)eframe.idr_nalu.h264_compat_
<< " length=" << f->Size() << " length=" << f->Size()
@ -271,6 +287,8 @@ class FakeVideoEncoder : public GMPVideoEncoder {
GMPVideoHost* host_; GMPVideoHost* host_;
GMPVideoEncoderCallback* callback_ = nullptr; GMPVideoEncoderCallback* callback_ = nullptr;
uint16_t frame_drop_cadence_ = 0;
uint16_t num_frames_since_drop_ = 0;
uint32_t frame_size_ = BIG_FRAME; uint32_t frame_size_ = BIG_FRAME;
uint32_t frames_encoded_ = 0; uint32_t frames_encoded_ = 0;
}; };

View file

@ -8,14 +8,14 @@
#include <stdio.h> #include <stdio.h>
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "nsExceptionHandler.h" #include "nsExceptionHandler.h"
#include "GMPLog.h"
#include "gmp-entrypoints.h" #include "gmp-entrypoints.h"
#include "prlink.h" #include "prlink.h"
#include "prenv.h" #include "prenv.h"
#include "prerror.h" #include "prerror.h"
#if defined(XP_WIN) && defined(MOZ_SANDBOX) #if defined(XP_WIN) && defined(MOZ_SANDBOX)
# include "mozilla/sandboxTarget.h" # include "mozilla/sandboxTarget.h"
# include "mozilla/sandboxing/SandboxInitialization.h" # include "nsWindowsHelpers.h"
# include "mozilla/sandboxing/sandboxLogging.h"
#endif #endif
#if defined(XP_LINUX) && defined(MOZ_SANDBOX) #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
# include "mozilla/Sandbox.h" # include "mozilla/Sandbox.h"
@ -83,18 +83,107 @@ class PassThroughGMPAdapter : public GMPAdapter {
PRLibrary* mLib = nullptr; PRLibrary* mLib = nullptr;
}; };
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
// This performs the same checks for an AppLocker policy that are performed in
// SaferpIsV2PolicyPresent from ntdll.dll, they are used to decide whether an
// AppLocker ioctl call is made.
static bool IsAppLockerPolicyPresent() {
// RuleCount check for policy configured via Local Security Policy.
DWORD ruleCount = 0;
DWORD ruleCountSize = sizeof(ruleCount);
if (RegGetValueW(HKEY_LOCAL_MACHINE,
LR"(SYSTEM\CurrentControlSet\Control\Srp\GP)", L"RuleCount",
RRF_RT_REG_DWORD, nullptr, &ruleCount,
&ruleCountSize) == ERROR_SUCCESS &&
ruleCount != 0) {
return true;
}
// Directory check for policy configured via Mobile Device Management.
static constexpr wchar_t appLockerMDMPath[] = LR"(\System32\AppLocker\MDM)";
wchar_t path[MAX_PATH + sizeof(appLockerMDMPath) / sizeof(wchar_t)];
UINT len = GetSystemWindowsDirectoryW(path, MAX_PATH);
if (len != 0 && len < MAX_PATH) {
wcscpy(path + len, appLockerMDMPath);
return GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES;
}
return false;
}
static void EnsureAppLockerCacheIsWarm(const wchar_t* aWidePath) {
// IOCTL to \Device\SrpDevice (\\.\SrpDevice via DosDevices) that triggers
// AppLocker to cache the allow/deny decision for the DLL, warming the NTFS
// EA cache before the sandbox starts.
static constexpr DWORD IOCTL_SRP_VERIFY_DLL = 0x225804;
static constexpr wchar_t kSrpDevicePath[] = LR"(\\.\SrpDevice)";
// Buffer layout: [HANDLE as 8 bytes][USHORT pathBytes][WCHAR path...]
// The handle field is always 8 bytes. On x86 the handle is zero-extended.
struct SrpIoctlBuffer {
uint64_t handle;
USHORT pathBytes;
WCHAR path[1];
};
static constexpr DWORD kSrpHeaderSize = offsetof(SrpIoctlBuffer, path);
UniquePtr<HANDLE, CloseHandleDeleter> fileHandle(CreateFileW(
aWidePath, FILE_READ_DATA | FILE_EXECUTE | SYNCHRONIZE,
FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, 0, nullptr));
if (fileHandle.get() == INVALID_HANDLE_VALUE) {
GMP_LOG_WARNING("EnsureAppLockerCacheIsWarm: CreateFileW failed (%lu)",
GetLastError());
return;
}
NtPathFromDosPath ntPath(aWidePath);
if (!ntPath.IsValid()) {
return;
}
DWORD ioctlSize = kSrpHeaderSize + ntPath.LengthInBytes();
auto buf = MakeUnique<uint8_t[]>(ioctlSize);
auto* srp = reinterpret_cast<SrpIoctlBuffer*>(buf.get());
// ULONG_PTR is pointer-sized (4 bytes on x86, 8 on x64). Casting to uint64_t
// zero-extends on x86, matching the cdq zero-extension in x86 ntdll.
srp->handle =
static_cast<uint64_t>(reinterpret_cast<ULONG_PTR>(fileHandle.get()));
srp->pathBytes = ntPath.LengthInBytes();
if (!ntPath.CopyTo(
mozilla::Span(srp->path, ntPath.LengthInBytes() / sizeof(WCHAR)))) {
MOZ_DIAGNOSTIC_ASSERT(false, "CopyTo failed: buffer too small");
return;
}
UniquePtr<HANDLE, CloseHandleDeleter> srpDevice(
CreateFileW(kSrpDevicePath, FILE_READ_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING, 0, nullptr));
if (srpDevice.get() == INVALID_HANDLE_VALUE) {
GMP_LOG_WARNING(
"EnsureAppLockerCacheIsWarm: opening SrpDevice failed (%lu)",
GetLastError());
return;
}
DWORD outBuf = 0;
DWORD bytesReturned = 0;
if (!DeviceIoControl(srpDevice.get(), IOCTL_SRP_VERIFY_DLL, srp, ioctlSize,
&outBuf, sizeof(outBuf), &bytesReturned, nullptr)) {
GMP_LOG_DEBUG(
"EnsureAppLockerCacheIsWarm: DeviceIoControl failed (%lu), "
"AppLocker may not be enabled",
GetLastError());
}
}
#endif
bool GMPLoader::Load(const char* aUTF8LibPath, uint32_t aUTF8LibPathLen, bool GMPLoader::Load(const char* aUTF8LibPath, uint32_t aUTF8LibPathLen,
const GMPPlatformAPI* aPlatformAPI, GMPAdapter* aAdapter) { const GMPPlatformAPI* aPlatformAPI, GMPAdapter* aAdapter) {
CrashReporter::AutoRecordAnnotation autoLibPath( CrashReporter::AutoRecordAnnotation autoLibPath(
CrashReporter::Annotation::GMPLibraryPath, CrashReporter::Annotation::GMPLibraryPath,
nsDependentCString(aUTF8LibPath)); nsDependentCString(aUTF8LibPath));
if (!getenv("MOZ_DISABLE_GMP_SANDBOX") && mSandboxStarter &&
!mSandboxStarter->Start(aUTF8LibPath)) {
MOZ_CRASH("Cannot start sandbox!");
return false;
}
// Load the GMP. // Load the GMP.
PRLibSpec libSpec; PRLibSpec libSpec;
#ifdef XP_WIN #ifdef XP_WIN
@ -111,6 +200,20 @@ bool GMPLoader::Load(const char* aUTF8LibPath, uint32_t aUTF8LibPathLen,
return false; return false;
} }
# if defined(MOZ_SANDBOX)
if (IsAppLockerPolicyPresent()) {
EnsureAppLockerCacheIsWarm(widePath.get());
}
# endif
#endif
if (!getenv("MOZ_DISABLE_GMP_SANDBOX") && mSandboxStarter &&
!mSandboxStarter->Start(aUTF8LibPath)) {
MOZ_CRASH("Cannot start sandbox!");
return false;
}
#ifdef XP_WIN
libSpec.value.pathname_u = widePath.get(); libSpec.value.pathname_u = widePath.get();
libSpec.type = PR_LibSpec_PathnameU; libSpec.type = PR_LibSpec_PathnameU;
#else #else

View file

@ -11,6 +11,8 @@
namespace mozilla::gmp { namespace mozilla::gmp {
class GMPVideoi420FrameImpl;
enum class GMPSharedMemClass { Decoded, Encoded }; enum class GMPSharedMemClass { Decoded, Encoded };
class GMPSharedMemManager { class GMPSharedMemManager {
@ -27,6 +29,8 @@ class GMPSharedMemManager {
virtual bool MgrAllocShmem(size_t aSize, ipc::Shmem* aMem) { return false; } virtual bool MgrAllocShmem(size_t aSize, ipc::Shmem* aMem) { return false; }
virtual void MgrDeallocShmem(ipc::Shmem& aMem) = 0; virtual void MgrDeallocShmem(ipc::Shmem& aMem) = 0;
virtual void MgrDecodedFrameDestroyed(GMPVideoi420FrameImpl* aFrame) {}
protected: protected:
virtual bool MgrIsOnOwningThread() const = 0; virtual bool MgrIsOnOwningThread() const = 0;

View file

@ -10,7 +10,6 @@
#include "mozilla/Unused.h" #include "mozilla/Unused.h"
#include "GMPPlatform.h" #include "GMPPlatform.h"
#include "GMPVideoEncodedFrameImpl.h" #include "GMPVideoEncodedFrameImpl.h"
#include "GMPVideoi420FrameImpl.h"
#include "runnable_utils.h" #include "runnable_utils.h"
namespace mozilla::gmp { namespace mozilla::gmp {
@ -67,9 +66,26 @@ void GMPVideoEncoderChild::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
MOZ_CRASH("Encoded without any frame data!"); MOZ_CRASH("Encoded without any frame data!");
} }
mLatestEncodedTimestamp = frameData.mTimestamp();
aEncodedFrame->Destroy(); aEncodedFrame->Destroy();
} }
void GMPVideoEncoderChild::MgrDecodedFrameDestroyed(
GMPVideoi420FrameImpl* aFrame) {
if (NS_WARN_IF(!mPlugin)) {
return;
}
// The OpenH264 encoder destroys the input frame if it has skipped encoding
// it. When it has encoded it, it calls the Encoded() callback before
// destroying the frame.
MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
if (aFrame->Timestamp() > mLatestEncodedTimestamp) {
(void)SendDroppedFrame(aFrame->Timestamp());
}
}
void GMPVideoEncoderChild::Error(GMPErr aError) { void GMPVideoEncoderChild::Error(GMPErr aError) {
if (NS_WARN_IF(!mPlugin)) { if (NS_WARN_IF(!mPlugin)) {
return; return;
@ -116,8 +132,10 @@ mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvEncode(
return IPC_FAIL(this, "!mVideoDecoder"); return IPC_FAIL(this, "!mVideoDecoder");
} }
// The `this` destroyed callback outlives the frame, because `mVideoEncoder`
// is responsible for destroying the frame, and we outlive `mVideoEncoder`.
auto* f = new GMPVideoi420FrameImpl(aInputFrame, std::move(aInputShmem), auto* f = new GMPVideoi420FrameImpl(aInputFrame, std::move(aInputShmem),
&mVideoHost); &mVideoHost, HostReportPolicy::Destroyed);
// Ignore any return code. It is OK for this to fail without killing the // Ignore any return code. It is OK for this to fail without killing the
// process. // process.

View file

@ -10,6 +10,7 @@
#include "mozilla/gmp/PGMPVideoEncoderChild.h" #include "mozilla/gmp/PGMPVideoEncoderChild.h"
#include "gmp-video-encode.h" #include "gmp-video-encode.h"
#include "GMPSharedMemManager.h" #include "GMPSharedMemManager.h"
#include "GMPVideoi420FrameImpl.h"
#include "GMPVideoHost.h" #include "GMPVideoHost.h"
namespace mozilla::gmp { namespace mozilla::gmp {
@ -39,6 +40,7 @@ class GMPVideoEncoderChild final : public PGMPVideoEncoderChild,
// GMPSharedMemManager // GMPSharedMemManager
void MgrDeallocShmem(Shmem& aMem) override { DeallocShmem(aMem); } void MgrDeallocShmem(Shmem& aMem) override { DeallocShmem(aMem); }
void MgrDecodedFrameDestroyed(GMPVideoi420FrameImpl* aFrame) override;
protected: protected:
bool MgrIsOnOwningThread() const override; bool MgrIsOnOwningThread() const override;
@ -66,6 +68,7 @@ class GMPVideoEncoderChild final : public PGMPVideoEncoderChild,
GMPContentChild* mPlugin; GMPContentChild* mPlugin;
GMPVideoEncoder* mVideoEncoder; GMPVideoEncoder* mVideoEncoder;
GMPVideoHostImpl mVideoHost; GMPVideoHostImpl mVideoHost;
uint64_t mLatestEncodedTimestamp = 0;
}; };
} // namespace mozilla::gmp } // namespace mozilla::gmp

View file

@ -271,6 +271,14 @@ mozilla::ipc::IPCResult GMPVideoEncoderParent::RecvEncodedData(
return IPC_OK(); return IPC_OK();
} }
mozilla::ipc::IPCResult GMPVideoEncoderParent::RecvDroppedFrame(
const uint64_t& aTimestamp) {
if (mCallback) {
mCallback->Dropped(aTimestamp);
}
return IPC_OK();
}
mozilla::ipc::IPCResult GMPVideoEncoderParent::RecvError(const GMPErr& aError) { mozilla::ipc::IPCResult GMPVideoEncoderParent::RecvError(const GMPErr& aError) {
if (mCallback) { if (mCallback) {
mCallback->Error(aError); mCallback->Error(aError);

View file

@ -73,6 +73,7 @@ class GMPVideoEncoderParent final : public GMPVideoEncoderProxy,
const GMPVideoEncodedFrameData& aEncodedFrame, const GMPVideoEncodedFrameData& aEncodedFrame,
nsTArray<uint8_t>&& aEncodedData, nsTArray<uint8_t>&& aEncodedData,
nsTArray<uint8_t>&& aCodecSpecificInfo) override; nsTArray<uint8_t>&& aCodecSpecificInfo) override;
mozilla::ipc::IPCResult RecvDroppedFrame(const uint64_t& aTimestamp) override;
mozilla::ipc::IPCResult RecvError(const GMPErr& aError) override; mozilla::ipc::IPCResult RecvError(const GMPErr& aError) override;
mozilla::ipc::IPCResult RecvShutdown() override; mozilla::ipc::IPCResult RecvShutdown() override;

View file

@ -19,6 +19,7 @@ class GMPVideoEncoderCallbackProxy : public GMPCallbackBase {
virtual ~GMPVideoEncoderCallbackProxy() = default; virtual ~GMPVideoEncoderCallbackProxy() = default;
virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame, virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
const nsTArray<uint8_t>& aCodecSpecificInfo) = 0; const nsTArray<uint8_t>& aCodecSpecificInfo) = 0;
virtual void Dropped(uint64_t aTimestamp) = 0;
virtual void Error(GMPErr aError) = 0; virtual void Error(GMPErr aError) = 0;
}; };

View file

@ -93,6 +93,9 @@ void GMPVideoHostImpl::DecodedFrameCreated(
void GMPVideoHostImpl::DecodedFrameDestroyed(GMPVideoi420FrameImpl* aFrame) { void GMPVideoHostImpl::DecodedFrameDestroyed(GMPVideoi420FrameImpl* aFrame) {
MOZ_ALWAYS_TRUE(mDecodedFrames.RemoveElement(aFrame)); MOZ_ALWAYS_TRUE(mDecodedFrames.RemoveElement(aFrame));
if (mSharedMemMgr && aFrame->mReportPolicy == HostReportPolicy::Destroyed) {
mSharedMemMgr->MgrDecodedFrameDestroyed(aFrame);
}
} }
} // namespace mozilla::gmp } // namespace mozilla::gmp

View file

@ -38,16 +38,25 @@ void GMPVideoi420FrameImpl::GMPFramePlane::Copy(uint8_t* aDst,
} }
} }
GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(GMPVideoHostImpl* aHost) GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(
: mHost(aHost), mWidth(0), mHeight(0), mTimestamp(0ll), mDuration(0ll) { GMPVideoHostImpl* aHost,
HostReportPolicy aReportPolicy /*= HostReportPolicy::None*/)
: mReportPolicy(aReportPolicy),
mHost(aHost),
mWidth(0),
mHeight(0),
mTimestamp(0ll),
mDuration(0ll) {
MOZ_ASSERT(aHost); MOZ_ASSERT(aHost);
aHost->DecodedFrameCreated(this); aHost->DecodedFrameCreated(this);
} }
GMPVideoi420FrameImpl::GMPVideoi420FrameImpl( GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(
const GMPVideoi420FrameData& aFrameData, ipc::Shmem&& aShmemBuffer, const GMPVideoi420FrameData& aFrameData, ipc::Shmem&& aShmemBuffer,
GMPVideoHostImpl* aHost) GMPVideoHostImpl* aHost,
: mHost(aHost), HostReportPolicy aReportPolicy /*= HostReportPolicy::None*/)
: mReportPolicy(aReportPolicy),
mHost(aHost),
mShmemBuffer(std::move(aShmemBuffer)), mShmemBuffer(std::move(aShmemBuffer)),
mYPlane(aFrameData.mYPlane()), mYPlane(aFrameData.mYPlane()),
mUPlane(aFrameData.mUPlane()), mUPlane(aFrameData.mUPlane()),
@ -63,8 +72,10 @@ GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(
GMPVideoi420FrameImpl::GMPVideoi420FrameImpl( GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(
const GMPVideoi420FrameData& aFrameData, nsTArray<uint8_t>&& aArrayBuffer, const GMPVideoi420FrameData& aFrameData, nsTArray<uint8_t>&& aArrayBuffer,
GMPVideoHostImpl* aHost) GMPVideoHostImpl* aHost,
: mHost(aHost), HostReportPolicy aReportPolicy /*= HostReportPolicy::None*/)
: mReportPolicy(aReportPolicy),
mHost(aHost),
mArrayBuffer(std::move(aArrayBuffer)), mArrayBuffer(std::move(aArrayBuffer)),
mYPlane(aFrameData.mYPlane()), mYPlane(aFrameData.mYPlane()),
mUPlane(aFrameData.mUPlane()), mUPlane(aFrameData.mUPlane()),
@ -146,30 +157,76 @@ bool GMPVideoi420FrameImpl::CheckFrameData(
// if so. Note: Size() greater than expected is also an error, but with no // if so. Note: Size() greater than expected is also an error, but with no
// negative consequences // negative consequences
int32_t half_width = (aFrameData.mWidth() + 1) / 2; int32_t half_width = (aFrameData.mWidth() + 1) / 2;
if ((aFrameData.mYPlane().mStride() <= 0) || int32_t half_height = (aFrameData.mHeight() + 1) / 2;
(aFrameData.mYPlane().mSize() <= 0) ||
(aFrameData.mYPlane().mOffset() < 0) || // Check for negative offsets
(aFrameData.mUPlane().mStride() <= 0) || if ((aFrameData.mYPlane().mOffset() < 0) ||
(aFrameData.mUPlane().mSize() <= 0) || (aFrameData.mUPlane().mOffset() < 0) ||
(aFrameData.mUPlane().mOffset() < (aFrameData.mVPlane().mOffset() < 0)) {
aFrameData.mYPlane().mOffset() + aFrameData.mYPlane().mSize()) ||
(aFrameData.mVPlane().mStride() <= 0) ||
(aFrameData.mVPlane().mSize() <= 0) ||
(aFrameData.mVPlane().mOffset() <
aFrameData.mUPlane().mOffset() + aFrameData.mUPlane().mSize()) ||
(aBufferSize < static_cast<size_t>(aFrameData.mVPlane().mOffset()) +
static_cast<size_t>(aFrameData.mVPlane().mSize())) ||
(aFrameData.mYPlane().mStride() < aFrameData.mWidth()) ||
(aFrameData.mUPlane().mStride() < half_width) ||
(aFrameData.mVPlane().mStride() < half_width) ||
(aFrameData.mYPlane().mSize() <
aFrameData.mYPlane().mStride() * aFrameData.mHeight()) ||
(aFrameData.mUPlane().mSize() <
aFrameData.mUPlane().mStride() * ((aFrameData.mHeight() + 1) / 2)) ||
(aFrameData.mVPlane().mSize() <
aFrameData.mVPlane().mStride() * ((aFrameData.mHeight() + 1) / 2))) {
return false; return false;
} }
// Check for non-positive strides and sizes
if ((aFrameData.mYPlane().mStride() <= 0) ||
(aFrameData.mYPlane().mSize() <= 0) ||
(aFrameData.mUPlane().mStride() <= 0) ||
(aFrameData.mUPlane().mSize() <= 0) ||
(aFrameData.mVPlane().mStride() <= 0) ||
(aFrameData.mVPlane().mSize() <= 0)) {
return false;
}
// Check stride requirements (must be at least as wide as the data)
if ((aFrameData.mYPlane().mStride() < aFrameData.mWidth()) ||
(aFrameData.mUPlane().mStride() < half_width) ||
(aFrameData.mVPlane().mStride() < half_width)) {
return false;
}
// Validate plane end calculations
auto y_plane_end = CheckedInt<int32_t>(aFrameData.mYPlane().mOffset()) +
aFrameData.mYPlane().mSize();
auto u_plane_end = CheckedInt<int32_t>(aFrameData.mUPlane().mOffset()) +
aFrameData.mUPlane().mSize();
auto v_plane_end = CheckedInt<int32_t>(aFrameData.mVPlane().mOffset()) +
aFrameData.mVPlane().mSize();
if (!y_plane_end.isValid() || !u_plane_end.isValid() ||
!v_plane_end.isValid()) {
return false;
}
// Check that planes don't overlap
if ((aFrameData.mUPlane().mOffset() < y_plane_end.value()) ||
(aFrameData.mVPlane().mOffset() < u_plane_end.value())) {
return false;
}
// Check buffer size
if (aBufferSize < static_cast<size_t>(v_plane_end.value())) {
return false;
}
// Validate size calculations
auto y_expected_size = CheckedInt<int32_t>(aFrameData.mYPlane().mStride()) *
aFrameData.mHeight();
auto u_expected_size =
CheckedInt<int32_t>(aFrameData.mUPlane().mStride()) * half_height;
auto v_expected_size =
CheckedInt<int32_t>(aFrameData.mVPlane().mStride()) * half_height;
if (!y_expected_size.isValid() || !u_expected_size.isValid() ||
!v_expected_size.isValid()) {
return false;
}
// Check that allocated sizes are sufficient
if ((aFrameData.mYPlane().mSize() < y_expected_size.value()) ||
(aFrameData.mUPlane().mSize() < u_expected_size.value()) ||
(aFrameData.mVPlane().mSize() < v_expected_size.value())) {
return false;
}
return true; return true;
} }

View file

@ -17,14 +17,24 @@ class GMPPlaneData;
class GMPVideoi420FrameData; class GMPVideoi420FrameData;
class GMPVideoHostImpl; class GMPVideoHostImpl;
class GMPVideoi420FrameImpl final : public GMPVideoi420Frame { enum class HostReportPolicy : uint8_t {
None,
Destroyed,
};
class GMPVideoi420FrameImpl : public GMPVideoi420Frame {
public: public:
explicit GMPVideoi420FrameImpl(GMPVideoHostImpl* aHost); explicit GMPVideoi420FrameImpl(
GMPVideoi420FrameImpl(const GMPVideoi420FrameData& aFrameData, GMPVideoHostImpl* aHost,
ipc::Shmem&& aShmemBuffer, GMPVideoHostImpl* aHost); HostReportPolicy aReportPolicy = HostReportPolicy::None);
GMPVideoi420FrameImpl(const GMPVideoi420FrameData& aFrameData, GMPVideoi420FrameImpl(
nsTArray<uint8_t>&& aArrayBuffer, const GMPVideoi420FrameData& aFrameData, ipc::Shmem&& aShmemBuffer,
GMPVideoHostImpl* aHost); GMPVideoHostImpl* aHost,
HostReportPolicy aReportPolicy = HostReportPolicy::None);
GMPVideoi420FrameImpl(
const GMPVideoi420FrameData& aFrameData, nsTArray<uint8_t>&& aArrayBuffer,
GMPVideoHostImpl* aHost,
HostReportPolicy aReportPolicy = HostReportPolicy::None);
virtual ~GMPVideoi420FrameImpl(); virtual ~GMPVideoi420FrameImpl();
// This is called during a normal destroy sequence, which is // This is called during a normal destroy sequence, which is
@ -75,7 +85,7 @@ class GMPVideoi420FrameImpl final : public GMPVideoi420Frame {
const uint8_t* Buffer() const; const uint8_t* Buffer() const;
int32_t AllocatedSize() const; int32_t AllocatedSize() const;
private: protected:
struct GMPFramePlane { struct GMPFramePlane {
explicit GMPFramePlane(const GMPPlaneData& aPlaneData); explicit GMPFramePlane(const GMPPlaneData& aPlaneData);
GMPFramePlane() = default; GMPFramePlane() = default;
@ -98,6 +108,10 @@ class GMPVideoi420FrameImpl final : public GMPVideoi420Frame {
GMPErr MaybeResize(int32_t aNewSize); GMPErr MaybeResize(int32_t aNewSize);
void DestroyBuffer(); void DestroyBuffer();
public:
const HostReportPolicy mReportPolicy;
protected:
GMPVideoHostImpl* mHost; GMPVideoHostImpl* mHost;
nsTArray<uint8_t> mArrayBuffer; nsTArray<uint8_t> mArrayBuffer;
ipc::Shmem mShmemBuffer; ipc::Shmem mShmemBuffer;

View file

@ -43,6 +43,7 @@ parent:
async EncodedData(GMPVideoEncodedFrameData aEncodedFrame, async EncodedData(GMPVideoEncodedFrameData aEncodedFrame,
uint8_t[] aEncodedData, uint8_t[] aEncodedData,
uint8_t[] aCodecSpecificInfo); uint8_t[] aCodecSpecificInfo);
async DroppedFrame(uint64_t aTimestamp);
async Error(GMPErr aErr); async Error(GMPErr aErr);
async Shutdown(); async Shutdown();
}; };

View file

@ -0,0 +1,279 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "api/video/i420_buffer.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "libwebrtcglue/WebrtcGmpVideoCodec.h"
#include "media/base/media_constants.h"
#include "mozilla/gtest/WaitFor.h"
#include "nsServiceManagerUtils.h"
using testing::_;
using testing::AtLeast;
using testing::Eq;
using testing::Ge;
using testing::Gt;
using testing::InSequence;
using testing::Property;
using testing::Test;
namespace mozilla {
struct TestWebrtcGmpVideoEncoder : public Test {
nsCOMPtr<nsIThread> mGmpThread;
RefPtr<WebrtcGmpVideoEncoder> mEncoder;
webrtc::VideoCodec mCodecSettings;
webrtc::VideoEncoder::Settings mSettings = {
webrtc::VideoEncoder::Capabilities(/*loss_notification=*/true),
/*number_of_cores=*/1, /*max_payload_size=*/0};
void SetUp() override {
mEncoder = MakeRefPtr<WebrtcGmpVideoEncoder>(
webrtc::SdpVideoFormat(cricket::kH264CodecName), "dummy");
mCodecSettings.codecType = webrtc::VideoCodecType::kVideoCodecH264;
mCodecSettings.numberOfSimulcastStreams = 1;
mCodecSettings.simulcastStream[0].active = true;
mCodecSettings.simulcastStream[0].numberOfTemporalLayers = 1;
mCodecSettings.width = 640;
mCodecSettings.height = 480;
mCodecSettings.maxFramerate = 60;
mCodecSettings.minBitrate = 50;
mCodecSettings.startBitrate = 200;
mCodecSettings.maxBitrate = 1000;
nsCOMPtr<mozIGeckoMediaPluginService> mps =
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
ASSERT_TRUE(mps);
mps->GetThread(getter_AddRefs(mGmpThread));
ASSERT_TRUE(mGmpThread);
}
void TearDown() override { mEncoder = nullptr; }
};
struct MockEncodedImageCallback : public webrtc::EncodedImageCallback {
MOCK_METHOD(Result, OnEncodedImage,
(const webrtc::EncodedImage&, const webrtc::CodecSpecificInfo*),
(override));
MOCK_METHOD(void, OnDroppedFrame, (DropReason), (override));
};
auto CreateBlackFrame(int width, int height) {
auto buffer = webrtc::I420Buffer::Create(width, height);
webrtc::I420Buffer::SetBlack(buffer.get());
return buffer;
}
TEST_F(TestWebrtcGmpVideoEncoder, EmptyLifecycle) {}
TEST_F(TestWebrtcGmpVideoEncoder, InitEncode) {
mEncoder->InitEncode(&mCodecSettings, mSettings);
WaitFor(*mEncoder->InitPluginEvent());
}
TEST_F(TestWebrtcGmpVideoEncoder, Encode) {
using Result = webrtc::EncodedImageCallback::Result;
mEncoder->InitEncode(&mCodecSettings, mSettings);
WaitFor(*mEncoder->InitPluginEvent());
MozPromiseHolder<GenericPromise> doneHolder;
RefPtr donePromise = doneHolder.Ensure(__func__);
MockEncodedImageCallback callback;
constexpr uint32_t rtp_time = 55;
EXPECT_CALL(
callback,
OnEncodedImage(
Property(&webrtc::EncodedImage::RtpTimestamp, Eq(rtp_time)), _))
.WillOnce([&] {
doneHolder.Resolve(true, "TestWebrtcGmpVideoEncoder::Encode");
return Result(Result::OK);
});
mEncoder->RegisterEncodeCompleteCallback(&callback);
std::vector<webrtc::VideoFrameType> types = {
webrtc::VideoFrameType::kVideoFrameKey};
EXPECT_EQ(
mEncoder->Encode(webrtc::VideoFrame::Builder()
.set_rtp_timestamp(rtp_time)
.set_video_frame_buffer(CreateBlackFrame(
mCodecSettings.width, mCodecSettings.height))
.build(),
&types),
WEBRTC_VIDEO_CODEC_OK);
EXPECT_EQ(WaitForResolve(donePromise), true);
}
TEST_F(TestWebrtcGmpVideoEncoder, BackPressure) {
using Result = webrtc::EncodedImageCallback::Result;
mEncoder->InitEncode(&mCodecSettings, mSettings);
WaitFor(*mEncoder->InitPluginEvent());
MozPromiseHolder<GenericPromise> doneHolder;
RefPtr donePromise = doneHolder.Ensure(__func__);
MockEncodedImageCallback callback;
constexpr uint32_t rtpTime = 55;
constexpr size_t iterations = 1000;
Atomic<uint32_t> lastRtpTime{};
Atomic<size_t> eventCount{};
const auto countIteration = [&] {
size_t c = ++eventCount;
EXPECT_LE(c, iterations);
if (c == iterations) {
doneHolder.Resolve(true, "TestWebrtcGmpVideoEncoder::BackPressure");
}
};
EXPECT_CALL(
callback,
OnEncodedImage(
Property(&webrtc::EncodedImage::RtpTimestamp,
testing::AllOf(Ge(rtpTime), Gt<uint32_t>(lastRtpTime))),
_))
.Times(AtLeast(1))
.WillRepeatedly([&](const auto& aImage, const auto*) {
lastRtpTime = aImage.RtpTimestamp();
countIteration();
return Result(Result::OK);
});
EXPECT_CALL(
callback,
OnDroppedFrame(MockEncodedImageCallback::DropReason::kDroppedByEncoder))
.Times(AtLeast(iterations / 10))
.WillRepeatedly(countIteration);
mEncoder->RegisterEncodeCompleteCallback(&callback);
std::vector<webrtc::VideoFrameType> types = {
webrtc::VideoFrameType::kVideoFrameKey};
EXPECT_EQ(
mEncoder->Encode(webrtc::VideoFrame::Builder()
.set_rtp_timestamp(rtpTime)
.set_video_frame_buffer(CreateBlackFrame(
mCodecSettings.width, mCodecSettings.height))
.build(),
&types),
WEBRTC_VIDEO_CODEC_OK);
for (size_t i = 1; i < iterations; ++i) {
mEncoder->Encode(webrtc::VideoFrame::Builder()
.set_rtp_timestamp(rtpTime + i)
.set_video_frame_buffer(CreateBlackFrame(
mCodecSettings.width, mCodecSettings.height))
.build(),
&types);
}
EXPECT_EQ(WaitForResolve(donePromise), true);
EXPECT_EQ(eventCount, iterations);
}
TEST_F(TestWebrtcGmpVideoEncoder, ReUse) {
using Result = webrtc::EncodedImageCallback::Result;
mEncoder->InitEncode(&mCodecSettings, mSettings);
WaitFor(*mEncoder->InitPluginEvent());
MozPromiseHolder<GenericPromise> doneHolder;
RefPtr donePromise = doneHolder.Ensure(__func__);
MockEncodedImageCallback callback;
constexpr uint32_t rtpTime = 55;
constexpr uint32_t rtpTime2 = rtpTime * 2;
EXPECT_CALL(callback,
OnEncodedImage(
Property(&webrtc::EncodedImage::RtpTimestamp, rtpTime2), _))
.WillOnce([&] {
doneHolder.Resolve(true, "TestWebrtcGmpVideoEncoder::ReUse");
return Result(Result::OK);
});
mEncoder->RegisterEncodeCompleteCallback(&callback);
// Block the GMP thread until after Shutdown() as to avoid racing between the
// first encoded callback and the Shutdown() call.
Monitor mon(__func__);
bool block = true;
MOZ_ALWAYS_SUCCEEDS(
mGmpThread->Dispatch(NS_NewRunnableFunction(__func__, [&] {
MonitorAutoLock lock(mon);
while (block) {
lock.Wait();
}
})));
std::vector<webrtc::VideoFrameType> types = {
webrtc::VideoFrameType::kVideoFrameKey};
EXPECT_EQ(
mEncoder->Encode(webrtc::VideoFrame::Builder()
.set_rtp_timestamp(rtpTime)
.set_video_frame_buffer(CreateBlackFrame(
mCodecSettings.width, mCodecSettings.height))
.build(),
&types),
WEBRTC_VIDEO_CODEC_OK);
// Shutdown mid-encode, then re-init and encode again.
mEncoder->Shutdown();
{
MonitorAutoLock lock(mon);
block = false;
lock.Notify();
}
mEncoder->InitEncode(&mCodecSettings, mSettings);
WaitFor(*mEncoder->InitPluginEvent());
mEncoder->RegisterEncodeCompleteCallback(&callback);
EXPECT_EQ(
mEncoder->Encode(webrtc::VideoFrame::Builder()
.set_rtp_timestamp(rtpTime2)
.set_video_frame_buffer(CreateBlackFrame(
mCodecSettings.width, mCodecSettings.height))
.build(),
&types),
WEBRTC_VIDEO_CODEC_OK);
EXPECT_EQ(WaitForResolve(donePromise), true);
}
TEST_F(TestWebrtcGmpVideoEncoder, TrackedFrameDrops) {
using Result = webrtc::EncodedImageCallback::Result;
// Tell the fakeopenh264 plugin to drop some allocated input frames without
// telling us. It will drop every fifth input frame. This shall get tracked
// as frame drops.
mCodecSettings.SetFrameDropEnabled(true);
mEncoder->InitEncode(&mCodecSettings, mSettings);
WaitFor(*mEncoder->InitPluginEvent());
Monitor m(__func__);
size_t numEvents = 0;
const auto handleEvent = ([&] {
MonitorAutoLock lock(m);
++numEvents;
lock.Notify();
});
MockEncodedImageCallback callback;
{
InSequence s;
EXPECT_CALL(callback, OnEncodedImage(_, _)).Times(4).WillRepeatedly([&] {
handleEvent();
return Result(Result::OK);
});
EXPECT_CALL(
callback,
OnDroppedFrame(MockEncodedImageCallback::DropReason::kDroppedByEncoder))
.WillOnce(handleEvent);
}
mEncoder->RegisterEncodeCompleteCallback(&callback);
constexpr uint32_t ntpTime = 55;
std::vector<webrtc::VideoFrameType> types = {
webrtc::VideoFrameType::kVideoFrameKey};
for (uint8_t i = 0; i < 5; ++i) {
EXPECT_EQ(
mEncoder->Encode(webrtc::VideoFrame::Builder()
.set_ntp_time_ms(ntpTime * (i + 1))
.set_video_frame_buffer(CreateBlackFrame(
mCodecSettings.width, mCodecSettings.height))
.build(),
&types),
WEBRTC_VIDEO_CODEC_OK);
MonitorAutoLock lock(m);
while (numEvents <= i) {
lock.Wait();
}
}
}
} // namespace mozilla

View file

@ -92,6 +92,7 @@ if CONFIG["MOZ_WEBRTC"] and CONFIG["OS_TARGET"] != "Android":
"TestAudioDeviceEnumerator.cpp", "TestAudioDeviceEnumerator.cpp",
"TestPacer.cpp", "TestPacer.cpp",
"TestVideoFrameConverter.cpp", "TestVideoFrameConverter.cpp",
"TestWebrtcGmpCodec.cpp",
] ]
TEST_HARNESS_FILES.gtest += [ TEST_HARNESS_FILES.gtest += [

View file

@ -187,13 +187,17 @@ RefPtr<MediaDataDecoder::DecodePromise> VPXDecoder::ProcessDecode(
if (img->fmt == VPX_IMG_FMT_I420) { if (img->fmt == VPX_IMG_FMT_I420) {
b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT; b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
MOZ_ASSERT(img->y_chroma_shift == 1);
b.mPlanes[1].mHeight = (img->d_h + 1) >> img->y_chroma_shift; b.mPlanes[1].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
MOZ_ASSERT(img->x_chroma_shift == 1);
b.mPlanes[1].mWidth = (img->d_w + 1) >> img->x_chroma_shift; b.mPlanes[1].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
b.mPlanes[2].mHeight = (img->d_h + 1) >> img->y_chroma_shift; b.mPlanes[2].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
b.mPlanes[2].mWidth = (img->d_w + 1) >> img->x_chroma_shift; b.mPlanes[2].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
} else if (img->fmt == VPX_IMG_FMT_I444) { } else if (img->fmt == VPX_IMG_FMT_I444) {
MOZ_ASSERT(b.mChromaSubsampling == gfx::ChromaSubsampling::FULL);
MOZ_ASSERT(img->y_chroma_shift == 0);
MOZ_ASSERT(img->x_chroma_shift == 0);
b.mPlanes[1].mHeight = img->d_h; b.mPlanes[1].mHeight = img->d_h;
b.mPlanes[1].mWidth = img->d_w; b.mPlanes[1].mWidth = img->d_w;

View file

@ -427,6 +427,24 @@ void GMPVideoEncoder::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
} }
} }
void GMPVideoEncoder::Dropped(uint64_t aTimestamp) {
MOZ_ASSERT(IsOnGMPThread());
RefPtr<EncodePromise::Private> promise;
if (!mPendingEncodes.Remove(aTimestamp, getter_AddRefs(promise))) {
GMP_LOG_WARNING(
"[%p] GMPVideoEncoder::Dropped -- no frame matching timestamp %" PRIu64,
this, aTimestamp);
return;
}
promise->Resolve(EncodedData(), __func__);
if (mPendingEncodes.IsEmpty()) {
mDrainPromise.ResolveIfExists(EncodedData(), __func__);
}
}
void GMPVideoEncoder::Teardown(const MediaResult& aResult, void GMPVideoEncoder::Teardown(const MediaResult& aResult,
StaticString aCallSite) { StaticString aCallSite) {
GMP_LOG_DEBUG("[%p] GMPVideoEncoder::Teardown", this); GMP_LOG_DEBUG("[%p] GMPVideoEncoder::Teardown", this);

View file

@ -38,6 +38,7 @@ class GMPVideoEncoder final : public MediaDataEncoder,
void Encoded(GMPVideoEncodedFrame* aEncodedFrame, void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
const nsTArray<uint8_t>& aCodecSpecificInfo) override; const nsTArray<uint8_t>& aCodecSpecificInfo) override;
void Dropped(uint64_t aTimestamp) override;
void Error(GMPErr aError) override; void Error(GMPErr aError) override;
void Terminated() override; void Terminated() override;

View file

@ -568,7 +568,7 @@ void AudioData::CopyTo(const AllowSharedBufferSource& aDestination,
uint32_t bytesPerSample = BytesPerSamples(destFormat.value()); uint32_t bytesPerSample = BytesPerSamples(destFormat.value());
CheckedInt<uint32_t> copyLength = bytesPerSample; CheckedInt<uint32_t> copyLength = bytesPerSample;
copyLength *= copyElementCount; copyLength *= copyElementCount;
if (copyLength.value() > destLength) { if (!copyLength.isValid() || copyLength.value() > destLength) {
auto msg = nsFmtCString(FMT_STRING("destination buffer of length {} too " auto msg = nsFmtCString(FMT_STRING("destination buffer of length {} too "
"small for copying {} elements"), "small for copying {} elements"),
destLength, bytesPerSample * copyElementCount); destLength, bytesPerSample * copyElementCount);

View file

@ -1905,17 +1905,17 @@ nsresult JsepSessionImpl::ValidateRemoteDescription(const Sdp& description) {
const SdpMediaSection& oldMsection = const SdpMediaSection& oldMsection =
mCurrentRemoteDescription->GetMediaSection(i); mCurrentRemoteDescription->GetMediaSection(i);
if (mSdpHelper.MsectionIsDisabled(newMsection) ||
mSdpHelper.MsectionIsDisabled(oldMsection)) {
continue;
}
if (oldMsection.GetMediaType() != newMsection.GetMediaType()) { if (oldMsection.GetMediaType() != newMsection.GetMediaType()) {
JSEP_SET_ERROR("Remote description changes the media type of m-line " JSEP_SET_ERROR("Remote description changes the media type of m-line "
<< i); << i);
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
} }
if (mSdpHelper.MsectionIsDisabled(newMsection) ||
mSdpHelper.MsectionIsDisabled(oldMsection)) {
continue;
}
bool differ = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection); bool differ = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection);
if (mIsPendingOfferer.isSome() && *mIsPendingOfferer && differ && if (mIsPendingOfferer.isSome() && *mIsPendingOfferer && differ &&

View file

@ -5,10 +5,14 @@
#include "jsep/JsepTrack.h" #include "jsep/JsepTrack.h"
#include "jsep/JsepCodecDescription.h" #include "jsep/JsepCodecDescription.h"
#include "jsep/JsepTrackEncoding.h" #include "jsep/JsepTrackEncoding.h"
#include "transport/logging.h"
#include <algorithm> #include <algorithm>
namespace mozilla { namespace mozilla {
MOZ_MTLOG_MODULE("jsep")
void JsepTrack::GetNegotiatedPayloadTypes( void JsepTrack::GetNegotiatedPayloadTypes(
std::vector<uint16_t>* payloadTypes) const { std::vector<uint16_t>* payloadTypes) const {
if (!mNegotiatedDetails) { if (!mNegotiatedDetails) {
@ -32,6 +36,8 @@ void JsepTrack::GetPayloadTypes(
for (const auto& codec : codecs) { for (const auto& codec : codecs) {
uint16_t pt; uint16_t pt;
if (!codec->GetPtAsInt(&pt)) { if (!codec->GetPtAsInt(&pt)) {
MOZ_MTLOG(ML_ERROR, "Codec " << codec->mName
<< " does not have a valid payload type");
MOZ_ASSERT(false); MOZ_ASSERT(false);
continue; continue;
} }
@ -52,14 +58,47 @@ void JsepTrack::EnsureSsrcs(SsrcGenerator& ssrcGenerator, size_t aNumber) {
uint32_t ssrc, rtxSsrc; uint32_t ssrc, rtxSsrc;
if (!ssrcGenerator.GenerateSsrc(&ssrc) || if (!ssrcGenerator.GenerateSsrc(&ssrc) ||
!ssrcGenerator.GenerateSsrc(&rtxSsrc)) { !ssrcGenerator.GenerateSsrc(&rtxSsrc)) {
MOZ_MTLOG(ML_ERROR, "Unable to generate SSRC");
return; return;
} }
mSsrcs.push_back(ssrc); mSsrcs.push_back(ssrc);
mSsrcToRtxSsrc[ssrc] = rtxSsrc; mSsrcToRtxSsrc[ssrc] = rtxSsrc;
MOZ_ASSERT(mSsrcs.size() == mSsrcToRtxSsrc.size()); if (mSsrcs.size() != mSsrcToRtxSsrc.size()) {
MOZ_MTLOG(ML_ERROR,
"[" << mTrackId
<< "]: mSsrcToRtxSsrc has different size than mSsrcs.");
MOZ_ASSERT(false);
mSsrcs.clear();
mSsrcToRtxSsrc.clear();
}
} }
} }
std::vector<uint32_t> JsepTrack::GetRtxSsrcs() const {
std::vector<uint32_t> result;
if (mRtxIsAllowed &&
Preferences::GetBool("media.peerconnection.video.use_rtx", false) &&
!mSsrcToRtxSsrc.empty()) {
if (mSsrcToRtxSsrc.size() != mSsrcs.size()) {
MOZ_MTLOG(ML_ERROR,
"[" << mTrackId
<< "]: mSsrcToRtxSsrc has different size than mSsrcs.");
return {};
}
for (const auto ssrc : mSsrcs) {
auto it = mSsrcToRtxSsrc.find(ssrc);
if (it != mSsrcToRtxSsrc.end()) {
result.push_back(it->second);
} else {
MOZ_MTLOG(ML_ERROR,
"[" << mTrackId << "]: No RTX SSRC found for SSRC " << ssrc);
return {};
}
}
}
return result;
}
void JsepTrack::PopulateCodecs( void JsepTrack::PopulateCodecs(
const std::vector<UniquePtr<JsepCodecDescription>>& prototype, const std::vector<UniquePtr<JsepCodecDescription>>& prototype,
bool aUsePreferredCodecsOrder) { bool aUsePreferredCodecsOrder) {
@ -117,7 +156,10 @@ void JsepTrack::AddToAnswer(const SdpMediaSection& offer,
} }
void JsepTrack::SetRids(const std::vector<std::string>& aRids) { void JsepTrack::SetRids(const std::vector<std::string>& aRids) {
MOZ_ASSERT(!aRids.empty()); if (!aRids.size()) {
MOZ_MTLOG(ML_ERROR, "cannot set empty rids");
return;
}
if (!mRids.empty()) { if (!mRids.empty()) {
return; return;
} }
@ -134,9 +176,17 @@ void JsepTrack::SetMaxEncodings(size_t aMax) {
void JsepTrack::RecvTrackSetRemote(const Sdp& aSdp, void JsepTrack::RecvTrackSetRemote(const Sdp& aSdp,
const SdpMediaSection& aMsection) { const SdpMediaSection& aMsection) {
mInHaveRemote = true; mInHaveRemote = true;
MOZ_ASSERT(mDirection == sdp::kRecv); if (mDirection != sdp::kRecv) {
MOZ_ASSERT(aMsection.GetMediaType() != MOZ_MTLOG(ML_ERROR, "RecvTrackSetRemote called on non-receive track");
SdpMediaSection::MediaType::kApplication); MOZ_ASSERT(false);
return;
}
if (aMsection.GetMediaType() == SdpMediaSection::kApplication) {
MOZ_MTLOG(ML_ERROR,
"RecvTrackSetRemote called on application media section");
MOZ_ASSERT(false);
return;
}
std::string error; std::string error;
SdpHelper helper(&error); SdpHelper helper(&error);
@ -187,7 +237,11 @@ void JsepTrack::RecvTrackSetRemote(const Sdp& aSdp,
} }
void JsepTrack::RecvTrackSetLocal(const SdpMediaSection& aMsection) { void JsepTrack::RecvTrackSetLocal(const SdpMediaSection& aMsection) {
MOZ_ASSERT(mDirection == sdp::kRecv); if (mDirection != sdp::kRecv) {
MOZ_MTLOG(ML_ERROR, "RecvTrackSetLocal called on non-receive track");
MOZ_ASSERT(false);
return;
}
// TODO: Should more stuff live in here? Anything that needs to happen when we // TODO: Should more stuff live in here? Anything that needs to happen when we
// decide we're ready to receive packets should probably go in here. // decide we're ready to receive packets should probably go in here.
@ -259,9 +313,16 @@ void JsepTrack::SendTrackSetRemote(SsrcGenerator& aSsrcGenerator,
void JsepTrack::AddToMsection( void JsepTrack::AddToMsection(
const std::vector<UniquePtr<JsepCodecDescription>>& codecs, const std::vector<UniquePtr<JsepCodecDescription>>& codecs,
SdpMediaSection* msection) const { SdpMediaSection* msection) const {
MOZ_ASSERT(msection->GetMediaType() == mType); if (msection->GetMediaType() != mType) {
MOZ_ASSERT(!codecs.empty()); MOZ_MTLOG(ML_ERROR, "AddToMsection called on wrong media section type");
MOZ_ASSERT(false);
return;
}
if (codecs.empty()) {
MOZ_MTLOG(ML_ERROR, "AddToMsection called with empty codecs");
MOZ_ASSERT(false);
return;
}
for (const auto& codec : codecs) { for (const auto& codec : codecs) {
codec->AddToMediaSection(*msection); codec->AddToMediaSection(*msection);
} }
@ -279,8 +340,16 @@ void JsepTrack::AddToMsection(
} }
void JsepTrack::UpdateSsrcs(SsrcGenerator& ssrcGenerator, size_t encodings) { void JsepTrack::UpdateSsrcs(SsrcGenerator& ssrcGenerator, size_t encodings) {
MOZ_ASSERT(mDirection == sdp::kSend); if (mDirection != sdp::kSend) {
MOZ_ASSERT(mType != SdpMediaSection::kApplication); MOZ_MTLOG(ML_ERROR, "UpdateSsrcs called on non-send track");
MOZ_ASSERT(false);
return;
}
if (mType == SdpMediaSection::kApplication) {
MOZ_MTLOG(ML_ERROR, "UpdateSsrcs called on application media section");
MOZ_ASSERT(false);
return;
}
size_t numSsrcs = std::max<size_t>(encodings, 1U); size_t numSsrcs = std::max<size_t>(encodings, 1U);
EnsureSsrcs(ssrcGenerator, numSsrcs); EnsureSsrcs(ssrcGenerator, numSsrcs);
@ -288,8 +357,10 @@ void JsepTrack::UpdateSsrcs(SsrcGenerator& ssrcGenerator, size_t encodings) {
if (mNegotiatedDetails && mNegotiatedDetails->GetEncodingCount() > numSsrcs) { if (mNegotiatedDetails && mNegotiatedDetails->GetEncodingCount() > numSsrcs) {
mNegotiatedDetails->TruncateEncodings(numSsrcs); mNegotiatedDetails->TruncateEncodings(numSsrcs);
} }
if (mSsrcs.empty()) {
MOZ_ASSERT(!mSsrcs.empty()); MOZ_MTLOG(ML_ERROR, "UpdateSsrcs resulted in empty mSsrcs");
MOZ_ASSERT(false);
}
} }
void JsepTrack::PruneSsrcs(size_t aNumSsrcs) { void JsepTrack::PruneSsrcs(size_t aNumSsrcs) {
@ -352,7 +423,12 @@ void JsepTrack::AddToMsection(const std::vector<std::string>& aRids,
UpdateSsrcs(ssrcGenerator, aRids.size()); UpdateSsrcs(ssrcGenerator, aRids.size());
if (requireRtxSsrcs) { if (requireRtxSsrcs) {
MOZ_ASSERT(mSsrcs.size() == mSsrcToRtxSsrc.size()); if (mSsrcs.size() != mSsrcToRtxSsrc.size()) {
MOZ_MTLOG(ML_ERROR,
"[" << mTrackId
<< "]: mSsrcToRtxSsrc has different size than mSsrcs.");
return;
}
std::vector<uint32_t> allSsrcs; std::vector<uint32_t> allSsrcs;
UniquePtr<SdpSsrcGroupAttributeList> group(new SdpSsrcGroupAttributeList); UniquePtr<SdpSsrcGroupAttributeList> group(new SdpSsrcGroupAttributeList);
for (const auto& ssrc : mSsrcs) { for (const auto& ssrc : mSsrcs) {

View file

@ -166,20 +166,7 @@ class JsepTrack {
virtual const std::vector<uint32_t>& GetSsrcs() const { return mSsrcs; } virtual const std::vector<uint32_t>& GetSsrcs() const { return mSsrcs; }
virtual std::vector<uint32_t> GetRtxSsrcs() const { virtual std::vector<uint32_t> GetRtxSsrcs() const;
std::vector<uint32_t> result;
if (mRtxIsAllowed &&
Preferences::GetBool("media.peerconnection.video.use_rtx", false) &&
!mSsrcToRtxSsrc.empty()) {
MOZ_ASSERT(mSsrcToRtxSsrc.size() == mSsrcs.size());
for (const auto ssrc : mSsrcs) {
auto it = mSsrcToRtxSsrc.find(ssrc);
MOZ_ASSERT(it != mSsrcToRtxSsrc.end());
result.push_back(it->second);
}
}
return result;
}
virtual void EnsureSsrcs(SsrcGenerator& ssrcGenerator, size_t aNumber); virtual void EnsureSsrcs(SsrcGenerator& ssrcGenerator, size_t aNumber);

View file

@ -13,6 +13,9 @@ LOCAL_INCLUDES += [
"/third_party/sipcc", "/third_party/sipcc",
] ]
UNIFIED_SOURCES += ["JsepSessionImpl.cpp", "JsepTrack.cpp", "SsrcGenerator.cpp"] UNIFIED_SOURCES += ["JsepSessionImpl.cpp", "SsrcGenerator.cpp"]
# Can not be built in unified build because of MOZ_MTLOG
SOURCES += ["JsepTrack.cpp"]
FINAL_LIBRARY = "xul" FINAL_LIBRARY = "xul"

View file

@ -257,6 +257,7 @@ void WebrtcGmpVideoEncoder::Close_g() {
mGMP = nullptr; mGMP = nullptr;
mHost = nullptr; mHost = nullptr;
mInitting = false; mInitting = false;
mInputImageMap.Clear();
if (mCachedPluginId) { if (mCachedPluginId) {
mReleasePluginEvent.Notify(*mCachedPluginId); mReleasePluginEvent.Notify(*mCachedPluginId);
@ -340,6 +341,14 @@ void WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange(uint32_t aWidth,
void WebrtcGmpVideoEncoder::Encode_g( void WebrtcGmpVideoEncoder::Encode_g(
const webrtc::VideoFrame& aInputImage, const webrtc::VideoFrame& aInputImage,
std::vector<webrtc::VideoFrameType> aFrameTypes) { std::vector<webrtc::VideoFrameType> aFrameTypes) {
auto reportDroppedOnExit = MakeScopeExit([&] {
MutexAutoLock lock(mCallbackMutex);
if (mCallback) {
mCallback->OnDroppedFrame(
webrtc::EncodedImageCallback::DropReason::kDroppedByEncoder);
}
});
if (!mGMP) { if (!mGMP) {
// destroyed via Terminate(), failed to init, or just not initted yet // destroyed via Terminate(), failed to init, or just not initted yet
GMP_LOG_DEBUG("GMP Encode: not initted yet"); GMP_LOG_DEBUG("GMP Encode: not initted yet");
@ -347,6 +356,13 @@ void WebrtcGmpVideoEncoder::Encode_g(
} }
MOZ_ASSERT(mHost); MOZ_ASSERT(mHost);
if (mInputImageMap.Length() >= kMaxImagesInFlight) {
GMP_LOG_WARNING(
"GMP Encode: Max number of frames already in flight. Dropping this "
"one.");
return;
}
if (static_cast<uint32_t>(aInputImage.width()) != mCodecParams.mWidth || if (static_cast<uint32_t>(aInputImage.width()) != mCodecParams.mWidth ||
static_cast<uint32_t>(aInputImage.height()) != mCodecParams.mHeight) { static_cast<uint32_t>(aInputImage.height()) != mCodecParams.mHeight) {
GMP_LOG_DEBUG("GMP Encode: resolution change from %ux%u to %dx%d", GMP_LOG_DEBUG("GMP Encode: resolution change from %ux%u to %dx%d",
@ -387,7 +403,9 @@ void WebrtcGmpVideoEncoder::Encode_g(
GMP_LOG_DEBUG("GMP Encode: failed to create frame"); GMP_LOG_DEBUG("GMP Encode: failed to create frame");
return; return;
} }
frame->SetTimestamp(AssertedCast<uint64_t>(aInputImage.ntp_time_ms() * 1000)); const auto gmpTimestamp =
AssertedCast<uint64_t>(aInputImage.ntp_time_ms() * 1000);
frame->SetTimestamp(gmpTimestamp);
GMPCodecSpecificInfo info{}; GMPCodecSpecificInfo info{};
info.mCodecType = kGMPVideoCodecH264; info.mCodecType = kGMPVideoCodecH264;
@ -421,18 +439,23 @@ void WebrtcGmpVideoEncoder::Encode_g(
MOZ_RELEASE_ASSERT(mInputImageMap.IsEmpty() || MOZ_RELEASE_ASSERT(mInputImageMap.IsEmpty() ||
mInputImageMap.LastElement().ntp_timestamp_ms < mInputImageMap.LastElement().ntp_timestamp_ms <
aInputImage.ntp_time_ms()); aInputImage.ntp_time_ms());
mInputImageMap.AppendElement(
InputImageData{.gmp_timestamp_us = frame->Timestamp(),
.ntp_timestamp_ms = aInputImage.ntp_time_ms(),
.timestamp_us = aInputImage.timestamp_us(),
.rtp_timestamp = aInputImage.rtp_timestamp(),
.frame_config = frameConfigs[0]});
GMP_LOG_DEBUG("GMP Encode: %" PRIu64, (frame->Timestamp())); GMP_LOG_DEBUG("GMP Encode: %" PRIu64, (frame->Timestamp()));
err = mGMP->Encode(std::move(frame), codecSpecificInfo, gmp_frame_types); err = mGMP->Encode(std::move(frame), codecSpecificInfo, gmp_frame_types);
if (err != GMPNoErr) { if (err != GMPNoErr) {
GMP_LOG_DEBUG("GMP Encode: failed to encode frame"); GMP_LOG_DEBUG("GMP Encode: failed to encode frame");
return;
} }
// Once in mInputImageMap, frame drops are reported by GMP callbacks
// (Encoded/Dropped).
reportDroppedOnExit.release();
mInputImageMap.AppendElement(
InputImageData{.gmp_timestamp_us = gmpTimestamp,
.ntp_timestamp_ms = aInputImage.ntp_time_ms(),
.timestamp_us = aInputImage.timestamp_us(),
.rtp_timestamp = aInputImage.rtp_timestamp(),
.frame_config = frameConfigs[0]});
} }
int32_t WebrtcGmpVideoEncoder::RegisterEncodeCompleteCallback( int32_t WebrtcGmpVideoEncoder::RegisterEncodeCompleteCallback(
@ -517,6 +540,7 @@ void WebrtcGmpVideoEncoder::Terminated() {
mGMP = nullptr; mGMP = nullptr;
mHost = nullptr; mHost = nullptr;
mInitting = false; mInitting = false;
mInputImageMap.Clear();
if (gmp) { if (gmp) {
// Do this last, since this could cause us to be destroyed // Do this last, since this could cause us to be destroyed
@ -526,20 +550,23 @@ void WebrtcGmpVideoEncoder::Terminated() {
// Could now notify that it's dead // Could now notify that it's dead
} }
static int32_t GmpTimestampComparator(const InputImageData& aA,
const InputImageData& aB) {
const auto& a = aA.gmp_timestamp_us;
const auto& b = aB.gmp_timestamp_us;
return a < b ? -1 : a != b;
}
void WebrtcGmpVideoEncoder::Encoded( void WebrtcGmpVideoEncoder::Encoded(
GMPVideoEncodedFrame* aEncodedFrame, GMPVideoEncodedFrame* aEncodedFrame,
const nsTArray<uint8_t>& aCodecSpecificInfo) { const nsTArray<uint8_t>& aCodecSpecificInfo) {
MOZ_ASSERT(mGMPThread->IsOnCurrentThread()); MOZ_ASSERT(mGMPThread->IsOnCurrentThread());
Maybe<InputImageData> data; Maybe<InputImageData> data;
auto gmp_timestamp_comparator = [](const InputImageData& aA, MOZ_ASSERT(!mInputImageMap.IsEmpty());
const InputImageData& aB) -> int32_t { MOZ_ASSERT(mInputImageMap.Length() <= kMaxImagesInFlight);
const auto& a = aA.gmp_timestamp_us;
const auto& b = aB.gmp_timestamp_us;
return a < b ? -1 : a != b;
};
size_t nextIdx = mInputImageMap.IndexOfFirstElementGt( size_t nextIdx = mInputImageMap.IndexOfFirstElementGt(
InputImageData{.gmp_timestamp_us = aEncodedFrame->TimeStamp()}, InputImageData{.gmp_timestamp_us = aEncodedFrame->TimeStamp()},
gmp_timestamp_comparator); GmpTimestampComparator);
const size_t numToRemove = nextIdx; const size_t numToRemove = nextIdx;
size_t numFramesDropped = numToRemove; size_t numFramesDropped = numToRemove;
MOZ_ASSERT(nextIdx != 0); MOZ_ASSERT(nextIdx != 0);
@ -676,6 +703,34 @@ void WebrtcGmpVideoEncoder::Encoded(
mCallback->OnEncodedImage(unit, &info); mCallback->OnEncodedImage(unit, &info);
} }
void WebrtcGmpVideoEncoder::Dropped(uint64_t aTimestamp) {
MOZ_ASSERT(mGMPThread->IsOnCurrentThread());
MOZ_ASSERT(!mInputImageMap.IsEmpty());
MOZ_ASSERT(mInputImageMap.Length() <= kMaxImagesInFlight);
size_t nextIdx = mInputImageMap.IndexOfFirstElementGt(
InputImageData{.gmp_timestamp_us = aTimestamp}, GmpTimestampComparator);
const size_t numDropped = nextIdx;
MOZ_ASSERT(nextIdx != 0);
MOZ_ASSERT(mInputImageMap.ElementAt(nextIdx - 1).gmp_timestamp_us ==
aTimestamp);
mInputImageMap.RemoveElementsAt(0, numDropped);
GMP_LOG_DEBUG("GMP Dropped: %" PRIu64
" dropped by encoder. Reporting %u frames dropped.",
aTimestamp, static_cast<uint32_t>(numDropped));
MutexAutoLock lock(mCallbackMutex);
if (!mCallback) {
return;
}
for (size_t i = 0; i < numDropped; ++i) {
mCallback->OnDroppedFrame(
webrtc::EncodedImageCallback::DropReason::kDroppedByEncoder);
}
}
// Decoder. // Decoder.
WebrtcGmpVideoDecoder::WebrtcGmpVideoDecoder(std::string aPCHandle, WebrtcGmpVideoDecoder::WebrtcGmpVideoDecoder(std::string aPCHandle,
TrackingId aTrackingId) TrackingId aTrackingId)

View file

@ -204,6 +204,8 @@ class WebrtcGmpVideoEncoder final : public GMPVideoEncoderCallbackProxy,
void Encoded(GMPVideoEncodedFrame* aEncodedFrame, void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
const nsTArray<uint8_t>& aCodecSpecificInfo) override; const nsTArray<uint8_t>& aCodecSpecificInfo) override;
void Dropped(uint64_t aTimestamp) override;
void Error(GMPErr aError) override {} void Error(GMPErr aError) override {}
private: private:

View file

@ -8,7 +8,17 @@
#include <cstring> #include <cstring>
#include "ipc/EnumSerializer.h"
#include "ipc/IPCMessageUtils.h" #include "ipc/IPCMessageUtils.h"
#include "ipc/IPCMessageUtilsSpecializations.h"
namespace IPC {
template <>
struct ParamTraits<mozilla::MediaPacket::Type>
: public ContiguousEnumSerializerInclusive<
mozilla::MediaPacket::Type, mozilla::MediaPacket::UNCLASSIFIED,
mozilla::MediaPacket::SCTP> {};
} // namespace IPC
namespace mozilla { namespace mozilla {
@ -31,66 +41,52 @@ MediaPacket MediaPacket::Clone() const {
} }
void MediaPacket::Serialize(IPC::MessageWriter* aWriter) const { void MediaPacket::Serialize(IPC::MessageWriter* aWriter) const {
aWriter->WriteUInt32(len_); WriteParam(aWriter, len_);
aWriter->WriteUInt32(capacity_); WriteParam(aWriter, capacity_);
WriteParam(aWriter, encrypted_len_);
WriteParam(aWriter, sdp_level_);
WriteParam(aWriter, type_);
if (len_) { if (len_) {
aWriter->WriteBytes(data_.get(), len_); aWriter->WriteBytes(data_.get(), len_);
} }
aWriter->WriteUInt32(encrypted_len_);
if (encrypted_len_) { if (encrypted_len_) {
aWriter->WriteBytes(encrypted_data_.get(), encrypted_len_); aWriter->WriteBytes(encrypted_data_.get(), encrypted_len_);
} }
aWriter->WriteInt32(sdp_level_.isSome() ? *sdp_level_ : -1);
aWriter->WriteInt32(type_);
} }
bool MediaPacket::Deserialize(IPC::MessageReader* aReader) { bool MediaPacket::Deserialize(IPC::MessageReader* aReader) {
Reset(); Reset();
uint32_t len; if (!ReadParam(aReader, &len_) || !ReadParam(aReader, &capacity_) ||
if (!aReader->ReadUInt32(&len)) { !ReadParam(aReader, &encrypted_len_) ||
!ReadParam(aReader, &sdp_level_) || !ReadParam(aReader, &type_)) {
return false; return false;
} }
uint32_t capacity;
if (!aReader->ReadUInt32(&capacity)) { if (capacity_ < len_) {
return false; return false;
} }
if (len) {
MOZ_RELEASE_ASSERT(capacity >= len); // Kinda arbitrary, but we want some sort of ceiling here.
UniquePtr<uint8_t[]> data(new uint8_t[capacity]); if ((capacity_ > 1024 * 1024) || (encrypted_len_ > 1024 * 1024)) {
if (!aReader->ReadBytesInto(data.get(), len)) { return false;
}
if (capacity_) {
data_.reset(new uint8_t[capacity_]);
if (len_) {
if (!aReader->ReadBytesInto(data_.get(), len_)) {
return false;
}
}
}
if (encrypted_len_) {
encrypted_data_.reset(new uint8_t[encrypted_len_]);
if (!aReader->ReadBytesInto(encrypted_data_.get(), encrypted_len_)) {
return false; return false;
} }
data_ = std::move(data);
len_ = len;
capacity_ = capacity;
} }
if (!aReader->ReadUInt32(&len)) {
return false;
}
if (len) {
UniquePtr<uint8_t[]> data(new uint8_t[len]);
if (!aReader->ReadBytesInto(data.get(), len)) {
return false;
}
encrypted_data_ = std::move(data);
encrypted_len_ = len;
}
int32_t sdp_level;
if (!aReader->ReadInt32(&sdp_level)) {
return false;
}
if (sdp_level >= 0) {
sdp_level_ = Some(sdp_level);
}
int32_t type;
if (!aReader->ReadInt32(&type)) {
return false;
}
type_ = static_cast<Type>(type);
return true; return true;
} }

View file

@ -75,6 +75,19 @@ nsresult NrIceStunAddr::Deserialize(const char* buffer, size_t buffer_size) {
nr_local_addr* from_addr = nr_local_addr* from_addr =
const_cast<nr_local_addr*>((const nr_local_addr*)buffer); const_cast<nr_local_addr*>((const nr_local_addr*)buffer);
// Just in case
constexpr size_t ifname_size =
sizeof(from_addr->addr.ifname) / sizeof(from_addr->addr.ifname[0]);
constexpr size_t as_string_size =
sizeof(from_addr->addr.as_string) / sizeof(from_addr->addr.as_string[0]);
constexpr size_t fqdn_size =
sizeof(from_addr->addr.fqdn) / sizeof(from_addr->addr.fqdn[0]);
from_addr->addr.ifname[ifname_size - 1] = '\0';
from_addr->addr.as_string[as_string_size - 1] = '\0';
from_addr->addr.fqdn[fqdn_size - 1] = '\0';
from_addr->addr.is_proxied = !!from_addr->addr.is_proxied;
from_addr->addr.tls = !!from_addr->addr.tls;
// At this point, from_addr->addr.addr is invalid (null), but will // At this point, from_addr->addr.addr is invalid (null), but will
// be fixed by nr_local_addr_copy. // be fixed by nr_local_addr_copy.
if (nr_local_addr_copy(localAddr_, from_addr)) { if (nr_local_addr_copy(localAddr_, from_addr)) {

View file

@ -334,6 +334,10 @@ RefPtr<GenericPromise> RemoteWorkerController::SetServiceWorkerSkipWaitingFlag()
AssertIsOnBackgroundThread(); AssertIsOnBackgroundThread();
MOZ_ASSERT(mObserver); MOZ_ASSERT(mObserver);
if (!mIsServiceWorker) {
return GenericPromise::CreateAndResolve(false, __func__);
}
RefPtr<GenericPromise::Private> promise = RefPtr<GenericPromise::Private> promise =
new GenericPromise::Private(__func__); new GenericPromise::Private(__func__);

View file

@ -1765,12 +1765,10 @@ void FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex,
void FilterNodeComponentTransferSoftware::GenerateLookupTable( void FilterNodeComponentTransferSoftware::GenerateLookupTable(
ptrdiff_t aComponent, uint8_t aTables[4][256], bool aDisabled) { ptrdiff_t aComponent, uint8_t aTables[4][256], bool aDisabled) {
if (aDisabled) { if (aDisabled || !FillLookupTable(aComponent, aTables[aComponent])) {
for (int32_t i = 0; i < 256; ++i) { for (int32_t i = 0; i < 256; ++i) {
aTables[aComponent][i] = i; aTables[aComponent][i] = i;
} }
} else {
FillLookupTable(aComponent, aTables[aComponent]);
} }
} }
@ -1927,32 +1925,29 @@ void FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex,
Invalidate(); Invalidate();
} }
void FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent, bool FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
uint8_t aTable[256]) { uint8_t aTable[256]) {
switch (aComponent) { switch (aComponent) {
case B8G8R8A8_COMPONENT_BYTEOFFSET_R: case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
FillLookupTableImpl(mTableR, aTable); return FillLookupTableImpl(mTableR, aTable);
break; break;
case B8G8R8A8_COMPONENT_BYTEOFFSET_G: case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
FillLookupTableImpl(mTableG, aTable); return FillLookupTableImpl(mTableG, aTable);
break;
case B8G8R8A8_COMPONENT_BYTEOFFSET_B: case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
FillLookupTableImpl(mTableB, aTable); return FillLookupTableImpl(mTableB, aTable);
break;
case B8G8R8A8_COMPONENT_BYTEOFFSET_A: case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
FillLookupTableImpl(mTableA, aTable); return FillLookupTableImpl(mTableA, aTable);
break;
default: default:
MOZ_ASSERT(false, "unknown component"); MOZ_ASSERT(false, "unknown component");
break; return false;
} }
} }
void FilterNodeTableTransferSoftware::FillLookupTableImpl( bool FilterNodeTableTransferSoftware::FillLookupTableImpl(
std::vector<Float>& aTableValues, uint8_t aTable[256]) { std::vector<Float>& aTableValues, uint8_t aTable[256]) {
uint32_t tvLength = aTableValues.size(); uint32_t tvLength = aTableValues.size();
if (tvLength < 2) { if (tvLength < 1) {
return; return false;
} }
for (size_t i = 0; i < 256; i++) { for (size_t i = 0; i < 256; i++) {
@ -1965,6 +1960,7 @@ void FilterNodeTableTransferSoftware::FillLookupTableImpl(
val = std::max(0, val); val = std::max(0, val);
aTable[i] = val; aTable[i] = val;
} }
return true;
} }
void FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex, void FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex,
@ -1990,32 +1986,28 @@ void FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex,
Invalidate(); Invalidate();
} }
void FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent, bool FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
uint8_t aTable[256]) { uint8_t aTable[256]) {
switch (aComponent) { switch (aComponent) {
case B8G8R8A8_COMPONENT_BYTEOFFSET_R: case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
FillLookupTableImpl(mTableR, aTable); return FillLookupTableImpl(mTableR, aTable);
break;
case B8G8R8A8_COMPONENT_BYTEOFFSET_G: case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
FillLookupTableImpl(mTableG, aTable); return FillLookupTableImpl(mTableG, aTable);
break;
case B8G8R8A8_COMPONENT_BYTEOFFSET_B: case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
FillLookupTableImpl(mTableB, aTable); return FillLookupTableImpl(mTableB, aTable);
break;
case B8G8R8A8_COMPONENT_BYTEOFFSET_A: case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
FillLookupTableImpl(mTableA, aTable); return FillLookupTableImpl(mTableA, aTable);
break;
default: default:
MOZ_ASSERT(false, "unknown component"); MOZ_ASSERT(false, "unknown component");
break; return false;
} }
} }
void FilterNodeDiscreteTransferSoftware::FillLookupTableImpl( bool FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(
std::vector<Float>& aTableValues, uint8_t aTable[256]) { std::vector<Float>& aTableValues, uint8_t aTable[256]) {
uint32_t tvLength = aTableValues.size(); uint32_t tvLength = aTableValues.size();
if (tvLength < 1) { if (tvLength < 1) {
return; return false;
} }
for (size_t i = 0; i < 256; i++) { for (size_t i = 0; i < 256; i++) {
@ -2027,6 +2019,7 @@ void FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(
val = std::max(0, val); val = std::max(0, val);
aTable[i] = val; aTable[i] = val;
} }
return true;
} }
FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware() FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
@ -2072,28 +2065,24 @@ void FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex,
Invalidate(); Invalidate();
} }
void FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent, bool FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
uint8_t aTable[256]) { uint8_t aTable[256]) {
switch (aComponent) { switch (aComponent) {
case B8G8R8A8_COMPONENT_BYTEOFFSET_R: case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
FillLookupTableImpl(mSlopeR, mInterceptR, aTable); return FillLookupTableImpl(mSlopeR, mInterceptR, aTable);
break;
case B8G8R8A8_COMPONENT_BYTEOFFSET_G: case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
FillLookupTableImpl(mSlopeG, mInterceptG, aTable); return FillLookupTableImpl(mSlopeG, mInterceptG, aTable);
break;
case B8G8R8A8_COMPONENT_BYTEOFFSET_B: case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
FillLookupTableImpl(mSlopeB, mInterceptB, aTable); return FillLookupTableImpl(mSlopeB, mInterceptB, aTable);
break;
case B8G8R8A8_COMPONENT_BYTEOFFSET_A: case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
FillLookupTableImpl(mSlopeA, mInterceptA, aTable); return FillLookupTableImpl(mSlopeA, mInterceptA, aTable);
break;
default: default:
MOZ_ASSERT(false, "unknown component"); MOZ_ASSERT(false, "unknown component");
break; return false;
} }
} }
void FilterNodeLinearTransferSoftware::FillLookupTableImpl( bool FilterNodeLinearTransferSoftware::FillLookupTableImpl(
Float aSlope, Float aIntercept, uint8_t aTable[256]) { Float aSlope, Float aIntercept, uint8_t aTable[256]) {
for (size_t i = 0; i < 256; i++) { for (size_t i = 0; i < 256; i++) {
int32_t val = NS_lround(aSlope * i + 255 * aIntercept); int32_t val = NS_lround(aSlope * i + 255 * aIntercept);
@ -2101,6 +2090,7 @@ void FilterNodeLinearTransferSoftware::FillLookupTableImpl(
val = std::max(0, val); val = std::max(0, val);
aTable[i] = val; aTable[i] = val;
} }
return true;
} }
FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware() FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
@ -2162,28 +2152,24 @@ void FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex,
Invalidate(); Invalidate();
} }
void FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent, bool FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
uint8_t aTable[256]) { uint8_t aTable[256]) {
switch (aComponent) { switch (aComponent) {
case B8G8R8A8_COMPONENT_BYTEOFFSET_R: case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable); return FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable);
break;
case B8G8R8A8_COMPONENT_BYTEOFFSET_G: case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable); return FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable);
break;
case B8G8R8A8_COMPONENT_BYTEOFFSET_B: case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable); return FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable);
break;
case B8G8R8A8_COMPONENT_BYTEOFFSET_A: case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable); return FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable);
break;
default: default:
MOZ_ASSERT(false, "unknown component"); MOZ_ASSERT(false, "unknown component");
break; return false;
} }
} }
void FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude, bool FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
Float aExponent, Float aExponent,
Float aOffset, Float aOffset,
uint8_t aTable[256]) { uint8_t aTable[256]) {
@ -2194,12 +2180,14 @@ void FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
val = std::max(0, val); val = std::max(0, val);
aTable[i] = val; aTable[i] = val;
} }
return true;
} }
FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware() FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
: mDivisor(0), : mDivisor(0),
mBias(0), mBias(0),
mEdgeMode(EDGE_MODE_DUPLICATE), mEdgeMode(EDGE_MODE_DUPLICATE),
mKernelUnitLength(1.0f, 1.0f),
mPreserveAlpha(false) {} mPreserveAlpha(false) {}
int32_t FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) { int32_t FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) {
@ -2245,7 +2233,21 @@ void FilterNodeConvolveMatrixSoftware::SetAttribute(
uint32_t aIndex, const Size& aKernelUnitLength) { uint32_t aIndex, const Size& aKernelUnitLength) {
switch (aIndex) { switch (aIndex) {
case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH: case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH:
// Spec for feConvolveMatrix:
// If the attribute (kernelUnitLength) is not specified, the default value
// is one pixel in the offscreen bitmap. If a negative or zero value is
// specified the default value will be used instead. The first number is
// the x value. The second number is the y value. If the value is not
// specified, it defaults to the same value as x.
mKernelUnitLength = aKernelUnitLength; mKernelUnitLength = aKernelUnitLength;
if (mKernelUnitLength.width <= 0.0f ||
!std::isfinite(mKernelUnitLength.width)) {
mKernelUnitLength.width = 1.0f;
}
if (mKernelUnitLength.height <= 0.0f ||
!std::isfinite(mKernelUnitLength.height)) {
mKernelUnitLength.height = mKernelUnitLength.width;
}
break; break;
default: default:
MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute"); MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
@ -2446,20 +2448,26 @@ template <typename CoordType>
already_AddRefed<DataSourceSurface> FilterNodeConvolveMatrixSoftware::DoRender( already_AddRefed<DataSourceSurface> FilterNodeConvolveMatrixSoftware::DoRender(
const IntRect& aRect, CoordType aKernelUnitLengthX, const IntRect& aRect, CoordType aKernelUnitLengthX,
CoordType aKernelUnitLengthY) { CoordType aKernelUnitLengthY) {
// Ensure multiply fits in an int32_t so convolve math won't overflow.
auto kernelArea = CheckedInt32(mKernelSize.width) * mKernelSize.height;
if (mKernelSize.width <= 0 || mKernelSize.height <= 0 || if (mKernelSize.width <= 0 || mKernelSize.height <= 0 ||
mKernelMatrix.size() != !kernelArea.isValid() ||
uint32_t(mKernelSize.width * mKernelSize.height) || mKernelMatrix.size() != size_t(kernelArea.value()) ||
!IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) || !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) ||
mDivisor == 0) { mDivisor == 0) {
return Factory::CreateDataSourceSurface(aRect.Size(), return Factory::CreateDataSourceSurface(aRect.Size(),
SurfaceFormat::B8G8R8A8, true); SurfaceFormat::B8G8R8A8, true);
} }
IntRect srcRect = InflatedSourceRect(aRect); RectDouble srcRectD(aRect);
srcRectD.Inflate(GetInflateSourceMargin());
// Inflate the source rect by another pixel because the bilinear filtering in // Inflate the source rect by another pixel because the bilinear filtering in
// ColorComponentAtPoint may want to access the margins. // ColorComponentAtPoint may want to access the margins.
srcRect.Inflate(1); srcRectD.Inflate(1);
if (!RectIsInt32Safe(srcRectD)) {
return nullptr;
}
IntRect srcRect = TruncatedToInt(srcRectD);
RefPtr<DataSourceSurface> input = RefPtr<DataSourceSurface> input =
GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect, GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect,
@ -2538,23 +2546,26 @@ IntRect FilterNodeConvolveMatrixSoftware::MapRectToSource(
aMax, aSourceNode); aMax, aSourceNode);
} }
MarginDouble FilterNodeConvolveMatrixSoftware::GetInflateSourceMargin() const {
double kulX = double(mKernelUnitLength.width);
double kulY = double(mKernelUnitLength.height);
MarginDouble margin;
margin.left = ceil(mTarget.x * kulX);
margin.top = ceil(mTarget.y * kulY);
margin.right = ceil((mKernelSize.width - mTarget.x - 1) * kulX);
margin.bottom = ceil((mKernelSize.height - mTarget.y - 1) * kulY);
return margin;
}
IntRect FilterNodeConvolveMatrixSoftware::InflatedSourceRect( IntRect FilterNodeConvolveMatrixSoftware::InflatedSourceRect(
const IntRect& aDestRect) { const IntRect& aDestRect) {
if (aDestRect.IsEmpty()) { if (aDestRect.IsEmpty()) {
return IntRect(); return IntRect();
} }
IntMargin margin; RectDouble srcRect(aDestRect);
margin.left = static_cast<int32_t>(ceil(mTarget.x * mKernelUnitLength.width)); srcRect.Inflate(GetInflateSourceMargin());
margin.top = static_cast<int32_t>(ceil(mTarget.y * mKernelUnitLength.height)); return RectIsInt32Safe(srcRect) ? TruncatedToInt(srcRect) : aDestRect;
margin.right = static_cast<int32_t>(
ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width));
margin.bottom = static_cast<int32_t>(
ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height));
IntRect srcRect = aDestRect;
srcRect.Inflate(margin);
return srcRect;
} }
IntRect FilterNodeConvolveMatrixSoftware::InflatedDestRect( IntRect FilterNodeConvolveMatrixSoftware::InflatedDestRect(
@ -2563,19 +2574,12 @@ IntRect FilterNodeConvolveMatrixSoftware::InflatedDestRect(
return IntRect(); return IntRect();
} }
IntMargin margin; RectDouble destRect(aSourceRect);
margin.left = static_cast<int32_t>( MarginDouble margin = GetInflateSourceMargin();
ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width)); std::swap(margin.left, margin.right);
margin.top = static_cast<int32_t>( std::swap(margin.top, margin.bottom);
ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height));
margin.right =
static_cast<int32_t>(ceil(mTarget.x * mKernelUnitLength.width));
margin.bottom =
static_cast<int32_t>(ceil(mTarget.y * mKernelUnitLength.height));
IntRect destRect = aSourceRect;
destRect.Inflate(margin); destRect.Inflate(margin);
return destRect; return RectIsInt32Safe(destRect) ? TruncatedToInt(destRect) : aSourceRect;
} }
IntRect FilterNodeConvolveMatrixSoftware::GetOutputRectInRect( IntRect FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(
@ -2609,6 +2613,11 @@ void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
uint32_t aValue) { uint32_t aValue) {
// Refuse channel values that exceed channel maximum.
if (aValue > ColorChannel::COLOR_CHANNEL_MAX) {
return;
}
switch (aIndex) { switch (aIndex) {
case ATT_DISPLACEMENT_MAP_X_CHANNEL: case ATT_DISPLACEMENT_MAP_X_CHANNEL:
mChannelX = static_cast<ColorChannel>(aValue); mChannelX = static_cast<ColorChannel>(aValue);
@ -2655,7 +2664,7 @@ already_AddRefed<DataSourceSurface> FilterNodeDisplacementMapSoftware::Render(
uint8_t* targetData = targetMap.GetData(); uint8_t* targetData = targetMap.GetData();
int32_t targetStride = targetMap.GetStride(); int32_t targetStride = targetMap.GetStride();
static const ptrdiff_t channelMap[4] = { static const ptrdiff_t channelMap[COLOR_CHANNEL_MAX + 1] = {
B8G8R8A8_COMPONENT_BYTEOFFSET_R, B8G8R8A8_COMPONENT_BYTEOFFSET_G, B8G8R8A8_COMPONENT_BYTEOFFSET_R, B8G8R8A8_COMPONENT_BYTEOFFSET_G,
B8G8R8A8_COMPONENT_BYTEOFFSET_B, B8G8R8A8_COMPONENT_BYTEOFFSET_A}; B8G8R8A8_COMPONENT_BYTEOFFSET_B, B8G8R8A8_COMPONENT_BYTEOFFSET_A};
uint16_t xChannel = channelMap[mChannelX]; uint16_t xChannel = channelMap[mChannelX];
@ -3346,7 +3355,8 @@ static inline Point3D Normalized(const Point3D& vec) {
template <typename LightType, typename LightingType> template <typename LightType, typename LightingType>
FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware( FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(
const char* aTypeName) const char* aTypeName)
: mSurfaceScale(0) : mSurfaceScale(0),
mKernelUnitLength(1.0f, 1.0f)
#if defined(MOZILLA_INTERNAL_API) && defined(NS_BUILD_REFCNT_LOGGING) #if defined(MOZILLA_INTERNAL_API) && defined(NS_BUILD_REFCNT_LOGGING)
, ,
mTypeName(aTypeName) mTypeName(aTypeName)
@ -3399,6 +3409,23 @@ void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
switch (aIndex) { switch (aIndex) {
case ATT_LIGHTING_KERNEL_UNIT_LENGTH: case ATT_LIGHTING_KERNEL_UNIT_LENGTH:
mKernelUnitLength = aKernelUnitLength; mKernelUnitLength = aKernelUnitLength;
// Spec for fe*Lighting:
// The first number is the <dx> value. The second number is the <dy>
// value. If the <dy> value is not specified, it defaults to the same
// value as <dx>. If kernelUnitLength is not specified, the dx and dy
// values should represent very small deltas relative to a given (x,y)
// position, which might be implemented in some cases as one pixel in the
// intermediate image offscreen bitmap, which is a pixel-based coordinate
// system, and thus potentially not scalable. If a negative or zero value
// is specified the default value will be used instead.
if (mKernelUnitLength.width <= 0.0f ||
!std::isfinite(mKernelUnitLength.width)) {
mKernelUnitLength.width = 1.0f;
}
if (mKernelUnitLength.height <= 0.0f ||
!std::isfinite(mKernelUnitLength.height)) {
mKernelUnitLength.height = mKernelUnitLength.width;
}
break; break;
default: default:
MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size"); MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size");
@ -3547,22 +3574,33 @@ FilterNodeLightingSoftware<LightType, LightingType>::Render(
return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height); return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
} }
template <typename LightType, typename LightingType>
MarginDouble FilterNodeLightingSoftware<
LightType, LightingType>::GetInflateSourceMargin() const {
double kulX = ceil(double(mKernelUnitLength.width));
double kulY = ceil(double(mKernelUnitLength.height));
return MarginDouble(kulY, kulX, kulY, kulX);
}
template <typename LightType, typename LightingType>
IntRect FilterNodeLightingSoftware<LightType, LightingType>::InflatedSourceRect(
const IntRect& aDestRect) {
RectDouble srcRect(aDestRect);
srcRect.Inflate(GetInflateSourceMargin());
return RectIsInt32Safe(srcRect) ? TruncatedToInt(srcRect) : aDestRect;
}
template <typename LightType, typename LightingType> template <typename LightType, typename LightingType>
void FilterNodeLightingSoftware< void FilterNodeLightingSoftware<
LightType, LightingType>::RequestFromInputsForRect(const IntRect& aRect) { LightType, LightingType>::RequestFromInputsForRect(const IntRect& aRect) {
IntRect srcRect = aRect; RequestInputRect(IN_LIGHTING_IN, InflatedSourceRect(aRect));
srcRect.Inflate(ceil(mKernelUnitLength.width),
ceil(mKernelUnitLength.height));
RequestInputRect(IN_LIGHTING_IN, srcRect);
} }
template <typename LightType, typename LightingType> template <typename LightType, typename LightingType>
IntRect FilterNodeLightingSoftware<LightType, LightingType>::MapRectToSource( IntRect FilterNodeLightingSoftware<LightType, LightingType>::MapRectToSource(
const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) { const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
IntRect srcRect = aRect; return MapInputRectToSource(IN_LIGHTING_IN, InflatedSourceRect(aRect), aMax,
srcRect.Inflate(ceil(mKernelUnitLength.width), aSourceNode);
ceil(mKernelUnitLength.height));
return MapInputRectToSource(IN_LIGHTING_IN, srcRect, aMax, aSourceNode);
} }
template <typename LightType, typename LightingType> template <typename LightType, typename LightingType>
@ -3576,14 +3614,17 @@ FilterNodeLightingSoftware<LightType, LightingType>::DoRender(
MOZ_ASSERT(aKernelUnitLengthY > 0, MOZ_ASSERT(aKernelUnitLengthY > 0,
"aKernelUnitLengthY can be a negative or zero value"); "aKernelUnitLengthY can be a negative or zero value");
IntRect srcRect = aRect; RectDouble srcRectD(aRect);
IntSize size = aRect.Size(); srcRectD.Inflate(GetInflateSourceMargin());
srcRect.Inflate(ceil(float(aKernelUnitLengthX)),
ceil(float(aKernelUnitLengthY)));
// Inflate the source rect by another pixel because the bilinear filtering in // Inflate the source rect by another pixel because the bilinear filtering in
// ColorComponentAtPoint may want to access the margins. // ColorComponentAtPoint may want to access the margins.
srcRect.Inflate(1); srcRectD.Inflate(1);
if (!RectIsInt32Safe(srcRectD)) {
return nullptr;
}
IntRect srcRect = TruncatedToInt(srcRectD);
IntSize size = aRect.Size();
RefPtr<DataSourceSurface> input = GetInputDataSourceSurface( RefPtr<DataSourceSurface> input = GetInputDataSourceSurface(
IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8, EDGE_MODE_NONE); IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8, EDGE_MODE_NONE);

View file

@ -360,7 +360,7 @@ class FilterNodeComponentTransferSoftware : public FilterNodeSoftware {
void RequestFromInputsForRect(const IntRect& aRect) override; void RequestFromInputsForRect(const IntRect& aRect) override;
virtual void GenerateLookupTable(ptrdiff_t aComponent, virtual void GenerateLookupTable(ptrdiff_t aComponent,
uint8_t aTables[4][256], bool aDisabled); uint8_t aTables[4][256], bool aDisabled);
virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) = 0; virtual bool FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) = 0;
bool mDisableR; bool mDisableR;
bool mDisableG; bool mDisableG;
@ -379,10 +379,10 @@ class FilterNodeTableTransferSoftware
uint32_t aSize) override; uint32_t aSize) override;
protected: protected:
void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override; bool FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
private: private:
void FillLookupTableImpl(std::vector<Float>& aTableValues, bool FillLookupTableImpl(std::vector<Float>& aTableValues,
uint8_t aTable[256]); uint8_t aTable[256]);
std::vector<Float> mTableR; std::vector<Float> mTableR;
@ -402,10 +402,10 @@ class FilterNodeDiscreteTransferSoftware
uint32_t aSize) override; uint32_t aSize) override;
protected: protected:
void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override; bool FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
private: private:
void FillLookupTableImpl(std::vector<Float>& aTableValues, bool FillLookupTableImpl(std::vector<Float>& aTableValues,
uint8_t aTable[256]); uint8_t aTable[256]);
std::vector<Float> mTableR; std::vector<Float> mTableR;
@ -425,10 +425,10 @@ class FilterNodeLinearTransferSoftware
void SetAttribute(uint32_t aIndex, Float aValue) override; void SetAttribute(uint32_t aIndex, Float aValue) override;
protected: protected:
void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override; bool FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
private: private:
void FillLookupTableImpl(Float aSlope, Float aIntercept, uint8_t aTable[256]); bool FillLookupTableImpl(Float aSlope, Float aIntercept, uint8_t aTable[256]);
Float mSlopeR; Float mSlopeR;
Float mSlopeG; Float mSlopeG;
@ -451,10 +451,10 @@ class FilterNodeGammaTransferSoftware
void SetAttribute(uint32_t aIndex, Float aValue) override; void SetAttribute(uint32_t aIndex, Float aValue) override;
protected: protected:
void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override; bool FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
private: private:
void FillLookupTableImpl(Float aAmplitude, Float aExponent, Float aOffset, bool FillLookupTableImpl(Float aAmplitude, Float aExponent, Float aOffset,
uint8_t aTable[256]); uint8_t aTable[256]);
Float mAmplitudeR; Float mAmplitudeR;
@ -502,6 +502,7 @@ class FilterNodeConvolveMatrixSoftware : public FilterNodeSoftware {
CoordType aKernelUnitLengthX, CoordType aKernelUnitLengthX,
CoordType aKernelUnitLengthY); CoordType aKernelUnitLengthY);
MarginDouble GetInflateSourceMargin() const;
IntRect InflatedSourceRect(const IntRect& aDestRect); IntRect InflatedSourceRect(const IntRect& aDestRect);
IntRect InflatedDestRect(const IntRect& aSourceRect); IntRect InflatedDestRect(const IntRect& aSourceRect);
@ -764,6 +765,9 @@ class FilterNodeLightingSoftware : public FilterNodeSoftware {
CoordType aKernelUnitLengthX, CoordType aKernelUnitLengthX,
CoordType aKernelUnitLengthY); CoordType aKernelUnitLengthY);
MarginDouble GetInflateSourceMargin() const;
IntRect InflatedSourceRect(const IntRect& aDestRect);
LightType mLight; LightType mLight;
LightingType mLighting; LightingType mLighting;
Float mSurfaceScale; Float mSurfaceScale;

View file

@ -193,7 +193,8 @@ enum ColorChannel {
COLOR_CHANNEL_R = 0, COLOR_CHANNEL_R = 0,
COLOR_CHANNEL_G, COLOR_CHANNEL_G,
COLOR_CHANNEL_B, COLOR_CHANNEL_B,
COLOR_CHANNEL_A COLOR_CHANNEL_A,
COLOR_CHANNEL_MAX = COLOR_CHANNEL_A
}; };
enum DisplacementMapInputs { enum DisplacementMapInputs {

View file

@ -169,6 +169,11 @@ struct MOZ_EMPTY_BASES IntRectTyped
aRect.Height()); aRect.Height());
} }
static IntRectTyped<Units> Truncate(const RectTyped<Units, double>& aRect) {
return IntRectTyped(int32_t(aRect.X()), int32_t(aRect.Y()),
int32_t(aRect.Width()), int32_t(aRect.Height()));
}
// Rounding isn't meaningful on an integer rectangle. // Rounding isn't meaningful on an integer rectangle.
void Round() {} void Round() {}
void RoundIn() {} void RoundIn() {}
@ -338,10 +343,10 @@ IntRectTyped<Units> RoundedToInt(const RectTyped<Units>& aRect) {
int32_t(copy.Width()), int32_t(copy.Height())); int32_t(copy.Width()), int32_t(copy.Height()));
} }
template <class Units> template <class Units, class F>
bool RectIsInt32Safe(const RectTyped<Units>& aRect) { bool RectIsInt32Safe(const RectTyped<Units, F>& aRect) {
float min = (float)std::numeric_limits<std::int32_t>::min(); F min = (F)std::numeric_limits<int32_t>::min();
float max = (float)std::numeric_limits<std::int32_t>::max(); F max = (F)std::numeric_limits<int32_t>::max();
return aRect.x > min && aRect.y > min && aRect.width < max && return aRect.x > min && aRect.y > min && aRect.width < max &&
aRect.height < max && aRect.XMost() < max && aRect.YMost() < max; aRect.height < max && aRect.XMost() < max && aRect.YMost() < max;
} }
@ -356,8 +361,8 @@ IntRectTyped<Units> RoundedOut(const RectTyped<Units>& aRect) {
return IntRectTyped<Units>::RoundOut(aRect); return IntRectTyped<Units>::RoundOut(aRect);
} }
template <class Units> template <class Units, class F>
IntRectTyped<Units> TruncatedToInt(const RectTyped<Units>& aRect) { IntRectTyped<Units> TruncatedToInt(const RectTyped<Units, F>& aRect) {
return IntRectTyped<Units>::Truncate(aRect); return IntRectTyped<Units>::Truncate(aRect);
} }

View file

@ -784,7 +784,7 @@ cairo_font_face_t* ScaledFontMac::CreateCairoFontFace(
already_AddRefed<UnscaledFont> UnscaledFontMac::CreateFromFontDescriptor( already_AddRefed<UnscaledFont> UnscaledFontMac::CreateFromFontDescriptor(
const uint8_t* aData, uint32_t aDataLength, uint32_t aIndex) { const uint8_t* aData, uint32_t aDataLength, uint32_t aIndex) {
if (aDataLength == 0) { if (aDataLength == 0 || aIndex > aDataLength) {
gfxWarning() << "Mac font descriptor is truncated."; gfxWarning() << "Mac font descriptor is truncated.";
return nullptr; return nullptr;
} }

View file

@ -61,7 +61,7 @@ class GLContextEGL final : public GLContext {
} }
static GLContextEGL* Cast(GLContext* gl) { static GLContextEGL* Cast(GLContext* gl) {
MOZ_ASSERT(gl->GetContextType() == GLContextType::EGL); MOZ_RELEASE_ASSERT(gl->GetContextType() == GLContextType::EGL);
return static_cast<GLContextEGL*>(gl); return static_cast<GLContextEGL*>(gl);
} }

View file

@ -1042,6 +1042,21 @@ hb_unsigned_mul_overflows (unsigned int count, unsigned int size, unsigned *resu
return (size > 0) && (count >= ((unsigned int) -1) / size); return (size > 0) && (count >= ((unsigned int) -1) / size);
} }
static inline bool
hb_unsigned_add_overflows (unsigned int a, unsigned int b, unsigned *result = nullptr)
{
#if hb_has_builtin(__builtin_add_overflow)
unsigned stack_result;
if (!result)
result = &stack_result;
return __builtin_add_overflow (a, b, result);
#endif
if (result)
*result = a + b;
return b > (unsigned int) -1 - a;
}
/* /*
* Sort and search. * Sort and search.

View file

@ -561,20 +561,29 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED,
DEBUG_MSG (ARABIC, nullptr, "fixed tiles: count=%d width=%" PRId32, n_fixed, w_fixed); DEBUG_MSG (ARABIC, nullptr, "fixed tiles: count=%d width=%" PRId32, n_fixed, w_fixed);
DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%" PRId32, n_repeating, w_repeating); DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%" PRId32, n_repeating, w_repeating);
/* Number of additional times to repeat each repeating tile. */ static constexpr unsigned STCH_MAX_GLYPHS = 256;
int n_copies = 0;
hb_position_t w_remaining = w_total - w_fixed; /* Number of additional times to repeat each repeating tile. */
if (sign * w_remaining > sign * w_repeating && sign * w_repeating > 0) unsigned int n_copies = 0;
n_copies = (sign * w_remaining) / (sign * w_repeating) - 1;
int64_t w_remaining_signed = (int64_t) w_total - w_fixed;
int64_t w_repeating_signed = w_repeating;
if (sign < 0)
{
w_remaining_signed = -w_remaining_signed;
w_repeating_signed = -w_repeating_signed;
}
hb_position_t w_remaining = (hb_position_t) (w_total - w_fixed);
if (w_remaining_signed > w_repeating_signed && w_repeating_signed > 0)
n_copies = w_remaining_signed / w_repeating_signed - 1;
/* See if we can improve the fit by adding an extra repeat and squeezing them together a bit. */ /* See if we can improve the fit by adding an extra repeat and squeezing them together a bit. */
hb_position_t extra_repeat_overlap = 0; hb_position_t extra_repeat_overlap = 0;
hb_position_t shortfall = sign * w_remaining - sign * w_repeating * (n_copies + 1); int64_t shortfall = w_remaining_signed - w_repeating_signed * (n_copies + 1);
if (shortfall > 0 && n_repeating > 0) if (shortfall > 0 && n_repeating > 0)
{ {
++n_copies; ++n_copies;
hb_position_t excess = (n_copies + 1) * sign * w_repeating - sign * w_remaining; int64_t excess = (n_copies + 1) * w_repeating_signed - w_remaining_signed;
if (excess > 0) if (excess > 0)
{ {
extra_repeat_overlap = excess / (n_copies * n_repeating); extra_repeat_overlap = excess / (n_copies * n_repeating);
@ -582,10 +591,22 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED,
} }
} }
unsigned int max_copies = 0;
if (n_repeating > 0)
{
unsigned int base_glyphs = n_fixed + n_repeating;
if (base_glyphs < STCH_MAX_GLYPHS)
max_copies = (STCH_MAX_GLYPHS - base_glyphs) / n_repeating;
}
n_copies = hb_min (n_copies, max_copies);
if (step == MEASURE) if (step == MEASURE)
{ {
extra_glyphs_needed += n_copies * n_repeating; unsigned int added_glyphs = 0;
DEBUG_MSG (ARABIC, nullptr, "will add extra %d copies of repeating tiles", n_copies); if (unlikely (hb_unsigned_mul_overflows (n_copies, n_repeating, &added_glyphs) ||
hb_unsigned_add_overflows (extra_glyphs_needed, added_glyphs, &extra_glyphs_needed)))
break;
DEBUG_MSG (ARABIC, nullptr, "will add extra %u copies of repeating tiles", n_copies);
} }
else else
{ {
@ -629,7 +650,9 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED,
if (step == MEASURE) if (step == MEASURE)
{ {
if (unlikely (!buffer->ensure (count + extra_glyphs_needed))) unsigned int total_glyphs = 0;
if (unlikely (hb_unsigned_add_overflows (count, extra_glyphs_needed, &total_glyphs) ||
!buffer->ensure (total_glyphs)))
break; break;
} }
else else

View file

@ -239,8 +239,12 @@ static AnimationHelper::SampleResult SampleAnimationForProperty(
#endif #endif
} }
uint32_t segmentIndex = 0;
size_t segmentSize = animation.mSegments.Length(); size_t segmentSize = animation.mSegments.Length();
if (segmentSize == 0) {
return AnimationHelper::SampleResult();
}
uint32_t segmentIndex = 0;
PropertyAnimation::SegmentData* segment = animation.mSegments.Elements(); PropertyAnimation::SegmentData* segment = animation.mSegments.Elements();
while (segment->mEndPortion < computedTiming.mProgress.Value() && while (segment->mEndPortion < computedTiming.mProgress.Value() &&
segmentIndex < segmentSize - 1) { segmentIndex < segmentSize - 1) {

View file

@ -156,7 +156,8 @@ BufferTextureData* BufferTextureData::CreateForYCbCr(
gfx::ColorRange aColorRange, gfx::ChromaSubsampling aSubsampling, gfx::ColorRange aColorRange, gfx::ChromaSubsampling aSubsampling,
TextureFlags aTextureFlags) { TextureFlags aTextureFlags) {
uint32_t bufSize = ImageDataSerializer::ComputeYCbCrBufferSize( uint32_t bufSize = ImageDataSerializer::ComputeYCbCrBufferSize(
aYSize, aYStride, aCbCrSize, aCbCrStride); aDisplay, aYSize, aYStride, aCbCrSize, aCbCrStride, aColorDepth,
aSubsampling);
if (bufSize == 0) { if (bufSize == 0) {
return nullptr; return nullptr;
} }
@ -274,11 +275,15 @@ bool BufferTextureData::BorrowMappedData(MappedTextureData& aData) {
gfx::IntSize size = GetSize(); gfx::IntSize size = GetSize();
auto stride = ImageDataSerializer::ComputeRGBStride(GetFormat(), size.width);
if (stride == 0) {
return false;
}
aData.data = GetBuffer(); aData.data = GetBuffer();
aData.size = size; aData.size = size;
aData.format = GetFormat(); aData.format = GetFormat();
aData.stride = aData.stride = stride;
ImageDataSerializer::ComputeRGBStride(aData.format, size.width);
return true; return true;
} }

View file

@ -645,7 +645,9 @@ Maybe<PlanarYCbCrData> PlanarYCbCrData::From(
yuvDesc.ySize().width < 0 || yuvDesc.ySize().height < 0 || yuvDesc.ySize().width < 0 || yuvDesc.ySize().height < 0 ||
yuvDesc.cbCrSize().width < 0 || yuvDesc.cbCrSize().height < 0 || yuvDesc.cbCrSize().width < 0 || yuvDesc.cbCrSize().height < 0 ||
yuvData.mYStride < 0 || yuvData.mCbCrStride < 0 || !yuvData.mYChannel || yuvData.mYStride < 0 || yuvData.mCbCrStride < 0 || !yuvData.mYChannel ||
!yuvData.mCbChannel || !yuvData.mCrChannel) { !yuvData.mCbChannel || !yuvData.mCrChannel ||
!(yuvData.YDataSize() <= yuvDesc.ySize()) ||
!(yuvData.CbCrDataSize() <= yuvDesc.cbCrSize())) {
gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData.mYSkip << "," gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData.mYSkip << ","
<< yuvData.mCbSkip << "," << yuvData.mCrSkip << ", " << yuvData.mCbSkip << "," << yuvData.mCrSkip << ", "
<< yuvDesc.ySize().width << "," << yuvDesc.ySize().height << yuvDesc.ySize().width << "," << yuvDesc.ySize().height
@ -653,7 +655,8 @@ Maybe<PlanarYCbCrData> PlanarYCbCrData::From(
<< yuvDesc.cbCrSize().height << ", " << yuvData.mYStride << yuvDesc.cbCrSize().height << ", " << yuvData.mYStride
<< "," << yuvData.mCbCrStride << ", " << "," << yuvData.mCbCrStride << ", "
<< yuvData.mYChannel << "," << yuvData.mCbChannel << "," << yuvData.mYChannel << "," << yuvData.mCbChannel << ","
<< yuvData.mCrChannel; << yuvData.mCrChannel << "," << yuvData.YDataSize().width
<< "," << yuvData.YDataSize().height;
return {}; return {};
} }
@ -750,8 +753,12 @@ nsresult PlanarYCbCrImage::BuildSurfaceDescriptorBuffer(
yOffset, cbOffset, crOffset); yOffset, cbOffset, crOffset);
uint32_t bufferSize = ImageDataSerializer::ComputeYCbCrBufferSize( uint32_t bufferSize = ImageDataSerializer::ComputeYCbCrBufferSize(
ySize, pdata->mYStride, cbcrSize, pdata->mCbCrStride, yOffset, cbOffset, pdata->mPictureRect, ySize, pdata->mYStride, cbcrSize, pdata->mCbCrStride,
crOffset); yOffset, cbOffset, crOffset, pdata->mColorDepth,
pdata->mChromaSubsampling);
if (bufferSize == 0) {
return NS_ERROR_FAILURE;
}
aSdBuffer.data() = aAllocate(bufferSize); aSdBuffer.data() = aAllocate(bufferSize);

View file

@ -58,36 +58,56 @@ uint32_t ComputeRGBBufferSize(IntSize aSize, SurfaceFormat aFormat) {
return bufsize; return bufsize;
} }
static bool CheckYCbCrStride(const gfx::IntSize& aSize, int32_t aStride,
gfx::ColorDepth aDepth) {
gfx::SurfaceFormat format = gfx::SurfaceFormatForColorDepth(aDepth);
CheckedInt32 minStride =
CheckedInt32(gfx::BytesPerPixel(format)) * aSize.width;
return minStride.isValid() && aStride >= minStride.value();
}
// Minimum required shmem size in bytes // Minimum required shmem size in bytes
uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride, uint32_t ComputeYCbCrBufferSize(const gfx::IntRect& aDisplay,
const gfx::IntSize& aYSize, int32_t aYStride,
const gfx::IntSize& aCbCrSize, const gfx::IntSize& aCbCrSize,
int32_t aCbCrStride) { int32_t aCbCrStride, gfx::ColorDepth aDepth,
const ChromaSubsampling aSubsampling) {
MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0); MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0);
if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || if (aDisplay.IsEmpty() || aDisplay.x < 0 || aDisplay.y < 0 ||
!gfx::IntRect(gfx::IntPoint(), aYSize).Contains(aDisplay) ||
aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 ||
aCbCrSize.width < 0 || aCbCrSize.width < 0 ||
!gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) || !gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) ||
!gfx::Factory::AllowedSurfaceSize( !gfx::Factory::AllowedSurfaceSize(
IntSize(aCbCrStride, aCbCrSize.height))) { IntSize(aCbCrStride, aCbCrSize.height)) ||
!CheckYCbCrStride(aYSize, aYStride, aDepth) ||
!CheckYCbCrStride(aCbCrSize, aCbCrStride, aDepth) ||
!(ChromaSize(aYSize, aSubsampling) <= aCbCrSize)) {
return 0; return 0;
} }
// Overflow checks are performed in AllowedSurfaceSize // Overflow checks are performed only individually in AllowedSurfaceSize
return GetAlignedStride<4>(aYSize.height, aYStride) + auto bufLen =
2 * GetAlignedStride<4>(aCbCrSize.height, aCbCrStride); CheckedInt<uint32_t>(GetAlignedStride<4>(aYSize.height, aYStride)) +
CheckedInt<uint32_t>(GetAlignedStride<4>(aCbCrSize.height, aCbCrStride)) *
2;
if (!bufLen.isValid()) {
return 0;
}
return bufLen.value();
} }
uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride, uint32_t ComputeYCbCrBufferSize(const gfx::IntRect& aDisplay,
const gfx::IntSize& aYSize, int32_t aYStride,
const gfx::IntSize& aCbCrSize, const gfx::IntSize& aCbCrSize,
int32_t aCbCrStride, uint32_t aYOffset, int32_t aCbCrStride, uint32_t aYOffset,
uint32_t aCbOffset, uint32_t aCrOffset) { uint32_t aCbOffset, uint32_t aCrOffset,
MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0); gfx::ColorDepth aDepth,
const ChromaSubsampling aSubsampling) {
if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || uint32_t minBufLen = ComputeYCbCrBufferSize(
aCbCrSize.width < 0 || aDisplay, aYSize, aYStride, aCbCrSize, aCbCrStride, aDepth, aSubsampling);
!gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) || if (minBufLen == 0) {
!gfx::Factory::AllowedSurfaceSize(
IntSize(aCbCrStride, aCbCrSize.height))) {
return 0; return 0;
} }
@ -105,7 +125,8 @@ uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride,
crEnd += cbCrLength; crEnd += cbCrLength;
if (!yEnd.isValid() || !cbEnd.isValid() || !crEnd.isValid() || if (!yEnd.isValid() || !cbEnd.isValid() || !crEnd.isValid() ||
yEnd.value() > aCbOffset || cbEnd.value() > aCrOffset) { yEnd.value() > aCbOffset || cbEnd.value() > aCrOffset ||
crEnd.value() < minBufLen) {
return 0; return 0;
} }

View file

@ -40,13 +40,18 @@ uint32_t ComputeRGBBufferSize(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
/// This function is meant as a helper to know how much shared memory we need /// This function is meant as a helper to know how much shared memory we need
/// to allocate in a shmem in order to place a shared YCbCr image blob of /// to allocate in a shmem in order to place a shared YCbCr image blob of
/// given dimensions. /// given dimensions.
uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride, uint32_t ComputeYCbCrBufferSize(const gfx::IntRect& aDisplay,
const gfx::IntSize& aYSize, int32_t aYStride,
const gfx::IntSize& aCbCrSize, const gfx::IntSize& aCbCrSize,
int32_t aCbCrStride); int32_t aCbCrStride, gfx::ColorDepth aDepth,
uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride, const gfx::ChromaSubsampling aSubsampling);
uint32_t ComputeYCbCrBufferSize(const gfx::IntRect& aDisplay,
const gfx::IntSize& aYSize, int32_t aYStride,
const gfx::IntSize& aCbCrSize, const gfx::IntSize& aCbCrSize,
int32_t aCbCrStride, uint32_t aYOffset, int32_t aCbCrStride, uint32_t aYOffset,
uint32_t aCbOffset, uint32_t aCrOffset); uint32_t aCbOffset, uint32_t aCrOffset,
gfx::ColorDepth aDepth,
const gfx::ChromaSubsampling aSubsampling);
uint32_t ComputeYCbCrBufferSize(uint32_t aBufferSize); uint32_t ComputeYCbCrBufferSize(uint32_t aBufferSize);
void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight, int32_t cbCrStride, void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight, int32_t cbCrStride,

View file

@ -13,6 +13,7 @@
#include "mozilla/layers/SharedSurfacesChild.h" #include "mozilla/layers/SharedSurfacesChild.h"
#include "mozilla/layers/SharedSurfacesParent.h" #include "mozilla/layers/SharedSurfacesParent.h"
#include "nsDebug.h" // for NS_ABORT_OOM #include "nsDebug.h" // for NS_ABORT_OOM
#include "mozilla/image/SurfaceCache.h"
#include "base/process_util.h" #include "base/process_util.h"
@ -46,7 +47,7 @@ void SourceSurfaceSharedDataWrapper::Init(
MOZ_CRASH("Invalid shared memory handle!"); MOZ_CRASH("Invalid shared memory handle!");
} }
bool mapped = EnsureMapped(len); bool mapped = EnsureMapped();
if ((sizeof(uintptr_t) <= 4 || if ((sizeof(uintptr_t) <= 4 ||
StaticPrefs::image_mem_shared_unmap_force_enabled_AtStartup()) && StaticPrefs::image_mem_shared_unmap_force_enabled_AtStartup()) &&
len / 1024 > len / 1024 >
@ -77,10 +78,15 @@ void SourceSurfaceSharedDataWrapper::Init(SourceSurfaceSharedData* aSurface) {
mBuf = aSurface->mBuf; mBuf = aSurface->mBuf;
} }
bool SourceSurfaceSharedDataWrapper::EnsureMapped(size_t aLength) { bool SourceSurfaceSharedDataWrapper::EnsureMapped() {
MOZ_ASSERT(!GetData()); MOZ_ASSERT(!GetData());
if (mBufHandle.Size() < aLength) { auto computedStride =
CheckedInt<int32_t>(mSize.width) * BytesPerPixel(mFormat);
if (mSize.width < 0 || mSize.height < 0 || mStride < 0 ||
!computedStride.isValid() || mStride < computedStride.value() ||
!image::SurfaceCache::IsLegalSize(mSize) ||
mBufHandle.Size() < GetAlignedDataLength()) {
return false; return false;
} }
@ -118,9 +124,8 @@ bool SourceSurfaceSharedDataWrapper::Map(MapType aMapType,
SharedSurfacesParent::RemoveTracking(this); SharedSurfacesParent::RemoveTracking(this);
} }
if (!dataPtr) { if (!dataPtr) {
size_t len = GetAlignedDataLength(); if (!EnsureMapped()) {
if (!EnsureMapped(len)) { NS_ABORT_OOM(GetAlignedDataLength());
NS_ABORT_OOM(len);
} }
dataPtr = GetData(); dataPtr = GetData();
} }

View file

@ -104,7 +104,7 @@ class SourceSurfaceSharedDataWrapper final : public DataSourceSurface {
return mozilla::ipc::shared_memory::PageAlignedSize(GetDataLength()); return mozilla::ipc::shared_memory::PageAlignedSize(GetDataLength());
} }
bool EnsureMapped(size_t aLength); bool EnsureMapped();
// Protects mapping and unmapping of mBuf. // Protects mapping and unmapping of mBuf.
Maybe<Mutex> mHandleLock; Maybe<Mutex> mHandleLock;

View file

@ -279,9 +279,10 @@ already_AddRefed<TextureHost> CreateBackendIndependentTextureHost(
case BufferDescriptor::TYCbCrDescriptor: { case BufferDescriptor::TYCbCrDescriptor: {
const YCbCrDescriptor& ycbcr = desc.get_YCbCrDescriptor(); const YCbCrDescriptor& ycbcr = desc.get_YCbCrDescriptor();
reqSize = ImageDataSerializer::ComputeYCbCrBufferSize( reqSize = ImageDataSerializer::ComputeYCbCrBufferSize(
ycbcr.ySize(), ycbcr.yStride(), ycbcr.cbCrSize(), ycbcr.display(), ycbcr.ySize(), ycbcr.yStride(),
ycbcr.cbCrStride(), ycbcr.yOffset(), ycbcr.cbOffset(), ycbcr.cbCrSize(), ycbcr.cbCrStride(), ycbcr.yOffset(),
ycbcr.crOffset()); ycbcr.cbOffset(), ycbcr.crOffset(), ycbcr.colorDepth(),
ycbcr.chromaSubsampling());
break; break;
} }
case BufferDescriptor::TRGBDescriptor: { case BufferDescriptor::TRGBDescriptor: {

View file

@ -181,7 +181,20 @@ class MOZ_STACK_CLASS AutoImageBridgeParentAsyncMessageSender final {
~AutoImageBridgeParentAsyncMessageSender() { ~AutoImageBridgeParentAsyncMessageSender() {
mImageBridge->SendPendingAsyncMessages(); mImageBridge->SendPendingAsyncMessages();
if (mToDestroy) { if (mToDestroy) {
// Iterate mToDestroy but de-duplicate it to avoid destroying the
// same texture parent actor twice.
nsTHashSet<PTextureParent*> seenTextureParents;
for (const auto& op : *mToDestroy) { for (const auto& op : *mToDestroy) {
// Peek inside the op (as DestroyActor does) to see if we are about
// to destroy a PTextureParent.
if (op.type() == OpDestroy::TPTexture) {
PTextureParent* textureParent = op.get_PTexture().AsParent();
if (!seenTextureParents.EnsureInserted(textureParent)) {
// Already seen, so skip this one.
continue;
}
}
mImageBridge->DestroyActor(op); mImageBridge->DestroyActor(op);
} }
} }

View file

@ -170,7 +170,8 @@ nsresult SharedPlanarYCbCrImage::CreateEmptyBuffer(
// will try to manage this memory without knowing it belongs to a // will try to manage this memory without knowing it belongs to a
// shmem. // shmem.
mBufferSize = ImageDataSerializer::ComputeYCbCrBufferSize( mBufferSize = ImageDataSerializer::ComputeYCbCrBufferSize(
aYSize, mData.mYStride, aCbCrSize, mData.mCbCrStride); mData.mPictureRect, aYSize, mData.mYStride, aCbCrSize, mData.mCbCrStride,
mData.mColorDepth, mData.mChromaSubsampling);
mSize = mData.mPictureRect.Size(); mSize = mData.mPictureRect.Size();
mOrigin = mData.mPictureRect.TopLeft(); mOrigin = mData.mPictureRect.TopLeft();

View file

@ -81,6 +81,11 @@ already_AddRefed<TextureHost> CreateTextureHostOGL(
#endif #endif
case SurfaceDescriptor::TEGLImageDescriptor: { case SurfaceDescriptor::TEGLImageDescriptor: {
if (aDeallocator && !aDeallocator->IsSameProcess()) {
gfxCriticalError()
<< "EGLImageDescriptor must only be used in same process";
return nullptr;
}
const EGLImageDescriptor& desc = aDesc.get_EGLImageDescriptor(); const EGLImageDescriptor& desc = aDesc.get_EGLImageDescriptor();
result = new EGLImageTextureHost(aFlags, (EGLImage)desc.image(), result = new EGLImageTextureHost(aFlags, (EGLImage)desc.image(),
(EGLSync)desc.fence(), desc.size(), (EGLSync)desc.fence(), desc.size(),
@ -109,6 +114,11 @@ already_AddRefed<TextureHost> CreateTextureHostOGL(
#endif #endif
case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture: { case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture: {
if (aDeallocator && !aDeallocator->IsSameProcess()) {
gfxCriticalError() << "SurfaceDescriptorSharedGLTexture must only be "
"used in same process";
return nullptr;
}
const auto& desc = aDesc.get_SurfaceDescriptorSharedGLTexture(); const auto& desc = aDesc.get_SurfaceDescriptorSharedGLTexture();
result = result =
new GLTextureHost(aFlags, desc.texture(), desc.target(), new GLTextureHost(aFlags, desc.texture(), desc.target(),

View file

@ -16,6 +16,7 @@
#include "GLContextProvider.h" #include "GLContextProvider.h"
#include "GLLibraryLoader.h" #include "GLLibraryLoader.h"
#include "nsExceptionHandler.h" #include "nsExceptionHandler.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/Range.h" #include "mozilla/Range.h"
#include "mozilla/EnumeratedRange.h" #include "mozilla/EnumeratedRange.h"
#include "mozilla/StaticPrefs_gfx.h" #include "mozilla/StaticPrefs_gfx.h"
@ -314,8 +315,19 @@ class MOZ_STACK_CLASS AutoWebRenderBridgeParentAsyncMessageSender final {
mWebRenderBridgeParent->SendPendingAsyncMessages(); mWebRenderBridgeParent->SendPendingAsyncMessages();
if (mActorsToDestroy) { if (mActorsToDestroy) {
// Destroy the actors after sending the async messages because the latter // Destroy the actors after sending the async messages because the latter
// may contain references to some actors. // may contain references to some actors. De-duplicate the array to avoid
// destroying the same texture parent actor twice.
nsTHashSet<PTextureParent*> seenTextureParents;
for (const auto& op : *mActorsToDestroy) { for (const auto& op : *mActorsToDestroy) {
// Peek inside the op (as DestroyActor does) to see if we are about
// to destroy a PTextureParent.
if (op.type() == OpDestroy::TPTexture) {
PTextureParent* textureParent = op.get_PTexture().AsParent();
if (!seenTextureParents.EnsureInserted(textureParent)) {
// Already seen, so skip this one.
continue;
}
}
mWebRenderBridgeParent->DestroyActor(op); mWebRenderBridgeParent->DestroyActor(op);
} }
} }
@ -1864,12 +1876,16 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvGetSnapshot(
return IPC_FAIL_NO_REASON(this); return IPC_FAIL_NO_REASON(this);
} }
uint32_t buffer_size = size.width * size.height * 4; auto buffer_size = (CheckedInt<size_t>(size.width) * size.height * 4);
if (!buffer_size.isValid()) {
return IPC_FAIL_NO_REASON(this);
}
FlushSceneBuilds(); FlushSceneBuilds();
FlushFrameGeneration(wr::RenderReasons::SNAPSHOT); FlushFrameGeneration(wr::RenderReasons::SNAPSHOT);
mApi->Readback(start, size, bufferTexture->GetFormat(), mApi->Readback(start, size, bufferTexture->GetFormat(),
Range<uint8_t>(buffer, buffer_size), aNeedsYFlip); Range<uint8_t>(buffer, buffer_size.value()),
aNeedsYFlip);
return IPC_OK(); return IPC_OK();
} }

View file

@ -65,8 +65,10 @@ EXPORTS.mozilla.image += [
"ICOFileHeaders.h", "ICOFileHeaders.h",
"ImageMemoryReporter.h", "ImageMemoryReporter.h",
"ImageUtils.h", "ImageUtils.h",
"PlaybackType.h",
"Resolution.h", "Resolution.h",
"SourceBuffer.h", "SourceBuffer.h",
"SurfaceCache.h",
"SurfaceFlags.h", "SurfaceFlags.h",
"WebRenderImageProvider.h", "WebRenderImageProvider.h",
] ]

View file

@ -426,7 +426,7 @@ constexpr uint64_t CanonicalizedNaNSignificand = 0x8000000000000;
#endif #endif
#if defined(JS_RUNTIME_CANONICAL_NAN) #if defined(JS_RUNTIME_CANONICAL_NAN)
extern uint64_t CanonicalizedNaNBits; extern JS_PUBLIC_API uint64_t CanonicalizedNaNBits;
#else #else
constexpr uint64_t CanonicalizedNaNBits = constexpr uint64_t CanonicalizedNaNBits =
mozilla::SpecificNaNBits<double, detail::CanonicalizedNaNSignBit, mozilla::SpecificNaNBits<double, detail::CanonicalizedNaNSignBit,

View file

@ -99,6 +99,7 @@ void DebugScriptObject::finalize(JS::GCContext* gcx, JSObject* obj) {
/* static */ /* static */
DebugScript* DebugScript::get(JSScript* script) { DebugScript* DebugScript::get(JSScript* script) {
MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(script));
MOZ_ASSERT(script->hasDebugScript()); MOZ_ASSERT(script->hasDebugScript());
DebugScriptMap* map = script->zone()->debugScriptMap; DebugScriptMap* map = script->zone()->debugScriptMap;
MOZ_ASSERT(map); MOZ_ASSERT(map);
@ -205,7 +206,12 @@ JSBreakpointSite* DebugScript::getOrCreateBreakpointSite(JSContext* cx,
/* static */ /* static */
void DebugScript::destroyBreakpointSite(JS::GCContext* gcx, JSScript* script, void DebugScript::destroyBreakpointSite(JS::GCContext* gcx, JSScript* script,
jsbytecode* pc) { jsbytecode* pc) {
if (IsAboutToBeFinalizedUnbarriered(script)) {
return;
}
DebugScript* debug = get(script); DebugScript* debug = get(script);
JSBreakpointSite*& site = debug->breakpoints[script->pcToOffset(pc)]; JSBreakpointSite*& site = debug->breakpoints[script->pcToOffset(pc)];
MOZ_ASSERT(site); MOZ_ASSERT(site);
MOZ_ASSERT(site->isEmpty()); MOZ_ASSERT(site->isEmpty());
@ -283,6 +289,10 @@ bool DebugScript::incrementStepperCount(JSContext* cx, HandleScript script) {
/* static */ /* static */
void DebugScript::decrementStepperCount(JS::GCContext* gcx, JSScript* script) { void DebugScript::decrementStepperCount(JS::GCContext* gcx, JSScript* script) {
if (IsAboutToBeFinalizedUnbarriered(script)) {
return;
}
DebugScript* debug = get(script); DebugScript* debug = get(script);
MOZ_ASSERT(debug); MOZ_ASSERT(debug);
MOZ_ASSERT(debug->stepperCount > 0); MOZ_ASSERT(debug->stepperCount > 0);
@ -328,6 +338,10 @@ bool DebugScript::incrementGeneratorObserverCount(JSContext* cx,
/* static */ /* static */
void DebugScript::decrementGeneratorObserverCount(JS::GCContext* gcx, void DebugScript::decrementGeneratorObserverCount(JS::GCContext* gcx,
JSScript* script) { JSScript* script) {
if (IsAboutToBeFinalizedUnbarriered(script)) {
return;
}
DebugScript* debug = get(script); DebugScript* debug = get(script);
MOZ_ASSERT(debug); MOZ_ASSERT(debug);
MOZ_ASSERT(debug->generatorObserverCount > 0); MOZ_ASSERT(debug->generatorObserverCount > 0);
@ -393,6 +407,10 @@ void DebugAPI::checkDebugScriptAfterMovingGC(DebugScript* ds) {
/* static */ /* static */
bool DebugAPI::stepModeEnabledSlow(JSScript* script) { bool DebugAPI::stepModeEnabledSlow(JSScript* script) {
if (IsAboutToBeFinalizedUnbarriered(script)) {
return false;
}
return DebugScript::get(script)->stepperCount > 0; return DebugScript::get(script)->stepperCount > 0;
} }

View file

@ -2025,26 +2025,27 @@ Completion Completion::fromJSFramePop(JSContext* cx, AbstractFramePtr frame,
// //
// GetGeneratorObjectForFrame can return nullptr even when a generator // GetGeneratorObjectForFrame can return nullptr even when a generator
// object does exist, if the frame is paused between the Generator and // object does exist, if the frame is paused between the Generator and
// SetAliasedVar opcodes. But by checking the opcode first we eliminate that // SetAliasedVar opcodes.
// possibility, so it's fine to call genObj->isClosed().
Rooted<AbstractGeneratorObject*> generatorObj( Rooted<AbstractGeneratorObject*> generatorObj(
cx, GetGeneratorObjectForFrame(cx, frame)); cx, GetGeneratorObjectForFrame(cx, frame));
switch (JSOp(*pc)) {
case JSOp::InitialYield:
MOZ_ASSERT(!generatorObj->isClosed());
return Completion(InitialYield(generatorObj));
case JSOp::Yield: if (generatorObj && !generatorObj->isClosed()) {
MOZ_ASSERT(!generatorObj->isClosed()); switch (JSOp(*pc)) {
return Completion(Yield(generatorObj, frame.returnValue())); case JSOp::InitialYield:
return Completion(InitialYield(generatorObj));
case JSOp::Await: case JSOp::Yield:
MOZ_ASSERT(!generatorObj->isClosed()); return Completion(Yield(generatorObj, frame.returnValue()));
return Completion(Await(generatorObj, frame.returnValue()));
default: case JSOp::Await:
return Completion(Return(frame.returnValue())); return Completion(Await(generatorObj, frame.returnValue()));
default:
break;
}
} }
return Completion(Return(frame.returnValue()));
} }
void Completion::trace(JSTracer* trc) { void Completion::trace(JSTracer* trc) {

View file

@ -894,7 +894,10 @@ bool MarkPagesUnusedSoft(void* region, size_t length) {
int status; int status;
do { do {
# if defined(XP_DARWIN) # if defined(XP_DARWIN)
status = madvise(region, length, MADV_FREE_REUSABLE); // Note: we use MADV_FREE instead of MADV_FREE_REUSABLE + MADV_FREE_REUSE to
// work around a kernel bug on macOS Tahoe. We should change this back once
// that bug is fixed. See bug 2015359.
status = madvise(region, length, MADV_FREE);
# elif defined(XP_SOLARIS) # elif defined(XP_SOLARIS)
status = posix_madvise(region, length, POSIX_MADV_DONTNEED); status = posix_madvise(region, length, POSIX_MADV_DONTNEED);
# else # else
@ -925,11 +928,6 @@ void MarkPagesInUseSoft(void* region, size_t length) {
MOZ_ASSERT(DecommitEnabled()); MOZ_ASSERT(DecommitEnabled());
CheckDecommit(region, length); CheckDecommit(region, length);
#if defined(XP_DARWIN)
while (madvise(region, length, MADV_FREE_REUSE) == -1 && errno == EAGAIN) {
}
#endif
MOZ_MAKE_MEM_UNDEFINED(region, length); MOZ_MAKE_MEM_UNDEFINED(region, length);
} }

View file

@ -1215,10 +1215,14 @@ void GCRuntime::checkHeapBeforeMinorGC(AutoHeapSession& session) {
// to tenured strings but contain nursery data. // to tenured strings but contain nursery data.
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
if (zone->isGCFinished()) {
continue; // Don't access zones that are being swept off thread.
}
for (ArenaIter aiter(zone, gc::AllocKind::STRING); !aiter.done(); for (ArenaIter aiter(zone, gc::AllocKind::STRING); !aiter.done();
aiter.next()) { aiter.next()) {
for (ArenaCellIterUnderGC cell(aiter.get()); !cell.done(); cell.next()) { for (ArenaCellIterUnderGC cell(aiter.get()); !cell.done(); cell.next()) {
if (cell->is<JSString>() && cell->as<JSString>()->isDependent()) { if (cell->as<JSString>()->isDependent()) {
JSDependentString* str = &cell->as<JSString>()->asDependent(); JSDependentString* str = &cell->as<JSString>()->asDependent();
if (str->isTenured() && str->base()->isTenured()) { if (str->isTenured() && str->base()->isTenured()) {
MOZ_RELEASE_ASSERT(!str->hasCharsInCollectedNurseryRegion()); MOZ_RELEASE_ASSERT(!str->hasCharsInCollectedNurseryRegion());

View file

@ -0,0 +1,17 @@
gczeal(23);
String + "";
var g = newGlobal({ newCompartment: true });
var dbg = Debugger(g);
dbg.onNewScript = function (script) {
script.setBreakpoint(0, () => {});
};
g.eval("");
// Trigger GC, which will mark the eval script about to be finalized,
// and the DebugScriptMap entry will be removed.
Uint8Array;
// This shouldn't try to use the DebugScriptMap entry.
dbg.clearAllBreakpoints();

View file

@ -0,0 +1,31 @@
// Don't crash on {return:} from onStep in a generator at a Yield.
// This test force-returns from each bytecode instruction in a generator.
let g = newGlobal({ newCompartment: true });
g.eval(`
function* gen() {
yield 1;
}
`)
let dbg = new Debugger(g);
let targetSteps = 0;
let found = true;
dbg.onEnterFrame = (frame) => {
let steps = 0;
frame.onStep = () => {
if (steps++ == targetSteps) {
found = true;
return { return: 0xdead };
}
}
}
dbg.uncaughtExceptionHook = () => undefined
while (found) {
found = false;
targetSteps++;
for (var y of g.gen()) {}
}

View file

@ -568,8 +568,9 @@ static bool BlockIsSingleTest(MBasicBlock* phiBlock, MBasicBlock* testBlock,
*ptest = nullptr; *ptest = nullptr;
if (phiBlock != testBlock) { if (phiBlock != testBlock) {
MOZ_ASSERT(phiBlock->numSuccessors() == 1 && MOZ_RELEASE_ASSERT(phiBlock->lastIns()->isGoto());
phiBlock->getSuccessor(0) == testBlock); MOZ_RELEASE_ASSERT(phiBlock->lastIns()->toGoto()->target() == testBlock);
MOZ_RELEASE_ASSERT(testBlock->numPredecessors() == 1);
if (!phiBlock->begin()->isGoto()) { if (!phiBlock->begin()->isGoto()) {
return false; return false;
} }
@ -686,7 +687,7 @@ static bool IsTestInputMaybeToBool(MTest* test, MDefinition* value) {
blockResult->setImplicitlyUsedUnchecked(); blockResult->setImplicitlyUsedUnchecked();
MInstruction* ins = block->lastIns(); MInstruction* ins = block->lastIns();
MOZ_ASSERT(ins->isGoto()); MOZ_RELEASE_ASSERT(ins->isGoto());
ins->toGoto()->target()->removePredecessor(block); ins->toGoto()->target()->removePredecessor(block);
block->discardLastIns(); block->discardLastIns();
@ -707,15 +708,14 @@ static bool IsTestInputMaybeToBool(MTest* test, MDefinition* value) {
MInstruction* ins = block->lastIns(); MInstruction* ins = block->lastIns();
if (ins->isTest()) { if (ins->isTest()) {
MTest* test = ins->toTest(); MTest* test = ins->toTest();
MOZ_ASSERT(test->input() == value); MOZ_RELEASE_ASSERT(test->input() == value);
if (ifTrue != test->ifTrue()) { if (ifTrue != test->ifTrue()) {
test->ifTrue()->removePredecessor(block); test->ifTrue()->removePredecessor(block);
if (!ifTrue->addPredecessorSameInputsAs(block, existingPred)) { if (!ifTrue->addPredecessorSameInputsAs(block, existingPred)) {
return false; return false;
} }
MOZ_ASSERT(test->ifTrue() == test->getSuccessor(0)); test->replaceSuccessor(MTest::TrueBranchIndex, ifTrue);
test->replaceSuccessor(0, ifTrue);
} }
if (ifFalse != test->ifFalse()) { if (ifFalse != test->ifFalse()) {
@ -723,14 +723,13 @@ static bool IsTestInputMaybeToBool(MTest* test, MDefinition* value) {
if (!ifFalse->addPredecessorSameInputsAs(block, existingPred)) { if (!ifFalse->addPredecessorSameInputsAs(block, existingPred)) {
return false; return false;
} }
MOZ_ASSERT(test->ifFalse() == test->getSuccessor(1)); test->replaceSuccessor(MTest::FalseBranchIndex, ifFalse);
test->replaceSuccessor(1, ifFalse);
} }
return true; return true;
} }
MOZ_ASSERT(ins->isGoto()); MOZ_RELEASE_ASSERT(ins->isGoto());
ins->toGoto()->target()->removePredecessor(block); ins->toGoto()->target()->removePredecessor(block);
block->discardLastIns(); block->discardLastIns();
@ -775,8 +774,8 @@ static bool IsDiamondPattern(MBasicBlock* initialBlock) {
return false; return false;
} }
MBasicBlock* phiBlock = trueBranch->getSuccessor(0); MBasicBlock* phiBlock = trueBranch->lastIns()->toGoto()->target();
if (phiBlock != falseBranch->getSuccessor(0)) { if (phiBlock != falseBranch->lastIns()->toGoto()->target()) {
return false; return false;
} }
if (phiBlock->numPredecessors() != 2) { if (phiBlock->numPredecessors() != 2) {
@ -820,13 +819,13 @@ static bool IsDiamondPattern(MBasicBlock* initialBlock) {
return true; return true;
} }
MBasicBlock* phiBlock = trueBranch->getSuccessor(0); MBasicBlock* phiBlock = trueBranch->lastIns()->toGoto()->target();
MBasicBlock* testBlock = phiBlock; MBasicBlock* testBlock = phiBlock;
if (testBlock->numSuccessors() == 1) { if (testBlock->lastIns()->isGoto()) {
if (testBlock->isLoopBackedge()) { if (testBlock->isLoopBackedge()) {
return true; return true;
} }
testBlock = testBlock->getSuccessor(0); testBlock = testBlock->lastIns()->toGoto()->target();
if (testBlock->numPredecessors() != 1) { if (testBlock->numPredecessors() != 1) {
return true; return true;
} }
@ -838,7 +837,7 @@ static bool IsDiamondPattern(MBasicBlock* initialBlock) {
return true; return true;
} }
MOZ_ASSERT(phi->numOperands() == 2); MOZ_RELEASE_ASSERT(phi->numOperands() == 2);
// Make sure the test block does not have any outgoing loop backedges. // Make sure the test block does not have any outgoing loop backedges.
if (!SplitCriticalEdgesForBlock(graph, testBlock)) { if (!SplitCriticalEdgesForBlock(graph, testBlock)) {
@ -929,8 +928,8 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
MBasicBlock* trueBranch = initialTest->ifTrue(); MBasicBlock* trueBranch = initialTest->ifTrue();
MBasicBlock* falseBranch = initialTest->ifFalse(); MBasicBlock* falseBranch = initialTest->ifFalse();
if (trueBranch->numSuccessors() == 1 && if (trueBranch->lastIns()->isGoto() &&
trueBranch->getSuccessor(0) == falseBranch) { trueBranch->lastIns()->toGoto()->target() == falseBranch) {
if (trueBranch->numPredecessors() != 1) { if (trueBranch->numPredecessors() != 1) {
return false; return false;
} }
@ -940,8 +939,8 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
return true; return true;
} }
if (falseBranch->numSuccessors() == 1 && if (falseBranch->lastIns()->isGoto() &&
falseBranch->getSuccessor(0) == trueBranch) { falseBranch->lastIns()->toGoto()->target() == trueBranch) {
if (trueBranch->numPredecessors() != 2) { if (trueBranch->numPredecessors() != 2) {
return false; return false;
} }
@ -999,19 +998,19 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
} }
MBasicBlock* phiBlock; MBasicBlock* phiBlock;
if (trueBranch->numSuccessors() == 1 && if (trueBranch->lastIns()->isGoto() &&
trueBranch->getSuccessor(0) == falseBranch) { trueBranch->lastIns()->toGoto()->target() == falseBranch) {
phiBlock = falseBranch; phiBlock = falseBranch;
} else { } else {
MOZ_ASSERT(falseBranch->getSuccessor(0) == trueBranch); MOZ_ASSERT(falseBranch->lastIns()->toGoto()->target() == trueBranch);
phiBlock = trueBranch; phiBlock = trueBranch;
} }
MBasicBlock* testBlock = phiBlock; MBasicBlock* testBlock = phiBlock;
if (testBlock->numSuccessors() == 1) { if (testBlock->lastIns()->isGoto()) {
MOZ_ASSERT(!testBlock->isLoopBackedge()); MOZ_RELEASE_ASSERT(!testBlock->isLoopBackedge());
testBlock = testBlock->getSuccessor(0); testBlock = testBlock->lastIns()->toGoto()->target();
if (testBlock->numPredecessors() != 1) { if (testBlock->numPredecessors() != 1) {
return true; return true;
} }
@ -1023,7 +1022,7 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
return true; return true;
} }
MOZ_ASSERT(phi->numOperands() == 2); MOZ_RELEASE_ASSERT(phi->numOperands() == 2);
// If the phi-operand doesn't match the initial input, we can't fold the test. // If the phi-operand doesn't match the initial input, we can't fold the test.
auto* phiInputForInitialBlock = auto* phiInputForInitialBlock =
@ -1194,17 +1193,17 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
} }
MBasicBlock* testBlock = phiBlock; MBasicBlock* testBlock = phiBlock;
if (testBlock->numSuccessors() == 1) { if (testBlock->lastIns()->isGoto()) {
if (testBlock->isLoopBackedge()) { if (testBlock->isLoopBackedge()) {
return true; return true;
} }
testBlock = testBlock->getSuccessor(0); testBlock = testBlock->lastIns()->toGoto()->target();
if (testBlock->numPredecessors() != 1) { if (testBlock->numPredecessors() != 1) {
return true; return true;
} }
} }
MOZ_ASSERT(!phiBlock->isLoopBackedge()); MOZ_RELEASE_ASSERT(!phiBlock->isLoopBackedge());
MPhi* phi = nullptr; MPhi* phi = nullptr;
MTest* finalTest = nullptr; MTest* finalTest = nullptr;
@ -1212,7 +1211,7 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
return true; return true;
} }
MOZ_ASSERT(phiBlock->numPredecessors() == phi->numOperands()); MOZ_RELEASE_ASSERT(phiBlock->numPredecessors() == phi->numOperands());
// If the phi-operand doesn't match the initial input, we can't fold the test. // If the phi-operand doesn't match the initial input, we can't fold the test.
auto* phiInputForInitialBlock = auto* phiInputForInitialBlock =
@ -1243,7 +1242,7 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
return true; return true;
} }
MOZ_ASSERT(!pred->isLoopBackedge()); MOZ_RELEASE_ASSERT(!pred->isLoopBackedge());
} }
// Ensure we found the single goto block. // Ensure we found the single goto block.
@ -1270,7 +1269,7 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
// Update all test instructions to point to the final target. // Update all test instructions to point to the final target.
while (phiBlock->numPredecessors()) { while (phiBlock->numPredecessors()) {
mozilla::DebugOnly<size_t> oldNumPred = phiBlock->numPredecessors(); size_t oldNumPred = phiBlock->numPredecessors();
auto* pred = phiBlock->getPredecessor(0); auto* pred = phiBlock->getPredecessor(0);
auto* test = pred->lastIns()->toTest(); auto* test = pred->lastIns()->toTest();
@ -1281,7 +1280,7 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
return false; return false;
} }
} else { } else {
MOZ_ASSERT(test->ifFalse() == phiBlock); MOZ_RELEASE_ASSERT(test->ifFalse() == phiBlock);
if (!UpdateTestSuccessors(graph.alloc(), pred, test->input(), if (!UpdateTestSuccessors(graph.alloc(), pred, test->input(),
test->ifTrue(), finalTest->ifFalse(), test->ifTrue(), finalTest->ifFalse(),
testBlock)) { testBlock)) {
@ -1290,7 +1289,7 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
} }
// Ensure we've made progress. // Ensure we've made progress.
MOZ_ASSERT(phiBlock->numPredecessors() + 1 == oldNumPred); MOZ_RELEASE_ASSERT(phiBlock->numPredecessors() + 1 == oldNumPred);
} }
// Remove phiBlock, if different from testBlock. // Remove phiBlock, if different from testBlock.

View file

@ -1346,7 +1346,6 @@ class MWasmLoadTableElement : public MBinaryInstruction,
wasm::RefType refType) wasm::RefType refType)
: MBinaryInstruction(classOpcode, elements, index) { : MBinaryInstruction(classOpcode, elements, index) {
setResultType(MIRType::WasmAnyRef); setResultType(MIRType::WasmAnyRef);
setMovable();
initWasmRefType(wasm::MaybeRefType(refType)); initWasmRefType(wasm::MaybeRefType(refType));
} }

View file

@ -2266,36 +2266,15 @@ ResizableArrayBufferObject::createBufferAndData(
size_t sourceByteLength = source->byteLength(); size_t sourceByteLength = source->byteLength();
size_t newMaxByteLength = source->maxByteLength(); size_t newMaxByteLength = source->maxByteLength();
if (newByteLength > sourceByteLength) {
// Copy into a larger buffer.
AutoSetNewObjectMetadata metadata(cx);
auto [buffer, toFill] = createBufferAndData<FillContents::Zero>(
cx, newByteLength, newMaxByteLength, metadata, nullptr);
if (!buffer) {
return nullptr;
}
// The `createBufferAndData()` call first zero-initializes the complete
// buffer and then we copy over |sourceByteLength| bytes from |source|. It
// seems prudent to only zero-initialize the trailing bytes of |toFill|
// to avoid writing twice to `toFill[0..newByteLength]`. We don't yet
// implement this optimization, because this method is only called for
// small, inline buffers, so any write optimizations probably won't make
// much of a difference.
std::copy_n(source->dataPointer(), sourceByteLength, toFill);
return buffer;
}
// Copy into a smaller or same size buffer.
AutoSetNewObjectMetadata metadata(cx); AutoSetNewObjectMetadata metadata(cx);
auto [buffer, toFill] = createBufferAndData<FillContents::Uninitialized>( auto [buffer, toFill] = createBufferAndData<FillContents::Zero>(
cx, newByteLength, newMaxByteLength, metadata, nullptr); cx, newByteLength, newMaxByteLength, metadata, nullptr);
if (!buffer) { if (!buffer) {
return nullptr; return nullptr;
} }
std::uninitialized_copy_n(source->dataPointer(), newByteLength, toFill); size_t nbytes = std::min(newByteLength, sourceByteLength);
std::copy_n(source->dataPointer(), nbytes, toFill);
return buffer; return buffer;
} }

View file

@ -281,6 +281,12 @@ void FontFace::MaybeResolve() {
return; return;
} }
if (NS_IsMainThread() && !nsContentUtils::IsSafeToRunScript()) {
nsContentUtils::AddScriptRunner(NewRunnableMethod(
"FontFace::MaybeResolve", this, &FontFace::MaybeResolve));
return;
}
mLoaded->MaybeResolve(this); mLoaded->MaybeResolve(this);
} }

View file

@ -1,5 +1,5 @@
libpng 1.6.47 - February 18, 2025 libpng 1.6.55 - February 9, 2026
================================= ================================
This is a public release of libpng, intended for use in production code. This is a public release of libpng, intended for use in production code.
@ -7,15 +7,12 @@ This is a public release of libpng, intended for use in production code.
Files available for download Files available for download
---------------------------- ----------------------------
Source files with LF line endings (for Unix/Linux): Source files:
* libpng-1.6.47.tar.xz (LZMA-compressed, recommended) * libpng-1.6.55.tar.xz (LZMA-compressed, recommended)
* libpng-1.6.47.tar.gz (deflate-compressed) * libpng-1.6.55.tar.gz (deflate-compressed)
* lpng1655.7z (LZMA-compressed)
Source files with CRLF line endings (for Windows): * lpng1655.zip (deflate-compressed)
* lpng1647.7z (LZMA-compressed, recommended)
* lpng1647.zip (deflate-compressed)
Other information: Other information:
@ -25,22 +22,16 @@ Other information:
* TRADEMARK.md * TRADEMARK.md
Changes from version 1.6.46 to version 1.6.47 Changes from version 1.6.54 to version 1.6.55
--------------------------------------------- ---------------------------------------------
* Modified the behaviour of colorspace chunks in order to adhere * Fixed CVE-2026-25646 (high severity):
to the new precedence rules formulated in the latest draft of Heap buffer overflow in `png_set_quantize`.
the PNG Specification. (Reported and fixed by Joshua Inscoe.)
(Contributed by John Bowler) * Resolved an oss-fuzz build issue involving nalloc.
* Fixed a latent bug in `png_write_iCCP`. (Contributed by Philippe Antoine.)
This would have been a read-beyond-end-of-malloc vulnerability,
introduced early in the libpng-1.6.0 development, yet (fortunately!)
it was inaccessible before the above-mentioned modification of the
colorspace precedence rules, due to pre-existing colorspace checks.
(Reported by Bob Friesenhahn; fixed by John Bowler)
Send comments/corrections/commendations to png-mng-implement at lists.sf.net. Send comments/corrections/commendations to png-mng-implement at lists.sf.net.
Subscription is required; visit Subscription is required; visit
https://lists.sourceforge.net/lists/listinfo/png-mng-implement <https://lists.sourceforge.net/lists/listinfo/png-mng-implement>
to subscribe. to subscribe.

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