Update On Tue Nov 7 19:41:39 CET 2023

This commit is contained in:
github-action[bot] 2023-11-07 19:41:40 +01:00
parent 3592b09145
commit 947dc8afa6
612 changed files with 27105 additions and 16510 deletions

14
Cargo.lock generated
View file

@ -235,22 +235,22 @@ dependencies = [
[[package]]
name = "audio_thread_priority"
version = "0.26.1"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b7cd1bfd03dab20ad72e0c5e58d65818d62c0d199d8dec8361053d0f073dbae"
checksum = "a7e6839a6e8e72338137b3126c7fe61af95b9acc3076d2b7109696f808d1234f"
dependencies = [
"cfg-if 0.1.999",
"cfg-if 1.0.0",
"dbus",
"libc",
"log",
"mach",
"winapi",
"windows-sys",
]
[[package]]
name = "audioipc2"
version = "0.5.0"
source = "git+https://github.com/mozilla/audioipc?rev=ec6af6ecf8ba50b51f13d18f417de8cfb8460543#ec6af6ecf8ba50b51f13d18f417de8cfb8460543"
source = "git+https://github.com/mozilla/audioipc?rev=6be424d75f1367e70f2f5ddcacd6d0237e81a6a9#6be424d75f1367e70f2f5ddcacd6d0237e81a6a9"
dependencies = [
"arrayvec",
"ashmem",
@ -278,7 +278,7 @@ dependencies = [
[[package]]
name = "audioipc2-client"
version = "0.5.0"
source = "git+https://github.com/mozilla/audioipc?rev=ec6af6ecf8ba50b51f13d18f417de8cfb8460543#ec6af6ecf8ba50b51f13d18f417de8cfb8460543"
source = "git+https://github.com/mozilla/audioipc?rev=6be424d75f1367e70f2f5ddcacd6d0237e81a6a9#6be424d75f1367e70f2f5ddcacd6d0237e81a6a9"
dependencies = [
"audio_thread_priority",
"audioipc2",
@ -289,7 +289,7 @@ dependencies = [
[[package]]
name = "audioipc2-server"
version = "0.5.0"
source = "git+https://github.com/mozilla/audioipc?rev=ec6af6ecf8ba50b51f13d18f417de8cfb8460543#ec6af6ecf8ba50b51f13d18f417de8cfb8460543"
source = "git+https://github.com/mozilla/audioipc?rev=6be424d75f1367e70f2f5ddcacd6d0237e81a6a9#6be424d75f1367e70f2f5ddcacd6d0237e81a6a9"
dependencies = [
"audio_thread_priority",
"audioipc2",

View file

@ -248,6 +248,11 @@ CachedTableCellAccessible* CachedTableCellAccessible::GetFrom(
if (auto cellIdx = cachedTable->mAccToCellIdx.Lookup(aAcc)) {
return &cachedTable->mCells[*cellIdx];
}
// We found a table, but it doesn't know about this cell. This can happen
// if a cell is outside of a row due to authoring error. We must not search
// ancestor tables, since this cell's data is not valid there and vice
// versa.
break;
}
return nullptr;
}

View file

@ -400,6 +400,13 @@ MARKUPMAP(
// A <tr> within a row isn't valid.
return nullptr;
}
const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aElement);
if (roleMapEntry && roleMapEntry->role != roles::NOTHING &&
roleMapEntry->role != roles::ROW) {
// There is a valid ARIA role which isn't "row". Don't treat this as an
// HTML table row.
return nullptr;
}
// Check if this <tr> is within a table. We check the grandparent because
// it might be inside a rowgroup. We don't specifically check for an HTML
// table because there are cases where there is a <tr> inside a

View file

@ -321,12 +321,17 @@ LayoutDeviceIntPoint nsAccUtils::GetScreenCoordsForParent(
LayoutDeviceIntPoint nsAccUtils::GetScreenCoordsForWindow(
Accessible* aAccessible) {
LayoutDeviceIntPoint coords(0, 0);
a11y::LocalAccessible* localAcc = aAccessible->AsLocal();
if (!localAcc) {
localAcc = aAccessible->AsRemote()->OuterDocOfRemoteBrowser();
if (!localAcc) {
// This could be null if the tab is closing but the document is still
// being shut down.
return coords;
}
}
LayoutDeviceIntPoint coords(0, 0);
nsCOMPtr<nsIDocShellTreeItem> treeItem(
nsCoreUtils::GetDocShellFor(localAcc->GetNode()));
if (!treeItem) return coords;

View file

@ -2644,6 +2644,7 @@ void DocAccessible::UncacheChildrenInSubtree(LocalAccessible* aRoot) {
}
void DocAccessible::ShutdownChildrenInSubtree(LocalAccessible* aAccessible) {
MOZ_ASSERT(!nsAccessibilityService::IsShutdown());
// Traverse through children and shutdown them before this accessible. When
// child gets shutdown then it removes itself from children array of its
// parent. Use jdx index to process the cases if child is not attached to the
@ -2658,7 +2659,16 @@ void DocAccessible::ShutdownChildrenInSubtree(LocalAccessible* aAccessible) {
// Don't cross document boundaries. The outerdoc shutdown takes care about
// its subdocument.
if (!child->IsDoc()) ShutdownChildrenInSubtree(child);
if (!child->IsDoc()) {
ShutdownChildrenInSubtree(child);
if (nsAccessibilityService::IsShutdown()) {
// If XPCOM is the only consumer (devtools & mochitests), shutting down
// the child's subtree can cause a11y to shut down because the last
// xpcom accessibles will be removed. In that case, return early, our
// work is done.
return;
}
}
}
UnbindFromDocument(aAccessible);

View file

@ -637,3 +637,29 @@ addAccessibleTask(
},
{ chrome: true, topLevel: true, remoteIframe: true }
);
/**
* Verify that we don't crash for authoring error like <tr role="grid">.
*/
addAccessibleTask(
`
<table id="table">
<tr><th>a</th></tr>
<tr role="grid"><td id="b">b</td></tr>
</table>
`,
async function (browser, docAcc) {
const table = findAccessibleChildByID(docAcc, "table", [
nsIAccessibleTable,
]);
is(table.rowCount, 1, "table rowCount correct");
is(table.columnCount, 1, "table columnCount correct");
const b = findAccessibleChildByID(docAcc, "b");
let queryOk = false;
try {
b.QueryInterface(nsIAccessibleTableCell);
queryOk = true;
} catch (e) {}
ok(!queryOk, "No nsIAccessibleTableCell on invalid cell b");
}
);

View file

@ -383,6 +383,7 @@ var gIdentityHandler = {
},
_isSchemelessHttpsFirstModeActive(isWindowPrivate) {
return (
!this._isHttpsOnlyModeActive(isWindowPrivate) &&
!this._isHttpsFirstModeActive(isWindowPrivate) &&
this._schemelessHttpsFirstModeEnabled
);

View file

@ -599,7 +599,7 @@
</panel>
</html:template>
#include ../../../toolkit/components/pictureinpicture/content/pictureInPicturePanel.xhtml
#include ../../../toolkit/components/pictureinpicture/content/pictureInPicturePanel.inc.xhtml
<menupopup id="unified-extensions-context-menu"
onpopupshowing="gUnifiedExtensions.updateContextMenu(this, event)"

View file

@ -270,7 +270,7 @@ add_task(async function test_devtools_network_on_request_finished() {
]);
ok(
callbackRes[0].startsWith("<html>"),
callbackRes[0].startsWith("<!DOCTYPE html>"),
"The expected content has been retrieved."
);
is(

View file

@ -7,6 +7,7 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs",
PlacesUIUtils: "resource:///modules/PlacesUIUtils.sys.mjs",
PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
});
ChromeUtils.defineLazyGetter(lazy, "relativeTimeFormat", () => {
@ -100,3 +101,49 @@ export function onToggleContainer(detailsContainer) {
);
}
}
/**
* This function doesn't just copy the link to the clipboard, it creates a
* URL object on the clipboard, so when it's pasted into an application that
* supports it, it displays the title as a link.
*/
export function placeLinkOnClipboard(title, uri) {
let node = {
type: 0,
title,
uri,
};
// Copied from doCommand/placesCmd_copy in PlacesUIUtils.sys.mjs
// This is a little hacky, but there is a lot of code in Places that handles
// clipboard stuff, so it's easier to reuse.
// This order is _important_! It controls how this and other applications
// select data to be inserted based on type.
let contents = [
{ type: lazy.PlacesUtils.TYPE_X_MOZ_URL, entries: [] },
{ type: lazy.PlacesUtils.TYPE_HTML, entries: [] },
{ type: lazy.PlacesUtils.TYPE_PLAINTEXT, entries: [] },
];
contents.forEach(function (content) {
content.entries.push(lazy.PlacesUtils.wrapNode(node, content.type));
});
let xferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(
Ci.nsITransferable
);
xferable.init(null);
function addData(type, data) {
xferable.addDataFlavor(type);
xferable.setTransferData(type, lazy.PlacesUtils.toISupportsString(data));
}
contents.forEach(function (content) {
addData(content.type, content.entries.join(lazy.PlacesUtils.endl));
});
Services.clipboard.setData(xferable, null, Ci.nsIClipboard.kGlobalClipboard);
}

View file

@ -11,11 +11,7 @@ import "chrome://browser/content/firefoxview/fxview-empty-state.mjs";
// eslint-disable-next-line import/no-unassigned-import
import "chrome://browser/content/firefoxview/fxview-tab-list.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
});
import { placeLinkOnClipboard } from "./helpers.mjs";
export class ViewPage extends MozLitElement {
static get properties() {
@ -75,52 +71,8 @@ export class ViewPage extends MozLitElement {
return window.browsingContext.embedderWindowGlobal.browsingContext.window;
}
/**
* This function doesn't just copy the link to the clipboard, it creates a
* URL object on the clipboard, so when it's pasted into an application that
* supports it, it displays the title as a link.
*/
copyLink(e) {
// Copied from doCommand/placesCmd_copy in PlacesUIUtils.sys.mjs
// This is a little hacky, but there is a lot of code in Places that handles
// clipboard stuff, so it's easier to reuse.
let node = {};
node.type = 0;
node.title = this.triggerNode.title;
node.uri = this.triggerNode.url;
// This order is _important_! It controls how this and other applications
// select data to be inserted based on type.
let contents = [
{ type: lazy.PlacesUtils.TYPE_X_MOZ_URL, entries: [] },
{ type: lazy.PlacesUtils.TYPE_HTML, entries: [] },
{ type: lazy.PlacesUtils.TYPE_PLAINTEXT, entries: [] },
];
contents.forEach(function (content) {
content.entries.push(lazy.PlacesUtils.wrapNode(node, content.type));
});
let xferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(
Ci.nsITransferable
);
xferable.init(null);
function addData(type, data) {
xferable.addDataFlavor(type);
xferable.setTransferData(type, lazy.PlacesUtils.toISupportsString(data));
}
contents.forEach(function (content) {
addData(content.type, content.entries.join(lazy.PlacesUtils.endl));
});
Services.clipboard.setData(
xferable,
null,
Ci.nsIClipboard.kGlobalClipboard
);
placeLinkOnClipboard(this.triggerNode.title, this.triggerNode.url);
this.recordContextMenuTelemetry("copy-link", e);
}

View file

@ -17,7 +17,7 @@ BROWSER_CHROME_MANIFESTS += [
"test/browser/telemetry/browser.toml",
]
MARIONETTE_LAYOUT_MANIFESTS += ["test/marionette/manifest.ini"]
MARIONETTE_UNIT_MANIFESTS += ["test/marionette/manifest.ini"]
XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.ini"]

View file

@ -129,12 +129,12 @@ export class ShoppingSidebarChild extends RemotePageChild {
break;
case "AdClicked":
aid = event.detail.aid;
this.#product.sendAttributionEvent("click", aid);
ShoppingProduct.sendAttributionEvent("click", aid);
Glean.shopping.surfaceAdsClicked.record();
break;
case "AdImpression":
aid = event.detail.aid;
this.#product.sendAttributionEvent("impression", aid);
ShoppingProduct.sendAttributionEvent("impression", aid);
Glean.shopping.surfaceAdsImpression.record();
break;
}

View file

@ -1,48 +0,0 @@
/* 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/. */
import { html } from "lit.all.mjs";
// Imported for side-effects.
// eslint-disable-next-line import/no-unassigned-import
import "toolkit-widgets/message-bar.js";
const MESSAGE_TYPES = {
default: "",
success: "success",
error: "error",
warning: "warning",
};
export default {
title: "UI Widgets/Message Bar",
component: "message-bar",
argTypes: {
type: {
options: Object.keys(MESSAGE_TYPES),
mapping: MESSAGE_TYPES,
control: { type: "select" },
},
},
parameters: {
status: "stable",
fluent: `
message-bar-text = A very expressive and slightly whimsical message goes here.
message-bar-button = Click me, please!
`,
},
};
const Template = ({ dismissable, type }) =>
html`
<message-bar type=${type} ?dismissable=${dismissable}>
<span data-l10n-id="message-bar-text"></span>
<button data-l10n-id="message-bar-button"></button>
</message-bar>
`;
export const Basic = Template.bind({});
Basic.args = { type: "", dismissable: false };
export const Dismissable = Template.bind({});
Dismissable.args = { type: "", dismissable: true };

View file

@ -339,7 +339,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "24d6bd9eb91d63ed6a97e0ae32fe8e5cf1da0def"
"revision": "24d70e9a9639a372e8e1e9d1647a49ce64094b9e"
},
"da": {
"pin": false,
@ -357,7 +357,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "ba8e876d8af866bf86941079402a109e29fa7be6"
"revision": "0c11157c7f8e8e65ba1e148dbfc3ccc1a28e04fa"
},
"de": {
"pin": false,
@ -1011,7 +1011,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "e1937b0c052a740cd041aaffcd24b301e452aef8"
"revision": "5eb85e45234ed6ba98f4fb463b4337d98e9351ce"
},
"kab": {
"pin": false,
@ -1623,7 +1623,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "3bdb6d8d01da0f46905c0ae96d9596f1ee3bd6a5"
"revision": "13d670a9b7a0c71a73e13893f407036cbb211c2f"
},
"sl": {
"pin": false,

View file

@ -53,17 +53,6 @@
}
}
/* Align the favicons and titles of rows with standard 16px icons with those
of rich suggestions. */
/* stylelint-disable-next-line media-query-no-invalid */
@media (-moz-bool-pref: "browser.urlbar.richSuggestions.featureGate") {
.urlbarView-row:not([rich-suggestion]),
.urlbarView-row[rich-suggestion][icon-size="16"] {
--urlbarView-icon-margin-start: calc((var(--urlbarView-rich-suggestion-default-icon-size) - 16px) / 2);
--urlbarView-icon-margin-end: calc(var(--urlbar-icon-padding) + var(--identity-box-margin-inline) + ((var(--urlbarView-rich-suggestion-default-icon-size) - 16px) / 2));
}
}
.urlbarView {
/* Don't handle window drag events in case we are overlapping a toolbar */
-moz-window-dragging: no-drag;
@ -99,6 +88,17 @@
.urlbarView-row {
--urlbarView-second-line-indent: calc(var(--urlbarView-icon-margin-start) + var(--urlbarView-icon-margin-end) + var(--urlbarView-favicon-width));
/* Align the favicons and titles of rows with standard 16px icons with those
of rich suggestions. */
/* stylelint-disable-next-line media-query-no-invalid */
@media (-moz-bool-pref: "browser.urlbar.richSuggestions.featureGate") {
&:not([rich-suggestion]),
&[rich-suggestion][icon-size="16"] {
--urlbarView-icon-margin-start: calc((var(--urlbarView-rich-suggestion-default-icon-size) - 16px) / 2);
--urlbarView-icon-margin-end: calc(var(--urlbar-icon-padding) + var(--identity-box-margin-inline) + ((var(--urlbarView-rich-suggestion-default-icon-size) - 16px) / 2));
}
}
display: flex;
align-items: center;
fill: currentColor;

View file

@ -653,7 +653,6 @@ _ZN4uuid6parser28_$LT$impl$u20$uuid..Uuid$GT$9parse_str17hef8066f10442e30fE
?s_HashKey@?$nsTHashtable@V?$nsBaseHashtableET@VnsStringHashKey@@PAUMiscContainer@@@@@@KAIPBX@Z
?s_MatchEntry@?$nsTHashtable@V?$nsBaseHashtableET@VnsStringHashKey@@V?$UniquePtr@URawServoSelectorList@@V?$DefaultDelete@URawServoSelectorList@@@mozilla@@@mozilla@@@@@@KA_NPBUPLDHashEntryHdr@@PBX@Z
?GetOrCreate@nsRFPService@mozilla@@SAPAV12@XZ
?OnComplete@LoginReputationParent@dom@mozilla@@UAG?AW4nsresult@@W44@I@Z
?GetSpoofedPresentedFrames@nsRFPService@mozilla@@SAINII@Z
?Release@nsUUIDGenerator@@UAGKXZ
??0AutoSuppressEventHandlingAndSuspend@dom@mozilla@@QAE@PAVBrowsingContextGroup@12@@Z

View file

@ -658,7 +658,6 @@ ZN4uuid6parser28_$LT$impl$u20$uuid..Uuid$GT$9parse_str17hd6e937141c32aa4eE
?DetermineOffsetFromReference@?$RangeBoundaryBase@V?$nsCOMPtr@VnsINode@@@@V?$nsCOMPtr@VnsIContent@@@@@mozilla@@AEBAXXZ
?s_HashKey@?$nsTHashtable@V?$nsBaseHashtableET@VnsStringHashKey@@PEAUMiscContainer@@@@@@KAIPEBX@Z
?GetOrCreate@nsRFPService@mozilla@@SAPEAV12@XZ
?OnComplete@LoginReputationParent@dom@mozilla@@UEAA?AW4nsresult@@W44@I@Z
?GetSpoofedPresentedFrames@nsRFPService@mozilla@@SAINII@Z
?Release@nsUUIDGenerator@@UEAAKXZ
??0AutoSuppressEventHandlingAndSuspend@dom@mozilla@@QEAA@PEAVBrowsingContextGroup@12@@Z

View file

@ -10,7 +10,6 @@ add_task(async function () {
await pushPref("browser.safebrowsing.blockedURIs.enabled", false);
await pushPref("browser.safebrowsing.downloads.enabled", false);
await pushPref("browser.safebrowsing.malware.enabled", false);
await pushPref("browser.safebrowsing.passwords.enabled", false);
await pushPref("browser.safebrowsing.phishing.enabled", false);
await pushPref("privacy.query_stripping.enabled", false);
await pushPref("extensions.systemAddon.update.enabled", false);

View file

@ -64,6 +64,7 @@ support-files = [
"test-console-stacktrace-mapped.html",
"test-console-table.html",
"test-console-workers.html",
"test-console-worklet.html",
"test-console.html",
"test-data.json",
"test-data.json^headers^",
@ -827,4 +828,6 @@ skip-if = ["true"] # Bug 1765369
["browser_webconsole_worker_promise_error.js"]
["browser_webconsole_worklet_console.js"]
["browser_webconsole_worklet_error.js"]

View file

@ -10,6 +10,12 @@ const TEST_URI =
"test/browser/test-console-workers.html";
add_task(async function () {
// Allow using SharedArrayBuffer in the test without special HTTP Headers
await pushPref(
"dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled",
true
);
info("Run the test with worker events dispatched to main thread");
await pushPref("dom.worker.console.dispatch_events_to_main_thread", true);
await testWorkerMessage();
@ -91,6 +97,11 @@ async function testWorkerMessage(directConnectionToWorkerThread = false) {
ok(symbolMessage, "Symbol logged from worker is visible in the console");
}
const sabMessage = await waitFor(() =>
findConsoleAPIMessage(hud, "sab-from-worker")
);
ok(sabMessage.textContent.includes("SharedArrayBuffer"));
info("Click on the clear button and wait for messages to be removed");
const onMessagesCacheCleared = hud.ui.once("messages-cache-cleared");
hud.ui.window.document.querySelector(".devtools-clear-icon").click();

View file

@ -0,0 +1,34 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that console api usage in worklet show in the console
"use strict";
const TEST_URI =
"https://example.com/browser/devtools/client/webconsole/" +
"test/browser/test-console-worklet.html";
add_task(async function () {
// Allow using SharedArrayBuffer in the test without special HTTP Headers
await pushPref(
"dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled",
true
);
const hud = await openNewTabAndConsole(TEST_URI);
await waitFor(() => findConsoleAPIMessage(hud, "string"));
await waitFor(() => findConsoleAPIMessage(hud, "42"));
const objectMessage = await waitFor(() =>
findConsoleAPIMessage(hud, "object")
);
ok(
objectMessage
.querySelector(".message-body")
.textContent.includes(`Object { object: true }`),
"The simple object is logged as expected"
);
await waitFor(() => findConsoleAPIMessage(hud, "SharedArrayBuffer"));
ok(true, "SharedArrayBuffer object is logged");
});

View file

@ -17,11 +17,13 @@
onmessage = function (e) {
console.log("log-from-worker", e.data.msg, globalThis);
console.log(Symbol("logged-symbol-from-worker"));
console.log("sab-from-worker", e.data.sab);
};
`);
function logFromWorker(msg) {
worker.postMessage({type: "log", msg});
const sab = new SharedArrayBuffer(1024);
worker.postMessage({type: "log", msg, sab});
}
</script>
</body>

View file

@ -0,0 +1,41 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Worklet console generator</title>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
</head>
<script type="worklet">
registerProcessor('test-param', class param extends AudioWorkletProcessor {
constructor() {
super();
this.port.onmessage = e => {
console.log("worklet", "string");
console.log("worklet", 42);
console.log("worklet", { object: true });
console.log("worklet", e.data.data); // Log the SharedArrayBuffer
};
}
process(input, output, parameters) {
return true;
}
});
</script>
<script>
"use strict";
const ac = new AudioContext();
const e = document.querySelector("script[type=worklet]")
const blob = new Blob([e.textContent], {type: "application/javascript"});
const url = URL.createObjectURL(blob);
ac.audioWorklet.addModule(url).then(() => {
const nodea = new AudioWorkletNode(ac, 'test-param');
// Instantiate and pass a SharedArrayBuffer to the Worklet
const normal_sab = new SharedArrayBuffer(1024);
nodea.port.postMessage({data: normal_sab });
});
</script>

View file

@ -47,18 +47,21 @@ class BlackboxingActor extends Actor {
*/
blackbox(url, ranges) {
if (!ranges.length) {
return this.watcherActor.addDataEntry(BLACKBOXING, [
{ url, range: null },
]);
return this.watcherActor.addOrSetDataEntry(
BLACKBOXING,
[{ url, range: null }],
"add"
);
}
return this.watcherActor.addDataEntry(
return this.watcherActor.addOrSetDataEntry(
BLACKBOXING,
ranges.map(range => {
return {
url,
range,
};
})
}),
"add"
);
}

View file

@ -32,7 +32,11 @@ class BreakpointListActor extends Actor {
}
setBreakpoint(location, options) {
return this.watcherActor.addDataEntry(BREAKPOINTS, [{ location, options }]);
return this.watcherActor.addOrSetDataEntry(
BREAKPOINTS,
[{ location, options }],
"add"
);
}
removeBreakpoint(location, options) {
@ -53,7 +57,11 @@ class BreakpointListActor extends Actor {
* Otherwise, should be set to any valid HTTP Method (GET, POST, ...)
*/
setXHRBreakpoint(path, method) {
return this.watcherActor.addDataEntry(XHR_BREAKPOINTS, [{ path, method }]);
return this.watcherActor.addOrSetDataEntry(
XHR_BREAKPOINTS,
[{ path, method }],
"add"
);
}
/**
@ -76,18 +84,8 @@ class BreakpointListActor extends Actor {
* See devtools/server/actors/utils/event-breakpoints.js
* for details.
*/
setActiveEventBreakpoints(ids) {
const existingIds =
this.watcherActor.getSessionDataForType(EVENT_BREAKPOINTS) || [];
const addIds = ids.filter(id => !existingIds.includes(id));
const removeIds = existingIds.filter(id => !ids.includes(id));
if (addIds.length) {
this.watcherActor.addDataEntry(EVENT_BREAKPOINTS, addIds);
}
if (removeIds.length) {
this.watcherActor.removeDataEntry(EVENT_BREAKPOINTS, removeIds);
}
async setActiveEventBreakpoints(ids) {
await this.watcherActor.addOrSetDataEntry(EVENT_BREAKPOINTS, ids, "set");
}
}

View file

@ -215,7 +215,11 @@ class TargetConfigurationActor extends Actor {
.map(key => ({ key, value: configuration[key] }));
this._updateParentProcessConfiguration(configuration);
await this.watcherActor.addDataEntry(TARGET_CONFIGURATION, cfgArray);
await this.watcherActor.addOrSetDataEntry(
TARGET_CONFIGURATION,
cfgArray,
"add"
);
return this._getConfiguration();
}

View file

@ -34,18 +34,31 @@ class BaseTargetActor extends Actor {
* @param Boolean isDocumentCreation
* Set to true if this function is called just after a new document (and its
* associated target) is created.
* @param String updateType
* "add" will only add the new entries in the existing data set.
* "set" will update the data set with the new entries.
*/
async addSessionDataEntry(type, entries, isDocumentCreation = false) {
async addOrSetSessionDataEntry(
type,
entries,
isDocumentCreation = false,
updateType
) {
const processor = SessionDataProcessors[type];
if (processor) {
await processor.addSessionDataEntry(this, entries, isDocumentCreation);
await processor.addOrSetSessionDataEntry(
this,
entries,
isDocumentCreation,
updateType
);
}
}
/**
* Remove data entries that have been previously added via addSessionDataEntry
* Remove data entries that have been previously added via addOrSetSessionDataEntry
*
* See addSessionDataEntry for argument description.
* See addOrSetSessionDataEntry for argument description.
*/
removeSessionDataEntry(type, entries) {
const processor = SessionDataProcessors[type];

View file

@ -5,9 +5,18 @@
"use strict";
module.exports = {
async addSessionDataEntry(targetActor, entries, isDocumentCreation) {
async addOrSetSessionDataEntry(
targetActor,
entries,
isDocumentCreation,
updateType
) {
const { sourcesManager } = targetActor;
if (updateType == "set") {
sourcesManager.clearAllBlackBoxing();
}
for (const { url, range } of entries) {
targetActor.sourcesManager.blackBox(url, range);
sourcesManager.blackBox(url, range);
}
},

View file

@ -9,21 +9,29 @@ const {
} = require("resource://devtools/server/actors/thread.js");
module.exports = {
async addSessionDataEntry(targetActor, entries, isDocumentCreation) {
const isTargetCreation =
targetActor.threadActor.state == THREAD_STATES.DETACHED;
async addOrSetSessionDataEntry(
targetActor,
entries,
isDocumentCreation,
updateType
) {
const { threadActor } = targetActor;
if (updateType == "set") {
threadActor.removeAllBreakpoints();
}
const isTargetCreation = threadActor.state == THREAD_STATES.DETACHED;
if (isTargetCreation && !targetActor.targetType.endsWith("worker")) {
// If addSessionDataEntry is called during target creation, attach the
// If addOrSetSessionDataEntry is called during target creation, attach the
// thread actor automatically and pass the initial breakpoints.
// However, do not attach the thread actor for Workers. They use a codepath
// which releases the worker on `attach`. For them, the client will call `attach`. (bug 1691986)
await targetActor.threadActor.attach({ breakpoints: entries });
await threadActor.attach({ breakpoints: entries });
} else {
// If addSessionDataEntry is called for an existing target, set the new
// If addOrSetSessionDataEntry is called for an existing target, set the new
// breakpoints on the already running thread actor.
await Promise.all(
entries.map(({ location, options }) =>
targetActor.threadActor.setBreakpoint(location, options)
threadActor.setBreakpoint(location, options)
)
);
}

View file

@ -9,15 +9,25 @@ const {
} = require("resource://devtools/server/actors/thread.js");
module.exports = {
async addSessionDataEntry(targetActor, entries, isDocumentCreation) {
async addOrSetSessionDataEntry(
targetActor,
entries,
isDocumentCreation,
updateType
) {
const { threadActor } = targetActor;
// Same as comments for XHR breakpoints. See lines 117-118
if (
targetActor.threadActor.state == THREAD_STATES.DETACHED &&
threadActor.state == THREAD_STATES.DETACHED &&
!targetActor.targetType.endsWith("worker")
) {
targetActor.threadActor.attach();
threadActor.attach();
}
if (updateType == "set") {
threadActor.setActiveEventBreakpoints(entries);
} else {
threadActor.addEventBreakpoints(entries);
}
targetActor.threadActor.addEventBreakpoints(entries);
},
removeSessionDataEntry(targetActor, entries, isDocumentCreation) {

View file

@ -7,7 +7,15 @@
const Resources = require("resource://devtools/server/actors/resources/index.js");
module.exports = {
async addSessionDataEntry(targetActor, entries, isDocumentCreation) {
async addOrSetSessionDataEntry(
targetActor,
entries,
isDocumentCreation,
updateType
) {
if (updateType == "set") {
Resources.unwatchAllResources(targetActor);
}
await Resources.watchResources(targetActor, entries);
},

View file

@ -5,7 +5,12 @@
"use strict";
module.exports = {
async addSessionDataEntry(targetActor, entries, isDocumentCreation) {
async addOrSetSessionDataEntry(
targetActor,
entries,
isDocumentCreation,
updateType
) {
// Only WindowGlobalTargetActor implements updateTargetConfiguration,
// skip targetActor data entry update for other targets.
if (typeof targetActor.updateTargetConfiguration == "function") {
@ -13,6 +18,10 @@ module.exports = {
for (const { key, value } of entries) {
options[key] = value;
}
// Regarding `updateType`, `entries` is always a partial set of configurations.
// We will acknowledge the passed attribute, but if we had set some other attributes
// before this call, they will stay as-is.
// So it is as if this session data was also using "add" updateType.
targetActor.updateTargetConfiguration(options, isDocumentCreation);
}
},

View file

@ -9,7 +9,12 @@ const {
} = require("resource://devtools/server/actors/thread.js");
module.exports = {
async addSessionDataEntry(targetActor, entries, isDocumentCreation) {
async addOrSetSessionDataEntry(
targetActor,
entries,
isDocumentCreation,
updateType
) {
const threadOptions = {};
for (const { key, value } of entries) {
@ -22,6 +27,10 @@ module.exports = {
) {
await targetActor.threadActor.attach(threadOptions);
} else {
// Regarding `updateType`, `entries` is always a partial set of configurations.
// We will acknowledge the passed attribute, but if we had set some other attributes
// before this call, they will stay as-is.
// So it is as if this session data was also using "add" updateType.
await targetActor.threadActor.reconfigure(threadOptions);
}
},

View file

@ -9,19 +9,29 @@ const {
} = require("resource://devtools/server/actors/thread.js");
module.exports = {
async addSessionDataEntry(targetActor, entries, isDocumentCreation) {
async addOrSetSessionDataEntry(
targetActor,
entries,
isDocumentCreation,
updateType
) {
const { threadActor } = targetActor;
if (updateType == "set") {
threadActor.removeAllXHRBreakpoints();
}
// The thread actor has to be initialized in order to correctly
// retrieve the stack trace when hitting an XHR
if (
targetActor.threadActor.state == THREAD_STATES.DETACHED &&
threadActor.state == THREAD_STATES.DETACHED &&
!targetActor.targetType.endsWith("worker")
) {
await targetActor.threadActor.attach();
await threadActor.attach();
}
await Promise.all(
entries.map(({ path, method }) =>
targetActor.threadActor.setXHRBreakpoint(path, method)
threadActor.setXHRBreakpoint(path, method)
)
);
},

View file

@ -66,7 +66,11 @@ class ThreadConfigurationActor extends Actor {
})
.map(key => ({ key, value: configuration[key] }));
await this.watcherActor.addDataEntry(THREAD_CONFIGURATION, configArray);
await this.watcherActor.addOrSetDataEntry(
THREAD_CONFIGURATION,
configArray,
"add"
);
}
}

View file

@ -564,6 +564,11 @@ class ThreadActor extends Actor {
actor.delete();
}
removeAllXHRBreakpoints() {
this._xhrBreakpoints = [];
return this._updateNetworkObserver();
}
removeXHRBreakpoint(path, method) {
const index = this._findXHRBreakpointIndex(path, method);
@ -1518,6 +1523,10 @@ class ThreadActor extends Actor {
}
}
removeAllBreakpoints() {
this.breakpointActorMap.removeAllBreakpoints();
}
removeAllWatchpoints() {
for (const actor of this.threadLifetimePool.poolChildren()) {
if (actor.typeName == "obj") {

View file

@ -69,6 +69,16 @@ class BreakpointActorMap {
const key = this._locationKey(location);
delete this._actors[key];
}
/**
* Unregister all currently active breakpoints.
*/
removeAllBreakpoints() {
for (const bpActor of Object.values(this._actors)) {
bpActor.removeScripts();
}
this._actors = {};
}
}
exports.BreakpointActorMap = BreakpointActorMap;

View file

@ -279,6 +279,10 @@ class SourcesManager extends EventEmitter {
return this.isBlackBoxed(url, line, column);
}
clearAllBlackBoxing() {
this.blackBoxedSources.clear();
}
/**
* Add the given source URL to the set of sources that are black boxed.
*

View file

@ -517,10 +517,11 @@ exports.WatcherActor = class WatcherActor extends Actor {
continue;
}
const targetHelperModule = TARGET_HELPERS[targetType];
await targetHelperModule.addSessionDataEntry({
await targetHelperModule.addOrSetSessionDataEntry({
watcher: this,
type: "resources",
entries: targetResourceTypes,
updateType: "add",
});
}
@ -547,9 +548,11 @@ exports.WatcherActor = class WatcherActor extends Actor {
resourceTypes,
targetActor.targetType
);
await targetActor.addSessionDataEntry(
await targetActor.addOrSetSessionDataEntry(
"resources",
targetActorResourceTypes
targetActorResourceTypes,
false,
"add"
);
}
}
@ -711,10 +714,13 @@ exports.WatcherActor = class WatcherActor extends Actor {
* @param {String} type
* Data type to contribute to.
* @param {Array<*>} entries
* List of values to add for this data type.
* List of values to add or set for this data type.
* @param {String} updateType
* "add" will only add the new entries in the existing data set.
* "set" will update the data set with the new entries.
*/
async addDataEntry(type, entries) {
WatcherRegistry.addSessionDataEntry(this, type, entries);
async addOrSetDataEntry(type, entries, updateType) {
WatcherRegistry.addOrSetSessionDataEntry(this, type, entries, updateType);
await Promise.all(
Object.values(Targets.TYPES)
@ -730,10 +736,11 @@ exports.WatcherActor = class WatcherActor extends Actor {
)
.map(async targetType => {
const targetHelperModule = TARGET_HELPERS[targetType];
await targetHelperModule.addSessionDataEntry({
await targetHelperModule.addOrSetSessionDataEntry({
watcher: this,
type,
entries,
updateType,
});
})
);
@ -741,7 +748,12 @@ exports.WatcherActor = class WatcherActor extends Actor {
// See comment in watchResources
const targetActor = this.getTargetActorInParentProcess();
if (targetActor) {
await targetActor.addSessionDataEntry(type, entries);
await targetActor.addOrSetSessionDataEntry(
type,
entries,
false,
updateType
);
}
}
@ -761,7 +773,7 @@ exports.WatcherActor = class WatcherActor extends Actor {
Object.values(Targets.TYPES)
.filter(
targetType =>
// See comment in addDataEntry
// See comment in addOrSetDataEntry
WatcherRegistry.isWatchingTargets(this, targetType) ||
targetType === Targets.TYPES.FRAME
)
@ -774,7 +786,7 @@ exports.WatcherActor = class WatcherActor extends Actor {
});
});
// See comment in addDataEntry
// See comment in addOrSetDataEntry
const targetActor = this.getTargetActorInParentProcess();
if (targetActor) {
targetActor.removeSessionDataEntry(type, entries);

View file

@ -117,6 +117,36 @@ const DATA_KEY_FUNCTION = {
return id;
},
};
// Optional validation method to assert the shape of each session data entry
const DATA_VALIDATION_FUNCTION = {
[SUPPORTED_DATA.BREAKPOINTS]({ location }) {
lazy.validateBreakpointLocation(location);
},
[SUPPORTED_DATA.XHR_BREAKPOINTS]({ path, method }) {
if (typeof path != "string") {
throw new Error(
`XHR Breakpoints expect to have path string, got ${typeof path} instead.`
);
}
if (typeof method != "string") {
throw new Error(
`XHR Breakpoints expect to have method string, got ${typeof method} instead.`
);
}
},
[SUPPORTED_DATA.EVENT_BREAKPOINTS](id) {
if (typeof id != "string") {
throw new Error(
`Event Breakpoints expect the id to be a string , got ${typeof id} instead.`
);
}
if (!lazy.validateEventBreakpoint(id)) {
throw new Error(
`The id string should be a valid event breakpoint id, ${id} is not.`
);
}
},
};
function idFunction(v) {
if (typeof v != "string") {
@ -139,14 +169,28 @@ const SessionDataHelpers = {
* The type of data to be added
* @param Array<Object> entries
* The values to be added to this type of data
* @param String updateType
* "add" will only add the new entries in the existing data set.
* "set" will update the data set with the new entries.
*/
addSessionDataEntry(sessionData, type, entries) {
addOrSetSessionDataEntry(sessionData, type, entries, updateType) {
const validationFunction = DATA_VALIDATION_FUNCTION[type];
if (validationFunction) {
entries.forEach(validationFunction);
}
// When we are replacing the whole entries, things are significantly simplier
if (updateType == "set") {
sessionData[type] = entries;
return;
}
if (!sessionData[type]) {
sessionData[type] = [];
}
const toBeAdded = [];
const keyFunction = DATA_KEY_FUNCTION[type] || idFunction;
for (const entry of entries) {
if (!sessionData[type]) {
sessionData[type] = [];
}
const existingIndex = sessionData[type].findIndex(existingEntry => {
return keyFunction(existingEntry) === keyFunction(entry);
});

View file

@ -150,7 +150,7 @@ export const WatcherRegistry = {
},
/**
* Notify that a given watcher added an entry in a given data type.
* Notify that a given watcher added or set some entries for given data type.
*
* @param WatcherActor watcher
* The WatcherActor which starts observing.
@ -158,8 +158,11 @@ export const WatcherRegistry = {
* The type of data to be added
* @param Array<Object> entries
* The values to be added to this type of data
* @param String updateType
* "add" will only add the new entries in the existing data set.
* "set" will update the data set with the new entries.
*/
addSessionDataEntry(watcher, type, entries) {
addOrSetSessionDataEntry(watcher, type, entries, updateType) {
const sessionData = this.getSessionData(watcher, {
createData: true,
});
@ -168,7 +171,12 @@ export const WatcherRegistry = {
throw new Error(`Unsupported session data type: ${type}`);
}
SessionDataHelpers.addSessionDataEntry(sessionData, type, entries);
SessionDataHelpers.addOrSetSessionDataEntry(
sessionData,
type,
entries,
updateType
);
// Register the JS Window Actor the first time we start watching for something (e.g. resource, target, …).
registerJSWindowActor();
@ -249,7 +257,12 @@ export const WatcherRegistry = {
* The new target type to start listening to.
*/
watchTargets(watcher, targetType) {
this.addSessionDataEntry(watcher, SUPPORTED_DATA.TARGETS, [targetType]);
this.addOrSetSessionDataEntry(
watcher,
SUPPORTED_DATA.TARGETS,
[targetType],
"add"
);
},
/**
@ -283,7 +296,12 @@ export const WatcherRegistry = {
* The new resource types to start listening to.
*/
watchResources(watcher, resourceTypes) {
this.addSessionDataEntry(watcher, SUPPORTED_DATA.RESOURCES, resourceTypes);
this.addOrSetSessionDataEntry(
watcher,
SUPPORTED_DATA.RESOURCES,
resourceTypes,
"add"
);
},
/**

View file

@ -211,8 +211,16 @@ function destroyTargets(watcher, options) {
* The type of data to be added
* @param Array<Object> entries
* The values to be added to this type of data
* @param String updateType
* "add" will only add the new entries in the existing data set.
* "set" will update the data set with the new entries.
*/
async function addSessionDataEntry({ watcher, type, entries }) {
async function addOrSetSessionDataEntry({
watcher,
type,
entries,
updateType,
}) {
const browsingContexts = getWatchingBrowsingContexts(watcher);
const promises = [];
for (const browsingContext of browsingContexts) {
@ -223,11 +231,12 @@ async function addSessionDataEntry({ watcher, type, entries }) {
const promise = browsingContext.currentWindowGlobal
.getActor("DevToolsFrame")
.addSessionDataEntry({
.addOrSetSessionDataEntry({
watcherActorID: watcher.actorID,
sessionContext: watcher.sessionContext,
type,
entries,
updateType,
});
promises.push(promise);
}
@ -238,7 +247,7 @@ async function addSessionDataEntry({ watcher, type, entries }) {
/**
* Notify all existing frame targets that some data entries have been removed
*
* See addSessionDataEntry for argument documentation.
* See addOrSetSessionDataEntry for argument documentation.
*/
function removeSessionDataEntry({ watcher, type, entries }) {
const browsingContexts = getWatchingBrowsingContexts(watcher);
@ -262,7 +271,7 @@ function removeSessionDataEntry({ watcher, type, entries }) {
module.exports = {
createTargets,
destroyTargets,
addSessionDataEntry,
addOrSetSessionDataEntry,
removeSessionDataEntry,
};

View file

@ -311,8 +311,16 @@ function destroyTargets(watcher, options) {
* The type of data to be added
* @param {Array<Object>} options.entries
* The values to be added to this type of data
* @param String updateType
* "add" will only add the new entries in the existing data set.
* "set" will update the data set with the new entries.
*/
async function addSessionDataEntry({ watcher, type, entries }) {
async function addOrSetSessionDataEntry({
watcher,
type,
entries,
updateType,
}) {
let expectedCount = Services.ppmm.childCount - 1;
if (expectedCount == 0) {
return;
@ -327,7 +335,7 @@ async function addSessionDataEntry({ watcher, type, entries }) {
maybeResolve();
};
Services.ppmm.addMessageListener(
"debug:add-session-data-entry-done",
"debug:add-or-set-session-data-entry-done",
listener
);
const onContentProcessClosed = (messageManager, topic, data) => {
@ -337,7 +345,7 @@ async function addSessionDataEntry({ watcher, type, entries }) {
const maybeResolve = () => {
if (count == expectedCount) {
Services.ppmm.removeMessageListener(
"debug:add-session-data-entry-done",
"debug:add-or-set-session-data-entry-done",
listener
);
Services.obs.removeObserver(
@ -350,10 +358,11 @@ async function addSessionDataEntry({ watcher, type, entries }) {
Services.obs.addObserver(onContentProcessClosed, "message-manager-close");
});
Services.ppmm.broadcastAsyncMessage("debug:add-session-data-entry", {
Services.ppmm.broadcastAsyncMessage("debug:add-or-set-session-data-entry", {
watcherActorID: watcher.actorID,
type,
entries,
updateType,
});
await onAllReplied;
@ -362,7 +371,7 @@ async function addSessionDataEntry({ watcher, type, entries }) {
/**
* Notify all existing content processes that some data entries have been removed
*
* See addSessionDataEntry for argument documentation.
* See addOrSetSessionDataEntry for argument documentation.
*/
function removeSessionDataEntry({ watcher, type, entries }) {
Services.ppmm.broadcastAsyncMessage("debug:remove-session-data-entry", {
@ -375,6 +384,6 @@ function removeSessionDataEntry({ watcher, type, entries }) {
module.exports = {
createTargets,
destroyTargets,
addSessionDataEntry,
addOrSetSessionDataEntry,
removeSessionDataEntry,
};

View file

@ -76,8 +76,16 @@ async function destroyTargets(watcher) {
* The type of data to be added
* @param Array<Object> entries
* The values to be added to this type of data
* @param String updateType
* "add" will only add the new entries in the existing data set.
* "set" will update the data set with the new entries.
*/
async function addSessionDataEntry({ watcher, type, entries }) {
async function addOrSetSessionDataEntry({
watcher,
type,
entries,
updateType,
}) {
const browsingContexts = watcher.getAllBrowsingContexts({
acceptSameProcessIframes: true,
forceAcceptTopLevelTarget: true,
@ -86,11 +94,12 @@ async function addSessionDataEntry({ watcher, type, entries }) {
for (const browsingContext of browsingContexts) {
const promise = browsingContext.currentWindowGlobal
.getActor(DEVTOOLS_WORKER_JS_WINDOW_ACTOR_NAME)
.addSessionDataEntry({
.addOrSetSessionDataEntry({
watcherActorID: watcher.actorID,
sessionContext: watcher.sessionContext,
type,
entries,
updateType,
});
promises.push(promise);
}
@ -101,7 +110,7 @@ async function addSessionDataEntry({ watcher, type, entries }) {
/**
* Notify all existing frame targets that some data entries have been removed
*
* See addSessionDataEntry for argument documentation.
* See addOrSetSessionDataEntry for argument documentation.
*/
function removeSessionDataEntry({ watcher, type, entries }) {
const browsingContexts = watcher.getAllBrowsingContexts({
@ -123,6 +132,6 @@ function removeSessionDataEntry({ watcher, type, entries }) {
module.exports = {
createTargets,
destroyTargets,
addSessionDataEntry,
addOrSetSessionDataEntry,
removeSessionDataEntry,
};

View file

@ -286,7 +286,12 @@ export class DevToolsFrameChild extends JSWindowActorChild {
if (!Array.isArray(entries) || !entries.length) {
continue;
}
targetActor.addSessionDataEntry(type, entries, isDocumentCreation);
targetActor.addOrSetSessionDataEntry(
type,
entries,
isDocumentCreation,
"set"
);
}
}
@ -431,13 +436,15 @@ export class DevToolsFrameChild extends JSWindowActorChild {
const { watcherActorID, options } = message.data;
return this._destroyTargetActor(watcherActorID, options);
}
case "DevToolsFrameParent:addSessionDataEntry": {
const { watcherActorID, sessionContext, type, entries } = message.data;
return this._addSessionDataEntry(
case "DevToolsFrameParent:addOrSetSessionDataEntry": {
const { watcherActorID, sessionContext, type, entries, updateType } =
message.data;
return this._addOrSetSessionDataEntry(
watcherActorID,
sessionContext,
type,
entries
entries,
updateType
);
}
case "DevToolsFrameParent:removeSessionDataEntry": {
@ -510,7 +517,13 @@ export class DevToolsFrameChild extends JSWindowActorChild {
return null;
}
_addSessionDataEntry(watcherActorID, sessionContext, type, entries) {
_addOrSetSessionDataEntry(
watcherActorID,
sessionContext,
type,
entries,
updateType
) {
// /!\ We may have an issue here as there could be multiple targets for a given
// (watcherActorID,browserId) pair.
// This should be clarified as part of Bug 1725623.
@ -524,7 +537,12 @@ export class DevToolsFrameChild extends JSWindowActorChild {
`No target actor for this Watcher Actor ID:"${watcherActorID}" / BrowserId:${sessionContext.browserId}`
);
}
return targetActor.addSessionDataEntry(type, entries);
return targetActor.addOrSetSessionDataEntry(
type,
entries,
false,
updateType
);
}
_removeSessionDataEntry(watcherActorID, sessionContext, type, entries) {

View file

@ -87,13 +87,20 @@ export class DevToolsFrameParent extends JSWindowActorParent {
/**
* Communicate to the content process that some data have been added.
*/
async addSessionDataEntry({ watcherActorID, sessionContext, type, entries }) {
async addOrSetSessionDataEntry({
watcherActorID,
sessionContext,
type,
entries,
updateType,
}) {
try {
await this.sendQuery("DevToolsFrameParent:addSessionDataEntry", {
await this.sendQuery("DevToolsFrameParent:addOrSetSessionDataEntry", {
watcherActorID,
sessionContext,
type,
entries,
updateType,
});
} catch (e) {
console.warn(

View file

@ -180,9 +180,14 @@ export class DevToolsWorkerChild extends JSWindowActorChild {
const { watcherActorID } = message.data;
return this._destroyTargetActors(watcherActorID);
}
case "DevToolsWorkerParent:addSessionDataEntry": {
const { watcherActorID, type, entries } = message.data;
return this._addSessionDataEntry(watcherActorID, type, entries);
case "DevToolsWorkerParent:addOrSetSessionDataEntry": {
const { watcherActorID, type, entries, updateType } = message.data;
return this._addOrSetSessionDataEntry(
watcherActorID,
type,
entries,
updateType
);
}
case "DevToolsWorkerParent:removeSessionDataEntry": {
const { watcherActorID, type, entries } = message.data;
@ -410,16 +415,17 @@ export class DevToolsWorkerChild extends JSWindowActorChild {
});
}
async _addSessionDataEntry(watcherActorID, type, entries) {
async _addOrSetSessionDataEntry(watcherActorID, type, entries, updateType) {
const watcherConnectionData = this._connections.get(watcherActorID);
if (!watcherConnectionData) {
return;
}
lazy.SessionDataHelpers.addSessionDataEntry(
lazy.SessionDataHelpers.addOrSetSessionDataEntry(
watcherConnectionData.sessionData,
type,
entries
entries,
updateType
);
const promises = [];
@ -428,11 +434,12 @@ export class DevToolsWorkerChild extends JSWindowActorChild {
workerThreadServerForwardingPrefix,
} of watcherConnectionData.workers) {
promises.push(
addSessionDataEntryInWorkerTarget({
addOrSetSessionDataEntryInWorkerTarget({
dbg,
workerThreadServerForwardingPrefix,
type,
entries,
updateType,
})
);
}
@ -508,14 +515,24 @@ export class DevToolsWorkerChild extends JSWindowActorChild {
/**
* Communicate the type and entries to the Worker Target actor, via the WorkerDebugger.
*
* @param {WorkerDebugger} dbg
* @param {String} workerThreadServerForwardingPrefix
* @param {String} type
* Session data type name
* @param {Array} entries
* Session data entries to add or set.
* @param {String} updateType
* Either "add" or "set", to control if we should only add some items,
* or replace the whole data set with the new entries.
* @returns {Promise} Returns a Promise that resolves once the data entry were handled
* by the worker target.
*/
function addSessionDataEntryInWorkerTarget({
function addOrSetSessionDataEntryInWorkerTarget({
dbg,
workerThreadServerForwardingPrefix,
type,
entries,
updateType,
}) {
if (!lazy.DevToolsUtils.isWorkerDebuggerAlive(dbg)) {
return Promise.resolve();
@ -527,7 +544,7 @@ function addSessionDataEntryInWorkerTarget({
const listener = {
onMessage: message => {
message = JSON.parse(message);
if (message.type === "session-data-entry-added") {
if (message.type === "session-data-entry-added-or-set") {
resolve();
dbg.removeListener(listener);
}
@ -540,10 +557,11 @@ function addSessionDataEntryInWorkerTarget({
dbg.postMessage(
JSON.stringify({
type: "add-session-data-entry",
type: "add-or-set-session-data-entry",
forwardingPrefix: workerThreadServerForwardingPrefix,
dataEntryType: type,
entries,
updateType,
})
);
});

View file

@ -91,13 +91,20 @@ export class DevToolsWorkerParent extends JSWindowActorParent {
/**
* Communicate to the content process that some data have been added.
*/
async addSessionDataEntry({ watcherActorID, sessionContext, type, entries }) {
async addOrSetSessionDataEntry({
watcherActorID,
sessionContext,
type,
entries,
updateType,
}) {
try {
await this.sendQuery("DevToolsWorkerParent:addSessionDataEntry", {
await this.sendQuery("DevToolsWorkerParent:addOrSetSessionDataEntry", {
watcherActorID,
sessionContext,
type,
entries,
updateType,
});
} catch (e) {
console.warn(

View file

@ -67,7 +67,7 @@ class ContentProcessStartup {
this.receiveMessage
);
Services.cpmm.addMessageListener(
"debug:add-session-data-entry",
"debug:add-or-set-session-data-entry",
this.receiveMessage
);
Services.cpmm.addMessageListener(
@ -92,7 +92,7 @@ class ContentProcessStartup {
this.receiveMessage
);
Services.cpmm.removeMessageListener(
"debug:add-session-data-entry",
"debug:add-or-set-session-data-entry",
this.receiveMessage
);
Services.cpmm.removeMessageListener(
@ -118,11 +118,12 @@ class ContentProcessStartup {
case "debug:destroy-target":
this.destroyTarget(msg.data.watcherActorID);
break;
case "debug:add-session-data-entry":
this.addSessionDataEntry(
case "debug:add-or-set-session-data-entry":
this.addOrSetSessionDataEntry(
msg.data.watcherActorID,
msg.data.type,
msg.data.entries
msg.data.entries,
msg.data.updateType
);
break;
case "debug:remove-session-data-entry":
@ -234,7 +235,7 @@ class ContentProcessStartup {
// Pass initialization data to the target actor
for (const type in sessionData) {
actor.addSessionDataEntry(type, sessionData[type]);
actor.addOrSetSessionDataEntry(type, sessionData[type], false, "set");
}
}
@ -250,7 +251,7 @@ class ContentProcessStartup {
this._connections.delete(watcherActorID);
}
async addSessionDataEntry(watcherActorID, type, entries) {
async addOrSetSessionDataEntry(watcherActorID, type, entries, updateType) {
const connectionInfo = this._connections.get(watcherActorID);
if (!connectionInfo) {
throw new Error(
@ -258,8 +259,8 @@ class ContentProcessStartup {
);
}
const { actor } = connectionInfo;
await actor.addSessionDataEntry(type, entries);
Services.cpmm.sendAsyncMessage("debug:add-session-data-entry-done", {
await actor.addOrSetSessionDataEntry(type, entries, false, updateType);
Services.cpmm.sendAsyncMessage("debug:add-or-set-session-data-entry-done", {
watcherActorID,
});
}

View file

@ -115,21 +115,29 @@ this.addEventListener("message", async function (event) {
for (const [type, entries] of Object.entries(
packet.options.sessionData
)) {
promises.push(workerTargetActor.addSessionDataEntry(type, entries));
promises.push(
workerTargetActor.addOrSetSessionDataEntry(
type,
entries,
false,
"set"
)
);
}
await Promise.all(promises);
}
break;
case "add-session-data-entry":
case "add-or-set-session-data-entry":
await connections
.get(packet.forwardingPrefix)
.workerTargetActor.addSessionDataEntry(
.workerTargetActor.addOrSetSessionDataEntry(
packet.dataEntryType,
packet.entries
packet.entries,
packet.updateType
);
postMessage(JSON.stringify({ type: "session-data-entry-added" }));
postMessage(JSON.stringify({ type: "session-data-entry-added-or-set" }));
break;
case "remove-session-data-entry":

View file

@ -18,30 +18,45 @@ function run_test() {
[TARGETS]: [],
};
SessionDataHelpers.addSessionDataEntry(sessionData, TARGETS, [
"frame",
"worker",
]);
info("Test adding a new entry");
SessionDataHelpers.addOrSetSessionDataEntry(
sessionData,
TARGETS,
["frame", "worker"],
"add"
);
deepEqual(
sessionData[TARGETS],
["frame", "worker"],
"the two elements were added"
);
SessionDataHelpers.addSessionDataEntry(sessionData, TARGETS, ["frame"]);
info("Test adding a duplicated entry");
SessionDataHelpers.addOrSetSessionDataEntry(
sessionData,
TARGETS,
["frame"],
"add"
);
deepEqual(
sessionData[TARGETS],
["frame", "worker"],
"addSessionDataEntry ignore duplicates"
"addOrSetSessionDataEntry ignore duplicates"
);
SessionDataHelpers.addSessionDataEntry(sessionData, TARGETS, ["process"]);
SessionDataHelpers.addOrSetSessionDataEntry(
sessionData,
TARGETS,
["process"],
"add"
);
deepEqual(
sessionData[TARGETS],
["frame", "worker", "process"],
"the third element is added"
);
info("Test removing an existing entry");
let removed = SessionDataHelpers.removeSessionDataEntry(
sessionData,
TARGETS,
@ -54,6 +69,7 @@ function run_test() {
"the element has been remove"
);
info("Test removing non-existing entry");
removed = SessionDataHelpers.removeSessionDataEntry(sessionData, TARGETS, [
"not-existing",
]);
@ -76,4 +92,33 @@ function run_test() {
"removedSessionDataEntry returned true as elements have been removed"
);
deepEqual(sessionData[TARGETS], [], "all elements were removed");
info("Test settting instead of adding data entries");
SessionDataHelpers.addOrSetSessionDataEntry(
sessionData,
TARGETS,
["frame"],
"add"
);
deepEqual(sessionData[TARGETS], ["frame"], "frame was re-added");
SessionDataHelpers.addOrSetSessionDataEntry(
sessionData,
TARGETS,
["process", "worker"],
"set"
);
deepEqual(
sessionData[TARGETS],
["process", "worker"],
"frame was replaced by process and worker"
);
info("Test setting an empty array");
SessionDataHelpers.addOrSetSessionDataEntry(sessionData, TARGETS, [], "set");
deepEqual(
sessionData[TARGETS],
[],
"Setting an empty array of entries clears the data entry"
);
}

View file

@ -74,7 +74,7 @@ add_task(async function () {
info("Dynamically add a breakpoint after the debugger statement");
const breakpointsFront = await watcher.getBreakpointListActor();
await breakpointsFront.setBreakpoint(
{ sourceUrl: testFile.path, line: 11 },
{ sourceUrl: testFile.path, line: 11, column: 0 },
{}
);

View file

@ -1,6 +1,7 @@
"use strict";
console.log("script evaluation");
console.log("Here is a SAB", new SharedArrayBuffer(1024));
addEventListener("install", function (evt) {
console.log("install event");

View file

@ -79,6 +79,12 @@ const expectedConsoleCalls = [
filename: /helper_serviceworker/,
arguments: ['script evaluation'],
},
{
level: "log",
filename: /helper_serviceworker/,
// Note that the second argument isn't a SharedArrayBuffer, but a DevTools "Object Front" instance.
arguments: ['Here is a SAB', { class : "SharedArrayBuffer" }],
},
{
level: "log",
filename: /helper_serviceworker/,
@ -108,7 +114,11 @@ const startTest = async function () {
await new Promise(resolve => {
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.enabled", true],
["devtools.webconsole.filter.serviceworkers", true]
["devtools.webconsole.filter.serviceworkers", true],
[
"dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled",
true
],
]}, resolve);
});

View file

@ -27,6 +27,12 @@ const firstTabExpectedCalls = [
filename: /helper_serviceworker/,
arguments: ['script evaluation'],
}
}, {
message: {
level: "log",
filename: /helper_serviceworker/,
arguments: ['Here is a SAB'],
}
}, {
message: {
level: "log",
@ -58,7 +64,11 @@ const startTest = async function () {
await new Promise(resolve => {
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.enabled", true],
["devtools.webconsole.filter.serviceworkers", true]
["devtools.webconsole.filter.serviceworkers", true],
[
"dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled",
true
],
]}, resolve);
});

View file

@ -34,7 +34,7 @@ Windows dependencies
All the commands of this tutorial must be run in the shell provided with the MozillaBuild Package (start-shell.bat)
:ref:`More information <Building Firefox On Windows>`
:ref:`More information on building Firefox on Windows <Building Firefox On Windows>`
Bootstrap a copy of the Firefox source code
-------------------------------------------
@ -58,7 +58,7 @@ To Setup Firefox On Windows
$ wget https://hg.mozilla.org/mozilla-central/raw-file/default/python/mozboot/bin/bootstrap.py
$ python3 bootstrap.py
More information :ref:`for Windows <Building Firefox On Windows>`
More information on :ref:`building Firefox for Windows <Building Firefox On Windows>`.
To Setup Firefox On macOS and Linux
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -68,7 +68,7 @@ To Setup Firefox On macOS and Linux
$ curl https://hg.mozilla.org/mozilla-central/raw-file/default/python/mozboot/bin/bootstrap.py -O
$ python3 bootstrap.py
More information :ref:`for Linux <Building Firefox On Linux>` and :ref:`for MacOS <Building Firefox On MacOS>`
More information on :ref:`building Firefox for Linux <Building Firefox On Linux>` and :ref:`building Firefox for MacOS <Building Firefox On MacOS>`.
To set up your editor
---------------------
@ -111,7 +111,11 @@ To run it:
$ ./mach run
:ref:`More information about Linux <Building Firefox On Linux>` / :ref:`More information about MacOS <Building Firefox On MacOS>`
This command will open your locally built Firefox in a new window.
:ref:`More information about building Firefox on Linux <Building Firefox On Linux>` / :ref:`More information about building Firefox on MacOS <Building Firefox On MacOS>`
If you encounter build errors, please reference the more detailed "Building Firefox" on your specific operating system document and specifically the "Troubleshooting" section.
.. _write_a_patch:

View file

@ -124,9 +124,19 @@ Now that your system is bootstrapped, you should be able to build!
cd mozilla-unified
hg up -C central
./mach build
./mach run
🎉 Congratulations! You've built your own home-grown Firefox!
You should see the following message in your terminal after a successful build:
.. code-block:: console
Your build was successful!
To take your build for a test drive, run: |mach run|
For more information on what to do now, see https://firefox-source-docs.mozilla.org/setup/contributing_code.html
You can now use the ``./mach run`` command to run your locally built Firefox!
If your build fails, please reference the steps in the `Troubleshooting section <#troubleshooting>`_.
Now the fun starts
------------------
@ -141,6 +151,14 @@ send patches to Mozilla, update your source code locally, and more.
Troubleshooting
---------------
Build errors
~~~~~~~~~~~~
If you encounter a build error when trying to setup your development environment, please follow these steps:
1. Copy the entire build error to your clipboard
2. Paste this error to `paste.mozilla.org <https://paste.mozilla.org>`_ in the text area and change the "Expire in one hour" option to "Expire in one week". Note: it won't take a week to get help but it's better to have the snippet be around for a bit longer than expected.
3. Go to the `introduction channel <https://chat.mozilla.org/#/room/#introduction:mozilla.org>`__ and ask for help with your build error. Make sure to post the link to the paste.mozilla.org snippet you created!
Using a non-native file system (NTFS, network drive, etc)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -104,9 +104,19 @@ Now that your system is bootstrapped, you should be able to build!
cd mozilla-unified
hg up -C central
./mach build
./mach run
🎉 Congratulations! You've built your own home-grown Firefox!
You should see the following message in your terminal after a successful build:
.. code-block:: console
Your build was successful!
To take your build for a test drive, run: |mach run|
For more information on what to do now, see https://firefox-source-docs.mozilla.org/setup/contributing_code.html
You can now use the ``./mach run`` command to run your locally built Firefox!
If your build fails, please reference the steps in the `Troubleshooting section <#troubleshooting>`_.
Now the fun starts
------------------
@ -117,3 +127,14 @@ say hello in the `Introduction channel
start working on <https://codetribute.mozilla.org/>`_.
See the :ref:`Firefox Contributors' Quick Reference` to learn how to test your changes,
send patches to Mozilla, update your source code locally, and more.
Troubleshooting
---------------
Build errors
~~~~~~~~~~~~
If you encounter a build error when trying to setup your development environment, please follow these steps:
1. Copy the entire build error to your clipboard
2. Paste this error to `paste.mozilla.org <https://paste.mozilla.org>`_ in the text area and change the "Expire in one hour" option to "Expire in one week". Note: it won't take a week to get help but it's better to have the snippet be around for a bit longer than expected.
3. Go to the `introduction channel <https://chat.mozilla.org/#/room/#introduction:mozilla.org>`__ and ask for help with your build error. Make sure to post the link to the paste.mozilla.org snippet you created!

View file

@ -1,5 +1,5 @@
Building Firefox On Windows
===========================
======================================
This document will help you get set up to build Firefox on your own
computer. Getting set up can take a while - we need to download a
@ -104,10 +104,12 @@ Microsoft Defender Antivirus manually
.. note::
If you're already missing files (you'll see them listed in ``hg status``, you can have them
brought back by reverting your source tree: ``hg update -C``).
If you are using Mercurial and you're already missing files (you'll see them listed in ``hg status``), you can have them
brought back by reverting your source tree: ``hg update -C``.
3. Build
If you are using Git and you're already missing files (you'll see them listed in ``git status``), you can have them brought back by discarding changes in your source tree: ``git restore .``.
1. Build
--------
Now that your system is bootstrapped, you should be able to build!
@ -117,9 +119,19 @@ Now that your system is bootstrapped, you should be able to build!
cd c:/mozilla-source/mozilla-unified
hg up -C central
./mach build
./mach run
🎉 Congratulations! You've built your own home-grown Firefox!
You should see the following message in your terminal after a successful build:
.. code-block:: console
Your build was successful!
To take your build for a test drive, run: |mach run|
For more information on what to do now, see https://firefox-source-docs.mozilla.org/setup/contributing_code.html
You can now use the ``./mach run`` command to run your locally built Firefox!
If your build fails, please reference the steps in the `Troubleshooting section <#troubleshooting>`_.
Now the fun starts
------------------
@ -140,6 +152,14 @@ send patches to Mozilla, update your source code locally, and more.
Troubleshooting
---------------
Build errors
~~~~~~~~~~~~
If you encounter a build error when trying to setup your development environment, please follow these steps:
1. Copy the entire build error to your clipboard
2. Paste this error on `paste.mozilla.org <https://paste.mozilla.org>`_ in the text area and change the "Expire in one hour" option to "Expire in one week". Note: it won't take a week to get help but it's better to have the snippet be around for a bit longer than expected.
3. Go to the `introduction channel <https://chat.mozilla.org/#/room/#introduction:mozilla.org>`__ and ask for help with your build error. Make sure to post the link to the paste.mozilla.org snippet you created!
MozillaBuild out-of-date
~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -2174,6 +2174,14 @@ void Document::AccumulatePageLoadTelemetry(
}
}
// Report the most up to date LCP time. For our histogram we actually report
// this on page unload.
if (TimeStamp lcpTime =
GetNavigationTiming()->GetLargestContentfulRenderTimeStamp()) {
aEventTelemetryDataOut.lcpTime = mozilla::Some(
static_cast<uint32_t>((lcpTime - navigationStart).ToMilliseconds()));
}
// DOM Content Loaded event
if (TimeStamp dclEventStart =
GetNavigationTiming()->GetDOMContentLoadedEventStartTimeStamp()) {
@ -8710,15 +8718,11 @@ already_AddRefed<nsSimpleContentList> Document::BlockedNodesByClassifier()
const {
RefPtr<nsSimpleContentList> list = new nsSimpleContentList(nullptr);
const nsTArray<nsWeakPtr> blockedNodes = mBlockedNodesByClassifier.Clone();
for (unsigned long i = 0; i < blockedNodes.Length(); i++) {
nsWeakPtr weakNode = blockedNodes[i];
nsCOMPtr<nsIContent> node = do_QueryReferent(weakNode);
// Consider only nodes to which we have managed to get strong references.
// Coping with nullptrs since it's expected for nodes to disappear when
// nobody else is referring to them.
if (node) {
for (const nsWeakPtr& weakNode : mBlockedNodesByClassifier) {
if (nsCOMPtr<nsIContent> node = do_QueryReferent(weakNode)) {
// Consider only nodes to which we have managed to get strong references.
// Coping with nullptrs since it's expected for nodes to disappear when
// nobody else is referring to them.
list->AppendElement(node);
}
}
@ -11465,6 +11469,7 @@ void Document::Destroy() {
}
ReportDocumentUseCounters();
ReportLCP();
SetDevToolsWatchingDOMMutations(false);
mIsGoingAway = true;
@ -16180,6 +16185,42 @@ void Document::ReportDocumentUseCounters() {
}
}
void Document::ReportLCP() {
const nsDOMNavigationTiming* timing = GetNavigationTiming();
if (!timing) {
return;
}
TimeStamp lcpTime = timing->GetLargestContentfulRenderTimeStamp();
if (!lcpTime) {
return;
}
mozilla::glean::perf::largest_contentful_paint.AccumulateRawDuration(
lcpTime - timing->GetNavigationStartTimeStamp());
if (!GetChannel()) {
return;
}
nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(GetChannel()));
if (!timedChannel) {
return;
}
TimeStamp responseStart;
timedChannel->GetResponseStart(&responseStart);
if (!responseStart) {
return;
}
mozilla::glean::perf::largest_contentful_paint_from_response_start
.AccumulateRawDuration(lcpTime - responseStart);
}
void Document::SendPageUseCounters() {
if (!mShouldReportUseCounters || !mShouldSendPageUseCounters) {
return;
@ -18651,13 +18692,7 @@ ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const {
}
}
// NOTE(emilio): We use IsInChromeDocShell rather than IsChromeDoc
// intentionally, to make chrome documents in content docshells (like about
// pages) use the content color scheme.
if (IsInChromeDocShell()) {
return LookAndFeel::ColorSchemeForChrome();
}
return LookAndFeel::PreferredColorSchemeForContent();
return PreferenceSheet::PrefsFor(*this).mColorScheme;
}
bool Document::HasRecentlyStartedForegroundLoads() {

View file

@ -1344,6 +1344,16 @@ class Document : public nsINode,
Maybe<ClientState> GetClientState() const;
Maybe<ServiceWorkerDescriptor> GetController() const;
// Given a node, get a weak reference to it and append that reference to
// mBlockedNodesByClassifier. Can be used later on to look up a node in it.
// (e.g., by the UI)
// /
void AddBlockedNodeByClassifier(nsINode* aNode) {
if (aNode) {
mBlockedNodesByClassifier.AppendElement(do_GetWeakReference(aNode));
}
}
// Returns the size of the mBlockedNodesByClassifier array.
//
// This array contains nodes that have been blocked to prevent user tracking,
@ -1359,17 +1369,14 @@ class Document : public nsINode,
// Weak references to blocked nodes are added in the mBlockedNodesByClassifier
// array but they are not removed when those nodes are removed from the tree
// or even garbage collected.
long BlockedNodeByClassifierCount() const {
size_t BlockedNodeByClassifierCount() const {
return mBlockedNodesByClassifier.Length();
}
//
// Returns strong references to mBlockedNodesByClassifier. (Document.h)
//
// This array contains nodes that have been blocked to prevent
// user tracking. They most likely have had their nsIChannel
// canceled by the URL classifier (Safebrowsing).
//
already_AddRefed<nsSimpleContentList> BlockedNodesByClassifier() const;
// Helper method that returns true if the document has storage-access sandbox
@ -3566,23 +3573,6 @@ class Document : public nsINode,
inline ImageDocument* AsImageDocument();
inline const ImageDocument* AsImageDocument() const;
/*
* Given a node, get a weak reference to it and append that reference to
* mBlockedNodesByClassifier. Can be used later on to look up a node in it.
* (e.g., by the UI)
*/
void AddBlockedNodeByClassifier(nsINode* node) {
if (!node) {
return;
}
nsWeakPtr weakNode = do_GetWeakReference(node);
if (weakNode) {
mBlockedNodesByClassifier.AppendElement(weakNode);
}
}
gfxUserFontSet* GetUserFontSet();
void FlushUserFontSet();
void MarkUserFontSetDirty();
@ -3607,6 +3597,10 @@ class Document : public nsINode,
// effect once per document, and so is called during document destruction.
void ReportDocumentUseCounters();
// Reports largest contentful paint via telemetry. We want the most up to
// date value for LCP and so this is called during document destruction.
void ReportLCP();
// Report how lazyload performs for this document.
void ReportDocumentLazyLoadCounters();

View file

@ -43,6 +43,10 @@ class PlacesBookmarkAddition final : public PlacesBookmark {
event->mLastVisitDate.SetNull();
}
event->mTargetFolderItemId = aInitDict.mTargetFolderItemId;
event->mTargetFolderGuid = aInitDict.mTargetFolderGuid;
event->mTargetFolderTitle = aInitDict.mTargetFolderTitle;
return event.forget();
}
@ -63,6 +67,9 @@ class PlacesBookmarkAddition final : public PlacesBookmark {
bool Hidden() { return mHidden; }
uint32_t VisitCount() { return mVisitCount; }
Nullable<uint64_t> GetLastVisitDate() { return mLastVisitDate; }
uint64_t TargetFolderItemId() { return mTargetFolderItemId; }
void GetTargetFolderGuid(nsCString& aGuid) { aGuid = mTargetFolderGuid; }
void GetTargetFolderTitle(nsString& aTitle) { aTitle = mTargetFolderTitle; }
int32_t mIndex;
nsString mTitle;
@ -72,6 +79,9 @@ class PlacesBookmarkAddition final : public PlacesBookmark {
bool mHidden;
uint32_t mVisitCount;
Nullable<uint64_t> mLastVisitDate;
int64_t mTargetFolderItemId;
nsCString mTargetFolderGuid;
nsString mTargetFolderTitle;
private:
~PlacesBookmarkAddition() = default;

View file

@ -57,6 +57,7 @@ void nsDOMNavigationTiming::Clear() {
mDOMContentLoadedEventEnd = TimeStamp();
mDOMComplete = TimeStamp();
mContentfulComposite = TimeStamp();
mLargestContentfulRender = TimeStamp();
mNonBlankPaint = TimeStamp();
mDocShellHasBeenActiveSinceNavigationStart = false;
@ -478,6 +479,16 @@ void nsDOMNavigationTiming::NotifyContentfulCompositeForRootContentDocument(
}
}
void nsDOMNavigationTiming::NotifyLargestContentfulRenderForRootContentDocument(
const DOMHighResTimeStamp& aRenderTime) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mNavigationStart.IsNull());
// This can get called multiple times and updates over time.
mLargestContentfulRender =
mNavigationStart + TimeDuration::FromMilliseconds(aRenderTime);
}
void nsDOMNavigationTiming::NotifyDOMContentFlushedForRootContentDocument() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mNavigationStart.IsNull());

View file

@ -71,6 +71,10 @@ class nsDOMNavigationTiming final : public mozilla::RelativeTimeline {
return mContentfulComposite;
}
mozilla::TimeStamp GetLargestContentfulRenderTimeStamp() const {
return mLargestContentfulRender;
}
DOMTimeMilliSec GetUnloadEventStart() {
return TimeStampToDOM(GetUnloadEventStartTimeStamp());
}
@ -104,6 +108,9 @@ class nsDOMNavigationTiming final : public mozilla::RelativeTimeline {
DOMTimeMilliSec GetTimeToContentfulComposite() const {
return TimeStampToDOM(mContentfulComposite);
}
DOMTimeMilliSec GetTimeToLargestContentfulRender() const {
return TimeStampToDOM(mLargestContentfulRender);
}
DOMTimeMilliSec GetTimeToTTFI() const { return TimeStampToDOM(mTTFI); }
DOMTimeMilliSec GetTimeToDOMContentFlushed() const {
return TimeStampToDOM(mDOMContentFlushed);
@ -172,6 +179,8 @@ class nsDOMNavigationTiming final : public mozilla::RelativeTimeline {
void NotifyNonBlankPaintForRootContentDocument();
void NotifyContentfulCompositeForRootContentDocument(
const mozilla::TimeStamp& aCompositeEndTime);
void NotifyLargestContentfulRenderForRootContentDocument(
const DOMHighResTimeStamp& aRenderTime);
void NotifyDOMContentFlushedForRootContentDocument();
void NotifyDocShellStateChanged(DocShellState aDocShellState);
@ -230,6 +239,7 @@ class nsDOMNavigationTiming final : public mozilla::RelativeTimeline {
mozilla::TimeStamp mNavigationStart;
mozilla::TimeStamp mNonBlankPaint;
mozilla::TimeStamp mContentfulComposite;
mozilla::TimeStamp mLargestContentfulRender;
mozilla::TimeStamp mDOMContentFlushed;
mozilla::TimeStamp mBeforeUnloadStart;

View file

@ -220,7 +220,10 @@ support-files = ["captureStream_common.js"]
["test_capture_throttled.html"]
support-files = ["captureStream_common.js"]
skip-if = ["os == 'android'"] # bug 1752351
skip-if = [
"os == 'android'", # Bug 1752351
"os == 'win'", # Bug 1777094
]
["test_drawImageIncomplete.html"]

View file

@ -1,3 +1,7 @@
/* 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 https://mozilla.org/MPL/2.0/. */
enum PlacesEventType {
"none",
@ -211,6 +215,9 @@ dictionary PlacesBookmarkAdditionInit {
required boolean hidden;
required unsigned long visitCount;
required unsigned long long? lastVisitDate;
required long long targetFolderItemId;
required ByteString? targetFolderGuid;
required DOMString? targetFolderTitle;
};
[ChromeOnly, Exposed=Window]
@ -256,6 +263,22 @@ interface PlacesBookmarkAddition : PlacesBookmark {
* Date of the last visit, in milliseconds since epoch.
*/
readonly attribute unsigned long long? lastVisitDate;
/**
* If this is a folder shortcut, the id of the target folder.
*/
readonly attribute long long targetFolderItemId;
/**
* If this is a folder shortcut, the unique ID associated with the target folder.
*/
readonly attribute ByteString targetFolderGuid;
/**
* If this is a folder shortcut, the title of the target folder.
*/
readonly attribute DOMString targetFolderTitle;
};
dictionary PlacesBookmarkRemovedInit {

View file

@ -328,8 +328,14 @@ class ConsoleRunnable : public StructuredCloneHolderBase {
ConsoleCommon::ClearException ce(aCx);
// This is the same policy as when writing from the other side, in
// WriteData.
JS::CloneDataPolicy cloneDataPolicy;
cloneDataPolicy.allowIntraClusterClonableSharedObjects();
cloneDataPolicy.allowSharedMemoryObjects();
JS::Rooted<JS::Value> argumentsValue(aCx);
if (!Read(aCx, &argumentsValue)) {
if (!Read(aCx, &argumentsValue, cloneDataPolicy)) {
return;
}

View file

@ -4274,7 +4274,7 @@ static CursorImage ComputeCustomCursor(nsPresContext* aPresContext,
: Nothing();
gfx::IntPoint hotspot = ComputeHotspot(container, specifiedHotspot);
CursorImage result{hotspot, std::move(container),
image.image.GetResolution(), loading};
image.image.GetResolution(style), loading};
if (ShouldBlockCustomCursor(aPresContext, aEvent, result)) {
continue;
}

View file

@ -218,7 +218,6 @@
#include "nsIScriptSecurityManager.h"
#include "nsIServiceWorkerManager.h"
#include "nsISiteSecurityService.h"
#include "nsISound.h"
#include "nsIStringBundle.h"
#include "nsITimer.h"
#include "nsIURL.h"
@ -3631,47 +3630,6 @@ ContentParent::AllocPClipboardWriteRequestParent(
return request.forget();
}
mozilla::ipc::IPCResult ContentParent::RecvPlaySound(nsIURI* aURI) {
// If the check here fails, it can only mean that this message was spoofed.
if (!aURI || !aURI->SchemeIs("chrome")) {
// PlaySound only accepts a valid chrome URI.
return IPC_FAIL(this, "Invalid aURI passed.");
}
nsCOMPtr<nsIURL> soundURL(do_QueryInterface(aURI));
if (!soundURL) {
return IPC_OK();
}
nsresult rv;
nsCOMPtr<nsISound> sound(do_GetService(NS_SOUND_CID, &rv));
NS_ENSURE_SUCCESS(rv, IPC_OK());
sound->Play(soundURL);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvBeep() {
nsresult rv;
nsCOMPtr<nsISound> sound(do_GetService(NS_SOUND_CID, &rv));
NS_ENSURE_SUCCESS(rv, IPC_OK());
sound->Beep();
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvPlayEventSound(
const uint32_t& aEventId) {
nsresult rv;
nsCOMPtr<nsISound> sound(do_GetService(NS_SOUND_CID, &rv));
NS_ENSURE_SUCCESS(rv, IPC_OK());
sound->PlayEventSound(aEventId);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvGetIconForExtension(
const nsACString& aFileExt, const uint32_t& aIconSize,
nsTArray<uint8_t>* bits) {

View file

@ -1002,10 +1002,6 @@ class ContentParent final : public PContentParent,
already_AddRefed<PClipboardWriteRequestParent>
AllocPClipboardWriteRequestParent(const int32_t& aClipboardType);
mozilla::ipc::IPCResult RecvPlaySound(nsIURI* aURI);
mozilla::ipc::IPCResult RecvBeep();
mozilla::ipc::IPCResult RecvPlayEventSound(const uint32_t& aEventId);
mozilla::ipc::IPCResult RecvGetIconForExtension(const nsACString& aFileExt,
const uint32_t& aIconSize,
nsTArray<uint8_t>* bits);

View file

@ -1240,12 +1240,6 @@ parent:
*/
async PClipboardWriteRequest(int32_t aClipboardType);
// 'Play', 'Beep' and 'PlayEventSound' are the only nsISound methods used in
// the content process.
[Compress] async PlaySound(nullable nsIURI aURL);
[Compress] async Beep();
[Compress] async PlayEventSound(uint32_t aEventId);
sync GetIconForExtension(nsCString aFileExt, uint32_t aIconSize)
returns (uint8_t[] bits);

View file

@ -18,6 +18,7 @@ struct ParamTraits<mozilla::glean::perf::PageLoadExtra> {
static void Write(MessageWriter* aWriter, const paramType& aParam) {
WriteParam(aWriter, aParam.fcpTime);
WriteParam(aWriter, aParam.lcpTime);
WriteParam(aWriter, aParam.jsExecTime);
WriteParam(aWriter, aParam.loadTime);
WriteParam(aWriter, aParam.loadType);
@ -32,6 +33,7 @@ struct ParamTraits<mozilla::glean::perf::PageLoadExtra> {
static bool Read(MessageReader* aReader, paramType* aResult) {
return ReadParam(aReader, &aResult->fcpTime) &&
ReadParam(aReader, &aResult->lcpTime) &&
ReadParam(aReader, &aResult->jsExecTime) &&
ReadParam(aReader, &aResult->loadTime) &&
ReadParam(aReader, &aResult->loadType) &&

View file

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "js/ForOfIterator.h" // JS::ForOfIterator
#include "js/JSON.h" // JS_ParseJSON
#include "json/json.h"
#include "nsContentUtils.h"
#include "nsIScriptError.h"
#include "DOMLocalization.h"
@ -646,13 +646,11 @@ void DOMLocalization::ConvertStringToL10nArgs(const nsString& aInput,
// There are no properties.
return;
}
// This method uses a temporary dictionary to automate
// converting a JSON string into an IDL Record via a dictionary.
//
// Once we get Record::Init(const nsAString& aJSON), we'll switch to
// that.
L10nArgsHelperDict helperDict;
if (!helperDict.Init(u"{\"args\": "_ns + aInput + u"}"_ns)) {
Json::Value args;
Json::Reader jsonReader;
if (!jsonReader.parse(NS_ConvertUTF16toUTF8(aInput).get(), args, false)) {
nsTArray<nsCString> errors{
"[dom/l10n] Failed to parse l10n-args JSON: "_ns +
NS_ConvertUTF16toUTF8(aInput),
@ -660,13 +658,43 @@ void DOMLocalization::ConvertStringToL10nArgs(const nsString& aInput,
MaybeReportErrorsToGecko(errors, aRv, GetParentObject());
return;
}
for (auto& entry : helperDict.mArgs.Entries()) {
if (!args.isObject()) {
nsTArray<nsCString> errors{
"[dom/l10n] Failed to parse l10n-args JSON: "_ns +
NS_ConvertUTF16toUTF8(aInput),
};
MaybeReportErrorsToGecko(errors, aRv, GetParentObject());
return;
}
for (Json::ValueConstIterator iter = args.begin(); iter != args.end();
++iter) {
L10nArgs::EntryType* newEntry = aRetVal.Entries().AppendElement(fallible);
if (!newEntry) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
newEntry->mKey = entry.mKey;
newEntry->mValue = entry.mValue;
newEntry->mKey = iter.name().c_str();
if (iter->isString()) {
newEntry->mValue.SetValue().RawSetAsUTF8String().Assign(
iter->asString().c_str(), iter->asString().length());
} else if (iter->isDouble()) {
newEntry->mValue.SetValue().RawSetAsDouble() = iter->asDouble();
} else if (iter->isBool()) {
if (iter->asBool()) {
newEntry->mValue.SetValue().RawSetAsUTF8String().Assign("true");
} else {
newEntry->mValue.SetValue().RawSetAsUTF8String().Assign("false");
}
} else if (iter->isNull()) {
newEntry->mValue.SetNull();
} else {
nsTArray<nsCString> errors{
"[dom/l10n] Failed to convert l10n-args JSON: "_ns +
NS_ConvertUTF16toUTF8(aInput),
};
MaybeReportErrorsToGecko(errors, aRv, GetParentObject());
}
}
}

View file

@ -23,6 +23,7 @@ UNIFIED_SOURCES += [
LOCAL_INCLUDES += [
"/dom/base",
"/toolkit/components/jsoncpp/include",
]
FINAL_LIBRARY = "xul"

View file

@ -28,6 +28,8 @@
["dom_localization/test_getAttributes.html"]
["dom_localization/test_l10n_args.html"]
["dom_localization/test_l10n_mutations.html"]
["dom_localization/test_overlay.html"]

View file

@ -0,0 +1,128 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test DOMLocalization's data-l10n-args handling</title>
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
const l10nReg = new L10nRegistry();
const fs = [
{ path: "/localization/en-US/mock.ftl", source: `
test-simple = Hello { $prop }
test-selector = { $prop ->
[true] YES
*[other] NO
}
test-error = Hey
` },
];
const source = L10nFileSource.createMock("test", "app", ["en-US"], "/localization/{locale}", fs);
l10nReg.registerSources([source]);
const tests = [
// [data-l10n-id, data-l10n-args, expected text content],
[`test-simple`, `{ "prop": "str" }`, `Hello str`],
[`test-simple`, `{ "prop": 10 }`, `Hello 10`],
[`test-simple`, `{ "prop": 10.1 }`, `Hello 10.1`],
[`test-simple`, `{ "prop": -2 }`, `Hello -2`],
[`test-simple`, `{ "prop": true }`, `Hello true`],
[`test-simple`, `{ "prop": false }`, `Hello false`],
[`test-selector`, `{ "prop": "true" }`, `YES`],
[`test-selector`, `{ "prop": "false" }`, `NO`],
[`test-selector`, `{ "prop": 10 }`, `NO`],
[`test-selector`, `{ "prop": true }`, `YES`],
[`test-selector`, `{ "prop": false }`, `NO`],
// Passing null is also valid as long as the property is not referred.
[`test-simple`, `{ "prop": "str", "prop2": null }`, `Hello str`],
];
const errorTests = [
// Unexpected top-level value.
`10`,
`"foo"`,
`true`,
`false`,
`[]`,
// Unsupported property value types.
`{ "prop": [] }`,
`{ "prop": {} }`,
// Syntax Error.
`a`,
`"`,
`'foo'`,
`{ prop: 10 }`,
`{ "prop" }`,
`{ "prop": }`,
`{ "prop": "a }`,
`[`,
`}`,
`{`,
`}`,
`{ "prop": [ }`,
`{ "prop": { }`,
`{ "prop": 10, }`,
];
window.onload = async function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
[],
false,
l10nReg,
["en-US"],
);
const container = document.querySelector("#test-container");
domLoc.addResourceIds(["/mock.ftl"]);
domLoc.connectRoot(document.body);
for (const [id, args, expected] of tests) {
const span = document.createElement("span");
span.setAttribute("data-l10n-id", id);
span.setAttribute("data-l10n-args", args);
container.append(span);
await domLoc.translateRoots();
is(span.textContent, expected, `translation for "${id}" with ${args} should become "${expected}"`);
span.remove();
}
for (const args of errorTests) {
const span = document.createElement("span");
span.setAttribute("data-l10n-id", "test-error");
span.setAttribute("data-l10n-args", args);
container.append(span);
let thrown = false;
try {
await domLoc.translateRoots();
} catch (e) {
thrown = true;
}
ok(thrown, `${args} should throw error`);
span.remove();
}
SimpleTest.finish();
};
</script>
</head>
<body>
<div id="test-container">
</div>
</body>
</html>

View file

@ -1,3 +1,7 @@
/* 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 https://mozilla.org/MPL/2.0/. */
#include "nsIThread.idl"
#include "nsIDOMWindow.idl"
#include "nsIPropertyBag2.idl"

View file

@ -11,6 +11,32 @@ $tags:
- 'Core :: DOM: Core & HTML'
perf:
largest_contentful_paint:
type: timing_distribution
time_unit: millisecond
description: >
Time from navigation start to largest contentful paint.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1862939
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1862939#c5
notification_emails:
- perf-telemetry-alerts@mozilla.com
expires: never
telemetry_mirror: PERF_LARGEST_CONTENTFUL_PAINT_MS
largest_contentful_paint_from_response_start:
type: timing_distribution
time_unit: millisecond
description: >
Time from response start to largest contentful paint.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1862939
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1862939#c5
notification_emails:
- perf-telemetry-alerts@mozilla.com
expires: never
telemetry_mirror: PERF_LARGEST_CONTENTFUL_PAINT_FROM_RESPONSE_START_MS
page_load:
type: event
description: >
@ -19,10 +45,12 @@ perf:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1759744
- https://bugzilla.mozilla.org/show_bug.cgi?id=1799727
- https://bugzilla.mozilla.org/show_bug.cgi?id=1834774
- https://bugzilla.mozilla.org/show_bug.cgi?id=1862939
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1759744#c5
- https://bugzilla.mozilla.org/show_bug.cgi?id=1799727#c4
- https://bugzilla.mozilla.org/show_bug.cgi?id=1834774#c3
- https://bugzilla.mozilla.org/show_bug.cgi?id=1862939#c5
notification_emails:
- perf-telemetry-alerts@mozilla.com
- dpalmeiro@mozilla.com
@ -45,7 +73,12 @@ perf:
unit: ms
fcp_time:
description:
"Time between firstContentfulPaint and naviationStart, in ms."
"Time between firstContentfulPaint and navigationStart, in ms."
type: quantity
unit: ms
lcp_time:
description:
"Time between largestContentfulPaint and navigationStart, at the point of onLoad firing, in ms. This may differ from the final LCP value as reported through the LCP histogram."
type: quantity
unit: ms
js_exec_time:

View file

@ -12,6 +12,7 @@
#include "PerformanceMainThread.h"
#include "LargestContentfulPaint.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/DOMIntersectionObserver.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Element.h"
@ -54,12 +55,14 @@ LargestContentfulPaint::LargestContentfulPaint(
PerformanceMainThread* aPerformance, const DOMHighResTimeStamp aRenderTime,
const DOMHighResTimeStamp aLoadTime, const unsigned long aSize,
nsIURI* aURI, Element* aElement,
const Maybe<const LCPImageEntryKey>& aLCPImageEntryKey)
const Maybe<const LCPImageEntryKey>& aLCPImageEntryKey,
bool aShouldExposeRenderTime)
: PerformanceEntry(aPerformance->GetParentObject(), u""_ns,
kLargestContentfulPaintName),
mPerformance(aPerformance),
mRenderTime(aRenderTime),
mLoadTime(aLoadTime),
mShouldExposeRenderTime(aShouldExposeRenderTime),
mSize(aSize),
mURI(aURI),
mElement(aElement),
@ -187,24 +190,11 @@ void LargestContentfulPaint::MaybeProcessImageForElementTiming(
DOMHighResTimeStamp nowTime =
performance->TimeStampToDOMHighResForRendering(TimeStamp::Now());
if (!request->IsData() && !request->ShouldReportRenderTimeForLCP()) {
// https://wicg.github.io/element-timing/#report-image-element-timing
LOG(" Added a pending image rendering (TAO FAILED)");
// For TAO failed requests, the renderTime is exposed as 0 for
// security reasons.
LCPHelpers::CreateLCPEntryForImage(performance, aElement, aRequest, nowTime,
0 /* aRenderTime */, entryKey);
return;
}
// Otherwise, add the triple (element, imageRequest, now) to roots images
// pending rendering.
//
// At this point, the loadTime of the image is known, but
// the renderTime is unknown, so it's added to ImagesPendingRendering
// as a placeholder, and the corresponding LCP entry will be created
// when the renderTime is known.
LOG(" Added a pending image rendering (TAO PASSED)");
LOG(" Added a pending image rendering");
performance->AddImagesPendingRendering(
ImagePendingRendering{entryKey, nowTime});
}
@ -270,6 +260,9 @@ void LCPHelpers::FinalizeLCPEntryForImage(
}
DOMHighResTimeStamp LargestContentfulPaint::RenderTime() const {
if (!mShouldExposeRenderTime) {
return 0;
}
return nsRFPService::ReduceTimePrecisionAsMSecs(
mRenderTime, mPerformance->GetRandomTimelineSeed(),
mPerformance->GetRTPCallerType());
@ -282,7 +275,8 @@ DOMHighResTimeStamp LargestContentfulPaint::LoadTime() const {
}
DOMHighResTimeStamp LargestContentfulPaint::StartTime() const {
DOMHighResTimeStamp startTime = !mRenderTime ? mLoadTime : mRenderTime;
DOMHighResTimeStamp startTime =
mShouldExposeRenderTime ? mRenderTime : mLoadTime;
return nsRFPService::ReduceTimePrecisionAsMSecs(
startTime, mPerformance->GetRandomTimelineSeed(),
mPerformance->GetRTPCallerType());
@ -299,6 +293,8 @@ Element* LargestContentfulPaint::GetContainingBlockForTextFrame(
void LargestContentfulPaint::QueueEntry() {
LOG("QueueEntry entry=%p", this);
mPerformance->QueueLargestContentfulPaintEntry(this);
ReportLCPToNavigationTimings();
}
void LargestContentfulPaint::GetUrl(nsAString& aUrl) {
@ -469,11 +465,20 @@ void LCPHelpers::CreateLCPEntryForImage(
nsCOMPtr<nsIURI> requestURI;
aRequestProxy->GetURI(getter_AddRefs(requestURI));
imgRequest* request = aRequestProxy->GetOwner();
// We should never get here unless request is valid.
MOZ_ASSERT(request);
bool taoPassed = request->IsData() || request->ShouldReportRenderTimeForLCP();
// https://wicg.github.io/element-timing/#report-image-element-timing
// For TAO failed requests, the renderTime is exposed as 0 for
// security reasons.
//
// At this point, we have all the information about the entry
// except the size.
RefPtr<LargestContentfulPaint> entry =
new LargestContentfulPaint(aPerformance, aRenderTime, aLoadTime, 0,
requestURI, aElement, Some(aImageEntryKey));
RefPtr<LargestContentfulPaint> entry = new LargestContentfulPaint(
aPerformance, aRenderTime, aLoadTime, 0, requestURI, aElement,
Some(aImageEntryKey), taoPassed);
LOG(" Upsert a LargestContentfulPaint entry=%p to LCPEntryMap.",
entry.get());
@ -496,8 +501,9 @@ void LCPHelpers::FinalizeLCPEntryForText(
aContainingBlock->SetFlags(ELEMENT_PROCESSED_BY_LCP_FOR_TEXT);
RefPtr<LargestContentfulPaint> entry = new LargestContentfulPaint(
aPerformance, aRenderTime, 0, 0, nullptr, aContainingBlock, Nothing());
RefPtr<LargestContentfulPaint> entry =
new LargestContentfulPaint(aPerformance, aRenderTime, 0, 0, nullptr,
aContainingBlock, Nothing(), true);
entry->UpdateSize(aContainingBlock, aTargetRectRelativeToSelf, aPerformance,
false);
@ -512,4 +518,31 @@ void LCPHelpers::FinalizeLCPEntryForText(
}
entry->QueueEntry();
}
void LargestContentfulPaint::ReportLCPToNavigationTimings() {
const Document* document = mElement->OwnerDoc();
MOZ_ASSERT(document);
nsDOMNavigationTiming* timing = document->GetNavigationTiming();
if (MOZ_UNLIKELY(!timing)) {
return;
}
if (document->IsResourceDoc()) {
return;
}
if (BrowsingContext* browsingContext = document->GetBrowsingContext()) {
if (browsingContext->GetEmbeddedInContentDocument()) {
return;
}
}
if (!document->IsTopLevelContentDocument()) {
return;
}
timing->NotifyLargestContentfulRenderForRootContentDocument(mRenderTime);
}
} // namespace mozilla::dom

View file

@ -168,12 +168,13 @@ class LargestContentfulPaint final : public PerformanceEntry {
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(LargestContentfulPaint,
PerformanceEntry)
LargestContentfulPaint(
PerformanceMainThread* aPerformance,
const DOMHighResTimeStamp aRenderTime,
const DOMHighResTimeStamp aLoadTime, const unsigned long aSize,
nsIURI* aURI, Element* aElement,
const Maybe<const LCPImageEntryKey>& aLCPImageEntryKey);
LargestContentfulPaint(PerformanceMainThread* aPerformance,
const DOMHighResTimeStamp aRenderTime,
const DOMHighResTimeStamp aLoadTime,
const unsigned long aSize, nsIURI* aURI,
Element* aElement,
const Maybe<const LCPImageEntryKey>& aLCPImageEntryKey,
bool aShouldExposeRenderTime);
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
@ -212,10 +213,17 @@ class LargestContentfulPaint final : public PerformanceEntry {
private:
~LargestContentfulPaint() = default;
void ReportLCPToNavigationTimings();
RefPtr<PerformanceMainThread> mPerformance;
// This is always set but only exposed to web content if
// mShouldExposeRenderTime is true.
DOMHighResTimeStamp mRenderTime;
DOMHighResTimeStamp mLoadTime;
// This is set to false when for security reasons web content it not allowed
// to see the RenderTime.
const bool mShouldExposeRenderTime;
unsigned long mSize;
nsCOMPtr<nsIURI> mURI;

View file

@ -701,7 +701,7 @@ bool nsHTTPSOnlyUtils::HttpsUpgradeUnrelatedErrorCode(nsresult aError) {
NS_ERROR_UNKNOWN_HOST == aError || NS_ERROR_PHISHING_URI == aError ||
NS_ERROR_MALWARE_URI == aError || NS_ERROR_UNWANTED_URI == aError ||
NS_ERROR_HARMFUL_URI == aError || NS_ERROR_CONTENT_CRASHED == aError ||
NS_ERROR_FRAME_CRASHED == aError;
NS_ERROR_FRAME_CRASHED == aError || NS_ERROR_SUPERFLUOS_AUTH == aError;
}
/* ------ Logging ------ */
@ -737,11 +737,14 @@ void nsHTTPSOnlyUtils::LogMessage(const nsAString& aMessage, uint32_t aFlags,
// Allow for easy distinction in devtools code.
auto category = aUseHttpsFirst ? "HTTPSFirst"_ns : "HTTPSOnly"_ns;
uint64_t innerWindowId = aLoadInfo->GetInnerWindowID();
if (innerWindowId > 0) {
uint64_t windowId = aLoadInfo->GetInnerWindowID();
if (!windowId) {
windowId = aLoadInfo->GetTriggeringWindowId();
}
if (windowId) {
// Send to content console
nsContentUtils::ReportToConsoleByWindowID(message, aFlags, category,
innerWindowId, aURI);
windowId, aURI);
} else {
// Send to browser console
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;

View file

@ -49,4 +49,6 @@ support-files = [
"file_slow_download.sjs",
]
["browser_superfluos_auth.js"]
["browser_upgrade_onion.js"]

View file

@ -0,0 +1,67 @@
/* Any copyright is dedicated to the Public Domain.
* https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// This test checks the superfluos auth prompt when HTTPS-First is enabled (Bug 1858565).
const TEST_URI = "https://www.mozilla.org@example.com/";
const { MockRegistrar } = ChromeUtils.importESModule(
"resource://testing-common/MockRegistrar.sys.mjs"
);
let respondMockPromptWithYes = false;
const gMockPromptService = {
firstTimeCalled: false,
confirmExBC() {
return respondMockPromptWithYes ? 0 : 1;
},
QueryInterface: ChromeUtils.generateQI(["nsIPromptService"]),
};
var gMockPromptServiceCID = MockRegistrar.register(
"@mozilla.org/prompter;1",
gMockPromptService
);
registerCleanupFunction(() => {
MockRegistrar.unregister(gMockPromptServiceCID);
});
function checkBrowserLoad(browser) {
return new Promise(resolve => {
BrowserTestUtils.browserLoaded(browser, false, null, true).then(() => {
resolve(true);
});
BrowserTestUtils.browserStopped(browser, false, null, true).then(() => {
resolve(false);
});
});
}
add_task(async function () {
await SpecialPowers.pushPrefEnv({
set: [["dom.security.https_first", true]],
});
respondMockPromptWithYes = false;
let didBrowserLoadPromise = checkBrowserLoad(gBrowser.selectedBrowser);
BrowserTestUtils.startLoadingURIString(gBrowser.selectedBrowser, TEST_URI);
let didBrowserLoad = await didBrowserLoadPromise;
ok(
!didBrowserLoad,
"The browser should stop the load when the user refuses to load a page with superfluos authentication"
);
respondMockPromptWithYes = true;
didBrowserLoadPromise = checkBrowserLoad(gBrowser.selectedBrowser);
BrowserTestUtils.startLoadingURIString(gBrowser.selectedBrowser, TEST_URI);
didBrowserLoad = await didBrowserLoadPromise;
ok(
didBrowserLoad,
"The browser should load when the user agrees to load a page with superfluos authentication"
);
});

View file

@ -318,7 +318,6 @@ support-files = [
["test_sizetocontent_clamp.html"]
skip-if = [
"os == 'android'", #Windows can't change size on Android
"display == 'wayland' && os_version == '22.04'" # Bug 1857032
]
["test_toJSON.html"]

View file

@ -35,6 +35,11 @@ var isWin8 = (navigator.userAgent.includes("Windows NT 6.2"));
var innerWidthMin = (isWin8 ? 120 : 100);
var innerWidthMax = (isWin8 ? 125 : 100);
// Window size with CSD decorations is 180 pixels
if (navigator.platform.includes("Linux")) {
innerWidthMax = 180;
}
var isExecuted = false;
function test() {

View file

@ -1,3 +1,7 @@
/* 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 https://mozilla.org/MPL/2.0/. */
[Func="mozilla::AddonManagerWebAPI::IsAPIEnabled",
Exposed=Window]
interface AddonEvent : Event {

View file

@ -1,3 +1,7 @@
/* 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 https://mozilla.org/MPL/2.0/. */
[Exposed=(Window, Worker), Pref="dom.enable_web_task_scheduling"]
interface TaskPriorityChangeEvent : Event {
constructor (DOMString type , TaskPriorityChangeEventInit priorityChangeEventInitDict);

View file

@ -1,3 +1,7 @@
/* 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 https://mozilla.org/MPL/2.0/. */
enum TaskPriority {
"user-blocking",
"user-visible",

View file

@ -1,3 +1,7 @@
/* 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 https://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface mozIDOMWindow;

View file

@ -1,3 +1,7 @@
/* 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 https://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsISimpleEnumerator;

View file

@ -3,6 +3,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
add_task(async function test() {
await SpecialPowers.pushPrefEnv({
set: [
[
"dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled",
true,
],
],
});
const testURL = getRootDirectory(gTestPath) + "empty.html";
let tab = BrowserTestUtils.addTab(gBrowser, testURL);
gBrowser.selectedTab = tab;
@ -27,17 +36,33 @@ add_task(async function test() {
consoleListener.prototype = {
onConsoleLogEvent(aSubject) {
var obj = aSubject.wrappedJSObject;
is(
obj.arguments[0],
"Hello world from a SharedWorker!",
"A message from a SharedWorker \\o/"
);
is(obj.ID, "sharedWorker_console.js", "The ID is SharedWorker");
is(obj.innerID, "SharedWorker", "The ID is SharedWorker");
is(order++, 1, "Then a log message.");
if (order == 1) {
is(
obj.arguments[0],
"Hello world from a SharedWorker!",
"A message from a SharedWorker \\o/"
);
is(obj.ID, "sharedWorker_console.js", "The ID is SharedWorker");
is(obj.innerID, "SharedWorker", "The ID is SharedWorker");
is(order++, 1, "Then a first log message.");
} else {
is(
obj.arguments[0],
"Here is a SAB",
"A message from a SharedWorker \\o/"
);
is(
obj.arguments[1].constructor.name,
"SharedArrayBuffer",
"We got a direct reference to the SharedArrayBuffer coming from the worker thread"
);
is(obj.ID, "sharedWorker_console.js", "The ID is SharedWorker");
is(obj.innerID, "SharedWorker", "The ID is SharedWorker");
is(order++, 2, "Then a second log message.");
ConsoleAPIStorage.removeLogEventListener(this.onConsoleLogEvent);
resolve();
ConsoleAPIStorage.removeLogEventListener(this.onConsoleLogEvent);
resolve();
}
},
observe: (aSubject, aTopic) => {

View file

@ -7,5 +7,6 @@
onconnect = function (evt) {
console.profile("Hello profiling from a SharedWorker!");
console.log("Hello world from a SharedWorker!");
console.log("Here is a SAB", new SharedArrayBuffer(1024));
evt.ports[0].postMessage("ok!");
};

View file

@ -761,7 +761,7 @@ DXGITextureHostD3D11::DXGITextureHostD3D11(
mGpuProcessTextureId(aDescriptor.gpuProcessTextureId()),
mArrayIndex(aDescriptor.arrayIndex()),
mSize(aDescriptor.size()),
mHandle(aDescriptor.handle()),
mHandle((HANDLE)aDescriptor.handle()),
mFormat(aDescriptor.format()),
mHasKeyedMutex(aDescriptor.hasKeyedMutex()),
mColorSpace(aDescriptor.colorSpace()),
@ -1088,9 +1088,9 @@ DXGIYCbCrTextureHostD3D11::DXGIYCbCrTextureHostD3D11(
mColorDepth(aDescriptor.colorDepth()),
mYUVColorSpace(aDescriptor.yUVColorSpace()),
mColorRange(aDescriptor.colorRange()) {
mHandles[0] = aDescriptor.handleY();
mHandles[1] = aDescriptor.handleCb();
mHandles[2] = aDescriptor.handleCr();
mHandles[0] = (HANDLE)aDescriptor.handleY();
mHandles[1] = (HANDLE)aDescriptor.handleCb();
mHandles[2] = (HANDLE)aDescriptor.handleCr();
}
void DXGIYCbCrTextureHostD3D11::CreateRenderTexture(

View file

@ -382,7 +382,7 @@ class DXGITextureHostD3D11 : public TextureHost {
uint32_t mArrayIndex = 0;
RefPtr<DataTextureSourceD3D11> mTextureSource;
gfx::IntSize mSize;
WindowsHandle mHandle;
HANDLE mHandle;
gfx::SurfaceFormat mFormat;
bool mHasKeyedMutex;
@ -441,7 +441,7 @@ class DXGIYCbCrTextureHostD3D11 : public TextureHost {
gfx::IntSize mSize;
gfx::IntSize mSizeY;
gfx::IntSize mSizeCbCr;
WindowsHandle mHandles[3];
HANDLE mHandles[3];
bool mIsLocked;
gfx::ColorDepth mColorDepth;
gfx::YUVColorSpace mYUVColorSpace;

View file

@ -21,8 +21,7 @@ namespace mozilla {
namespace wr {
RenderDXGITextureHost::RenderDXGITextureHost(
WindowsHandle aHandle,
Maybe<layers::GpuProcessTextureId>& aGpuProcessTextureId,
HANDLE aHandle, Maybe<layers::GpuProcessTextureId>& aGpuProcessTextureId,
uint32_t aArrayIndex, gfx::SurfaceFormat aFormat,
gfx::ColorSpace2 aColorSpace, gfx::ColorRange aColorRange,
gfx::IntSize aSize, bool aHasKeyedMutex)
@ -445,7 +444,7 @@ bool RenderDXGITextureHost::SyncObjectNeeded() {
}
RenderDXGIYCbCrTextureHost::RenderDXGIYCbCrTextureHost(
WindowsHandle (&aHandles)[3], gfx::YUVColorSpace aYUVColorSpace,
HANDLE (&aHandles)[3], gfx::YUVColorSpace aYUVColorSpace,
gfx::ColorDepth aColorDepth, gfx::ColorRange aColorRange,
gfx::IntSize aSizeY, gfx::IntSize aSizeCbCr)
: mHandles{aHandles[0], aHandles[1], aHandles[2]},

View file

@ -22,8 +22,7 @@ namespace wr {
class RenderDXGITextureHost final : public RenderTextureHostSWGL {
public:
RenderDXGITextureHost(
WindowsHandle aHandle,
Maybe<layers::GpuProcessTextureId>& aGpuProcessTextureId,
HANDLE aHandle, Maybe<layers::GpuProcessTextureId>& aGpuProcessTextureId,
uint32_t aArrayIndex, gfx::SurfaceFormat aFormat, gfx::ColorSpace2,
gfx::ColorRange aColorRange, gfx::IntSize aSize, bool aHasKeyedMutex);
@ -94,7 +93,7 @@ class RenderDXGITextureHost final : public RenderTextureHostSWGL {
RefPtr<gl::GLContext> mGL;
WindowsHandle mHandle;
HANDLE mHandle;
Maybe<layers::GpuProcessTextureId> mGpuProcessTextureId;
RefPtr<ID3D11Texture2D> mTexture;
uint32_t mArrayIndex = 0;
@ -127,7 +126,7 @@ class RenderDXGITextureHost final : public RenderTextureHostSWGL {
class RenderDXGIYCbCrTextureHost final : public RenderTextureHostSWGL {
public:
explicit RenderDXGIYCbCrTextureHost(WindowsHandle (&aHandles)[3],
explicit RenderDXGIYCbCrTextureHost(HANDLE (&aHandles)[3],
gfx::YUVColorSpace aYUVColorSpace,
gfx::ColorDepth aColorDepth,
gfx::ColorRange aColorRange,
@ -190,7 +189,7 @@ class RenderDXGIYCbCrTextureHost final : public RenderTextureHostSWGL {
RefPtr<gl::GLContext> mGL;
WindowsHandle mHandles[3];
HANDLE mHandles[3];
RefPtr<ID3D11Texture2D> mTextures[3];
RefPtr<IDXGIKeyedMutex> mKeyedMutexs[3];

View file

@ -167,9 +167,13 @@ void UPowerClient::BeginListening() {
UpdateTrackedDevices();
},
[](GUniquePtr<GError>&& aError) {
g_warning(
"Failed to create DBus proxy for org.freedesktop.UPower: %s\n",
aError->message);
if (!g_error_matches(aError.get(), G_IO_ERROR,
G_IO_ERROR_CANCELLED)) {
g_warning(
"Failed to create DBus proxy for org.freedesktop.UPower: "
"%s\n",
aError->message);
}
});
}
@ -275,9 +279,12 @@ void UPowerClient::UpdateTrackedDevices() {
G_CALLBACK(DeviceChanged), this);
},
[this](GUniquePtr<GError>&& aError) {
g_warning(
"Failed to enumerate devices of org.freedesktop.UPower: %s\n",
aError->message);
if (!g_error_matches(aError.get(), G_IO_ERROR,
G_IO_ERROR_CANCELLED)) {
g_warning(
"Failed to enumerate devices of org.freedesktop.UPower: %s\n",
aError->message);
}
g_signal_connect(mUPowerProxy, "g-signal",
G_CALLBACK(DeviceChanged), this);
});

View file

@ -28,7 +28,7 @@ static constexpr DWORD kShutdownWaitMs = 80000;
// Sanitizers also slow things down in some cases; see bug 1806224.
static constexpr DWORD kShutdownWaitMs = 40000;
#else
static constexpr DWORD kShutdownWaitMs = 8000;
static constexpr DWORD kShutdownWaitMs = 20000;
#endif
namespace {

View file

@ -1703,6 +1703,13 @@ void MessageChannel::DispatchMessage(ActorLifecycleProxy* aProxy,
UniquePtr<Message> reply;
#ifdef FUZZING_SNAPSHOT
if (IsCrossProcess()) {
aMsg = mozilla::fuzzing::IPCFuzzController::instance().replaceIPCMessage(
std::move(aMsg));
}
#endif
IPC_LOG("DispatchMessage: seqno=%d, xid=%d", aMsg->seqno(),
aMsg->transaction_id());
AddProfilerMarker(*aMsg, MessageDirection::eReceiving);
@ -1736,6 +1743,12 @@ void MessageChannel::DispatchMessage(ActorLifecycleProxy* aProxy,
}
}
#ifdef FUZZING_SNAPSHOT
if (aMsg->IsFuzzMsg()) {
mozilla::fuzzing::IPCFuzzController::instance().syncAfterReplace();
}
#endif
if (reply && ChannelConnected == mChannelState) {
IPC_LOG("Sending reply seqno=%d, xid=%d", aMsg->seqno(),
aMsg->transaction_id());

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