Merge autoland to mozilla-central a=merge

This commit is contained in:
Norisz Fay 2022-05-26 12:22:24 +03:00
Родитель 1a796fb96b 3fd8b7b5b0
Коммит e9d995e793
181 изменённых файлов: 12730 добавлений и 9989 удалений

4
.gitignore поставляемый
Просмотреть файл

@ -189,3 +189,7 @@ config/external/icu4x
# Ignore the index files generated by clangd.
.cache/clangd/index/
# Ignore Storybook generated files
browser/components/storybook/node_modules/
browser/components/storybook/storybook-static/

Просмотреть файл

@ -157,7 +157,6 @@ _OPT\.OBJ/
^tools/browsertime/node_modules/
^tools/lint/eslint/eslint-plugin-mozilla/node_modules/
^browser/components/newtab/node_modules/
^browser/components/storybook/node_modules/
# Ignore talos virtualenv and tp5n files.
# The tp5n set is supposed to be decompressed at
@ -243,3 +242,7 @@ toolkit/components/certviewer/content/package-lock.json
# Ignore mypy files
\.mypy_cache/
# Ignore Storybook generated files
^browser/components/storybook/node_modules/
^browser/components/storybook/storybook-static/

Просмотреть файл

@ -2,6 +2,4 @@
- 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/. -->
<!ENTITY brandShorterName "Firefox">
<!ENTITY brandShortName "Firefox Developer Edition">
<!ENTITY brandFullName "Firefox Developer Edition">

Просмотреть файл

@ -2,6 +2,4 @@
- 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/. -->
<!ENTITY brandShorterName "Nightly">
<!ENTITY brandShortName "Nightly">
<!ENTITY brandFullName "Firefox Nightly">

Просмотреть файл

@ -2,6 +2,4 @@
- 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/. -->
<!ENTITY brandShorterName "Firefox">
<!ENTITY brandShortName "Firefox">
<!ENTITY brandFullName "Mozilla Firefox">

Просмотреть файл

@ -2,6 +2,4 @@
- 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/. -->
<!ENTITY brandShorterName "Nightly">
<!ENTITY brandShortName "Nightly">
<!ENTITY brandFullName "Nightly">

Просмотреть файл

@ -44,10 +44,12 @@ class ColorwaySelector extends HTMLFieldSetElement {
for (let input of this.children) {
if (input.value == this.activeTheme.id) {
input.classList.add("active");
input.setAttribute("aria-current", true);
this.updateName(this.selectedTheme.name);
this.updateDescription(input.value);
} else {
input.classList.remove("active");
input.setAttribute("aria-current", false);
}
}
}
@ -57,6 +59,7 @@ class ColorwaySelector extends HTMLFieldSetElement {
input.type = "radio";
input.name = "colorway";
input.value = theme.id;
input.setAttribute("title", theme.name);
input.style.setProperty("--colorway-icon", `url(${theme.iconURL})`);
input.onclick = () => {
this.selectedTheme = theme;

Просмотреть файл

@ -40,6 +40,24 @@ body > header {
padding: .2em 1em;
}
#use-fx-home-controls:not(.success) > .success-prompt,
#use-fx-home-controls.success > .reset-prompt {
display: none;
}
#use-fx-home-controls > .success-prompt::before {
display: inline-block;
content: "";
background: var(--green-50) url('chrome://global/skin/icons/check.svg') center center no-repeat;
-moz-context-properties: fill;
fill: white;
width: 22px;
height: 22px;
border-radius: 15px;
vertical-align: middle;
margin-inline-end: 0.5em;
}
body > section {
grid-area: main;
}

Просмотреть файл

@ -4,3 +4,7 @@
colorway-collection-life-in-color = Life In Color
colorway-collection-true-colors = True Colors
colorway-fx-home-link = Use { -brand-product-name } Home for colorful new tabs
colorway-fx-home-link-success = { -brand-product-name } Home is now your home page
colorway-fx-home-apply-button = Apply
colorway-fx-home-undo-button = Undo

Просмотреть файл

@ -13,7 +13,8 @@
href="chrome://global/skin/in-content/common.css">
<link rel="stylesheet" type="text/css"
href="chrome://browser/content/colorwaycloset.css">
<link rel="localization" href="browser/colorways.ftl"/>
<link rel="localization" href="branding/brand.ftl">
<link rel="localization" href="preview/colorwaycloset.ftl">
<script src="chrome://browser/content/colorwaycloset.js" defer="async"></script>
<script type="module" src="chrome://browser/content/ColorwayClosetSelector.js"></script>
</head>
@ -30,6 +31,16 @@
<p id="colorway-description"></p>
</div>
</section>
<section id="use-fx-home-controls" hidden>
<div class="reset-prompt">
<span data-l10n-id="colorway-fx-home-link"></span>
<button data-l10n-id="colorway-fx-home-apply-button"></button>
</div>
<div class="success-prompt">
<span data-l10n-id="colorway-fx-home-link-success"></span>
<button data-l10n-id="colorway-fx-home-undo-button"></button>
</div>
</section>
</main>
</body>
</html>

Просмотреть файл

@ -5,19 +5,40 @@
const { BuiltInThemes } = ChromeUtils.import(
"resource:///modules/BuiltInThemes.jsm"
);
const { HomePage } = ChromeUtils.import("resource:///modules/HomePage.jsm");
function showUseFXHomeControls(fluentStrings) {
let homeState;
const useFXHomeControls = document.getElementById("use-fx-home-controls");
useFXHomeControls.hidden = HomePage.isDefault;
if (!HomePage.isDefault) {
useFXHomeControls
.querySelector(".reset-prompt > button")
.addEventListener("click", () => {
homeState = HomePage.get();
HomePage.reset();
useFXHomeControls.classList.add("success");
});
useFXHomeControls
.querySelector(".success-prompt > button")
.addEventListener("click", () => {
HomePage.set(homeState);
useFXHomeControls.classList.remove("success");
});
}
}
const collection = BuiltInThemes.findActiveColorwayCollection();
if (collection) {
const { expiry, l10nId } = collection;
const fluentStrings = new Localization(["preview/colorwaycloset.ftl"], true);
const formatter = new Intl.DateTimeFormat("default", {
month: "long",
day: "numeric",
});
document.getElementById(
"collection-title"
).innerText = fluentStrings.formatValueSync(l10nId);
const collectionTitle = document.getElementById("collection-title");
document.l10n.setAttributes(collectionTitle, l10nId);
document.querySelector(
"#collection-expiry-date > span"
).innerText = formatter.format(expiry);
showUseFXHomeControls();
}

Просмотреть файл

@ -31,6 +31,18 @@ add_task(async function about_colorwaycloset_smoke_test() {
document.getElementById("colorway-description"),
"colorway description exists"
);
const useFXHomeControls = document.getElementById("use-fx-home-controls");
ok(useFXHomeControls, "firefox home controls exists");
useFXHomeControls.toggleAttribute("hidden", false);
ok(
document.querySelector("#use-fx-home-controls > .reset-prompt"),
"firefox home controls reset prompt exists"
);
ok(
document.querySelector("#use-fx-home-controls > .success-prompt"),
"firefox home controls reset prompt exists"
);
}
);
});

Просмотреть файл

@ -339,9 +339,10 @@ this.chrome_settings_overrides = class extends ExtensionAPI {
extension.startupReason
);
}
// Ensure the item is disabled. If addSetting was called above,
// Item may be null, and enabled may be undefined.
if (disable && item?.enabled !== false) {
// Ensure the item is disabled (either if exists and is not default or if it does not
// exist yet).
if (disable) {
item = await ExtensionSettingsStore.disable(
extension.id,
DEFAULT_SEARCH_STORE_TYPE,
@ -466,6 +467,37 @@ this.chrome_settings_overrides = class extends ExtensionAPI {
DEFAULT_SEARCH_STORE_TYPE,
DEFAULT_SEARCH_SETTING_NAME
);
// Check for an inconsistency between the value returned by getLevelOfcontrol
// and the current engine actually set.
if (
control === "controlled_by_this_extension" &&
Services.search.defaultEngine.name !== engineName
) {
// Check for and fix any inconsistency between the extensions settings storage
// and the current engine actually set. If settings claims the extension is default
// but the search service claims otherwise, select what the search service claims
// (See Bug 1767550).
const allSettings = ExtensionSettingsStore.getAllSettings(
DEFAULT_SEARCH_STORE_TYPE,
DEFAULT_SEARCH_SETTING_NAME
);
for (const setting of allSettings) {
if (setting.value !== Services.search.defaultEngine.name) {
await ExtensionSettingsStore.disable(
setting.id,
DEFAULT_SEARCH_STORE_TYPE,
DEFAULT_SEARCH_SETTING_NAME
);
}
}
control = await ExtensionSettingsStore.getLevelOfControl(
extension.id,
DEFAULT_SEARCH_STORE_TYPE,
DEFAULT_SEARCH_SETTING_NAME
);
}
if (control === "controlled_by_this_extension") {
await Services.search.setDefault(
Services.search.getEngineByName(engineName)

Просмотреть файл

@ -337,6 +337,47 @@ add_task(async function test_overrides_update_homepage_change() {
await extension.unload();
});
async function withHandlingDefaultSearchPrompt({ extensionId, respond }, cb) {
const promptResponseHandled = TestUtils.topicObserved(
"webextension-defaultsearch-prompt-response"
);
const prompted = TestUtils.topicObserved(
"webextension-defaultsearch-prompt",
(subject, message) => {
if (subject.wrappedJSObject.id == extensionId) {
return subject.wrappedJSObject.respond(respond);
}
}
);
await Promise.all([cb(), prompted, promptResponseHandled]);
}
async function assertUpdateDoNotPrompt(extension, updateExtensionInfo) {
let deferredUpgradePrompt = topicObservable(
"webextension-defaultsearch-prompt",
(subject, message) => {
if (subject.wrappedJSObject.id == extension.id) {
ok(false, "should not prompt on update");
}
}
);
await Promise.race([
extension.upgrade(updateExtensionInfo),
deferredUpgradePrompt.promise,
]);
deferredUpgradePrompt.resolve();
await AddonTestUtils.waitForSearchProviderStartup(extension);
equal(
extension.version,
updateExtensionInfo.manifest.version,
"The updated addon has the expected version."
);
}
add_task(async function test_default_search_prompts() {
/* This tests the scenario where an addon did not gain
* default search during install, and later upgrades.
@ -368,22 +409,15 @@ add_task(async function test_default_search_prompts() {
let extension = ExtensionTestUtils.loadExtension(extensionInfo);
// Mock a response from the default search prompt where we
// say no to setting this as the default when installing.
let prompted = TestUtils.topicObserved(
"webextension-defaultsearch-prompt",
(subject, message) => {
if (subject.wrappedJSObject.id == extension.id) {
return subject.wrappedJSObject.respond(false);
}
}
);
let defaultEngineName = (await Services.search.getDefault()).name;
ok(defaultEngineName !== "Example", "Search is not Example.");
await extension.startup();
await prompted;
// Mock a response from the default search prompt where we
// say no to setting this as the default when installing.
await withHandlingDefaultSearchPrompt(
{ extensionId: EXTENSION_ID, respond: false },
() => extension.startup()
);
equal(
extension.version,
@ -396,70 +430,354 @@ add_task(async function test_default_search_prompts() {
"Default engine is the default after startup."
);
extensionInfo.manifest = {
version: "2.0",
applications: {
gecko: {
id: EXTENSION_ID,
},
},
chrome_settings_overrides: {
search_provider: {
name: "Example",
search_url: "https://example.com/?q={searchTerms}",
is_default: true,
},
},
};
let deferredUpgradePrompt = topicObservable(
"webextension-defaultsearch-prompt",
(subject, message) => {
if (subject.wrappedJSObject.id == extension.id) {
ok(false, "should not prompt on update");
}
}
info(
"Verify that updating the extension does not prompt and does not take over the default engine"
);
await Promise.race([
extension.upgrade(extensionInfo),
deferredUpgradePrompt.promise,
]);
deferredUpgradePrompt.resolve();
await AddonTestUtils.waitForSearchProviderStartup(extension);
equal(
extension.version,
"2.0",
"The updated addon has the expected version."
);
// An upgraded extension does not become the default engine.
extensionInfo.manifest.version = "2.0";
await assertUpdateDoNotPrompt(extension, extensionInfo);
equal(
(await Services.search.getDefault()).name,
defaultEngineName,
"Default engine is still the default after startup."
"Default engine is still the default after update."
);
info("Verify that disable/enable the extension does prompt the user");
let addon = await AddonManager.getAddonByID(EXTENSION_ID);
await addon.disable();
prompted = TestUtils.topicObserved(
"webextension-defaultsearch-prompt",
(subject, message) => {
if (subject.wrappedJSObject.id == extension.id) {
return subject.wrappedJSObject.respond(false);
}
await withHandlingDefaultSearchPrompt(
{ extensionId: EXTENSION_ID, respond: false },
async () => {
await addon.disable();
await addon.enable();
}
);
await Promise.all([addon.enable(), prompted]);
// we still said no.
equal(
(await Services.search.getDefault()).name,
defaultEngineName,
"Default engine is the default after startup."
"Default engine is the default after being disabling/enabling."
);
await extension.unload();
});
async function test_default_search_on_updating_addons_installed_before_bug1757760({
builtinAsInitialDefault,
}) {
/* This tests covers a scenario similar to the previous test but with an extension-settings.json file
content like the one that would be available in the profile if the add-on was installed on firefox
versions that didn't include the changes from Bug 1757760 (See Bug 1767550).
*/
const EXTENSION_ID = `test_old_addon@tests.mozilla.org`;
const EXTENSION_ID2 = `test_old_addon2@tests.mozilla.org`;
const extensionInfo = {
useAddonManager: "permanent",
manifest: {
version: "1.1",
browser_specific_settings: {
gecko: {
id: EXTENSION_ID,
},
},
chrome_settings_overrides: {
search_provider: {
name: "Test SearchEngine",
search_url: "https://example.com/?q={searchTerms}",
is_default: true,
},
},
},
};
const extensionInfo2 = {
useAddonManager: "permanent",
manifest: {
version: "1.2",
browser_specific_settings: {
gecko: {
id: EXTENSION_ID2,
},
},
chrome_settings_overrides: {
search_provider: {
name: "Test SearchEngine2",
search_url: "https://example.com/?q={searchTerms}",
is_default: true,
},
},
},
};
const { ExtensionSettingsStore } = ChromeUtils.import(
"resource://gre/modules/ExtensionSettingsStore.jsm"
);
async function assertExtensionSettingsStore(
extensionInfo,
expectedLevelOfControl
) {
const { id } = extensionInfo.manifest.browser_specific_settings.gecko;
info(`Asserting ExtensionSettingsStore for ${id}`);
const item = ExtensionSettingsStore.getSetting(
"default_search",
"defaultSearch",
id
);
equal(
item.value,
extensionInfo.manifest.chrome_settings_overrides.search_provider.name,
"Got the expected item returned by ExtensionSettingsStore.getSetting"
);
const control = await ExtensionSettingsStore.getLevelOfControl(
id,
"default_search",
"defaultSearch"
);
equal(
control,
expectedLevelOfControl,
`Got expected levelOfControl for ${id}`
);
}
info("Install test extensions without opt-in to the related search engines");
let extension = ExtensionTestUtils.loadExtension(extensionInfo);
let extension2 = ExtensionTestUtils.loadExtension(extensionInfo2);
// Mock a response from the default search prompt where we
// say no to setting this as the default when installing.
await withHandlingDefaultSearchPrompt(
{ extensionId: EXTENSION_ID, respond: false },
() => extension.startup()
);
equal(
extension.version,
"1.1",
"first installed addon has the expected version."
);
// Mock a response from the default search prompt where we
// say no to setting this as the default when installing.
await withHandlingDefaultSearchPrompt(
{ extensionId: EXTENSION_ID2, respond: false },
() => extension2.startup()
);
equal(
extension2.version,
"1.2",
"second installed addon has the expected version."
);
info("Setup preconditions (set the initial default search engine)");
// Sanity check to be sure the initial engine expected as precondition
// for the scenario covered by the current test case.
let initialEngine;
if (builtinAsInitialDefault) {
initialEngine = Services.search.originalDefaultEngine;
} else {
initialEngine = Services.search.getEngineByName(
extensionInfo.manifest.chrome_settings_overrides.search_provider.name
);
}
await Services.search.setDefault(initialEngine);
let defaultEngineName = (await Services.search.getDefault()).name;
Assert.equal(
defaultEngineName,
initialEngine.name,
`initial default search engine expected to be ${
builtinAsInitialDefault ? "app-provided" : EXTENSION_ID
}`
);
Assert.notEqual(
defaultEngineName,
extensionInfo2.manifest.chrome_settings_overrides.search_provider.name,
"initial default search engine name should not be the same as the second extension search_provider"
);
equal(
(await Services.search.getDefault()).name,
initialEngine.name,
`Default engine should still be set to the ${
builtinAsInitialDefault ? "app-provided" : EXTENSION_ID
}.`
);
// Mock an update from settings stored as in an older Firefox version where Bug 1757760 was not landed yet.
info(
"Setup preconditions (inject mock extension-settings.json data and assert on the expected setting and levelOfControl)"
);
let addon = await AddonManager.getAddonByID(EXTENSION_ID);
let addon2 = await AddonManager.getAddonByID(EXTENSION_ID2);
const extensionSettingsData = {
version: 2,
url_overrides: {},
prefs: {},
homepageNotification: {},
tabHideNotification: {},
default_search: {
defaultSearch: {
initialValue: Services.search.originalDefaultEngine.name,
precedenceList: [
{
id: EXTENSION_ID2,
// The install dates are used in ExtensionSettingsStore.getLevelOfControl
// and to recreate the expected preconditions the last extension installed
// should have a installDate timestamp > then the first one.
installDate: addon2.installDate.getTime() + 1000,
value:
extensionInfo2.manifest.chrome_settings_overrides.search_provider
.name,
// When an addon with a default search engine override is installed in Firefox versions
// without the changes landed from Bug 1757760, `enabled` will be set to true in all cases
// (Prompt never answered, or when No or Yes is selected by the user).
enabled: true,
},
{
id: EXTENSION_ID,
installDate: addon.installDate.getTime(),
value:
extensionInfo.manifest.chrome_settings_overrides.search_provider
.name,
enabled: true,
},
],
},
},
newTabNotification: {},
commands: {},
};
const file = Services.dirsvc.get("ProfD", Ci.nsIFile);
file.append("extension-settings.json");
info(`writing mock settings data into ${file.path}`);
await IOUtils.writeJSON(file.path, extensionSettingsData);
await ExtensionSettingsStore._reloadFile(false);
equal(
(await Services.search.getDefault()).name,
initialEngine.name,
"Default engine is still set to the initial one."
);
// The following assertions verify that the migration applied from ExtensionSettingsStore
// fixed the inconsistent state and kept the search engine unchanged.
//
// - With the fixed settings we expect both to be resolved to "controllable_by_this_extension".
// - Without the fix applied during the migration the levelOfControl resolved would be:
// - for the last installed: "controlled_by_this_extension"
// - for the first installed: "controlled_by_other_extensions"
await assertExtensionSettingsStore(
extensionInfo2,
"controlled_by_this_extension"
);
await assertExtensionSettingsStore(
extensionInfo,
"controlled_by_other_extensions"
);
info(
"Verify that updating the extension does not prompt and does not take over the default engine"
);
extensionInfo2.manifest.version = "2.2";
await assertUpdateDoNotPrompt(extension2, extensionInfo2);
extensionInfo.manifest.version = "2.1";
await assertUpdateDoNotPrompt(extension, extensionInfo);
equal(
(await Services.search.getDefault()).name,
initialEngine.name,
"Default engine is still the same after updating both the test extensions."
);
// After both the extensions have been updated and their inconsistent state
// updated internally, both extensions should have levelOfControl "controllable_*".
await assertExtensionSettingsStore(
extensionInfo2,
"controllable_by_this_extension"
);
await assertExtensionSettingsStore(
extensionInfo,
// We expect levelOfControl to be controlled_by_this_extension if the test case
// is expecting the third party extension to stay set as default.
builtinAsInitialDefault
? "controllable_by_this_extension"
: "controlled_by_this_extension"
);
info("Verify that disable/enable the extension does prompt the user");
await withHandlingDefaultSearchPrompt(
{ extensionId: EXTENSION_ID2, respond: false },
async () => {
await addon2.disable();
await addon2.enable();
}
);
// we said no.
equal(
(await Services.search.getDefault()).name,
initialEngine.name,
`Default engine should still be the same after disabling/enabling ${EXTENSION_ID2}.`
);
await withHandlingDefaultSearchPrompt(
{ extensionId: EXTENSION_ID, respond: false },
async () => {
await addon.disable();
await addon.enable();
}
);
// we said no.
equal(
(await Services.search.getDefault()).name,
Services.search.originalDefaultEngine.name,
`Default engine should be set to the original default after disabling/enabling ${EXTENSION_ID}.`
);
await withHandlingDefaultSearchPrompt(
{ extensionId: EXTENSION_ID, respond: true },
async () => {
await addon.disable();
await addon.enable();
}
);
// we responded yes.
equal(
(await Services.search.getDefault()).name,
extensionInfo.manifest.chrome_settings_overrides.search_provider.name,
"Default engine should be set to the one opted-in from the last prompt."
);
await extension.unload();
await extension2.unload();
}
add_task(function test_builtin_default_search_after_updating_old_addons() {
return test_default_search_on_updating_addons_installed_before_bug1757760({
builtinAsInitialDefault: true,
});
});
add_task(function test_third_party_default_search_after_updating_old_addons() {
return test_default_search_on_updating_addons_installed_before_bug1757760({
builtinAsInitialDefault: false,
});
});

Просмотреть файл

@ -6,12 +6,13 @@ body {
display: flex;
align-items: stretch;
padding-block: 40px 80px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Ubuntu", "Helvetica Neue", sans-serif;
font: message-box;
font-size: 1.5em;
}
h1 {
font-size: 24px;
font-weight: 700;
font-weight: 500;
font-size: 1.5em;
}
body > nav {
@ -77,6 +78,7 @@ body > main > aside {
.page-section-header > .section-description {
grid-area: desc;
color: var(--in-content-deemphasized-text)
}
.setup-step > h2 {
@ -101,7 +103,7 @@ body > main > aside {
.closed-tab-li {
display: grid;
grid-template-columns: repeat(8, 1fr);
grid-template-columns: min-content repeat(8, 1fr);
column-gap: 16px;
padding: 8px;
cursor: pointer;
@ -119,6 +121,7 @@ body > main > aside {
.closed-tab-li-title {
grid-column: span 5;
padding-inline-start: 2px;
font-weight: 500;
}
.closed-tab-li-url {
@ -130,14 +133,23 @@ body > main > aside {
text-align: end;
}
.closed-tab-li-url, .closed-tab-li-time {
color: var(--in-content-deemphasized-text);
font-weight: 400;
}
.closed-tab-li-title, .closed-tab-li-url {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.icon {
background-position: center center;
background-repeat: no-repeat;
display: inline-block;
-moz-context-properties: fill;
fill: currentColor;
height: 16px;
width: 16px;
margin-top: 10px;
}
@ -152,3 +164,13 @@ body > main > aside {
.icon.history {
background-image: url('chrome://browser/skin/history.svg');
}
.favicon {
background-size: cover;
margin: 2px;
}
.favicon, .icon {
width: 16px;
height: 16px;
}

Просмотреть файл

@ -6,7 +6,7 @@
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src chrome:; object-src 'none'">
<meta http-equiv="Content-Security-Policy" content="default-src chrome:; object-src 'none'; img-src data: chrome:;">
<meta name="color-scheme" content="light dark">
<title data-l10n-id="firefoxview-page-title"></title>
<link rel="localization" href="branding/brand.ftl">

Просмотреть файл

@ -29,4 +29,5 @@ window.addEventListener("load", () => {
window.addEventListener("unload", () => {
tabsSetupFlowManager?.uninit();
document.getElementById("recently-closed-tabs-container").cleanup();
});

Просмотреть файл

@ -14,8 +14,19 @@ XPCOMUtils.defineLazyModuleGetters(globalThis, {
});
const relativeTimeFormat = new Services.intl.RelativeTimeFormat(undefined, {});
const SS_NOTIFY_CLOSED_OBJECTS_CHANGED = "sessionstore-closed-objects-changed";
function getWindow() {
return window.browsingContext.embedderWindowGlobal.browsingContext.window;
}
class RecentlyClosedTabsList extends HTMLElement {
constructor() {
super();
this.maxTabsLength = 25;
this.closedTabsData = [];
}
get tabsList() {
return this.querySelector("ol");
}
@ -29,20 +40,18 @@ class RecentlyClosedTabsList extends HTMLElement {
connectedCallback() {
this.addEventListener("click", this);
this.addEventListener("keydown", this);
}
handleEvent(event) {
if (event.type == "click") {
const item = event.target.closest(".closed-tab-li");
event.preventDefault();
this.openTab(item.dataset.targetURI);
if (
event.type == "click" ||
(event.type == "keydown" && event.keyCode == KeyEvent.DOM_VK_RETURN)
) {
this.openTabAndUpdate(event);
}
}
getWindow() {
return window.windowRoot.ownerGlobal;
}
convertTimestamp(timestamp) {
const elapsed = Date.now() - timestamp;
const nowThresholdMs = 91000;
@ -79,66 +88,145 @@ class RecentlyClosedTabsList extends HTMLElement {
return displayHost.length ? displayHost : uriString;
}
getTargetURI(tab) {
let targetURI = "";
getTabStateValue(tab, key) {
let value = "";
const tabEntries = tab.state.entries;
const activeIndex = tabEntries.length - 1;
if (activeIndex >= 0 && tabEntries[activeIndex]) {
targetURI = tabEntries[activeIndex].url;
value = tabEntries[activeIndex][key];
}
return targetURI;
return value;
}
openTab(targetURI) {
window.open(targetURI, "_blank");
openTabAndUpdate(event) {
event.preventDefault();
const item = event.target.closest(".closed-tab-li");
const index = [...this.tabsList.children].indexOf(item);
SessionStore.undoCloseTab(getWindow(), index);
this.tabsList.removeChild(item);
}
generateTabs() {
let closedTabs = SessionStore.getClosedTabData(this.getWindow());
closedTabs = closedTabs.slice(0, 25);
initiateTabsList() {
let closedTabs = SessionStore.getClosedTabData(getWindow());
closedTabs = closedTabs.slice(0, this.maxTabsLength);
this.closedTabsData = closedTabs;
for (const tab of closedTabs) {
let li = document.createElement("li");
li.classList.add("closed-tab-li");
if (tab.image) {
// TODO - figure out how to render this properly
PlacesUIUtils.setImage(tab, li);
}
let title = document.createElement("span");
title.textContent = `${tab.title}`;
title.classList.add("closed-tab-li-title");
const targetURI = this.getTargetURI(tab);
li.dataset.targetURI = targetURI;
document.l10n.setAttributes(li, "firefoxview-closed-tabs-tab-button", {
targetURI,
});
let url = document.createElement("span");
if (targetURI) {
url.textContent = this.formatURIForDisplay(targetURI);
url.classList.add("closed-tab-li-url");
}
let time = document.createElement("span");
time.textContent = this.convertTimestamp(tab.closedAt);
time.classList.add("closed-tab-li-time");
li.append(title, url, time);
this.tabsList.appendChild(li);
const li = this.generateListItem(tab);
this.tabsList.append(li);
}
this.tabsList.hidden = false;
}
updateTabsList() {
let newClosedTabs = SessionStore.getClosedTabData(getWindow());
newClosedTabs = newClosedTabs.slice(0, this.maxTabsLength);
if (this.closedTabsData.length && !newClosedTabs.length) {
// if a user purges history, clear the list
[...this.tabsList.children].forEach(node =>
this.tabsList.removeChild(node)
);
document
.getElementById("recently-closed-tabs-container")
.togglePlaceholderVisibility(true);
this.tabsList.hidden = true;
this.closedTabsData = [];
return;
}
const tabsToAdd = newClosedTabs.filter(
newTab =>
!this.closedTabsData.some(tab => {
return (
this.getTabStateValue(tab, "ID") ==
this.getTabStateValue(newTab, "ID")
);
})
);
if (!tabsToAdd.length) {
return;
}
for (let tab of tabsToAdd.reverse()) {
if (this.tabsList.children.length == this.maxTabsLength) {
this.tabsList.lastChild.remove();
}
let li = this.generateListItem(tab);
this.tabsList.prepend(li);
}
this.closedTabsData = newClosedTabs;
// for situations where the tab list will initially be empty (such as
// with new profiles or automatic session restore is disabled) and
// this.initiateTabsList won't be called
if (this.tabsList.hidden) {
this.tabsList.hidden = false;
document
.getElementById("recently-closed-tabs-container")
.togglePlaceholderVisibility(false);
}
}
setFavicon(tab) {
const imageUrl = tab.image
? PlacesUIUtils.getImageURL(tab)
: "chrome://global/skin/icons/defaultFavicon.svg";
let favicon = document.createElement("div");
favicon.style.backgroundImage = `url('${imageUrl}')`;
favicon.classList.add("favicon");
favicon.setAttribute("role", "presentation");
return favicon;
}
generateListItem(tab) {
const li = document.createElement("li");
li.classList.add("closed-tab-li");
li.setAttribute("tabindex", 0);
li.setAttribute("role", "button");
const title = document.createElement("span");
title.textContent = `${tab.title}`;
title.classList.add("closed-tab-li-title");
const favicon = this.setFavicon(tab);
li.append(favicon);
const targetURI = this.getTabStateValue(tab, "url");
li.dataset.targetURI = targetURI;
document.l10n.setAttributes(li, "firefoxview-closed-tabs-tab-button", {
targetURI,
});
const url = document.createElement("span");
if (targetURI) {
url.textContent = this.formatURIForDisplay(targetURI);
url.classList.add("closed-tab-li-url");
}
const time = document.createElement("span");
time.textContent = this.convertTimestamp(tab.closedAt);
time.classList.add("closed-tab-li-time");
li.append(title, url, time);
return li;
}
}
customElements.define("recently-closed-tabs-list", RecentlyClosedTabsList);
class RecentlyClosedTabsContainer extends HTMLElement {
getWindow = () => window.windowRoot.ownerGlobal;
constructor() {
super();
this.observerAdded = false;
this.boundObserve = (...args) => this.observe(...args);
}
connectedCallback() {
this.noTabsElement = this.querySelector(
@ -149,28 +237,77 @@ class RecentlyClosedTabsContainer extends HTMLElement {
"#collapsible-tabs-container"
);
this.collapsibleButton = this.querySelector("#collapsible-tabs-button");
this.collapsibleButton.addEventListener("click", this);
getWindow().gBrowser.tabContainer.addEventListener("TabSelect", this);
}
cleanup() {
getWindow().gBrowser.tabContainer.removeEventListener("TabSelect", this);
if (this.observerAdded) {
Services.obs.removeObserver(
this.boundObserve,
SS_NOTIFY_CLOSED_OBJECTS_CHANGED
);
}
}
// we observe when a tab closes but since this notification fires more frequently and on
// all windows, we remove the observer when another tab is selected; we check for changes
// to the session store once the user return to this tab.
handleObservers(contentDocument) {
if (
!this.observerAdded &&
contentDocument &&
contentDocument.URL == "about:firefoxview"
) {
Services.obs.addObserver(
this.boundObserve,
SS_NOTIFY_CLOSED_OBJECTS_CHANGED
);
this.observerAdded = true;
this.list.updateTabsList();
} else if (this.observerAdded) {
Services.obs.removeObserver(
this.boundObserve,
SS_NOTIFY_CLOSED_OBJECTS_CHANGED
);
this.observerAdded = false;
}
}
observe = () => this.list.updateTabsList();
onLoad() {
if (this.getClosedTabCount() == 0) {
this.noTabsElement.hidden = false;
this.collapsibleContainer.classList.add("empty-container");
this.togglePlaceholderVisibility(true);
} else {
this.list.generateTabs();
this.list.initiateTabsList();
}
Services.obs.addObserver(
this.boundObserve,
SS_NOTIFY_CLOSED_OBJECTS_CHANGED
);
this.observerAdded = true;
}
handleEvent(event) {
if (event.type == "click" && event.target == this.collapsibleButton) {
this.toggleTabs();
} else if (event.type == "TabSelect") {
this.handleObservers(event.target.linkedBrowser.contentDocument);
}
}
togglePlaceholderVisibility(visible) {
this.noTabsElement.toggleAttribute("hidden", !visible);
this.collapsibleContainer.classList.toggle("empty-container", visible);
}
getClosedTabCount = () => {
try {
return SessionStore.getClosedTabCount(this.getWindow());
return SessionStore.getClosedTabCount(getWindow());
} catch (ex) {
return 0;
}

Просмотреть файл

@ -1,6 +1,9 @@
[DEFAULT]
run-if = nightly_build # about:firefoxview is only enabled on Nightly
support-files = head.js
[browser_firefoxview.js]
[browser_firefoxview_tab.js]
[browser_recently_closed_tabs.js]
[browser_setup_state.js]

Просмотреть файл

@ -0,0 +1,213 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
XPCOMUtils.defineLazyModuleGetters(globalThis, {
SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
});
const URLs = [
"http://mochi.test:8888/browser/",
"http://www.example.com/",
"http://example.net",
"http://example.org",
];
async function add_new_tab(URL) {
let tab = BrowserTestUtils.addTab(gBrowser, URL);
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
return tab;
}
async function close_tab(tab) {
const sessionStorePromise = BrowserTestUtils.waitForSessionStoreUpdate(tab);
BrowserTestUtils.removeTab(tab);
await sessionStorePromise;
}
function clearHistory() {
Services.obs.notifyObservers(null, "browser:purge-session-history");
}
add_task(async function test_empty_list() {
clearHistory();
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
const closedObjectsChanged = TestUtils.topicObserved(
"sessionstore-closed-objects-changed"
);
ok(
document
.querySelector("#collapsible-tabs-container")
.classList.contains("empty-container"),
"collapsible container should have correct styling when the list is empty"
);
testVisibility(browser, {
expectedVisible: {
"#recently-closed-tabs-placeholder": true,
"ol.closed-tabs-list": false,
},
});
const tab1 = await add_new_tab(URLs[0]);
await close_tab(tab1);
await closedObjectsChanged;
ok(
!document
.querySelector("#collapsible-tabs-container")
.classList.contains("empty-container"),
"collapsible container should have correct styling when the list is not empty"
);
testVisibility(browser, {
expectedVisible: {
"#recently-closed-tabs-placeholder": false,
"ol.closed-tabs-list": true,
},
});
ok(
document.querySelector("ol.closed-tabs-list").children.length === 1,
"recently-closed-tabs-list should have one list item"
);
}
);
});
add_task(async function test_list_ordering() {
const existingData = SessionStore.getClosedTabCount(window);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
const closedObjectsChanged = TestUtils.topicObserved(
"sessionstore-closed-objects-changed"
);
const tab1 = await add_new_tab(URLs[0]);
const tab2 = await add_new_tab(URLs[1]);
const tab3 = await add_new_tab(URLs[2]);
gBrowser.selectedTab = tab3;
await close_tab(tab3);
await closedObjectsChanged;
await close_tab(tab2);
await closedObjectsChanged;
await close_tab(tab1);
await closedObjectsChanged;
const tabsList = document.querySelector("ol.closed-tabs-list");
await BrowserTestUtils.waitForMutationCondition(
tabsList,
{ childList: true },
() => tabsList.children.length > 1
);
ok(
document.querySelector("ol.closed-tabs-list").children.length ===
3 + existingData,
"recently-closed-tabs-list should have one list item"
);
// check that the ordering is correct when user navigates to another tab, and then closes multiple tabs.
ok(
document
.querySelector("ol.closed-tabs-list")
.firstChild.textContent.includes("mochi.test"),
"first list item in recently-closed-tabs-list is in the correct order"
);
ok(
document
.querySelector("ol.closed-tabs-list")
.children[2].textContent.includes("example.net"),
"last list item in recently-closed-tabs-list is in the correct order"
);
}
);
});
add_task(async function test_max_list_items() {
// the tabs opened from the previous test provide seed data
const mockMaxTabsLength = SessionStore.getClosedTabCount(window);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
// override this value for testing purposes
document.querySelector(
"recently-closed-tabs-list"
).maxTabsLength = mockMaxTabsLength;
ok(
!document
.querySelector("#collapsible-tabs-container")
.classList.contains("empty-container"),
"collapsible container should have correct styling when the list is not empty"
);
testVisibility(browser, {
expectedVisible: {
"#recently-closed-tabs-placeholder": false,
"ol.closed-tabs-list": true,
},
});
ok(
document.querySelector("ol.closed-tabs-list").childNodes.length ===
mockMaxTabsLength,
`recently-closed-tabs-list should have ${mockMaxTabsLength} list items`
);
ok(
document
.querySelector("ol.closed-tabs-list")
.firstChild.textContent.includes("about:firefoxview"),
"first list item in recently-closed-tabs-list is from previous test (session store)"
);
const closedObjectsChanged = TestUtils.topicObserved(
"sessionstore-closed-objects-changed"
);
// add another tab
const tab = await add_new_tab(URLs[3]);
await close_tab(tab);
await closedObjectsChanged;
ok(
document
.querySelector("ol.closed-tabs-list")
.firstChild.textContent.includes("example.org"),
"first list item in recently-closed-tabs-list should have been updated"
);
ok(
document.querySelector("ol.closed-tabs-list").childNodes.length ===
mockMaxTabsLength,
`recently-closed-tabs-list should still have ${mockMaxTabsLength} list items`
);
}
);
});

Просмотреть файл

@ -0,0 +1,20 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-unused-vars */
function testVisibility(browser, expected) {
const { document } = browser.contentWindow;
for (let [selector, shouldBeVisible] of Object.entries(
expected.expectedVisible
)) {
const elem = document.querySelector(selector);
if (shouldBeVisible) {
ok(
BrowserTestUtils.is_visible(elem),
`Expected ${selector} to be visible`
);
} else {
ok(BrowserTestUtils.is_hidden(elem), `Expected ${selector} to be hidden`);
}
}
}

Просмотреть файл

@ -1806,14 +1806,13 @@ var PlacesUIUtils = {
}
},
setImage(aItem, aElement) {
getImageURL(aItem) {
let iconURL = aItem.image;
// don't initiate a connection just to fetch a favicon (see bug 467828)
if (/^https?:/.test(iconURL)) {
iconURL = "moz-anno:favicon:" + iconURL;
}
aElement.setAttribute("image", iconURL);
return iconURL;
},
/**

Просмотреть файл

@ -21,6 +21,7 @@ support-files =
[browser_privatebrowsing_DownloadLastDirWithCPS.js]
[browser_privatebrowsing_about_default_promo.js]
[browser_privatebrowsing_about_focus_promo.js]
[browser_privatebrowsing_about_nimbus.js]
[browser_privatebrowsing_about_nimbus_messaging.js]
[browser_privatebrowsing_about_nimbus_impressions.js]
@ -44,7 +45,6 @@ skip-if = verify
[browser_privatebrowsing_downloadLastDir_c.js]
[browser_privatebrowsing_downloadLastDir_toggle.js]
[browser_privatebrowsing_favicon.js]
[browser_privatebrowsing_focus_promo.js]
[browser_privatebrowsing_history_shift_click.js]
[browser_privatebrowsing_last_private_browsing_context_exited.js]
[browser_privatebrowsing_lastpbcontextexited.js]

Просмотреть файл

@ -57,7 +57,7 @@ add_task(async function test_focus_promo_in_disallowed_region() {
add_task(
async function test_klar_promo_in_certain_regions_with_English_locale() {
const testLocale = "en-GB"; // British English
const testLocale = "en-US"; // US English
setLocale(testLocale);
const testRegion = async region => {

Просмотреть файл

@ -192,7 +192,8 @@ function createEntry(
element.setAttribute("label", aMenuLabel);
if (aClosedTab.image) {
PlacesUIUtils.setImage(aClosedTab, element);
const iconURL = PlacesUIUtils.getImageURL(aClosedTab);
element.setAttribute("image", iconURL);
}
if (!aIsWindowsFragment) {
element.setAttribute("value", aIndex);

Просмотреть файл

@ -1081,6 +1081,7 @@ var SessionStoreInternal = {
// Non-SHIP code calls this when the frame script is unloaded.
this.onFinalTabStateUpdateComplete(aSubject);
}
this._notifyOfClosedObjectsChange();
break;
}
},

Просмотреть файл

@ -41,7 +41,11 @@ CONTENT_WIN.addEventListener("DOMContentLoaded", function onDCL(evt) {
case "childList": {
// We really only care about elements appending inside pages.
if (!mutation.addedNodes || !mutation.target.closest(".page")) {
let parent =
mutation.target instanceof HTMLDocument
? mutation.target.documentElement
: mutation.target;
if (!mutation.addedNodes || !parent.closest(".page")) {
break;
}
FormAutofillUtils.localizeMarkup(mutation.target);

Просмотреть файл

@ -64,6 +64,12 @@ let AVAILABLE_PIP_OVERRIDES;
},
},
hulu: {
"https://www.hulu.com/watch/*": {
videoWrapperScriptPath: "video-wrappers/hulu.js",
},
},
instagram: {
"https://www.instagram.com/*": { policy: TOGGLE_POLICIES.ONE_QUARTER },
},

Просмотреть файл

@ -32,6 +32,7 @@ FINAL_TARGET_FILES.features["pictureinpicture@mozilla.org"]["video-wrappers"] +=
"video-wrappers/dailymotion.js",
"video-wrappers/funimation.js",
"video-wrappers/hotstar.js",
"video-wrappers/hulu.js",
"video-wrappers/mock-wrapper.js",
"video-wrappers/netflix.js",
"video-wrappers/piped.js",

Просмотреть файл

@ -0,0 +1,48 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
class PictureInPictureVideoWrapper {
constructor(video) {
this.player = video.wrappedJSObject.__HuluDashPlayer__;
}
play() {
this.player.play();
}
pause() {
this.player.pause();
}
isMuted(video) {
return video.volume === 0;
}
setMuted() {
let muteButton = document.querySelector(".VolumeControl > div");
muteButton.click();
}
setCaptionContainerObserver(video, updateCaptionsFunction) {
let container = document.querySelector(".ClosedCaption");
if (container) {
updateCaptionsFunction("");
const callback = function(mutationsList, observer) {
let text = container.querySelector(".CaptionBox").innerText;
updateCaptionsFunction(text);
};
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

Просмотреть файл

@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "Web Compatibility Interventions",
"description": "Urgent post-release fixes for web compatibility.",
"version": "101.7.0",
"version": "101.8.0",
"applications": {
"gecko": {
"id": "webcompat@mozilla.org",

Просмотреть файл

@ -9,13 +9,56 @@
*
* Some sites rely on Maxmind's GeoIP library which gets blocked by ETP's
* fingerprinter blocking. With the library window global not being defined
* functionality may break or the site does not render at all. This shim adds a
* dummy object which returns errors for any request to mitigate the breakage.
* functionality may break or the site does not render at all. This shim
* has it return the United States as the location for all users.
*/
if (!window.geoip2) {
const callback = (_, onError) => {
onError("");
const continent = {
code: "NA",
geoname_id: 6255149,
names: {
de: "Nordamerika",
en: "North America",
es: "Norteamérica",
fr: "Amérique du Nord",
ja: "北アメリカ",
"pt-BR": "América do Norte",
ru: "Северная Америка",
"zh-CN": "北美洲",
},
};
const country = {
geoname_id: 6252001,
iso_code: "US",
names: {
de: "USA",
en: "United States",
es: "Estados Unidos",
fr: "États-Unis",
ja: "アメリカ合衆国",
"pt-BR": "Estados Unidos",
ru: "США",
"zh-CN": "美国",
},
};
const city = {
names: {
en: "",
},
};
const callback = onSuccess => {
requestAnimationFrame(() => {
onSuccess({
city,
continent,
country,
registered_country: country,
});
});
};
window.geoip2 = {

Просмотреть файл

@ -261,7 +261,7 @@ const ColorwayCollections = [
colorwayClosetEnabled && AppConstants.NIGHTLY_BUILD
? "2022-04-20"
: "2022-05-03",
l10nId: "colorway-collection-life-in-color",
l10nId: "colorway-collection-true-colors",
},
];

Просмотреть файл

@ -119,6 +119,7 @@ https://sub2.test1.example.com:443 privileged
https://sub2.test2.example.com:443 privileged
https://example.net:443 privileged
https://nocert.example.com:443 privileged,nocert
https://nocert.example.org:443 privileged,nocert
https://self-signed.example.com:443 privileged,cert=selfsigned
https://untrusted.example.com:443 privileged,cert=untrusted
https://expired.example.com:443 privileged,cert=expired

Просмотреть файл

@ -1208,7 +1208,22 @@
"../node_modules/babel-loader/lib/index.js??ref--1!../packages/devtools-source-map/src/utils/network-request.js": 1056,
"../node_modules/babel-loader/lib/index.js??ref--1!../../shared/worker-dispatcher.js": 1057,
"../node_modules/babel-loader/lib/index.js??ref--1!../packages/devtools-source-map/src/utils/privileged-network-request.js": 1058,
"../node_modules/babel-loader/lib/index.js??ref--1!../../shared/worker-utils.js": 1059
"../node_modules/babel-loader/lib/index.js??ref--1!../../shared/worker-utils.js": 1059,
"../packages/devtools-source-map/node_modules/whatwg-url/lib/url-state-machine.js": 1060,
"../packages/devtools-source-map/node_modules/whatwg-url/lib/urlencoded.js": 1061,
"../packages/devtools-source-map/node_modules/webidl-conversions/lib/index.js": 1062,
"../packages/devtools-source-map/node_modules/whatwg-url/lib/utils.js": 1063,
"../node_modules/node-libs-browser/node_modules/punycode/punycode.js": 1064,
"../packages/devtools-source-map/node_modules/whatwg-url/lib/infra.js": 1065,
"../packages/devtools-source-map/node_modules/whatwg-url/lib/URLSearchParams.js": 1066,
"../packages/devtools-source-map/node_modules/whatwg-url/lib/public-api.js": 1067,
"../packages/devtools-source-map/node_modules/whatwg-url/lib/URL.js": 1068,
"../packages/devtools-source-map/node_modules/whatwg-url/lib/URL-impl.js": 1069,
"../packages/devtools-source-map/node_modules/tr46/index.js": 1070,
"../packages/devtools-source-map/node_modules/tr46/lib/regexes.js": 1071,
"../node_modules/json-loader/index.js!../packages/devtools-source-map/node_modules/tr46/lib/mappingTable.json": 1072,
"../packages/devtools-source-map/node_modules/whatwg-url/lib/URLSearchParams-impl.js": 1073,
"../node_modules/lodash.sortby/index.js": 1074
},
"usedIds": {
"0": 0,
@ -2270,7 +2285,22 @@
"1056": 1056,
"1057": 1057,
"1058": 1058,
"1059": 1059
"1059": 1059,
"1060": 1060,
"1061": 1061,
"1062": 1062,
"1063": 1063,
"1064": 1064,
"1065": 1065,
"1066": 1066,
"1067": 1067,
"1068": 1068,
"1069": 1069,
"1070": 1070,
"1071": 1071,
"1072": 1072,
"1073": 1073,
"1074": 1074
}
},
"chunks": {
@ -2415,7 +2445,7 @@
"byName": {},
"byBlocks": {},
"usedIds": {
"0": 0
"1": 1
}
}
}
@ -2436,7 +2466,7 @@
"byName": {},
"byBlocks": {},
"usedIds": {
"0": 0
"1": 1
}
}
}

Просмотреть файл

@ -2,49 +2,6 @@
* 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/>. */
const whatwgUrl = `
(() => {
let factory;
function define(...args) {
if (factory) {
throw new Error("expected a single define call");
}
if (
args.length !== 2 ||
!Array.isArray(args[0]) ||
args[0].length !== 0 ||
typeof args[1] !== "function"
) {
throw new Error("whatwg-url had unexpected factory arguments.");
}
factory = args[1];
}
define.amd = true;
const existingDefine = Object.getOwnPropertyDescriptor(globalThis, "define");
globalThis.define = define;
let err;
try {
importScripts("resource://devtools/client/shared/vendor/whatwg-url.js");
if (!factory) {
throw new Error("Failed to load whatwg-url factory");
}
} finally {
if (existingDefine) {
Object.defineProperty(globalThis, "define", existingDefine);
} else {
delete globalThis.define;
}
}
return factory();
})()
`;
module.exports = {
"./source-editor": "devtools/client/sourceeditor/editor",
"../editor/source-editor": "devtools/client/sourceeditor/editor",
@ -59,5 +16,4 @@ module.exports = {
"devtools-services": "Services",
"wasmparser/dist/cjs/WasmParser": "devtools/client/shared/vendor/WasmParser",
"wasmparser/dist/cjs/WasmDis": "devtools/client/shared/vendor/WasmDis",
"whatwg-url": `var ${whatwgUrl}`,
};

Просмотреть файл

@ -25,6 +25,7 @@ Array [
"thread": "FakeThread",
},
],
"filename": "a",
"source": Object {
"extensionName": null,
"id": "a",
@ -42,12 +43,8 @@ Array [
]
`;
exports[`breakpoints should not re-add a breakpoint 1`] = `Array []`;
exports[`breakpoints should not show a breakpoint that does not have text 1`] = `Array []`;
exports[`breakpoints should not show a breakpoint that does not have text 2`] = `Array []`;
exports[`breakpoints should remap breakpoints on pretty print 1`] = `
Object {
"disabled": false,
@ -96,6 +93,7 @@ Array [
"thread": "FakeThread",
},
],
"filename": "a",
"source": Object {
"extensionName": null,
"id": "a",

Просмотреть файл

@ -42,7 +42,10 @@ class EmptyLines extends Component {
shouldComponentUpdate(nextProps) {
const { breakableLines, selectedSource } = this.props;
return (
breakableLines != nextProps.breakableLines ||
// Breakable lines are something that evolves over time,
// but we either have them loaded or not. So only compare the size
// as sometimes we always get a blank new empty Set instance.
breakableLines.size != nextProps.breakableLines.size ||
selectedSource.id != nextProps.selectedSource.id
);
}

Просмотреть файл

@ -15,10 +15,7 @@ import actions from "../../../actions";
import { getSelectedLocation } from "../../../utils/selected-location";
import { createHeadlessEditor } from "../../../utils/editor/create-editor";
import {
makeBreakpointId,
sortSelectedBreakpoints,
} from "../../../utils/breakpoint";
import { makeBreakpointId } from "../../../utils/breakpoint";
import { getSelectedSource, getBreakpointSources } from "../../../selectors";
@ -88,23 +85,18 @@ class Breakpoints extends Component {
}
const editor = this.getEditor();
const sources = [...breakpointSources.map(({ source }) => source)];
const sources = breakpointSources.map(({ source }) => source);
return (
<div className="pane breakpoints-list">
{breakpointSources.map(({ source, breakpoints }) => {
const sortedBreakpoints = sortSelectedBreakpoints(
breakpoints,
selectedSource
);
return [
<BreakpointHeading
key={source.id}
source={source}
sources={sources}
/>,
...sortedBreakpoints.map(breakpoint => (
breakpoints.map(breakpoint => (
<Breakpoint
breakpoint={breakpoint}
source={source}

Просмотреть файл

@ -3,55 +3,83 @@
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
import { createSelector } from "reselect";
import { getSelectedSource, getSourceFromId } from "./sources";
import { getSelectedSource, getSourcesMap } from "./sources";
import { getBreakpointsList } from "./breakpoints";
import { getFilename } from "../utils/source";
import { getSelectedLocation } from "../utils/selected-location";
import { sortSelectedBreakpoints } from "../utils/breakpoint";
function getBreakpointsForSource(source, selectedSource, breakpoints) {
return sortSelectedBreakpoints(breakpoints, selectedSource)
.filter(
bp =>
!bp.options.hidden &&
(bp.text || bp.originalText || bp.options.condition || bp.disabled)
)
.filter(
bp => getSelectedLocation(bp, selectedSource).sourceId == source.id
);
// Returns all the breakpoints for the given selected source
// Depending on the selected source, this will match original or generated
// location of the given selected source.
function _getBreakpointsForSource(visibleBreakpoints, source, selectedSource) {
return visibleBreakpoints.filter(
bp => getSelectedLocation(bp, selectedSource).sourceId == source.id
);
}
const getSourcesForBreakpoints = state => {
const selectedSource = getSelectedSource(state);
const breakpointSourceIds = getBreakpointsList(state).map(
// Returns a sorted list of sources for which we have breakpoints
// We will return generated or original source IDs based on the currently selected source.
const _getSourcesForBreakpoints = (breakpoints, sourcesMap, selectedSource) => {
const breakpointSourceIds = breakpoints.map(
breakpoint => getSelectedLocation(breakpoint, selectedSource).sourceId
);
return [...new Set(breakpointSourceIds)]
.map(sourceId => {
const source = getSourceFromId(state, sourceId);
const filename = getFilename(source);
return { source, filename };
})
.filter(({ source }) => source && !source.isBlackBoxed)
.sort((a, b) => a.filename - b.filename)
.map(({ source }) => source);
const sources = [];
// We may have more than one breakpoint per sourceId,
// so use a Set to have a unique list of source IDs.
for (const sourceId of [...new Set(breakpointSourceIds)]) {
const source = sourcesMap.get(sourceId);
// Ignore any source that is no longer in the sources reducer
// or blackboxed sources.
if (!source || source.isBlackBoxed) {
continue;
}
const bps = _getBreakpointsForSource(breakpoints, source, selectedSource);
// Ignore sources which have no breakpoints
if (bps.length === 0) {
continue;
}
sources.push({
source,
breakpoints: bps,
filename: getFilename(source),
});
}
return sources.sort((a, b) => a.filename.localeCompare(b.filename));
};
// Returns a list of sources with their related breakpoints:
// [{ source, breakpoints [breakpoint1, ...] }, ...]
//
// This only returns sources for which we have a visible breakpoint.
// This will return either generated or original source based on the currently
// selected source.
export const getBreakpointSources = createSelector(
getBreakpointsList,
getSourcesForBreakpoints,
getSourcesMap,
getSelectedSource,
(breakpoints, sources, selectedSource) => {
return sources
.map(source => ({
source,
breakpoints: getBreakpointsForSource(
source,
selectedSource,
breakpoints
),
}))
.filter(({ breakpoints: bps }) => bps.length > 0);
(breakpoints, sourcesMap, selectedSource) => {
const visibleBreakpoints = breakpoints.filter(
bp =>
!bp.options.hidden &&
(bp.text || bp.originalText || bp.options.condition || bp.disabled)
);
const sortedVisibleBreakpoints = sortSelectedBreakpoints(
visibleBreakpoints,
selectedSource
);
return _getSourcesForBreakpoints(
sortedVisibleBreakpoints,
sourcesMap,
selectedSource
);
}
);

Просмотреть файл

@ -90,15 +90,24 @@ export function getSourceActorBreakableLines(state, id) {
* @param {Object} state
* @param {Array<String>} ids
* List of Source Actor IDs
* @return {AsyncValue<Array<Number>>}
* @param {Boolean} isHTML
* True, if we are fetching the breakable lines for an HTML source.
* For them, we have to aggregate the lines of each source actors.
* Otherwise, we might still have many source actors, but one per thread.
* In this case, we simply return the first source actor to have the lines ready.
* @return {Array<Number>}
* List of all the breakable lines.
*/
export function getBreakableLinesForSourceActors(state, ids) {
export function getBreakableLinesForSourceActors(state, ids, isHTML) {
const allBreakableLines = [];
for (const id of ids) {
const { breakableLines } = getSourceActor(state, id);
if (breakableLines && breakableLines.state == "fulfilled") {
allBreakableLines.push(...breakableLines.value);
if (isHTML) {
allBreakableLines.push(...breakableLines.value);
} else {
return breakableLines.value;
}
}
}
return allBreakableLines;

Просмотреть файл

@ -140,7 +140,7 @@ export function getHasSiblingOfSameName(state, source) {
return getSourcesUrlsInSources(state, source.url).length > 1;
}
// This is only used externaly by tabs selectors
// This is only used externaly by tabs and breakpointSources selectors
export function getSourcesMap(state) {
return state.sources.sources;
}
@ -377,7 +377,7 @@ export function getBreakableLines(state, sourceId) {
// We pull generated file breakable lines directly from the source actors
// so that breakable lines can be added as new source actors on HTML loads.
return getBreakableLinesForSourceActors(state, sourceActorIDs);
return getBreakableLinesForSourceActors(state, sourceActorIDs, source.isHTML);
}
export const getSelectedBreakableLines = createSelector(

Просмотреть файл

@ -113,11 +113,7 @@ describe("sources-tree", () => {
expect(base.name).toBe("webpack://");
expect(base.contents).toHaveLength(1);
const emptyNode = base.contents[0];
expect(emptyNode.name).toBe("");
expect(emptyNode.contents).toHaveLength(1);
const userNode = emptyNode.contents[0];
const userNode = base.contents[0];
expect(userNode.name).toBe("Users");
expect(userNode.contents).toHaveLength(1);

Просмотреть файл

@ -2,8 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
import { URL as URLParser } from "whatwg-url";
const defaultUrl = {
hash: "",
host: "",
@ -57,7 +55,7 @@ export function parse(url) {
let urlObj;
try {
urlObj = new URLParser(url);
urlObj = new URL(url);
} catch (err) {
urlObj = { ...defaultUrl };
// If we're given simply a filename...
@ -89,6 +87,10 @@ export function parse(url) {
urlObj.pathname = url;
}
}
// When provided a special URL like "webpack:///webpack/foo",
// prevents passing the three slashes in the path, and pass only onea.
// This will prevent displaying modules in empty-name sub folders.
urlObj.pathname = urlObj.pathname.replace(/\/+/, "/");
urlObj.path = urlObj.pathname + urlObj.search;
// Cache the result

Просмотреть файл

@ -21,7 +21,6 @@ const mappings = {
"devtools-services": "Services",
"wasmparser/dist/cjs/WasmParser": "devtools/client/shared/vendor/WasmParser",
"wasmparser/dist/cjs/WasmDis": "devtools/client/shared/vendor/WasmDis",
"whatwg-url": "devtools/client/shared/vendor/whatwg-url",
"framework-actions": "devtools/client/framework/actions/index",
"inspector-shared-utils": "devtools/client/inspector/shared/utils",
};

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

1
devtools/client/shared/vendor/moz.build поставляемый
Просмотреть файл

@ -23,7 +23,6 @@ DevToolsModules(
'seamless-immutable.js',
'WasmDis.js',
'WasmParser.js',
'whatwg-url.js',
)
# react dev versions are used if enable-debug-js-modules is set in .mozconfig.

8851
devtools/client/shared/vendor/whatwg-url.js поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -3561,6 +3561,7 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
const char* errorDescriptionID = nullptr;
AutoTArray<nsString, 3> formatStrs;
bool addHostPort = false;
bool isBadStsCertError = false;
nsresult rv = NS_OK;
nsAutoString messageStr;
nsAutoCString cssClass;
@ -3710,6 +3711,7 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
// In the future we should differentiate between an HSTS host and a
// pinned host and display a more informative message to the user.
if (isStsHost || isPinnedHost) {
isBadStsCertError = true;
cssClass.AssignLiteral("badStsCert");
}
@ -3870,19 +3872,21 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
}
}
nsresult delegateErrorCode = aError;
// If the HTTPS-Only Mode upgraded this request and the upgrade might have
// caused this error, we replace the error-page with about:httpsonlyerror
bool isHttpsOnlyError =
nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(aFailedChannel, aError);
if (isHttpsOnlyError) {
if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(aFailedChannel, aError)) {
errorPage.AssignLiteral("httpsonlyerror");
delegateErrorCode = NS_ERROR_HTTPS_ONLY;
} else if (isBadStsCertError) {
delegateErrorCode = NS_ERROR_BAD_HSTS_CERT;
}
if (nsCOMPtr<nsILoadURIDelegate> loadURIDelegate = GetLoadURIDelegate()) {
nsresult code = isHttpsOnlyError ? NS_ERROR_HTTPS_ONLY : aError;
nsCOMPtr<nsIURI> errorPageURI;
rv = loadURIDelegate->HandleLoadError(aURI, code, NS_ERROR_GET_MODULE(code),
getter_AddRefs(errorPageURI));
rv = loadURIDelegate->HandleLoadError(
aURI, delegateErrorCode, NS_ERROR_GET_MODULE(delegateErrorCode),
getter_AddRefs(errorPageURI));
// If the docshell is going away there's no point in showing an error page.
if (NS_FAILED(rv) || mIsBeingDestroyed) {
*aDisplayedErrorPage = false;

Просмотреть файл

@ -15,20 +15,9 @@
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(AbortController)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbortController)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mSignal)
tmp->mReason.setUndefined();
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbortController)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mSignal)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AbortController)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReason)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(AbortController,
(mGlobal, mSignal),
(mReason))
NS_IMPL_CYCLE_COLLECTING_ADDREF(AbortController)
NS_IMPL_CYCLE_COLLECTING_RELEASE(AbortController)

Просмотреть файл

@ -566,38 +566,6 @@ void ChromeUtils::Import(const GlobalObject& aGlobal,
aRetval.set(exports);
}
/* static */
void ChromeUtils::ImportModule(const GlobalObject& aGlobal,
const nsAString& aResourceURI,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv) {
RefPtr<mozJSComponentLoader> moduleloader = mozJSComponentLoader::Get();
MOZ_ASSERT(moduleloader);
NS_ConvertUTF16toUTF8 registryLocation(aResourceURI);
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE(
"ChromeUtils::ImportModule", OTHER, registryLocation);
JSContext* cx = aGlobal.Context();
JS::RootedObject moduleNamespace(cx);
nsresult rv =
moduleloader->ImportModule(cx, registryLocation, &moduleNamespace);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
MOZ_ASSERT(!JS_IsExceptionPending(cx));
if (!JS_WrapObject(cx, &moduleNamespace)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
aRetval.set(moduleNamespace);
}
namespace module_getter {
static const size_t SLOT_ID = 0;
static const size_t SLOT_URI = 1;

Просмотреть файл

@ -196,11 +196,6 @@ class ChromeUtils {
const Optional<JS::Handle<JSObject*>>& aTargetObj,
JS::MutableHandle<JSObject*> aRetval, ErrorResult& aRv);
static void ImportModule(const GlobalObject& aGlobal,
const nsAString& aResourceURI,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv);
static void DefineModuleGetter(const GlobalObject& global,
JS::Handle<JSObject*> target,
const nsAString& id,

Просмотреть файл

@ -147,24 +147,9 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Exception)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Exception)
NS_IMPL_CYCLE_COLLECTION_CLASS(Exception)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Exception)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Exception)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mThrownJSVal)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Exception)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mData)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
tmp->mThrownJSVal.setNull();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(Exception,
(mLocation, mData),
(mThrownJSVal))
Exception::Exception(const nsACString& aMessage, nsresult aResult,
const nsACString& aName, nsIStackFrame* aLocation,

Просмотреть файл

@ -316,7 +316,8 @@ static BrowsingContextOrigin SimilarOrigin(const Element& aTarget,
// NOTE: This returns nullptr if |aDocument| is in another process from the top
// level content document.
static Document* GetTopLevelContentDocumentInThisProcess(Document& aDocument) {
static const Document* GetTopLevelContentDocumentInThisProcess(
const Document& aDocument) {
auto* wc = aDocument.GetTopLevelWindowContext();
return wc ? wc->GetExtantDoc() : nullptr;
}
@ -462,9 +463,9 @@ struct OopIframeMetrics {
nsRect mRemoteDocumentVisibleRect;
};
static Maybe<OopIframeMetrics> GetOopIframeMetrics(Document& aDocument,
Document* aRootDocument) {
Document* rootDoc =
static Maybe<OopIframeMetrics> GetOopIframeMetrics(
const Document& aDocument, const Document* aRootDocument) {
const Document* rootDoc =
nsContentUtils::GetInProcessSubtreeRootDocument(&aDocument);
MOZ_ASSERT(rootDoc);
@ -522,9 +523,10 @@ static Maybe<OopIframeMetrics> GetOopIframeMetrics(Document& aDocument,
}
// https://w3c.github.io/IntersectionObserver/#update-intersection-observations-algo
// (step 2)
void DOMIntersectionObserver::Update(Document* aDocument,
DOMHighResTimeStamp time) {
// step 2.1
IntersectionInput DOMIntersectionObserver::ComputeInput(
const Document& aDocument, const nsINode* aRoot,
const StyleRect<LengthPercentage>* aRootMargin) {
// 1 - Let rootBounds be observer's root intersection rectangle.
// ... but since the intersection rectangle depends on the target, we defer
// the inflation until later.
@ -533,10 +535,11 @@ void DOMIntersectionObserver::Update(Document* aDocument,
// document.
nsRect rootRect;
nsIFrame* rootFrame = nullptr;
nsINode* root = mRoot;
const nsINode* root = aRoot;
const bool isImplicitRoot = !aRoot;
Maybe<nsRect> remoteDocumentVisibleRect;
if (mRoot && mRoot->IsElement()) {
if ((rootFrame = mRoot->AsElement()->GetPrimaryFrame())) {
if (aRoot && aRoot->IsElement()) {
if ((rootFrame = aRoot->AsElement()->GetPrimaryFrame())) {
nsRect rootRectRelativeToRootFrame;
if (nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame)) {
// rootRectRelativeToRootFrame should be the content rect of rootFrame,
@ -552,10 +555,10 @@ void DOMIntersectionObserver::Update(Document* aDocument,
rootFrame, rootRectRelativeToRootFrame, containingBlock);
}
} else {
MOZ_ASSERT(!mRoot || mRoot->IsDocument());
Document* rootDocument =
mRoot ? mRoot->AsDocument()
: GetTopLevelContentDocumentInThisProcess(*aDocument);
MOZ_ASSERT(!aRoot || aRoot->IsDocument());
const Document* rootDocument =
aRoot ? aRoot->AsDocument()
: GetTopLevelContentDocumentInThisProcess(aDocument);
root = rootDocument;
if (rootDocument) {
@ -581,7 +584,7 @@ void DOMIntersectionObserver::Update(Document* aDocument,
}
if (Maybe<OopIframeMetrics> metrics =
GetOopIframeMetrics(*aDocument, rootDocument)) {
GetOopIframeMetrics(aDocument, rootDocument)) {
rootFrame = metrics->mInProcessRootFrame;
if (!rootDocument) {
rootRect = metrics->mInProcessRootRect;
@ -592,101 +595,104 @@ void DOMIntersectionObserver::Update(Document* aDocument,
nsMargin rootMargin; // This root margin is NOT applied in `implicit root`
// case, e.g. in out-of-process iframes.
for (const auto side : mozilla::AllPhysicalSides()) {
nscoord basis = side == eSideTop || side == eSideBottom ? rootRect.Height()
: rootRect.Width();
rootMargin.Side(side) = mRootMargin.Get(side).Resolve(
basis, static_cast<nscoord (*)(float)>(NSToCoordRoundWithClamp));
if (aRootMargin) {
for (const auto side : mozilla::AllPhysicalSides()) {
nscoord basis = side == eSideTop || side == eSideBottom
? rootRect.Height()
: rootRect.Width();
rootMargin.Side(side) = aRootMargin->Get(side).Resolve(
basis, static_cast<nscoord (*)(float)>(NSToCoordRoundWithClamp));
}
}
return {isImplicitRoot, root, rootFrame,
rootRect, rootMargin, remoteDocumentVisibleRect};
}
// https://w3c.github.io/IntersectionObserver/#update-intersection-observations-algo
// (steps 2.1 - 2.5)
IntersectionOutput DOMIntersectionObserver::Intersect(
const IntersectionInput& aInput, Element& aTarget) {
const bool isSimilarOrigin = SimilarOrigin(aTarget, aInput.mRootNode) ==
BrowsingContextOrigin::Similar;
nsIFrame* targetFrame = aTarget.GetPrimaryFrame();
if (!targetFrame || !aInput.mRootFrame) {
return {isSimilarOrigin};
}
// "From the perspective of an IntersectionObserver, the skipped contents
// of an element are never intersecting the intersection root. This is
// true even if both the root and the target elements are in the skipped
// contents."
// https://drafts.csswg.org/css-contain/#cv-notes
if (targetFrame->AncestorHidesContent()) {
return {isSimilarOrigin};
}
// 2.2. If the intersection root is not the implicit root, and target is
// not in the same Document as the intersection root, skip to step 11.
if (!aInput.mIsImplicitRoot &&
aInput.mRootNode->OwnerDoc() != aTarget.OwnerDoc()) {
return {isSimilarOrigin};
}
// 2.3. If the intersection root is an element and target is not a descendant
// of the intersection root in the containing block chain, skip to step 11.
//
// NOTE(emilio): We also do this if target is the implicit root, pending
// clarification in
// https://github.com/w3c/IntersectionObserver/issues/456.
if (aInput.mRootFrame == targetFrame ||
!nsLayoutUtils::IsAncestorFrameCrossDocInProcess(aInput.mRootFrame,
targetFrame)) {
return {isSimilarOrigin};
}
nsRect rootBounds = aInput.mRootRect;
if (isSimilarOrigin) {
rootBounds.Inflate(aInput.mRootMargin);
}
// 2.4. Set targetRect to the DOMRectReadOnly obtained by running the
// getBoundingClientRect() algorithm on target.
nsRect targetRect = targetFrame->GetBoundingClientRect();
// 2.5. Let intersectionRect be the result of running the compute the
// intersection algorithm on target and observers intersection root.
Maybe<nsRect> intersectionRect =
ComputeTheIntersection(targetFrame, aInput.mRootFrame, rootBounds,
aInput.mRemoteDocumentVisibleRect);
return {isSimilarOrigin, rootBounds, targetRect, intersectionRect};
}
// https://w3c.github.io/IntersectionObserver/#update-intersection-observations-algo
// (step 2)
void DOMIntersectionObserver::Update(Document* aDocument,
DOMHighResTimeStamp time) {
auto input = ComputeInput(*aDocument, mRoot, &mRootMargin);
// 2. For each target in observers internal [[ObservationTargets]] slot,
// processed in the same order that observe() was called on each target:
for (Element* target : mObservationTargets) {
nsIFrame* targetFrame = target->GetPrimaryFrame();
BrowsingContextOrigin origin = SimilarOrigin(*target, root);
Maybe<nsRect> intersectionRect;
nsRect targetRect;
nsRect rootBounds;
const bool canComputeIntersection = [&] {
if (!targetFrame || !rootFrame) {
return false;
}
// "From the perspective of an IntersectionObserver, the skipped contents
// of an element are never intersecting the intersection root. This is
// true even if both the root and the target elements are in the skipped
// contents."
// https://drafts.csswg.org/css-contain/#cv-notes
if (targetFrame->AncestorHidesContent()) {
return false;
}
// 2.1. If the intersection root is not the implicit root and target is
// not a descendant of the intersection root in the containing block
// chain, skip further processing for target.
//
// NOTE(emilio): We don't just "skip further processing" because that
// violates the invariant that there's at least one observation for a
// target (though that is also violated by 2.2), but it also causes
// different behavior when `target` is `display: none`, or not, which is
// really really odd, see:
// https://github.com/w3c/IntersectionObserver/issues/457
//
// NOTE(emilio): We also do this if target is the implicit root, pending
// clarification in
// https://github.com/w3c/IntersectionObserver/issues/456.
if (rootFrame == targetFrame ||
!nsLayoutUtils::IsAncestorFrameCrossDocInProcess(rootFrame,
targetFrame)) {
return false;
}
// 2.2. If the intersection root is not the implicit root, and target is
// not in the same Document as the intersection root, skip further
// processing for target.
//
// NOTE(emilio): We don't just "skip further processing", because that
// doesn't match reality and other browsers, see
// https://github.com/w3c/IntersectionObserver/issues/457.
if (mRoot && mRoot->OwnerDoc() != target->OwnerDoc()) {
return false;
}
return true;
}();
if (canComputeIntersection) {
rootBounds = rootRect;
if (origin == BrowsingContextOrigin::Similar) {
rootBounds.Inflate(rootMargin);
}
// 2.3. Let targetRect be a DOMRectReadOnly obtained by running the
// getBoundingClientRect() algorithm on target.
targetRect = targetFrame->GetBoundingClientRect();
// 2.4. Let intersectionRect be the result of running the compute the
// intersection algorithm on target.
intersectionRect = ComputeTheIntersection(
targetFrame, rootFrame, rootBounds, remoteDocumentVisibleRect);
}
// 2.1 - 2.4.
IntersectionOutput output = Intersect(input, *target);
// 2.5. Let targetArea be targetRects area.
int64_t targetArea =
(int64_t)targetRect.Width() * (int64_t)targetRect.Height();
int64_t targetArea = (int64_t)output.mTargetRect.Width() *
(int64_t)output.mTargetRect.Height();
// 2.6. Let intersectionArea be intersectionRects area.
int64_t intersectionArea = !intersectionRect
? 0
: (int64_t)intersectionRect->Width() *
(int64_t)intersectionRect->Height();
int64_t intersectionArea =
!output.mIntersectionRect
? 0
: (int64_t)output.mIntersectionRect->Width() *
(int64_t)output.mIntersectionRect->Height();
// 2.7. Let isIntersecting be true if targetRect and rootBounds intersect or
// are edge-adjacent, even if the intersection has zero area (because
// rootBounds or targetRect have zero area); otherwise, let isIntersecting
// be false.
const bool isIntersecting = intersectionRect.isSome();
const bool isIntersecting = output.Intersects();
// 2.8. If targetArea is non-zero, let intersectionRatio be intersectionArea
// divided by targetArea. Otherwise, let intersectionRatio be 1 if
@ -729,9 +735,9 @@ void DOMIntersectionObserver::Update(Document* aDocument,
// entry's isIntersecting value.
QueueIntersectionObserverEntry(
target, time,
origin == BrowsingContextOrigin::Similar ? Some(rootBounds)
: Nothing(),
targetRect, intersectionRect, thresholdIndex > 0, intersectionRatio);
output.mIsSimilarOrigin ? Some(output.mRootBounds) : Nothing(),
output.mTargetRect, output.mIntersectionRect, thresholdIndex > 0,
intersectionRatio);
}
}
}

Просмотреть файл

@ -80,6 +80,32 @@ class DOMIntersectionObserverEntry final : public nsISupports,
} \
}
// An input suitable to compute intersections with multiple targets.
struct IntersectionInput {
// Whether the root is implicit (null, originally).
const bool mIsImplicitRoot = false;
// The computed root node. For the implicit root, this will be the in-process
// root document we can compute coordinates against (along with the remote
// document visible rect if appropriate).
const nsINode* mRootNode = nullptr;
nsIFrame* mRootFrame = nullptr;
// The rect of mRootFrame in client coordinates.
nsRect mRootRect;
// The root margin computed against the root rect.
nsMargin mRootMargin;
// If this is in an OOP iframe, the visible rect of the OOP frame.
Maybe<nsRect> mRemoteDocumentVisibleRect;
};
struct IntersectionOutput {
const bool mIsSimilarOrigin;
const nsRect mRootBounds;
const nsRect mTargetRect;
const Maybe<nsRect> mIntersectionRect;
bool Intersects() const { return mIntersectionRect.isSome(); }
};
class DOMIntersectionObserver final : public nsISupports,
public nsWrapperCache {
virtual ~DOMIntersectionObserver() { Disconnect(); }
@ -122,6 +148,11 @@ class DOMIntersectionObserver final : public nsISupports,
void TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal);
static IntersectionInput ComputeInput(
const Document& aDocument, const nsINode* aRoot,
const StyleRect<LengthPercentage>* aRootMargin);
static IntersectionOutput Intersect(const IntersectionInput&, Element&);
void Update(Document* aDocument, DOMHighResTimeStamp time);
MOZ_CAN_RUN_SCRIPT void Notify();

Просмотреть файл

@ -36,26 +36,10 @@ DOMRequest::DOMRequest(nsIGlobalObject* aGlobal)
DOMRequest::~DOMRequest() { mozilla::DropJSObjects(this); }
NS_IMPL_CYCLE_COLLECTION_CLASS(DOMRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMRequest,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMRequest,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
tmp->mResult.setUndefined();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest, DOMEventTargetHelper)
// Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
// DOMEventTargetHelper does it for us.
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResult)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_INHERITED_WITH_JS_MEMBERS(DOMRequest,
DOMEventTargetHelper,
(mError, mPromise),
(mResult))
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMRequest)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)

Просмотреть файл

@ -14206,8 +14206,7 @@ void Document::TryCancelDialog() {
// Check if the document is blocked by modal dialog
for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) {
nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
if (HTMLDialogElement* dialog =
HTMLDialogElement::FromNodeOrNull(element)) {
if (auto* dialog = HTMLDialogElement::FromNodeOrNull(element)) {
dialog->QueueCancelDialog();
break;
}
@ -14532,24 +14531,22 @@ static void UpdateViewportScrollbarOverrideForFullscreen(Document* aDoc) {
}
}
static void NotifyFullScreenChangedForMediaElement(Element* aElement,
bool aIsInFullScreen) {
static void NotifyFullScreenChangedForMediaElement(Element& aElement) {
// When a media element enters the fullscreen, we would like to notify that
// to the media controller in order to update its status.
if (!aElement->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video)) {
return;
if (auto* mediaElem = HTMLMediaElement::FromNode(aElement)) {
mediaElem->NotifyFullScreenChanged();
}
HTMLMediaElement* mediaElem = HTMLMediaElement::FromNodeOrNull(aElement);
mediaElem->NotifyFullScreenChanged();
}
static void ClearFullscreenStateOnElement(Element* aElement) {
/* static */
void Document::ClearFullscreenStateOnElement(Element& aElement) {
// Remove styles from existing top element.
EventStateManager::SetFullscreenState(aElement, false);
NotifyFullScreenChangedForMediaElement(aElement, false);
aElement.RemoveStates(NS_EVENT_STATE_FULLSCREEN);
NotifyFullScreenChangedForMediaElement(aElement);
// Reset iframe fullscreen flag.
if (aElement->IsHTMLElement(nsGkAtoms::iframe)) {
static_cast<HTMLIFrameElement*>(aElement)->SetFullscreenFlag(false);
if (auto* iframe = HTMLIFrameElement::FromNode(aElement)) {
iframe->SetFullscreenFlag(false);
}
}
@ -14569,7 +14566,7 @@ void Document::CleanupFullscreenState() {
}
if (element->State().HasState(NS_EVENT_STATE_FULLSCREEN)) {
ClearFullscreenStateOnElement(element);
ClearFullscreenStateOnElement(*element);
return true;
}
return false;
@ -14594,29 +14591,30 @@ void Document::UnsetFullscreenElement() {
});
MOZ_ASSERT(removedElement->State().HasState(NS_EVENT_STATE_FULLSCREEN));
ClearFullscreenStateOnElement(removedElement);
ClearFullscreenStateOnElement(*removedElement);
UpdateViewportScrollbarOverrideForFullscreen(this);
}
void Document::SetFullscreenElement(Element* aElement) {
void Document::SetFullscreenElement(Element& aElement) {
TopLayerPush(aElement);
EventStateManager::SetFullscreenState(aElement, true);
NotifyFullScreenChangedForMediaElement(aElement, true);
aElement.AddStates(NS_EVENT_STATE_FULLSCREEN);
NotifyFullScreenChangedForMediaElement(aElement);
UpdateViewportScrollbarOverrideForFullscreen(this);
}
void Document::TopLayerPush(Element* aElement) {
NS_ASSERTION(aElement, "Must pass non-null to TopLayerPush()");
void Document::TopLayerPush(Element& aElement) {
auto predictFunc = [&aElement](Element* element) {
return element == aElement;
return element == &aElement;
};
TopLayerPop(predictFunc);
mTopLayer.AppendElement(do_GetWeakReference(aElement));
NS_ASSERTION(GetTopLayerTop() == aElement, "Should match");
mTopLayer.AppendElement(do_GetWeakReference(&aElement));
NS_ASSERTION(GetTopLayerTop() == &aElement, "Should match");
}
void Document::SetBlockedByModalDialog(HTMLDialogElement& aDialogElement) {
void Document::AddModalDialog(HTMLDialogElement& aDialogElement) {
TopLayerPush(aDialogElement);
Element* root = GetRootElement();
MOZ_RELEASE_ASSERT(root, "dialog in document without root?");
@ -14626,7 +14624,8 @@ void Document::SetBlockedByModalDialog(HTMLDialogElement& aDialogElement) {
// NS_EVENT_STATE_TOPMOST_MODAL_DIALOG to remove the inertness
// explicitly.
root->AddStates(NS_EVENT_STATE_MOZINERT);
aDialogElement.AddStates(NS_EVENT_STATE_TOPMOST_MODAL_DIALOG);
aDialogElement.AddStates(NS_EVENT_STATE_MODAL_DIALOG |
NS_EVENT_STATE_TOPMOST_MODAL_DIALOG);
// It's possible that there's another modal dialog has opened
// previously which doesn't have the inertness (because we've
@ -14646,8 +14645,16 @@ void Document::SetBlockedByModalDialog(HTMLDialogElement& aDialogElement) {
}
}
void Document::UnsetBlockedByModalDialog(HTMLDialogElement& aDialogElement) {
aDialogElement.RemoveStates(NS_EVENT_STATE_TOPMOST_MODAL_DIALOG);
void Document::RemoveModalDialog(HTMLDialogElement& aDialogElement) {
aDialogElement.RemoveStates(NS_EVENT_STATE_MODAL_DIALOG |
NS_EVENT_STATE_TOPMOST_MODAL_DIALOG);
auto predicate = [&aDialogElement](Element* element) -> bool {
return element == &aDialogElement;
};
DebugOnly<Element*> removedElement = TopLayerPop(predicate);
MOZ_ASSERT(removedElement == &aDialogElement);
// The document could still be blocked by another modal dialog.
// We need to remove the inertness from this modal dialog.
@ -14656,9 +14663,8 @@ void Document::UnsetBlockedByModalDialog(HTMLDialogElement& aDialogElement) {
if (auto* dialog = HTMLDialogElement::FromNodeOrNull(element)) {
if (dialog != &aDialogElement) {
dialog->AddStates(NS_EVENT_STATE_TOPMOST_MODAL_DIALOG);
// Return here because we want to keep the inertness for the
// root element as the document is still blocked by a modal
// dialog
// Return early here because we want to keep the inertness for the root
// element as the document is still blocked by a modal dialog.
return;
}
}
@ -14670,7 +14676,7 @@ void Document::UnsetBlockedByModalDialog(HTMLDialogElement& aDialogElement) {
}
}
Element* Document::TopLayerPop(FunctionRef<bool(Element*)> aPredicateFunc) {
Element* Document::TopLayerPop(FunctionRef<bool(Element*)> aPredicate) {
if (mTopLayer.IsEmpty()) {
return nullptr;
}
@ -14681,7 +14687,7 @@ Element* Document::TopLayerPop(FunctionRef<bool(Element*)> aPredicateFunc) {
Element* removedElement = nullptr;
for (auto i : Reversed(IntegerRange(mTopLayer.Length()))) {
nsCOMPtr<Element> element(do_QueryReferent(mTopLayer[i]));
if (element && aPredicateFunc(element)) {
if (element && aPredicate(element)) {
removedElement = element;
mTopLayer.RemoveElementAt(i);
break;
@ -15182,7 +15188,7 @@ bool Document::ApplyFullscreen(UniquePtr<FullscreenRequest> aRequest) {
// element, and the fullscreen-ancestor styles on ancestors of the element
// in this document.
Element* elem = aRequest->Element();
SetFullscreenElement(elem);
SetFullscreenElement(*elem);
// Set the iframe fullscreen flag.
if (auto* iframe = HTMLIFrameElement::FromNode(elem)) {
iframe->SetFullscreenFlag(true);
@ -15229,7 +15235,7 @@ bool Document::ApplyFullscreen(UniquePtr<FullscreenRequest> aRequest) {
}
Document* parent = child->GetInProcessParentDocument();
parent->SetFullscreenElement(element);
parent->SetFullscreenElement(*element);
changed.AppendElement(parent);
child = parent;
}

Просмотреть файл

@ -1905,32 +1905,33 @@ class Document : public nsINode,
void RequestFullscreenInParentProcess(UniquePtr<FullscreenRequest> aRequest,
bool applyFullScreenDirectly);
static void ClearFullscreenStateOnElement(Element&);
// Pushes aElement onto the top layer
void TopLayerPush(Element&);
// Removes the topmost element for which aPredicate returns true from the top
// layer. The removed element, if any, is returned.
Element* TopLayerPop(FunctionRef<bool(Element*)> aPredicate);
public:
// Removes all the elements with fullscreen flag set from the top layer, and
// clears their fullscreen flag.
void CleanupFullscreenState();
// Pushes aElement onto the top layer
void TopLayerPush(Element* aElement);
// Removes the topmost element which have aPredicate return true from the top
// layer. The removed element, if any, is returned.
Element* TopLayerPop(FunctionRef<bool(Element*)> aPredicateFunc);
// Pops the fullscreen element from the top layer and clears its
// fullscreen flag.
void UnsetFullscreenElement();
// Pushes the given element into the top of top layer and set fullscreen
// flag.
void SetFullscreenElement(Element* aElement);
void SetFullscreenElement(Element&);
// Cancel the dialog element if the document is blocked by the dialog
void TryCancelDialog();
void SetBlockedByModalDialog(HTMLDialogElement&);
void UnsetBlockedByModalDialog(HTMLDialogElement&);
void AddModalDialog(HTMLDialogElement&);
void RemoveModalDialog(HTMLDialogElement&);
/**
* Called when a frame in a child process has entered fullscreen or when a

Просмотреть файл

@ -12,32 +12,10 @@
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(Pose)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Pose)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
tmp->mPosition = nullptr;
tmp->mLinearVelocity = nullptr;
tmp->mLinearAcceleration = nullptr;
tmp->mOrientation = nullptr;
tmp->mAngularVelocity = nullptr;
tmp->mAngularAcceleration = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Pose)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Pose)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPosition)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLinearVelocity)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLinearAcceleration)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOrientation)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAngularVelocity)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAngularAcceleration)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(
Pose, (mParent),
(mPosition, mLinearVelocity, mLinearAcceleration, mOrientation,
mAngularVelocity, mAngularAcceleration))
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Pose, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Pose, Release)

Просмотреть файл

@ -6991,11 +6991,11 @@ void nsContentUtils::FireMutationEventsForDirectParsing(
}
/* static */
Document* nsContentUtils::GetInProcessSubtreeRootDocument(Document* aDoc) {
const Document* nsContentUtils::GetInProcessSubtreeRootDocument(const Document* aDoc) {
if (!aDoc) {
return nullptr;
}
Document* doc = aDoc;
const Document* doc = aDoc;
while (doc->GetInProcessParentDocument()) {
doc = doc->GetInProcessParentDocument();
}
@ -7589,9 +7589,17 @@ nsresult nsContentUtils::IPCTransferableToTransferable(
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
const nsString& text = item.data().get_nsString();
rv = dataWrapper->SetData(text);
NS_ENSURE_SUCCESS(rv, rv);
if (item.data().type() == IPCDataTransferData::TShmem) {
Shmem itemData = item.data().get_Shmem();
const nsDependentSubstring text(itemData.get<char16_t>(),
itemData.Size<char16_t>());
rv = dataWrapper->SetData(text);
NS_ENSURE_SUCCESS(rv, rv);
} else {
const nsString& text = item.data().get_nsString();
rv = dataWrapper->SetData(text);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper);
NS_ENSURE_SUCCESS(rv, rv);
@ -7673,6 +7681,13 @@ nsresult nsContentUtils::IPCTransferableItemToVariant(
});
if (aDataTransferItem.dataType() == TransferableDataType::String) {
if (aDataTransferItem.data().type() == IPCDataTransferData::TShmem) {
Shmem data = aDataTransferItem.data().get_Shmem();
aVariant->SetAsAString(
nsDependentSubstring(data.get<char16_t>(), data.Size<char16_t>()));
return NS_OK;
}
const nsString& data = aDataTransferItem.data().get_nsString();
aVariant->SetAsAString(data);
return NS_OK;
@ -7851,17 +7866,23 @@ bool nsContentUtils::IsFlavorImage(const nsACString& aFlavor) {
aFlavor.EqualsLiteral(kGIFImageMime);
}
static Shmem ConvertToShmem(mozilla::dom::ContentChild* aChild,
mozilla::dom::ContentParent* aParent,
const nsACString& aInput) {
static bool AllocateShmem(mozilla::dom::ContentChild* aChild,
mozilla::dom::ContentParent* aParent, size_t aSize,
mozilla::ipc::Shmem* aShmem) {
MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
MOZ_ASSERT(aShmem);
IShmemAllocator* allocator = aChild ? static_cast<IShmemAllocator*>(aChild)
: static_cast<IShmemAllocator*>(aParent);
return allocator->AllocShmem(aSize, SharedMemory::TYPE_BASIC, aShmem);
}
static Shmem ConvertToShmem(mozilla::dom::ContentChild* aChild,
mozilla::dom::ContentParent* aParent,
const nsACString& aInput) {
Shmem result;
if (!allocator->AllocShmem(aInput.Length(), SharedMemory::TYPE_BASIC,
&result)) {
if (!AllocateShmem(aChild, aParent, aInput.Length(), &result)) {
return result;
}
@ -7870,6 +7891,20 @@ static Shmem ConvertToShmem(mozilla::dom::ContentChild* aChild,
return result;
}
static Shmem ConvertToShmem(mozilla::dom::ContentChild* aChild,
mozilla::dom::ContentParent* aParent,
const nsAString& aInput) {
Shmem result;
uint32_t size = aInput.Length() * sizeof(char16_t);
if (!AllocateShmem(aChild, aParent, size, &result)) {
return result;
}
memcpy(result.get<char>(), aInput.BeginReading(), size);
return result;
}
void nsContentUtils::TransferableToIPCTransferable(
nsITransferable* aTransferable, IPCDataTransfer* aIPCDataTransfer,
bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
@ -7919,9 +7954,31 @@ void nsContentUtils::TransferableToIPCTransferable(
if (nsCOMPtr<nsISupportsString> text = do_QueryInterface(data)) {
nsAutoString dataAsString;
text->GetData(dataAsString);
Maybe<Shmem> dataAsShmem;
uint32_t size = dataAsString.Length() * sizeof(char16_t);
// XXX IPCDataTransfer could contain multiple items, we give each item
// same bucket size. The IPC message includes more than data payload, so
// subtract 10 KB to make the total size within the bucket size. It
// would be nice if we could have a smarter way to decide when to use
// Shmem.
uint32_t threshold =
(IPC::Channel::kMaximumMessageSize / flavorList.Length()) -
(10 * 1024);
if (size > threshold) {
dataAsShmem.emplace(ConvertToShmem(aChild, aParent, dataAsString));
if (!dataAsShmem->IsReadable() || !dataAsShmem->Size<char16_t>()) {
continue;
}
}
IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
item->flavor() = flavorStr;
item->data() = dataAsString;
if (dataAsShmem) {
item->data() = dataAsShmem.value();
} else {
item->data() = dataAsString;
}
item->dataType() = TransferableDataType::String;
continue;
}

Просмотреть файл

@ -2459,7 +2459,11 @@ class nsContentUtils {
* Returns the in-process subtree root document in a document hierarchy.
* This could be a chrome document.
*/
static Document* GetInProcessSubtreeRootDocument(Document* aDoc);
static Document* GetInProcessSubtreeRootDocument(Document* aDoc) {
return const_cast<Document*>(
GetInProcessSubtreeRootDocument(const_cast<const Document*>(aDoc)));
}
static const Document* GetInProcessSubtreeRootDocument(const Document* aDoc);
static void GetShiftText(nsAString& text);
static void GetControlText(nsAString& text);

Просмотреть файл

@ -569,21 +569,12 @@ class IdleRequestExecutor final : public nsIRunnable,
Maybe<int32_t> mDelayedExecutorHandle;
};
NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTION(IdleRequestExecutor, mWindow,
mDelayedExecutorDispatcher)
NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDelayedExecutorDispatcher)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDelayedExecutorDispatcher)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)

Просмотреть файл

@ -495,6 +495,7 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID)
class_, native_members_, js_members_) \
NS_IMPL_CYCLE_COLLECTION_CLASS(class_) \
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(class_) \
using ::ImplCycleCollectionUnlink; \
NS_IMPL_CYCLE_COLLECTION_UNLINK( \
MOZ_FOR_EACH_EXPAND_HELPER native_members_) \
NS_IMPL_CYCLE_COLLECTION_UNLINK(MOZ_FOR_EACH_EXPAND_HELPER js_members_) \

Просмотреть файл

@ -145,9 +145,22 @@ bool OffscreenCanvasDisplayHelper::CommitFrameToCompositor(
} else {
surface = aContext->GetFrontBufferSnapshot(/* requireAlphaPremult */ false);
if (surface) {
auto surfaceImage = MakeRefPtr<layers::SourceSurfaceImage>(surface);
surfaceImage->SetTextureFlags(flags);
image = surfaceImage;
bool usable = true;
if (surface->GetType() == gfx::SurfaceType::WEBGL) {
// Ensure we can map in the surface. If we get a SourceSurfaceWebgl
// surface, then it may not be backed by raw pixels yet. We need to map
// it on the owning thread rather than the ImageBridge thread.
gfx::DataSourceSurface::ScopedMap map(
static_cast<gfx::DataSourceSurface*>(surface.get()),
gfx::DataSourceSurface::READ);
usable = map.IsMapped();
}
if (usable) {
auto surfaceImage = MakeRefPtr<layers::SourceSurfaceImage>(surface);
surfaceImage->SetTextureFlags(flags);
image = surfaceImage;
}
}
}

Просмотреть файл

@ -428,20 +428,6 @@ partial namespace ChromeUtils {
[Throws]
object import(UTF8String aResourceURI, optional object aTargetObj);
/**
* Synchronously loads and evaluates the JS module source located at
* 'aResourceURI'.
*
* @param aResourceURI A resource:// URI string to load the module from.
* @returns the module's namespace object.
*
* The implementation maintains a hash of aResourceURI->global obj.
* Subsequent invocations of import with 'aResourceURI' pointing to
* the same file will not cause the module to be re-evaluated.
*/
[Throws]
object importModule(DOMString aResourceURI);
/**
* Defines a property on the given target which lazily imports a JavaScript
* module when accessed.

Просмотреть файл

@ -97,22 +97,9 @@ EventListenerInfo::EventListenerInfo(
EventListenerInfo::~EventListenerInfo() { DropJSObjects(this); }
NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerInfo)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerInfo)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerInfo)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
tmp->mScriptedListener = nullptr;
tmp->mScriptedListenerGlobal = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(EventListenerInfo)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptedListener)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptedListenerGlobal)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_WITH_JS_MEMBERS(EventListenerInfo, (mListenerManager),
(mScriptedListener,
mScriptedListenerGlobal))
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventListenerInfo)
NS_INTERFACE_MAP_ENTRY(nsIEventListenerInfo)

Просмотреть файл

@ -5540,12 +5540,6 @@ static Element* GetLabelTarget(nsIContent* aPossibleLabel) {
return label->GetLabeledElement();
}
/* static */
void EventStateManager::SetFullscreenState(Element* aElement,
bool aIsFullscreen) {
DoStateChange(aElement, NS_EVENT_STATE_FULLSCREEN, aIsFullscreen);
}
/* static */
inline void EventStateManager::DoStateChange(Element* aElement,
EventStates aState,

Просмотреть файл

@ -286,9 +286,6 @@ class EventStateManager : public nsSupportsWeakReference, public nsIObserver {
static void SetActiveManager(EventStateManager* aNewESM,
nsIContent* aContent);
// Sets the fullscreen event state on aElement to aIsFullscreen.
static void SetFullscreenState(dom::Element* aElement, bool aIsFullscreen);
static bool IsRemoteTarget(nsIContent* target);
static bool IsTopLevelRemoteTarget(nsIContent* aTarget);

Просмотреть файл

@ -13,24 +13,9 @@
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(GamepadTouch)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(GamepadTouch)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
tmp->mPosition = nullptr;
tmp->mSurfaceDimensions = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(GamepadTouch)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(GamepadTouch)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPosition)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mSurfaceDimensions)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(GamepadTouch, (mParent),
(mPosition,
mSurfaceDimensions))
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(GamepadTouch, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(GamepadTouch, Release)

Просмотреть файл

@ -85,27 +85,19 @@ bool HTMLDialogElement::IsInTopLayer() const {
}
void HTMLDialogElement::AddToTopLayerIfNeeded() {
MOZ_ASSERT(IsInComposedDoc());
if (IsInTopLayer()) {
return;
}
Document* doc = OwnerDoc();
doc->TopLayerPush(this);
doc->SetBlockedByModalDialog(*this);
AddStates(NS_EVENT_STATE_MODAL_DIALOG);
OwnerDoc()->AddModalDialog(*this);
}
void HTMLDialogElement::RemoveFromTopLayerIfNeeded() {
if (!IsInTopLayer()) {
return;
}
auto predictFunc = [&](Element* element) { return element == this; };
Document* doc = OwnerDoc();
DebugOnly<Element*> removedElement = doc->TopLayerPop(predictFunc);
MOZ_ASSERT(removedElement == this);
RemoveStates(NS_EVENT_STATE_MODAL_DIALOG);
doc->UnsetBlockedByModalDialog(*this);
OwnerDoc()->RemoveModalDialog(*this);
}
void HTMLDialogElement::StorePreviouslyFocusedElement() {

Просмотреть файл

@ -532,7 +532,8 @@ RemoteDecoderManagerChild::LaunchUtilityProcessIfNeeded() {
managerThread, __func__,
[](ipc::PBackgroundChild::
EnsureUtilityProcessAndCreateBridgePromise::
ResolveOrRejectValue&& aResult) {
ResolveOrRejectValue&& aResult)
-> RefPtr<GenericNonExclusivePromise> {
nsCOMPtr<nsISerialEventTarget> managerThread = GetManagerThread();
if (!managerThread || aResult.IsReject()) {
// The parent process died or we got shutdown

Просмотреть файл

@ -18,22 +18,12 @@
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(MIDIMessageEvent)
NS_IMPL_CYCLE_COLLECTION_INHERITED_WITH_JS_MEMBERS(MIDIMessageEvent, Event, (),
(mData))
NS_IMPL_ADDREF_INHERITED(MIDIMessageEvent, Event)
NS_IMPL_RELEASE_INHERITED(MIDIMessageEvent, Event)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MIDIMessageEvent, Event)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MIDIMessageEvent, Event)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mData)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MIDIMessageEvent, Event)
tmp->mData = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MIDIMessageEvent)
NS_INTERFACE_MAP_END_INHERITING(Event)

Просмотреть файл

@ -12,6 +12,7 @@
#include "mozilla/HoldDropJSObjects.h"
#include "mozilla/dom/PushUtil.h"
#include "nsIGlobalObject.h"
#include "nsWrapperCache.h"
namespace mozilla::dom {
@ -30,19 +31,9 @@ PushSubscriptionOptions::~PushSubscriptionOptions() {
mozilla::DropJSObjects(this);
}
NS_IMPL_CYCLE_COLLECTION_CLASS(PushSubscriptionOptions)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PushSubscriptionOptions)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
tmp->mAppServerKey = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PushSubscriptionOptions)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(PushSubscriptionOptions)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAppServerKey)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(PushSubscriptionOptions,
(mGlobal),
(mAppServerKey))
NS_IMPL_CYCLE_COLLECTING_ADDREF(PushSubscriptionOptions)
NS_IMPL_CYCLE_COLLECTING_RELEASE(PushSubscriptionOptions)

Просмотреть файл

@ -1291,9 +1291,11 @@ static nsresult CheckAllowFileProtocolScriptLoad(nsIChannel* aChannel) {
if (!nsContentUtils::IsJavascriptMIMEType(
NS_ConvertUTF8toUTF16(contentType))) {
Telemetry::Accumulate(Telemetry::SCRIPT_FILE_PROTOCOL_CORRECT_MIME, false);
return NS_ERROR_CONTENT_BLOCKED;
}
Telemetry::Accumulate(Telemetry::SCRIPT_FILE_PROTOCOL_CORRECT_MIME, true);
return NS_OK;
}

Просмотреть файл

@ -40,11 +40,16 @@ support-files =
file_framing_error_pages.sjs
[browser_same_site_cookies_bug1748693.js]
support-files =
file_same_site_cookies_bug1748693.sjs
file_same_site_cookies_bug1748693.sjs
[browser_file_nonscript.js]
support-files =
file_loads_nonscript.html
file_nonscript
file_nonscript.xyz
file_nonscript.html
file_nonscript.txt
file_nonscript.json
file_script.js
[browser_restrict_privileged_about_script.js]
# This test intentionally asserts when in debug builds. Let's rely on opt builds when in CI.
skip-if = debug

Просмотреть файл

@ -13,9 +13,22 @@ add_task(async function test_fileurl_nonscript_load() {
BrowserTestUtils.removeTab(tab);
});
let ran = await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
return content.window.wrappedJSObject.ran;
let counter = await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Cu.exportFunction(Assert.equal.bind(Assert), content.window, {
defineAs: "equal",
});
content.window.postMessage("run", "*");
await new Promise(resolve => {
content.window.addEventListener("message", event => {
if (event.data === "done") {
resolve();
}
});
});
return content.window.wrappedJSObject.counter;
});
is(ran, "error", "Script should not have run");
is(counter, 1, "Only one script should have run");
});

Просмотреть файл

@ -5,7 +5,45 @@
</head>
<body>
<script>
window.ran = 'before';
/* global equal */
const files = ["file_nonscript",
"file_nonscript.xyz",
"file_nonscript.html",
"file_nonscript.txt",
"file_nonscript.json"];
async function run() {
window.counter = 0;
for (let file of files) {
let script = document.createElement("script");
let promise = new Promise((resolve, reject) => {
script.addEventListener("error", resolve, {once: true});
script.addEventListener("load", reject, {once: true});
});
script.src = file;
document.body.append(script);
let event = await promise;
equal(event.type, "error");
equal(window.counter, 0);
}
let script = document.createElement("script");
let promise = new Promise((resolve, reject) => {
script.addEventListener("load", resolve, {once: true});
script.addEventListener("error", reject, {once: true});
});
script.src = "file_script.js";
document.body.append(script);
let event = await promise;
equal(event.type, "load");
equal(window.counter, 1);
window.postMessage("done", "*");
}
window.addEventListener("message", run, {once: true})
</script>
<script src="file_nonscript.xyz" onerror="window.ran = 'error'"></script>
</html>

Просмотреть файл

@ -0,0 +1 @@
window.counter++;

Просмотреть файл

@ -0,0 +1 @@
window.counter++;

Просмотреть файл

@ -0,0 +1 @@
window.counter++;

Просмотреть файл

@ -0,0 +1 @@
window.counter++;

Просмотреть файл

@ -1 +1 @@
window.ran = 'ran';
window.counter++;

Просмотреть файл

@ -0,0 +1 @@
window.counter++;

Просмотреть файл

@ -26,9 +26,6 @@ struct PipeToReadRequest;
class WriteFinishedPromiseHandler;
class ShutdownActionFinishedPromiseHandler;
// TODO: Bug 1756794
using ::ImplCycleCollectionUnlink;
// https://streams.spec.whatwg.org/#readable-stream-pipe-to (Steps 14-15.)
//
// This class implements everything that is required to read all chunks from
@ -649,16 +646,7 @@ struct PipeToReadRequest : public ReadRequest {
virtual ~PipeToReadRequest() = default;
};
NS_IMPL_CYCLE_COLLECTION_CLASS(PipeToReadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PipeToReadRequest, ReadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPipeToPump)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PipeToReadRequest,
ReadRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPipeToPump)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_INHERITED(PipeToReadRequest, ReadRequest, mPipeToPump)
NS_IMPL_ADDREF_INHERITED(PipeToReadRequest, ReadRequest)
NS_IMPL_RELEASE_INHERITED(PipeToReadRequest, ReadRequest)

Просмотреть файл

@ -14,9 +14,6 @@
namespace mozilla::dom {
// TODO: Bug 1756794
using ::ImplCycleCollectionUnlink;
NS_IMPL_CYCLE_COLLECTION_WITH_JS_MEMBERS(TeeState,
(mStream, mReader, mBranch1, mBranch2,
mCancelPromise),

Просмотреть файл

@ -14,5 +14,7 @@ prefs=
[proper-realm-cancel.js]
[proper-realm-pull.js]
[large-pipeto.js]
skip-if = os == "win"
skip-if =
os == "win"
tsan # Causes claim expired errors; see Bug 1770170.
[too-big-array-buffer.js]

Просмотреть файл

@ -124,22 +124,9 @@ JSObject* VRFieldOfView::WrapObject(JSContext* aCx,
return VRFieldOfView_Binding::Wrap(aCx, this, aGivenProto);
}
NS_IMPL_CYCLE_COLLECTION_CLASS(VREyeParameters)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VREyeParameters)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent, mFOV)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
tmp->mOffset = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VREyeParameters)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent, mFOV)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(VREyeParameters)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOffset)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(VREyeParameters,
(mParent, mFOV),
(mOffset))
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VREyeParameters, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VREyeParameters, Release)

Просмотреть файл

@ -9,25 +9,14 @@
#include "mozilla/dom/Pose.h"
#include "mozilla/dom/DOMPointBinding.h"
#include "mozilla/HoldDropJSObjects.h"
#include "nsWrapperCache.h"
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(XRRigidTransform)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XRRigidTransform)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent, mPosition, mOrientation, mInverse)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
tmp->mMatrixArray = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XRRigidTransform)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent, mPosition, mOrientation, mInverse)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(XRRigidTransform)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMatrixArray)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(XRRigidTransform,
(mParent, mPosition,
mOrientation, mInverse),
(mMatrixArray))
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(XRRigidTransform, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(XRRigidTransform, Release)

Просмотреть файл

@ -9,25 +9,13 @@
#include "mozilla/HoldDropJSObjects.h"
#include "mozilla/dom/XRRigidTransform.h"
#include "mozilla/dom/Pose.h"
#include "nsWrapperCache.h"
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(XRView)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XRView)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent, mTransform)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
tmp->mJSProjectionMatrix = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XRView)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent, mTransform)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(XRView)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJSProjectionMatrix)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(XRView,
(mParent, mTransform),
(mJSProjectionMatrix))
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(XRView, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(XRView, Release)

Просмотреть файл

@ -7,24 +7,12 @@
#include "mozilla/dom/AuthenticatorResponse.h"
#include "nsPIDOMWindow.h"
#include "nsWrapperCache.h"
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(AuthenticatorResponse)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AuthenticatorResponse)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
tmp->mClientDataJSONCachedObj = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AuthenticatorResponse)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mClientDataJSONCachedObj)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AuthenticatorResponse)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(
AuthenticatorResponse, (mParent), (mClientDataJSONCachedObj))
NS_IMPL_CYCLE_COLLECTING_ADDREF(AuthenticatorResponse)
NS_IMPL_CYCLE_COLLECTING_RELEASE(AuthenticatorResponse)

Просмотреть файл

@ -242,10 +242,11 @@ bool GLXLibrary::SupportsVideoSync(Display* aDisplay) {
}
static int (*sOldErrorHandler)(Display*, XErrorEvent*);
ScopedXErrorHandler::ErrorEvent sErrorEvent;
static XErrorEvent sErrorEvent = {};
static int GLXErrorHandler(Display* display, XErrorEvent* ev) {
if (!sErrorEvent.mError.error_code) {
sErrorEvent.mError = *ev;
if (!sErrorEvent.error_code) {
sErrorEvent = *ev;
}
return 0;
}
@ -264,21 +265,23 @@ GLXLibrary::WrapperScope::~WrapperScope() {
if (mDisplay) {
FinishX(mDisplay);
}
if (sErrorEvent.mError.error_code) {
if (sErrorEvent.error_code) {
char buffer[100] = {};
if (mDisplay) {
XGetErrorText(mDisplay, sErrorEvent.mError.error_code, buffer,
sizeof(buffer));
XGetErrorText(mDisplay, sErrorEvent.error_code, buffer, sizeof(buffer));
} else {
SprintfLiteral(buffer, "%d", sErrorEvent.mError.error_code);
SprintfLiteral(buffer, "%d", sErrorEvent.error_code);
}
printf_stderr("X ERROR after %s: %s (%i) - Request: %i.%i, Serial: %lu",
mFuncName, buffer, sErrorEvent.mError.error_code,
sErrorEvent.mError.request_code,
sErrorEvent.mError.minor_code, sErrorEvent.mError.serial);
mFuncName, buffer, sErrorEvent.error_code,
sErrorEvent.request_code, sErrorEvent.minor_code,
sErrorEvent.serial);
MOZ_ASSERT_UNREACHABLE("AfterGLXCall sErrorEvent");
}
XSetErrorHandler(sOldErrorHandler);
const auto was = XSetErrorHandler(sOldErrorHandler);
if (was != GLXErrorHandler) {
NS_WARNING("Concurrent XSetErrorHandlers");
}
}
}
@ -338,24 +341,17 @@ already_AddRefed<GLContextGLX> GLContextGLX::CreateGLContext(
const auto CreateWithAttribs =
[&](const std::vector<int>& attribs) -> RefPtr<GLContextGLX> {
OffMainThreadScopedXErrorHandler handler;
auto terminated = attribs;
terminated.push_back(0);
// X Errors can happen even if this context creation returns non-null, and
// we should not try to use such contexts. (Errors may come from the
// distant server, or something)
const auto glxContext = glx.fCreateContextAttribs(
*display, cfg, nullptr, X11True, terminated.data());
if (!glxContext) return nullptr;
const RefPtr<GLContextGLX> ret =
new GLContextGLX(desc, display, drawable, glxContext, deleteDrawable,
isDoubleBuffered, pixmap);
if (handler.SyncAndGetError(*display)) return nullptr;
if (!ret->Init()) return nullptr;
if (handler.SyncAndGetError(*display)) return nullptr;
return ret;
};
@ -439,13 +435,12 @@ GLContextGLX::~GLContextGLX() {
}
// see bug 659842 comment 76
#ifdef DEBUG
bool success =
#endif
mGLX->fMakeCurrent(*mDisplay, X11None, nullptr);
MOZ_ASSERT(success,
"glXMakeCurrent failed to release GL context before we call "
"glXDestroyContext!");
bool success = mGLX->fMakeCurrent(*mDisplay, X11None, nullptr);
if (!success) {
NS_WARNING(
"glXMakeCurrent failed to release GL context before we call "
"glXDestroyContext!");
}
mGLX->fDestroyContext(*mDisplay, mContext);
@ -476,7 +471,9 @@ bool GLContextGLX::MakeCurrentImpl() const {
}
const bool succeeded = mGLX->fMakeCurrent(*mDisplay, mDrawable, mContext);
NS_ASSERTION(succeeded, "Failed to make GL context current!");
if (!succeeded) {
NS_WARNING("Failed to make GL context current!");
}
if (!IsOffscreen() && mGLX->SupportsSwapControl()) {
// Many GLX implementations default to blocking until the next
@ -868,14 +865,12 @@ static already_AddRefed<GLContextGLX> CreateOffscreenPixmapContext(
int depth;
FindVisualAndDepth(*display, visid, &visual, &depth);
OffMainThreadScopedXErrorHandler xErrorHandler;
bool error = false;
gfx::IntSize dummySize(16, 16);
RefPtr<gfxXlibSurface> surface = gfxXlibSurface::Create(
display, DefaultScreenOfDisplay(display->get()), visual, dummySize);
if (surface->CairoStatus() != 0) {
mozilla::Unused << xErrorHandler.SyncAndGetError(*display);
return nullptr;
}
@ -888,8 +883,7 @@ static already_AddRefed<GLContextGLX> CreateOffscreenPixmapContext(
error = true;
}
bool serverError = xErrorHandler.SyncAndGetError(*display);
if (error || serverError) return nullptr;
if (error) return nullptr;
auto fullDesc = GLContextDesc{desc};
fullDesc.isOffscreen = true;

Просмотреть файл

@ -39,43 +39,4 @@ void FinishX(Display* aDisplay) {
XSync(aDisplay, X11False);
}
ScopedXErrorHandler::ErrorEvent* ScopedXErrorHandler::sXErrorPtr;
int ScopedXErrorHandler::ErrorHandler(Display*, XErrorEvent* ev) {
// only record the error if no error was previously recorded.
// this means that in case of multiple errors, it's the first error that we
// report.
if (!sXErrorPtr->mError.error_code) sXErrorPtr->mError = *ev;
return 0;
}
ScopedXErrorHandler::ScopedXErrorHandler(bool aAllowOffMainThread) {
if (!aAllowOffMainThread) {
// Off main thread usage is not safe in general, but OMTC GL layers uses
// this with the main thread blocked, which makes it safe.
NS_WARNING_ASSERTION(
NS_IsMainThread(),
"ScopedXErrorHandler being called off main thread, may cause issues");
}
// let sXErrorPtr point to this object's mXError object, but don't reset this
// mXError object! think of the case of nested ScopedXErrorHandler's.
mOldXErrorPtr = sXErrorPtr;
sXErrorPtr = &mXError;
mOldErrorHandler = XSetErrorHandler(ErrorHandler);
}
ScopedXErrorHandler::~ScopedXErrorHandler() {
sXErrorPtr = mOldXErrorPtr;
XSetErrorHandler(mOldErrorHandler);
}
bool ScopedXErrorHandler::SyncAndGetError(Display* dpy, XErrorEvent* ev) {
FinishX(dpy);
bool retval = mXError.mError.error_code != 0;
if (ev) *ev = mXError.mError;
mXError = ErrorEvent(); // reset
return retval;
}
} // namespace mozilla

Просмотреть файл

@ -70,75 +70,6 @@ struct ScopedXFreePtrTraits {
};
SCOPED_TEMPLATE(ScopedXFree, ScopedXFreePtrTraits)
/**
* On construction, set a graceful X error handler that doesn't crash the
* application and records X errors. On destruction, restore the X error handler
* to what it was before construction.
*
* The SyncAndGetError() method allows to know whether a X error occurred,
* optionally allows to get the full XErrorEvent, and resets the recorded X
* error state so that a single X error will be reported only once.
*
* Nesting is correctly handled: multiple nested ScopedXErrorHandler's don't
* interfere with each other's state. However, if SyncAndGetError is not called
* on the nested ScopedXErrorHandler, then any X errors caused by X calls made
* while the nested ScopedXErrorHandler was in place may then be caught by the
* other ScopedXErrorHandler. This is just a result of X being asynchronous and
* us not doing any implicit syncing: the only method in this class what causes
* syncing is SyncAndGetError().
*
* This class is not thread-safe at all. It is assumed that only one thread is
* using any ScopedXErrorHandler's. Given that it's not used on Mac, it should
* be easy to make it thread-safe by using thread-local storage with __thread.
*/
class ScopedXErrorHandler {
public:
// trivial wrapper around XErrorEvent, just adding ctor initializing by zero.
struct ErrorEvent {
XErrorEvent mError;
ErrorEvent() { memset(this, 0, sizeof(ErrorEvent)); }
};
private:
// this ScopedXErrorHandler's ErrorEvent object
ErrorEvent mXError;
// static pointer for use by the error handler
static ErrorEvent* sXErrorPtr;
// what to restore sXErrorPtr to on destruction
ErrorEvent* mOldXErrorPtr;
// what to restore the error handler to on destruction
int (*mOldErrorHandler)(Display*, XErrorEvent*);
public:
static int ErrorHandler(Display*, XErrorEvent* ev);
/**
* @param aAllowOffMainThread whether to warn if used off main thread
*/
explicit ScopedXErrorHandler(bool aAllowOffMainThread = false);
~ScopedXErrorHandler();
/** \returns true if a X error occurred since the last time this method was
* called on this ScopedXErrorHandler object, or since the creation of this
* ScopedXErrorHandler object if this method was never called on it.
*
* \param ev this optional parameter, if set, will be filled with the
* XErrorEvent object. If multiple errors occurred, the first one will be
* returned.
*/
bool SyncAndGetError(Display* dpy, XErrorEvent* ev = nullptr);
};
class OffMainThreadScopedXErrorHandler : public ScopedXErrorHandler {
public:
OffMainThreadScopedXErrorHandler() : ScopedXErrorHandler(true) {}
};
} // namespace mozilla
#endif // mozilla_X11Util_h

Просмотреть файл

@ -2,10 +2,17 @@ header = """/* This Source Code Form is subject to the terms of the Mozilla Publ
* 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/. */"""
autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
* To generate this file:
* 1. Get the latest cbindgen using `cargo install --force cbindgen`
* a. Alternatively, you can clone `https://github.com/eqrion/cbindgen` and use a tagged release
* 2. Run `rustup run nightly cbindgen toolkit/library/rust/ --lockfile Cargo.lock --crate wgpu_bindings -o dom/webgpu/ffi/wgpu_ffi_generated.h`
*
* This file is generated based on the configuration in
* `gfx/wgpu_bindings/moz.build`, which directs the build system module
* `build/RunCbindgen.py` to run the following command at the top of
* the object file directory:
*
* $CBINDGEN $TOPSRCDIR --lockfile $TOPSRCDIR/Cargo.lock --crate wgpu_bindings --metadata config/cbindgen-metadata.json --cpp-compat > gfx/wgpu_bindings/wgpu_ffi_generated.h
*
* where:
* - $TOPSRCDIR is the top of the Firefox source tree, and
* - $CBINDGEN is the path to the cbindgen executable provided by mozbuild (the exact version often matters)
*/
struct WGPUByteBuf;

Просмотреть файл

@ -2435,7 +2435,7 @@ impl BatchBuilder {
let mut gpu_blocks = Vec::<GpuBlockData>::with_capacity(3 + max_tiles_per_header * 2);
for chunk in image_instance.visible_tiles.chunks(max_tiles_per_header) {
gpu_blocks.clear();
gpu_blocks.push(PremultipliedColorF::WHITE.into()); //color
gpu_blocks.push(image_data.color.premultiplied().into()); //color
gpu_blocks.push(PremultipliedColorF::WHITE.into()); //bg color
gpu_blocks.push([-1.0, 0.0, 0.0, 0.0].into()); //stretch size
// negative first value makes the shader code ignore it and use the local size instead

Просмотреть файл

@ -72,7 +72,8 @@ void SetThisProcessName(const char* aProcessName) {
displayNameKey = reinterpret_cast<CFStringRef>(*(CFStringRef*)displayNameKeyAddr);
}
// Rename will fail without this
// We need this to ensure we have a connection to the Process Manager, not
// doing so will silently fail and process name wont be updated.
ProcessSerialNumber psn;
if (::GetCurrentProcess(&psn) != noErr) {
return;

Просмотреть файл

@ -102,7 +102,6 @@ bool UtilityProcessChild::Init(base::ProcessId aParentPid,
mSandbox = (SandboxingKind)aSandboxingKind;
mozilla::ipc::SetThisProcessName("Utility Process");
profiler_set_process_name(nsCString("Utility Process"));
// Notify the parent process that we have finished our init and that it can
@ -121,6 +120,10 @@ void CGSShutdownServerConnections();
mozilla::ipc::IPCResult UtilityProcessChild::RecvInit(
const Maybe<FileDescriptor>& aBrokerFd,
const bool& aCanRecordReleaseTelemetry) {
// Do this now (before closing WindowServer on macOS) to avoid risking
// blocking in GetCurrentProcess() called on that platform
mozilla::ipc::SetThisProcessName("Utility Process");
#if defined(MOZ_SANDBOX)
# if defined(XP_MACOSX)
// Close all current connections to the WindowServer. This ensures that the

Просмотреть файл

@ -467,8 +467,6 @@ MSG_DEF(JSMSG_WASM_BAD_BUF_ARG, 0, JSEXN_TYPEERR, "first argument mus
MSG_DEF(JSMSG_WASM_BAD_MOD_ARG, 0, JSEXN_TYPEERR, "first argument must be a WebAssembly.Module")
MSG_DEF(JSMSG_WASM_BAD_BUF_MOD_ARG, 0, JSEXN_TYPEERR, "first argument must be a WebAssembly.Module, ArrayBuffer or typed array object")
MSG_DEF(JSMSG_WASM_BAD_DESC_ARG, 1, JSEXN_TYPEERR, "first argument must be a {0} descriptor")
MSG_DEF(JSMSG_WASM_BAD_ELEMENT, 0, JSEXN_TYPEERR, "\"element\" property of table descriptor must be \"funcref\"")
MSG_DEF(JSMSG_WASM_BAD_ELEMENT_GENERALIZED, 0, JSEXN_TYPEERR, "\"element\" property of table descriptor must be \"funcref\" or \"externref\"")
MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG, 0, JSEXN_TYPEERR, "second argument must be an object")
MSG_DEF(JSMSG_WASM_BAD_IMPORT_FIELD, 1, JSEXN_TYPEERR, "import object field '{0}' is not an Object")
MSG_DEF(JSMSG_WASM_BAD_REF_NONNULLABLE_VALUE, 0, JSEXN_TYPEERR, "cannot pass null to non-nullable WebAssembly reference")

Просмотреть файл

@ -94,19 +94,29 @@ assertErrorMessage(() => ins.exports.newfn(3),
// RULE: WebAssembly.Global of type v128 is constructable from JS with a default
// value.
var gi = new WebAssembly.Global({value: "v128"});
var gm = new WebAssembly.Global({value: "v128", mutable:true});
// RULE: WebAssembly.Global constructor for type v128 does not accept any value
// but throws TypeError.
// RULE: WebAssembly.Global constructor for type v128 is not constructable with
// or without a default value.
assertErrorMessage(() => new WebAssembly.Global({value: "v128"}, 37),
TypeError,
/cannot pass.*v128.*to or from JS/);
assertErrorMessage(() => new WebAssembly.Global({value: "v128"}),
TypeError,
/cannot pass.*v128.*to or from JS/);
assertErrorMessage(() => new WebAssembly.Global({value: "v128", mutable: true}),
TypeError,
/cannot pass.*v128.*to or from JS/);
// RULE: WebAssembly.Global of type v128 have getters and setters that throw
// TypeError when called from JS.
let {gi, gm} = wasmEvalText(`
(module
(global (export "gi") v128 v128.const i64x2 0 0)
(global (export "gm") (mut v128) v128.const i64x2 0 0)
)`).exports;
assertErrorMessage(() => gi.value,
TypeError,
/cannot pass.*v128.*to or from JS/);

Просмотреть файл

@ -88,6 +88,7 @@ namespace jit {
ABIFUNCTION_JS_CODEGEN_ARM_LIST(_) \
ABIFUNCTION_WASM_CODEGEN_DEBUG_LIST(_) \
_(js::ArgumentsObject::finishForIonPure) \
_(js::ArgumentsObject::finishInlineForIonPure) \
_(js::ArrayShiftMoveElements) \
_(js::ecmaAtan2) \
_(js::ecmaHypot) \

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше