Update On Tue Mar 7 19:50:03 CET 2023
This commit is contained in:
parent
e45c015c4f
commit
142b230924
1505 changed files with 72487 additions and 23975 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
@ -1595,7 +1595,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "error-support"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=fe2867dbe82a2aaa85a856648107be94b1534683#fe2867dbe82a2aaa85a856648107be94b1534683"
|
||||
dependencies = [
|
||||
"error-support-macros",
|
||||
"lazy_static",
|
||||
|
@ -1607,7 +1607,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "error-support-macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=fe2867dbe82a2aaa85a856648107be94b1534683#fe2867dbe82a2aaa85a856648107be94b1534683"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2715,7 +2715,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "interrupt-support"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=fe2867dbe82a2aaa85a856648107be94b1534683#fe2867dbe82a2aaa85a856648107be94b1534683"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"parking_lot 0.12.999",
|
||||
|
@ -3837,7 +3837,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "nss_build_common"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=fe2867dbe82a2aaa85a856648107be94b1534683#fe2867dbe82a2aaa85a856648107be94b1534683"
|
||||
|
||||
[[package]]
|
||||
name = "nsstring"
|
||||
|
@ -5045,7 +5045,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sql-support"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=fe2867dbe82a2aaa85a856648107be94b1534683#fe2867dbe82a2aaa85a856648107be94b1534683"
|
||||
dependencies = [
|
||||
"ffi-support",
|
||||
"interrupt-support",
|
||||
|
@ -5227,7 +5227,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sync-guid"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=fe2867dbe82a2aaa85a856648107be94b1534683#fe2867dbe82a2aaa85a856648107be94b1534683"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"rand 0.8.5",
|
||||
|
@ -5238,7 +5238,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sync15"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=fe2867dbe82a2aaa85a856648107be94b1534683#fe2867dbe82a2aaa85a856648107be94b1534683"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"error-support",
|
||||
|
@ -5268,7 +5268,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "tabs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=fe2867dbe82a2aaa85a856648107be94b1534683#fe2867dbe82a2aaa85a856648107be94b1534683"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"error-support",
|
||||
|
@ -6042,7 +6042,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
|||
[[package]]
|
||||
name = "viaduct"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=fe2867dbe82a2aaa85a856648107be94b1534683#fe2867dbe82a2aaa85a856648107be94b1534683"
|
||||
dependencies = [
|
||||
"ffi-support",
|
||||
"log",
|
||||
|
@ -6199,7 +6199,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "webext-storage"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=fe2867dbe82a2aaa85a856648107be94b1534683#fe2867dbe82a2aaa85a856648107be94b1534683"
|
||||
dependencies = [
|
||||
"error-support",
|
||||
"ffi-support",
|
||||
|
@ -6355,7 +6355,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "wgpu-core"
|
||||
version = "0.15.0"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=74303308cd6030071889e865c40aa638214ed938#74303308cd6030071889e865c40aa638214ed938"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=73b4257b17cc62ecc8df6d6aa3730bd9c6cba4b9#73b4257b17cc62ecc8df6d6aa3730bd9c6cba4b9"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bit-vec",
|
||||
|
@ -6378,7 +6378,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "wgpu-hal"
|
||||
version = "0.15.1"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=74303308cd6030071889e865c40aa638214ed938#74303308cd6030071889e865c40aa638214ed938"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=73b4257b17cc62ecc8df6d6aa3730bd9c6cba4b9#73b4257b17cc62ecc8df6d6aa3730bd9c6cba4b9"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"arrayvec",
|
||||
|
@ -6415,7 +6415,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "wgpu-types"
|
||||
version = "0.15.0"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=74303308cd6030071889e865c40aa638214ed938#74303308cd6030071889e865c40aa638214ed938"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=73b4257b17cc62ecc8df6d6aa3730bd9c6cba4b9#73b4257b17cc62ecc8df6d6aa3730bd9c6cba4b9"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"js-sys",
|
||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -170,12 +170,12 @@ warp = { git = "https://github.com/glandium/warp", rev = "4af45fae95bc98b0eba1ef
|
|||
cssparser = { git = "https://github.com/servo/rust-cssparser", rev = "b196a164dcbb317016d4aa6c58c13147e6045ebb" }
|
||||
|
||||
# application-services overrides to make updating them all simpler.
|
||||
interrupt-support = { git = "https://github.com/bendk/application-services", rev = "ecb35df5fc40357c49922f90e86bf4147fa52953" }
|
||||
sql-support = { git = "https://github.com/bendk/application-services", rev = "ecb35df5fc40357c49922f90e86bf4147fa52953" }
|
||||
sync15 = { git = "https://github.com/bendk/application-services", rev = "ecb35df5fc40357c49922f90e86bf4147fa52953" }
|
||||
tabs = { git = "https://github.com/bendk/application-services", rev = "ecb35df5fc40357c49922f90e86bf4147fa52953" }
|
||||
viaduct = { git = "https://github.com/bendk/application-services", rev = "ecb35df5fc40357c49922f90e86bf4147fa52953" }
|
||||
webext-storage = { git = "https://github.com/bendk/application-services", rev = "ecb35df5fc40357c49922f90e86bf4147fa52953" }
|
||||
interrupt-support = { git = "https://github.com/mozilla/application-services", rev = "fe2867dbe82a2aaa85a856648107be94b1534683" }
|
||||
sql-support = { git = "https://github.com/mozilla/application-services", rev = "fe2867dbe82a2aaa85a856648107be94b1534683" }
|
||||
sync15 = { git = "https://github.com/mozilla/application-services", rev = "fe2867dbe82a2aaa85a856648107be94b1534683" }
|
||||
tabs = { git = "https://github.com/mozilla/application-services", rev = "fe2867dbe82a2aaa85a856648107be94b1534683" }
|
||||
viaduct = { git = "https://github.com/mozilla/application-services", rev = "fe2867dbe82a2aaa85a856648107be94b1534683" }
|
||||
webext-storage = { git = "https://github.com/mozilla/application-services", rev = "fe2867dbe82a2aaa85a856648107be94b1534683" }
|
||||
|
||||
# Patch mio 0.6 to use winapi 0.3 and miow 0.3, getting rid of winapi 0.2.
|
||||
# There is not going to be new version of mio 0.6, mio now being >= 0.7.11.
|
||||
|
|
|
@ -14,6 +14,8 @@ support-files = fullscreen.html FullscreenFrame.sys.mjs
|
|||
support-files = fullscreen.html fullscreen_frame.html
|
||||
[browser_fullscreen_enterInUrlbar.js]
|
||||
skip-if = (os == 'mac') || (os == 'linux') # Bug 1648649
|
||||
[browser_fullscreen_from_minimize.js]
|
||||
skip-if = (os == 'linux') || (os == 'win') # Bug 1818795 and Bug 1818796
|
||||
[browser_fullscreen_newtab.js]
|
||||
[browser_fullscreen_permissions_prompt.js]
|
||||
[browser_fullscreen_warning.js]
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// This test checks whether fullscreen windows can transition to minimized windows,
|
||||
// and back again. This is sometimes not directly supported by the OS widgets. For
|
||||
// example, in macOS, the minimize button is greyed-out in the title bar of
|
||||
// fullscreen windows, making this transition impossible for users to initiate.
|
||||
// Still, web APIs do allow arbitrary combinations of window calls, and this test
|
||||
// exercises some of those combinations.
|
||||
|
||||
const restoreWindowToNormal = async () => {
|
||||
// Get the window to normal state by calling window.restore(). This may take
|
||||
// multiple attempts since a call to restore could bring the window to either
|
||||
// NORMAL or MAXIMIZED state.
|
||||
while (window.windowState != window.STATE_NORMAL) {
|
||||
info(
|
||||
`Calling window.restore(), to try to reach "normal" state ${window.STATE_NORMAL}.`
|
||||
);
|
||||
let promiseSizeModeChange = BrowserTestUtils.waitForEvent(
|
||||
window,
|
||||
"sizemodechange"
|
||||
);
|
||||
window.restore();
|
||||
await promiseSizeModeChange;
|
||||
info(`Window reached state ${window.windowState}.`);
|
||||
}
|
||||
};
|
||||
|
||||
add_task(async function() {
|
||||
registerCleanupFunction(function() {
|
||||
window.restore();
|
||||
});
|
||||
|
||||
// We reuse these variables to create new promises for each transition.
|
||||
let promiseSizeModeChange;
|
||||
let promiseFullscreen;
|
||||
|
||||
await restoreWindowToNormal();
|
||||
ok(!window.fullScreen, "Window should not be fullscreen at start of test.");
|
||||
|
||||
// Get to fullscreen.
|
||||
info("Requesting fullscreen.");
|
||||
promiseFullscreen = document.documentElement.requestFullscreen();
|
||||
await promiseFullscreen;
|
||||
ok(window.fullScreen, "Window should be fullscreen before being minimized.");
|
||||
|
||||
// Transition between fullscreen and minimize states.
|
||||
info("Requesting minimize on a fullscreen window.");
|
||||
promiseSizeModeChange = BrowserTestUtils.waitForEvent(
|
||||
window,
|
||||
"sizemodechange"
|
||||
);
|
||||
window.minimize();
|
||||
await promiseSizeModeChange;
|
||||
is(
|
||||
window.windowState,
|
||||
window.STATE_MINIMIZED,
|
||||
"Window should be minimized after fullscreen."
|
||||
);
|
||||
|
||||
// Whether or not the previous transition worked, restore the window
|
||||
// and then minimize it.
|
||||
await restoreWindowToNormal();
|
||||
|
||||
info("Requesting minimize on a normal window.");
|
||||
promiseSizeModeChange = BrowserTestUtils.waitForEvent(
|
||||
window,
|
||||
"sizemodechange"
|
||||
);
|
||||
window.minimize();
|
||||
await promiseSizeModeChange;
|
||||
is(
|
||||
window.windowState,
|
||||
window.STATE_MINIMIZED,
|
||||
"Window should be minimized before fullscreen."
|
||||
);
|
||||
|
||||
info("Requesting fullscreen on a minimized window.");
|
||||
promiseFullscreen = document.documentElement.requestFullscreen();
|
||||
await promiseFullscreen;
|
||||
ok(window.fullScreen, "Window should be fullscreen after being minimized.");
|
||||
});
|
|
@ -404,7 +404,7 @@ skip-if =
|
|||
os == "win" && !debug
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_windowactivation.js]
|
||||
skip-if =
|
||||
skip-if =
|
||||
verify
|
||||
os == "linux" && debug # Bug 1678774
|
||||
support-files =
|
||||
|
|
|
@ -37,6 +37,9 @@ add_task(async function() {
|
|||
|
||||
let tabStripRect = gBrowser.tabContainer.arrowScrollbox.getBoundingClientRect();
|
||||
let newTabButtonRect = gBrowser.tabContainer.newTabButton.getBoundingClientRect();
|
||||
let firefoxViewRect = document
|
||||
.getElementById("firefox-view-button")
|
||||
.getBoundingClientRect();
|
||||
let inRange = (val, min, max) => min <= val && val <= max;
|
||||
|
||||
// Add a reflow observer and open a new tab.
|
||||
|
@ -98,6 +101,12 @@ add_task(async function() {
|
|||
document.getElementById("appcontent").getBoundingClientRect()
|
||||
.top,
|
||||
},
|
||||
{
|
||||
name:
|
||||
"bug 1820549 - flicker caused by delayed load of Firefox " +
|
||||
"View icon",
|
||||
condition: r => rectInBoundingClientRect(r, firefoxViewRect),
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
|
|
@ -365,7 +365,9 @@ if (!isDevtools) {
|
|||
}
|
||||
|
||||
if (AppConstants.MOZ_CODE_COVERAGE) {
|
||||
whitelist.add("chrome://remote/content/marionette/PerTestCoverageUtils.jsm");
|
||||
whitelist.add(
|
||||
"chrome://remote/content/marionette/PerTestCoverageUtils.sys.mjs"
|
||||
);
|
||||
}
|
||||
|
||||
const gInterestingCategories = new Set([
|
||||
|
|
|
@ -177,8 +177,18 @@ add_task(async function test_check_minimize_response() {
|
|||
set: [["prompts.windowPromptSubDialog", true]],
|
||||
});
|
||||
|
||||
let promiseSizeModeChange = BrowserTestUtils.waitForEvent(
|
||||
window,
|
||||
"sizemodechange"
|
||||
);
|
||||
window.minimize();
|
||||
await promiseSizeModeChange;
|
||||
is(window.windowState, window.STATE_MINIMIZED, "Should be minimized.");
|
||||
|
||||
promiseSizeModeChange = BrowserTestUtils.waitForEvent(
|
||||
window,
|
||||
"sizemodechange"
|
||||
);
|
||||
let dialogPromise = BrowserTestUtils.promiseAlertDialogOpen();
|
||||
// Use an async alert to avoid blocking.
|
||||
Services.prompt.asyncAlert(
|
||||
|
@ -188,6 +198,7 @@ add_task(async function test_check_minimize_response() {
|
|||
"some message"
|
||||
);
|
||||
let dialogWin = await dialogPromise;
|
||||
await promiseSizeModeChange;
|
||||
|
||||
isnot(
|
||||
window.windowState,
|
||||
|
|
|
@ -617,7 +617,7 @@ let JSWINDOWACTORS = {
|
|||
child: {
|
||||
esModuleURI: "resource:///actors/MigrationWizardChild.sys.mjs",
|
||||
events: {
|
||||
"MigrationWizard:Init": { wantUntrusted: true },
|
||||
"MigrationWizard:RequestState": { wantUntrusted: true },
|
||||
"MigrationWizard:BeginMigration": { wantsUntrusted: true },
|
||||
},
|
||||
},
|
||||
|
|
|
@ -129,6 +129,7 @@ skip-if = verify
|
|||
tags = overflowable-toolbar
|
||||
[browser_addons_area.js]
|
||||
[browser_allow_dragging_removable_false.js]
|
||||
[browser_bookmarks_empty_message.js]
|
||||
[browser_bookmarks_toolbar_collapsed_restore_default.js]
|
||||
[browser_bookmarks_toolbar_shown_newtab.js]
|
||||
[browser_bootstrapped_custom_toolbar.js]
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(async function empty_message_on_non_empty_bookmarks_toolbar() {
|
||||
await resetCustomization();
|
||||
ok(CustomizableUI.inDefaultState, "Default state to begin");
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.toolbars.bookmarks.visibility", "always"]],
|
||||
});
|
||||
|
||||
CustomizableUI.removeWidgetFromArea("import-button");
|
||||
CustomizableUI.removeWidgetFromArea("personal-bookmarks");
|
||||
CustomizableUI.addWidgetToArea(
|
||||
"bookmarks-menu-button",
|
||||
CustomizableUI.AREA_BOOKMARKS,
|
||||
0
|
||||
);
|
||||
|
||||
let newWin = await BrowserTestUtils.openNewBrowserWindow();
|
||||
let doc = newWin.document;
|
||||
ok(
|
||||
BrowserTestUtils.is_visible(doc.getElementById("PersonalToolbar")),
|
||||
"Personal toolbar should be visible"
|
||||
);
|
||||
ok(
|
||||
doc.getElementById("personal-toolbar-empty").hidden,
|
||||
"Empty message should be hidden"
|
||||
);
|
||||
|
||||
await BrowserTestUtils.closeWindow(newWin);
|
||||
await resetCustomization();
|
||||
});
|
|
@ -1691,7 +1691,7 @@ export var Policies = {
|
|||
|
||||
Preferences: {
|
||||
onBeforeAddons(manager, param) {
|
||||
const allowedPrefixes = [
|
||||
let allowedPrefixes = [
|
||||
"accessibility.",
|
||||
"app.update.",
|
||||
"browser.",
|
||||
|
@ -1717,6 +1717,9 @@ export var Policies = {
|
|||
"ui.",
|
||||
"widget.",
|
||||
];
|
||||
if (!AppConstants.MOZ_REQUIRE_SIGNING) {
|
||||
allowedPrefixes.push("xpinstall.signatures.required");
|
||||
}
|
||||
const allowedSecurityPrefs = [
|
||||
"security.block_fileuri_script_with_wrong_mime",
|
||||
"security.default_personal_cert",
|
||||
|
|
|
@ -31,8 +31,12 @@ export class MigrationWizardChild extends JSWindowActorChild {
|
|||
*/
|
||||
async handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "MigrationWizard:Init": {
|
||||
case "MigrationWizard:RequestState": {
|
||||
this.#wizardEl = event.target;
|
||||
this.setComponentState({
|
||||
page: MigrationWizardConstants.PAGES.LOADING,
|
||||
});
|
||||
|
||||
let migrators = await this.sendQuery("GetAvailableMigrators");
|
||||
this.setComponentState({
|
||||
migrators,
|
||||
|
|
|
@ -51,6 +51,7 @@ const MigrationDialog = {
|
|||
args.onResize();
|
||||
});
|
||||
observer.observe(this._wiz);
|
||||
this._wiz.requestState();
|
||||
},
|
||||
|
||||
handleEvent(event) {
|
||||
|
|
|
@ -11,6 +11,7 @@ export const MigrationWizardConstants = Object.freeze({
|
|||
* @type {Object<string, string>}
|
||||
*/
|
||||
PAGES: Object.freeze({
|
||||
LOADING: "loading",
|
||||
SELECTION: "selection",
|
||||
PROGRESS: "progress",
|
||||
SAFARI_PERMISSION: "safari-permission",
|
||||
|
|
|
@ -25,7 +25,19 @@ export class MigrationWizard extends HTMLElement {
|
|||
return `
|
||||
<template>
|
||||
<link rel="stylesheet" href="chrome://browser/skin/migration/migration-wizard.css">
|
||||
<named-deck id="wizard-deck" selected-view="page-selection" aria-live="polite">
|
||||
<named-deck id="wizard-deck" selected-view="page-loading" aria-live="polite" aria-busy="true">
|
||||
<div name="page-loading">
|
||||
<h3 data-l10n-id="migration-wizard-selection-header"></h3>
|
||||
<div class="loading-block large"></div>
|
||||
<div class="loading-block small"></div>
|
||||
<div class="loading-block small"></div>
|
||||
<moz-button-group class="buttons">
|
||||
<!-- If possible, use the same button labels as the SELECTION page with the same strings.
|
||||
That'll prevent flicker when the load state exits if we then enter the SELECTION page. -->
|
||||
<button class="cancel-close" data-l10n-id="migration-cancel-button-label" disabled></button>
|
||||
<button data-l10n-id="migration-import-button-label" disabled></button>
|
||||
</moz-button-group>
|
||||
</div>
|
||||
|
||||
<div name="page-selection">
|
||||
<h3 data-l10n-id="migration-wizard-selection-header"></h3>
|
||||
|
@ -94,6 +106,7 @@ export class MigrationWizard extends HTMLElement {
|
|||
<button class="primary" id="done-button" data-l10n-id="migration-done-button-label"></button>
|
||||
</moz-button-group>
|
||||
</div>
|
||||
|
||||
<div name="page-safari-permission">
|
||||
<h3>TODO: Safari permission page</h3>
|
||||
</div>
|
||||
|
@ -108,7 +121,6 @@ export class MigrationWizard extends HTMLElement {
|
|||
<button class="cancel-close" data-l10n-id="migration-cancel-button-label"></button>
|
||||
</moz-button-group>
|
||||
</div>
|
||||
|
||||
</named-deck>
|
||||
</template>
|
||||
`;
|
||||
|
@ -173,8 +185,14 @@ export class MigrationWizard extends HTMLElement {
|
|||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (this.hasAttribute("auto-request-state")) {
|
||||
this.requestState();
|
||||
}
|
||||
}
|
||||
|
||||
requestState() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("MigrationWizard:Init", { bubbles: true })
|
||||
new CustomEvent("MigrationWizard:RequestState", { bubbles: true })
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -211,6 +229,10 @@ export class MigrationWizard extends HTMLElement {
|
|||
}
|
||||
}
|
||||
|
||||
this.#deck.toggleAttribute(
|
||||
"aria-busy",
|
||||
state.page == MigrationWizardConstants.PAGES.LOADING
|
||||
);
|
||||
this.#deck.setAttribute("selected-view", `page-${state.page}`);
|
||||
|
||||
if (window.IS_STORYBOOK) {
|
||||
|
|
|
@ -29,7 +29,9 @@ The following diagram tries to illustrate how the pieces of the migration wizard
|
|||
|
||||
The ``MigrationWizard`` reusable component (``<migration-wizard>``) is a custom element that can be imported from ``migration-wizard.mjs``. The module is expected to load in a DOM window context, whereupon the custom element is automatically registered for that document.
|
||||
|
||||
After binding to the document, the ``MigrationWizard`` dispatches a ``MigrationWizard:Init`` custom event, which causes a ``MigrationWizardChild`` to instantiate and be associated with it. After receiving the migrator state from the ``MigrationWizardParent``, the ``MigrationWizardChild`` will dispatch a ``MigrationWizard:Ready`` event on the ``MigrationWizard``, mainly to aid in testing.
|
||||
After binding to the document, if the ``MigrationWizard`` has the ``auto-request-state`` attribute set on it, it will dispatch a ``MigrationWizard:RequestState`` custom event, which causes a ``MigrationWizardChild`` to instantiate and be associated with it. After receiving the migrator state from the ``MigrationWizardParent``, the ``MigrationWizardChild`` will dispatch a ``MigrationWizard:Ready`` event on the ``MigrationWizard``, mainly to aid in testing. The ``auto-request-state`` attribute is useful in situations where the ``MigrationWizard`` element is being used declaratively.
|
||||
|
||||
If the ``auto-request-state`` attribute is not set, calling ``requestState()`` on the ``MigrationWizard`` will perform the above step. This is useful in situations where the ``MigrationWizard`` element is being constructed dynamically and the callers wants finer-grain control over when the state will be requested.
|
||||
|
||||
Notably, the ``MigrationWizard`` does not contain any internal logic or privileged code to perform any migrations or to directly interact with the migration mechanisms. Its sole function is to accept input from the user and emit that input as events. The associated ``MigrationWizardChild`` will listen for those events, and take care of calling into the ``MigrationWizard`` to update the state of the reusable component. This means that the reusable component can be embedded in unprivileged contexts and have its states presented in a tool like Storybook.
|
||||
|
||||
|
|
|
@ -58,23 +58,50 @@
|
|||
});
|
||||
|
||||
/**
|
||||
* Tests that the MigrationWizard:Init event is fired when the <migration-wizard>
|
||||
* is added to the DOM, and then ensures that the starting page is correct.
|
||||
* Tests that the MigrationWizard:RequestState event is fired when the
|
||||
* <migration-wizard> is added to the DOM if the auto-request-state attribute
|
||||
* is set, and then ensures that the starting page is correct.
|
||||
*
|
||||
* This also tests that the MigrationWizard:RequestState is not fired automatically
|
||||
* if the auto-request-state attribute is not set, but is then fired upon calling
|
||||
* requestState().
|
||||
*
|
||||
* This uses a dynamically created <migration-wizard> instead of the one already
|
||||
* in the content div to make sure that the init event is captured.
|
||||
*/
|
||||
add_task(async function test_init_event() {
|
||||
const REQUEST_STATE_EVENT = "MigrationWizard:RequestState";
|
||||
|
||||
let wiz = document.createElement("migration-wizard");
|
||||
wiz.toggleAttribute("auto-request-state", true);
|
||||
let content = document.getElementById("content");
|
||||
let promise = new Promise(resolve => {
|
||||
content.addEventListener("MigrationWizard:Init", resolve, { once: true });
|
||||
content.addEventListener(REQUEST_STATE_EVENT, resolve, { once: true });
|
||||
});
|
||||
content.appendChild(wiz);
|
||||
await promise;
|
||||
ok(true, "Saw MigrationWizard:Init event.");
|
||||
ok(true, `Saw ${REQUEST_STATE_EVENT} event.`);
|
||||
let shadowRoot = wiz.openOrClosedShadowRoot;
|
||||
let deck = shadowRoot.querySelector("#wizard-deck");
|
||||
is(deck.selectedViewName, "page-selection", "Should have the browser / profile selection page selected");
|
||||
is(deck.selectedViewName, "page-loading", "Should have the loading page selected");
|
||||
wiz.remove();
|
||||
|
||||
wiz.toggleAttribute("auto-request-state", false);
|
||||
let sawEvent = false;
|
||||
let handler = () => {
|
||||
sawEvent = true;
|
||||
};
|
||||
content.addEventListener(REQUEST_STATE_EVENT, handler);
|
||||
content.appendChild(wiz);
|
||||
ok(!sawEvent, `Should not have seen ${REQUEST_STATE_EVENT} event.`);
|
||||
content.removeEventListener(REQUEST_STATE_EVENT, handler);
|
||||
|
||||
promise = new Promise(resolve => {
|
||||
content.addEventListener(REQUEST_STATE_EVENT, resolve, { once: true });
|
||||
});
|
||||
wiz.requestState();
|
||||
await promise;
|
||||
ok(true, `Saw ${REQUEST_STATE_EVENT} event.`);
|
||||
wiz.remove();
|
||||
});
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ Please note that some targeting attributes require stricter controls on the tele
|
|||
* [currentDate](#currentdate)
|
||||
* [devToolsOpenedCount](#devtoolsopenedcount)
|
||||
* [isDefaultBrowser](#isdefaultbrowser)
|
||||
* [isDefaultHandler](#isdefaulthandler)
|
||||
* [defaultPDFHandler](#defaultpdfhandler)
|
||||
* [firefoxVersion](#firefoxversion)
|
||||
* [locale](#locale)
|
||||
* [localeLanguageCode](#localelanguagecode)
|
||||
|
@ -220,6 +222,45 @@ Is Firefox the user's default browser?
|
|||
declare const isDefaultBrowser: boolean;
|
||||
```
|
||||
|
||||
### `isDefaultHandler`
|
||||
|
||||
Is Firefox the user's default handler for various file extensions?
|
||||
|
||||
Windows-only.
|
||||
|
||||
#### Definition
|
||||
|
||||
```ts
|
||||
declare const isDefaultHandler: {
|
||||
pdf: boolean;
|
||||
html: boolean;
|
||||
};
|
||||
```
|
||||
|
||||
#### Examples
|
||||
* Is Firefox the default PDF handler?
|
||||
```ts
|
||||
isDefaultHandler.pdf
|
||||
```
|
||||
|
||||
### `defaultPDFHandler`
|
||||
|
||||
Information about the user's default PDF handler
|
||||
|
||||
Windows-only.
|
||||
|
||||
#### Definition
|
||||
|
||||
```ts
|
||||
declare const defaultPDFHandler: {
|
||||
// Does the user have a default PDF handler registered?
|
||||
registered: boolean;
|
||||
|
||||
// Is the default PDF handler a known browser?
|
||||
knownBrowser: boolean;
|
||||
};
|
||||
```
|
||||
|
||||
### `firefoxVersion`
|
||||
|
||||
The major Firefox version of the browser
|
||||
|
|
|
@ -1140,14 +1140,30 @@ class _ASRouter {
|
|||
* and ASRouter._getMessagesContext parameters and values
|
||||
*/
|
||||
async getTargetingParameters(environment, localContext) {
|
||||
const targetingParameters = {};
|
||||
for (const param of Object.keys(environment)) {
|
||||
targetingParameters[param] = await environment[param];
|
||||
}
|
||||
for (const param of Object.keys(localContext)) {
|
||||
targetingParameters[param] = await localContext[param];
|
||||
// Resolve objects that may contain promises.
|
||||
async function resolve(object) {
|
||||
const target = {};
|
||||
|
||||
for (const param of Object.keys(object)) {
|
||||
target[param] = await object[param];
|
||||
|
||||
if (
|
||||
typeof target[param] === "object" &&
|
||||
target[param] !== null &&
|
||||
!(target[param] instanceof Date)
|
||||
) {
|
||||
target[param] = await resolve(target[param]);
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
const targetingParameters = {
|
||||
...(await resolve(environment)),
|
||||
...(await resolve(localContext)),
|
||||
};
|
||||
|
||||
return targetingParameters;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/* This Source Code Form is subject to the terms of the Mozilla PublicddonMa
|
||||
* 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/. */
|
||||
|
||||
|
@ -290,6 +290,24 @@ const QueryCache = {
|
|||
FRECENT_SITES_UPDATE_INTERVAL,
|
||||
lazy.AddonManager // eslint-disable-line mozilla/valid-lazy
|
||||
),
|
||||
isDefaultHTMLHandler: new CachedTargetingGetter(
|
||||
"isDefaultHandlerFor",
|
||||
[".html"],
|
||||
FRECENT_SITES_UPDATE_INTERVAL,
|
||||
ShellService
|
||||
),
|
||||
isDefaultPDFHandler: new CachedTargetingGetter(
|
||||
"isDefaultHandlerFor",
|
||||
[".pdf"],
|
||||
FRECENT_SITES_UPDATE_INTERVAL,
|
||||
ShellService
|
||||
),
|
||||
defaultPDFHandler: new CachedTargetingGetter(
|
||||
"getDefaultPDFHandler",
|
||||
null,
|
||||
FRECENT_SITES_UPDATE_INTERVAL,
|
||||
ShellService
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -817,6 +835,19 @@ const TargetingGetters = {
|
|||
let button = lazy.CustomizableUI.getWidget("firefox-view-button");
|
||||
return button.areaType;
|
||||
},
|
||||
|
||||
isDefaultHandler: {
|
||||
get html() {
|
||||
return QueryCache.getters.isDefaultHTMLHandler.get();
|
||||
},
|
||||
get pdf() {
|
||||
return QueryCache.getters.isDefaultPDFHandler.get();
|
||||
},
|
||||
},
|
||||
|
||||
get defaultPDFHandler() {
|
||||
return QueryCache.getters.defaultPDFHandler.get();
|
||||
},
|
||||
};
|
||||
|
||||
const ASRouterTargeting = {
|
||||
|
@ -833,26 +864,43 @@ const ASRouterTargeting = {
|
|||
* integer.
|
||||
*/
|
||||
async getEnvironmentSnapshot(target = ASRouterTargeting.Environment) {
|
||||
// One promise for each named property. Label promises with property name.
|
||||
let promises = Object.keys(target).map(async name => {
|
||||
// Each promise needs to check if we're shutting down when it is evaluated.
|
||||
if (Services.startup.shuttingDown) {
|
||||
throw new Error("shutting down, so not querying targeting environment");
|
||||
}
|
||||
return [name, await target[name]];
|
||||
});
|
||||
async function resolveRecursive(object) {
|
||||
// One promise for each named property. Label promises with property name.
|
||||
const promises = Object.keys(object).map(async key => {
|
||||
// Each promise needs to check if we're shutting down when it is evaluated.
|
||||
if (Services.startup.shuttingDown) {
|
||||
throw new Error(
|
||||
"shutting down, so not querying targeting environment"
|
||||
);
|
||||
}
|
||||
|
||||
// Ignore properties that are rejected.
|
||||
let results = await Promise.allSettled(promises);
|
||||
let value = await object[key];
|
||||
|
||||
let environment = {};
|
||||
for (let result of results) {
|
||||
if (result.status === "fulfilled") {
|
||||
let [name, value] = result.value;
|
||||
environment[name] = value;
|
||||
if (
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
!(value instanceof Date)
|
||||
) {
|
||||
value = await resolveRecursive(value);
|
||||
}
|
||||
|
||||
return [key, value];
|
||||
});
|
||||
|
||||
const resolved = {};
|
||||
for (const result of await Promise.allSettled(promises)) {
|
||||
// Ignore properties that are rejected.
|
||||
if (result.status === "fulfilled") {
|
||||
const [key, value] = result.value;
|
||||
resolved[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
const environment = await resolveRecursive(target);
|
||||
|
||||
// Should we need to migrate in the future.
|
||||
const snapshot = { environment, version: 1 };
|
||||
|
||||
|
|
|
@ -20,9 +20,62 @@ add_task(async function should_ignore_rejections() {
|
|||
};
|
||||
|
||||
let snapshot = await ASRouterTargeting.getEnvironmentSnapshot(target);
|
||||
deepEqual(snapshot, { environment: { foo: 1 }, version: 1 });
|
||||
Assert.deepEqual(snapshot, { environment: { foo: 1 }, version: 1 });
|
||||
});
|
||||
|
||||
add_task(async function nested_objects() {
|
||||
const target = {
|
||||
get foo() {
|
||||
return Promise.resolve("foo");
|
||||
},
|
||||
get bar() {
|
||||
return Promise.reject(new Error("bar"));
|
||||
},
|
||||
baz: {
|
||||
get qux() {
|
||||
return Promise.resolve("qux");
|
||||
},
|
||||
get quux() {
|
||||
return Promise.reject(new Error("quux"));
|
||||
},
|
||||
get corge() {
|
||||
return {
|
||||
get grault() {
|
||||
return Promise.resolve("grault");
|
||||
},
|
||||
get garply() {
|
||||
return Promise.reject(new Error("garply"));
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const snapshot = await ASRouterTargeting.getEnvironmentSnapshot(target);
|
||||
|
||||
Assert.deepEqual(
|
||||
snapshot,
|
||||
{
|
||||
environment: {
|
||||
foo: "foo",
|
||||
baz: {
|
||||
qux: "qux",
|
||||
corge: {
|
||||
grault: "grault",
|
||||
},
|
||||
},
|
||||
},
|
||||
version: 1,
|
||||
},
|
||||
"getEnvironmentSnapshot should resolve nested promises"
|
||||
);
|
||||
});
|
||||
|
||||
/*
|
||||
* NB: This test is last because it manipulates shutdown phases.
|
||||
*
|
||||
* Adding tests after this one will result in failures.
|
||||
*/
|
||||
add_task(async function should_ignore_rejections() {
|
||||
// The order that `ASRouterTargeting.getEnvironmentSnapshot`
|
||||
// enumerates the target object matters here, but it's guaranteed to
|
||||
|
@ -50,5 +103,5 @@ add_task(async function should_ignore_rejections() {
|
|||
|
||||
let snapshot = await ASRouterTargeting.getEnvironmentSnapshot(target);
|
||||
// `baz` is dropped since we're shutting down by the time it's processed.
|
||||
deepEqual(snapshot, { environment: { foo: 1, bar: 2 }, version: 1 });
|
||||
Assert.deepEqual(snapshot, { environment: { foo: 1, bar: 2 }, version: 1 });
|
||||
});
|
||||
|
|
|
@ -12,16 +12,13 @@ support-files =
|
|||
keyword_form.html
|
||||
|
||||
[browser_addBookmarkForFrame.js]
|
||||
skip-if = (verify && debug)
|
||||
[browser_autoshow_bookmarks_toolbar.js]
|
||||
[browser_autoshow_bookmarks_toolbar_delayed_apply.js]
|
||||
[browser_bookmarkMenu_hiddenWindow.js]
|
||||
skip-if = os != 'mac' # Mac-only functionality
|
||||
[browser_bookmarkProperties_addFolderDefaultButton.js]
|
||||
[browser_bookmarkProperties_addKeywordForThisSearch.js]
|
||||
skip-if = (verify && debug)
|
||||
[browser_bookmarkProperties_bookmarkAllTabs.js]
|
||||
skip-if = (verify && debug)
|
||||
[browser_bookmarkProperties_cancel.js]
|
||||
[browser_bookmarkProperties_editFolder.js]
|
||||
[browser_bookmarkProperties_editTagContainer.js]
|
||||
|
@ -34,11 +31,8 @@ skip-if = (verify && debug)
|
|||
[browser_bookmarkProperties_xulStore.js]
|
||||
[browser_bookmark_add_tags.js]
|
||||
https_first_disabled = true
|
||||
skip-if =
|
||||
os == "linux" && bits == 64 && os_version == '18.04' # Bug 1711117
|
||||
[browser_bookmark_all_tabs.js]
|
||||
https_first_disabled = true
|
||||
skip-if = (verify && debug && (os == 'linux'))
|
||||
support-files =
|
||||
bookmark_dummy_1.html
|
||||
bookmark_dummy_2.html
|
||||
|
@ -46,36 +40,20 @@ support-files =
|
|||
[browser_bookmark_change_location.js]
|
||||
[browser_bookmark_context_menu_contents.js]
|
||||
[browser_bookmark_copy_folder_tree.js]
|
||||
skip-if = os == 'linux' # times out on linux, Bug 1739081
|
||||
[browser_bookmark_folder_moveability.js]
|
||||
[browser_bookmark_menu_ctrl_click.js]
|
||||
[browser_bookmark_popup.js]
|
||||
skip-if =
|
||||
os == "linux" && asan # Bug 1712814
|
||||
os == "linux" && tsan # Bug 1712814
|
||||
os == "linux" && debug # mouseover not reliable on linux debug builds
|
||||
verify && (os == 'win') # mouseover not reliable on linux debug builds
|
||||
skip-if = verify && os == "win"
|
||||
[browser_bookmark_popup_delayed_apply.js]
|
||||
skip-if =
|
||||
os == "linux" && asan # Bug 1712814
|
||||
os == "linux" && tsan # Bug 1712814
|
||||
os == "linux" && debug # mouseover not reliable on linux debug builds
|
||||
[browser_bookmark_private_window.js]
|
||||
skip-if = (verify && debug && (os == 'win' || os == 'mac'))
|
||||
[browser_bookmark_remove_tags.js]
|
||||
[browser_bookmark_titles.js]
|
||||
https_first_disabled = true
|
||||
support-files = ../../../../base/content/test/general/dummy_page.html
|
||||
skip-if =
|
||||
os == "win" && os_version == "6.1" # Skip on Azure - frequent failure
|
||||
[browser_bookmarklet_windowOpen.js]
|
||||
support-files =
|
||||
bookmarklet_windowOpen_dummy.html
|
||||
[browser_bookmarksProperties.js]
|
||||
skip-if =
|
||||
verify && debug && (os == 'win' || os == 'mac')
|
||||
os == "linux" && (tsan || asan || debug) # Bug 1712814, Bug 1713510
|
||||
|
||||
[browser_bookmarks_change_title.js]
|
||||
[browser_bookmarks_change_url.js]
|
||||
[browser_bookmarks_sidebar_search.js]
|
||||
|
@ -89,37 +67,23 @@ support-files =
|
|||
support-files =
|
||||
favicon-normal16.png
|
||||
[browser_check_correct_controllers.js]
|
||||
skip-if = tsan # Intermittently times out on TSan
|
||||
[browser_click_bookmarks_on_toolbar.js]
|
||||
https_first_disabled = true
|
||||
[browser_controller_onDrop.js]
|
||||
skip-if =
|
||||
os == "linux" && (tsan || asan) # Bug 1714384
|
||||
os == "linux" && debug # Bug 1714384
|
||||
[browser_controller_onDrop_query.js]
|
||||
skip-if =
|
||||
os == "linux" && (tsan || asan || debug) # Bug 1714384 & Bug 1753849
|
||||
[browser_controller_onDrop_sidebar.js]
|
||||
skip-if =
|
||||
os == "linux" && (tsan || asan) # Bug 1714384
|
||||
os == 'linux' && bits == 64 && debug # Bug 1757104
|
||||
[browser_controller_onDrop_tagFolder.js]
|
||||
[browser_copy_query_without_tree.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_cutting_bookmarks.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_default_bookmark_location.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_default_bookmark_location_delayed_apply.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_drag_bookmarks_on_toolbar.js]
|
||||
[browser_drag_folder_on_newTab.js]
|
||||
https_first_disabled = true
|
||||
[browser_editBookmark_keywords.js]
|
||||
skip-if = os == "linux" && bits == 64 && os_version == "18.04" # bug 1569849
|
||||
[browser_editBookmark_tags_liveUpdate.js]
|
||||
[browser_enable_toolbar_sidebar.js]
|
||||
skip-if = (verify && debug && (os == 'mac' || os == 'linux'))
|
||||
skip-if = verify && debug && os == 'win'
|
||||
[browser_forgetthissite.js]
|
||||
[browser_history_sidebar_search.js]
|
||||
[browser_import_button.js]
|
||||
|
@ -127,13 +91,9 @@ skip-if = (verify && debug && (os == 'mac' || os == 'linux'))
|
|||
[browser_library_bookmark_pages.js]
|
||||
[browser_library_bulk_tag_bookmarks.js]
|
||||
[browser_library_commands.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_library_delete.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_library_delete_bookmarks_in_tags.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_library_delete_tags.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_library_downloads.js]
|
||||
[browser_library_history_telemetry.js]
|
||||
[browser_library_left_pane_middleclick.js]
|
||||
|
@ -152,33 +112,23 @@ skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
|||
[browser_library_warnOnOpen.js]
|
||||
[browser_markPageAsFollowedLink.js]
|
||||
[browser_panelview_bookmarks_delete.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_paste_bookmarks.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_paste_into_tags.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_paste_resets_cut_highlights.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_remove_bookmarks.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_sidebar_history_telemetry.js]
|
||||
[browser_sidebar_open_bookmarks.js]
|
||||
[browser_sidebarpanels_click.js]
|
||||
skip-if = (os == "mac" && debug) # Bug 1467049
|
||||
[browser_sort_in_library.js]
|
||||
[browser_stayopenmenu.js]
|
||||
[browser_toolbar_drop_bookmarklet.js]
|
||||
skip-if = os == "linux" # Bug 1751649
|
||||
[browser_toolbar_drop_multiple_with_bookmarklet.js]
|
||||
[browser_toolbar_drop_text.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_toolbar_library_open_recent.js]
|
||||
https_first_disabled = true
|
||||
[browser_toolbar_other_bookmarks.js]
|
||||
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
|
||||
[browser_toolbar_overflow.js]
|
||||
[browser_toolbarbutton_menu_context.js]
|
||||
[browser_toolbarbutton_menu_show_in_folder.js]
|
||||
[browser_views_iconsupdate.js]
|
||||
skip-if = verify
|
||||
[browser_views_liveupdate.js]
|
||||
|
|
|
@ -79,52 +79,59 @@ add_task(async function test_delayed_apply() {
|
|||
set: [["browser.bookmarks.editDialog.delayedApply.enabled", true]],
|
||||
});
|
||||
const win = await BrowserTestUtils.openNewBrowserWindow();
|
||||
await BrowserTestUtils.openNewForegroundTab({
|
||||
gBrowser: win.gBrowser,
|
||||
url: TEST_URL,
|
||||
});
|
||||
registerCleanupFunction(async () => {
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
|
||||
// Init panel
|
||||
win.StarUI._createPanelIfNeeded();
|
||||
const panel = win.document.getElementById("editBookmarkPanel");
|
||||
const star = win.BookmarkingUI.star;
|
||||
let shownPromise = promisePopupShown(panel);
|
||||
star.click();
|
||||
await shownPromise;
|
||||
|
||||
// add a tag
|
||||
await fillBookmarkTextField("editBMPanel_tagsField", testTag, win);
|
||||
const doneButton = win.document.getElementById("editBookmarkPanelDoneButton");
|
||||
let promiseNotification = PlacesTestUtils.waitForNotification(
|
||||
"bookmark-tags-changed"
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser: win.gBrowser,
|
||||
url: TEST_URL,
|
||||
},
|
||||
async () => {
|
||||
// Init panel
|
||||
await TestUtils.waitForCondition(
|
||||
() => BookmarkingUI.status !== BookmarkingUI.STATUS_UPDATING
|
||||
);
|
||||
await clickBookmarkStar(win);
|
||||
|
||||
// add a tag
|
||||
await fillBookmarkTextField("editBMPanel_tagsField", testTag, win);
|
||||
let promiseNotification = PlacesTestUtils.waitForNotification(
|
||||
"bookmark-tags-changed"
|
||||
);
|
||||
await hideBookmarksPanel(win);
|
||||
await promiseNotification;
|
||||
|
||||
// test that the tag has been added in the backend
|
||||
is(PlacesUtils.tagging.getTagsForURI(testURI)[0], testTag, "tags match");
|
||||
|
||||
// change the tag
|
||||
await TestUtils.waitForCondition(
|
||||
() => BookmarkingUI.status !== BookmarkingUI.STATUS_UPDATING
|
||||
);
|
||||
await clickBookmarkStar(win);
|
||||
await fillBookmarkTextField("editBMPanel_tagsField", testTagUpper, win);
|
||||
// The old sync API doesn't notify a tags change, and fixing it would be
|
||||
// quite complex, so we just wait for a title change until tags are
|
||||
// refactored.
|
||||
promiseNotification = PlacesTestUtils.waitForNotification(
|
||||
"bookmark-title-changed"
|
||||
);
|
||||
await hideBookmarksPanel(win);
|
||||
await promiseNotification;
|
||||
|
||||
// test that the tag has been added in the backend
|
||||
is(
|
||||
PlacesUtils.tagging.getTagsForURI(testURI)[0],
|
||||
testTagUpper,
|
||||
"tags match"
|
||||
);
|
||||
|
||||
// Cleanup.
|
||||
PlacesUtils.tagging.untagURI(testURI, [testTag]);
|
||||
await PlacesUtils.bookmarks.remove(bm.guid);
|
||||
}
|
||||
);
|
||||
doneButton.click();
|
||||
await promiseNotification;
|
||||
|
||||
// test that the tag has been added in the backend
|
||||
is(PlacesUtils.tagging.getTagsForURI(testURI)[0], testTag, "tags match");
|
||||
|
||||
// change the tag
|
||||
shownPromise = promisePopupShown(panel);
|
||||
star.click();
|
||||
await shownPromise;
|
||||
await fillBookmarkTextField("editBMPanel_tagsField", testTagUpper, win);
|
||||
// The old sync API doesn't notify a tags change, and fixing it would be
|
||||
// quite complex, so we just wait for a title change until tags are
|
||||
// refactored.
|
||||
promiseNotification = PlacesTestUtils.waitForNotification(
|
||||
"bookmark-title-changed"
|
||||
);
|
||||
doneButton.click();
|
||||
await promiseNotification;
|
||||
|
||||
// test that the tag has been added in the backend
|
||||
is(PlacesUtils.tagging.getTagsForURI(testURI)[0], testTagUpper, "tags match");
|
||||
|
||||
// Cleanup.
|
||||
PlacesUtils.tagging.untagURI(testURI, [testTag]);
|
||||
await PlacesUtils.bookmarks.remove(bm.guid);
|
||||
});
|
||||
|
|
|
@ -14,7 +14,7 @@ add_task(async function bookmark_leak_window() {
|
|||
let tree = library.document.getElementById("placesList");
|
||||
tree.selectItems(["toolbar_____"]);
|
||||
|
||||
await synthesizeClickOnSelectedTreeCell(tree, { type: "mousedown" });
|
||||
await synthesizeClickOnSelectedTreeCell(tree);
|
||||
await promiseLibraryClosed(library);
|
||||
|
||||
Assert.ok(
|
||||
|
|
|
@ -1727,11 +1727,12 @@ var gMainPane = {
|
|||
|
||||
// If we've been opened before, remove the old wizard and insert a
|
||||
// new one to put it back into its starting state.
|
||||
migrationWizardDialog.firstElementChild?.remove();
|
||||
let wizard = document.createElement("migration-wizard");
|
||||
wizard.toggleAttribute("dialog-mode", true);
|
||||
migrationWizardDialog.appendChild(wizard);
|
||||
|
||||
if (!migrationWizardDialog.firstElementChild) {
|
||||
let wizard = document.createElement("migration-wizard");
|
||||
wizard.toggleAttribute("dialog-mode", true);
|
||||
migrationWizardDialog.appendChild(wizard);
|
||||
}
|
||||
migrationWizardDialog.firstElementChild.requestState();
|
||||
migrationWizardDialog.showModal();
|
||||
},
|
||||
|
||||
|
|
|
@ -42,6 +42,10 @@ var { Weave } = ChromeUtils.importESModule(
|
|||
"resource://services-sync/main.sys.mjs"
|
||||
);
|
||||
|
||||
var { FirefoxRelayTelemetry } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/FirefoxRelayTelemetry.mjs"
|
||||
);
|
||||
|
||||
var { FxAccounts, getFxAccountsSingleton } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/FxAccounts.sys.mjs"
|
||||
);
|
||||
|
|
|
@ -2486,11 +2486,12 @@ var gPrivacyPane = {
|
|||
|
||||
toggleRelayIntegration() {
|
||||
const checkbox = document.getElementById("relayIntegration");
|
||||
|
||||
if (checkbox.checked) {
|
||||
FirefoxRelay.markAsEnabled();
|
||||
FirefoxRelayTelemetry.recordRelayPrefEvent("enabled");
|
||||
} else {
|
||||
FirefoxRelay.markAsDisabled();
|
||||
FirefoxRelayTelemetry.recordRelayPrefEvent("disabled");
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ function test() {
|
|||
|
||||
// Now minimize window_B. The selected window shouldn't have the secret data
|
||||
window_B.minimize();
|
||||
waitForFocus(function() {
|
||||
waitForFocus(async function() {
|
||||
state = JSON.parse(ss.getBrowserState());
|
||||
selectedWindow = state.windows[state.selectedWindow - 1];
|
||||
ok(
|
||||
|
@ -33,7 +33,12 @@ function test() {
|
|||
);
|
||||
|
||||
// Now minimize the last open window (assumes no other tests left windows open)
|
||||
let promiseSizeModeChange = BrowserTestUtils.waitForEvent(
|
||||
window,
|
||||
"sizemodechange"
|
||||
);
|
||||
window.minimize();
|
||||
await promiseSizeModeChange;
|
||||
state = JSON.parse(ss.getBrowserState());
|
||||
is(
|
||||
state.selectedWindow,
|
||||
|
|
|
@ -306,7 +306,7 @@ add_task(async function test_pinned_tab_dataloss() {
|
|||
await allTabsRestored;
|
||||
|
||||
let tabs = gBrowser.tabs;
|
||||
BrowserTestUtils.crashFrame(tabs[17].linkedBrowser);
|
||||
await BrowserTestUtils.crashFrame(tabs[17].linkedBrowser);
|
||||
|
||||
await TestUtils.topicObserved("sessionstore-state-write-complete");
|
||||
|
||||
|
|
|
@ -184,6 +184,41 @@ let ShellServiceInternal = {
|
|||
return true;
|
||||
}
|
||||
|
||||
const handler = this.getDefaultPDFHandler();
|
||||
if (handler === null) {
|
||||
// We only get an exception when something went really wrong. Fail
|
||||
// safely: don't set Firefox as default PDF handler.
|
||||
lazy.log.warn(
|
||||
"Could not determine default PDF handler: not setting Firefox as " +
|
||||
"default PDF handler!"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!handler.registered) {
|
||||
lazy.log.debug(
|
||||
"Current default PDF handler has no registered association; " +
|
||||
"should set as default PDF handler."
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (handler.knownBrowser) {
|
||||
lazy.log.debug(
|
||||
"Current default PDF handler progID matches known browser; should " +
|
||||
"set as default PDF handler."
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
lazy.log.debug(
|
||||
"Current default PDF handler progID does not match known browser " +
|
||||
"prefix; should not set as default PDF handler."
|
||||
);
|
||||
return false;
|
||||
},
|
||||
|
||||
getDefaultPDFHandler() {
|
||||
const knownBrowserPrefixes = [
|
||||
"AppXq0fevzme2pys62n3e0fbqa7peapykr8v", // Edge before Blink, per https://stackoverflow.com/a/32724723.
|
||||
"Brave", // For "BraveFile".
|
||||
|
@ -194,6 +229,7 @@ let ShellServiceInternal = {
|
|||
"Opera", // For "OperaStable", presumably varying with channel.
|
||||
"Yandex", // For "YandexPDF.IHKFKZEIOKEMR6BGF62QXCRIKM", presumably varying with installation.
|
||||
];
|
||||
|
||||
let currentProgID = "";
|
||||
try {
|
||||
// Returns the empty string when no association is registered, in
|
||||
|
@ -203,37 +239,26 @@ let ShellServiceInternal = {
|
|||
} catch (e) {
|
||||
// We only get an exception when something went really wrong. Fail
|
||||
// safely: don't set Firefox as default PDF handler.
|
||||
lazy.log.warn(
|
||||
"Failed to queryCurrentDefaultHandlerFor: " +
|
||||
"not setting Firefox as default PDF handler!"
|
||||
);
|
||||
return false;
|
||||
lazy.log.warn("Failed to queryCurrentDefaultHandlerFor:");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (currentProgID == "") {
|
||||
lazy.log.debug(
|
||||
`Current default PDF handler has no registered association; ` +
|
||||
`should set as default PDF handler.`
|
||||
);
|
||||
return true;
|
||||
return { registered: false, knownBrowser: false };
|
||||
}
|
||||
|
||||
let knownBrowserPrefix = knownBrowserPrefixes.find(it =>
|
||||
const knownBrowserPrefix = knownBrowserPrefixes.find(it =>
|
||||
currentProgID.startsWith(it)
|
||||
);
|
||||
|
||||
if (knownBrowserPrefix) {
|
||||
lazy.log.debug(
|
||||
`Current default PDF handler progID matches known browser prefix: ` +
|
||||
`'${knownBrowserPrefix}'; should set as default PDF handler.`
|
||||
);
|
||||
return true;
|
||||
lazy.log.debug(`Found known browser prefix: ${knownBrowserPrefix}`);
|
||||
}
|
||||
|
||||
lazy.log.debug(
|
||||
`Current default PDF handler progID does not match known browser prefix; ` +
|
||||
`should not set as default PDF handler.`
|
||||
);
|
||||
return false;
|
||||
return {
|
||||
registered: true,
|
||||
knownBrowser: !!knownBrowserPrefix,
|
||||
};
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -289,40 +314,13 @@ let ShellServiceInternal = {
|
|||
arguments: exeArgs,
|
||||
});
|
||||
telemetryResult = "ErrOther";
|
||||
|
||||
// Exit codes, see toolkit/mozapps/defaultagent/SetDefaultBrowser.h
|
||||
const S_OK = 0;
|
||||
const STILL_ACTIVE = 0x103;
|
||||
const MOZ_E_NO_PROGID = 0xa0000001;
|
||||
const MOZ_E_HASH_CHECK = 0xa0000002;
|
||||
const MOZ_E_REJECTED = 0xa0000003;
|
||||
const MOZ_E_BUILD = 0xa0000004;
|
||||
|
||||
const exeWaitTimeoutMs = 2000; // 2 seconds
|
||||
const exeWaitPromise = exeProcess.wait();
|
||||
const timeoutPromise = new Promise(function(resolve, reject) {
|
||||
lazy.setTimeout(
|
||||
() => resolve({ exitCode: STILL_ACTIVE }),
|
||||
exeWaitTimeoutMs
|
||||
);
|
||||
});
|
||||
const { exitCode } = await Promise.race([exeWaitPromise, timeoutPromise]);
|
||||
|
||||
if (exitCode != S_OK) {
|
||||
telemetryResult =
|
||||
new Map([
|
||||
[STILL_ACTIVE, "ErrExeTimeout"],
|
||||
[MOZ_E_NO_PROGID, "ErrExeProgID"],
|
||||
[MOZ_E_HASH_CHECK, "ErrExeHash"],
|
||||
[MOZ_E_REJECTED, "ErrExeRejected"],
|
||||
[MOZ_E_BUILD, "ErrBuild"],
|
||||
]).get(exitCode) ?? "ErrExeOther";
|
||||
throw new Error(
|
||||
`WDBA nonzero exit code ${exitCode}: ${telemetryResult}`
|
||||
);
|
||||
this._handleWDBAResult(exeProcess);
|
||||
} catch (ex) {
|
||||
if (ex instanceof WDBAError) {
|
||||
telemetryResult = ex.telemetryResult;
|
||||
}
|
||||
|
||||
telemetryResult = "Success";
|
||||
throw ex;
|
||||
} finally {
|
||||
try {
|
||||
const histogram = Services.telemetry.getHistogramById(
|
||||
|
@ -333,6 +331,43 @@ let ShellServiceInternal = {
|
|||
}
|
||||
},
|
||||
|
||||
async setAsDefaultPDFHandlerUserChoice() {
|
||||
if (AppConstants.platform != "win") {
|
||||
throw new Error("Windows-only");
|
||||
}
|
||||
|
||||
// See comment in setAsDefaultUserChoice for an explanation of why we shell
|
||||
// out to WDBA.
|
||||
let telemetryResult = "ErrOther";
|
||||
|
||||
try {
|
||||
const aumi = lazy.XreDirProvider.getInstallHash();
|
||||
const exeProcess = await this._callExternalDefaultBrowserAgent({
|
||||
arguments: [
|
||||
"set-default-extension-handlers-user-choice",
|
||||
aumi,
|
||||
".pdf",
|
||||
"FirefoxPDF",
|
||||
],
|
||||
});
|
||||
telemetryResult = "ErrOther";
|
||||
this._handleWDBAResult(exeProcess);
|
||||
} catch (ex) {
|
||||
if (ex instanceof WDBAError) {
|
||||
telemetryResult = ex.telemetryResult;
|
||||
}
|
||||
|
||||
throw ex;
|
||||
} finally {
|
||||
try {
|
||||
const histogram = Services.telemetry.getHistogramById(
|
||||
"BROWSER_SET_DEFAULT_PDF_HANDLER_USER_CHOICE_RESULT"
|
||||
);
|
||||
histogram.add(telemetryResult);
|
||||
} catch (ex) {}
|
||||
}
|
||||
},
|
||||
|
||||
// override nsIShellService.setDefaultBrowser() on the ShellService proxy.
|
||||
setDefaultBrowser(claimAllTypes, forAllUsers) {
|
||||
// On Windows 10, our best chance is to set UserChoice, so try that first.
|
||||
|
@ -387,6 +422,16 @@ let ShellServiceInternal = {
|
|||
.add(setAsDefaultError);
|
||||
},
|
||||
|
||||
setAsDefaultPDFHandler(onlyIfKnownBrowser = false) {
|
||||
if (onlyIfKnownBrowser && !this.getDefaultPDFHandler().knownBrowser) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
|
||||
this.setAsDefaultPDFHandlerUserChoice();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if we're the default handler for the given file extension (like
|
||||
* ".pdf") or protocol (like "https"). Windows-only for now.
|
||||
|
@ -467,6 +512,39 @@ let ShellServiceInternal = {
|
|||
}
|
||||
}
|
||||
},
|
||||
|
||||
async _handleWDBAResult(exeProcess, exeWaitTimeoutMs = 2000) {
|
||||
// Exit codes, see toolkit/mozapps/defaultagent/SetDefaultBrowser.h
|
||||
const S_OK = 0;
|
||||
const STILL_ACTIVE = 0x103;
|
||||
const MOZ_E_NO_PROGID = 0xa0000001;
|
||||
const MOZ_E_HASH_CHECK = 0xa0000002;
|
||||
const MOZ_E_REJECTED = 0xa0000003;
|
||||
const MOZ_E_BUILD = 0xa0000004;
|
||||
|
||||
const exeWaitPromise = exeProcess.wait();
|
||||
const timeoutPromise = new Promise(function(resolve) {
|
||||
lazy.setTimeout(
|
||||
() => resolve({ exitCode: STILL_ACTIVE }),
|
||||
exeWaitTimeoutMs
|
||||
);
|
||||
});
|
||||
|
||||
const { exitCode } = await Promise.race([exeWaitPromise, timeoutPromise]);
|
||||
|
||||
if (exitCode != S_OK) {
|
||||
const telemetryResult =
|
||||
new Map([
|
||||
[STILL_ACTIVE, "ErrExeTimeout"],
|
||||
[MOZ_E_NO_PROGID, "ErrExeProgID"],
|
||||
[MOZ_E_HASH_CHECK, "ErrExeHash"],
|
||||
[MOZ_E_REJECTED, "ErrExeRejected"],
|
||||
[MOZ_E_BUILD, "ErrBuild"],
|
||||
]).get(exitCode) ?? "ErrExeOther";
|
||||
|
||||
throw new WDBAError(exitCode, telemetryResult);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetters(ShellServiceInternal, {
|
||||
|
@ -491,3 +569,12 @@ var ShellService = new Proxy(ShellServiceInternal, {
|
|||
return undefined;
|
||||
},
|
||||
});
|
||||
|
||||
class WDBAError extends Error {
|
||||
constructor(exitCode, telemetryResult) {
|
||||
super(`WDBA nonzero exit code ${exitCode}: ${telemetryResult}`);
|
||||
|
||||
this.exitCode = exitCode;
|
||||
this.telemetryResult = telemetryResult;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -219,3 +219,71 @@ add_task(async function remoteDisable() {
|
|||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
add_task(async function test_setAsDefaultPDFHandler_knownBrowser() {
|
||||
const sandbox = sinon.createSandbox();
|
||||
|
||||
const aumi = XreDirProvider.getInstallHash();
|
||||
const expectedArguments = [
|
||||
"set-default-extension-handlers-user-choice",
|
||||
aumi,
|
||||
".pdf",
|
||||
"FirefoxPDF",
|
||||
];
|
||||
|
||||
try {
|
||||
const pdfHandlerResult = { registered: true, knownBrowser: true };
|
||||
sinon.stub(ShellService, "getDefaultPDFHandler").returns(pdfHandlerResult);
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(true) when knownBrowser = true");
|
||||
ShellService.setAsDefaultPDFHandler(true);
|
||||
Assert.ok(
|
||||
_callExternalDefaultBrowserAgentStub.called,
|
||||
"Called default browser agent"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
_callExternalDefaultBrowserAgentStub.firstCall.args,
|
||||
[{ arguments: expectedArguments }],
|
||||
"Called default browser agent with expected arguments"
|
||||
);
|
||||
_callExternalDefaultBrowserAgentStub.resetHistory();
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(false) when knownBrowser = true");
|
||||
ShellService.setAsDefaultPDFHandler(false);
|
||||
Assert.ok(
|
||||
_callExternalDefaultBrowserAgentStub.called,
|
||||
"Called default browser agent"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
_callExternalDefaultBrowserAgentStub.firstCall.args,
|
||||
[{ arguments: expectedArguments }],
|
||||
"Called default browser agent with expected arguments"
|
||||
);
|
||||
_callExternalDefaultBrowserAgentStub.resetHistory();
|
||||
|
||||
pdfHandlerResult.knownBrowser = false;
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(true) when knownBrowser = false");
|
||||
ShellService.setAsDefaultPDFHandler(true);
|
||||
Assert.ok(
|
||||
_callExternalDefaultBrowserAgentStub.notCalled,
|
||||
"Did not call default browser agent"
|
||||
);
|
||||
_callExternalDefaultBrowserAgentStub.resetHistory();
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(false) when knownBrowser = false");
|
||||
ShellService.setAsDefaultPDFHandler(false);
|
||||
Assert.ok(
|
||||
_callExternalDefaultBrowserAgentStub.called,
|
||||
"Called default browser agent"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
_callExternalDefaultBrowserAgentStub.firstCall.args,
|
||||
[{ arguments: expectedArguments }],
|
||||
"Called default browser agent with expected arguments"
|
||||
);
|
||||
_callExternalDefaultBrowserAgentStub.resetHistory();
|
||||
} finally {
|
||||
sandbox.restore();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -89,6 +89,14 @@ const Template = ({ state, dialogMode }) => html`
|
|||
</div>
|
||||
`;
|
||||
|
||||
export const LoadingSkeleton = Template.bind({});
|
||||
LoadingSkeleton.args = {
|
||||
dialogMode: true,
|
||||
state: {
|
||||
page: MigrationWizardConstants.PAGES.LOADING,
|
||||
},
|
||||
};
|
||||
|
||||
export const MainSelectorVariant1 = Template.bind({});
|
||||
MainSelectorVariant1.args = {
|
||||
dialogMode: true,
|
||||
|
|
|
@ -88,7 +88,7 @@ urlbar:
|
|||
`tip_unknown`, `intervention_clear`, `intervention_refresh`,
|
||||
`intervention_update`, `intervention_unknown`, `tab_to_search`,
|
||||
`top_site`, `calc`, `unit`, `suggest_sponsor`,
|
||||
`suggest_non_sponsor`, `quicksuggest` and `weather`.
|
||||
`suggest_non_sponsor`, and `weather`.
|
||||
If the result type did not fall into any of these, this will be
|
||||
`unknown` and a bug should be filed to investigate it.
|
||||
type: string
|
||||
|
@ -168,8 +168,8 @@ urlbar:
|
|||
`tip_onboard`, `tip_persist`, `tip_redirect`, `tip_unknown`,
|
||||
`intervention_clear`, `intervention_refresh`, `intervention_update`,
|
||||
`intervention_unknown`, `tab_to_search`, `top_site`, `calc`, `unit`,
|
||||
`site_specific_contextual_search`, `quicksuggest`, `weather`,
|
||||
`experimental_addon`, `suggest_sponsor` and `suggest_non_sponsor`.
|
||||
`site_specific_contextual_search`, `weather`, `experimental_addon`,
|
||||
`suggest_sponsor` and `suggest_non_sponsor`.
|
||||
If the result type did not fall into any of these, this will be
|
||||
`unknown`.
|
||||
type: string
|
||||
|
@ -228,8 +228,8 @@ urlbar:
|
|||
`tip_unknown`, `intervention_clear`, `intervention_refresh`,
|
||||
`intervention_update`, `intervention_unknown`, `tab_to_search`,
|
||||
`top_site`, `calc`, `unit`, `site_specific_contextual_search`,
|
||||
`quicksuggest`, `weather`, `experimental_addon`, `suggest_sponsor`
|
||||
and `suggest_non_sponsor`. If the result type did not fall into any of
|
||||
`weather`, `experimental_addon`, `suggest_sponsor` and
|
||||
`suggest_non_sponsor`. If the result type did not fall into any of
|
||||
these, this will be `unknown` and a bug should be filed to investigate
|
||||
it. If engagement_type is `drop_go` or `paste_go`, this will be empty
|
||||
string because no results are shown.
|
||||
|
@ -324,10 +324,9 @@ urlbar:
|
|||
`remote_tab`, `addon`, `tip_onboard`, `tip_persist`, `tip_redirect`,
|
||||
`tip_unknown`, `intervention_clear`, `intervention_refresh`,
|
||||
`intervention_update`, `intervention_unknown`, `tab_to_search`,
|
||||
`top_site`, `calc`, `unit`, `suggest_sponsor`,
|
||||
`suggest_non_sponsor`, `quicksuggest` and `weather`. If the result
|
||||
type did not fall into any of these, this will be `unknown` and a bug
|
||||
should be filed to investigate it.
|
||||
`top_site`, `calc`, `unit`, `suggest_sponsor`, `suggest_non_sponsor`,
|
||||
and `weather`. If the result type did not fall into any of these,
|
||||
this will be `unknown` and a bug should be filed to investigate it.
|
||||
type: string
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1800579
|
||||
|
|
|
@ -398,21 +398,6 @@ const AVAILABLE_INJECTIONS = [
|
|||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "bug1754473",
|
||||
platform: "android",
|
||||
domain: "m.intl.taobao.com",
|
||||
bug: "1754473",
|
||||
contentScripts: {
|
||||
matches: ["*://m.intl.taobao.com/*"],
|
||||
css: [
|
||||
{
|
||||
file:
|
||||
"injections/css/bug1754473-m.intl.taobao.com-number-arrow-buttons-overlapping-fix.css",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "bug1748455",
|
||||
platform: "android",
|
||||
|
@ -490,20 +475,6 @@ const AVAILABLE_INJECTIONS = [
|
|||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "bug1778239",
|
||||
platform: "all",
|
||||
domain: "m.pji.co.kr",
|
||||
bug: "1778239",
|
||||
contentScripts: {
|
||||
matches: ["*://m.pji.co.kr/*"],
|
||||
js: [
|
||||
{
|
||||
file: "injections/js/bug1778239-m.pji.co.kr-banner-hide.js",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "bug1774005",
|
||||
platform: "all",
|
||||
|
@ -519,6 +490,7 @@ const AVAILABLE_INJECTIONS = [
|
|||
"*://mobilevikings.be/*/registration/*", // Bug 1797400
|
||||
"*://www.northcountrypublicradio.org/contact/subscribe.html*", // Bug 1778382,
|
||||
"*://www.schoolnutritionandfitness.com/*", // Bug 1793761
|
||||
"*://*.ersthelfer.tv/*", // Bug 1817520
|
||||
],
|
||||
js: [
|
||||
{
|
||||
|
@ -637,8 +609,11 @@ const AVAILABLE_INJECTIONS = [
|
|||
contentScripts: {
|
||||
matches: [
|
||||
"*://*.aptsovation.com/*",
|
||||
"*://*.avanabayview.com/*", // #118617
|
||||
"*://*.breakpointeandcoronado.com/*", // #117735
|
||||
"*://*.liveatlasathens.com/*", // #111189
|
||||
"*://*.liveobserverpark.com/*", // #105244
|
||||
"*://*.midwayurban.com/*", // #116523
|
||||
"*://*.nhcalaska.com/*",
|
||||
"*://*.prospectportal.com/*", // #115206
|
||||
"*://*.securityproperties.com/*",
|
||||
|
@ -739,21 +714,6 @@ const AVAILABLE_INJECTIONS = [
|
|||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "bug1800127",
|
||||
platform: "all",
|
||||
domain: "www.burgerking.es",
|
||||
bug: "1800127",
|
||||
contentScripts: {
|
||||
matches: ["*://www.burgerking.es/*"],
|
||||
css: [
|
||||
{
|
||||
file:
|
||||
"injections/css/bug1800127-www.burgerking.es-webkit-fill-available-fix.css",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "bug1800131",
|
||||
platform: "all",
|
||||
|
@ -801,7 +761,7 @@ const AVAILABLE_INJECTIONS = [
|
|||
},
|
||||
{
|
||||
id: "bug1811325",
|
||||
platform: "all",
|
||||
platform: "desktop",
|
||||
domain: "www.bdo.com.ph",
|
||||
bug: "1811325",
|
||||
customFunc: "acceptLanguageFix",
|
||||
|
@ -809,6 +769,98 @@ const AVAILABLE_INJECTIONS = [
|
|||
urls: ["*://www.bdo.com.ph/*", "*://www.inmac-wstore.com/*"],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "bug1448747",
|
||||
platform: "android",
|
||||
domain: "FastClick breakage",
|
||||
bug: "1448747",
|
||||
contentScripts: {
|
||||
matches: [
|
||||
"*://*.co2meter.com/*", // 10959
|
||||
"*://*.franmar.com/*", // 27273
|
||||
"*://*.themusiclab.org/*", // 49667
|
||||
"*://*.oregonfoodbank.org/*", // 53203
|
||||
"*://*.fourbarrelcoffee.com/*", // 59427
|
||||
"*://bluetokaicoffee.com/*", // 99867
|
||||
"*://bathpublishing.com/*", // 100145
|
||||
"*://dylantalkstone.com/*", // 101356
|
||||
"*://renewd.com.au/*", // 104998
|
||||
"*://gofreeconcepts.de/*", // 105534
|
||||
"*://*.lamudi.co.id/*", // 106767
|
||||
"*://*.thehawksmoor.com/*", // 107549
|
||||
"*://weaversofireland.com/*", // 116816
|
||||
"*://*.iledefrance-mobilites.fr/*", // 117344
|
||||
"*://*.lawnmowerpartsworld.com/*", // 117577
|
||||
"*://*.discountcoffee.co.uk/*", // 118757
|
||||
],
|
||||
js: [
|
||||
{
|
||||
file: "injections/js/bug1448747-fastclick-shim.js",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "bug1818818",
|
||||
platform: "android",
|
||||
domain: "FastClick breakage - legacy",
|
||||
bug: "1818818",
|
||||
contentScripts: {
|
||||
matches: [
|
||||
"*://*.chatiw.com/*", // 5544
|
||||
"*://*.marksandspencer.com/*", // 101811
|
||||
"*://*.wellcare.com/*", // 116595
|
||||
],
|
||||
js: [
|
||||
{
|
||||
file: "injections/js/bug1818818-fastclick-legacy-shim.js",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "bug1819476",
|
||||
platform: "all",
|
||||
domain: "axisbank.com",
|
||||
bug: "1819476",
|
||||
contentScripts: {
|
||||
matches: ["*://*.axisbank.com/*"],
|
||||
js: [
|
||||
{
|
||||
file:
|
||||
"injections/js/bug1819476-axisbank.com-webkitSpeechRecognition-shim.js",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "bug1819450",
|
||||
platform: "android",
|
||||
domain: "cmbchina.com",
|
||||
bug: "1819450",
|
||||
contentScripts: {
|
||||
matches: ["*://www.cmbchina.com/*", "*://cmbchina.com/*"],
|
||||
js: [
|
||||
{
|
||||
file: "injections/js/bug1819450-cmbchina.com-ua-change.js",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "bug1819678",
|
||||
platform: "android",
|
||||
domain: "cnki.net",
|
||||
bug: "1819678",
|
||||
contentScripts: {
|
||||
matches: ["*://*.cnki.net/*"],
|
||||
js: [
|
||||
{
|
||||
file: "injections/js/bug1819678-cnki.net-undisable-search-field.js",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = AVAILABLE_INJECTIONS;
|
||||
|
|
|
@ -928,6 +928,25 @@ const AVAILABLE_UA_OVERRIDES = [
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Bug 1819702 - UA override for feelgoodcontacts.com
|
||||
* Webcompat issue #118030 - https://webcompat.com/issues/118030
|
||||
*
|
||||
* Spoof the UA to receive the mobile version instead
|
||||
* of the broken desktop version for Android.
|
||||
*/
|
||||
id: "bug1819702",
|
||||
platform: "android",
|
||||
domain: "feelgoodcontacts.com",
|
||||
bug: "1819702",
|
||||
config: {
|
||||
matches: ["*://*.feelgoodcontacts.com/*"],
|
||||
uaTransformer: originalUA => {
|
||||
return UAHelpers.getDeviceAppropriateChromeUA();
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = AVAILABLE_UA_OVERRIDES;
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* WebCompat issue #67308 - https://webcompat.com/issues/67308
|
||||
*
|
||||
* The site depends on Sencha Touch and should receive some specific
|
||||
* -moz-box-flex to be working.
|
||||
* -webkit-box-flex be working.
|
||||
*/
|
||||
#home_refresh_var {
|
||||
-moz-box-flex: 1;
|
||||
-webkit-box-flex: 1;
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
/**
|
||||
* m.intl.taobao.com - The quantity value on the shopping cart is hardly
|
||||
* visible due to https://bugzilla.mozilla.org/show_bug.cgi?id=966844,
|
||||
* but a CSS change can fix it in the meantime.
|
||||
* Bug #1754473 - https://bugzilla.mozilla.org/show_bug.cgi?id=1754473
|
||||
*/
|
||||
.o-t-item .item-info .edit-quantity .btn-input input[type="number"] {
|
||||
appearance: textfield;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
/**
|
||||
* www.burgerking.es - Images are not appearing in the carousel
|
||||
* Bug #1800127 - https://bugzilla.mozilla.org/show_bug.cgi?id=1800127
|
||||
* WebCompat issue #111677 - https://webcompat.com/issues/111677
|
||||
*
|
||||
* The page is not adding -moz-available as a fallback for
|
||||
* -webkit-fill-available, which we add here.
|
||||
*/
|
||||
.bk-feat-prod__img,
|
||||
.bk-login__hr2,
|
||||
.bk-hero-for-children__faldon__center {
|
||||
width: -moz-available;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
"use strict";
|
||||
|
||||
/**
|
||||
* Bug 1448747 - Neutralize FastClick
|
||||
*
|
||||
* The patch is applied on sites using FastClick library
|
||||
* to make sure `FastClick.notNeeded` returns `true`.
|
||||
* This allows to disable FastClick and fix various breakage caused
|
||||
* by the library (mainly non-functioning drop-down lists).
|
||||
**/
|
||||
|
||||
/* globals exportFunction */
|
||||
|
||||
(function() {
|
||||
const proto = CSS2Properties.prototype.wrappedJSObject;
|
||||
const descriptor = Object.getOwnPropertyDescriptor(proto, "touchAction");
|
||||
const { get } = descriptor;
|
||||
|
||||
descriptor.get = exportFunction(function() {
|
||||
try {
|
||||
throw Error();
|
||||
} catch (e) {
|
||||
if (e.stack?.includes("notNeeded")) {
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
return get.call(this);
|
||||
}, window);
|
||||
|
||||
Object.defineProperty(proto, "touchAction", descriptor);
|
||||
})();
|
|
@ -1,18 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
/**
|
||||
* Bug 1778239 - Dismiss the "optimized for Chrome" banner on m.pji.co.kr
|
||||
*
|
||||
* This interventions force-sets a window variable `flag` to true, to bypass
|
||||
* the need to use a Chrome user-agent string to hide the banner.
|
||||
*/
|
||||
|
||||
/* globals exportFunction */
|
||||
|
||||
Object.defineProperty(window.wrappedJSObject, "flag", {
|
||||
get: exportFunction(function() {
|
||||
return true;
|
||||
}, window),
|
||||
|
||||
set: exportFunction(function() {}, window),
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
"use strict";
|
||||
|
||||
/**
|
||||
* Bug 1818818 - Neutralize FastClick
|
||||
*
|
||||
* The patch is applied on sites using older version of FastClick library.
|
||||
* This allows to disable FastClick and fix various breakage caused
|
||||
* by the library.
|
||||
**/
|
||||
|
||||
/* globals exportFunction */
|
||||
|
||||
const proto = CSS2Properties.prototype.wrappedJSObject;
|
||||
Object.defineProperty(proto, "msTouchAction", {
|
||||
get: exportFunction(function() {
|
||||
return "none";
|
||||
}, window),
|
||||
|
||||
set: exportFunction(function() {}, window),
|
||||
});
|
|
@ -0,0 +1,25 @@
|
|||
"use strict";
|
||||
|
||||
/**
|
||||
* Bug 1819450 - cmbchina.com - Override UA
|
||||
*
|
||||
* The site is using UA detection to redirect to
|
||||
* m.cmbchina.com (mobile version of the site). Adding `SAMSUNG` allows
|
||||
* to bypass the detection of mobile browser.
|
||||
*/
|
||||
|
||||
/* globals exportFunction */
|
||||
|
||||
console.info(
|
||||
"The user agent has been overridden for compatibility reasons. See https://bugzilla.mozilla.org/show_bug.cgi?id=1081239 for details."
|
||||
);
|
||||
|
||||
const MODIFIED_UA = navigator.userAgent + " SAMSUNG";
|
||||
|
||||
Object.defineProperty(window.navigator.wrappedJSObject, "userAgent", {
|
||||
get: exportFunction(function() {
|
||||
return MODIFIED_UA;
|
||||
}, window),
|
||||
|
||||
set: exportFunction(function() {}, window),
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
"use strict";
|
||||
|
||||
/**
|
||||
* axisbank.com - Shim webkitSpeechRecognition
|
||||
* WebCompat issue #117770 - https://webcompat.com/issues/117770
|
||||
*
|
||||
* The page with bank offerings is not loading options due to the
|
||||
* site relying on webkitSpeechRecognition, which is undefined in Firefox.
|
||||
* Shimming it to `class {}` makes the pages work.
|
||||
*/
|
||||
|
||||
/* globals exportFunction */
|
||||
|
||||
console.info(
|
||||
"webkitSpeechRecognition was shimmed for compatibility reasons. See https://webcompat.com/issues/117770 for details."
|
||||
);
|
||||
|
||||
Object.defineProperty(window.wrappedJSObject, "webkitSpeechRecognition", {
|
||||
value: exportFunction(function() {
|
||||
return class {};
|
||||
}, window),
|
||||
});
|
|
@ -0,0 +1,41 @@
|
|||
"use strict";
|
||||
|
||||
/**
|
||||
* Bug 1819678 - cnki.net - Cannot use search field
|
||||
* WebCompat issue #115777 - https://webcompat.com/issues/115777
|
||||
*
|
||||
* This patch ensures that the search input never has the [disabled]
|
||||
* attribute, so that users may tap/click on it to search.
|
||||
*
|
||||
* See https://bugzilla.mozilla.org/show_bug.cgi?id=1819678 for details.
|
||||
*/
|
||||
|
||||
console.info(
|
||||
"search input disabled attribute was removed for compatibility reasons. See https://webcompat.com/issues/115777 for details."
|
||||
);
|
||||
|
||||
const SELECTOR = `.searchimg[disabled]`;
|
||||
|
||||
function check(target) {
|
||||
if (target.nodeName === "INPUT" && target.matches(SELECTOR)) {
|
||||
target.removeAttribute("disabled");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
new MutationObserver(mutations => {
|
||||
for (const { addedNodes, target, attributeName } of mutations) {
|
||||
if (attributeName === "disabled") {
|
||||
check(target);
|
||||
} else {
|
||||
addedNodes?.forEach(node => {
|
||||
if (!check(node)) {
|
||||
node
|
||||
.querySelectorAll?.(SELECTOR)
|
||||
?.forEach(n => n.removeAttribute("disabled"));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}).observe(document, { attributes: true, childList: true, subtree: true });
|
|
@ -2,7 +2,7 @@
|
|||
"manifest_version": 2,
|
||||
"name": "Web Compatibility Interventions",
|
||||
"description": "Urgent post-release fixes for web compatibility.",
|
||||
"version": "111.1.0",
|
||||
"version": "112.0.0",
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "webcompat@mozilla.org",
|
||||
|
|
|
@ -57,7 +57,6 @@ FINAL_TARGET_FILES.features["webcompat@mozilla.org"]["injections"]["css"] += [
|
|||
"injections/css/bug1712833-buskocchi.desuca.co.jp-fix-map-height.css",
|
||||
"injections/css/bug1741234-patient.alphalabs.ca-height-fix.css",
|
||||
"injections/css/bug1748455-reddit.com-gallery-image-width-fix.css",
|
||||
"injections/css/bug1754473-m.intl.taobao.com-number-arrow-buttons-overlapping-fix.css",
|
||||
"injections/css/bug1765947-veniceincoming.com-left-fix.css",
|
||||
"injections/css/bug1770962-coldwellbankerhomes.com-image-height.css",
|
||||
"injections/css/bug1774490-rainews.it-gallery-fix.css",
|
||||
|
@ -66,12 +65,12 @@ FINAL_TARGET_FILES.features["webcompat@mozilla.org"]["injections"]["css"] += [
|
|||
"injections/css/bug1789164-zdnet.com-cropped-section.css",
|
||||
"injections/css/bug1799994-www.vivobarefoot.com-product-filters-fix.css",
|
||||
"injections/css/bug1800000-www.honda.co.uk-choose-dealer-button-fix.css",
|
||||
"injections/css/bug1800127-www.burgerking.es-webkit-fill-available-fix.css",
|
||||
"injections/css/bug1800143-www.nintendo.co.jp-zoomed-in-image-scrolling-fix.css",
|
||||
]
|
||||
|
||||
FINAL_TARGET_FILES.features["webcompat@mozilla.org"]["injections"]["js"] += [
|
||||
"injections/js/bug0000000-testbed-js-injection.js",
|
||||
"injections/js/bug1448747-fastclick-shim.js",
|
||||
"injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js",
|
||||
"injections/js/bug1457335-histography.io-ua-change.js",
|
||||
"injections/js/bug1472075-bankofamerica.com-ua-change.js",
|
||||
|
@ -84,13 +83,16 @@ FINAL_TARGET_FILES.features["webcompat@mozilla.org"]["injections"]["js"] += [
|
|||
"injections/js/bug1731825-office365-email-handling-prompt-autohide.js",
|
||||
"injections/js/bug1739489-draftjs-beforeinput.js",
|
||||
"injections/js/bug1774005-installtrigger-shim.js",
|
||||
"injections/js/bug1778239-m.pji.co.kr-banner-hide.js",
|
||||
"injections/js/bug1784302-effectiveType-shim.js",
|
||||
"injections/js/bug1795490-www.china-airlines.com-undisable-date-fields-on-mobile.js",
|
||||
"injections/js/bug1799968-www.samsung.com-appVersion-linux-fix.js",
|
||||
"injections/js/bug1799980-healow.com-infinite-loop-fix.js",
|
||||
"injections/js/bug1800131-www.almosafer.com-undisable-date-fields.js",
|
||||
"injections/js/bug1803976-www.youtube.com-performance-now-precision.js",
|
||||
"injections/js/bug1818818-fastclick-legacy-shim.js",
|
||||
"injections/js/bug1819450-cmbchina.com-ua-change.js",
|
||||
"injections/js/bug1819476-axisbank.com-webkitSpeechRecognition-shim.js",
|
||||
"injections/js/bug1819678-cnki.net-undisable-search-field.js",
|
||||
]
|
||||
|
||||
FINAL_TARGET_FILES.features["webcompat@mozilla.org"]["shims"] += [
|
||||
|
|
|
@ -123,7 +123,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "8ad0ee7793893e9ea840c5280350fc3af28d88fb"
|
||||
"revision": "36aaeffbf1a5a8b81448e33b04df3a00bfd15b14"
|
||||
},
|
||||
"bg": {
|
||||
"pin": false,
|
||||
|
@ -285,7 +285,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "3e1bd0a7c61877a5c02525a1662adecceaf25563"
|
||||
"revision": "44fc88f8011e3ae2b792c1065d96b95a90793b42"
|
||||
},
|
||||
"ckb": {
|
||||
"pin": false,
|
||||
|
@ -339,7 +339,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "8095410763433e946c56c38c96a40078758ff74e"
|
||||
"revision": "62321fae7da1268f4b8ad5f30c15df581d7bb35e"
|
||||
},
|
||||
"da": {
|
||||
"pin": false,
|
||||
|
@ -429,7 +429,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "161850caf1ebee864ca1479b3cdad9d543ced621"
|
||||
"revision": "9a2fc1e74b318d213e384e21b3910a9c6ca71ed1"
|
||||
},
|
||||
"en-GB": {
|
||||
"pin": false,
|
||||
|
@ -447,7 +447,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "4d5a21db26c39ea3e1fd45939f8eb73794c914f3"
|
||||
"revision": "742939597ff0700df56c4174f857ef92b5f94bcc"
|
||||
},
|
||||
"eo": {
|
||||
"pin": false,
|
||||
|
@ -483,7 +483,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "6501002e6b877c2749e06c534fb86d38fbb0b7c8"
|
||||
"revision": "57b0bf5298f9f22cca9c0e0a9fabc7f9a224d1c6"
|
||||
},
|
||||
"es-CL": {
|
||||
"pin": false,
|
||||
|
@ -519,7 +519,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "a219c2d30524d5653da6572e1de359cb77843cb0"
|
||||
"revision": "d1e1b22c20ec46f04e248095407067eda62d6ce3"
|
||||
},
|
||||
"es-MX": {
|
||||
"pin": false,
|
||||
|
@ -645,7 +645,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "5991de2eb69e2750920120039baac79e1b958b34"
|
||||
"revision": "7815172192306f36fe83aedee9063995d15f4609"
|
||||
},
|
||||
"fur": {
|
||||
"pin": false,
|
||||
|
@ -663,7 +663,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "6143b07d641a468618fdda8a599d7ab7a91adc00"
|
||||
"revision": "cda35bd7b49bc8dbf65a5301b8c2338540497190"
|
||||
},
|
||||
"fy-NL": {
|
||||
"pin": false,
|
||||
|
@ -753,7 +753,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "050dc5aabc3e7430682768ba1911201eeb26ed79"
|
||||
"revision": "98b35c76df671e4c8b8d7aeca1b465103e8c70e3"
|
||||
},
|
||||
"gu-IN": {
|
||||
"pin": false,
|
||||
|
@ -789,7 +789,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "53a86c82f05147d17606e6da13ec51968041ee28"
|
||||
"revision": "7d60be5175fb64cf22606e99d12709ae060c4fda"
|
||||
},
|
||||
"hi-IN": {
|
||||
"pin": false,
|
||||
|
@ -915,7 +915,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "8893d1667c15a628403686127786f1c1f4ae7157"
|
||||
"revision": "2bbad77391eef37953ff3e711e332a30d41b4b0b"
|
||||
},
|
||||
"id": {
|
||||
"pin": false,
|
||||
|
@ -969,7 +969,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "92c0d518f408c9a34a0d3ad2e423a97558b328ba"
|
||||
"revision": "1a6028d7d98009693f91e0d6e42b5daa2317176a"
|
||||
},
|
||||
"ja": {
|
||||
"pin": false,
|
||||
|
@ -1101,7 +1101,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "c742acfe39481292e6283a867568be81e384b1c2"
|
||||
"revision": "fe83aee5dece7fab148dceafe89242e2d7a259c2"
|
||||
},
|
||||
"lij": {
|
||||
"pin": false,
|
||||
|
@ -1137,7 +1137,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "1b55316c0e299a4eb9fdd88f83a0b55afa940741"
|
||||
"revision": "91d786254a95dc7eeaebcc52811525e65dac6a73"
|
||||
},
|
||||
"lt": {
|
||||
"pin": false,
|
||||
|
@ -1299,7 +1299,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "48a178adb6a181d4673f13256f342c64ff4046fb"
|
||||
"revision": "e284f427b8d73b47b81c647f9dc5c48ea2143f04"
|
||||
},
|
||||
"ne-NP": {
|
||||
"pin": false,
|
||||
|
@ -1371,7 +1371,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "a487b1d59e3eec344d06653deaa9b121f2d646d9"
|
||||
"revision": "4cdae5485409ad64b9e78549b37e450d9049ac48"
|
||||
},
|
||||
"pa-IN": {
|
||||
"pin": false,
|
||||
|
@ -1425,7 +1425,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "7edf282909c4df1dd76326135a69df8aa60379e2"
|
||||
"revision": "e5630e1dd09cda9e426518f665dfb68867339418"
|
||||
},
|
||||
"pt-PT": {
|
||||
"pin": false,
|
||||
|
@ -1641,7 +1641,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "d209af0379c66cf84c056b619e84dc5f613b53eb"
|
||||
"revision": "51ebb18a009fc223ae6247fdd69bb3dfc23cb0f5"
|
||||
},
|
||||
"son": {
|
||||
"pin": false,
|
||||
|
@ -1785,7 +1785,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "52349c514005ae3789a24de9e544515efa3d3895"
|
||||
"revision": "1e8cf1e0cfd0e64c76b34800a161ef27afd5eb73"
|
||||
},
|
||||
"th": {
|
||||
"pin": false,
|
||||
|
@ -1803,7 +1803,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "1f642f0d0e364f7d955a012ec80d952c40347223"
|
||||
"revision": "6cb62f27638845f9337fec887822cc4ea4f169c8"
|
||||
},
|
||||
"tl": {
|
||||
"pin": false,
|
||||
|
@ -1929,7 +1929,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "d7a640ff34d196baeee8a109178932eff54619e8"
|
||||
"revision": "5fbc11466709083d98cb6d313417bf328ccf3c47"
|
||||
},
|
||||
"wo": {
|
||||
"pin": false,
|
||||
|
@ -1983,7 +1983,7 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "20713b2ed832c58ecfd71f492a9aedf965a9d43d"
|
||||
"revision": "16db0e7e1fc4a26c29f642afba97d07fe4035cb1"
|
||||
},
|
||||
"zh-TW": {
|
||||
"pin": false,
|
||||
|
@ -2001,6 +2001,6 @@
|
|||
"win64-aarch64-devedition",
|
||||
"win64-devedition"
|
||||
],
|
||||
"revision": "cdebf7bb2af5b397b749050850b110f3944bc734"
|
||||
"revision": "009494f72ebed524d6f66cc4a6f4145f640e9d35"
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 25.54em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
|
@ -15,6 +16,29 @@ h3 {
|
|||
min-height: 1em;
|
||||
}
|
||||
|
||||
div[name="page-loading"] > .buttons > button {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.loading-block {
|
||||
background-color: var(--in-content-button-background);
|
||||
border-radius: 4px;
|
||||
opacity: 0.4;
|
||||
margin-block-start: 16px;
|
||||
}
|
||||
|
||||
.loading-block.large {
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.loading-block.small {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin-block-start: 16px;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 0;
|
||||
}
|
||||
|
|
|
@ -398,11 +398,53 @@ p {
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
/* The colors for .promo-cta .primary must be kept in sync with the dark mode
|
||||
primary button colors from common-shared.cs */
|
||||
.promo-cta .primary {
|
||||
padding-block: 11px;
|
||||
margin: 8px 0;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
background-color: rgb(0,221,255);
|
||||
color: rgb(43,42,51);
|
||||
}
|
||||
|
||||
.promo-cta .primary:focus-visible {
|
||||
outline-color: rgb(0,221,255);
|
||||
}
|
||||
|
||||
.promo-cta .primary:hover {
|
||||
background-color: rgb(128,235,255) !important;
|
||||
color: rgb(43,42,51) !important;
|
||||
}
|
||||
|
||||
.promo-cta .primary:hover:active {
|
||||
background-color: rgb(170,242,255) !important;
|
||||
color: rgb(43,42,51) !important;
|
||||
}
|
||||
|
||||
@media (prefers-contrast) {
|
||||
.promo-cta .primary {
|
||||
background-color: ButtonText;
|
||||
color: ButtonFace;
|
||||
border-color: ButtonFace;
|
||||
}
|
||||
|
||||
.promo-cta .primary:focus-visible {
|
||||
outline-color: -moz-DialogText;
|
||||
}
|
||||
|
||||
.promo-cta .primary:hover {
|
||||
background-color: SelectedItem !important;
|
||||
color: SelectedItemText !important;
|
||||
border-color: SelectedItemText;
|
||||
}
|
||||
|
||||
.promo-cta .primary:hover:active {
|
||||
background-color: SelectedItemText !important;
|
||||
color: SelectedItem !important;
|
||||
border-color: SelectedItem;
|
||||
}
|
||||
}
|
||||
|
||||
.promo.bottom .promo-cta {
|
||||
|
|
|
@ -127,14 +127,12 @@ function runTests() {
|
|||
console.log("[devtools-node-test-runner] Extract suite argument");
|
||||
const suiteArg = process.argv.find(arg => arg.includes("suite="));
|
||||
const suite = suiteArg.split("=")[1];
|
||||
if (!SUITES[suite]) {
|
||||
if (suite !== "all" && !SUITES[suite]) {
|
||||
throw new Error(
|
||||
"Invalid suite argument to devtools-node-test-runner: " + suite
|
||||
);
|
||||
}
|
||||
|
||||
console.log("[devtools-node-test-runner] Found test suite: " + suite);
|
||||
|
||||
console.log("[devtools-node-test-runner] Check `yarn` is available");
|
||||
try {
|
||||
// This will throw if yarn is unavailable
|
||||
|
@ -147,46 +145,65 @@ function runTests() {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (SUITES[suite].dependencies) {
|
||||
console.log("[devtools-node-test-runner] Running `yarn` for dependencies");
|
||||
for (const dep of SUITES[suite].dependencies) {
|
||||
const depPath = path.join(__dirname, dep);
|
||||
chdir(depPath);
|
||||
const failedSuites = [];
|
||||
const suites = suite == "all" ? SUITES : { [suite]: SUITES[suite] };
|
||||
for (const [suiteName, suiteData] of Object.entries(suites)) {
|
||||
console.log("[devtools-node-test-runner] Running suite: " + suiteName);
|
||||
|
||||
console.log("[devtools-node-test-runner] Run `yarn` in " + depPath);
|
||||
execOut(YARN_PROCESS);
|
||||
if (suiteData.dependencies) {
|
||||
console.log(
|
||||
"[devtools-node-test-runner] Running `yarn` for dependencies"
|
||||
);
|
||||
for (const dep of suiteData.dependencies) {
|
||||
const depPath = path.join(__dirname, dep);
|
||||
chdir(depPath);
|
||||
|
||||
console.log("[devtools-node-test-runner] Run `yarn` in " + depPath);
|
||||
execOut(YARN_PROCESS);
|
||||
}
|
||||
}
|
||||
|
||||
const testPath = path.join(__dirname, suiteData.path);
|
||||
chdir(testPath);
|
||||
|
||||
console.log("[devtools-node-test-runner] Run `yarn` in test folder");
|
||||
execOut(YARN_PROCESS);
|
||||
|
||||
console.log(`TEST START | ${suiteData.type} | ${suiteName}`);
|
||||
|
||||
console.log("[devtools-node-test-runner] Run `yarn test` in test folder");
|
||||
const { out, err } = execOut(YARN_PROCESS, ["test-ci"]);
|
||||
|
||||
if (err) {
|
||||
console.log("[devtools-node-test-runner] Error log");
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
console.log("[devtools-node-test-runner] Parse errors from the test logs");
|
||||
const errors = getErrors(suiteName, out, err) || [];
|
||||
if (errors.length) {
|
||||
failedSuites.push(suiteName);
|
||||
}
|
||||
for (const error of errors) {
|
||||
console.log(
|
||||
`TEST-UNEXPECTED-FAIL | ${suiteData.type} | ${suiteName} | ${error}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const testPath = path.join(__dirname, SUITES[suite].path);
|
||||
chdir(testPath);
|
||||
|
||||
console.log("[devtools-node-test-runner] Run `yarn` in test folder");
|
||||
execOut(YARN_PROCESS);
|
||||
|
||||
console.log(`TEST START | ${SUITES[suite].type} | ${suite}`);
|
||||
|
||||
console.log("[devtools-node-test-runner] Run `yarn test` in test folder");
|
||||
const { out, err } = execOut(YARN_PROCESS, ["test-ci"]);
|
||||
|
||||
if (err) {
|
||||
console.log("[devtools-node-test-runner] Error log");
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
console.log("[devtools-node-test-runner] Parse errors from the test logs");
|
||||
const errors = getErrors(suite, out, err) || [];
|
||||
for (const error of errors) {
|
||||
console.log(
|
||||
`TEST-UNEXPECTED-FAIL | ${SUITES[suite].type} | ${suite} | ${error}`
|
||||
);
|
||||
}
|
||||
|
||||
const success = errors.length === 0;
|
||||
const success = failedSuites.length === 0;
|
||||
if (success) {
|
||||
console.log(`[devtools-node-test-runner] Test suite [${suite}] succeeded`);
|
||||
console.log(
|
||||
`[devtools-node-test-runner] Test suites [${Object.keys(suites).join(
|
||||
", "
|
||||
)}] succeeded`
|
||||
);
|
||||
} else {
|
||||
console.log(`[devtools-node-test-runner] Test suite [${suite}] failed`);
|
||||
console.log(
|
||||
`[devtools-node-test-runner] Test suites [${failedSuites.join(
|
||||
", "
|
||||
)}] failed`
|
||||
);
|
||||
console.log(
|
||||
"[devtools-node-test-runner] You can find documentation about the " +
|
||||
"devtools node tests at https://firefox-source-docs.mozilla.org/devtools/tests/node-tests.html"
|
||||
|
|
|
@ -21,6 +21,13 @@ function initialSourceActorsState() {
|
|||
// Breakable lines object is of the form: { state: <"pending"|"fulfilled">, value: Array<Number> }
|
||||
// The array is the list of all lines where breakpoints can be set
|
||||
mutableBreakableLines: new Map(),
|
||||
|
||||
// Set(Source Actor ID: string)
|
||||
// List of all IDs of source actor which have a valid related source map / original source.
|
||||
// The SourceActor object may have a sourceMapURL attribute set,
|
||||
// but this may be invalid. The source map URL or source map file content may be invalid.
|
||||
// In these scenarios we will remove the source actor from this set.
|
||||
mutableSourceActorsWithSourceMap: new Set(),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -31,6 +38,12 @@ export default function update(state = initialSourceActorsState(), action) {
|
|||
case "INSERT_SOURCE_ACTORS": {
|
||||
for (const sourceActor of action.sourceActors) {
|
||||
state.mutableSourceActors.set(sourceActor.id, sourceActor);
|
||||
|
||||
// If the sourceMapURL attribute is set, consider that it is valid.
|
||||
// But this may be revised later and removed from this Set.
|
||||
if (sourceActor.sourceMapURL) {
|
||||
state.mutableSourceActorsWithSourceMap.add(sourceActor.id);
|
||||
}
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
|
@ -46,6 +59,8 @@ export default function update(state = initialSourceActorsState(), action) {
|
|||
for (const sourceActor of state.mutableSourceActors.values()) {
|
||||
if (sourceActor.thread == action.threadActorID) {
|
||||
state.mutableSourceActors.delete(sourceActor.id);
|
||||
state.mutableBreakableLines.delete(sourceActor.id);
|
||||
state.mutableSourceActorsWithSourceMap.delete(sourceActor.id);
|
||||
}
|
||||
}
|
||||
return {
|
||||
|
@ -57,34 +72,17 @@ export default function update(state = initialSourceActorsState(), action) {
|
|||
return updateBreakableLines(state, action);
|
||||
|
||||
case "CLEAR_SOURCE_ACTOR_MAP_URL":
|
||||
return clearSourceActorMapURL(state, action.sourceActorId);
|
||||
if (state.mutableSourceActorsWithSourceMap.delete(action.sourceActorId)) {
|
||||
return {
|
||||
...state,
|
||||
};
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
function clearSourceActorMapURL(state, sourceActorId) {
|
||||
const existingSourceActor = state.mutableSourceActors.get(sourceActorId);
|
||||
if (!existingSourceActor) {
|
||||
return state;
|
||||
}
|
||||
|
||||
// /!\ We end up mutating sourceActor objects here /!\
|
||||
// The `sourceMapURL` attribute isn't reliable and we must query the selectors
|
||||
// each time we try to interpret its value via sourceActor.sourceMapURL!
|
||||
//
|
||||
// We should probably move this attribute into a dedicated map,
|
||||
// and uncouple it from sourceActor object.
|
||||
state.mutableSourceActors.set(sourceActorId, {
|
||||
...existingSourceActor,
|
||||
sourceMapURL: "",
|
||||
});
|
||||
|
||||
return {
|
||||
...state,
|
||||
};
|
||||
}
|
||||
|
||||
function updateBreakableLines(state, action) {
|
||||
const value = asyncActionAsValue(action);
|
||||
const { sourceActorId } = action;
|
||||
|
|
|
@ -29,6 +29,19 @@ export function getSourceActor(state, sourceActorId) {
|
|||
return state.sourceActors.mutableSourceActors.get(sourceActorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports if the Source Actor relates to a valid source map / original source.
|
||||
*
|
||||
* @param {Object} state
|
||||
* @param {String} sourceActorId
|
||||
* Source Actor ID
|
||||
* @return {Boolean}
|
||||
* True if it has a valid source map/original object.
|
||||
*/
|
||||
export function isSourceActorWithSourceMap(state, sourceActorId) {
|
||||
return state.sourceActors.mutableSourceActorsWithSourceMap.has(sourceActorId);
|
||||
}
|
||||
|
||||
// Used by threads selectors
|
||||
/**
|
||||
* Get all Source Actor objects for a given thread. See create.js:createSourceActor()
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
hasSourceActor,
|
||||
getSourceActor,
|
||||
getBreakableLinesForSourceActors,
|
||||
isSourceActorWithSourceMap,
|
||||
} from "./source-actors";
|
||||
import { getSourceTextContent } from "./sources-content";
|
||||
|
||||
|
@ -208,8 +209,13 @@ export function getSourceActorsForSource(state, id) {
|
|||
}
|
||||
|
||||
export function isSourceWithMap(state, id) {
|
||||
return getSourceActorsForSource(state, id).some(
|
||||
sourceActor => sourceActor.sourceMapURL
|
||||
const actorsInfo = state.sources.actors[id];
|
||||
if (!actorsInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return actorsInfo.some(actorInfo =>
|
||||
isSourceActorWithSourceMap(state, actorInfo.id)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -88,9 +88,8 @@ add_task(async () => {
|
|||
// The source may be reported as pretty printable while we are fetching the sourcemap,
|
||||
// but once the sourcemap is reported to be failing, we no longer report to be pretty printable.
|
||||
await waitFor(
|
||||
() =>
|
||||
dbg.selectors.getSourceActorsForSource(source1.id)[0].sourceMapURL === "",
|
||||
"Wait for the selector source to clear its sourceMapURL"
|
||||
() => !dbg.selectors.isSourceWithMap(source1.id),
|
||||
"Wait for the selector to report the source to be source-map less"
|
||||
);
|
||||
|
||||
assertPrettyPrintButton(dbg, L10N.getStr("sourceTabs.prettyPrint"), false);
|
||||
|
|
|
@ -247,6 +247,7 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [
|
|||
"padding-inline-start",
|
||||
"page-orientation",
|
||||
"math-depth",
|
||||
"-moz-box-collapse",
|
||||
"-moz-box-layout",
|
||||
"-moz-top-layer",
|
||||
"scroll-timeline-axis",
|
||||
|
|
|
@ -27,8 +27,7 @@ function run_test() {
|
|||
);
|
||||
|
||||
// This is a CCW.
|
||||
XPCOMUtils.defineLazyScriptGetter(
|
||||
this, "foo", "chrome://global/content/viewZoomOverlay.js");
|
||||
XPCOMUtils.defineLazyGetter(this, "foo", function() { return "foo"; });
|
||||
`);
|
||||
|
||||
// Neither scripted getter should be considered safe.
|
||||
|
|
|
@ -199,7 +199,7 @@ reformat:
|
|||
::
|
||||
|
||||
// clang-format off
|
||||
my code which should not be reformated
|
||||
my code which should not be reformatted
|
||||
// clang-format on
|
||||
|
||||
You can find an `example of code not
|
||||
|
|
|
@ -112,3 +112,8 @@ It may also be useful to have access to kernel addresses during profiling. These
|
|||
```
|
||||
sudo sh -c "echo 0 > /proc/sys/kernel/kptr_restrict"
|
||||
```
|
||||
|
||||
The max stack depth is 127 by default. This is often too few. It can be increased with:
|
||||
```
|
||||
sudo sh -c "echo 4000 > /proc/sys/kernel/perf_event_max_stack"
|
||||
```
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "ChromeUtils.h"
|
||||
|
||||
#include "JSOracleParent.h"
|
||||
#include "js/CallAndConstruct.h" // JS::Call
|
||||
#include "js/CharacterEncoding.h"
|
||||
#include "js/Object.h" // JS::GetClass
|
||||
#include "js/PropertyAndElement.h" // JS_DefineProperty, JS_DefinePropertyById, JS_Enumerate, JS_GetProperty, JS_GetPropertyById, JS_SetProperty, JS_SetPropertyById, JS::IdVector
|
||||
|
@ -635,11 +634,10 @@ void ChromeUtils::ImportESModule(
|
|||
aRetval.set(moduleNamespace);
|
||||
}
|
||||
|
||||
namespace lazy_getter {
|
||||
namespace module_getter {
|
||||
|
||||
static const size_t SLOT_ID = 0;
|
||||
static const size_t SLOT_URI = 1;
|
||||
static const size_t SLOT_LAMBDA = 1;
|
||||
|
||||
static bool ExtractArgs(JSContext* aCx, JS::CallArgs& aArgs,
|
||||
JS::MutableHandle<JSObject*> aCallee,
|
||||
|
@ -661,56 +659,6 @@ static bool ExtractArgs(JSContext* aCx, JS::CallArgs& aArgs,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool JSLazyGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp) {
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
|
||||
JS::Rooted<JSObject*> callee(aCx);
|
||||
JS::Rooted<JSObject*> thisObj(aCx);
|
||||
JS::Rooted<jsid> id(aCx);
|
||||
if (!ExtractArgs(aCx, args, &callee, &thisObj, &id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> lambda(
|
||||
aCx, js::GetFunctionNativeReserved(callee, SLOT_LAMBDA));
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (!JS::Call(aCx, thisObj, lambda, JS::HandleValueArray::empty(), &value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!JS_DefinePropertyById(aCx, thisObj, id, value, JSPROP_ENUMERATE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().set(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool DefineLazyGetter(JSContext* aCx, JS::Handle<JSObject*> aTarget,
|
||||
JS::Handle<JS::Value> aName,
|
||||
JS::Handle<JSObject*> aLambda) {
|
||||
JS::Rooted<jsid> id(aCx);
|
||||
if (!JS_ValueToId(aCx, aName, &id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> getter(
|
||||
aCx, JS_GetFunctionObject(
|
||||
js::NewFunctionByIdWithReserved(aCx, JSLazyGetter, 0, 0, id)));
|
||||
if (!getter) {
|
||||
JS_ReportOutOfMemory(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
js::SetFunctionNativeReserved(getter, SLOT_ID, aName);
|
||||
|
||||
JS::Rooted<JS::Value> lambdaValue(aCx, JS::ObjectValue(*aLambda));
|
||||
js::SetFunctionNativeReserved(getter, SLOT_LAMBDA, lambdaValue);
|
||||
|
||||
return JS_DefinePropertyById(aCx, aTarget, id, getter, nullptr,
|
||||
JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
enum class ModuleType { JSM, ESM };
|
||||
|
||||
static bool ModuleGetterImpl(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
|
||||
|
@ -872,21 +820,7 @@ static bool DefineESModuleGetter(JSContext* aCx, JS::Handle<JSObject*> aTarget,
|
|||
return JS_DefinePropertyById(aCx, aTarget, aId, getter, setter,
|
||||
JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
} // namespace lazy_getter
|
||||
|
||||
/* static */
|
||||
void ChromeUtils::DefineLazyGetter(const GlobalObject& aGlobal,
|
||||
JS::Handle<JSObject*> aTarget,
|
||||
JS::Handle<JS::Value> aName,
|
||||
JS::Handle<JSObject*> aLambda,
|
||||
ErrorResult& aRv) {
|
||||
JSContext* cx = aGlobal.Context();
|
||||
if (!lazy_getter::DefineLazyGetter(cx, aTarget, aName, aLambda)) {
|
||||
aRv.NoteJSContextException(cx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // namespace module_getter
|
||||
|
||||
/* static */
|
||||
void ChromeUtils::DefineModuleGetter(const GlobalObject& global,
|
||||
|
@ -894,8 +828,8 @@ void ChromeUtils::DefineModuleGetter(const GlobalObject& global,
|
|||
const nsAString& id,
|
||||
const nsAString& resourceURI,
|
||||
ErrorResult& aRv) {
|
||||
if (!lazy_getter::DefineJSModuleGetter(global.Context(), target, id,
|
||||
resourceURI)) {
|
||||
if (!module_getter::DefineJSModuleGetter(global.Context(), target, id,
|
||||
resourceURI)) {
|
||||
aRv.NoteJSContextException(global.Context());
|
||||
}
|
||||
}
|
||||
|
@ -905,7 +839,7 @@ void ChromeUtils::DefineESModuleGetters(const GlobalObject& global,
|
|||
JS::Handle<JSObject*> target,
|
||||
JS::Handle<JSObject*> modules,
|
||||
ErrorResult& aRv) {
|
||||
JSContext* cx = global.Context();
|
||||
auto cx = global.Context();
|
||||
|
||||
JS::Rooted<JS::IdVector> props(cx, JS::IdVector(cx));
|
||||
if (!JS_Enumerate(cx, modules, &props)) {
|
||||
|
@ -928,7 +862,8 @@ void ChromeUtils::DefineESModuleGetters(const GlobalObject& global,
|
|||
return;
|
||||
}
|
||||
|
||||
if (!lazy_getter::DefineESModuleGetter(cx, target, prop, resourceURIVal)) {
|
||||
if (!module_getter::DefineESModuleGetter(cx, target, prop,
|
||||
resourceURIVal)) {
|
||||
aRv.NoteJSContextException(cx);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -209,11 +209,6 @@ class ChromeUtils {
|
|||
JS::MutableHandle<JSObject*> aRetval,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static void DefineLazyGetter(const GlobalObject& aGlobal,
|
||||
JS::Handle<JSObject*> aTarget,
|
||||
JS::Handle<JS::Value> aName,
|
||||
JS::Handle<JSObject*> aLambda, ErrorResult& aRv);
|
||||
|
||||
static void DefineModuleGetter(const GlobalObject& global,
|
||||
JS::Handle<JSObject*> target,
|
||||
const nsAString& id,
|
||||
|
|
|
@ -90,12 +90,13 @@ already_AddRefed<Selection> Highlight::CreateHighlightSelection(
|
|||
selection->SetHighlightName(aHighlightName);
|
||||
AutoFrameSelectionBatcher selectionBatcher(__FUNCTION__);
|
||||
selectionBatcher.AddFrameSelection(aFrameSelection);
|
||||
// NOLINTNEXTLINE(performance-for-range-copy)
|
||||
for (const RefPtr<AbstractRange> range : mRanges) {
|
||||
for (const RefPtr<AbstractRange>& range : mRanges) {
|
||||
if (range->GetComposedDocOfContainers() ==
|
||||
aFrameSelection->GetPresShell()->GetDocument()) {
|
||||
selection->AddHighlightRangeAndSelectFramesAndNotifyListeners(*range,
|
||||
aRv);
|
||||
// since this is run in a context guarded by a selection batcher,
|
||||
// no strong reference is needed to keep `range` alive.
|
||||
selection->AddHighlightRangeAndSelectFramesAndNotifyListeners(
|
||||
MOZ_KnownLive(*range), aRv);
|
||||
}
|
||||
}
|
||||
return selection.forget();
|
||||
|
@ -110,12 +111,14 @@ void Highlight::Add(AbstractRange& aRange, ErrorResult& aRv) {
|
|||
mRanges.AppendElement(&aRange);
|
||||
AutoFrameSelectionBatcher selectionBatcher(__FUNCTION__,
|
||||
mHighlightRegistries.Count());
|
||||
for (const RefPtr<HighlightRegistry> registry :
|
||||
for (const RefPtr<HighlightRegistry>& registry :
|
||||
mHighlightRegistries.Keys()) {
|
||||
auto frameSelection = registry->GetFrameSelection();
|
||||
selectionBatcher.AddFrameSelection(frameSelection);
|
||||
|
||||
registry->MaybeAddRangeToHighlightSelection(aRange, *this, aRv);
|
||||
// since this is run in a context guarded by a selection batcher,
|
||||
// no strong reference is needed to keep `registry` alive.
|
||||
MOZ_KnownLive(registry)->MaybeAddRangeToHighlightSelection(aRange, *this,
|
||||
aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
@ -134,8 +137,8 @@ void Highlight::Clear(ErrorResult& aRv) {
|
|||
mHighlightRegistries.Keys()) {
|
||||
auto frameSelection = registry->GetFrameSelection();
|
||||
selectionBatcher.AddFrameSelection(frameSelection);
|
||||
// Because of the selection batcher, this call does *not* run script.
|
||||
// MOZ_KnownLive() is needed regardless.
|
||||
// since this is run in a context guarded by a selection batcher,
|
||||
// no strong reference is needed to keep `registry` alive.
|
||||
MOZ_KnownLive(registry)->RemoveHighlightSelection(*this);
|
||||
}
|
||||
}
|
||||
|
@ -151,8 +154,8 @@ bool Highlight::Delete(AbstractRange& aRange, ErrorResult& aRv) {
|
|||
mHighlightRegistries.Keys()) {
|
||||
auto frameSelection = registry->GetFrameSelection();
|
||||
selectionBatcher.AddFrameSelection(frameSelection);
|
||||
// Because of the selection batcher, this call does *not* run script.
|
||||
// MOZ_KnownLive() is needed regardless.
|
||||
// since this is run in a context guarded by a selection batcher,
|
||||
// no strong reference is needed to keep `registry` alive.
|
||||
MOZ_KnownLive(registry)->MaybeRemoveRangeFromHighlightSelection(aRange,
|
||||
*this);
|
||||
}
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
* Implementation of mozilla::dom::Selection
|
||||
*/
|
||||
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "Selection.h"
|
||||
|
||||
#include "ErrorList.h"
|
||||
#include "LayoutConstants.h"
|
||||
#include "mozilla/intl/BidiEmbeddingLevel.h"
|
||||
|
||||
#include "mozilla/AccessibleCaretEventHub.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/AutoCopyListener.h"
|
||||
|
@ -27,6 +27,7 @@
|
|||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/HTMLEditor.h"
|
||||
#include "mozilla/IntegerRange.h"
|
||||
#include "mozilla/intl/BidiEmbeddingLevel.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/RangeBoundary.h"
|
||||
|
@ -1318,7 +1319,7 @@ void Selection::Clear(nsPresContext* aPresContext) {
|
|||
|
||||
mStyledRanges.UnregisterSelection();
|
||||
for (uint32_t i = 0; i < mStyledRanges.Length(); ++i) {
|
||||
SelectFrames(aPresContext, mStyledRanges.mRanges[i].mRange, false);
|
||||
SelectFrames(aPresContext, *mStyledRanges.mRanges[i].mRange, false);
|
||||
}
|
||||
mStyledRanges.Clear();
|
||||
|
||||
|
@ -1656,7 +1657,7 @@ void Selection::SelectFramesInAllRanges(nsPresContext* aPresContext) {
|
|||
for (size_t i = 0; i < mStyledRanges.Length(); ++i) {
|
||||
nsRange* range = mStyledRanges.mRanges[i].mRange->AsDynamicRange();
|
||||
MOZ_ASSERT(range->IsInAnySelection());
|
||||
SelectFrames(aPresContext, range, range->IsInAnySelection());
|
||||
SelectFrames(aPresContext, *range, range->IsInAnySelection());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1665,32 +1666,37 @@ void Selection::SelectFramesInAllRanges(nsPresContext* aPresContext) {
|
|||
* traversing through the frames
|
||||
*/
|
||||
nsresult Selection::SelectFrames(nsPresContext* aPresContext,
|
||||
AbstractRange* aRange, bool aSelect) const {
|
||||
AbstractRange& aRange, bool aSelect) const {
|
||||
if (!mFrameSelection || !aPresContext || !aPresContext->GetPresShell()) {
|
||||
// nothing to do
|
||||
return NS_OK;
|
||||
}
|
||||
MOZ_ASSERT(aRange && aRange->IsPositioned());
|
||||
|
||||
if (aRange->IsStaticRange() && !aRange->AsStaticRange()->IsValid()) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aRange.IsPositioned());
|
||||
|
||||
const Document* const document = GetDocument();
|
||||
if (MOZ_UNLIKELY(!document ||
|
||||
aRange.GetComposedDocOfContainers() != document)) {
|
||||
return NS_OK; // Do nothing if the range is now in different document.
|
||||
}
|
||||
|
||||
if (aRange.IsStaticRange() && !aRange.AsStaticRange()->IsValid()) {
|
||||
// TODO jjaschke: Actions necessary to unselect invalid static ranges?
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mFrameSelection->IsInTableSelectionMode()) {
|
||||
nsINode* node = aRange->GetClosestCommonInclusiveAncestor();
|
||||
nsIFrame* frame = node->IsContent()
|
||||
? node->AsContent()->GetPrimaryFrame()
|
||||
: aPresContext->PresShell()->GetRootFrame();
|
||||
const nsIContent* const commonAncestorContent =
|
||||
nsIContent::FromNodeOrNull(aRange.GetClosestCommonInclusiveAncestor());
|
||||
nsIFrame* const frame = commonAncestorContent
|
||||
? commonAncestorContent->GetPrimaryFrame()
|
||||
: aPresContext->PresShell()->GetRootFrame();
|
||||
if (frame) {
|
||||
if (frame->IsTextFrame()) {
|
||||
nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
|
||||
|
||||
MOZ_ASSERT(node == aRange->GetStartContainer());
|
||||
MOZ_ASSERT(node == aRange->GetEndContainer());
|
||||
textFrame->SelectionStateChanged(aRange->StartOffset(),
|
||||
aRange->EndOffset(), aSelect,
|
||||
mSelectionType);
|
||||
MOZ_ASSERT(commonAncestorContent == aRange.GetStartContainer());
|
||||
MOZ_ASSERT(commonAncestorContent == aRange.GetEndContainer());
|
||||
static_cast<nsTextFrame*>(frame)->SelectionStateChanged(
|
||||
aRange.StartOffset(), aRange.EndOffset(), aSelect, mSelectionType);
|
||||
} else {
|
||||
frame->SelectionStateChanged();
|
||||
}
|
||||
|
@ -1701,36 +1707,36 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext,
|
|||
|
||||
// Loop through the content iterator for each content node; for each text
|
||||
// node, call SetSelected on it:
|
||||
nsINode* startNode = aRange->GetStartContainer();
|
||||
nsIContent* startContent =
|
||||
startNode->IsContent() ? startNode->AsContent() : nullptr;
|
||||
if (!startContent) {
|
||||
nsIContent* const startContent =
|
||||
nsIContent::FromNodeOrNull(aRange.GetStartContainer());
|
||||
if (MOZ_UNLIKELY(!startContent)) {
|
||||
// Don't warn, bug 1055722
|
||||
// XXX The range can start from a document node and such range can be
|
||||
// added to Selection with JS. Therefore, even in such cases,
|
||||
// shouldn't we handle selection in the range?
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
MOZ_DIAGNOSTIC_ASSERT(startContent->IsInComposedDoc());
|
||||
|
||||
// We must call first one explicitly
|
||||
bool isFirstContentTextNode = startContent->IsText();
|
||||
nsINode* endNode = aRange->GetEndContainer();
|
||||
nsINode* const endNode = aRange.GetEndContainer();
|
||||
if (NS_WARN_IF(!endNode)) {
|
||||
// We null-checked start node above, therefore, end node should also be
|
||||
// non-null here.
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
const bool isFirstContentTextNode = startContent->IsText();
|
||||
if (isFirstContentTextNode) {
|
||||
nsIFrame* frame = startContent->GetPrimaryFrame();
|
||||
// The frame could be an SVG text frame, in which case we don't treat it
|
||||
// as a text frame.
|
||||
if (frame) {
|
||||
if (nsIFrame* const frame = startContent->GetPrimaryFrame()) {
|
||||
// The frame could be an SVG text frame, in which case we don't treat it
|
||||
// as a text frame.
|
||||
if (frame->IsTextFrame()) {
|
||||
nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
|
||||
uint32_t startOffset = aRange->StartOffset();
|
||||
uint32_t endOffset;
|
||||
if (endNode == startContent) {
|
||||
endOffset = aRange->EndOffset();
|
||||
} else {
|
||||
endOffset = startContent->Length();
|
||||
}
|
||||
textFrame->SelectionStateChanged(startOffset, endOffset, aSelect,
|
||||
mSelectionType);
|
||||
const uint32_t startOffset = aRange.StartOffset();
|
||||
const uint32_t endOffset = endNode == startContent
|
||||
? aRange.EndOffset()
|
||||
: startContent->Length();
|
||||
static_cast<nsTextFrame*>(frame)->SelectionStateChanged(
|
||||
startOffset, endOffset, aSelect, mSelectionType);
|
||||
} else {
|
||||
frame->SelectionStateChanged();
|
||||
}
|
||||
|
@ -1739,8 +1745,8 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext,
|
|||
|
||||
// If the range is in a node and the node is a leaf node, we don't need to
|
||||
// walk the subtree.
|
||||
if (aRange->Collapsed() ||
|
||||
(startNode == endNode && !startNode->HasChildren())) {
|
||||
if (aRange.Collapsed() ||
|
||||
(startContent == endNode && !startContent->HasChildren())) {
|
||||
if (!isFirstContentTextNode) {
|
||||
SelectFramesOf(startContent, aSelect);
|
||||
}
|
||||
|
@ -1748,38 +1754,31 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext,
|
|||
}
|
||||
|
||||
ContentSubtreeIterator subtreeIter;
|
||||
subtreeIter.Init(aRange);
|
||||
subtreeIter.Init(&aRange);
|
||||
if (isFirstContentTextNode && !subtreeIter.IsDone() &&
|
||||
subtreeIter.GetCurrentNode() == startNode) {
|
||||
subtreeIter.GetCurrentNode() == startContent) {
|
||||
subtreeIter.Next(); // first content has already been handled.
|
||||
}
|
||||
PostContentIterator postOrderIter;
|
||||
for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
|
||||
nsINode* node = subtreeIter.GetCurrentNode();
|
||||
MOZ_ASSERT(node);
|
||||
nsIContent* content = node->IsContent() ? node->AsContent() : nullptr;
|
||||
SelectFramesOfInclusiveDescendantsOfContent(postOrderIter, content,
|
||||
aSelect);
|
||||
MOZ_DIAGNOSTIC_ASSERT(subtreeIter.GetCurrentNode());
|
||||
if (nsIContent* const content =
|
||||
nsIContent::FromNodeOrNull(subtreeIter.GetCurrentNode())) {
|
||||
SelectFramesOfInclusiveDescendantsOfContent(postOrderIter, content,
|
||||
aSelect);
|
||||
}
|
||||
}
|
||||
|
||||
// We must now do the last one if it is not the same as the first
|
||||
if (endNode != startNode) {
|
||||
nsIContent* endContent =
|
||||
endNode->IsContent() ? endNode->AsContent() : nullptr;
|
||||
// XXX The range can end at a document node and such range can be
|
||||
// added to Selection with JS. Therefore, even in such cases,
|
||||
// shouldn't we handle selection in the range?
|
||||
if (NS_WARN_IF(!endContent)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
if (endContent->IsText()) {
|
||||
nsIFrame* frame = endContent->GetPrimaryFrame();
|
||||
// The frame could be an SVG text frame, in which case we'll ignore it.
|
||||
if (frame && frame->IsTextFrame()) {
|
||||
nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
|
||||
textFrame->SelectionStateChanged(0, aRange->EndOffset(), aSelect,
|
||||
mSelectionType);
|
||||
}
|
||||
if (endNode == startContent || !endNode->IsText()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (nsIFrame* const frame = endNode->AsText()->GetPrimaryFrame()) {
|
||||
// The frame could be an SVG text frame, in which case we'll ignore it.
|
||||
if (frame->IsTextFrame()) {
|
||||
static_cast<nsTextFrame*>(frame)->SelectionStateChanged(
|
||||
0, aRange.EndOffset(), aSelect, mSelectionType);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
|
@ -1909,8 +1908,9 @@ Selection::Repaint(nsPresContext* aPresContext) {
|
|||
int32_t i;
|
||||
|
||||
for (i = 0; i < arrCount; i++) {
|
||||
MOZ_ASSERT(mStyledRanges.mRanges[i].mRange);
|
||||
nsresult rv =
|
||||
SelectFrames(aPresContext, mStyledRanges.mRanges[i].mRange, true);
|
||||
SelectFrames(aPresContext, *mStyledRanges.mRanges[i].mRange, true);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
|
@ -2226,7 +2226,7 @@ void Selection::AddRangeAndSelectFramesAndNotifyListenersInternal(
|
|||
}
|
||||
|
||||
RefPtr<nsPresContext> presContext = GetPresContext();
|
||||
SelectFrames(presContext, range, true);
|
||||
SelectFrames(presContext, *range, true);
|
||||
|
||||
// Be aware, this instance may be destroyed after this call.
|
||||
NotifySelectionListeners();
|
||||
|
@ -2246,7 +2246,7 @@ void Selection::AddHighlightRangeAndSelectFramesAndNotifyListeners(
|
|||
}
|
||||
|
||||
RefPtr<nsPresContext> presContext = GetPresContext();
|
||||
SelectFrames(presContext, &aRange, true);
|
||||
SelectFrames(presContext, aRange, true);
|
||||
|
||||
// Be aware, this instance may be destroyed after this call.
|
||||
NotifySelectionListeners();
|
||||
|
@ -2301,7 +2301,7 @@ void Selection::RemoveRangeAndUnselectFramesAndNotifyListeners(
|
|||
|
||||
// clear the selected bit from the removed range's frames
|
||||
RefPtr<nsPresContext> presContext = GetPresContext();
|
||||
SelectFrames(presContext, &aRange, false);
|
||||
SelectFrames(presContext, aRange, false);
|
||||
|
||||
// add back the selected bit for each range touching our nodes
|
||||
nsTArray<AbstractRange*> affectedRanges;
|
||||
|
@ -2312,7 +2312,8 @@ void Selection::RemoveRangeAndUnselectFramesAndNotifyListeners(
|
|||
return;
|
||||
}
|
||||
for (uint32_t i = 0; i < affectedRanges.Length(); i++) {
|
||||
SelectFrames(presContext, affectedRanges[i], true);
|
||||
MOZ_ASSERT(affectedRanges[i]);
|
||||
SelectFrames(presContext, *affectedRanges[i], true);
|
||||
}
|
||||
|
||||
if (&aRange == mAnchorFocusRange) {
|
||||
|
@ -2472,7 +2473,7 @@ void Selection::CollapseInternal(InLimiter aInLimiter,
|
|||
return;
|
||||
}
|
||||
SetAnchorFocusRange(0);
|
||||
SelectFrames(presContext, range, true);
|
||||
SelectFrames(presContext, *range, true);
|
||||
|
||||
RefPtr<Selection> kungFuDeathGrip{this};
|
||||
// Be aware, this instance may be destroyed after this call.
|
||||
|
@ -2643,9 +2644,9 @@ void Selection::ReplaceAnchorFocusRange(nsRange* aRange) {
|
|||
NS_ENSURE_TRUE_VOID(mAnchorFocusRange);
|
||||
RefPtr<nsPresContext> presContext = GetPresContext();
|
||||
if (presContext) {
|
||||
SelectFrames(presContext, mAnchorFocusRange, false);
|
||||
SelectFrames(presContext, *mAnchorFocusRange, false);
|
||||
SetAnchorFocusToRange(aRange);
|
||||
SelectFrames(presContext, mAnchorFocusRange, true);
|
||||
SelectFrames(presContext, *mAnchorFocusRange, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2788,7 +2789,7 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset,
|
|||
// resulting in a range that selects nothing.
|
||||
if (shouldClearRange) {
|
||||
// Repaint the current range with the selection removed.
|
||||
SelectFrames(presContext, range, false);
|
||||
SelectFrames(presContext, *range, false);
|
||||
|
||||
res = range->CollapseTo(&aContainer, aOffset);
|
||||
if (NS_FAILED(res)) {
|
||||
|
@ -2818,7 +2819,7 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset,
|
|||
aRv.Throw(res);
|
||||
return;
|
||||
}
|
||||
SelectFrames(presContext, difRange, true);
|
||||
SelectFrames(presContext, *difRange, true);
|
||||
res = SetAnchorFocusToRange(range);
|
||||
if (NS_FAILED(res)) {
|
||||
aRv.Throw(res);
|
||||
|
@ -2832,7 +2833,7 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset,
|
|||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
SelectFrames(presContext, range, true);
|
||||
SelectFrames(presContext, *range, true);
|
||||
res = SetAnchorFocusToRange(range);
|
||||
if (NS_FAILED(res)) {
|
||||
aRv.Throw(res);
|
||||
|
@ -2857,10 +2858,10 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset,
|
|||
aRv.Throw(res);
|
||||
return;
|
||||
}
|
||||
SelectFrames(presContext, difRange, false); // deselect now
|
||||
SelectFrames(presContext, *difRange, false); // deselect now
|
||||
difRange->SetEnd(range->GetEndContainer(), range->EndOffset());
|
||||
SelectFrames(presContext, difRange, true); // must reselect last node
|
||||
// maybe more
|
||||
SelectFrames(presContext, *difRange, true); // must reselect last node
|
||||
// maybe more
|
||||
} else if (*anchorOldFocusOrder >= 0 &&
|
||||
*anchorNewFocusOrder <= 0) { // 1,a,2 or 1a,2 or 1,a2 or 1a2
|
||||
if (GetDirection() == eDirPrevious) {
|
||||
|
@ -2892,7 +2893,7 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset,
|
|||
return;
|
||||
}
|
||||
// deselect from 1 to a
|
||||
SelectFrames(presContext, difRange, false);
|
||||
SelectFrames(presContext, *difRange, false);
|
||||
} else {
|
||||
res = SetAnchorFocusToRange(range);
|
||||
if (NS_FAILED(res)) {
|
||||
|
@ -2901,7 +2902,7 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset,
|
|||
}
|
||||
}
|
||||
// select from a to 2
|
||||
SelectFrames(presContext, range, true);
|
||||
SelectFrames(presContext, *range, true);
|
||||
} else if (*oldFocusNewFocusOrder <= 0 &&
|
||||
*anchorNewFocusOrder >= 0) { // 1,2,a or 12,a or 1,2a or 12a
|
||||
// deselect from 1 to 2
|
||||
|
@ -2922,9 +2923,9 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset,
|
|||
aRv.Throw(res);
|
||||
return;
|
||||
}
|
||||
SelectFrames(presContext, difRange, false);
|
||||
SelectFrames(presContext, *difRange, false);
|
||||
difRange->SetStart(range->GetStartContainer(), range->StartOffset());
|
||||
SelectFrames(presContext, difRange, true); // must reselect last node
|
||||
SelectFrames(presContext, *difRange, true); // must reselect last node
|
||||
} else if (*anchorNewFocusOrder >= 0 &&
|
||||
*anchorOldFocusOrder <= 0) { // 2,a,1 or 2a,1 or 2,a1 or 2a1
|
||||
if (GetDirection() == eDirNext) {
|
||||
|
@ -2948,7 +2949,7 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset,
|
|||
aRv.Throw(res);
|
||||
return;
|
||||
}
|
||||
SelectFrames(presContext, difRange, false);
|
||||
SelectFrames(presContext, *difRange, false);
|
||||
} else {
|
||||
res = SetAnchorFocusToRange(range);
|
||||
if (NS_FAILED(res)) {
|
||||
|
@ -2957,7 +2958,7 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset,
|
|||
}
|
||||
}
|
||||
// select from 2 to a
|
||||
SelectFrames(presContext, range, true);
|
||||
SelectFrames(presContext, *range, true);
|
||||
} else if (*oldFocusNewFocusOrder >= 0 &&
|
||||
*anchorOldFocusOrder >= 0) { // 2,1,a or 21,a or 2,1a or 21a
|
||||
// select from 2 to 1
|
||||
|
@ -2974,7 +2975,7 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset,
|
|||
return;
|
||||
}
|
||||
|
||||
SelectFrames(presContext, difRange, true);
|
||||
SelectFrames(presContext, *difRange, true);
|
||||
res = SetAnchorFocusToRange(range);
|
||||
if (NS_FAILED(res)) {
|
||||
aRv.Throw(res);
|
||||
|
|
|
@ -808,7 +808,7 @@ class Selection final : public nsSupportsWeakReference,
|
|||
PostContentIterator& aPostOrderIter, nsIContent* aContent,
|
||||
bool aSelected) const;
|
||||
|
||||
nsresult SelectFrames(nsPresContext* aPresContext, AbstractRange* aRange,
|
||||
nsresult SelectFrames(nsPresContext* aPresContext, AbstractRange& aRange,
|
||||
bool aSelect) const;
|
||||
|
||||
/**
|
||||
|
|
78
dom/base/test/fullscreen/file_fullscreen-single.html
Normal file
78
dom/base/test/fullscreen/file_fullscreen-single.html
Normal file
|
@ -0,0 +1,78 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
|
||||
Open one window, focus it and enter fullscreen, then exit fullscreen.
|
||||
|
||||
-->
|
||||
<head>
|
||||
<title>Simple Fullscreen Enter and Exit Test</title>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="file_fullscreen-utils.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="fullscreen-div"><p>Fullscreen div</p></div>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
function ok(condition, msg) {
|
||||
opener.ok(condition, "[single] " + msg);
|
||||
}
|
||||
|
||||
function is(value, expected, msg) {
|
||||
opener.is(value, expected, "[single] " + msg);
|
||||
}
|
||||
|
||||
function isnot(value, unexpected, msg) {
|
||||
opener.isnot(value, unexpected, "[single] " + msg);
|
||||
}
|
||||
|
||||
function info(msg) {
|
||||
opener.info("[single] " + msg);
|
||||
}
|
||||
|
||||
function windowResized() {
|
||||
info(`Window resized to width: ${window.innerWidth}, height: ${window.innerHeight}.`);
|
||||
}
|
||||
|
||||
async function begin() {
|
||||
window.addEventListener('resize', windowResized);
|
||||
|
||||
info(`Starting window width: ${window.innerWidth}, height: ${window.innerHeight}.`);
|
||||
let windowedWidth = window.innerWidth;
|
||||
let windowedHeight = window.innerHeight;
|
||||
|
||||
info("Requesting fullscreen.");
|
||||
let entryPromise = document.getElementById('fullscreen-div').requestFullscreen()
|
||||
info("Fullscreen requested, waiting for promise to resolve.");
|
||||
|
||||
await entryPromise;
|
||||
|
||||
info("element.requestFullscreen() promise resolved.");
|
||||
info(`Fullscreen window width: ${window.innerWidth}, height: ${window.innerHeight}.`);
|
||||
isnot(document.fullscreenElement, null, "document.fullscreenElement should exist.");
|
||||
ok(window.fullScreen, "window.fullScreen");
|
||||
isnot(windowedWidth, window.innerWidth, "window width should be changed.");
|
||||
isnot(windowedHeight, window.innerHeight, "window height should be changed.");
|
||||
|
||||
info("Requesting fullscreen exit.");
|
||||
let exitPromise = document.exitFullscreen()
|
||||
info("Fullscreen exit requested, waiting for promise to resolve.");
|
||||
|
||||
await exitPromise;
|
||||
|
||||
info("document.exitFullscreen() promise resolved.");
|
||||
info(`Restored window width: ${window.innerWidth}, height: ${window.innerHeight}.`);
|
||||
is(document.fullscreenElement, null, "document.fullscreenElement should be null.");
|
||||
ok(!window.fullScreen, "window.fullScreen should be false.");
|
||||
is(window.innerWidth, windowedWidth, "window width should be restored.");
|
||||
is(window.innerHeight, windowedHeight, "window height should be restored.");
|
||||
opener.nextTest();
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,8 +1,8 @@
|
|||
[DEFAULT]
|
||||
tags = fullscreen
|
||||
support-files =
|
||||
file_fullscreen-api.html
|
||||
file_fullscreen-api-race.html
|
||||
file_fullscreen-api.html
|
||||
file_fullscreen-async.html
|
||||
file_fullscreen-backdrop.html
|
||||
file_fullscreen-denied-inner.html
|
||||
|
@ -27,6 +27,7 @@ support-files =
|
|||
file_fullscreen-scrollbar.html
|
||||
file_fullscreen-selector.html
|
||||
file_fullscreen-shadowdom.html
|
||||
file_fullscreen-single.html
|
||||
file_fullscreen-sub-iframe.html
|
||||
file_fullscreen-svg-element.html
|
||||
file_fullscreen-table.html
|
||||
|
@ -34,14 +35,13 @@ support-files =
|
|||
file_fullscreen-utils.js
|
||||
file_fullscreen-with-full-zoom.html
|
||||
|
||||
[test_fullscreen-api-race.html]
|
||||
skip-if = toolkit == 'android' # same as test_fullscreen-api.html, 1356570
|
||||
os == "mac" && debug
|
||||
[test_fullscreen-api.html]
|
||||
allow_xul_xbl = true # XUL is used in file_fullscreen-api.html
|
||||
skip-if =
|
||||
toolkit == 'android'
|
||||
os == 'mac' && bits == 64 && debug # Bug 1579623
|
||||
[test_fullscreen-api-race.html]
|
||||
skip-if = toolkit == 'android' # same as test_fullscreen-api.html, 1356570
|
||||
verify && debug && os == 'mac'
|
||||
os == "mac" && debug
|
||||
[test_fullscreen_meta_viewport.html]
|
||||
[test_fullscreen_modal.html]
|
||||
|
|
|
@ -28,6 +28,7 @@ SimpleTest.requestFlakyTimeout("untriaged");
|
|||
// run in an iframe, which by default will not have the allowfullscreen
|
||||
// attribute set, so full-screen won't work.
|
||||
var gTestWindows = [
|
||||
{ test: "file_fullscreen-single.html" },
|
||||
{ test: "file_fullscreen-multiple.html",
|
||||
prefs: [["full-screen-api.exit-on.windowRaise", false],
|
||||
["full-screen-api.exit-on.windowOpen", false]] },
|
||||
|
@ -72,6 +73,7 @@ function nextTest() {
|
|||
info("main window focused, starting next test");
|
||||
SimpleTest.executeSoon(runNextTest);
|
||||
}, {once: true});
|
||||
info("testWindow.close()");
|
||||
testWindow.close();
|
||||
} else {
|
||||
SimpleTest.executeSoon(runNextTest);
|
||||
|
|
|
@ -839,20 +839,20 @@ void CanvasGradient::AddColorStop(float aOffset, const nsACString& aColorstr,
|
|||
return aRv.ThrowIndexSizeError("Offset out of 0-1.0 range");
|
||||
}
|
||||
|
||||
PresShell* presShell = mContext ? mContext->GetPresShell() : nullptr;
|
||||
ServoStyleSet* styleSet = presShell ? presShell->StyleSet() : nullptr;
|
||||
if (!mContext) {
|
||||
return aRv.ThrowSyntaxError("No canvas context");
|
||||
}
|
||||
|
||||
nscolor color;
|
||||
bool ok = ServoCSSParser::ComputeColor(styleSet, NS_RGB(0, 0, 0), aColorstr,
|
||||
&color);
|
||||
if (!ok) {
|
||||
auto color = mContext->ParseColor(
|
||||
aColorstr, CanvasRenderingContext2D::ResolveCurrentColor::No);
|
||||
if (!color) {
|
||||
return aRv.ThrowSyntaxError("Invalid color");
|
||||
}
|
||||
|
||||
GradientStop newStop;
|
||||
|
||||
newStop.offset = aOffset;
|
||||
newStop.color = ToDeviceColor(color);
|
||||
newStop.color = ToDeviceColor(*color);
|
||||
|
||||
mRawStops.AppendElement(newStop);
|
||||
}
|
||||
|
@ -1083,32 +1083,44 @@ JSObject* CanvasRenderingContext2D::WrapObject(
|
|||
return CanvasRenderingContext2D_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
bool CanvasRenderingContext2D::ParseColor(const nsACString& aString,
|
||||
nscolor* aColor) {
|
||||
CanvasRenderingContext2D::ColorStyleCacheEntry
|
||||
CanvasRenderingContext2D::ParseColorSlow(const nsACString& aString) {
|
||||
ColorStyleCacheEntry result{nsCString(aString)};
|
||||
Document* document = mCanvasElement ? mCanvasElement->OwnerDoc() : nullptr;
|
||||
css::Loader* loader = document ? document->CSSLoader() : nullptr;
|
||||
|
||||
PresShell* presShell = GetPresShell();
|
||||
ServoStyleSet* set = presShell ? presShell->StyleSet() : nullptr;
|
||||
|
||||
// First, try computing the color without handling currentcolor.
|
||||
bool wasCurrentColor = false;
|
||||
if (!ServoCSSParser::ComputeColor(set, NS_RGB(0, 0, 0), aString, aColor,
|
||||
&wasCurrentColor, loader)) {
|
||||
return false;
|
||||
nscolor color;
|
||||
if (ServoCSSParser::ComputeColor(set, NS_RGB(0, 0, 0), aString, &color,
|
||||
&wasCurrentColor, loader)) {
|
||||
result.mWasCurrentColor = wasCurrentColor;
|
||||
result.mColor.emplace(color);
|
||||
}
|
||||
|
||||
if (wasCurrentColor && mCanvasElement) {
|
||||
// Otherwise, get the value of the color property, flushing style
|
||||
// if necessary.
|
||||
return result;
|
||||
}
|
||||
|
||||
Maybe<nscolor> CanvasRenderingContext2D::ParseColor(
|
||||
const nsACString& aString, ResolveCurrentColor aResolveCurrentColor) {
|
||||
auto entry = mColorStyleCache.Lookup(aString);
|
||||
if (!entry) {
|
||||
entry.Set(ParseColorSlow(aString));
|
||||
}
|
||||
|
||||
const auto& data = entry.Data();
|
||||
if (data.mWasCurrentColor && mCanvasElement &&
|
||||
aResolveCurrentColor == ResolveCurrentColor::Yes) {
|
||||
// If it was currentColor, get the value of the color property, flushing
|
||||
// style if necessary.
|
||||
RefPtr<const ComputedStyle> canvasStyle =
|
||||
nsComputedDOMStyle::GetComputedStyle(mCanvasElement);
|
||||
if (canvasStyle) {
|
||||
*aColor = canvasStyle->StyleText()->mColor.ToColor();
|
||||
return Some(canvasStyle->StyleText()->mColor.ToColor());
|
||||
}
|
||||
// Beware that the presShell could be gone here.
|
||||
}
|
||||
return true;
|
||||
return data.mColor;
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2D::ResetBitmap(bool aFreeBuffer) {
|
||||
|
@ -1171,12 +1183,12 @@ void CanvasRenderingContext2D::SetStyleFromString(const nsACString& aStr,
|
|||
Style aWhichStyle) {
|
||||
MOZ_ASSERT(!aStr.IsVoid());
|
||||
|
||||
nscolor color;
|
||||
if (!ParseColor(aStr, &color)) {
|
||||
Maybe<nscolor> color = ParseColor(aStr);
|
||||
if (!color) {
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentState().SetColorStyle(aWhichStyle, color);
|
||||
CurrentState().SetColorStyle(aWhichStyle, *color);
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2D::GetStyleAsUnion(
|
||||
|
@ -2325,12 +2337,12 @@ already_AddRefed<CanvasPattern> CanvasRenderingContext2D::CreatePattern(
|
|||
// shadows
|
||||
//
|
||||
void CanvasRenderingContext2D::SetShadowColor(const nsACString& aShadowColor) {
|
||||
nscolor color;
|
||||
if (!ParseColor(aShadowColor, &color)) {
|
||||
Maybe<nscolor> color = ParseColor(aShadowColor);
|
||||
if (!color) {
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentState().shadowColor = color;
|
||||
CurrentState().shadowColor = *color;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -3514,7 +3526,7 @@ bool CanvasRenderingContext2D::SetFontInternal(const nsACString& aFont,
|
|||
}
|
||||
|
||||
nsPresContext* c = presShell->GetPresContext();
|
||||
CacheKey key{aFont, c->RestyleManager()->GetRestyleGeneration()};
|
||||
FontStyleCacheKey key{aFont, c->RestyleManager()->GetRestyleGeneration()};
|
||||
auto entry = mFontStyleCache.Lookup(key);
|
||||
if (!entry) {
|
||||
FontStyleData newData;
|
||||
|
@ -5372,8 +5384,8 @@ void CanvasRenderingContext2D::DrawWindow(nsGlobalWindowInner& aWindow,
|
|||
return;
|
||||
}
|
||||
|
||||
nscolor backgroundColor;
|
||||
if (!ParseColor(aBgColor, &backgroundColor)) {
|
||||
Maybe<nscolor> backgroundColor = ParseColor(aBgColor);
|
||||
if (!backgroundColor) {
|
||||
aError.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
@ -5459,7 +5471,7 @@ void CanvasRenderingContext2D::DrawWindow(nsGlobalWindowInner& aWindow,
|
|||
|
||||
RefPtr<PresShell> presShell = presContext->PresShell();
|
||||
|
||||
Unused << presShell->RenderDocument(r, renderDocFlags, backgroundColor,
|
||||
Unused << presShell->RenderDocument(r, renderDocFlags, *backgroundColor,
|
||||
&thebes.ref());
|
||||
// If this canvas was contained in the drawn window, the pre-transaction
|
||||
// callback may have returned its DT. If so, we must reacquire it here.
|
||||
|
|
|
@ -133,6 +133,10 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
|
|||
}
|
||||
}
|
||||
|
||||
enum class ResolveCurrentColor : bool { No, Yes };
|
||||
Maybe<nscolor> ParseColor(const nsACString&,
|
||||
ResolveCurrentColor = ResolveCurrentColor::Yes);
|
||||
|
||||
void GetGlobalCompositeOperation(nsAString& aOp,
|
||||
mozilla::ErrorResult& aError) override;
|
||||
void SetGlobalCompositeOperation(const nsAString& aOp,
|
||||
|
@ -572,9 +576,6 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
|
|||
void GetStyleAsUnion(OwningUTF8StringOrCanvasGradientOrCanvasPattern& aValue,
|
||||
Style aWhichStyle);
|
||||
|
||||
// Returns whether a color was successfully parsed.
|
||||
bool ParseColor(const nsACString& aString, nscolor* aColor);
|
||||
|
||||
static void StyleColorToString(const nscolor& aColor, nsACString& aStr);
|
||||
|
||||
// Returns whether a filter was successfully parsed.
|
||||
|
@ -1010,28 +1011,29 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
|
|||
return mStyleStack[mStyleStack.Length() - 1];
|
||||
}
|
||||
|
||||
struct CacheKey {
|
||||
CacheKey() : mFont(), mGeneration(0) {}
|
||||
CacheKey(const nsACString& aFont, uint64_t aGeneration)
|
||||
struct FontStyleCacheKey {
|
||||
FontStyleCacheKey() = default;
|
||||
FontStyleCacheKey(const nsACString& aFont, uint64_t aGeneration)
|
||||
: mFont(aFont), mGeneration(aGeneration) {}
|
||||
nsCString mFont;
|
||||
uint64_t mGeneration;
|
||||
uint64_t mGeneration = 0;
|
||||
};
|
||||
|
||||
struct FontStyleData {
|
||||
CacheKey mKey;
|
||||
FontStyleCacheKey mKey;
|
||||
nsCString mUsedFont;
|
||||
RefPtr<const ComputedStyle> mStyle;
|
||||
};
|
||||
|
||||
class FontStyleCache
|
||||
: public MruCache<CacheKey, FontStyleData, FontStyleCache> {
|
||||
: public MruCache<FontStyleCacheKey, FontStyleData, FontStyleCache> {
|
||||
public:
|
||||
static HashNumber Hash(const CacheKey& aKey) {
|
||||
static HashNumber Hash(const FontStyleCacheKey& aKey) {
|
||||
HashNumber hash = HashString(aKey.mFont);
|
||||
return AddToHash(hash, aKey.mGeneration);
|
||||
}
|
||||
static bool Match(const CacheKey& aKey, const FontStyleData& aVal) {
|
||||
static bool Match(const FontStyleCacheKey& aKey,
|
||||
const FontStyleData& aVal) {
|
||||
return aVal.mKey.mGeneration == aKey.mGeneration &&
|
||||
aVal.mKey.mFont == aKey.mFont;
|
||||
}
|
||||
|
@ -1039,6 +1041,24 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
|
|||
|
||||
FontStyleCache mFontStyleCache;
|
||||
|
||||
struct ColorStyleCacheEntry {
|
||||
nsCString mKey;
|
||||
Maybe<nscolor> mColor;
|
||||
bool mWasCurrentColor = false;
|
||||
};
|
||||
class ColorStyleCache
|
||||
: public MruCache<nsACString, ColorStyleCacheEntry, ColorStyleCache> {
|
||||
public:
|
||||
static HashNumber Hash(const nsACString& aKey) { return HashString(aKey); }
|
||||
static bool Match(const nsACString& aKey,
|
||||
const ColorStyleCacheEntry& aVal) {
|
||||
return aVal.mKey == aKey;
|
||||
}
|
||||
};
|
||||
ColorStyleCache mColorStyleCache;
|
||||
|
||||
ColorStyleCacheEntry ParseColorSlow(const nsACString&);
|
||||
|
||||
friend class CanvasGeneralPattern;
|
||||
friend class AdjustedTarget;
|
||||
friend class AdjustedTargetForShadow;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "mozilla/gfx/PathSkia.h"
|
||||
#include "mozilla/gfx/Swizzle.h"
|
||||
#include "mozilla/layers/ImageDataSerializer.h"
|
||||
#include "skia/include/core/SkPixmap.h"
|
||||
|
||||
#include "ClientWebGLContext.h"
|
||||
#include "WebGLChild.h"
|
||||
|
@ -634,7 +635,7 @@ bool DrawTargetWebgl::GenerateComplexClipMask() {
|
|||
mClipBounds = *clip;
|
||||
} else {
|
||||
// If we can't get bounds, then just use the entire viewport.
|
||||
mClipBounds = IntRect(IntPoint(), mSize);
|
||||
mClipBounds = GetRect();
|
||||
}
|
||||
// If initializing the clip mask, then allocate the entire texture to ensure
|
||||
// all pixels get filled with an empty mask regardless. Otherwise, restrict
|
||||
|
@ -672,9 +673,8 @@ bool DrawTargetWebgl::GenerateComplexClipMask() {
|
|||
// Finally, upload the texture data and initialize texture storage if
|
||||
// necessary.
|
||||
if (init && mClipBounds.Size() != mSize) {
|
||||
mSharedContext->UploadSurface(nullptr, SurfaceFormat::A8,
|
||||
IntRect(IntPoint(), mSize), IntPoint(), true,
|
||||
true);
|
||||
mSharedContext->UploadSurface(nullptr, SurfaceFormat::A8, GetRect(),
|
||||
IntPoint(), true, true);
|
||||
init = false;
|
||||
}
|
||||
mSharedContext->UploadSurface(data, SurfaceFormat::A8,
|
||||
|
@ -703,8 +703,8 @@ bool DrawTargetWebgl::SetSimpleClipRect() {
|
|||
// trivially discard the draw request.
|
||||
// If the clip rect is larger than the viewport, just set it to the
|
||||
// viewport.
|
||||
if (!clip->IsEmpty() && clip->Contains(IntRect(IntPoint(), mSize))) {
|
||||
clip = Some(IntRect(IntPoint(), mSize));
|
||||
if (!clip->IsEmpty() && clip->Contains(GetRect())) {
|
||||
clip = Some(GetRect());
|
||||
}
|
||||
mSharedContext->SetClipRect(*clip);
|
||||
mSharedContext->SetNoClipMask();
|
||||
|
@ -716,7 +716,7 @@ bool DrawTargetWebgl::SetSimpleClipRect() {
|
|||
bool DrawTargetWebgl::PrepareContext(bool aClipped) {
|
||||
if (!aClipped) {
|
||||
// If no clipping requested, just set the clip rect to the viewport.
|
||||
mSharedContext->SetClipRect(IntRect(IntPoint(), mSize));
|
||||
mSharedContext->SetClipRect(GetRect());
|
||||
mSharedContext->SetNoClipMask();
|
||||
// Ensure the clip gets reset if clipping is later requested for the target.
|
||||
mRefreshClipState = true;
|
||||
|
@ -984,6 +984,12 @@ bool DrawTargetWebgl::SharedContext::ReadInto(uint8_t* aDstData,
|
|||
mWebgl->FramebufferTexture2D(
|
||||
LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D,
|
||||
aHandle->GetWebGLTexture(), 0);
|
||||
} else if (mCurrentTarget && mCurrentTarget->mIsClear) {
|
||||
// If reading from a target that is still clear, then avoid the readback by
|
||||
// just clearing the data.
|
||||
SkPixmap(MakeSkiaImageInfo(aBounds.Size(), aFormat), aDstData, aDstStride)
|
||||
.erase(IsOpaque(aFormat) ? SK_ColorBLACK : SK_ColorTRANSPARENT);
|
||||
return true;
|
||||
}
|
||||
|
||||
webgl::ReadPixelsDesc desc;
|
||||
|
@ -1075,6 +1081,7 @@ bool DrawTargetWebgl::MarkChanged() {
|
|||
return false;
|
||||
}
|
||||
mSkiaValid = false;
|
||||
mIsClear = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1321,14 +1328,33 @@ bool DrawTargetWebgl::SharedContext::CreateShaders() {
|
|||
return true;
|
||||
}
|
||||
|
||||
inline ColorPattern DrawTargetWebgl::GetClearPattern() const {
|
||||
return ColorPattern(
|
||||
DeviceColor(0.0f, 0.0f, 0.0f, IsOpaque(mFormat) ? 1.0f : 0.0f));
|
||||
}
|
||||
|
||||
void DrawTargetWebgl::ClearRect(const Rect& aRect) {
|
||||
// OP_SOURCE may not be bounded by a mask, so we ensure that a clip is pushed
|
||||
// here to avoid a group being pushed for it.
|
||||
PushClipRect(aRect);
|
||||
ColorPattern pattern(
|
||||
DeviceColor(0.0f, 0.0f, 0.0f, IsOpaque(mFormat) ? 1.0f : 0.0f));
|
||||
DrawRect(aRect, pattern, DrawOptions(1.0f, CompositionOp::OP_SOURCE));
|
||||
DrawRect(aRect, GetClearPattern(),
|
||||
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
|
||||
PopClip();
|
||||
|
||||
// If the clear rectangle encompasses the entire viewport and is not clipped,
|
||||
// then mark the target as entirely clear.
|
||||
if (mTransform.PreservesAxisAlignedRectangles() &&
|
||||
mTransform.TransformBounds(aRect).Contains(Rect(GetRect())) &&
|
||||
mSharedContext->IsCurrentTarget(this) && !mSharedContext->HasClipMask() &&
|
||||
mSharedContext->mClipRect.Contains(GetRect())) {
|
||||
mIsClear = true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline DeviceColor PremultiplyColor(const DeviceColor& aColor,
|
||||
float aAlpha = 1.0f) {
|
||||
float a = aColor.a * aAlpha;
|
||||
return DeviceColor(aColor.r * a, aColor.g * a, aColor.b * a, a);
|
||||
}
|
||||
|
||||
// Attempts to create the framebuffer used for drawing and also any relevant
|
||||
|
@ -1349,7 +1375,8 @@ bool DrawTargetWebgl::CreateFramebuffer() {
|
|||
LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D,
|
||||
mTex, 0);
|
||||
webgl->Viewport(0, 0, mSize.width, mSize.height);
|
||||
webgl->ClearColor(0.0f, 0.0f, 0.0f, IsOpaque(mFormat) ? 1.0f : 0.0f);
|
||||
DeviceColor color = PremultiplyColor(GetClearPattern().mColor);
|
||||
webgl->ClearColor(color.b, color.g, color.r, color.a);
|
||||
webgl->Clear(LOCAL_GL_COLOR_BUFFER_BIT);
|
||||
mSharedContext->ClearTarget();
|
||||
mSharedContext->ClearLastTexture();
|
||||
|
@ -1360,9 +1387,17 @@ bool DrawTargetWebgl::CreateFramebuffer() {
|
|||
void DrawTargetWebgl::CopySurface(SourceSurface* aSurface,
|
||||
const IntRect& aSourceRect,
|
||||
const IntPoint& aDestination) {
|
||||
// Intersect the source and destination rectangles with the viewport bounds.
|
||||
IntRect destRect =
|
||||
IntRect(aDestination, aSourceRect.Size()).SafeIntersect(GetRect());
|
||||
IntRect srcRect = destRect - aDestination + aSourceRect.TopLeft();
|
||||
if (srcRect.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSkiaValid) {
|
||||
if (mSkiaLayer) {
|
||||
if (IntRect(aDestination, aSourceRect.Size()).Contains(GetRect())) {
|
||||
if (destRect.Contains(GetRect())) {
|
||||
// If the the destination would override the entire layer, discard the
|
||||
// layer.
|
||||
mSkiaLayer = false;
|
||||
|
@ -1375,15 +1410,36 @@ void DrawTargetWebgl::CopySurface(SourceSurface* aSurface,
|
|||
// If there is no layer, copying is safe.
|
||||
MarkSkiaChanged();
|
||||
}
|
||||
mSkia->CopySurface(aSurface, aSourceRect, aDestination);
|
||||
mSkia->CopySurface(aSurface, srcRect, destRect.TopLeft());
|
||||
return;
|
||||
}
|
||||
|
||||
Matrix matrix = Matrix::Translation(aDestination - aSourceRect.TopLeft());
|
||||
if (!mSharedContext->IsCompatibleSurface(aSurface)) {
|
||||
// If this data surface completely overwrites the framebuffer, then just
|
||||
// copy it to the Skia target.
|
||||
if (destRect.Contains(GetRect())) {
|
||||
MarkSkiaChanged(true);
|
||||
mSkia->DetachAllSnapshots();
|
||||
mSkiaNoClip->CopySurface(aSurface, srcRect, destRect.TopLeft());
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is a data surface with a matching format, try to upload the data
|
||||
// directly to the backing texture of the framebuffer.
|
||||
if (aSurface->GetFormat() == mFormat && PrepareContext(false) &&
|
||||
MarkChanged()) {
|
||||
if (RefPtr<DataSourceSurface> data = aSurface->GetDataSurface()) {
|
||||
mSharedContext->UploadSurface(data, mFormat, srcRect,
|
||||
destRect.TopLeft(), false, false, mTex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Matrix matrix = Matrix::Translation(destRect.TopLeft() - srcRect.TopLeft());
|
||||
SurfacePattern pattern(aSurface, ExtendMode::CLAMP, matrix);
|
||||
DrawRect(Rect(IntRect(aDestination, aSourceRect.Size())), pattern,
|
||||
DrawOptions(1.0f, CompositionOp::OP_SOURCE), Nothing(), nullptr,
|
||||
false, false);
|
||||
DrawRect(Rect(destRect), pattern, DrawOptions(1.0f, CompositionOp::OP_SOURCE),
|
||||
Nothing(), nullptr, false, false);
|
||||
}
|
||||
|
||||
void DrawTargetWebgl::PushClip(const Path* aPath) {
|
||||
|
@ -1469,6 +1525,11 @@ bool DrawTargetWebgl::SharedContext::SupportsPattern(const Pattern& aPattern) {
|
|||
return false;
|
||||
}
|
||||
if (surfacePattern.mSurface) {
|
||||
// If the surface is already uploaded to a texture, then just use it.
|
||||
if (IsCompatibleSurface(surfacePattern.mSurface)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
IntSize size = surfacePattern.mSurface->GetSize();
|
||||
// The maximum size a surface can be before triggering a fallback to
|
||||
// software. Bound the maximum surface size by the actual texture size
|
||||
|
@ -1607,7 +1668,8 @@ void DrawTargetWebgl::DrawRectFallback(const Rect& aRect,
|
|||
}
|
||||
|
||||
inline already_AddRefed<WebGLTextureJS>
|
||||
DrawTargetWebgl::SharedContext::GetCompatibleSnapshot(SourceSurface* aSurface) {
|
||||
DrawTargetWebgl::SharedContext::GetCompatibleSnapshot(
|
||||
SourceSurface* aSurface) const {
|
||||
if (aSurface->GetType() == SurfaceType::WEBGL) {
|
||||
RefPtr<SourceSurfaceWebgl> webglSurf =
|
||||
static_cast<SourceSurfaceWebgl*>(aSurface);
|
||||
|
@ -1630,11 +1692,15 @@ DrawTargetWebgl::SharedContext::GetCompatibleSnapshot(SourceSurface* aSurface) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool DrawTargetWebgl::SharedContext::UploadSurface(DataSourceSurface* aData,
|
||||
SurfaceFormat aFormat,
|
||||
const IntRect& aSrcRect,
|
||||
const IntPoint& aDstOffset,
|
||||
bool aInit, bool aZero) {
|
||||
inline bool DrawTargetWebgl::SharedContext::IsCompatibleSurface(
|
||||
SourceSurface* aSurface) const {
|
||||
return bool(RefPtr<WebGLTextureJS>(GetCompatibleSnapshot(aSurface)));
|
||||
}
|
||||
|
||||
bool DrawTargetWebgl::SharedContext::UploadSurface(
|
||||
DataSourceSurface* aData, SurfaceFormat aFormat, const IntRect& aSrcRect,
|
||||
const IntPoint& aDstOffset, bool aInit, bool aZero,
|
||||
const RefPtr<WebGLTextureJS>& aTex) {
|
||||
webgl::TexUnpackBlobDesc texDesc = {
|
||||
LOCAL_GL_TEXTURE_2D,
|
||||
{uint32_t(aSrcRect.width), uint32_t(aSrcRect.height), 1}};
|
||||
|
@ -1702,9 +1768,15 @@ bool DrawTargetWebgl::SharedContext::UploadSurface(DataSourceSurface* aData,
|
|||
aFormat == SurfaceFormat::A8 ? LOCAL_GL_RED : LOCAL_GL_RGBA;
|
||||
webgl::PackingInfo texPI = {extFormat, LOCAL_GL_UNSIGNED_BYTE};
|
||||
// Do the (partial) upload for the shared or standalone texture.
|
||||
if (aTex) {
|
||||
mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, aTex);
|
||||
}
|
||||
mWebgl->RawTexImage(0, aInit ? intFormat : 0,
|
||||
{uint32_t(aDstOffset.x), uint32_t(aDstOffset.y), 0},
|
||||
texPI, std::move(texDesc));
|
||||
if (aTex) {
|
||||
mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, mLastTexture);
|
||||
}
|
||||
if (!aData && aZero) {
|
||||
mWebgl->BindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
@ -1810,10 +1882,10 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
|
|||
// Only an uncached draw if not using the vertex cache.
|
||||
mCurrentTarget->mProfile.OnUncachedDraw();
|
||||
}
|
||||
auto color = static_cast<const ColorPattern&>(aPattern).mColor;
|
||||
float a = color.a * aOptions.mAlpha;
|
||||
DeviceColor premulColor(color.r * a, color.g * a, color.b * a, a);
|
||||
if (((a == 1.0f && aOptions.mCompositionOp == CompositionOp::OP_OVER) ||
|
||||
DeviceColor color = PremultiplyColor(
|
||||
static_cast<const ColorPattern&>(aPattern).mColor, aOptions.mAlpha);
|
||||
if (((color.a == 1.0f &&
|
||||
aOptions.mCompositionOp == CompositionOp::OP_OVER) ||
|
||||
aOptions.mCompositionOp == CompositionOp::OP_SOURCE) &&
|
||||
!aStrokeOptions && !aVertexRange && !HasClipMask()) {
|
||||
// Certain color patterns can be mapped to scissored clears. The
|
||||
|
@ -1828,8 +1900,7 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
|
|||
mWebgl->Scissor(scissorRect.x, scissorRect.y, scissorRect.width,
|
||||
scissorRect.height);
|
||||
}
|
||||
mWebgl->ClearColor(premulColor.b, premulColor.g, premulColor.r,
|
||||
premulColor.a);
|
||||
mWebgl->ClearColor(color.b, color.g, color.r, color.a);
|
||||
mWebgl->Clear(LOCAL_GL_COLOR_BUFFER_BIT);
|
||||
success = true;
|
||||
break;
|
||||
|
@ -1841,8 +1912,8 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
|
|||
// The source operator can support clipping and AA by emulating it with
|
||||
// the over op. Supply the color with blend state, and set the shader
|
||||
// color to white, to avoid needing dual-source blending.
|
||||
blendColor = Some(premulColor);
|
||||
premulColor = DeviceColor(1, 1, 1, 1);
|
||||
blendColor = Some(color);
|
||||
color = DeviceColor(1, 1, 1, 1);
|
||||
}
|
||||
SetBlendState(aOptions.mCompositionOp, blendColor);
|
||||
// Since it couldn't be mapped to a scissored clear, we need to use the
|
||||
|
@ -1869,8 +1940,7 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
|
|||
{(const uint8_t*)&aaData, sizeof(aaData)});
|
||||
mDirtyAA = aaData == 0.0f;
|
||||
}
|
||||
float colorData[4] = {premulColor.b, premulColor.g, premulColor.r,
|
||||
premulColor.a};
|
||||
float colorData[4] = {color.b, color.g, color.r, color.a};
|
||||
Matrix xform(aRect.width, 0.0f, 0.0f, aRect.height, aRect.x, aRect.y);
|
||||
if (aTransformed) {
|
||||
xform *= currentTransform;
|
||||
|
@ -2086,11 +2156,12 @@ bool DrawTargetWebgl::SharedContext::DrawRectAccel(
|
|||
{(const uint8_t*)&aaData, sizeof(aaData)});
|
||||
mDirtyAA = aaData == 0.0f;
|
||||
}
|
||||
DeviceColor color = aMaskColor && format != SurfaceFormat::A8
|
||||
? DeviceColor::Mask(1.0f, aMaskColor->a)
|
||||
: aMaskColor.valueOr(DeviceColor(1, 1, 1, 1));
|
||||
float a = color.a * aOptions.mAlpha;
|
||||
float colorData[4] = {color.b * a, color.g * a, color.r * a, a};
|
||||
DeviceColor color =
|
||||
PremultiplyColor(aMaskColor && format != SurfaceFormat::A8
|
||||
? DeviceColor::Mask(1.0f, aMaskColor->a)
|
||||
: aMaskColor.valueOr(DeviceColor(1, 1, 1, 1)),
|
||||
aOptions.mAlpha);
|
||||
float colorData[4] = {color.b, color.g, color.r, color.a};
|
||||
float swizzleData =
|
||||
aMaskColor && format == SurfaceFormat::A8 ? 1.0f : 0.0f;
|
||||
Matrix xform(aRect.width, 0.0f, 0.0f, aRect.height, aRect.x, aRect.y);
|
||||
|
@ -3892,12 +3963,21 @@ void DrawTargetWebgl::MarkSkiaChanged(const DrawOptions& aOptions) {
|
|||
if (mWebglValid) {
|
||||
mProfile.OnLayer();
|
||||
mSkiaLayer = true;
|
||||
mSkiaLayerClear = mIsClear;
|
||||
mSkia->DetachAllSnapshots();
|
||||
mSkiaNoClip->ClearRect(Rect(mSkiaNoClip->GetRect()));
|
||||
if (mSkiaLayerClear) {
|
||||
// Avoid blending later by making sure the layer background is filled
|
||||
// with opaque alpha values if necessary.
|
||||
mSkiaNoClip->FillRect(Rect(mSkiaNoClip->GetRect()), GetClearPattern(),
|
||||
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
|
||||
} else {
|
||||
mSkiaNoClip->ClearRect(Rect(mSkiaNoClip->GetRect()));
|
||||
}
|
||||
}
|
||||
}
|
||||
// The WebGL context is no longer up-to-date.
|
||||
mWebglValid = false;
|
||||
mIsClear = false;
|
||||
} else {
|
||||
// For other composition ops, just overwrite the Skia data.
|
||||
MarkSkiaChanged();
|
||||
|
@ -3914,18 +3994,25 @@ void DrawTargetWebgl::ReadIntoSkia() {
|
|||
IntSize size;
|
||||
int32_t stride;
|
||||
SurfaceFormat format;
|
||||
// If there's no existing snapshot and we can successfully map the Skia
|
||||
// target for reading, then try to read into that.
|
||||
if (!mSnapshot && mSkia->LockBits(&data, &size, &stride, &format)) {
|
||||
(void)ReadInto(data, stride);
|
||||
mSkia->ReleaseBits(data);
|
||||
} else if (RefPtr<SourceSurface> snapshot = Snapshot()) {
|
||||
// Otherwise, fall back to getting a snapshot from WebGL if available
|
||||
// and then copying that to Skia.
|
||||
mSkia->CopySurface(snapshot, GetRect(), IntPoint(0, 0));
|
||||
if (mIsClear) {
|
||||
// If the WebGL target is still clear, then just clear the Skia target.
|
||||
mSkia->DetachAllSnapshots();
|
||||
mSkiaNoClip->FillRect(Rect(mSkiaNoClip->GetRect()), GetClearPattern(),
|
||||
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
|
||||
} else {
|
||||
// If there's no existing snapshot and we can successfully map the Skia
|
||||
// target for reading, then try to read into that.
|
||||
if (!mSnapshot && mSkia->LockBits(&data, &size, &stride, &format)) {
|
||||
(void)ReadInto(data, stride);
|
||||
mSkia->ReleaseBits(data);
|
||||
} else if (RefPtr<SourceSurface> snapshot = Snapshot()) {
|
||||
// Otherwise, fall back to getting a snapshot from WebGL if available
|
||||
// and then copying that to Skia.
|
||||
mSkia->CopySurface(snapshot, GetRect(), IntPoint(0, 0));
|
||||
}
|
||||
// Signal that we've hit a complete software fallback.
|
||||
mProfile.OnFallback();
|
||||
}
|
||||
// Signal that we've hit a complete software fallback.
|
||||
mProfile.OnFallback();
|
||||
}
|
||||
mSkiaValid = true;
|
||||
// The Skia data is flat after reading, so disable any layering.
|
||||
|
@ -3937,13 +4024,17 @@ void DrawTargetWebgl::FlattenSkia() {
|
|||
if (!mSkiaValid || !mSkiaLayer) {
|
||||
return;
|
||||
}
|
||||
mSkiaLayer = false;
|
||||
if (mSkiaLayerClear) {
|
||||
// If the WebGL target is clear, then there is nothing to blend.
|
||||
return;
|
||||
}
|
||||
if (RefPtr<DataSourceSurface> base = ReadSnapshot()) {
|
||||
mSkia->DetachAllSnapshots();
|
||||
mSkiaNoClip->DrawSurface(base, Rect(GetRect()), Rect(GetRect()),
|
||||
DrawSurfaceOptions(SamplingFilter::POINT),
|
||||
DrawOptions(1.f, CompositionOp::OP_DEST_OVER));
|
||||
}
|
||||
mSkiaLayer = false;
|
||||
}
|
||||
|
||||
// Attempts to draw the contents of the Skia target into the WebGL context.
|
||||
|
@ -3962,6 +4053,20 @@ bool DrawTargetWebgl::FlushFromSkia() {
|
|||
// WebGL target either, so only try to blend if there is a valid Skia target.
|
||||
mWebglValid = true;
|
||||
if (mSkiaValid) {
|
||||
AutoRestoreContext restore(this);
|
||||
|
||||
// If the Skia target is clear, then there is no need to use a snapshot.
|
||||
// Directly clear the WebGL target instead.
|
||||
if (mIsClear) {
|
||||
if (!DrawRect(Rect(GetRect()), GetClearPattern(),
|
||||
DrawOptions(1.0f, CompositionOp::OP_SOURCE), Nothing(),
|
||||
nullptr, false, false, true)) {
|
||||
mWebglValid = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<SourceSurface> skiaSnapshot = mSkia->Snapshot();
|
||||
if (!skiaSnapshot) {
|
||||
// There's a valid Skia target to draw to, but for some reason there is
|
||||
|
@ -3969,15 +4074,26 @@ bool DrawTargetWebgl::FlushFromSkia() {
|
|||
mWebglValid = false;
|
||||
return false;
|
||||
}
|
||||
AutoRestoreContext restore(this);
|
||||
|
||||
// If there is no layer, then just upload it directly.
|
||||
if (!mSkiaLayer) {
|
||||
if (PrepareContext(false) && MarkChanged()) {
|
||||
if (RefPtr<DataSourceSurface> data = skiaSnapshot->GetDataSurface()) {
|
||||
mSharedContext->UploadSurface(data, mFormat, GetRect(), IntPoint(),
|
||||
false, false, mTex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Failed to upload the Skia snapshot.
|
||||
mWebglValid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
SurfacePattern pattern(skiaSnapshot, ExtendMode::CLAMP);
|
||||
// If there is a layer, blend the snapshot with the WebGL context,
|
||||
// otherwise copy it.
|
||||
// If there is a layer, blend the snapshot with the WebGL context.
|
||||
if (!DrawRect(Rect(GetRect()), pattern,
|
||||
DrawOptions(1.0f, mSkiaLayer ? CompositionOp::OP_OVER
|
||||
: CompositionOp::OP_SOURCE),
|
||||
Nothing(), mSkiaLayer ? &mSnapshotTexture : nullptr, false,
|
||||
false, true, true)) {
|
||||
DrawOptions(1.0f, CompositionOp::OP_OVER), Nothing(),
|
||||
&mSnapshotTexture, false, false, true, true)) {
|
||||
// If accelerated drawing failed for some reason, then leave the Skia
|
||||
// target unchanged.
|
||||
mWebglValid = false;
|
||||
|
|
|
@ -83,10 +83,14 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
|
|||
mozilla::ipc::Shmem mShmem;
|
||||
// The currently cached snapshot of the WebGL context
|
||||
RefPtr<DataSourceSurface> mSnapshot;
|
||||
// Whether the framebuffer is still in the initially clear state.
|
||||
bool mIsClear = true;
|
||||
// Whether or not the Skia target has valid contents and is being drawn to
|
||||
bool mSkiaValid = false;
|
||||
// Whether or not Skia layering over the WebGL context is enabled
|
||||
bool mSkiaLayer = false;
|
||||
// Whether the WebGL target was clear when the Skia layer was established.
|
||||
bool mSkiaLayerClear = false;
|
||||
// Whether or not the WebGL context has valid contents and is being drawn to
|
||||
bool mWebglValid = true;
|
||||
// Whether or not the clip state has changed since last used by SharedContext.
|
||||
|
@ -297,11 +301,13 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
|
|||
const IntRect& aRect, TextureHandle* aHandle = nullptr);
|
||||
|
||||
already_AddRefed<WebGLTextureJS> GetCompatibleSnapshot(
|
||||
SourceSurface* aSurface);
|
||||
SourceSurface* aSurface) const;
|
||||
bool IsCompatibleSurface(SourceSurface* aSurface) const;
|
||||
|
||||
bool UploadSurface(DataSourceSurface* aData, SurfaceFormat aFormat,
|
||||
const IntRect& aSrcRect, const IntPoint& aDstOffset,
|
||||
bool aInit, bool aZero = false);
|
||||
bool aInit, bool aZero = false,
|
||||
const RefPtr<WebGLTextureJS>& aTex = nullptr);
|
||||
bool DrawRectAccel(const Rect& aRect, const Pattern& aPattern,
|
||||
const DrawOptions& aOptions,
|
||||
Maybe<DeviceColor> aMaskColor = Nothing(),
|
||||
|
@ -504,6 +510,8 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
|
|||
bool aAccelOnly = false, bool aForceUpdate = false,
|
||||
const StrokeOptions* aStrokeOptions = nullptr);
|
||||
|
||||
ColorPattern GetClearPattern() const;
|
||||
|
||||
bool ShouldAccelPath(const DrawOptions& aOptions,
|
||||
const StrokeOptions* aStrokeOptions);
|
||||
void DrawPath(const Path* aPath, const Pattern& aPattern,
|
||||
|
@ -522,14 +530,18 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
|
|||
}
|
||||
}
|
||||
|
||||
void MarkSkiaChanged() {
|
||||
void MarkSkiaChanged(bool aOverwrite = false) {
|
||||
WaitForShmem();
|
||||
if (!mSkiaValid) {
|
||||
if (aOverwrite) {
|
||||
mSkiaValid = true;
|
||||
mSkiaLayer = false;
|
||||
} else if (!mSkiaValid) {
|
||||
ReadIntoSkia();
|
||||
} else if (mSkiaLayer) {
|
||||
FlattenSkia();
|
||||
}
|
||||
mWebglValid = false;
|
||||
mIsClear = false;
|
||||
}
|
||||
|
||||
void MarkSkiaChanged(const DrawOptions& aOptions);
|
||||
|
|
|
@ -254,21 +254,6 @@ namespace ChromeUtils {
|
|||
*/
|
||||
double dateNow();
|
||||
|
||||
/**
|
||||
* Defines a getter on a specified object that will be created upon first
|
||||
* use.
|
||||
*
|
||||
* @param aTarget
|
||||
* The object to define the lazy getter on.
|
||||
* @param aName
|
||||
* The name of the getter to define on aTarget.
|
||||
* @param aLambda
|
||||
* A function that returns what the getter should return. This will
|
||||
* only ever be called once.
|
||||
*/
|
||||
[Throws]
|
||||
undefined defineLazyGetter(object aTarget, any aName, object aLambda);
|
||||
|
||||
/**
|
||||
* IF YOU ADD NEW METHODS HERE, MAKE SURE THEY ARE THREAD-SAFE.
|
||||
*/
|
||||
|
|
|
@ -95,7 +95,7 @@ interface, you need to do the following:
|
|||
instance by the compiler via one of that class's non-explicit
|
||||
constructors.)
|
||||
If many instances of `MyInterface` are expected to be created
|
||||
quicky, the return value of `GetParentObject` should itself inherit
|
||||
quickly, the return value of `GetParentObject` should itself inherit
|
||||
from `nsWrapperCache` for optimal performance. Returning null from
|
||||
`GetParentObject` is allowed in situations in which it's OK to
|
||||
associate the resulting object with a random global object for
|
||||
|
@ -205,7 +205,7 @@ will require these method declarations:
|
|||
``` cpp
|
||||
class MyClass
|
||||
{
|
||||
void DoSomething(int32_t aNumber);
|
||||
void DoSomething(int32_t a number);
|
||||
double DoSomething(MyClass* aOtherInstance);
|
||||
|
||||
already_AddRefed<MyInterface> DoSomethingElse(Optional<int32_t> aMaybeNumber,
|
||||
|
|
|
@ -449,15 +449,6 @@ IPCResult FileSystemManagerParent::RecvRenameEntry(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
IPCResult FileSystemManagerParent::RecvNeedQuota(
|
||||
FileSystemQuotaRequest&& aRequest, NeedQuotaResolver&& aResolver) {
|
||||
AssertIsOnIOTarget();
|
||||
|
||||
aResolver(0u);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void FileSystemManagerParent::RequestAllowToClose() {
|
||||
::mozilla::ipc::AssertIsOnBackgroundThread();
|
||||
|
||||
|
|
|
@ -65,9 +65,6 @@ class FileSystemManagerParent : public PFileSystemManagerParent {
|
|||
mozilla::ipc::IPCResult RecvRenameEntry(
|
||||
FileSystemRenameEntryRequest&& aRequest, MoveEntryResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvNeedQuota(FileSystemQuotaRequest&& aRequest,
|
||||
NeedQuotaResolver&& aResolver);
|
||||
|
||||
void RequestAllowToClose();
|
||||
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
|
|
@ -245,12 +245,6 @@ union FileSystemMoveEntryResponse
|
|||
void_t;
|
||||
};
|
||||
|
||||
struct FileSystemQuotaRequest
|
||||
{
|
||||
FileSystemChildMetadata handle;
|
||||
uint64_t quotaNeeded;
|
||||
};
|
||||
|
||||
} // namespace fs
|
||||
|
||||
async protocol PFileSystemManager
|
||||
|
@ -406,14 +400,6 @@ async protocol PFileSystemManager
|
|||
async RenameEntry(FileSystemRenameEntryRequest request)
|
||||
returns(FileSystemMoveEntryResponse response);
|
||||
|
||||
/**
|
||||
* Request for quota needed to finish a write, beyond the amount preallocated
|
||||
* at creation of the AccessHandle. While officially async, this is used in a
|
||||
* sync manner from write() by spinning an event loop.
|
||||
*/
|
||||
async NeedQuota(FileSystemQuotaRequest aRequest)
|
||||
returns (uint64_t aQuotaGranted);
|
||||
|
||||
child:
|
||||
async PFileSystemWritableFileStream();
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ XPCSHELL_TESTS_MANIFESTS += [
|
|||
TESTING_JS_MODULES.dom.fs.test.common += [
|
||||
"nsresult.js",
|
||||
"test_basics.js",
|
||||
"test_fileSystemDirectoryHandle.js",
|
||||
"test_syncAccessHandle.js",
|
||||
"test_writableFileStream.js",
|
||||
]
|
||||
|
|
|
@ -89,6 +89,50 @@ exported_symbols.smokeTest = async function smokeTest() {
|
|||
}
|
||||
};
|
||||
|
||||
exported_symbols.quotaTest = async function() {
|
||||
const storage = navigator.storage;
|
||||
const allowCreate = { create: true };
|
||||
|
||||
{
|
||||
let root = await storage.getDirectory();
|
||||
Assert.ok(root, "Can we access the root directory?");
|
||||
|
||||
const fileHandle = await root.getFileHandle("test.txt", allowCreate);
|
||||
Assert.ok(!!fileHandle, "Can we get file handle?");
|
||||
|
||||
const cachedOriginUsage = await Utils.getCachedOriginUsage();
|
||||
|
||||
const writable = await fileHandle.createWritable();
|
||||
Assert.ok(!!writable, "Can we create writable file stream?");
|
||||
|
||||
const buffer = new ArrayBuffer(42);
|
||||
Assert.ok(!!buffer, "Can we create array buffer?");
|
||||
|
||||
const result = await writable.write(buffer);
|
||||
Assert.equal(result, undefined, "Can we write entire buffer?");
|
||||
|
||||
await writable.close();
|
||||
|
||||
const cachedOriginUsage2 = await Utils.getCachedOriginUsage();
|
||||
|
||||
Assert.equal(
|
||||
cachedOriginUsage2 - cachedOriginUsage,
|
||||
buffer.byteLength,
|
||||
"Is cached origin usage correct after writing?"
|
||||
);
|
||||
|
||||
await root.removeEntry("test.txt");
|
||||
|
||||
const cachedOriginUsage3 = await Utils.getCachedOriginUsage();
|
||||
|
||||
Assert.equal(
|
||||
cachedOriginUsage3,
|
||||
cachedOriginUsage,
|
||||
"Is cached origin usage correct after removing file?"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
for (const [key, value] of Object.entries(exported_symbols)) {
|
||||
Object.defineProperty(value, "name", {
|
||||
value: key,
|
||||
|
|
|
@ -15,11 +15,14 @@ async function require_module(id) {
|
|||
|
||||
const { Assert } = await import("/tests/dom/quota/test/modules/Assert.js");
|
||||
|
||||
const { Utils } = await import("/tests/dom/quota/test/modules/Utils.js");
|
||||
|
||||
const proto = {
|
||||
Assert,
|
||||
Cr: SpecialPowers.Cr,
|
||||
navigator,
|
||||
TextEncoder,
|
||||
Utils,
|
||||
};
|
||||
|
||||
require_module.moduleLoader = new ModuleLoader(base, depth, proto);
|
||||
|
|
|
@ -20,6 +20,5 @@ scheme=https
|
|||
scheme=https
|
||||
[test_writableFileStream.html]
|
||||
scheme=https
|
||||
skip-if = true # This should be enabled once bug 1798513 is fixed.
|
||||
[test_writableFileStream_worker.html]
|
||||
scheme=https
|
||||
|
|
|
@ -11,9 +11,7 @@
|
|||
|
||||
<script type="text/javascript" src="head.js"></script>
|
||||
<script type="text/javascript">
|
||||
// XXX This should be done in a task, but SimpleTest currently doesn't
|
||||
// support adding new tasks during a task execution.
|
||||
async function init() {
|
||||
add_task(async function init() {
|
||||
const testSet = "dom/fs/test/common/test_basics.js";
|
||||
|
||||
const testCases = await require_module(testSet);
|
||||
|
@ -21,9 +19,7 @@
|
|||
Object.values(testCases).forEach(testItem => {
|
||||
add_task(testItem);
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -11,9 +11,7 @@
|
|||
|
||||
<script type="text/javascript" src="head.js"></script>
|
||||
<script type="text/javascript">
|
||||
// XXX This should be done in a task, but SimpleTest currently doesn't
|
||||
// support adding new tasks during a task execution.
|
||||
async function init() {
|
||||
add_task(async function init() {
|
||||
const testSet = "dom/fs/test/common/test_fileSystemDirectoryHandle.js";
|
||||
|
||||
const testCases = await require_module(testSet);
|
||||
|
@ -21,9 +19,7 @@
|
|||
Object.values(testCases).forEach(testItem => {
|
||||
add_task(testItem);
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -11,19 +11,18 @@
|
|||
|
||||
<script type="text/javascript" src="head.js"></script>
|
||||
<script type="text/javascript">
|
||||
// XXX This should be done in a task, but SimpleTest currently doesn't
|
||||
// support adding new tasks during a task execution.
|
||||
async function init() {
|
||||
add_task(async function init() {
|
||||
const testSet = "dom/fs/test/common/test_writableFileStream.js";
|
||||
|
||||
const testCases = await require_module(testSet);
|
||||
|
||||
Object.values(testCases).forEach(testItem => {
|
||||
add_task(testItem);
|
||||
// We can't shrink storage size in a mochitest.
|
||||
if (testItem.name != "quotaTest") {
|
||||
add_task(testItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ async function require_module(id) {
|
|||
|
||||
importScripts("/tests/dom/quota/test/modules/worker/Assert.js");
|
||||
|
||||
importScripts("/tests/dom/quota/test/modules/worker/Utils.js");
|
||||
|
||||
require_module.moduleLoader = new globalThis.ModuleLoader(base, depth);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,13 +15,19 @@ async function require_module(id) {
|
|||
|
||||
Cu.importGlobalProperties(["storage"]);
|
||||
|
||||
const { Utils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/dom/quota/test/modules/Utils.sys.mjs"
|
||||
);
|
||||
|
||||
const proto = {
|
||||
Assert,
|
||||
Cr,
|
||||
DOMException,
|
||||
navigator: {
|
||||
storage,
|
||||
},
|
||||
TextEncoder,
|
||||
Utils,
|
||||
};
|
||||
|
||||
require_module.moduleLoader = new ModuleLoader(base, depth, proto);
|
||||
|
|
|
@ -11,5 +11,4 @@ head = head.js
|
|||
[test_fileSystemDirectoryHandle_worker.js]
|
||||
[test_syncAccessHandle_worker.js]
|
||||
[test_writableFileStream.js]
|
||||
skip-if = true # This should be enabled once bug 1798513 is fixed.
|
||||
[test_writableFileStream_worker.js]
|
||||
|
|
|
@ -3978,6 +3978,30 @@ ContentParent::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
|
||||
Preferences::GetPreference(&pref, GeckoProcessType_Content,
|
||||
GetRemoteType());
|
||||
|
||||
// This check is a bit of a hack. We want to avoid sending excessive
|
||||
// preference updates to subprocesses for performance reasons, but we
|
||||
// currently don't have a great mechanism for doing so. (See Bug 1819714)
|
||||
// We're going to hijack the sanitization mechanism to accomplish our goal
|
||||
// but it imposes the following complications:
|
||||
// 1) It doesn't avoid sending anything to other (non-web-content)
|
||||
// subprocesses so we're not getting any perf gains there
|
||||
// 2) It confuses the subprocesses w.r.t. sanitization. The point of
|
||||
// sending a preference update of a sanitized preference is so that
|
||||
// content process knows when it's asked to resolve a sanitized
|
||||
// preference, and it can send telemetry and/or crash. With this
|
||||
// change, a sanitized pref that is created during the browser session
|
||||
// will not be sent to the content process, and therefore the content
|
||||
// process won't know it should telemetry/crash on access - it'll just
|
||||
// silently fail to resolve it. After browser restart, the sanitized
|
||||
// pref will be populated into the content process via the shared pref
|
||||
// map and _then_ if it is accessed, the content process will crash.
|
||||
// We're seeing good telemetry/crash rates right now, so we're okay with
|
||||
// this limitation.
|
||||
if (pref.isSanitized()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (IsInitialized()) {
|
||||
MOZ_ASSERT(mQueuedPrefs.IsEmpty());
|
||||
if (!SendPreferenceUpdate(pref)) {
|
||||
|
@ -3987,6 +4011,8 @@ ContentParent::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
MOZ_ASSERT(!IsDead());
|
||||
mQueuedPrefs.AppendElement(pref);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!IsAlive()) {
|
||||
|
|
|
@ -551,6 +551,10 @@ nsresult VP8TrackEncoder::Encode(VideoSegment* aSegment) {
|
|||
|
||||
MOZ_DIAGNOSTIC_ASSERT(encodedFrame);
|
||||
|
||||
if (mKeyFrameInterval > TimeDuration::FromSeconds(0)) {
|
||||
mDurationSinceLastKeyframe += chunk.GetDuration();
|
||||
}
|
||||
|
||||
// Move forward the mEncodedTimestamp.
|
||||
mEncodedTimestamp += chunk.GetDuration();
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ module.exports = {
|
|||
"Assert.js",
|
||||
"ModuleLoader.js",
|
||||
"StorageUtils.js",
|
||||
"Utils.js",
|
||||
"UtilsParent.js",
|
||||
"WorkerDriver.js",
|
||||
],
|
||||
parserOptions: {
|
||||
|
|
|
@ -29,6 +29,26 @@ export async function setStoragePrefs(optionalPrefsToSet) {
|
|||
await SpecialPowers.pushPrefEnv({ set: prefsToSet });
|
||||
}
|
||||
|
||||
export async function getUsageForOrigin(principal, fromMemory) {
|
||||
const request = SpecialPowers.Services.qms.getUsageForPrincipal(
|
||||
principal,
|
||||
function() {},
|
||||
fromMemory
|
||||
);
|
||||
|
||||
await new Promise(function(resolve) {
|
||||
request.callback = SpecialPowers.wrapCallback(function() {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
if (request.resultCode != SpecialPowers.Cr.NS_OK) {
|
||||
throw new RequestError(request.resultCode, request.resultName);
|
||||
}
|
||||
|
||||
return request.result;
|
||||
}
|
||||
|
||||
export async function clearStoragesForOrigin(principal) {
|
||||
const request = SpecialPowers.Services.qms.clearStoragesForPrincipal(
|
||||
principal
|
||||
|
|
14
dom/quota/test/modules/content/Utils.js
Normal file
14
dom/quota/test/modules/content/Utils.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
import { getUsageForOrigin } from "/tests/dom/quota/test/modules/StorageUtils.js";
|
||||
|
||||
export const Utils = {
|
||||
async getCachedOriginUsage() {
|
||||
const principal = SpecialPowers.wrap(document).nodePrincipal;
|
||||
const result = await getUsageForOrigin(principal, true);
|
||||
return result.usage;
|
||||
},
|
||||
};
|
21
dom/quota/test/modules/content/UtilsParent.js
Normal file
21
dom/quota/test/modules/content/UtilsParent.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
import { Utils } from "/tests/dom/quota/test/modules/Utils.js";
|
||||
|
||||
export const UtilsParent = {
|
||||
async OnMessageReceived(worker, msg) {
|
||||
switch (msg.op) {
|
||||
case "getCachedOriginUsage": {
|
||||
const result = await Utils.getCachedOriginUsage();
|
||||
worker.postMessage(result);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown op ${msg.op}`);
|
||||
}
|
||||
},
|
||||
};
|
|
@ -10,10 +10,25 @@ export async function runTestInWorker(script, base, listener) {
|
|||
base
|
||||
);
|
||||
|
||||
let modules = {};
|
||||
|
||||
const worker = new Worker(globalHeadUrl.href);
|
||||
|
||||
worker.onmessage = function(event) {
|
||||
worker.onmessage = async function(event) {
|
||||
const data = event.data;
|
||||
const moduleName = data.moduleName;
|
||||
const objectName = data.objectName;
|
||||
|
||||
if (moduleName && objectName) {
|
||||
if (!modules[moduleName]) {
|
||||
// eslint-disable-next-line no-unsanitized/method
|
||||
modules[moduleName] = await import(
|
||||
"/tests/dom/quota/test/modules/" + moduleName + ".js"
|
||||
);
|
||||
}
|
||||
await modules[moduleName][objectName].OnMessageReceived(worker, data);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data.op) {
|
||||
case "ok":
|
||||
|
|
|
@ -12,7 +12,7 @@ module.exports = {
|
|||
|
||||
overrides: [
|
||||
{
|
||||
files: ["Assert.js", "ModuleLoader.js"],
|
||||
files: ["Assert.js", "ModuleLoader.js", "Utils.js", "UtilsChild.js"],
|
||||
parserOptions: {
|
||||
sourceType: "script",
|
||||
},
|
||||
|
|
41
dom/quota/test/modules/content/worker/Utils.js
Normal file
41
dom/quota/test/modules/content/worker/Utils.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
let UtilsChild;
|
||||
|
||||
async function ensureUtilsChild() {
|
||||
if (UtilsChild) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { UtilsChild: importedUtilsChild } = await import(
|
||||
"/tests/dom/quota/test/modules/worker/UtilsChild.js"
|
||||
);
|
||||
|
||||
UtilsChild = importedUtilsChild;
|
||||
|
||||
throw Error("Please switch to dynamic module import");
|
||||
} catch (e) {
|
||||
if (e.message == "Please switch to dynamic module import") {
|
||||
throw e;
|
||||
}
|
||||
|
||||
importScripts("/tests/dom/quota/test/modules/worker/UtilsChild.js");
|
||||
|
||||
const { UtilsChild: importedUtilsChild } = globalThis.importUtilsChild();
|
||||
|
||||
UtilsChild = importedUtilsChild;
|
||||
}
|
||||
}
|
||||
|
||||
const Utils = {
|
||||
async getCachedOriginUsage() {
|
||||
await ensureUtilsChild();
|
||||
|
||||
const result = await UtilsChild.getCachedOriginUsage();
|
||||
return result;
|
||||
},
|
||||
};
|
26
dom/quota/test/modules/content/worker/UtilsChild.js
Normal file
26
dom/quota/test/modules/content/worker/UtilsChild.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
const _UtilsChild = {
|
||||
async getCachedOriginUsage() {
|
||||
postMessage({
|
||||
moduleName: "UtilsParent",
|
||||
objectName: "UtilsParent",
|
||||
op: "getCachedOriginUsage",
|
||||
});
|
||||
|
||||
return new Promise(function(resolve) {
|
||||
addEventListener("message", async function onMessage(event) {
|
||||
removeEventListener("message", onMessage);
|
||||
const data = event.data;
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
function importUtilsChild() {
|
||||
return { UtilsChild: _UtilsChild };
|
||||
}
|
|
@ -48,6 +48,26 @@ export function clearStoragePrefs(optionalPrefsToClear) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function getUsageForOrigin(principal, fromMemory) {
|
||||
const request = Services.qms.getUsageForPrincipal(
|
||||
principal,
|
||||
function() {},
|
||||
fromMemory
|
||||
);
|
||||
|
||||
await new Promise(function(resolve) {
|
||||
request.callback = function() {
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
|
||||
if (request.resultCode != Cr.NS_OK) {
|
||||
throw new RequestError(request.resultCode, request.resultName);
|
||||
}
|
||||
|
||||
return request.result;
|
||||
}
|
||||
|
||||
export async function clearStoragesForOrigin(principal) {
|
||||
const request = Services.qms.clearStoragesForPrincipal(principal);
|
||||
|
||||
|
|
|
@ -3,9 +3,20 @@
|
|||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
import { resetStorage } from "resource://testing-common/dom/quota/test/modules/StorageUtils.sys.mjs";
|
||||
import {
|
||||
getUsageForOrigin,
|
||||
resetStorage,
|
||||
} from "resource://testing-common/dom/quota/test/modules/StorageUtils.sys.mjs";
|
||||
|
||||
export const Utils = {
|
||||
async getCachedOriginUsage() {
|
||||
const principal = Cc["@mozilla.org/systemprincipal;1"].createInstance(
|
||||
Ci.nsIPrincipal
|
||||
);
|
||||
const result = await getUsageForOrigin(principal, true);
|
||||
return result.usage;
|
||||
},
|
||||
|
||||
async shrinkStorageSize(size) {
|
||||
Services.prefs.setIntPref(
|
||||
"dom.quotaManager.temporaryStorage.fixedLimit",
|
||||
|
|
|
@ -8,6 +8,11 @@ import { Utils } from "resource://testing-common/dom/quota/test/modules/Utils.sy
|
|||
export const UtilsParent = {
|
||||
async OnMessageReceived(worker, msg) {
|
||||
switch (msg.op) {
|
||||
case "getCachedOriginUsage": {
|
||||
const result = await Utils.getCachedOriginUsage();
|
||||
worker.postMessage(result);
|
||||
break;
|
||||
}
|
||||
case "shrinkStorageSize": {
|
||||
const result = await Utils.shrinkStorageSize(msg.size);
|
||||
worker.postMessage(result);
|
||||
|
|
|
@ -13,7 +13,7 @@ export async function runTestInWorker(script, base, listener) {
|
|||
|
||||
const worker = new Worker(globalHeadUrl.href);
|
||||
|
||||
worker.onmessage = function(event) {
|
||||
worker.onmessage = async function(event) {
|
||||
const data = event.data;
|
||||
const moduleName = data.moduleName;
|
||||
const objectName = data.objectName;
|
||||
|
@ -26,7 +26,7 @@ export async function runTestInWorker(script, base, listener) {
|
|||
".sys.mjs"
|
||||
);
|
||||
}
|
||||
modules[moduleName][objectName].OnMessageReceived(worker, data);
|
||||
await modules[moduleName][objectName].OnMessageReceived(worker, data);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue