icecat: add release icecat-140.9.0-1gnu1 for ecne
This commit is contained in:
parent
8eb1f1732f
commit
a5f93cb214
1197 changed files with 30593 additions and 15344 deletions
|
|
@ -85,9 +85,9 @@ git = "https://github.com/mozilla/audioipc"
|
|||
rev = "e6f44a2bd1e57d11dfc737632a9e849077632330"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=2407441a2f67341a0e13b4ba6547555e387c671c"]
|
||||
[source."git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=579b75af21c040700eee6a1d8520e222699fe4cd"]
|
||||
git = "https://github.com/mozilla/cubeb-coreaudio-rs"
|
||||
rev = "2407441a2f67341a0e13b4ba6547555e387c671c"
|
||||
rev = "579b75af21c040700eee6a1d8520e222699fe4cd"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/mozilla/cubeb-pulse-rs?rev=8678dcab1c287de79c4c184ccc2e065bc62b70e2"]
|
||||
|
|
|
|||
|
|
@ -263,8 +263,7 @@ jobs:
|
|||
- mozilla-release
|
||||
- mozilla-esr140
|
||||
when:
|
||||
- {weekday: 'Monday', hour: 8, minute: 0}
|
||||
- {weekday: 'Thursday', hour: 8, minute: 0}
|
||||
- {weekday: 'Monday', hour: 5, minute: 0}
|
||||
|
||||
- name: daily-beta-perf
|
||||
job:
|
||||
|
|
|
|||
|
|
@ -22,4 +22,4 @@
|
|||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Merge day clobber 2026-01-12
|
||||
Merge day clobber 2026-02-23
|
||||
4
icecat/Cargo.lock
generated
4
icecat/Cargo.lock
generated
|
|
@ -1086,7 +1086,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "coreaudio-sys-utils"
|
||||
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 = [
|
||||
"core-foundation-sys",
|
||||
"coreaudio-sys",
|
||||
|
|
@ -1398,7 +1398,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cubeb-coreaudio"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=2407441a2f67341a0e13b4ba6547555e387c671c#2407441a2f67341a0e13b4ba6547555e387c671c"
|
||||
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=579b75af21c040700eee6a1d8520e222699fe4cd#579b75af21c040700eee6a1d8520e222699fe4cd"
|
||||
dependencies = [
|
||||
"atomic",
|
||||
"audio-mixer",
|
||||
|
|
|
|||
|
|
@ -1768,11 +1768,12 @@ void DocAccessible::DoInitialUpdate() {
|
|||
}
|
||||
#endif
|
||||
|
||||
// Fire reorder event after the document tree is constructed. Note, since
|
||||
// this reorder event is processed by parent document then events targeted to
|
||||
// this document may be fired prior to this reorder event. If this is
|
||||
// a problem then consider to keep event processing per tab document.
|
||||
if (!IsRoot()) {
|
||||
// Fire a reorder event on the OuterDocAccessible after the document tree is
|
||||
// constructed. Note that since this reorder event is processed by the parent
|
||||
// document, events targeted to this child document may be fired prior to this
|
||||
// reorder event. We don't fire a reorder event for remote documents; the
|
||||
// parent process handles that.
|
||||
if (!IPCDoc() && !IsRoot()) {
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(LocalParent());
|
||||
ParentDocument()->FireDelayedEvent(reorderEvent);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ DocAccessibleParent::DocAccessibleParent()
|
|||
mTopLevel(false),
|
||||
mTopLevelInContentProcess(false),
|
||||
mShutdown(false),
|
||||
mIsInitialTreeDone(false),
|
||||
mFocus(0),
|
||||
mCaretId(0),
|
||||
mCaretOffset(-1),
|
||||
|
|
@ -145,7 +146,9 @@ mozilla::ipc::IPCResult DocAccessibleParent::ProcessShowEvent(
|
|||
// Otherwise, clients might crawl the incomplete subtree and they won't get
|
||||
// mutation events for the remaining pieces.
|
||||
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);
|
||||
root = GetAccessible(mPendingShowChild);
|
||||
MOZ_ASSERT(root);
|
||||
AttachChild(rootParent, mPendingShowIndex, root);
|
||||
if (!AttachChild(rootParent, mPendingShowIndex, root)) {
|
||||
return IPC_FAIL(this, "failed to attach pending show child");
|
||||
}
|
||||
mPendingShowChild = 0;
|
||||
mPendingShowParent = 0;
|
||||
mPendingShowIndex = 0;
|
||||
|
|
@ -234,6 +239,11 @@ RemoteAccessible* DocAccessibleParent::CreateAcc(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (aAccData.GenericTypes() & eDocument) {
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid acc type");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
newProxy = new RemoteAccessible(aAccData.ID(), this, aAccData.Role(),
|
||||
aAccData.Type(), aAccData.GenericTypes(),
|
||||
aAccData.RoleMapEntryIndex());
|
||||
|
|
@ -246,9 +256,20 @@ RemoteAccessible* DocAccessibleParent::CreateAcc(
|
|||
return newProxy;
|
||||
}
|
||||
|
||||
void DocAccessibleParent::AttachChild(RemoteAccessible* aParent,
|
||||
bool DocAccessibleParent::AttachChild(RemoteAccessible* aParent,
|
||||
uint32_t aIndex,
|
||||
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);
|
||||
aChild->SetParent(aParent);
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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.
|
||||
auto children{std::move(aAcc->mChildren)};
|
||||
for (RemoteAccessible* child : children) {
|
||||
if (child == aAcc) {
|
||||
MOZ_ASSERT_UNREACHABLE(
|
||||
"Somehow an accessible got added as a child of itself!");
|
||||
}
|
||||
ShutdownOrPrepareForMove(child);
|
||||
}
|
||||
}
|
||||
|
|
@ -603,6 +633,18 @@ mozilla::ipc::IPCResult DocAccessibleParent::RecvMutationEvents(
|
|||
|
||||
mozilla::ipc::IPCResult DocAccessibleParent::RecvRequestAckMutationEvents() {
|
||||
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();
|
||||
}
|
||||
return IPC_OK();
|
||||
|
|
@ -882,6 +924,11 @@ mozilla::ipc::IPCResult DocAccessibleParent::RecvBindChildDoc(
|
|||
ipc::IPCResult DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
|
||||
uint64_t aParentID,
|
||||
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
|
||||
// document it self.
|
||||
ProxyEntry* e = mAccessibles.GetEntry(aParentID);
|
||||
|
|
@ -930,11 +977,20 @@ ipc::IPCResult DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
|
|||
aChildDoc->SetEmulatedWindowHandle(mEmulatedWindowHandle);
|
||||
}
|
||||
#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
|
||||
// this isn't possible when the document is in a different process to its
|
||||
// embedder.
|
||||
// FireEvent fires both OS and XPCOM events.
|
||||
}
|
||||
// We need to fire a reorder event on the embedder. We do this here rather
|
||||
// than in the content process for two reasons:
|
||||
// 1. It isn't possible for the content process to fire a reorder event on the
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -325,11 +325,14 @@ class DocAccessibleParent : public RemoteAccessible,
|
|||
};
|
||||
|
||||
RemoteAccessible* CreateAcc(const AccessibleData& aAccData);
|
||||
void AttachChild(RemoteAccessible* aParent, uint32_t aIndex,
|
||||
bool AttachChild(RemoteAccessible* aParent, uint32_t aIndex,
|
||||
RemoteAccessible* aChild);
|
||||
[[nodiscard]] bool CheckDocTree() const;
|
||||
xpcAccessibleGeneric* GetXPCAccessible(RemoteAccessible* aProxy);
|
||||
|
||||
/**
|
||||
* Fire an event to both OS and XPCOM consumers.
|
||||
*/
|
||||
void FireEvent(RemoteAccessible* aAcc, const uint32_t& aType);
|
||||
|
||||
/**
|
||||
|
|
@ -365,9 +368,10 @@ class DocAccessibleParent : public RemoteAccessible,
|
|||
uint32_t mPendingShowIndex = 0;
|
||||
nsTHashSet<uint64_t> mMovingIDs;
|
||||
uint64_t mActorID;
|
||||
bool mTopLevel;
|
||||
bool mTopLevelInContentProcess;
|
||||
bool mShutdown;
|
||||
bool mTopLevel : 1;
|
||||
bool mTopLevelInContentProcess : 1;
|
||||
bool mShutdown : 1;
|
||||
bool mIsInitialTreeDone : 1;
|
||||
RefPtr<dom::CanonicalBrowsingContext> mBrowsingContext;
|
||||
|
||||
nsTHashSet<RefPtr<dom::BrowserBridgeParent>> mPendingOOPChildDocs;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ support-files = [
|
|||
["browser_hidden_iframe.js"]
|
||||
https_first_disabled = true
|
||||
|
||||
["browser_iframe_recreation.js"]
|
||||
|
||||
["browser_nested_iframe.js"]
|
||||
|
||||
["browser_reframe_root.js"]
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
);
|
||||
|
|
@ -1 +1 @@
|
|||
140.8.0
|
||||
140.9.0
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
140.8.0esr
|
||||
140.9.0esr
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ MOZ_BRANDING_DIRECTORY=browser/branding/unofficial
|
|||
MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/branding/official
|
||||
# IceCat settings
|
||||
MOZ_APP_BASENAME=IceCat
|
||||
MOZ_APP_VERSION=140.8.0
|
||||
MOZ_APP_VERSION=140.9.0
|
||||
MOZ_DATA_REPORTING=0
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@
|
|||
# hardcoded milestones in the tree from these two files.
|
||||
#--------------------------------------------------------
|
||||
|
||||
140.8.0
|
||||
140.9.0
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
* New upstream stable release (icecat-140.8.0-2gnu1)
|
||||
|
|
|
|||
|
|
@ -746,7 +746,7 @@ class RequestListContextMenu {
|
|||
};
|
||||
|
||||
const options = JSON.stringify(fetchOptions, null, 4);
|
||||
const fetchString = `await fetch("${url}", ${options});`;
|
||||
const fetchString = `await fetch(${JSON.stringify(url)}, ${options});`;
|
||||
return fetchString;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,10 +50,11 @@ add_task(async function () {
|
|||
const types = ["end", "response", "duration", "latency"];
|
||||
|
||||
for (const t of types) {
|
||||
info("Check the timing column for type: " + t);
|
||||
await waitUntil(() => {
|
||||
const node = item.querySelector(".requests-list-" + t + "-time");
|
||||
const value = parseInt(node.textContent, 10);
|
||||
return value > 0;
|
||||
return value >= 0;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* Tests if Copy as Fetch works.
|
||||
*/
|
||||
|
||||
add_task(async function () {
|
||||
add_task(async function testBasicCopyAsFetch() {
|
||||
const { tab, monitor } = await initNetMonitor(HTTPS_CURL_URL, {
|
||||
requestCount: 1,
|
||||
});
|
||||
|
|
@ -15,7 +15,9 @@ add_task(async function () {
|
|||
|
||||
// GET request, no cookies (first request)
|
||||
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",
|
||||
"headers": {
|
||||
"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",
|
||||
"method": "GET",
|
||||
"mode": "cors"
|
||||
});`);
|
||||
});`
|
||||
);
|
||||
|
||||
await teardown(monitor);
|
||||
|
||||
|
|
@ -54,39 +57,71 @@ add_task(async function () {
|
|||
);
|
||||
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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,13 @@ const DEFAULT_DPPX = window.devicePixelRatio;
|
|||
|
||||
/* eslint-disable max-len */
|
||||
const TEST_DEVICE = {
|
||||
name: "iPhone 6/7/8",
|
||||
width: 375,
|
||||
height: 667,
|
||||
pixelRatio: 2,
|
||||
name: "iPhone 17 / 17 Pro",
|
||||
width: 402,
|
||||
height: 874,
|
||||
pixelRatio: 3,
|
||||
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,
|
||||
icecatOS: false,
|
||||
os: "iOS",
|
||||
featured: true,
|
||||
};
|
||||
/* eslint-enable max-len */
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
MOZ_ASSERT(IsTop(),
|
||||
"Should only set InRDMPane in the top-level browsing context");
|
||||
|
|
|
|||
|
|
@ -1136,6 +1136,7 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
|
|||
return IsTop();
|
||||
}
|
||||
|
||||
bool CanSet(FieldIndex<IDX_InRDMPane>, const bool&, ContentParent* aSource);
|
||||
void DidSet(FieldIndex<IDX_InRDMPane>, bool aOldValue);
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY void DidSet(FieldIndex<IDX_ForceDesktopViewport>,
|
||||
bool aOldValue);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "DocumentOrShadowRoot.h"
|
||||
#include "mozilla/AnimationComparator.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/PointerLockManager.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/StyleSheet.h"
|
||||
|
|
@ -112,6 +113,10 @@ void DocumentOrShadowRoot::RemoveSheetFromStylesIfApplicable(
|
|||
void DocumentOrShadowRoot::OnSetAdoptedStyleSheets(StyleSheet& aSheet,
|
||||
uint32_t aIndex,
|
||||
ErrorResult& aRv) {
|
||||
if (MOZ_UNLIKELY(aIndex > mAdoptedStyleSheets.Length())) {
|
||||
MOZ_ASSERT_UNREACHABLE("Out of sync proxy");
|
||||
return;
|
||||
}
|
||||
Document& doc = *AsNode().OwnerDoc();
|
||||
// 1. If value’s constructed flag is not set, or its constructor document is
|
||||
// not equal to this DocumentOrShadowRoot's node document, throw a
|
||||
|
|
@ -164,7 +169,10 @@ void DocumentOrShadowRoot::OnSetAdoptedStyleSheets(StyleSheet& aSheet,
|
|||
void DocumentOrShadowRoot::OnDeleteAdoptedStyleSheets(StyleSheet& aSheet,
|
||||
uint32_t aIndex,
|
||||
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);
|
||||
auto existingIndex = mAdoptedStyleSheets.LastIndexOf(&aSheet);
|
||||
if (existingIndex != mAdoptedStyleSheets.NoIndex && existingIndex >= aIndex) {
|
||||
|
|
|
|||
|
|
@ -271,6 +271,7 @@ template <typename char_type>
|
|||
const nsTSubstring<char_type>& aMimeType,
|
||||
nsTSubstring<char_type>& aOutEssence,
|
||||
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 nsTDependentSubstring<char_type> kCharset(kCHARSET, 7);
|
||||
|
||||
|
|
@ -278,8 +279,8 @@ template <typename char_type>
|
|||
nsTAutoString<char_type> prevContentType;
|
||||
nsTAutoString<char_type> prevCharset;
|
||||
|
||||
prevContentType.Assign(aOutEssence);
|
||||
prevCharset.Assign(aOutCharset);
|
||||
aOutEssence.Truncate();
|
||||
aOutCharset.Truncate();
|
||||
|
||||
nsTArray<nsTDependentSubstring<char_type>> mimeTypeParts =
|
||||
SplitMimetype(aMimeType);
|
||||
|
|
@ -292,9 +293,7 @@ template <typename char_type>
|
|||
parsed = Parse(mimeTypeString);
|
||||
|
||||
if (!parsed) {
|
||||
aOutEssence.Truncate();
|
||||
aOutCharset.Truncate();
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
parsed->GetEssence(aOutEssence);
|
||||
|
|
@ -322,6 +321,10 @@ template <typename char_type>
|
|||
}
|
||||
}
|
||||
|
||||
if (aOutEssence.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,12 +44,15 @@ class ContentPermissionRequestParent : public PContentPermissionRequestParent {
|
|||
// @param aIsRequestDelegatedToUnsafeThirdParty see
|
||||
// mIsRequestDelegatedToUnsafeThirdParty.
|
||||
ContentPermissionRequestParent(
|
||||
const nsTArray<PermissionRequest>& aRequests, Element* aElement,
|
||||
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
|
||||
Element* aElement, nsIPrincipal* aPrincipal,
|
||||
nsIPrincipal* aTopLevelPrincipal,
|
||||
const bool aHasValidTransientUserGestureActivation,
|
||||
const bool aIsRequestDelegatedToUnsafeThirdParty);
|
||||
virtual ~ContentPermissionRequestParent();
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
||||
void Init(nsTArray<PermissionRequest>&& aRequests);
|
||||
|
||||
bool IsBeingDestroyed();
|
||||
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
|
@ -64,16 +67,13 @@ class ContentPermissionRequestParent : public PContentPermissionRequestParent {
|
|||
nsTArray<PermissionRequest> mRequests;
|
||||
|
||||
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 void ActorDestroy(ActorDestroyReason why) override;
|
||||
};
|
||||
|
||||
ContentPermissionRequestParent::ContentPermissionRequestParent(
|
||||
const nsTArray<PermissionRequest>& aRequests, Element* aElement,
|
||||
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
|
||||
Element* aElement, nsIPrincipal* aPrincipal,
|
||||
nsIPrincipal* aTopLevelPrincipal,
|
||||
const bool aHasValidTransientUserGestureActivation,
|
||||
const bool aIsRequestDelegatedToUnsafeThirdParty) {
|
||||
MOZ_COUNT_CTOR(ContentPermissionRequestParent);
|
||||
|
|
@ -81,7 +81,6 @@ ContentPermissionRequestParent::ContentPermissionRequestParent(
|
|||
mPrincipal = aPrincipal;
|
||||
mTopLevelPrincipal = aTopLevelPrincipal;
|
||||
mElement = aElement;
|
||||
mRequests = aRequests.Clone();
|
||||
mHasValidTransientUserGestureActivation =
|
||||
aHasValidTransientUserGestureActivation;
|
||||
mIsRequestDelegatedToUnsafeThirdParty = aIsRequestDelegatedToUnsafeThirdParty;
|
||||
|
|
@ -91,13 +90,14 @@ ContentPermissionRequestParent::~ContentPermissionRequestParent() {
|
|||
MOZ_COUNT_DTOR(ContentPermissionRequestParent);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentPermissionRequestParent::Recvprompt() {
|
||||
void ContentPermissionRequestParent::Init(
|
||||
nsTArray<PermissionRequest>&& aRequests) {
|
||||
mRequests = std::move(aRequests);
|
||||
mProxy = new nsContentPermissionRequestProxy(this);
|
||||
if (NS_FAILED(mProxy->Init(mRequests))) {
|
||||
RefPtr<nsContentPermissionRequestProxy> proxy(mProxy);
|
||||
proxy->Cancel();
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentPermissionRequestParent::RecvDestroy() {
|
||||
|
|
@ -239,12 +239,12 @@ nsresult nsContentPermissionUtils::CreatePermissionArray(
|
|||
/* static */
|
||||
PContentPermissionRequestParent*
|
||||
nsContentPermissionUtils::CreateContentPermissionRequestParent(
|
||||
const nsTArray<PermissionRequest>& aRequests, Element* aElement,
|
||||
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
|
||||
Element* aElement, nsIPrincipal* aPrincipal,
|
||||
nsIPrincipal* aTopLevelPrincipal,
|
||||
const bool aHasValidTransientUserGestureActivation,
|
||||
const bool aIsRequestDelegatedToUnsafeThirdParty, const TabId& aTabId) {
|
||||
PContentPermissionRequestParent* parent = new ContentPermissionRequestParent(
|
||||
aRequests, aElement, aPrincipal, aTopLevelPrincipal,
|
||||
aElement, aPrincipal, aTopLevelPrincipal,
|
||||
aHasValidTransientUserGestureActivation,
|
||||
aIsRequestDelegatedToUnsafeThirdParty);
|
||||
ContentPermissionRequestParentMap()[parent] = aTabId;
|
||||
|
|
@ -252,6 +252,14 @@ nsContentPermissionUtils::CreateContentPermissionRequestParent(
|
|||
return parent;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void nsContentPermissionUtils::InitContentPermissionRequestParent(
|
||||
PContentPermissionRequestParent* aActor,
|
||||
nsTArray<PermissionRequest>&& aRequests) {
|
||||
static_cast<ContentPermissionRequestParent*>(aActor)->Init(
|
||||
std::move(aRequests));
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult nsContentPermissionUtils::AskPermission(
|
||||
nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow) {
|
||||
|
|
@ -301,7 +309,6 @@ nsresult nsContentPermissionUtils::AskPermission(
|
|||
}
|
||||
ContentPermissionRequestChildMap()[req.get()] = child->GetTabId();
|
||||
|
||||
req->Sendprompt();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,11 +63,15 @@ class nsContentPermissionUtils {
|
|||
// @param aIsRequestDelegatedToUnsafeThirdParty see
|
||||
// ContentPermissionRequestParent.
|
||||
static PContentPermissionRequestParent* CreateContentPermissionRequestParent(
|
||||
const nsTArray<PermissionRequest>& aRequests, Element* aElement,
|
||||
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
|
||||
Element* aElement, nsIPrincipal* aPrincipal,
|
||||
nsIPrincipal* aTopLevelPrincipal,
|
||||
const bool aHasValidTransientUserGestureActivation,
|
||||
const bool aIsRequestDelegatedToUnsafeThirdParty, const TabId& aTabId);
|
||||
|
||||
static void InitContentPermissionRequestParent(
|
||||
PContentPermissionRequestParent* aActor,
|
||||
nsTArray<PermissionRequest>&& aRequests);
|
||||
|
||||
static nsresult AskPermission(nsIContentPermissionRequest* aRequest,
|
||||
nsPIDOMWindowInner* aWindow);
|
||||
|
||||
|
|
|
|||
|
|
@ -821,8 +821,8 @@ TEST(MimeTypeParsing, contentTypes1)
|
|||
|
||||
bool parsed = CMimeType::Parse(val, contentType, contentCharset);
|
||||
|
||||
ASSERT_FALSE(parsed);
|
||||
ASSERT_TRUE(contentType.EqualsLiteral(""));
|
||||
ASSERT_TRUE(parsed);
|
||||
ASSERT_TRUE(contentType.EqualsLiteral("text/plain"));
|
||||
ASSERT_TRUE(contentCharset.EqualsLiteral(""));
|
||||
}
|
||||
|
||||
|
|
@ -1074,3 +1074,43 @@ TEST(MimeTypeParsing, contentTypes20)
|
|||
ASSERT_TRUE(contentType.EqualsLiteral("text/plain"));
|
||||
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(""));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,39 +259,6 @@ nsTArray<nsCString>& TErrorResult<CleanupPolicy>::CreateErrorMessageHelper(
|
|||
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>
|
||||
void TErrorResult<CleanupPolicy>::SetPendingExceptionWithMessage(
|
||||
JSContext* aCx, const char* context) {
|
||||
|
|
@ -401,34 +368,106 @@ struct TErrorResult<CleanupPolicy>::DOMExceptionInfo {
|
|||
};
|
||||
|
||||
template <typename CleanupPolicy>
|
||||
void TErrorResult<CleanupPolicy>::SerializeDOMExceptionInfo(
|
||||
void TErrorResult<CleanupPolicy>::SerializeErrorResult(
|
||||
IPC::MessageWriter* aWriter) const {
|
||||
using namespace IPC;
|
||||
AssertInOwningThread();
|
||||
MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
|
||||
MOZ_ASSERT(mExtra.mDOMExceptionInfo);
|
||||
WriteParam(aWriter, mExtra.mDOMExceptionInfo->mMessage);
|
||||
WriteParam(aWriter, mExtra.mDOMExceptionInfo->mRv);
|
||||
|
||||
// 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(!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>
|
||||
bool TErrorResult<CleanupPolicy>::DeserializeDOMExceptionInfo(
|
||||
bool TErrorResult<CleanupPolicy>::DeserializeErrorResult(
|
||||
IPC::MessageReader* aReader) {
|
||||
using namespace IPC;
|
||||
AssertInOwningThread();
|
||||
nsCString message;
|
||||
nsresult rv;
|
||||
if (!ReadParam(aReader, &message) || !ReadParam(aReader, &rv)) {
|
||||
|
||||
nsresult result;
|
||||
if (!ReadParam(aReader, &result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mUnionState == HasNothing);
|
||||
MOZ_ASSERT(IsDOMException());
|
||||
InitDOMExceptionInfo(new DOMExceptionInfo(rv, message));
|
||||
switch (result) {
|
||||
case NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION:
|
||||
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
|
||||
mUnionState = HasDOMExceptionInfo;
|
||||
#endif // DEBUG
|
||||
return true;
|
||||
mUnionState = HasMessage;
|
||||
#endif
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,8 @@
|
|||
#ifndef IPC_ErrorIPCUtils_h
|
||||
#define IPC_ErrorIPCUtils_h
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "ipc/EnumSerializer.h"
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
|
||||
namespace IPC {
|
||||
|
|
@ -24,64 +21,18 @@ struct ParamTraits<mozilla::dom::ErrNum>
|
|||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::ErrorResult> {
|
||||
typedef mozilla::ErrorResult paramType;
|
||||
|
||||
static void Write(MessageWriter* aWriter, const paramType& aParam) {
|
||||
// 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,
|
||||
const mozilla::ErrorResult& aParam) {
|
||||
aParam.SerializeErrorResult(aWriter);
|
||||
}
|
||||
|
||||
static void Write(MessageWriter* aWriter, paramType&& aParam) {
|
||||
Write(aWriter, static_cast<const paramType&>(aParam));
|
||||
static void Write(MessageWriter* aWriter, mozilla::ErrorResult&& aParam) {
|
||||
aParam.SerializeErrorResult(aWriter);
|
||||
aParam.SuppressException();
|
||||
}
|
||||
|
||||
static bool Read(MessageReader* aReader, paramType* aResult) {
|
||||
paramType readValue;
|
||||
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;
|
||||
static bool Read(MessageReader* aReader, mozilla::ErrorResult* aResult) {
|
||||
return aResult->DeserializeErrorResult(aReader);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -90,14 +41,11 @@ struct ParamTraits<mozilla::CopyableErrorResult> {
|
|||
typedef mozilla::CopyableErrorResult paramType;
|
||||
|
||||
static void Write(MessageWriter* aWriter, const paramType& aParam) {
|
||||
ParamTraits<mozilla::ErrorResult>::Write(aWriter, aParam);
|
||||
aParam.SerializeErrorResult(aWriter);
|
||||
}
|
||||
|
||||
static bool Read(MessageReader* aReader, paramType* aResult) {
|
||||
// We can't cast *aResult to ErrorResult&, so cheat and just cast
|
||||
// to ErrorResult*.
|
||||
return ParamTraits<mozilla::ErrorResult>::Read(
|
||||
aReader, reinterpret_cast<mozilla::ErrorResult*>(aResult));
|
||||
return aResult->DeserializeErrorResult(aReader);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -401,7 +401,7 @@ class TErrorResult {
|
|||
|
||||
// Check whether the TErrorResult says to just throw whatever is on
|
||||
// the JSContext already.
|
||||
bool IsJSContextException() {
|
||||
bool IsJSContextException() const {
|
||||
return ErrorCode() == NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT;
|
||||
}
|
||||
|
||||
|
|
@ -481,11 +481,10 @@ class TErrorResult {
|
|||
|
||||
friend struct IPC::ParamTraits<TErrorResult>;
|
||||
friend struct IPC::ParamTraits<ErrorResult>;
|
||||
void SerializeMessage(IPC::MessageWriter* aWriter) const;
|
||||
bool DeserializeMessage(IPC::MessageReader* aReader);
|
||||
friend struct IPC::ParamTraits<CopyableErrorResult>;
|
||||
|
||||
void SerializeDOMExceptionInfo(IPC::MessageWriter* aWriter) const;
|
||||
bool DeserializeDOMExceptionInfo(IPC::MessageReader* aReader);
|
||||
void SerializeErrorResult(IPC::MessageWriter* aWriter) const;
|
||||
bool DeserializeErrorResult(IPC::MessageReader* aReader);
|
||||
|
||||
// Helper method that creates a new Message for this TErrorResult,
|
||||
// and returns the arguments array from that Message.
|
||||
|
|
|
|||
|
|
@ -277,6 +277,9 @@ bool ObservableArrayProxyHandler::GetBackingListObject(
|
|||
if (NS_WARN_IF(!newBackingListObj)) {
|
||||
return false;
|
||||
}
|
||||
if (NS_WARN_IF(!JS_SetPrototype(aCx, newBackingListObj, nullptr))) {
|
||||
return false;
|
||||
}
|
||||
slotValue = JS::ObjectValue(*newBackingListObj);
|
||||
js::SetProxyReservedSlot(aProxy, OBSERVABLE_ARRAY_BACKING_LIST_OBJECT_SLOT,
|
||||
slotValue);
|
||||
|
|
|
|||
|
|
@ -1110,10 +1110,6 @@ bool TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec,
|
|||
////
|
||||
|
||||
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;
|
||||
uint8_t srcBPP;
|
||||
|
|
@ -1165,6 +1161,12 @@ bool TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec,
|
|||
const auto& dstUnpacking = dstUnpackingRes.inspect();
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -418,6 +418,11 @@ GlobalKeyListener::WalkHandlersResult GlobalKeyListener::WalkHandlersAndExecute(
|
|||
|
||||
bool GlobalKeyListener::IsReservedKey(WidgetKeyboardEvent* aKeyEvent,
|
||||
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();
|
||||
// reserved="true" means that the key is always reserved. reserved="false"
|
||||
// means that the key is never reserved. Otherwise, we check site-specific
|
||||
|
|
|
|||
|
|
@ -18723,6 +18723,14 @@ mozilla::ipc::IPCResult NormalTransactionOp::RecvContinue(
|
|||
const PreprocessResponse& aResponse) {
|
||||
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()) {
|
||||
case PreprocessResponse::Tnsresult:
|
||||
SetFailureCode(aResponse.get_nsresult());
|
||||
|
|
|
|||
|
|
@ -280,10 +280,13 @@ IPCResult BrowserBridgeParent::RecvSetEmbedderAccessible(
|
|||
# if defined(ANDROID)
|
||||
MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor());
|
||||
# endif
|
||||
MOZ_ASSERT(aDoc || mEmbedderAccessibleDoc,
|
||||
"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 (!aDoc && !mEmbedderAccessibleDoc) {
|
||||
return IPC_FAIL(this, "Embedder doc shouldn't be cleared if it wasn't set");
|
||||
}
|
||||
if (mEmbedderAccessibleDoc && aDoc && mEmbedderAccessibleDoc != aDoc) {
|
||||
return IPC_FAIL(this,
|
||||
"Embedder doc shouldn't change from one doc to another");
|
||||
}
|
||||
if (!aDoc && mEmbedderAccessibleDoc &&
|
||||
!mEmbedderAccessibleDoc->IsShutdown()) {
|
||||
// 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);
|
||||
mEmbedderAccessibleID = aID;
|
||||
if (!aDoc) {
|
||||
MOZ_ASSERT(!aID);
|
||||
if (aID) {
|
||||
return IPC_FAIL(this, "Attempt to clear embedder but id given");
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
MOZ_ASSERT(aID);
|
||||
if (!aID) {
|
||||
return IPC_FAIL(this, "Attempt to set embedder without id");
|
||||
}
|
||||
if (GetDocAccessibleParent()) {
|
||||
// The embedded DocAccessibleParent has already been created. This can
|
||||
// happen if, for example, an iframe is hidden and then shown or
|
||||
// an iframe is reflowed by layout.
|
||||
// happen if, for example, an iframe is hidden and then shown or an iframe
|
||||
// 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);
|
||||
}
|
||||
return IPC_OK();
|
||||
|
|
|
|||
|
|
@ -2440,6 +2440,10 @@ mozilla::ipc::IPCResult BrowserChild::RecvRealKeyEvent(
|
|||
// we need to clear the flag explicitly here because ParamTraits should
|
||||
// keep checking the flag for avoiding regression.
|
||||
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);
|
||||
|
||||
return IPC_OK();
|
||||
|
|
|
|||
|
|
@ -1291,6 +1291,14 @@ mozilla::ipc::IPCResult BrowserParent::RecvPDocAccessibleConstructor(
|
|||
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) {
|
||||
doc->SetBrowsingContext(aBrowsingContext.get_canonical());
|
||||
}
|
||||
|
|
@ -1325,13 +1333,6 @@ mozilla::ipc::IPCResult BrowserParent::RecvPDocAccessibleConstructor(
|
|||
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();
|
||||
a11y::DocManager::RemoteDocAdded(doc);
|
||||
# ifdef XP_WIN
|
||||
|
|
@ -2782,7 +2783,10 @@ mozilla::ipc::IPCResult BrowserParent::RecvReplyKeyEvent(
|
|||
NS_WARN_IF(data.mPseudoCharCode != aEvent.mPseudoCharCode) ||
|
||||
NS_WARN_IF(data.mKeyNameIndex != aEvent.mKeyNameIndex) ||
|
||||
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
|
||||
// event with the ID.
|
||||
return Nothing();
|
||||
|
|
@ -3909,13 +3913,18 @@ mozilla::ipc::IPCResult BrowserParent::RecvInvokeDragSession(
|
|||
cookieJarSettings, aSourceWindowContext.GetMaybeDiscarded(),
|
||||
aSourceTopWindowContext.GetMaybeDiscarded());
|
||||
|
||||
if (aVisualDnDData) {
|
||||
const auto checkedSize = CheckedInt<size_t>(aDragRect.height) * aStride;
|
||||
if (checkedSize.isValid() &&
|
||||
aVisualDnDData->Size() >= checkedSize.value()) {
|
||||
if (aVisualDnDData && aDragRect.width >= 0 && aDragRect.height >= 0) {
|
||||
const auto checkedSize = CheckedInt<int32_t>(aDragRect.height) * aStride;
|
||||
const auto computedStride =
|
||||
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(
|
||||
gfx::IntSize(aDragRect.width, aDragRect.height), aFormat,
|
||||
aVisualDnDData->Data(), aStride));
|
||||
aVisualDnDData->Data(), checkedStride.value()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5130,8 +5130,18 @@ ContentParent::AllocPContentPermissionRequestParent(
|
|||
topPrincipal = principal;
|
||||
}
|
||||
return nsContentPermissionUtils::CreateContentPermissionRequestParent(
|
||||
aRequests, tp->GetOwnerElement(), aPrincipal, topPrincipal,
|
||||
aIsHandlingUserInput, aMaybeUnsafePermissionDelegate, aTabId);
|
||||
tp->GetOwnerElement(), aPrincipal, topPrincipal, aIsHandlingUserInput,
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -538,6 +538,12 @@ class ContentParent final : public PContentParent,
|
|||
nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput,
|
||||
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(
|
||||
PContentPermissionRequestParent* actor);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ protocol PContentPermissionRequest
|
|||
manager PContent;
|
||||
|
||||
parent:
|
||||
async prompt();
|
||||
async Destroy();
|
||||
|
||||
child:
|
||||
|
|
|
|||
|
|
@ -6595,6 +6595,12 @@ mozilla::ipc::IPCResult LSRequestBase::RecvCancel() {
|
|||
mozilla::ipc::IPCResult LSRequestBase::RecvFinish() {
|
||||
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();
|
||||
|
||||
return IPC_OK();
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "MediaInfo.h"
|
||||
#include "MediaResult.h"
|
||||
#include "PerformanceRecorder.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "YCbCrUtils.h"
|
||||
#include "mozilla/gfx/gfxVars.h"
|
||||
|
|
@ -28,8 +29,12 @@
|
|||
# include "mozilla/gfx/gfxVars.h"
|
||||
#endif
|
||||
|
||||
#define LOG(level, msg, ...) \
|
||||
MOZ_LOG_FMT(sPDMLog, level, "%s: " msg, __func__, ##__VA_ARGS__)
|
||||
namespace mozilla {
|
||||
|
||||
extern LazyLogModule sPDMLog;
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
using layers::PlanarYCbCrData;
|
||||
using layers::PlanarYCbCrImage;
|
||||
|
|
@ -186,6 +191,19 @@ static bool ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane) {
|
|||
|
||||
static MediaResult ValidateBufferAndPicture(
|
||||
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
|
||||
// in the decoder
|
||||
if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth ||
|
||||
|
|
@ -193,7 +211,6 @@ static MediaResult ValidateBufferAndPicture(
|
|||
return MediaResult(NS_ERROR_INVALID_ARG,
|
||||
"Chroma planes with different sizes");
|
||||
}
|
||||
|
||||
// The following situations could be triggered by invalid input
|
||||
if (aPicture.width <= 0 || aPicture.height <= 0) {
|
||||
return MediaResult(NS_ERROR_INVALID_ARG, "Empty picture rect");
|
||||
|
|
@ -203,7 +220,12 @@ static MediaResult ValidateBufferAndPicture(
|
|||
!ValidatePlane(aBuffer.mPlanes[2])) {
|
||||
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
|
||||
// the frame we've been supplied without indexing out of bounds.
|
||||
CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
|
||||
|
|
@ -291,6 +313,7 @@ PlanarYCbCrData ConstructPlanarYCbCrData(const VideoInfo& aInfo,
|
|||
data.mYSkip = AssertedCast<int32_t>(Y.mSkip);
|
||||
data.mCbChannel = Cb.mData;
|
||||
data.mCrChannel = Cr.mData;
|
||||
MOZ_ASSERT(Cb.mStride == Cr.mStride);
|
||||
data.mCbCrStride = AssertedCast<int32_t>(Cb.mStride);
|
||||
data.mCbSkip = AssertedCast<int32_t>(Cb.mSkip);
|
||||
data.mCrSkip = AssertedCast<int32_t>(Cr.mSkip);
|
||||
|
|
@ -409,6 +432,22 @@ already_AddRefed<VideoData> VideoData::CreateAndCopyData(
|
|||
NS_ERROR(r.Message().get());
|
||||
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,
|
||||
aTimecode, aInfo.mDisplay, 0));
|
||||
|
|
@ -635,3 +674,4 @@ CryptoScheme StringToCryptoScheme(const nsAString& aString) {
|
|||
}
|
||||
|
||||
} // namespace mozilla
|
||||
#undef LOG
|
||||
|
|
|
|||
|
|
@ -129,6 +129,10 @@ class FakeVideoEncoder : public GMPVideoEncoder {
|
|||
GMPVideoEncoderCallback* callback, int32_t numberOfCores,
|
||||
uint32_t maxPayloadSize) override {
|
||||
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)
|
||||
? maxPayloadSize
|
||||
: BIG_FRAME;
|
||||
|
|
@ -146,6 +150,18 @@ class FakeVideoEncoder : public GMPVideoEncoder {
|
|||
|
||||
void SendFrame(GMPVideoi420Frame* inputImage, GMPVideoFrameType frame_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.
|
||||
// Send SPS/PPS/IDR to avoid confusing people
|
||||
// Copy the data. This really should convert this to network byte order.
|
||||
|
|
@ -201,7 +217,7 @@ class FakeVideoEncoder : public GMPVideoEncoder {
|
|||
f->SetCompleteFrame(true);
|
||||
f->SetBufferType(GMP_BufferLength32);
|
||||
|
||||
GMPLOG(GL_DEBUG, "Encoding complete. type= "
|
||||
GMPLOG(GL_DEBUG, "Encoding complete. type="
|
||||
<< f->FrameType()
|
||||
<< " NAL_type=" << (int)eframe.idr_nalu.h264_compat_
|
||||
<< " length=" << f->Size()
|
||||
|
|
@ -271,6 +287,8 @@ class FakeVideoEncoder : public GMPVideoEncoder {
|
|||
|
||||
GMPVideoHost* host_;
|
||||
GMPVideoEncoderCallback* callback_ = nullptr;
|
||||
uint16_t frame_drop_cadence_ = 0;
|
||||
uint16_t num_frames_since_drop_ = 0;
|
||||
uint32_t frame_size_ = BIG_FRAME;
|
||||
uint32_t frames_encoded_ = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@
|
|||
#include <stdio.h>
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsExceptionHandler.h"
|
||||
#include "GMPLog.h"
|
||||
#include "gmp-entrypoints.h"
|
||||
#include "prlink.h"
|
||||
#include "prenv.h"
|
||||
#include "prerror.h"
|
||||
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
|
||||
# include "mozilla/sandboxTarget.h"
|
||||
# include "mozilla/sandboxing/SandboxInitialization.h"
|
||||
# include "mozilla/sandboxing/sandboxLogging.h"
|
||||
# include "nsWindowsHelpers.h"
|
||||
#endif
|
||||
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
|
||||
# include "mozilla/Sandbox.h"
|
||||
|
|
@ -83,18 +83,107 @@ class PassThroughGMPAdapter : public GMPAdapter {
|
|||
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,
|
||||
const GMPPlatformAPI* aPlatformAPI, GMPAdapter* aAdapter) {
|
||||
CrashReporter::AutoRecordAnnotation autoLibPath(
|
||||
CrashReporter::Annotation::GMPLibraryPath,
|
||||
nsDependentCString(aUTF8LibPath));
|
||||
|
||||
if (!getenv("MOZ_DISABLE_GMP_SANDBOX") && mSandboxStarter &&
|
||||
!mSandboxStarter->Start(aUTF8LibPath)) {
|
||||
MOZ_CRASH("Cannot start sandbox!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the GMP.
|
||||
PRLibSpec libSpec;
|
||||
#ifdef XP_WIN
|
||||
|
|
@ -111,6 +200,20 @@ bool GMPLoader::Load(const char* aUTF8LibPath, uint32_t aUTF8LibPathLen,
|
|||
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.type = PR_LibSpec_PathnameU;
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
namespace mozilla::gmp {
|
||||
|
||||
class GMPVideoi420FrameImpl;
|
||||
|
||||
enum class GMPSharedMemClass { Decoded, Encoded };
|
||||
|
||||
class GMPSharedMemManager {
|
||||
|
|
@ -27,6 +29,8 @@ class GMPSharedMemManager {
|
|||
virtual bool MgrAllocShmem(size_t aSize, ipc::Shmem* aMem) { return false; }
|
||||
virtual void MgrDeallocShmem(ipc::Shmem& aMem) = 0;
|
||||
|
||||
virtual void MgrDecodedFrameDestroyed(GMPVideoi420FrameImpl* aFrame) {}
|
||||
|
||||
protected:
|
||||
virtual bool MgrIsOnOwningThread() const = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#include "mozilla/Unused.h"
|
||||
#include "GMPPlatform.h"
|
||||
#include "GMPVideoEncodedFrameImpl.h"
|
||||
#include "GMPVideoi420FrameImpl.h"
|
||||
#include "runnable_utils.h"
|
||||
|
||||
namespace mozilla::gmp {
|
||||
|
|
@ -67,9 +66,26 @@ void GMPVideoEncoderChild::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
|
|||
MOZ_CRASH("Encoded without any frame data!");
|
||||
}
|
||||
|
||||
mLatestEncodedTimestamp = frameData.mTimestamp();
|
||||
|
||||
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) {
|
||||
if (NS_WARN_IF(!mPlugin)) {
|
||||
return;
|
||||
|
|
@ -116,8 +132,10 @@ mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvEncode(
|
|||
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),
|
||||
&mVideoHost);
|
||||
&mVideoHost, HostReportPolicy::Destroyed);
|
||||
|
||||
// Ignore any return code. It is OK for this to fail without killing the
|
||||
// process.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/gmp/PGMPVideoEncoderChild.h"
|
||||
#include "gmp-video-encode.h"
|
||||
#include "GMPSharedMemManager.h"
|
||||
#include "GMPVideoi420FrameImpl.h"
|
||||
#include "GMPVideoHost.h"
|
||||
|
||||
namespace mozilla::gmp {
|
||||
|
|
@ -39,6 +40,7 @@ class GMPVideoEncoderChild final : public PGMPVideoEncoderChild,
|
|||
|
||||
// GMPSharedMemManager
|
||||
void MgrDeallocShmem(Shmem& aMem) override { DeallocShmem(aMem); }
|
||||
void MgrDecodedFrameDestroyed(GMPVideoi420FrameImpl* aFrame) override;
|
||||
|
||||
protected:
|
||||
bool MgrIsOnOwningThread() const override;
|
||||
|
|
@ -66,6 +68,7 @@ class GMPVideoEncoderChild final : public PGMPVideoEncoderChild,
|
|||
GMPContentChild* mPlugin;
|
||||
GMPVideoEncoder* mVideoEncoder;
|
||||
GMPVideoHostImpl mVideoHost;
|
||||
uint64_t mLatestEncodedTimestamp = 0;
|
||||
};
|
||||
|
||||
} // namespace mozilla::gmp
|
||||
|
|
|
|||
|
|
@ -271,6 +271,14 @@ mozilla::ipc::IPCResult GMPVideoEncoderParent::RecvEncodedData(
|
|||
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) {
|
||||
if (mCallback) {
|
||||
mCallback->Error(aError);
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ class GMPVideoEncoderParent final : public GMPVideoEncoderProxy,
|
|||
const GMPVideoEncodedFrameData& aEncodedFrame,
|
||||
nsTArray<uint8_t>&& aEncodedData,
|
||||
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 RecvShutdown() override;
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class GMPVideoEncoderCallbackProxy : public GMPCallbackBase {
|
|||
virtual ~GMPVideoEncoderCallbackProxy() = default;
|
||||
virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
|
||||
const nsTArray<uint8_t>& aCodecSpecificInfo) = 0;
|
||||
virtual void Dropped(uint64_t aTimestamp) = 0;
|
||||
virtual void Error(GMPErr aError) = 0;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -93,6 +93,9 @@ void GMPVideoHostImpl::DecodedFrameCreated(
|
|||
|
||||
void GMPVideoHostImpl::DecodedFrameDestroyed(GMPVideoi420FrameImpl* aFrame) {
|
||||
MOZ_ALWAYS_TRUE(mDecodedFrames.RemoveElement(aFrame));
|
||||
if (mSharedMemMgr && aFrame->mReportPolicy == HostReportPolicy::Destroyed) {
|
||||
mSharedMemMgr->MgrDecodedFrameDestroyed(aFrame);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla::gmp
|
||||
|
|
|
|||
|
|
@ -38,16 +38,25 @@ void GMPVideoi420FrameImpl::GMPFramePlane::Copy(uint8_t* aDst,
|
|||
}
|
||||
}
|
||||
|
||||
GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(GMPVideoHostImpl* aHost)
|
||||
: mHost(aHost), mWidth(0), mHeight(0), mTimestamp(0ll), mDuration(0ll) {
|
||||
GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(
|
||||
GMPVideoHostImpl* aHost,
|
||||
HostReportPolicy aReportPolicy /*= HostReportPolicy::None*/)
|
||||
: mReportPolicy(aReportPolicy),
|
||||
mHost(aHost),
|
||||
mWidth(0),
|
||||
mHeight(0),
|
||||
mTimestamp(0ll),
|
||||
mDuration(0ll) {
|
||||
MOZ_ASSERT(aHost);
|
||||
aHost->DecodedFrameCreated(this);
|
||||
}
|
||||
|
||||
GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(
|
||||
const GMPVideoi420FrameData& aFrameData, ipc::Shmem&& aShmemBuffer,
|
||||
GMPVideoHostImpl* aHost)
|
||||
: mHost(aHost),
|
||||
GMPVideoHostImpl* aHost,
|
||||
HostReportPolicy aReportPolicy /*= HostReportPolicy::None*/)
|
||||
: mReportPolicy(aReportPolicy),
|
||||
mHost(aHost),
|
||||
mShmemBuffer(std::move(aShmemBuffer)),
|
||||
mYPlane(aFrameData.mYPlane()),
|
||||
mUPlane(aFrameData.mUPlane()),
|
||||
|
|
@ -63,8 +72,10 @@ GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(
|
|||
|
||||
GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(
|
||||
const GMPVideoi420FrameData& aFrameData, nsTArray<uint8_t>&& aArrayBuffer,
|
||||
GMPVideoHostImpl* aHost)
|
||||
: mHost(aHost),
|
||||
GMPVideoHostImpl* aHost,
|
||||
HostReportPolicy aReportPolicy /*= HostReportPolicy::None*/)
|
||||
: mReportPolicy(aReportPolicy),
|
||||
mHost(aHost),
|
||||
mArrayBuffer(std::move(aArrayBuffer)),
|
||||
mYPlane(aFrameData.mYPlane()),
|
||||
mUPlane(aFrameData.mUPlane()),
|
||||
|
|
@ -146,30 +157,76 @@ bool GMPVideoi420FrameImpl::CheckFrameData(
|
|||
// if so. Note: Size() greater than expected is also an error, but with no
|
||||
// negative consequences
|
||||
int32_t half_width = (aFrameData.mWidth() + 1) / 2;
|
||||
if ((aFrameData.mYPlane().mStride() <= 0) ||
|
||||
(aFrameData.mYPlane().mSize() <= 0) ||
|
||||
(aFrameData.mYPlane().mOffset() < 0) ||
|
||||
(aFrameData.mUPlane().mStride() <= 0) ||
|
||||
(aFrameData.mUPlane().mSize() <= 0) ||
|
||||
(aFrameData.mUPlane().mOffset() <
|
||||
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))) {
|
||||
int32_t half_height = (aFrameData.mHeight() + 1) / 2;
|
||||
|
||||
// Check for negative offsets
|
||||
if ((aFrameData.mYPlane().mOffset() < 0) ||
|
||||
(aFrameData.mUPlane().mOffset() < 0) ||
|
||||
(aFrameData.mVPlane().mOffset() < 0)) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,14 +17,24 @@ class GMPPlaneData;
|
|||
class GMPVideoi420FrameData;
|
||||
class GMPVideoHostImpl;
|
||||
|
||||
class GMPVideoi420FrameImpl final : public GMPVideoi420Frame {
|
||||
enum class HostReportPolicy : uint8_t {
|
||||
None,
|
||||
Destroyed,
|
||||
};
|
||||
|
||||
class GMPVideoi420FrameImpl : public GMPVideoi420Frame {
|
||||
public:
|
||||
explicit GMPVideoi420FrameImpl(GMPVideoHostImpl* aHost);
|
||||
GMPVideoi420FrameImpl(const GMPVideoi420FrameData& aFrameData,
|
||||
ipc::Shmem&& aShmemBuffer, GMPVideoHostImpl* aHost);
|
||||
GMPVideoi420FrameImpl(const GMPVideoi420FrameData& aFrameData,
|
||||
nsTArray<uint8_t>&& aArrayBuffer,
|
||||
GMPVideoHostImpl* aHost);
|
||||
explicit GMPVideoi420FrameImpl(
|
||||
GMPVideoHostImpl* aHost,
|
||||
HostReportPolicy aReportPolicy = HostReportPolicy::None);
|
||||
GMPVideoi420FrameImpl(
|
||||
const GMPVideoi420FrameData& aFrameData, ipc::Shmem&& aShmemBuffer,
|
||||
GMPVideoHostImpl* aHost,
|
||||
HostReportPolicy aReportPolicy = HostReportPolicy::None);
|
||||
GMPVideoi420FrameImpl(
|
||||
const GMPVideoi420FrameData& aFrameData, nsTArray<uint8_t>&& aArrayBuffer,
|
||||
GMPVideoHostImpl* aHost,
|
||||
HostReportPolicy aReportPolicy = HostReportPolicy::None);
|
||||
virtual ~GMPVideoi420FrameImpl();
|
||||
|
||||
// This is called during a normal destroy sequence, which is
|
||||
|
|
@ -75,7 +85,7 @@ class GMPVideoi420FrameImpl final : public GMPVideoi420Frame {
|
|||
const uint8_t* Buffer() const;
|
||||
int32_t AllocatedSize() const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
struct GMPFramePlane {
|
||||
explicit GMPFramePlane(const GMPPlaneData& aPlaneData);
|
||||
GMPFramePlane() = default;
|
||||
|
|
@ -98,6 +108,10 @@ class GMPVideoi420FrameImpl final : public GMPVideoi420Frame {
|
|||
GMPErr MaybeResize(int32_t aNewSize);
|
||||
void DestroyBuffer();
|
||||
|
||||
public:
|
||||
const HostReportPolicy mReportPolicy;
|
||||
|
||||
protected:
|
||||
GMPVideoHostImpl* mHost;
|
||||
nsTArray<uint8_t> mArrayBuffer;
|
||||
ipc::Shmem mShmemBuffer;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ parent:
|
|||
async EncodedData(GMPVideoEncodedFrameData aEncodedFrame,
|
||||
uint8_t[] aEncodedData,
|
||||
uint8_t[] aCodecSpecificInfo);
|
||||
async DroppedFrame(uint64_t aTimestamp);
|
||||
async Error(GMPErr aErr);
|
||||
async Shutdown();
|
||||
};
|
||||
|
|
|
|||
279
icecat/dom/media/gtest/TestWebrtcGmpCodec.cpp
Normal file
279
icecat/dom/media/gtest/TestWebrtcGmpCodec.cpp
Normal 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
|
||||
|
|
@ -92,6 +92,7 @@ if CONFIG["MOZ_WEBRTC"] and CONFIG["OS_TARGET"] != "Android":
|
|||
"TestAudioDeviceEnumerator.cpp",
|
||||
"TestPacer.cpp",
|
||||
"TestVideoFrameConverter.cpp",
|
||||
"TestWebrtcGmpCodec.cpp",
|
||||
]
|
||||
|
||||
TEST_HARNESS_FILES.gtest += [
|
||||
|
|
|
|||
|
|
@ -187,13 +187,17 @@ RefPtr<MediaDataDecoder::DecodePromise> VPXDecoder::ProcessDecode(
|
|||
|
||||
if (img->fmt == VPX_IMG_FMT_I420) {
|
||||
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;
|
||||
MOZ_ASSERT(img->x_chroma_shift == 1);
|
||||
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].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
|
||||
} 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].mWidth = img->d_w;
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
StaticString aCallSite) {
|
||||
GMP_LOG_DEBUG("[%p] GMPVideoEncoder::Teardown", this);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class GMPVideoEncoder final : public MediaDataEncoder,
|
|||
|
||||
void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
|
||||
const nsTArray<uint8_t>& aCodecSpecificInfo) override;
|
||||
void Dropped(uint64_t aTimestamp) override;
|
||||
void Error(GMPErr aError) override;
|
||||
void Terminated() override;
|
||||
|
||||
|
|
|
|||
|
|
@ -568,7 +568,7 @@ void AudioData::CopyTo(const AllowSharedBufferSource& aDestination,
|
|||
uint32_t bytesPerSample = BytesPerSamples(destFormat.value());
|
||||
CheckedInt<uint32_t> copyLength = bytesPerSample;
|
||||
copyLength *= copyElementCount;
|
||||
if (copyLength.value() > destLength) {
|
||||
if (!copyLength.isValid() || copyLength.value() > destLength) {
|
||||
auto msg = nsFmtCString(FMT_STRING("destination buffer of length {} too "
|
||||
"small for copying {} elements"),
|
||||
destLength, bytesPerSample * copyElementCount);
|
||||
|
|
|
|||
|
|
@ -1905,17 +1905,17 @@ nsresult JsepSessionImpl::ValidateRemoteDescription(const Sdp& description) {
|
|||
const SdpMediaSection& oldMsection =
|
||||
mCurrentRemoteDescription->GetMediaSection(i);
|
||||
|
||||
if (mSdpHelper.MsectionIsDisabled(newMsection) ||
|
||||
mSdpHelper.MsectionIsDisabled(oldMsection)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (oldMsection.GetMediaType() != newMsection.GetMediaType()) {
|
||||
JSEP_SET_ERROR("Remote description changes the media type of m-line "
|
||||
<< i);
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (mSdpHelper.MsectionIsDisabled(newMsection) ||
|
||||
mSdpHelper.MsectionIsDisabled(oldMsection)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool differ = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection);
|
||||
|
||||
if (mIsPendingOfferer.isSome() && *mIsPendingOfferer && differ &&
|
||||
|
|
|
|||
|
|
@ -5,10 +5,14 @@
|
|||
#include "jsep/JsepTrack.h"
|
||||
#include "jsep/JsepCodecDescription.h"
|
||||
#include "jsep/JsepTrackEncoding.h"
|
||||
#include "transport/logging.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
MOZ_MTLOG_MODULE("jsep")
|
||||
|
||||
void JsepTrack::GetNegotiatedPayloadTypes(
|
||||
std::vector<uint16_t>* payloadTypes) const {
|
||||
if (!mNegotiatedDetails) {
|
||||
|
|
@ -32,6 +36,8 @@ void JsepTrack::GetPayloadTypes(
|
|||
for (const auto& codec : codecs) {
|
||||
uint16_t pt;
|
||||
if (!codec->GetPtAsInt(&pt)) {
|
||||
MOZ_MTLOG(ML_ERROR, "Codec " << codec->mName
|
||||
<< " does not have a valid payload type");
|
||||
MOZ_ASSERT(false);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -52,14 +58,47 @@ void JsepTrack::EnsureSsrcs(SsrcGenerator& ssrcGenerator, size_t aNumber) {
|
|||
uint32_t ssrc, rtxSsrc;
|
||||
if (!ssrcGenerator.GenerateSsrc(&ssrc) ||
|
||||
!ssrcGenerator.GenerateSsrc(&rtxSsrc)) {
|
||||
MOZ_MTLOG(ML_ERROR, "Unable to generate SSRC");
|
||||
return;
|
||||
}
|
||||
mSsrcs.push_back(ssrc);
|
||||
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(
|
||||
const std::vector<UniquePtr<JsepCodecDescription>>& prototype,
|
||||
bool aUsePreferredCodecsOrder) {
|
||||
|
|
@ -117,7 +156,10 @@ void JsepTrack::AddToAnswer(const SdpMediaSection& offer,
|
|||
}
|
||||
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -134,9 +176,17 @@ void JsepTrack::SetMaxEncodings(size_t aMax) {
|
|||
void JsepTrack::RecvTrackSetRemote(const Sdp& aSdp,
|
||||
const SdpMediaSection& aMsection) {
|
||||
mInHaveRemote = true;
|
||||
MOZ_ASSERT(mDirection == sdp::kRecv);
|
||||
MOZ_ASSERT(aMsection.GetMediaType() !=
|
||||
SdpMediaSection::MediaType::kApplication);
|
||||
if (mDirection != sdp::kRecv) {
|
||||
MOZ_MTLOG(ML_ERROR, "RecvTrackSetRemote called on non-receive track");
|
||||
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;
|
||||
SdpHelper helper(&error);
|
||||
|
||||
|
|
@ -187,7 +237,11 @@ void JsepTrack::RecvTrackSetRemote(const Sdp& aSdp,
|
|||
}
|
||||
|
||||
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
|
||||
// decide we're ready to receive packets should probably go in here.
|
||||
|
|
@ -259,9 +313,16 @@ void JsepTrack::SendTrackSetRemote(SsrcGenerator& aSsrcGenerator,
|
|||
void JsepTrack::AddToMsection(
|
||||
const std::vector<UniquePtr<JsepCodecDescription>>& codecs,
|
||||
SdpMediaSection* msection) const {
|
||||
MOZ_ASSERT(msection->GetMediaType() == mType);
|
||||
MOZ_ASSERT(!codecs.empty());
|
||||
|
||||
if (msection->GetMediaType() != mType) {
|
||||
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) {
|
||||
codec->AddToMediaSection(*msection);
|
||||
}
|
||||
|
|
@ -279,8 +340,16 @@ void JsepTrack::AddToMsection(
|
|||
}
|
||||
|
||||
void JsepTrack::UpdateSsrcs(SsrcGenerator& ssrcGenerator, size_t encodings) {
|
||||
MOZ_ASSERT(mDirection == sdp::kSend);
|
||||
MOZ_ASSERT(mType != SdpMediaSection::kApplication);
|
||||
if (mDirection != sdp::kSend) {
|
||||
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);
|
||||
|
||||
EnsureSsrcs(ssrcGenerator, numSsrcs);
|
||||
|
|
@ -288,8 +357,10 @@ void JsepTrack::UpdateSsrcs(SsrcGenerator& ssrcGenerator, size_t encodings) {
|
|||
if (mNegotiatedDetails && mNegotiatedDetails->GetEncodingCount() > numSsrcs) {
|
||||
mNegotiatedDetails->TruncateEncodings(numSsrcs);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mSsrcs.empty());
|
||||
if (mSsrcs.empty()) {
|
||||
MOZ_MTLOG(ML_ERROR, "UpdateSsrcs resulted in empty mSsrcs");
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void JsepTrack::PruneSsrcs(size_t aNumSsrcs) {
|
||||
|
|
@ -352,7 +423,12 @@ void JsepTrack::AddToMsection(const std::vector<std::string>& aRids,
|
|||
UpdateSsrcs(ssrcGenerator, aRids.size());
|
||||
|
||||
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;
|
||||
UniquePtr<SdpSsrcGroupAttributeList> group(new SdpSsrcGroupAttributeList);
|
||||
for (const auto& ssrc : mSsrcs) {
|
||||
|
|
|
|||
|
|
@ -166,20 +166,7 @@ class JsepTrack {
|
|||
|
||||
virtual const std::vector<uint32_t>& GetSsrcs() const { return mSsrcs; }
|
||||
|
||||
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 std::vector<uint32_t> GetRtxSsrcs() const;
|
||||
|
||||
virtual void EnsureSsrcs(SsrcGenerator& ssrcGenerator, size_t aNumber);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ LOCAL_INCLUDES += [
|
|||
"/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"
|
||||
|
|
|
|||
|
|
@ -257,6 +257,7 @@ void WebrtcGmpVideoEncoder::Close_g() {
|
|||
mGMP = nullptr;
|
||||
mHost = nullptr;
|
||||
mInitting = false;
|
||||
mInputImageMap.Clear();
|
||||
|
||||
if (mCachedPluginId) {
|
||||
mReleasePluginEvent.Notify(*mCachedPluginId);
|
||||
|
|
@ -340,6 +341,14 @@ void WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange(uint32_t aWidth,
|
|||
void WebrtcGmpVideoEncoder::Encode_g(
|
||||
const webrtc::VideoFrame& aInputImage,
|
||||
std::vector<webrtc::VideoFrameType> aFrameTypes) {
|
||||
auto reportDroppedOnExit = MakeScopeExit([&] {
|
||||
MutexAutoLock lock(mCallbackMutex);
|
||||
if (mCallback) {
|
||||
mCallback->OnDroppedFrame(
|
||||
webrtc::EncodedImageCallback::DropReason::kDroppedByEncoder);
|
||||
}
|
||||
});
|
||||
|
||||
if (!mGMP) {
|
||||
// destroyed via Terminate(), failed to init, or just not initted yet
|
||||
GMP_LOG_DEBUG("GMP Encode: not initted yet");
|
||||
|
|
@ -347,6 +356,13 @@ void WebrtcGmpVideoEncoder::Encode_g(
|
|||
}
|
||||
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 ||
|
||||
static_cast<uint32_t>(aInputImage.height()) != mCodecParams.mHeight) {
|
||||
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");
|
||||
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{};
|
||||
info.mCodecType = kGMPVideoCodecH264;
|
||||
|
|
@ -421,18 +439,23 @@ void WebrtcGmpVideoEncoder::Encode_g(
|
|||
MOZ_RELEASE_ASSERT(mInputImageMap.IsEmpty() ||
|
||||
mInputImageMap.LastElement().ntp_timestamp_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()));
|
||||
err = mGMP->Encode(std::move(frame), codecSpecificInfo, gmp_frame_types);
|
||||
if (err != GMPNoErr) {
|
||||
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(
|
||||
|
|
@ -517,6 +540,7 @@ void WebrtcGmpVideoEncoder::Terminated() {
|
|||
mGMP = nullptr;
|
||||
mHost = nullptr;
|
||||
mInitting = false;
|
||||
mInputImageMap.Clear();
|
||||
|
||||
if (gmp) {
|
||||
// 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
|
||||
}
|
||||
|
||||
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(
|
||||
GMPVideoEncodedFrame* aEncodedFrame,
|
||||
const nsTArray<uint8_t>& aCodecSpecificInfo) {
|
||||
MOZ_ASSERT(mGMPThread->IsOnCurrentThread());
|
||||
Maybe<InputImageData> data;
|
||||
auto gmp_timestamp_comparator = [](const InputImageData& aA,
|
||||
const InputImageData& aB) -> int32_t {
|
||||
const auto& a = aA.gmp_timestamp_us;
|
||||
const auto& b = aB.gmp_timestamp_us;
|
||||
return a < b ? -1 : a != b;
|
||||
};
|
||||
MOZ_ASSERT(!mInputImageMap.IsEmpty());
|
||||
MOZ_ASSERT(mInputImageMap.Length() <= kMaxImagesInFlight);
|
||||
size_t nextIdx = mInputImageMap.IndexOfFirstElementGt(
|
||||
InputImageData{.gmp_timestamp_us = aEncodedFrame->TimeStamp()},
|
||||
gmp_timestamp_comparator);
|
||||
GmpTimestampComparator);
|
||||
const size_t numToRemove = nextIdx;
|
||||
size_t numFramesDropped = numToRemove;
|
||||
MOZ_ASSERT(nextIdx != 0);
|
||||
|
|
@ -676,6 +703,34 @@ void WebrtcGmpVideoEncoder::Encoded(
|
|||
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.
|
||||
WebrtcGmpVideoDecoder::WebrtcGmpVideoDecoder(std::string aPCHandle,
|
||||
TrackingId aTrackingId)
|
||||
|
|
|
|||
|
|
@ -204,6 +204,8 @@ class WebrtcGmpVideoEncoder final : public GMPVideoEncoderCallbackProxy,
|
|||
void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
|
||||
const nsTArray<uint8_t>& aCodecSpecificInfo) override;
|
||||
|
||||
void Dropped(uint64_t aTimestamp) override;
|
||||
|
||||
void Error(GMPErr aError) override {}
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,17 @@
|
|||
|
||||
#include <cstring>
|
||||
|
||||
#include "ipc/EnumSerializer.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 {
|
||||
|
||||
|
|
@ -31,66 +41,52 @@ MediaPacket MediaPacket::Clone() const {
|
|||
}
|
||||
|
||||
void MediaPacket::Serialize(IPC::MessageWriter* aWriter) const {
|
||||
aWriter->WriteUInt32(len_);
|
||||
aWriter->WriteUInt32(capacity_);
|
||||
WriteParam(aWriter, len_);
|
||||
WriteParam(aWriter, capacity_);
|
||||
WriteParam(aWriter, encrypted_len_);
|
||||
WriteParam(aWriter, sdp_level_);
|
||||
WriteParam(aWriter, type_);
|
||||
|
||||
if (len_) {
|
||||
aWriter->WriteBytes(data_.get(), len_);
|
||||
}
|
||||
aWriter->WriteUInt32(encrypted_len_);
|
||||
if (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) {
|
||||
Reset();
|
||||
uint32_t len;
|
||||
if (!aReader->ReadUInt32(&len)) {
|
||||
if (!ReadParam(aReader, &len_) || !ReadParam(aReader, &capacity_) ||
|
||||
!ReadParam(aReader, &encrypted_len_) ||
|
||||
!ReadParam(aReader, &sdp_level_) || !ReadParam(aReader, &type_)) {
|
||||
return false;
|
||||
}
|
||||
uint32_t capacity;
|
||||
if (!aReader->ReadUInt32(&capacity)) {
|
||||
|
||||
if (capacity_ < len_) {
|
||||
return false;
|
||||
}
|
||||
if (len) {
|
||||
MOZ_RELEASE_ASSERT(capacity >= len);
|
||||
UniquePtr<uint8_t[]> data(new uint8_t[capacity]);
|
||||
if (!aReader->ReadBytesInto(data.get(), len)) {
|
||||
|
||||
// Kinda arbitrary, but we want some sort of ceiling here.
|
||||
if ((capacity_ > 1024 * 1024) || (encrypted_len_ > 1024 * 1024)) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,19 @@ nsresult NrIceStunAddr::Deserialize(const char* buffer, size_t buffer_size) {
|
|||
nr_local_addr* from_addr =
|
||||
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
|
||||
// be fixed by nr_local_addr_copy.
|
||||
if (nr_local_addr_copy(localAddr_, from_addr)) {
|
||||
|
|
|
|||
|
|
@ -334,6 +334,10 @@ RefPtr<GenericPromise> RemoteWorkerController::SetServiceWorkerSkipWaitingFlag()
|
|||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(mObserver);
|
||||
|
||||
if (!mIsServiceWorker) {
|
||||
return GenericPromise::CreateAndResolve(false, __func__);
|
||||
}
|
||||
|
||||
RefPtr<GenericPromise::Private> promise =
|
||||
new GenericPromise::Private(__func__);
|
||||
|
||||
|
|
|
|||
|
|
@ -1765,12 +1765,10 @@ void FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex,
|
|||
|
||||
void FilterNodeComponentTransferSoftware::GenerateLookupTable(
|
||||
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) {
|
||||
aTables[aComponent][i] = i;
|
||||
}
|
||||
} else {
|
||||
FillLookupTable(aComponent, aTables[aComponent]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1927,32 +1925,29 @@ void FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex,
|
|||
Invalidate();
|
||||
}
|
||||
|
||||
void FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
|
||||
bool FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
|
||||
uint8_t aTable[256]) {
|
||||
switch (aComponent) {
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
|
||||
FillLookupTableImpl(mTableR, aTable);
|
||||
return FillLookupTableImpl(mTableR, aTable);
|
||||
break;
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
|
||||
FillLookupTableImpl(mTableG, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mTableG, aTable);
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
|
||||
FillLookupTableImpl(mTableB, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mTableB, aTable);
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
|
||||
FillLookupTableImpl(mTableA, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mTableA, aTable);
|
||||
default:
|
||||
MOZ_ASSERT(false, "unknown component");
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FilterNodeTableTransferSoftware::FillLookupTableImpl(
|
||||
bool FilterNodeTableTransferSoftware::FillLookupTableImpl(
|
||||
std::vector<Float>& aTableValues, uint8_t aTable[256]) {
|
||||
uint32_t tvLength = aTableValues.size();
|
||||
if (tvLength < 2) {
|
||||
return;
|
||||
if (tvLength < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 256; i++) {
|
||||
|
|
@ -1965,6 +1960,7 @@ void FilterNodeTableTransferSoftware::FillLookupTableImpl(
|
|||
val = std::max(0, val);
|
||||
aTable[i] = val;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex,
|
||||
|
|
@ -1990,32 +1986,28 @@ void FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex,
|
|||
Invalidate();
|
||||
}
|
||||
|
||||
void FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
|
||||
bool FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
|
||||
uint8_t aTable[256]) {
|
||||
switch (aComponent) {
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
|
||||
FillLookupTableImpl(mTableR, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mTableR, aTable);
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
|
||||
FillLookupTableImpl(mTableG, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mTableG, aTable);
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
|
||||
FillLookupTableImpl(mTableB, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mTableB, aTable);
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
|
||||
FillLookupTableImpl(mTableA, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mTableA, aTable);
|
||||
default:
|
||||
MOZ_ASSERT(false, "unknown component");
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(
|
||||
bool FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(
|
||||
std::vector<Float>& aTableValues, uint8_t aTable[256]) {
|
||||
uint32_t tvLength = aTableValues.size();
|
||||
if (tvLength < 1) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 256; i++) {
|
||||
|
|
@ -2027,6 +2019,7 @@ void FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(
|
|||
val = std::max(0, val);
|
||||
aTable[i] = val;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
|
||||
|
|
@ -2072,28 +2065,24 @@ void FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex,
|
|||
Invalidate();
|
||||
}
|
||||
|
||||
void FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
|
||||
bool FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
|
||||
uint8_t aTable[256]) {
|
||||
switch (aComponent) {
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
|
||||
FillLookupTableImpl(mSlopeR, mInterceptR, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mSlopeR, mInterceptR, aTable);
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
|
||||
FillLookupTableImpl(mSlopeG, mInterceptG, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mSlopeG, mInterceptG, aTable);
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
|
||||
FillLookupTableImpl(mSlopeB, mInterceptB, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mSlopeB, mInterceptB, aTable);
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
|
||||
FillLookupTableImpl(mSlopeA, mInterceptA, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mSlopeA, mInterceptA, aTable);
|
||||
default:
|
||||
MOZ_ASSERT(false, "unknown component");
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FilterNodeLinearTransferSoftware::FillLookupTableImpl(
|
||||
bool FilterNodeLinearTransferSoftware::FillLookupTableImpl(
|
||||
Float aSlope, Float aIntercept, uint8_t aTable[256]) {
|
||||
for (size_t i = 0; i < 256; i++) {
|
||||
int32_t val = NS_lround(aSlope * i + 255 * aIntercept);
|
||||
|
|
@ -2101,6 +2090,7 @@ void FilterNodeLinearTransferSoftware::FillLookupTableImpl(
|
|||
val = std::max(0, val);
|
||||
aTable[i] = val;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
|
||||
|
|
@ -2162,28 +2152,24 @@ void FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex,
|
|||
Invalidate();
|
||||
}
|
||||
|
||||
void FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
|
||||
bool FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
|
||||
uint8_t aTable[256]) {
|
||||
switch (aComponent) {
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
|
||||
FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable);
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
|
||||
FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable);
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
|
||||
FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable);
|
||||
case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
|
||||
FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable);
|
||||
break;
|
||||
return FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable);
|
||||
default:
|
||||
MOZ_ASSERT(false, "unknown component");
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
|
||||
bool FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
|
||||
Float aExponent,
|
||||
Float aOffset,
|
||||
uint8_t aTable[256]) {
|
||||
|
|
@ -2194,12 +2180,14 @@ void FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
|
|||
val = std::max(0, val);
|
||||
aTable[i] = val;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
|
||||
: mDivisor(0),
|
||||
mBias(0),
|
||||
mEdgeMode(EDGE_MODE_DUPLICATE),
|
||||
mKernelUnitLength(1.0f, 1.0f),
|
||||
mPreserveAlpha(false) {}
|
||||
|
||||
int32_t FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) {
|
||||
|
|
@ -2245,7 +2233,21 @@ void FilterNodeConvolveMatrixSoftware::SetAttribute(
|
|||
uint32_t aIndex, const Size& aKernelUnitLength) {
|
||||
switch (aIndex) {
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
|
||||
|
|
@ -2446,20 +2448,26 @@ template <typename CoordType>
|
|||
already_AddRefed<DataSourceSurface> FilterNodeConvolveMatrixSoftware::DoRender(
|
||||
const IntRect& aRect, CoordType aKernelUnitLengthX,
|
||||
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 ||
|
||||
mKernelMatrix.size() !=
|
||||
uint32_t(mKernelSize.width * mKernelSize.height) ||
|
||||
!kernelArea.isValid() ||
|
||||
mKernelMatrix.size() != size_t(kernelArea.value()) ||
|
||||
!IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) ||
|
||||
mDivisor == 0) {
|
||||
return Factory::CreateDataSourceSurface(aRect.Size(),
|
||||
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
|
||||
// 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 =
|
||||
GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect,
|
||||
|
|
@ -2538,23 +2546,26 @@ IntRect FilterNodeConvolveMatrixSoftware::MapRectToSource(
|
|||
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(
|
||||
const IntRect& aDestRect) {
|
||||
if (aDestRect.IsEmpty()) {
|
||||
return IntRect();
|
||||
}
|
||||
|
||||
IntMargin margin;
|
||||
margin.left = static_cast<int32_t>(ceil(mTarget.x * mKernelUnitLength.width));
|
||||
margin.top = static_cast<int32_t>(ceil(mTarget.y * mKernelUnitLength.height));
|
||||
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;
|
||||
RectDouble srcRect(aDestRect);
|
||||
srcRect.Inflate(GetInflateSourceMargin());
|
||||
return RectIsInt32Safe(srcRect) ? TruncatedToInt(srcRect) : aDestRect;
|
||||
}
|
||||
|
||||
IntRect FilterNodeConvolveMatrixSoftware::InflatedDestRect(
|
||||
|
|
@ -2563,19 +2574,12 @@ IntRect FilterNodeConvolveMatrixSoftware::InflatedDestRect(
|
|||
return IntRect();
|
||||
}
|
||||
|
||||
IntMargin margin;
|
||||
margin.left = static_cast<int32_t>(
|
||||
ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width));
|
||||
margin.top = static_cast<int32_t>(
|
||||
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;
|
||||
RectDouble destRect(aSourceRect);
|
||||
MarginDouble margin = GetInflateSourceMargin();
|
||||
std::swap(margin.left, margin.right);
|
||||
std::swap(margin.top, margin.bottom);
|
||||
destRect.Inflate(margin);
|
||||
return destRect;
|
||||
return RectIsInt32Safe(destRect) ? TruncatedToInt(destRect) : aSourceRect;
|
||||
}
|
||||
|
||||
IntRect FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(
|
||||
|
|
@ -2609,6 +2613,11 @@ void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
|
|||
|
||||
void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
|
||||
uint32_t aValue) {
|
||||
// Refuse channel values that exceed channel maximum.
|
||||
if (aValue > ColorChannel::COLOR_CHANNEL_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aIndex) {
|
||||
case ATT_DISPLACEMENT_MAP_X_CHANNEL:
|
||||
mChannelX = static_cast<ColorChannel>(aValue);
|
||||
|
|
@ -2655,7 +2664,7 @@ already_AddRefed<DataSourceSurface> FilterNodeDisplacementMapSoftware::Render(
|
|||
uint8_t* targetData = targetMap.GetData();
|
||||
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_B, B8G8R8A8_COMPONENT_BYTEOFFSET_A};
|
||||
uint16_t xChannel = channelMap[mChannelX];
|
||||
|
|
@ -3346,7 +3355,8 @@ static inline Point3D Normalized(const Point3D& vec) {
|
|||
template <typename LightType, typename LightingType>
|
||||
FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(
|
||||
const char* aTypeName)
|
||||
: mSurfaceScale(0)
|
||||
: mSurfaceScale(0),
|
||||
mKernelUnitLength(1.0f, 1.0f)
|
||||
#if defined(MOZILLA_INTERNAL_API) && defined(NS_BUILD_REFCNT_LOGGING)
|
||||
,
|
||||
mTypeName(aTypeName)
|
||||
|
|
@ -3399,6 +3409,23 @@ void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
|
|||
switch (aIndex) {
|
||||
case ATT_LIGHTING_KERNEL_UNIT_LENGTH:
|
||||
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;
|
||||
default:
|
||||
MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size");
|
||||
|
|
@ -3547,22 +3574,33 @@ FilterNodeLightingSoftware<LightType, LightingType>::Render(
|
|||
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>
|
||||
void FilterNodeLightingSoftware<
|
||||
LightType, LightingType>::RequestFromInputsForRect(const IntRect& aRect) {
|
||||
IntRect srcRect = aRect;
|
||||
srcRect.Inflate(ceil(mKernelUnitLength.width),
|
||||
ceil(mKernelUnitLength.height));
|
||||
RequestInputRect(IN_LIGHTING_IN, srcRect);
|
||||
RequestInputRect(IN_LIGHTING_IN, InflatedSourceRect(aRect));
|
||||
}
|
||||
|
||||
template <typename LightType, typename LightingType>
|
||||
IntRect FilterNodeLightingSoftware<LightType, LightingType>::MapRectToSource(
|
||||
const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
|
||||
IntRect srcRect = aRect;
|
||||
srcRect.Inflate(ceil(mKernelUnitLength.width),
|
||||
ceil(mKernelUnitLength.height));
|
||||
return MapInputRectToSource(IN_LIGHTING_IN, srcRect, aMax, aSourceNode);
|
||||
return MapInputRectToSource(IN_LIGHTING_IN, InflatedSourceRect(aRect), aMax,
|
||||
aSourceNode);
|
||||
}
|
||||
|
||||
template <typename LightType, typename LightingType>
|
||||
|
|
@ -3576,14 +3614,17 @@ FilterNodeLightingSoftware<LightType, LightingType>::DoRender(
|
|||
MOZ_ASSERT(aKernelUnitLengthY > 0,
|
||||
"aKernelUnitLengthY can be a negative or zero value");
|
||||
|
||||
IntRect srcRect = aRect;
|
||||
IntSize size = aRect.Size();
|
||||
srcRect.Inflate(ceil(float(aKernelUnitLengthX)),
|
||||
ceil(float(aKernelUnitLengthY)));
|
||||
|
||||
RectDouble srcRectD(aRect);
|
||||
srcRectD.Inflate(GetInflateSourceMargin());
|
||||
// Inflate the source rect by another pixel because the bilinear filtering in
|
||||
// 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(
|
||||
IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8, EDGE_MODE_NONE);
|
||||
|
|
|
|||
|
|
@ -360,7 +360,7 @@ class FilterNodeComponentTransferSoftware : public FilterNodeSoftware {
|
|||
void RequestFromInputsForRect(const IntRect& aRect) override;
|
||||
virtual void GenerateLookupTable(ptrdiff_t aComponent,
|
||||
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 mDisableG;
|
||||
|
|
@ -379,10 +379,10 @@ class FilterNodeTableTransferSoftware
|
|||
uint32_t aSize) override;
|
||||
|
||||
protected:
|
||||
void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
|
||||
bool FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
|
||||
|
||||
private:
|
||||
void FillLookupTableImpl(std::vector<Float>& aTableValues,
|
||||
bool FillLookupTableImpl(std::vector<Float>& aTableValues,
|
||||
uint8_t aTable[256]);
|
||||
|
||||
std::vector<Float> mTableR;
|
||||
|
|
@ -402,10 +402,10 @@ class FilterNodeDiscreteTransferSoftware
|
|||
uint32_t aSize) override;
|
||||
|
||||
protected:
|
||||
void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
|
||||
bool FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
|
||||
|
||||
private:
|
||||
void FillLookupTableImpl(std::vector<Float>& aTableValues,
|
||||
bool FillLookupTableImpl(std::vector<Float>& aTableValues,
|
||||
uint8_t aTable[256]);
|
||||
|
||||
std::vector<Float> mTableR;
|
||||
|
|
@ -425,10 +425,10 @@ class FilterNodeLinearTransferSoftware
|
|||
void SetAttribute(uint32_t aIndex, Float aValue) override;
|
||||
|
||||
protected:
|
||||
void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
|
||||
bool FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
|
||||
|
||||
private:
|
||||
void FillLookupTableImpl(Float aSlope, Float aIntercept, uint8_t aTable[256]);
|
||||
bool FillLookupTableImpl(Float aSlope, Float aIntercept, uint8_t aTable[256]);
|
||||
|
||||
Float mSlopeR;
|
||||
Float mSlopeG;
|
||||
|
|
@ -451,10 +451,10 @@ class FilterNodeGammaTransferSoftware
|
|||
void SetAttribute(uint32_t aIndex, Float aValue) override;
|
||||
|
||||
protected:
|
||||
void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
|
||||
bool FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
|
||||
|
||||
private:
|
||||
void FillLookupTableImpl(Float aAmplitude, Float aExponent, Float aOffset,
|
||||
bool FillLookupTableImpl(Float aAmplitude, Float aExponent, Float aOffset,
|
||||
uint8_t aTable[256]);
|
||||
|
||||
Float mAmplitudeR;
|
||||
|
|
@ -502,6 +502,7 @@ class FilterNodeConvolveMatrixSoftware : public FilterNodeSoftware {
|
|||
CoordType aKernelUnitLengthX,
|
||||
CoordType aKernelUnitLengthY);
|
||||
|
||||
MarginDouble GetInflateSourceMargin() const;
|
||||
IntRect InflatedSourceRect(const IntRect& aDestRect);
|
||||
IntRect InflatedDestRect(const IntRect& aSourceRect);
|
||||
|
||||
|
|
@ -764,6 +765,9 @@ class FilterNodeLightingSoftware : public FilterNodeSoftware {
|
|||
CoordType aKernelUnitLengthX,
|
||||
CoordType aKernelUnitLengthY);
|
||||
|
||||
MarginDouble GetInflateSourceMargin() const;
|
||||
IntRect InflatedSourceRect(const IntRect& aDestRect);
|
||||
|
||||
LightType mLight;
|
||||
LightingType mLighting;
|
||||
Float mSurfaceScale;
|
||||
|
|
|
|||
|
|
@ -193,7 +193,8 @@ enum ColorChannel {
|
|||
COLOR_CHANNEL_R = 0,
|
||||
COLOR_CHANNEL_G,
|
||||
COLOR_CHANNEL_B,
|
||||
COLOR_CHANNEL_A
|
||||
COLOR_CHANNEL_A,
|
||||
COLOR_CHANNEL_MAX = COLOR_CHANNEL_A
|
||||
};
|
||||
|
||||
enum DisplacementMapInputs {
|
||||
|
|
|
|||
|
|
@ -169,6 +169,11 @@ struct MOZ_EMPTY_BASES IntRectTyped
|
|||
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.
|
||||
void Round() {}
|
||||
void RoundIn() {}
|
||||
|
|
@ -338,10 +343,10 @@ IntRectTyped<Units> RoundedToInt(const RectTyped<Units>& aRect) {
|
|||
int32_t(copy.Width()), int32_t(copy.Height()));
|
||||
}
|
||||
|
||||
template <class Units>
|
||||
bool RectIsInt32Safe(const RectTyped<Units>& aRect) {
|
||||
float min = (float)std::numeric_limits<std::int32_t>::min();
|
||||
float max = (float)std::numeric_limits<std::int32_t>::max();
|
||||
template <class Units, class F>
|
||||
bool RectIsInt32Safe(const RectTyped<Units, F>& aRect) {
|
||||
F min = (F)std::numeric_limits<int32_t>::min();
|
||||
F max = (F)std::numeric_limits<int32_t>::max();
|
||||
return aRect.x > min && aRect.y > min && aRect.width < 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);
|
||||
}
|
||||
|
||||
template <class Units>
|
||||
IntRectTyped<Units> TruncatedToInt(const RectTyped<Units>& aRect) {
|
||||
template <class Units, class F>
|
||||
IntRectTyped<Units> TruncatedToInt(const RectTyped<Units, F>& aRect) {
|
||||
return IntRectTyped<Units>::Truncate(aRect);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -784,7 +784,7 @@ cairo_font_face_t* ScaledFontMac::CreateCairoFontFace(
|
|||
|
||||
already_AddRefed<UnscaledFont> UnscaledFontMac::CreateFromFontDescriptor(
|
||||
const uint8_t* aData, uint32_t aDataLength, uint32_t aIndex) {
|
||||
if (aDataLength == 0) {
|
||||
if (aDataLength == 0 || aIndex > aDataLength) {
|
||||
gfxWarning() << "Mac font descriptor is truncated.";
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class GLContextEGL final : public GLContext {
|
|||
}
|
||||
|
||||
static GLContextEGL* Cast(GLContext* gl) {
|
||||
MOZ_ASSERT(gl->GetContextType() == GLContextType::EGL);
|
||||
MOZ_RELEASE_ASSERT(gl->GetContextType() == GLContextType::EGL);
|
||||
return static_cast<GLContextEGL*>(gl);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1042,6 +1042,21 @@ hb_unsigned_mul_overflows (unsigned int count, unsigned int size, unsigned *resu
|
|||
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.
|
||||
|
|
|
|||
|
|
@ -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, "repeating tiles: count=%d width=%" PRId32, n_repeating, w_repeating);
|
||||
|
||||
/* Number of additional times to repeat each repeating tile. */
|
||||
int n_copies = 0;
|
||||
static constexpr unsigned STCH_MAX_GLYPHS = 256;
|
||||
|
||||
hb_position_t w_remaining = w_total - w_fixed;
|
||||
if (sign * w_remaining > sign * w_repeating && sign * w_repeating > 0)
|
||||
n_copies = (sign * w_remaining) / (sign * w_repeating) - 1;
|
||||
/* Number of additional times to repeat each repeating tile. */
|
||||
unsigned int n_copies = 0;
|
||||
|
||||
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. */
|
||||
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)
|
||||
{
|
||||
++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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
extra_glyphs_needed += n_copies * n_repeating;
|
||||
DEBUG_MSG (ARABIC, nullptr, "will add extra %d copies of repeating tiles", n_copies);
|
||||
unsigned int added_glyphs = 0;
|
||||
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
|
||||
{
|
||||
|
|
@ -629,7 +650,9 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED,
|
|||
|
||||
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;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -239,8 +239,12 @@ static AnimationHelper::SampleResult SampleAnimationForProperty(
|
|||
#endif
|
||||
}
|
||||
|
||||
uint32_t segmentIndex = 0;
|
||||
size_t segmentSize = animation.mSegments.Length();
|
||||
if (segmentSize == 0) {
|
||||
return AnimationHelper::SampleResult();
|
||||
}
|
||||
|
||||
uint32_t segmentIndex = 0;
|
||||
PropertyAnimation::SegmentData* segment = animation.mSegments.Elements();
|
||||
while (segment->mEndPortion < computedTiming.mProgress.Value() &&
|
||||
segmentIndex < segmentSize - 1) {
|
||||
|
|
|
|||
|
|
@ -156,7 +156,8 @@ BufferTextureData* BufferTextureData::CreateForYCbCr(
|
|||
gfx::ColorRange aColorRange, gfx::ChromaSubsampling aSubsampling,
|
||||
TextureFlags aTextureFlags) {
|
||||
uint32_t bufSize = ImageDataSerializer::ComputeYCbCrBufferSize(
|
||||
aYSize, aYStride, aCbCrSize, aCbCrStride);
|
||||
aDisplay, aYSize, aYStride, aCbCrSize, aCbCrStride, aColorDepth,
|
||||
aSubsampling);
|
||||
if (bufSize == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -274,11 +275,15 @@ bool BufferTextureData::BorrowMappedData(MappedTextureData& aData) {
|
|||
|
||||
gfx::IntSize size = GetSize();
|
||||
|
||||
auto stride = ImageDataSerializer::ComputeRGBStride(GetFormat(), size.width);
|
||||
if (stride == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aData.data = GetBuffer();
|
||||
aData.size = size;
|
||||
aData.format = GetFormat();
|
||||
aData.stride =
|
||||
ImageDataSerializer::ComputeRGBStride(aData.format, size.width);
|
||||
aData.stride = stride;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -645,7 +645,9 @@ Maybe<PlanarYCbCrData> PlanarYCbCrData::From(
|
|||
yuvDesc.ySize().width < 0 || yuvDesc.ySize().height < 0 ||
|
||||
yuvDesc.cbCrSize().width < 0 || yuvDesc.cbCrSize().height < 0 ||
|
||||
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 << ","
|
||||
<< yuvData.mCbSkip << "," << yuvData.mCrSkip << ", "
|
||||
<< yuvDesc.ySize().width << "," << yuvDesc.ySize().height
|
||||
|
|
@ -653,7 +655,8 @@ Maybe<PlanarYCbCrData> PlanarYCbCrData::From(
|
|||
<< yuvDesc.cbCrSize().height << ", " << yuvData.mYStride
|
||||
<< "," << yuvData.mCbCrStride << ", "
|
||||
<< yuvData.mYChannel << "," << yuvData.mCbChannel << ","
|
||||
<< yuvData.mCrChannel;
|
||||
<< yuvData.mCrChannel << "," << yuvData.YDataSize().width
|
||||
<< "," << yuvData.YDataSize().height;
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -750,8 +753,12 @@ nsresult PlanarYCbCrImage::BuildSurfaceDescriptorBuffer(
|
|||
yOffset, cbOffset, crOffset);
|
||||
|
||||
uint32_t bufferSize = ImageDataSerializer::ComputeYCbCrBufferSize(
|
||||
ySize, pdata->mYStride, cbcrSize, pdata->mCbCrStride, yOffset, cbOffset,
|
||||
crOffset);
|
||||
pdata->mPictureRect, ySize, pdata->mYStride, cbcrSize, pdata->mCbCrStride,
|
||||
yOffset, cbOffset, crOffset, pdata->mColorDepth,
|
||||
pdata->mChromaSubsampling);
|
||||
if (bufferSize == 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
aSdBuffer.data() = aAllocate(bufferSize);
|
||||
|
||||
|
|
|
|||
|
|
@ -58,36 +58,56 @@ uint32_t ComputeRGBBufferSize(IntSize aSize, SurfaceFormat aFormat) {
|
|||
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
|
||||
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,
|
||||
int32_t aCbCrStride) {
|
||||
int32_t aCbCrStride, gfx::ColorDepth aDepth,
|
||||
const ChromaSubsampling aSubsampling) {
|
||||
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 ||
|
||||
!gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) ||
|
||||
!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;
|
||||
}
|
||||
|
||||
// Overflow checks are performed in AllowedSurfaceSize
|
||||
return GetAlignedStride<4>(aYSize.height, aYStride) +
|
||||
2 * GetAlignedStride<4>(aCbCrSize.height, aCbCrStride);
|
||||
// Overflow checks are performed only individually in AllowedSurfaceSize
|
||||
auto bufLen =
|
||||
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,
|
||||
int32_t aCbCrStride, uint32_t aYOffset,
|
||||
uint32_t aCbOffset, uint32_t aCrOffset) {
|
||||
MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0);
|
||||
|
||||
if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 ||
|
||||
aCbCrSize.width < 0 ||
|
||||
!gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) ||
|
||||
!gfx::Factory::AllowedSurfaceSize(
|
||||
IntSize(aCbCrStride, aCbCrSize.height))) {
|
||||
uint32_t aCbOffset, uint32_t aCrOffset,
|
||||
gfx::ColorDepth aDepth,
|
||||
const ChromaSubsampling aSubsampling) {
|
||||
uint32_t minBufLen = ComputeYCbCrBufferSize(
|
||||
aDisplay, aYSize, aYStride, aCbCrSize, aCbCrStride, aDepth, aSubsampling);
|
||||
if (minBufLen == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -105,7 +125,8 @@ uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride,
|
|||
crEnd += cbCrLength;
|
||||
|
||||
if (!yEnd.isValid() || !cbEnd.isValid() || !crEnd.isValid() ||
|
||||
yEnd.value() > aCbOffset || cbEnd.value() > aCrOffset) {
|
||||
yEnd.value() > aCbOffset || cbEnd.value() > aCrOffset ||
|
||||
crEnd.value() < minBufLen) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
/// to allocate in a shmem in order to place a shared YCbCr image blob of
|
||||
/// 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,
|
||||
int32_t aCbCrStride);
|
||||
uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride,
|
||||
int32_t aCbCrStride, gfx::ColorDepth aDepth,
|
||||
const gfx::ChromaSubsampling aSubsampling);
|
||||
uint32_t ComputeYCbCrBufferSize(const gfx::IntRect& aDisplay,
|
||||
const gfx::IntSize& aYSize, int32_t aYStride,
|
||||
const gfx::IntSize& aCbCrSize,
|
||||
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);
|
||||
|
||||
void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight, int32_t cbCrStride,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "mozilla/layers/SharedSurfacesChild.h"
|
||||
#include "mozilla/layers/SharedSurfacesParent.h"
|
||||
#include "nsDebug.h" // for NS_ABORT_OOM
|
||||
#include "mozilla/image/SurfaceCache.h"
|
||||
|
||||
#include "base/process_util.h"
|
||||
|
||||
|
|
@ -46,7 +47,7 @@ void SourceSurfaceSharedDataWrapper::Init(
|
|||
MOZ_CRASH("Invalid shared memory handle!");
|
||||
}
|
||||
|
||||
bool mapped = EnsureMapped(len);
|
||||
bool mapped = EnsureMapped();
|
||||
if ((sizeof(uintptr_t) <= 4 ||
|
||||
StaticPrefs::image_mem_shared_unmap_force_enabled_AtStartup()) &&
|
||||
len / 1024 >
|
||||
|
|
@ -77,10 +78,15 @@ void SourceSurfaceSharedDataWrapper::Init(SourceSurfaceSharedData* aSurface) {
|
|||
mBuf = aSurface->mBuf;
|
||||
}
|
||||
|
||||
bool SourceSurfaceSharedDataWrapper::EnsureMapped(size_t aLength) {
|
||||
bool SourceSurfaceSharedDataWrapper::EnsureMapped() {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -118,9 +124,8 @@ bool SourceSurfaceSharedDataWrapper::Map(MapType aMapType,
|
|||
SharedSurfacesParent::RemoveTracking(this);
|
||||
}
|
||||
if (!dataPtr) {
|
||||
size_t len = GetAlignedDataLength();
|
||||
if (!EnsureMapped(len)) {
|
||||
NS_ABORT_OOM(len);
|
||||
if (!EnsureMapped()) {
|
||||
NS_ABORT_OOM(GetAlignedDataLength());
|
||||
}
|
||||
dataPtr = GetData();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ class SourceSurfaceSharedDataWrapper final : public DataSourceSurface {
|
|||
return mozilla::ipc::shared_memory::PageAlignedSize(GetDataLength());
|
||||
}
|
||||
|
||||
bool EnsureMapped(size_t aLength);
|
||||
bool EnsureMapped();
|
||||
|
||||
// Protects mapping and unmapping of mBuf.
|
||||
Maybe<Mutex> mHandleLock;
|
||||
|
|
|
|||
|
|
@ -279,9 +279,10 @@ already_AddRefed<TextureHost> CreateBackendIndependentTextureHost(
|
|||
case BufferDescriptor::TYCbCrDescriptor: {
|
||||
const YCbCrDescriptor& ycbcr = desc.get_YCbCrDescriptor();
|
||||
reqSize = ImageDataSerializer::ComputeYCbCrBufferSize(
|
||||
ycbcr.ySize(), ycbcr.yStride(), ycbcr.cbCrSize(),
|
||||
ycbcr.cbCrStride(), ycbcr.yOffset(), ycbcr.cbOffset(),
|
||||
ycbcr.crOffset());
|
||||
ycbcr.display(), ycbcr.ySize(), ycbcr.yStride(),
|
||||
ycbcr.cbCrSize(), ycbcr.cbCrStride(), ycbcr.yOffset(),
|
||||
ycbcr.cbOffset(), ycbcr.crOffset(), ycbcr.colorDepth(),
|
||||
ycbcr.chromaSubsampling());
|
||||
break;
|
||||
}
|
||||
case BufferDescriptor::TRGBDescriptor: {
|
||||
|
|
|
|||
|
|
@ -181,7 +181,20 @@ class MOZ_STACK_CLASS AutoImageBridgeParentAsyncMessageSender final {
|
|||
~AutoImageBridgeParentAsyncMessageSender() {
|
||||
mImageBridge->SendPendingAsyncMessages();
|
||||
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) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,7 +170,8 @@ nsresult SharedPlanarYCbCrImage::CreateEmptyBuffer(
|
|||
// will try to manage this memory without knowing it belongs to a
|
||||
// shmem.
|
||||
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();
|
||||
mOrigin = mData.mPictureRect.TopLeft();
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,11 @@ already_AddRefed<TextureHost> CreateTextureHostOGL(
|
|||
#endif
|
||||
|
||||
case SurfaceDescriptor::TEGLImageDescriptor: {
|
||||
if (aDeallocator && !aDeallocator->IsSameProcess()) {
|
||||
gfxCriticalError()
|
||||
<< "EGLImageDescriptor must only be used in same process";
|
||||
return nullptr;
|
||||
}
|
||||
const EGLImageDescriptor& desc = aDesc.get_EGLImageDescriptor();
|
||||
result = new EGLImageTextureHost(aFlags, (EGLImage)desc.image(),
|
||||
(EGLSync)desc.fence(), desc.size(),
|
||||
|
|
@ -109,6 +114,11 @@ already_AddRefed<TextureHost> CreateTextureHostOGL(
|
|||
#endif
|
||||
|
||||
case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture: {
|
||||
if (aDeallocator && !aDeallocator->IsSameProcess()) {
|
||||
gfxCriticalError() << "SurfaceDescriptorSharedGLTexture must only be "
|
||||
"used in same process";
|
||||
return nullptr;
|
||||
}
|
||||
const auto& desc = aDesc.get_SurfaceDescriptorSharedGLTexture();
|
||||
result =
|
||||
new GLTextureHost(aFlags, desc.texture(), desc.target(),
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include "GLContextProvider.h"
|
||||
#include "GLLibraryLoader.h"
|
||||
#include "nsExceptionHandler.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/Range.h"
|
||||
#include "mozilla/EnumeratedRange.h"
|
||||
#include "mozilla/StaticPrefs_gfx.h"
|
||||
|
|
@ -314,8 +315,19 @@ class MOZ_STACK_CLASS AutoWebRenderBridgeParentAsyncMessageSender final {
|
|||
mWebRenderBridgeParent->SendPendingAsyncMessages();
|
||||
if (mActorsToDestroy) {
|
||||
// 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) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1864,12 +1876,16 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvGetSnapshot(
|
|||
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();
|
||||
FlushFrameGeneration(wr::RenderReasons::SNAPSHOT);
|
||||
mApi->Readback(start, size, bufferTexture->GetFormat(),
|
||||
Range<uint8_t>(buffer, buffer_size), aNeedsYFlip);
|
||||
Range<uint8_t>(buffer, buffer_size.value()),
|
||||
aNeedsYFlip);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,8 +65,10 @@ EXPORTS.mozilla.image += [
|
|||
"ICOFileHeaders.h",
|
||||
"ImageMemoryReporter.h",
|
||||
"ImageUtils.h",
|
||||
"PlaybackType.h",
|
||||
"Resolution.h",
|
||||
"SourceBuffer.h",
|
||||
"SurfaceCache.h",
|
||||
"SurfaceFlags.h",
|
||||
"WebRenderImageProvider.h",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -426,7 +426,7 @@ constexpr uint64_t CanonicalizedNaNSignificand = 0x8000000000000;
|
|||
#endif
|
||||
|
||||
#if defined(JS_RUNTIME_CANONICAL_NAN)
|
||||
extern uint64_t CanonicalizedNaNBits;
|
||||
extern JS_PUBLIC_API uint64_t CanonicalizedNaNBits;
|
||||
#else
|
||||
constexpr uint64_t CanonicalizedNaNBits =
|
||||
mozilla::SpecificNaNBits<double, detail::CanonicalizedNaNSignBit,
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ void DebugScriptObject::finalize(JS::GCContext* gcx, JSObject* obj) {
|
|||
|
||||
/* static */
|
||||
DebugScript* DebugScript::get(JSScript* script) {
|
||||
MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(script));
|
||||
MOZ_ASSERT(script->hasDebugScript());
|
||||
DebugScriptMap* map = script->zone()->debugScriptMap;
|
||||
MOZ_ASSERT(map);
|
||||
|
|
@ -205,7 +206,12 @@ JSBreakpointSite* DebugScript::getOrCreateBreakpointSite(JSContext* cx,
|
|||
/* static */
|
||||
void DebugScript::destroyBreakpointSite(JS::GCContext* gcx, JSScript* script,
|
||||
jsbytecode* pc) {
|
||||
if (IsAboutToBeFinalizedUnbarriered(script)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DebugScript* debug = get(script);
|
||||
|
||||
JSBreakpointSite*& site = debug->breakpoints[script->pcToOffset(pc)];
|
||||
MOZ_ASSERT(site);
|
||||
MOZ_ASSERT(site->isEmpty());
|
||||
|
|
@ -283,6 +289,10 @@ bool DebugScript::incrementStepperCount(JSContext* cx, HandleScript script) {
|
|||
|
||||
/* static */
|
||||
void DebugScript::decrementStepperCount(JS::GCContext* gcx, JSScript* script) {
|
||||
if (IsAboutToBeFinalizedUnbarriered(script)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DebugScript* debug = get(script);
|
||||
MOZ_ASSERT(debug);
|
||||
MOZ_ASSERT(debug->stepperCount > 0);
|
||||
|
|
@ -328,6 +338,10 @@ bool DebugScript::incrementGeneratorObserverCount(JSContext* cx,
|
|||
/* static */
|
||||
void DebugScript::decrementGeneratorObserverCount(JS::GCContext* gcx,
|
||||
JSScript* script) {
|
||||
if (IsAboutToBeFinalizedUnbarriered(script)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DebugScript* debug = get(script);
|
||||
MOZ_ASSERT(debug);
|
||||
MOZ_ASSERT(debug->generatorObserverCount > 0);
|
||||
|
|
@ -393,6 +407,10 @@ void DebugAPI::checkDebugScriptAfterMovingGC(DebugScript* ds) {
|
|||
|
||||
/* static */
|
||||
bool DebugAPI::stepModeEnabledSlow(JSScript* script) {
|
||||
if (IsAboutToBeFinalizedUnbarriered(script)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return DebugScript::get(script)->stepperCount > 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2025,26 +2025,27 @@ Completion Completion::fromJSFramePop(JSContext* cx, AbstractFramePtr frame,
|
|||
//
|
||||
// GetGeneratorObjectForFrame can return nullptr even when a generator
|
||||
// object does exist, if the frame is paused between the Generator and
|
||||
// SetAliasedVar opcodes. But by checking the opcode first we eliminate that
|
||||
// possibility, so it's fine to call genObj->isClosed().
|
||||
// SetAliasedVar opcodes.
|
||||
Rooted<AbstractGeneratorObject*> generatorObj(
|
||||
cx, GetGeneratorObjectForFrame(cx, frame));
|
||||
switch (JSOp(*pc)) {
|
||||
case JSOp::InitialYield:
|
||||
MOZ_ASSERT(!generatorObj->isClosed());
|
||||
return Completion(InitialYield(generatorObj));
|
||||
|
||||
case JSOp::Yield:
|
||||
MOZ_ASSERT(!generatorObj->isClosed());
|
||||
return Completion(Yield(generatorObj, frame.returnValue()));
|
||||
if (generatorObj && !generatorObj->isClosed()) {
|
||||
switch (JSOp(*pc)) {
|
||||
case JSOp::InitialYield:
|
||||
return Completion(InitialYield(generatorObj));
|
||||
|
||||
case JSOp::Await:
|
||||
MOZ_ASSERT(!generatorObj->isClosed());
|
||||
return Completion(Await(generatorObj, frame.returnValue()));
|
||||
case JSOp::Yield:
|
||||
return Completion(Yield(generatorObj, frame.returnValue()));
|
||||
|
||||
default:
|
||||
return Completion(Return(frame.returnValue()));
|
||||
case JSOp::Await:
|
||||
return Completion(Await(generatorObj, frame.returnValue()));
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Completion(Return(frame.returnValue()));
|
||||
}
|
||||
|
||||
void Completion::trace(JSTracer* trc) {
|
||||
|
|
|
|||
|
|
@ -894,7 +894,10 @@ bool MarkPagesUnusedSoft(void* region, size_t length) {
|
|||
int status;
|
||||
do {
|
||||
# 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)
|
||||
status = posix_madvise(region, length, POSIX_MADV_DONTNEED);
|
||||
# else
|
||||
|
|
@ -925,11 +928,6 @@ void MarkPagesInUseSoft(void* region, size_t length) {
|
|||
MOZ_ASSERT(DecommitEnabled());
|
||||
CheckDecommit(region, length);
|
||||
|
||||
#if defined(XP_DARWIN)
|
||||
while (madvise(region, length, MADV_FREE_REUSE) == -1 && errno == EAGAIN) {
|
||||
}
|
||||
#endif
|
||||
|
||||
MOZ_MAKE_MEM_UNDEFINED(region, length);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1215,10 +1215,14 @@ void GCRuntime::checkHeapBeforeMinorGC(AutoHeapSession& session) {
|
|||
// to tenured strings but contain nursery data.
|
||||
|
||||
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();
|
||||
aiter.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();
|
||||
if (str->isTenured() && str->base()->isTenured()) {
|
||||
MOZ_RELEASE_ASSERT(!str->hasCharsInCollectedNurseryRegion());
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
@ -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()) {}
|
||||
}
|
||||
|
|
@ -568,8 +568,9 @@ static bool BlockIsSingleTest(MBasicBlock* phiBlock, MBasicBlock* testBlock,
|
|||
*ptest = nullptr;
|
||||
|
||||
if (phiBlock != testBlock) {
|
||||
MOZ_ASSERT(phiBlock->numSuccessors() == 1 &&
|
||||
phiBlock->getSuccessor(0) == testBlock);
|
||||
MOZ_RELEASE_ASSERT(phiBlock->lastIns()->isGoto());
|
||||
MOZ_RELEASE_ASSERT(phiBlock->lastIns()->toGoto()->target() == testBlock);
|
||||
MOZ_RELEASE_ASSERT(testBlock->numPredecessors() == 1);
|
||||
if (!phiBlock->begin()->isGoto()) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -686,7 +687,7 @@ static bool IsTestInputMaybeToBool(MTest* test, MDefinition* value) {
|
|||
blockResult->setImplicitlyUsedUnchecked();
|
||||
|
||||
MInstruction* ins = block->lastIns();
|
||||
MOZ_ASSERT(ins->isGoto());
|
||||
MOZ_RELEASE_ASSERT(ins->isGoto());
|
||||
ins->toGoto()->target()->removePredecessor(block);
|
||||
block->discardLastIns();
|
||||
|
||||
|
|
@ -707,15 +708,14 @@ static bool IsTestInputMaybeToBool(MTest* test, MDefinition* value) {
|
|||
MInstruction* ins = block->lastIns();
|
||||
if (ins->isTest()) {
|
||||
MTest* test = ins->toTest();
|
||||
MOZ_ASSERT(test->input() == value);
|
||||
MOZ_RELEASE_ASSERT(test->input() == value);
|
||||
|
||||
if (ifTrue != test->ifTrue()) {
|
||||
test->ifTrue()->removePredecessor(block);
|
||||
if (!ifTrue->addPredecessorSameInputsAs(block, existingPred)) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(test->ifTrue() == test->getSuccessor(0));
|
||||
test->replaceSuccessor(0, ifTrue);
|
||||
test->replaceSuccessor(MTest::TrueBranchIndex, ifTrue);
|
||||
}
|
||||
|
||||
if (ifFalse != test->ifFalse()) {
|
||||
|
|
@ -723,14 +723,13 @@ static bool IsTestInputMaybeToBool(MTest* test, MDefinition* value) {
|
|||
if (!ifFalse->addPredecessorSameInputsAs(block, existingPred)) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(test->ifFalse() == test->getSuccessor(1));
|
||||
test->replaceSuccessor(1, ifFalse);
|
||||
test->replaceSuccessor(MTest::FalseBranchIndex, ifFalse);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(ins->isGoto());
|
||||
MOZ_RELEASE_ASSERT(ins->isGoto());
|
||||
ins->toGoto()->target()->removePredecessor(block);
|
||||
block->discardLastIns();
|
||||
|
||||
|
|
@ -775,8 +774,8 @@ static bool IsDiamondPattern(MBasicBlock* initialBlock) {
|
|||
return false;
|
||||
}
|
||||
|
||||
MBasicBlock* phiBlock = trueBranch->getSuccessor(0);
|
||||
if (phiBlock != falseBranch->getSuccessor(0)) {
|
||||
MBasicBlock* phiBlock = trueBranch->lastIns()->toGoto()->target();
|
||||
if (phiBlock != falseBranch->lastIns()->toGoto()->target()) {
|
||||
return false;
|
||||
}
|
||||
if (phiBlock->numPredecessors() != 2) {
|
||||
|
|
@ -820,13 +819,13 @@ static bool IsDiamondPattern(MBasicBlock* initialBlock) {
|
|||
return true;
|
||||
}
|
||||
|
||||
MBasicBlock* phiBlock = trueBranch->getSuccessor(0);
|
||||
MBasicBlock* phiBlock = trueBranch->lastIns()->toGoto()->target();
|
||||
MBasicBlock* testBlock = phiBlock;
|
||||
if (testBlock->numSuccessors() == 1) {
|
||||
if (testBlock->lastIns()->isGoto()) {
|
||||
if (testBlock->isLoopBackedge()) {
|
||||
return true;
|
||||
}
|
||||
testBlock = testBlock->getSuccessor(0);
|
||||
testBlock = testBlock->lastIns()->toGoto()->target();
|
||||
if (testBlock->numPredecessors() != 1) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -838,7 +837,7 @@ static bool IsDiamondPattern(MBasicBlock* initialBlock) {
|
|||
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.
|
||||
if (!SplitCriticalEdgesForBlock(graph, testBlock)) {
|
||||
|
|
@ -929,8 +928,8 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
|
|||
MBasicBlock* trueBranch = initialTest->ifTrue();
|
||||
MBasicBlock* falseBranch = initialTest->ifFalse();
|
||||
|
||||
if (trueBranch->numSuccessors() == 1 &&
|
||||
trueBranch->getSuccessor(0) == falseBranch) {
|
||||
if (trueBranch->lastIns()->isGoto() &&
|
||||
trueBranch->lastIns()->toGoto()->target() == falseBranch) {
|
||||
if (trueBranch->numPredecessors() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -940,8 +939,8 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (falseBranch->numSuccessors() == 1 &&
|
||||
falseBranch->getSuccessor(0) == trueBranch) {
|
||||
if (falseBranch->lastIns()->isGoto() &&
|
||||
falseBranch->lastIns()->toGoto()->target() == trueBranch) {
|
||||
if (trueBranch->numPredecessors() != 2) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -999,19 +998,19 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
|
|||
}
|
||||
|
||||
MBasicBlock* phiBlock;
|
||||
if (trueBranch->numSuccessors() == 1 &&
|
||||
trueBranch->getSuccessor(0) == falseBranch) {
|
||||
if (trueBranch->lastIns()->isGoto() &&
|
||||
trueBranch->lastIns()->toGoto()->target() == falseBranch) {
|
||||
phiBlock = falseBranch;
|
||||
} else {
|
||||
MOZ_ASSERT(falseBranch->getSuccessor(0) == trueBranch);
|
||||
MOZ_ASSERT(falseBranch->lastIns()->toGoto()->target() == trueBranch);
|
||||
phiBlock = trueBranch;
|
||||
}
|
||||
|
||||
MBasicBlock* testBlock = phiBlock;
|
||||
if (testBlock->numSuccessors() == 1) {
|
||||
MOZ_ASSERT(!testBlock->isLoopBackedge());
|
||||
if (testBlock->lastIns()->isGoto()) {
|
||||
MOZ_RELEASE_ASSERT(!testBlock->isLoopBackedge());
|
||||
|
||||
testBlock = testBlock->getSuccessor(0);
|
||||
testBlock = testBlock->lastIns()->toGoto()->target();
|
||||
if (testBlock->numPredecessors() != 1) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1023,7 +1022,7 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
|
|||
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.
|
||||
auto* phiInputForInitialBlock =
|
||||
|
|
@ -1194,17 +1193,17 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
|
|||
}
|
||||
|
||||
MBasicBlock* testBlock = phiBlock;
|
||||
if (testBlock->numSuccessors() == 1) {
|
||||
if (testBlock->lastIns()->isGoto()) {
|
||||
if (testBlock->isLoopBackedge()) {
|
||||
return true;
|
||||
}
|
||||
testBlock = testBlock->getSuccessor(0);
|
||||
testBlock = testBlock->lastIns()->toGoto()->target();
|
||||
if (testBlock->numPredecessors() != 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!phiBlock->isLoopBackedge());
|
||||
MOZ_RELEASE_ASSERT(!phiBlock->isLoopBackedge());
|
||||
|
||||
MPhi* phi = nullptr;
|
||||
MTest* finalTest = nullptr;
|
||||
|
|
@ -1212,7 +1211,7 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
|
|||
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.
|
||||
auto* phiInputForInitialBlock =
|
||||
|
|
@ -1243,7 +1242,7 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
|
|||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!pred->isLoopBackedge());
|
||||
MOZ_RELEASE_ASSERT(!pred->isLoopBackedge());
|
||||
}
|
||||
|
||||
// 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.
|
||||
while (phiBlock->numPredecessors()) {
|
||||
mozilla::DebugOnly<size_t> oldNumPred = phiBlock->numPredecessors();
|
||||
size_t oldNumPred = phiBlock->numPredecessors();
|
||||
|
||||
auto* pred = phiBlock->getPredecessor(0);
|
||||
auto* test = pred->lastIns()->toTest();
|
||||
|
|
@ -1281,7 +1280,7 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
|
|||
return false;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(test->ifFalse() == phiBlock);
|
||||
MOZ_RELEASE_ASSERT(test->ifFalse() == phiBlock);
|
||||
if (!UpdateTestSuccessors(graph.alloc(), pred, test->input(),
|
||||
test->ifTrue(), finalTest->ifFalse(),
|
||||
testBlock)) {
|
||||
|
|
@ -1290,7 +1289,7 @@ static bool IsTrianglePattern(MBasicBlock* initialBlock) {
|
|||
}
|
||||
|
||||
// Ensure we've made progress.
|
||||
MOZ_ASSERT(phiBlock->numPredecessors() + 1 == oldNumPred);
|
||||
MOZ_RELEASE_ASSERT(phiBlock->numPredecessors() + 1 == oldNumPred);
|
||||
}
|
||||
|
||||
// Remove phiBlock, if different from testBlock.
|
||||
|
|
|
|||
|
|
@ -1346,7 +1346,6 @@ class MWasmLoadTableElement : public MBinaryInstruction,
|
|||
wasm::RefType refType)
|
||||
: MBinaryInstruction(classOpcode, elements, index) {
|
||||
setResultType(MIRType::WasmAnyRef);
|
||||
setMovable();
|
||||
initWasmRefType(wasm::MaybeRefType(refType));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2266,36 +2266,15 @@ ResizableArrayBufferObject::createBufferAndData(
|
|||
size_t sourceByteLength = source->byteLength();
|
||||
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);
|
||||
auto [buffer, toFill] = createBufferAndData<FillContents::Uninitialized>(
|
||||
auto [buffer, toFill] = createBufferAndData<FillContents::Zero>(
|
||||
cx, newByteLength, newMaxByteLength, metadata, nullptr);
|
||||
if (!buffer) {
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -281,6 +281,12 @@ void FontFace::MaybeResolve() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (NS_IsMainThread() && !nsContentUtils::IsSafeToRunScript()) {
|
||||
nsContentUtils::AddScriptRunner(NewRunnableMethod(
|
||||
"FontFace::MaybeResolve", this, &FontFace::MaybeResolve));
|
||||
return;
|
||||
}
|
||||
|
||||
mLoaded->MaybeResolve(this);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
@ -7,15 +7,12 @@ This is a public release of libpng, intended for use in production code.
|
|||
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.47.tar.gz (deflate-compressed)
|
||||
|
||||
Source files with CRLF line endings (for Windows):
|
||||
|
||||
* lpng1647.7z (LZMA-compressed, recommended)
|
||||
* lpng1647.zip (deflate-compressed)
|
||||
* libpng-1.6.55.tar.xz (LZMA-compressed, recommended)
|
||||
* libpng-1.6.55.tar.gz (deflate-compressed)
|
||||
* lpng1655.7z (LZMA-compressed)
|
||||
* lpng1655.zip (deflate-compressed)
|
||||
|
||||
Other information:
|
||||
|
||||
|
|
@ -25,22 +22,16 @@ Other information:
|
|||
* 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
|
||||
to the new precedence rules formulated in the latest draft of
|
||||
the PNG Specification.
|
||||
(Contributed by John Bowler)
|
||||
* Fixed a latent bug in `png_write_iCCP`.
|
||||
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)
|
||||
|
||||
* Fixed CVE-2026-25646 (high severity):
|
||||
Heap buffer overflow in `png_set_quantize`.
|
||||
(Reported and fixed by Joshua Inscoe.)
|
||||
* Resolved an oss-fuzz build issue involving nalloc.
|
||||
(Contributed by Philippe Antoine.)
|
||||
|
||||
Send comments/corrections/commendations to png-mng-implement at lists.sf.net.
|
||||
Subscription is required; visit
|
||||
https://lists.sourceforge.net/lists/listinfo/png-mng-implement
|
||||
<https://lists.sourceforge.net/lists/listinfo/png-mng-implement>
|
||||
to subscribe.
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue