Update On Thu May 26 20:31:23 CEST 2022

This commit is contained in:
github-action[bot] 2022-05-26 20:31:24 +02:00
parent 9d52f5e324
commit f0bf5d908a
412 changed files with 18315 additions and 13152 deletions

View file

@ -712,7 +712,7 @@ var gHistorySwipeAnimation = {
}
if (box != null) {
this._isStoppingAnimation = true;
box.style.transition = "opacity 0.2s cubic-bezier(.07,.95,0,1)";
box.style.transition = "opacity 0.35s cubic-bezier(.25,.1,0.25,1)";
box.addEventListener("transitionend", this, true);
box.style.opacity = 0;
} else {

View file

@ -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">

View file

@ -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">

View file

@ -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">

View file

@ -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">

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -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>

View file

@ -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();
}

View file

@ -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"
);
}
);
});

View file

@ -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)

View file

@ -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,
});
});

View file

@ -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;
}

View file

@ -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">

View file

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

View file

@ -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;
}

View file

@ -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]

View file

@ -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`
);
}
);
});

View file

@ -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`);
}
}
}

View file

@ -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;
},
/**

View file

@ -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]

View file

@ -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 => {

View file

@ -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);

View file

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

View file

@ -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);

View file

@ -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 },
},

View file

@ -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",

View file

@ -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;

View file

@ -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",

View file

@ -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 = {

View file

@ -123,7 +123,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "048f60a16451587ac53994ab382d0e236575b4e7"
"revision": "2276685799fc17196d0a79019e6c1e3bea3d122e"
},
"bg": {
"pin": false,
@ -339,7 +339,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "1bc8385c268cf7e39b6b13bd7c2b20d192fb0847"
"revision": "18a3618fb08f9f3d0e6f5e3b4204193f154d6c98"
},
"da": {
"pin": false,
@ -357,7 +357,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "345f0d117ec12e01e798088f7eab03d63dbcae1c"
"revision": "14dc6a9616e6dbc5ba0ea8b49894125011e41d0c"
},
"de": {
"pin": false,
@ -429,7 +429,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "a219ed903a978412acafa2ea85a6435bcf01c905"
"revision": "17c8bcafcbbcc13a297d69608d5ab681b48d4be5"
},
"en-GB": {
"pin": false,
@ -447,7 +447,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "015dd831a6357e93770bb2955f0af4906d99bbfb"
"revision": "92289c24f1f63320b075a1e759ca871388076c28"
},
"eo": {
"pin": false,
@ -483,7 +483,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "5008da5104ab9c3a668b1afd1a6ae7901d8b905f"
"revision": "e843a7c6eba61c7bdbee9887ff3bd48c7a9cb08b"
},
"es-CL": {
"pin": false,
@ -501,7 +501,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "0f970e57544997fe43283f0e4d24e51b6373de82"
"revision": "08eb0dd5fc9f9cec9826c4cac02b9bfd2d83c6bf"
},
"es-ES": {
"pin": false,
@ -573,7 +573,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "d29cdded7da104c34ef6b3a818f88026ac91d8d7"
"revision": "89eb1d19cbc2092da7c92305075e65b32cd4512d"
},
"fa": {
"pin": false,
@ -627,7 +627,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "6d48503c482c327c83e06e0f87fc73af2cb0e293"
"revision": "a4cac829965a4562b98df47ac5a5f3fe31a35fbf"
},
"fr": {
"pin": false,
@ -645,7 +645,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "98833773fe60b5281869128f22fdbe726eba9cc7"
"revision": "906fdc5ffdd0eba897ab0796422a61e2d325640a"
},
"fy-NL": {
"pin": false,
@ -663,7 +663,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "ac57290cc39cc4ae1c4c1a7559fb7c64c5b0d03b"
"revision": "5b22201eded974b72789b1974623023c84286948"
},
"ga-IE": {
"pin": false,
@ -951,7 +951,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "e3ccb400d668c2d011038e74337866aac8f72f2d"
"revision": "ed918e4ee4ab3b3f6f421c816eb4fcf32f76330f"
},
"ja": {
"pin": false,
@ -1083,7 +1083,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "14bf6e2159af25720755ab4f5d0371a830fc83e8"
"revision": "938cd55ca866be95176186e1d852dc5bf6779c1d"
},
"lij": {
"pin": false,
@ -1317,7 +1317,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "04991bd26e5d6f4a7f97d00a35dcce153ad5a485"
"revision": "52db668d98e87cff8c22b4448a50230aa05029b1"
},
"nn-NO": {
"pin": false,
@ -1353,7 +1353,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "65e529545e4250ea0e02f3b51a8f57bcae311091"
"revision": "cb7af2807118217481bd87837d23b972378cb0c7"
},
"pa-IN": {
"pin": false,
@ -1443,7 +1443,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "f8ebda3f7a4c0aa71b312e4b28f14c8ca08c5fca"
"revision": "12e718da5d7b059ba9dc4a18f56990a5adfc8658"
},
"ro": {
"pin": false,
@ -1479,7 +1479,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "3b48c63c96978f4890b5a3dd20e330da90af7ea5"
"revision": "233a1c00ee183d01ca8592ddd3df434159ba1b8b"
},
"sat": {
"pin": false,

View file

@ -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",
},
];

View file

@ -11,7 +11,6 @@
"win64-no-symlink.patch",
"D116995.diff",
"revert-llvmorg-14-init-14141-gd6d3000a2f6d.patch",
"revert-llvmorg-14-init-11890-gf86deb18cab6.patch",
"llvmorg-15-init-283-g4db89e23190d.patch"
"revert-llvmorg-14-init-11890-gf86deb18cab6.patch"
]
}

View file

@ -1,27 +0,0 @@
From 4db89e23190d1d1590d88df08056d327e651c94c Mon Sep 17 00:00:00 2001
From: Shoaib Meenai <smeenai@fb.com>
Date: Thu, 3 Feb 2022 13:39:54 -0800
Subject: [PATCH] [cmake] Increase -fms-compatibility-version in Windows
toolchain file
Make it match LLVM's new minimum requirement (after https://reviews.llvm.org/D114639).
---
llvm/cmake/platforms/WinMsvc.cmake | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/cmake/platforms/WinMsvc.cmake b/llvm/cmake/platforms/WinMsvc.cmake
index d30701a31858..ebb4da419e46 100644
--- a/llvm/cmake/platforms/WinMsvc.cmake
+++ b/llvm/cmake/platforms/WinMsvc.cmake
@@ -259,7 +259,7 @@ set(CROSS_TOOLCHAIN_FLAGS_NATIVE "${_CTF_NATIVE_DEFAULT}" CACHE STRING "")
set(COMPILE_FLAGS
-D_CRT_SECURE_NO_WARNINGS
--target=${TRIPLE_ARCH}-windows-msvc
- -fms-compatibility-version=19.14
+ -fms-compatibility-version=19.20
-imsvc "${ATLMFC_INCLUDE}"
-imsvc "${MSVC_INCLUDE}"
-imsvc "${WINSDK_INCLUDE}/ucrt"
--
2.35.0.1.g829a698654

View file

@ -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

View file

@ -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
}
}
}

View file

@ -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}`,
};

View file

@ -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",

View file

@ -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
);
}

View file

@ -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}

View file

@ -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
);
}
);

View file

@ -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;

View file

@ -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(

View file

@ -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);

View file

@ -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

View file

@ -580,6 +580,10 @@ netmonitor.toolbar.status3=Status
# in the network table toolbar, above the "method" column.
netmonitor.toolbar.method=Method
# LOCALIZATION NOTE (netmonitor.toolbar.priority): This is the label displayed
# in the network table toolbar, above the "priority" column.
netmonitor.toolbar.priority=Priority
# LOCALIZATION NOTE (netmonitor.toolbar.file): This is the label displayed
# in the network table toolbar, above the "file" column.
netmonitor.toolbar.file=File
@ -1082,6 +1086,10 @@ netmonitor.headers.referrerPolicy=Referrer Policy
# in the network details headers tab identifying the content blocking mode.
netmonitor.headers.contentBlocking=Blocking
# LOCALIZATION NOTE (netmonitor.headers.requestPriority): This is the label displayed
# in the network details headers tab identifying the request priority.
netmonitor.headers.requestPriority=Request Priority
# LOCALIZATION NOTE (netmonitor.summary.editAndResend): This is the label displayed
# on the button in the headers tab that opens a form to edit and resend the currently
# displayed request

View file

@ -18,6 +18,7 @@ const dom = require("devtools/client/shared/vendor/react-dom-factories");
const {
getFormattedIPAndPort,
getFormattedSize,
getRequestPriorityAsText,
} = require("devtools/client/netmonitor/src/utils/format-utils");
const { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
const {
@ -105,6 +106,7 @@ const HEADERS_CONTENT_BLOCKING = L10N.getStr(
const HEADERS_ETP = L10N.getStr(
"netmonitor.trackingResource.enhancedTrackingProtection"
);
const HEADERS_PRIORITY = L10N.getStr("netmonitor.headers.requestPriority");
/**
* Headers panel component
@ -541,6 +543,7 @@ class HeadersPanel extends Component {
statusText,
urlDetails,
referrerPolicy,
priority,
isThirdPartyTrackingResource,
contentSize,
transferredSize,
@ -739,11 +742,16 @@ class HeadersPanel extends Component {
? this.renderSummary(HEADERS_REFERRER, referrerPolicy)
: null;
const summaryPriority = priority
? this.renderSummary(HEADERS_PRIORITY, getRequestPriorityAsText(priority))
: null;
const summaryItems = [
summaryStatus,
summaryVersion,
summarySize,
summaryReferrerPolicy,
summaryPriority,
trackingProtectionStatus,
trackingProtectionDetails,
].filter(summaryItem => summaryItem !== null);

View file

@ -0,0 +1,34 @@
/* 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";
const { Component } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const {
getRequestPriorityAsText,
} = require("devtools/client/netmonitor/src/utils/format-utils");
class RequestListColumnPriority extends Component {
static get propTypes() {
return {
item: PropTypes.object.isRequired,
};
}
shouldComponentUpdate(nextProps) {
return this.props.item.method !== nextProps.item.method;
}
render() {
const { priority } = this.props.item;
return dom.td(
{ className: "requests-list-column" },
getRequestPriorityAsText(priority)
);
}
}
module.exports = RequestListColumnPriority;

View file

@ -37,7 +37,8 @@ const {
RequestListColumnTransferredSize,
RequestListColumnType,
RequestListColumnUrl,
RequestListColumnWaterfall
RequestListColumnWaterfall,
RequestListColumnPriority
*/
loader.lazyGetter(this, "RequestListColumnInitiator", function() {
return createFactory(
@ -124,6 +125,11 @@ loader.lazyGetter(this, "RequestListColumnWaterfall", function() {
require("devtools/client/netmonitor/src/components/request-list/RequestListColumnWaterfall")
);
});
loader.lazyGetter(this, "RequestListColumnPriority", function() {
return createFactory(
require("devtools/client/netmonitor/src/components/request-list/RequestListColumnPriority")
);
});
/**
* Used by shouldComponentUpdate: compare two items, and compare only properties
@ -154,6 +160,7 @@ const UPDATED_REQ_ITEM_PROPS = [
"responseHeaders",
"waitingTime",
"isEventStream",
"priority",
];
const UPDATED_REQ_PROPS = [
@ -210,6 +217,7 @@ const COLUMN_COMPONENTS = [
},
{ column: "transferred", ColumnComponent: RequestListColumnTransferredSize },
{ column: "contentSize", ColumnComponent: RequestListColumnContentSize },
{ column: "priority", ColumnComponent: RequestListColumnPriority },
{
column: "startTime",
ColumnComponent: RequestListColumnTime,

View file

@ -10,6 +10,7 @@ DevToolsModules(
"RequestListColumnFile.js",
"RequestListColumnInitiator.js",
"RequestListColumnMethod.js",
"RequestListColumnPriority.js",
"RequestListColumnProtocol.js",
"RequestListColumnRemoteIP.js",
"RequestListColumnResponseHeader.js",

View file

@ -227,6 +227,7 @@ const UPDATE_PROPS = [
"stacktrace",
"isThirdPartyTrackingResource",
"referrerPolicy",
"priority",
"blockedReason",
"blockingExtension",
"channelId",
@ -327,6 +328,11 @@ const HEADERS = [
filterKey: "size",
canFilter: true,
},
{
name: "priority",
boxName: "priority",
canFilter: true,
},
{
name: "startTime",
boxName: "start-time",

View file

@ -43,6 +43,7 @@ const cols = {
setCookies: false,
transferred: true,
contentSize: true,
priority: false,
startTime: false,
endTime: false,
responseTime: false,

View file

@ -9,6 +9,10 @@ const {
SUPPORTED_HTTP_CODES,
} = require("devtools/client/netmonitor/src/constants");
const {
getRequestPriorityAsText,
} = require("devtools/client/netmonitor/src/utils/format-utils");
/**
* Generates a value for the given filter
* ie. if flag = status-code, will generate "200" from the given request item.
@ -50,6 +54,9 @@ function getAutocompleteValuesForFlag(flag, request) {
case "set-cookie-value":
values = responseCookies.map(c => c.value);
break;
case "priority":
values.push(getRequestPriorityAsText(request.priority));
break;
case "set-cookie-domain":
values = responseCookies.map(c =>
c.hasOwnProperty("domain") ? c.domain : request.urlDetails.host

View file

@ -36,6 +36,7 @@ const {
} = require("devtools/client/netmonitor/src/constants");
const {
getFormattedIPAndPort,
getRequestPriorityAsText,
} = require("devtools/client/netmonitor/src/utils/format-utils");
const { getUnicodeUrl } = require("devtools/client/shared/unicode-url");
const {
@ -218,6 +219,8 @@ function isFlagFilterMatch(item, { type, value, negative }) {
return false;
}
},
priority: () =>
getRequestPriorityAsText(item.priority).toLowerCase() == value,
"set-cookie-domain": () => {
if (responseCookies.length > 0) {
const { host } = item.urlDetails;

View file

@ -20,6 +20,11 @@ const MAX_SECOND = 60 * MAX_MILLISECOND;
const REQUEST_DECIMALS = 2;
// Constants for formatting the priority, derived from nsISupportsPriority.idl
const PRIORITY_HIGH = -10;
const PRIORITY_NORMAL = 0;
const PRIORITY_LOW = 10;
function getSizeWithDecimals(size, decimals = REQUEST_DECIMALS) {
return L10N.numberWithDecimals(size, decimals);
}
@ -97,10 +102,31 @@ function getFormattedIPAndPort(ip, port) {
return ip.match(/:+/) ? `[${ip}]:${port}` : `${ip}:${port}`;
}
/**
* Formats the priority of a request
* Based on unix conventions
* See xpcom/threads/nsISupportsPriority.idl
*
* @param {Number} priority - request priority
*/
function getRequestPriorityAsText(priority) {
if (priority < PRIORITY_HIGH) {
return "Highest";
} else if (priority >= PRIORITY_HIGH && priority < PRIORITY_NORMAL) {
return "High";
} else if (priority === PRIORITY_NORMAL) {
return "Normal";
} else if (priority > PRIORITY_NORMAL && priority <= PRIORITY_LOW) {
return "Low";
}
return "Lowest";
}
module.exports = {
getFormattedIPAndPort,
getFormattedSize,
getFormattedTime,
getSizeWithDecimals,
getTimeWithDecimals,
getRequestPriorityAsText,
};

View file

@ -44,6 +44,11 @@ function waterfall(first, second) {
return result || compareValues(first.id, second.id);
}
function priority(first, second) {
const result = compareValues(first.priority, second.priority);
return result || waterfall(first, second);
}
function status(first, second) {
const result = compareValues(getStatusValue(first), getStatusValue(second));
return result || waterfall(first, second);
@ -309,5 +314,6 @@ const sorters = {
latency,
waterfall,
url,
priority,
};
exports.Sorters = Object.assign(sorters, responseHeaders);

View file

@ -178,6 +178,7 @@ skip-if = true # Bug 1479782
skip-if = verify # Bug 1607678
[browser_net_footer-summary.js]
[browser_net_header-ref-policy.js]
[browser_net_header-request-priority.js]
[browser_net_decode-url.js]
[browser_net_details_copy.js]
[browser_net_decode-params.js]

View file

@ -106,6 +106,7 @@ add_task(async function() {
Status: "200OK",
Version: "HTTP/1.1",
Transferred: "650 B (465 B size)",
"Request Priority": "Highest",
},
null,
"\t"

View file

@ -136,7 +136,7 @@ add_task(async function() {
// Space separated tokens
// The last token where autocomplete is available shall generate the popup
EventUtils.sendString(" p");
EventUtils.sendString(" pro");
testAutocompleteContents(["protocol:"], document);
// The new value of the text box should be previousTokens + latest value selected
@ -180,6 +180,7 @@ add_task(async function() {
"-larger-than:",
"-method:",
"-mime-type:",
"-priority:",
"-protocol:",
"-regexp:",
"-remote-ip:",

View file

@ -0,0 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests if "Request Priority" is displayed in the header panel.
*/
add_task(async function() {
const { monitor } = await initNetMonitor(POST_RAW_URL, {
requestCount: 1,
});
const { document } = monitor.panelWin;
const waitReq = waitForNetworkEvents(monitor, 1);
EventUtils.sendMouseEvent(
{ type: "click" },
document.querySelector(".requests-list-reload-notice-button")
);
await waitReq;
// Wait until the tab panel summary is displayed
const wait = waitUntil(
() => document.querySelectorAll(".tabpanel-summary-label")[0]
);
EventUtils.sendMouseEvent(
{ type: "mousedown" },
document.querySelectorAll(".request-list-item")[0]
);
await wait;
const requestPriorityHeaderExists = Array.from(
document.querySelectorAll(".tabpanel-summary-label")
).some(header => header.textContent === "Request Priority");
is(
requestPriorityHeaderExists,
true,
'"Request Priority" header is displayed in the header panel.'
);
return teardown(monitor);
});

View file

@ -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",
};

File diff suppressed because one or more lines are too long

View file

@ -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.

File diff suppressed because one or more lines are too long

View file

@ -86,6 +86,7 @@ const NetworkEventActor = protocol.ActorClassWithSpec(networkEventSpec, {
this._isThirdPartyTrackingResource =
networkEvent.isThirdPartyTrackingResource;
this._referrerPolicy = networkEvent.referrerPolicy;
this._priority = networkEvent.priority;
this._channelId = networkEvent.channelId;
this._browsingContextID = networkEvent.browsingContextID;
this.innerWindowId = networkEvent.innerWindowId;
@ -139,6 +140,7 @@ const NetworkEventActor = protocol.ActorClassWithSpec(networkEventSpec, {
private: this._private,
isThirdPartyTrackingResource: this._isThirdPartyTrackingResource,
referrerPolicy: this._referrerPolicy,
priority: this._priority,
blockedReason: this._blockedReason,
blockingExtension: this._blockingExtension,
// For websocket requests the serial is used instead of the channel id.

View file

@ -69,6 +69,7 @@ const NetworkEventActor = protocol.ActorClassWithSpec(networkEventSpec, {
private: this._private,
isThirdPartyTrackingResource: this._isThirdPartyTrackingResource,
referrerPolicy: this._referrerPolicy,
priority: this._priority,
blockedReason: this._blockedReason,
blockingExtension: this._blockingExtension,
channelId: this._channelId,
@ -113,6 +114,7 @@ const NetworkEventActor = protocol.ActorClassWithSpec(networkEventSpec, {
this._isThirdPartyTrackingResource =
networkEvent.isThirdPartyTrackingResource;
this._referrerPolicy = networkEvent.referrerPolicy;
this._priority = networkEvent.priority;
this._channelId = networkEvent.channelId;
// Stack trace info isn't sent automatically. The client

View file

@ -186,6 +186,10 @@ exports.createNetworkEvent = function(
? referrerInfo.getReferrerPolicyString()
: "";
if (channel instanceof Ci.nsISupportsPriority) {
event.priority = channel.priority;
}
// Determine the cause and if this is an XHR request.
let causeType = Ci.nsIContentPolicy.TYPE_OTHER;
let causeUri = null;

View file

@ -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;

View file

@ -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)

View file

@ -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;

View file

@ -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,

View file

@ -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,

View file

@ -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);
}
}
}

View file

@ -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();

View file

@ -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)

View file

@ -6127,7 +6127,7 @@ nsresult Document::EditingStateChanged() {
// get editing session, make sure this is a strong reference so the
// window can't get deleted during the rest of this call.
nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
const nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
if (!window) {
return NS_ERROR_FAILURE;
}
@ -6227,8 +6227,7 @@ nsresult Document::EditingStateChanged() {
bool clearFocus = focusedFrame ? !focusedFrame->IsFocusable()
: !focusedContent->IsFocusable();
if (clearFocus) {
nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (fm) {
if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
fm->ClearFocus(window);
// If we need to dispatch blur event, we should put off after
// modifying mEditingState since blur event handler may change
@ -12261,24 +12260,28 @@ void Document::NotifyAbortedLoad() {
}
}
static void FireOrClearDelayedEvents(nsTArray<nsCOMPtr<Document>>& aDocuments,
bool aFireEvents) {
nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (!fm) return;
MOZ_CAN_RUN_SCRIPT static void FireOrClearDelayedEvents(
nsTArray<nsCOMPtr<Document>>&& aDocuments, bool aFireEvents) {
RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
if (MOZ_UNLIKELY(!fm)) {
return;
}
for (uint32_t i = 0; i < aDocuments.Length(); ++i) {
nsTArray<nsCOMPtr<Document>> documents = std::move(aDocuments);
for (uint32_t i = 0; i < documents.Length(); ++i) {
nsCOMPtr<Document> document = std::move(documents[i]);
// NB: Don't bother trying to fire delayed events on documents that were
// closed before this event ran.
if (!aDocuments[i]->EventHandlingSuppressed()) {
fm->FireDelayedEvents(aDocuments[i]);
RefPtr<PresShell> presShell = aDocuments[i]->GetPresShell();
if (!document->EventHandlingSuppressed()) {
fm->FireDelayedEvents(document);
RefPtr<PresShell> presShell = document->GetPresShell();
if (presShell) {
// Only fire events for active documents.
bool fire = aFireEvents && aDocuments[i]->GetInnerWindow() &&
aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow();
bool fire = aFireEvents && document->GetInnerWindow() &&
document->GetInnerWindow()->IsCurrentInnerWindow();
presShell->FireOrClearDelayedEvents(fire);
}
aDocuments[i]->FireOrClearPostMessageEvents(aFireEvents);
document->FireOrClearPostMessageEvents(aFireEvents);
}
}
}
@ -12612,8 +12615,10 @@ class nsDelayedEventDispatcher : public Runnable {
mDocuments(std::move(aDocuments)) {}
virtual ~nsDelayedEventDispatcher() = default;
NS_IMETHOD Run() override {
FireOrClearDelayedEvents(mDocuments, true);
// MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. See
// bug 1535398.
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
FireOrClearDelayedEvents(std::move(mDocuments), true);
return NS_OK;
}
@ -12674,7 +12679,7 @@ void Document::UnsuppressEventHandlingAndFireEvents(bool aFireEvents) {
new nsDelayedEventDispatcher(std::move(documents));
Dispatch(TaskCategory::Other, ded.forget());
} else {
FireOrClearDelayedEvents(documents, false);
FireOrClearDelayedEvents(std::move(documents), false);
}
}
@ -14206,8 +14211,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 +14536,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 +14571,7 @@ void Document::CleanupFullscreenState() {
}
if (element->State().HasState(NS_EVENT_STATE_FULLSCREEN)) {
ClearFullscreenStateOnElement(element);
ClearFullscreenStateOnElement(*element);
return true;
}
return false;
@ -14594,29 +14596,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 +14629,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 +14650,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 +14668,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 +14681,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 +14692,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 +15193,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 +15240,7 @@ bool Document::ApplyFullscreen(UniquePtr<FullscreenRequest> aRequest) {
}
Document* parent = child->GetInProcessParentDocument();
parent->SetFullscreenElement(element);
parent->SetFullscreenElement(*element);
changed.AppendElement(parent);
child = parent;
}
@ -16814,6 +16825,8 @@ Selection* Document::GetSelection(ErrorResult& aRv) {
}
nsresult Document::HasStorageAccessSync(bool& aHasStorageAccess) {
// Step 1: check if cookie permissions are available or denied to this
// document's principal
nsCOMPtr<nsPIDOMWindowInner> inner = this->GetInnerWindow();
if (!inner) {
aHasStorageAccess = false;
@ -16832,6 +16845,8 @@ nsresult Document::HasStorageAccessSync(bool& aHasStorageAccess) {
}
}
// Step 2: Check if the browser settings determine whether or not this
// document has access to its unpartitioned cookies.
bool isThirdPartyDocument = AntiTrackingUtils::IsThirdPartyDocument(this);
Maybe<bool> resultBecauseBrowserSettings =
ContentBlocking::CheckBrowserSettingsDecidesStorageAccessAPI(
@ -16846,6 +16861,8 @@ nsresult Document::HasStorageAccessSync(bool& aHasStorageAccess) {
}
}
// Step 3: Check if the location of this call (embedded, top level, same-site)
// determines if cookies are permitted or not.
Maybe<bool> resultBecauseCallContext =
ContentBlocking::CheckCallingContextDecidesStorageAccessAPI(this, false);
if (resultBecauseCallContext.isSome()) {
@ -16858,6 +16875,8 @@ nsresult Document::HasStorageAccessSync(bool& aHasStorageAccess) {
}
}
// Step 4: Check if the permissions for this document determine if if has
// access or is denied cookies.
Maybe<bool> resultBecausePreviousPermission =
ContentBlocking::CheckExistingPermissionDecidesStorageAccessAPI(this);
if (resultBecausePreviousPermission.isSome()) {
@ -16870,6 +16889,7 @@ nsresult Document::HasStorageAccessSync(bool& aHasStorageAccess) {
}
}
// If you get here, we default to not giving you permission.
aHasStorageAccess = false;
return NS_OK;
}
@ -16925,6 +16945,111 @@ Document::GetContentBlockingEvents() {
});
}
RefPtr<MozPromise<int, bool, true>> Document::RequestStorageAccessAsyncHelper(
nsPIDOMWindowInner* aInnerWindow, BrowsingContext* aBrowsingContext,
nsIPrincipal* aPrincipal, bool aHasUserInteraction,
ContentBlockingNotifier::StorageAccessPermissionGrantedReason aNotifier) {
RefPtr<Document> self(this);
RefPtr<nsPIDOMWindowInner> inner(aInnerWindow);
RefPtr<nsIPrincipal> principal(aPrincipal);
// This is a lambda function that has some variables bound to it. It will be
// called later in CompleteAllowAccessFor inside of AllowAccessFor.
auto performFinalChecks = [inner, self, principal, aHasUserInteraction]() {
// Create the user prompt
RefPtr<ContentBlocking::StorageAccessFinalCheckPromise::Private> p =
new ContentBlocking::StorageAccessFinalCheckPromise::Private(__func__);
RefPtr<StorageAccessPermissionRequest> sapr =
StorageAccessPermissionRequest::Create(
inner, principal,
// Allow
[p] {
Telemetry::AccumulateCategorical(
Telemetry::LABELS_STORAGE_ACCESS_API_UI::Allow);
p->Resolve(ContentBlocking::eAllow, __func__);
},
// Block
[p] {
Telemetry::AccumulateCategorical(
Telemetry::LABELS_STORAGE_ACCESS_API_UI::Deny);
p->Reject(false, __func__);
});
using PromptResult = ContentPermissionRequestBase::PromptResult;
PromptResult pr = sapr->CheckPromptPrefs();
if (pr == PromptResult::Pending) {
// We're about to show a prompt, record the request attempt
Telemetry::AccumulateCategorical(
Telemetry::LABELS_STORAGE_ACCESS_API_UI::Request);
}
// Try to auto-grant the storage access so the user doesn't see the prompt.
self->AutomaticStorageAccessPermissionCanBeGranted(aHasUserInteraction)
->Then(
GetCurrentSerialEventTarget(), __func__,
// If the autogrant check didn't fail, call this function
[p, pr, sapr, inner](
const Document::AutomaticStorageAccessPermissionGrantPromise::
ResolveOrRejectValue& aValue) -> void {
// Make a copy because we can't modified copy-captured lambda
// variables.
PromptResult pr2 = pr;
// If the user didn't already click "allow" and we can autogrant,
// do that!
bool storageAccessCanBeGrantedAutomatically =
aValue.IsResolve() && aValue.ResolveValue();
bool autoGrant = false;
if (pr2 == PromptResult::Pending &&
storageAccessCanBeGrantedAutomatically) {
pr2 = PromptResult::Granted;
autoGrant = true;
Telemetry::AccumulateCategorical(
Telemetry::LABELS_STORAGE_ACCESS_API_UI::
AllowAutomatically);
}
// If we can complete the permission request, do so.
if (pr2 != PromptResult::Pending) {
MOZ_ASSERT_IF(pr2 != PromptResult::Granted,
pr2 == PromptResult::Denied);
if (pr2 == PromptResult::Granted) {
ContentBlocking::StorageAccessPromptChoices choice =
ContentBlocking::eAllow;
if (autoGrant) {
choice = ContentBlocking::eAllowAutoGrant;
}
if (!autoGrant) {
p->Resolve(choice, __func__);
} else {
sapr->MaybeDelayAutomaticGrants()->Then(
GetCurrentSerialEventTarget(), __func__,
[p, choice] { p->Resolve(choice, __func__); },
[p] { p->Reject(false, __func__); });
}
return;
}
p->Reject(false, __func__);
return;
}
// If we get here, the auto-decision failed and we need to
// wait for the user prompt to complete.
sapr->RequestDelayedTask(
inner->EventTargetFor(TaskCategory::Other),
ContentPermissionRequestBase::DelayedTaskType::Request);
});
return p;
};
// Try to allow access for the given principal.
return ContentBlocking::AllowAccessFor(principal, aBrowsingContext, aNotifier,
performFinalChecks);
}
already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccess(
mozilla::ErrorResult& aRv) {
nsIGlobalObject* global = GetScopeObject();
@ -17007,7 +17132,7 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccess(
// Get pointers to some objects that will be used in the async portion
RefPtr<BrowsingContext> bc = this->GetBrowsingContext();
nsCOMPtr<nsPIDOMWindowInner> inner = this->GetInnerWindow();
nsPIDOMWindowInner* inner = this->GetInnerWindow();
if (!inner) {
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
@ -17026,92 +17151,12 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccess(
// This prevents usage of other transient activation-gated APIs.
this->ConsumeTransientUserGestureActivation();
auto performFinalChecks =
[inner,
self]() -> RefPtr<ContentBlocking::StorageAccessFinalCheckPromise> {
RefPtr<ContentBlocking::StorageAccessFinalCheckPromise::Private> p =
new ContentBlocking::StorageAccessFinalCheckPromise::Private(__func__);
RefPtr<StorageAccessPermissionRequest> sapr =
StorageAccessPermissionRequest::Create(
inner,
// Allow
[p] {
Telemetry::AccumulateCategorical(
Telemetry::LABELS_STORAGE_ACCESS_API_UI::Allow);
p->Resolve(ContentBlocking::eAllow, __func__);
},
// Block
[p] {
Telemetry::AccumulateCategorical(
Telemetry::LABELS_STORAGE_ACCESS_API_UI::Deny);
p->Reject(false, __func__);
});
using PromptResult = ContentPermissionRequestBase::PromptResult;
PromptResult pr = sapr->CheckPromptPrefs();
if (pr == PromptResult::Pending) {
// We're about to show a prompt, record the request attempt
Telemetry::AccumulateCategorical(
Telemetry::LABELS_STORAGE_ACCESS_API_UI::Request);
}
self->AutomaticStorageAccessPermissionCanBeGranted(true)->Then(
GetCurrentSerialEventTarget(), __func__,
[p, pr, sapr,
inner](const AutomaticStorageAccessPermissionGrantPromise::
ResolveOrRejectValue& aValue) -> void {
// Make a copy because we can't modified copy-captured lambda
// variables.
PromptResult pr2 = pr;
bool storageAccessCanBeGrantedAutomatically =
aValue.IsResolve() && aValue.ResolveValue();
bool autoGrant = false;
if (pr2 == PromptResult::Pending &&
storageAccessCanBeGrantedAutomatically) {
pr2 = PromptResult::Granted;
autoGrant = true;
Telemetry::AccumulateCategorical(
Telemetry::LABELS_STORAGE_ACCESS_API_UI::AllowAutomatically);
}
if (pr2 != PromptResult::Pending) {
MOZ_ASSERT_IF(pr2 != PromptResult::Granted,
pr2 == PromptResult::Denied);
if (pr2 == PromptResult::Granted) {
ContentBlocking::StorageAccessPromptChoices choice =
ContentBlocking::eAllow;
if (autoGrant) {
choice = ContentBlocking::eAllowAutoGrant;
}
if (!autoGrant) {
p->Resolve(choice, __func__);
} else {
sapr->MaybeDelayAutomaticGrants()->Then(
GetCurrentSerialEventTarget(), __func__,
[p, choice] { p->Resolve(choice, __func__); },
[p] { p->Reject(false, __func__); });
}
return;
}
p->Reject(false, __func__);
return;
}
sapr->RequestDelayedTask(
inner->EventTargetFor(TaskCategory::Other),
ContentPermissionRequestBase::DelayedTaskType::Request);
});
return p;
};
ContentBlocking::AllowAccessFor(NodePrincipal(), bc,
ContentBlockingNotifier::eStorageAccessAPI,
performFinalChecks)
// Step 5. Start an async call to request storage access. This will either
// perform an automatic decision or notify the user, then perform some follow
// on work changing state to reflect the result of the API. If it resolves,
// the request was granted. If it rejects it was denied.
RequestStorageAccessAsyncHelper(inner, bc, NodePrincipal(), true,
ContentBlockingNotifier::eStorageAccessAPI)
->Then(
GetCurrentSerialEventTarget(), __func__,
[self, outer, promise] {
@ -17139,233 +17184,146 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccessForOrigin(
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
// Window doesn't have user activation, reject.
bool hasUserActivation = this->HasValidTransientUserGestureActivation();
if (aRequireUserActivation && !hasUserActivation) {
nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
nsLiteralCString("requestStorageAccess"),
this, nsContentUtils::eDOM_PROPERTIES,
"RequestStorageAccessUserGesture");
promise->MaybeRejectWithUndefined();
return promise.forget();
}
nsCOMPtr<nsPIDOMWindowInner> inner = GetInnerWindow();
if (!inner) {
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// We only allow request storage access for third-party origin from the
// first-party context.
if (AntiTrackingUtils::IsThirdPartyWindow(inner, nullptr)) {
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// If the document has a null origin, reject.
if (NodePrincipal()->GetIsNullPrincipal()) {
nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
nsLiteralCString("requestStorageAccess"),
this, nsContentUtils::eDOM_PROPERTIES,
"RequestStorageAccessNullPrincipal");
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// Step 1: Check if the provided URI is different-site to this Document
nsCOMPtr<nsIURI> thirdPartyURI;
nsresult rv = NS_NewURI(getter_AddRefs(thirdPartyURI), aThirdPartyOrigin);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return nullptr;
}
// If the browser forbids any storage access, reject.
if (CookieJarSettings()->GetBlockingAllContexts()) {
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
return promise.forget();
bool isThirdPartyDocument;
rv = NodePrincipal()->IsThirdPartyURI(thirdPartyURI, &isThirdPartyDocument);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return nullptr;
}
// Only enforce third-party checks when there is a reason to enforce them.
if (!CookieJarSettings()->GetRejectThirdPartyContexts()) {
// If the the thrid party origin is equal to the window's, resolve.
if (NodePrincipal()->IsSameOrigin(thirdPartyURI)) {
Maybe<bool> resultBecauseBrowserSettings =
ContentBlocking::CheckBrowserSettingsDecidesStorageAccessAPI(
CookieJarSettings(), isThirdPartyDocument);
if (resultBecauseBrowserSettings.isSome()) {
if (resultBecauseBrowserSettings.value()) {
promise->MaybeResolveWithUndefined();
return promise.forget();
}
}
// Check any additional rules that the browser has.
if (CookieJarSettings()->GetBlockingAllThirdPartyContexts()) {
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
return promise.forget();
}
if (CookieJarSettings()->GetRejectThirdPartyContexts()) {
RefPtr<BrowsingContext> bc = GetBrowsingContext();
if (!bc) {
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
// Step 2: Check that this Document is same-site to the top, and check that
// we have user activation if we require it.
Maybe<bool> resultBecauseCallContext =
ContentBlocking::CheckSameSiteCallingContextDecidesStorageAccessAPI(
this, aRequireUserActivation);
if (resultBecauseCallContext.isSome()) {
if (resultBecauseCallContext.value()) {
promise->MaybeResolveWithUndefined();
return promise.forget();
}
nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
thirdPartyURI, NodePrincipal()->OriginAttributesRef());
if (!principal) {
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
return promise.forget();
}
RefPtr<Document> self(this);
// Consume user activation before entering the async part of this method.
// This prevents usage of other transient activation-gated APIs.
this->ConsumeTransientUserGestureActivation();
auto performFinalChecks = [inner, self, principal, hasUserActivation]() {
RefPtr<ContentBlocking::StorageAccessFinalCheckPromise::Private> p =
new ContentBlocking::StorageAccessFinalCheckPromise::Private(
__func__);
RefPtr<StorageAccessPermissionRequest> sapr =
StorageAccessPermissionRequest::Create(
inner, principal,
// Allow
[p] {
Telemetry::AccumulateCategorical(
Telemetry::LABELS_STORAGE_ACCESS_API_UI::Allow);
p->Resolve(ContentBlocking::eAllow, __func__);
},
// Block
[p] {
Telemetry::AccumulateCategorical(
Telemetry::LABELS_STORAGE_ACCESS_API_UI::Deny);
p->Reject(false, __func__);
});
using PromptResult = ContentPermissionRequestBase::PromptResult;
PromptResult pr = sapr->CheckPromptPrefs();
if (pr == PromptResult::Pending) {
// We're about to show a prompt, record the request attempt
Telemetry::AccumulateCategorical(
Telemetry::LABELS_STORAGE_ACCESS_API_UI::Request);
}
self->AutomaticStorageAccessPermissionCanBeGranted(hasUserActivation)
->Then(GetCurrentSerialEventTarget(), __func__,
[p, pr, sapr,
inner](const AutomaticStorageAccessPermissionGrantPromise::
ResolveOrRejectValue& aValue) -> void {
// Make a copy because we can't modified copy-captured lambda
// variables.
PromptResult pr2 = pr;
bool storageAccessCanBeGrantedAutomatically =
aValue.IsResolve() && aValue.ResolveValue();
bool autoGrant = false;
if (pr2 == PromptResult::Pending &&
storageAccessCanBeGrantedAutomatically) {
pr2 = PromptResult::Granted;
autoGrant = true;
Telemetry::AccumulateCategorical(
Telemetry::LABELS_STORAGE_ACCESS_API_UI::
AllowAutomatically);
}
if (pr2 != PromptResult::Pending) {
MOZ_ASSERT_IF(pr2 != PromptResult::Granted,
pr2 == PromptResult::Denied);
if (pr2 == PromptResult::Granted) {
ContentBlocking::StorageAccessPromptChoices choice =
ContentBlocking::eAllow;
if (autoGrant) {
choice = ContentBlocking::eAllowAutoGrant;
}
if (!autoGrant) {
p->Resolve(choice, __func__);
} else {
sapr->MaybeDelayAutomaticGrants()->Then(
GetCurrentSerialEventTarget(), __func__,
[p, choice] { p->Resolve(choice, __func__); },
[p] { p->Reject(false, __func__); });
}
return;
}
p->Reject(false, __func__);
return;
}
sapr->RequestDelayedTask(
inner->EventTargetFor(TaskCategory::Other),
ContentPermissionRequestBase::DelayedTaskType::Request);
});
return p;
};
// Only do something special for the third party that storage has been
// disabled by anti-tracking feature.
AsyncStorageDisabledByAntiTracking(bc, principal)
->Then(
GetCurrentSerialEventTarget(), __func__,
[performFinalChecks, promise, bc, principal,
self](AsyncStorageDisabledByAntiTrackingPromise::
ResolveOrRejectValue&& aValue) {
if (aValue.IsReject()) {
// Storage was enabled by anti-tracking feature.
return ContentBlocking::StorageAccessPermissionGrantPromise::
CreateAndResolve(0, __func__);
}
MOZ_ASSERT(aValue.IsResolve());
// Storage was disabled by anti-tracking feature.
// If the storage was disabled by the cookie permission, we don't
// bother to show the prompt.
uint32_t rejectReason = aValue.ResolveValue();
if (rejectReason ==
nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION) {
return ContentBlocking::StorageAccessPermissionGrantPromise::
CreateAndReject(true, __func__);
;
}
MOZ_ASSERT(!self->CookieJarSettings()
->GetIsOnContentBlockingAllowList());
return ContentBlocking::AllowAccessFor(
principal, bc,
ContentBlockingNotifier::ePrivilegeStorageAccessForOriginAPI,
performFinalChecks);
})
->Then(
GetCurrentSerialEventTarget(), __func__,
[self, promise] {
self->NotifyUserGestureActivation();
promise->MaybeResolveWithUndefined();
},
[promise] { promise->MaybeRejectWithUndefined(); });
promise->MaybeRejectWithUndefined();
return promise.forget();
}
promise->MaybeResolveWithUndefined();
// Step 3: Get some useful variables that can be captured by the lambda for
// the asynchronous portion
RefPtr<BrowsingContext> bc = this->GetBrowsingContext();
nsCOMPtr<nsPIDOMWindowInner> inner = this->GetInnerWindow();
if (!inner) {
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
return promise.forget();
}
RefPtr<nsGlobalWindowOuter> outer =
nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
if (!outer) {
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
return promise.forget();
}
nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
thirdPartyURI, NodePrincipal()->OriginAttributesRef());
if (!principal) {
this->ConsumeTransientUserGestureActivation();
promise->MaybeRejectWithUndefined();
return promise.forget();
}
RefPtr<Document> self(this);
bool hasUserActivation = HasValidTransientUserGestureActivation();
// Consume user activation before entering the async part of this method.
// This prevents usage of other transient activation-gated APIs.
this->ConsumeTransientUserGestureActivation();
// Step 4a: Start the async part of this function. Check the cookie
// permission, but this can't be done in this process. We needs the cookie
// permission of the URL as if it were embedded on this page, so we need to
// make this check in the ContentParent.
ContentBlocking::AsyncCheckCookiesPermittedDecidesStorageAccessAPI(
GetBrowsingContext(), principal)
->Then(
GetCurrentSerialEventTarget(), __func__,
[inner, thirdPartyURI, bc, principal, hasUserActivation, self,
promise](Maybe<bool> cookieResult) {
// Handle the result of the cookie permission check that took place
// in the ContentParent.
if (cookieResult.isSome()) {
if (cookieResult.value()) {
return MozPromise<int, bool, true>::CreateAndResolve(true,
__func__);
}
return MozPromise<int, bool, true>::CreateAndReject(false,
__func__);
}
// Step 4b: Check for the existing storage access permission
nsAutoCString type;
bool ok =
AntiTrackingUtils::CreateStoragePermissionKey(principal, type);
if (!ok) {
return MozPromise<int, bool, true>::CreateAndReject(false,
__func__);
}
if (AntiTrackingUtils::CheckStoragePermission(
self->NodePrincipal(), type,
nsContentUtils::IsInPrivateBrowsing(self), nullptr, 0)) {
return MozPromise<int, bool, true>::CreateAndResolve(true,
__func__);
}
// Step 4c: Try to request storage access, either automatically or
// with a user-prompt. This is the part that is async in the
// typical requestStorageAccess function.
return self->RequestStorageAccessAsyncHelper(
inner, bc, principal, hasUserActivation,
ContentBlockingNotifier::ePrivilegeStorageAccessForOriginAPI);
},
// If the IPC rejects, we should reject our promise here which will
// cause a rejection of the promise we already returned
[promise]() {
return MozPromise<int, bool, true>::CreateAndReject(false,
__func__);
})
->Then(
GetCurrentSerialEventTarget(), __func__,
// If the previous handlers resolved, we should reinstate user
// activation and resolve the promise we returned in Step 5.
[self, promise] {
self->NotifyUserGestureActivation();
promise->MaybeResolveWithUndefined();
},
// If the previous handler rejected, we should reject the promise
// returned by this function.
[promise] { promise->MaybeRejectWithUndefined(); });
// Step 5: While the async stuff is happening, we should return the promise so
// our caller can continue executing.
return promise.forget();
}

View file

@ -25,6 +25,7 @@
#include "mozilla/BasicEvents.h"
#include "mozilla/BitSet.h"
#include "mozilla/OriginTrials.h"
#include "mozilla/ContentBlockingNotifier.h"
#include "mozilla/CORSMode.h"
#include "mozilla/CallState.h"
#include "mozilla/EventStates.h"
@ -1264,6 +1265,16 @@ class Document : public nsINode,
nsresult HasStorageAccessSync(bool& aHasStorageAccess);
already_AddRefed<Promise> HasStorageAccess(ErrorResult& aRv);
// This function performs the asynchronous portion of checking if requests
// for storage access will be sucessful or not. This includes creating a
// permission prompt request and trying to perform an "autogrant"
// This will return a promise whose values correspond to those of a
// ContentBlocking::AllowAccessFor call that ends the funciton.
RefPtr<MozPromise<int, bool, true>> RequestStorageAccessAsyncHelper(
nsPIDOMWindowInner* aInnerWindow, BrowsingContext* aBrowsingContext,
nsIPrincipal* aPrincipal, bool aHasUserInteraction,
ContentBlockingNotifier::StorageAccessPermissionGrantedReason aNotifier);
already_AddRefed<Promise> RequestStorageAccess(ErrorResult& aRv);
already_AddRefed<Promise> RequestStorageAccessForOrigin(
@ -1905,32 +1916,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
@ -2771,7 +2783,8 @@ class Document : public nsINode,
* @param aFireEvents If true, delayed events (focus/blur) will be fired
* asynchronously.
*/
void UnsuppressEventHandlingAndFireEvents(bool aFireEvents);
MOZ_CAN_RUN_SCRIPT_BOUNDARY void UnsuppressEventHandlingAndFireEvents(
bool aFireEvents);
uint32_t EventHandlingSuppressed() const { return mEventsSuppressed; }

View file

@ -447,10 +447,11 @@ int32_t Element::TabIndex() {
void Element::Focus(const FocusOptions& aOptions, CallerType aCallerType,
ErrorResult& aError) {
RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
if (!fm) {
const RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
if (MOZ_UNLIKELY(!fm)) {
return;
}
const OwningNonNull<Element> kungFuDeathGrip(*this);
// Also other browsers seem to have the hack to not re-focus (and flush) when
// the element is already focused.
// Until https://github.com/whatwg/html/issues/4512 is clarified, we'll
@ -458,7 +459,7 @@ void Element::Focus(const FocusOptions& aOptions, CallerType aCallerType,
// I.e., `focus({ preventScroll: true})` followed by `focus( { preventScroll:
// false })` won't re-focus.
if (fm->CanSkipFocus(this)) {
fm->NotifyOfReFocus(*this);
fm->NotifyOfReFocus(kungFuDeathGrip);
fm->NeedsFlushBeforeEventHandling(this);
return;
}
@ -466,7 +467,7 @@ void Element::Focus(const FocusOptions& aOptions, CallerType aCallerType,
if (aCallerType == CallerType::NonSystem) {
fmFlags |= nsIFocusManager::FLAG_NONSYSTEMCALLER;
}
aError = fm->SetFocus(this, fmFlags);
aError = fm->SetFocus(kungFuDeathGrip, fmFlags);
}
void Element::SetTabIndex(int32_t aTabIndex, mozilla::ErrorResult& aError) {
@ -493,10 +494,10 @@ void Element::Blur(mozilla::ErrorResult& aError) {
return;
}
nsPIDOMWindowOuter* win = doc->GetWindow();
nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (win && fm) {
aError = fm->ClearFocus(win);
if (nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow()) {
if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
aError = fm->ClearFocus(win);
}
}
}

View file

@ -301,6 +301,8 @@ class Element : public FragmentOrElement {
/**
* Make focus on this element.
*/
// TODO: Convert Focus() to MOZ_CAN_RUN_SCRIPT and get rid of the
// kungFuDeathGrip in it.
MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual void Focus(const FocusOptions& aOptions,
const CallerType aCallerType,
ErrorResult& aError);
@ -308,7 +310,7 @@ class Element : public FragmentOrElement {
/**
* Show blur and clear focus.
*/
virtual void Blur(mozilla::ErrorResult& aError);
MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual void Blur(mozilla::ErrorResult& aError);
/**
* The style state of this element. This is the real state of the element

View file

@ -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)

View file

@ -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;
}

View file

@ -1972,7 +1972,8 @@ class nsContentUtils {
* closed. At opening, aInstalling should be TRUE, otherwise, it should be
* FALSE.
*/
static void NotifyInstalledMenuKeyboardListener(bool aInstalling);
MOZ_CAN_RUN_SCRIPT static void NotifyInstalledMenuKeyboardListener(
bool aInstalling);
/**
* Check whether the nsIURI uses the given scheme.
@ -2459,7 +2460,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);

View file

@ -2124,7 +2124,7 @@ nsDOMWindowUtils::GetNodeObservedByIMEContentObserver(nsINode** aNode) {
*aNode = nullptr;
return NS_OK;
}
*aNode = do_AddRef(observer->GetObservingContent()).take();
*aNode = do_AddRef(observer->GetObservingElement()).take();
return NS_OK;
}

View file

@ -216,7 +216,9 @@ void nsFocusManager::Shutdown() { sInstance = nullptr; }
// static
void nsFocusManager::PrefChanged(const char* aPref, void* aSelf) {
static_cast<nsFocusManager*>(aSelf)->PrefChanged(aPref);
if (RefPtr<nsFocusManager> fm = static_cast<nsFocusManager*>(aSelf)) {
fm->PrefChanged(aPref);
}
}
void nsFocusManager::PrefChanged(const char* aPref) {
@ -391,9 +393,9 @@ nsFocusManager::GetActiveBrowsingContext(BrowsingContext** aBrowsingContext) {
void nsFocusManager::FocusWindow(nsPIDOMWindowOuter* aWindow,
CallerType aCallerType) {
if (sInstance) {
sInstance->SetFocusedWindowWithCallerType(
aWindow, aCallerType, sInstance->GenerateFocusActionId());
if (RefPtr<nsFocusManager> fm = sInstance) {
fm->SetFocusedWindowWithCallerType(aWindow, aCallerType,
sInstance->GenerateFocusActionId());
}
}
@ -575,7 +577,7 @@ nsFocusManager::ClearFocus(mozIDOMWindowProxy* aWindow) {
nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
if (IsSameOrAncestor(window, GetFocusedBrowsingContext())) {
BrowsingContext* bc = window->GetBrowsingContext();
RefPtr<BrowsingContext> bc = window->GetBrowsingContext();
bool isAncestor = (GetFocusedBrowsingContext() != bc);
uint64_t actionId = GenerateFocusActionId();
if (Blur(bc, nullptr, isAncestor, true, actionId)) {
@ -643,9 +645,8 @@ nsFocusManager::MoveCaretToFocus(mozIDOMWindowProxy* aWindow) {
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
nsCOMPtr<nsIContent> content = window->GetFocusedElement();
if (content) {
MoveCaretToFocus(presShell, content);
if (RefPtr<Element> focusedElement = window->GetFocusedElement()) {
MoveCaretToFocus(presShell, focusedElement);
}
}
}
@ -693,8 +694,8 @@ void nsFocusManager::WindowRaised(mozIDOMWindowProxy* aWindow,
}
// lower the existing window, if any. This shouldn't happen usually.
if (mActiveWindow) {
WindowLowered(mActiveWindow, aActionId);
if (nsCOMPtr<nsPIDOMWindowOuter> activeWindow = mActiveWindow) {
WindowLowered(activeWindow, aActionId);
}
} else if (bc->IsTop()) {
BrowsingContext* active = GetActiveBrowsingContext();
@ -706,7 +707,8 @@ void nsFocusManager::WindowRaised(mozIDOMWindowProxy* aWindow,
if (active && active != bc) {
if (active->IsInProcess()) {
WindowLowered(active->GetDOMWindow(), aActionId);
nsCOMPtr<nsPIDOMWindowOuter> activeWindow = active->GetDOMWindow();
WindowLowered(activeWindow, aActionId);
}
// No else, because trying to lower other-process windows
// from here can result in the BrowsingContext no longer
@ -884,12 +886,15 @@ nsresult nsFocusManager::ContentRemoved(Document* aDocument,
if (childWindow &&
IsSameOrAncestor(childWindow, GetFocusedBrowsingContext())) {
if (XRE_IsParentProcess()) {
ClearFocus(mActiveWindow);
nsCOMPtr<nsPIDOMWindowOuter> activeWindow = mActiveWindow;
ClearFocus(activeWindow);
} else {
BrowsingContext* active = GetActiveBrowsingContext();
if (active) {
if (active->IsInProcess()) {
ClearFocus(active->GetDOMWindow());
nsCOMPtr<nsPIDOMWindowOuter> activeWindow =
active->GetDOMWindow();
ClearFocus(activeWindow);
} else {
mozilla::dom::ContentChild* contentChild =
mozilla::dom::ContentChild::GetSingleton();
@ -1038,27 +1043,27 @@ void nsFocusManager::WindowHidden(mozIDOMWindowProxy* aWindow,
// window, or an ancestor of the focused window. Either way, the focus is no
// longer valid, so it needs to be updated.
RefPtr<Element> oldFocusedElement = std::move(mFocusedElement);
const RefPtr<Element> oldFocusedElement = std::move(mFocusedElement);
nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
if (!focusedDocShell) {
return;
}
RefPtr<PresShell> presShell = focusedDocShell->GetPresShell();
const RefPtr<PresShell> presShell = focusedDocShell->GetPresShell();
if (oldFocusedElement && oldFocusedElement->IsInComposedDoc()) {
NotifyFocusStateChange(oldFocusedElement, nullptr, 0, false, false);
window->UpdateCommands(u"focus"_ns, nullptr, 0);
if (presShell) {
SendFocusOrBlurEvent(eBlur, presShell,
oldFocusedElement->GetComposedDoc(),
oldFocusedElement, false);
RefPtr<Document> composedDoc = oldFocusedElement->GetComposedDoc();
SendFocusOrBlurEvent(eBlur, presShell, composedDoc, oldFocusedElement,
false);
}
}
nsPresContext* focusedPresContext =
const RefPtr<nsPresContext> focusedPresContext =
presShell ? presShell->GetPresContext() : nullptr;
IMEStateManager::OnChangeFocus(focusedPresContext, nullptr,
GetFocusMoveActionCause(0));
@ -1124,16 +1129,17 @@ void nsFocusManager::WindowHidden(mozIDOMWindowProxy* aWindow,
// directly.
if (XRE_IsParentProcess()) {
if (mActiveWindow == mFocusedWindow || mActiveWindow == window) {
WindowLowered(mActiveWindow, aActionId);
nsCOMPtr<nsPIDOMWindowOuter> activeWindow = mActiveWindow;
if (activeWindow == mFocusedWindow || activeWindow == window) {
WindowLowered(activeWindow, aActionId);
} else {
ClearFocus(mActiveWindow);
ClearFocus(activeWindow);
}
} else {
BrowsingContext* active = GetActiveBrowsingContext();
if (active) {
nsPIDOMWindowOuter* activeWindow = active->GetDOMWindow();
if (activeWindow) {
if (nsCOMPtr<nsPIDOMWindowOuter> activeWindow =
active->GetDOMWindow()) {
if ((mFocusedWindow &&
mFocusedWindow->GetBrowsingContext() == active) ||
(window->GetBrowsingContext() == active)) {
@ -1217,12 +1223,6 @@ void nsFocusManager::WasNuked(nsPIDOMWindowOuter* aWindow) {
}
}
nsresult nsFocusManager::FocusPlugin(Element* aPlugin) {
NS_ENSURE_ARG(aPlugin);
SetFocusInner(aPlugin, 0, true, false, GenerateFocusActionId());
return NS_OK;
}
nsFocusManager::BlurredElementInfo::BlurredElementInfo(Element& aElement)
: mElement(aElement) {}
@ -1442,7 +1442,8 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags,
return;
}
RefPtr<BrowsingContext> focusedBrowsingContext = GetFocusedBrowsingContext();
const RefPtr<BrowsingContext> focusedBrowsingContext =
GetFocusedBrowsingContext();
// check if the element to focus is a frame (iframe) containing a child
// document. Frames are never directly focused; instead focusing a frame
@ -1464,7 +1465,7 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags,
newWindow = GetCurrentWindow(elementToFocus);
}
BrowsingContext* newBrowsingContext = nullptr;
RefPtr<BrowsingContext> newBrowsingContext;
if (newWindow) {
newBrowsingContext = newWindow->GetBrowsingContext();
}
@ -1712,10 +1713,10 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags,
// D is focused and we want to focus C. Once D has been blurred, we need
// to clear out the focus in A, otherwise A would still maintain that B
// was focused, and B that D was focused.
RefPtr<BrowsingContext> commonAncestor;
if (focusMovesToDifferentBC) {
commonAncestor = GetCommonAncestor(newWindow, focusedBrowsingContext);
}
RefPtr<BrowsingContext> commonAncestor =
focusMovesToDifferentBC
? GetCommonAncestor(newWindow, focusedBrowsingContext)
: nullptr;
bool needToClearFocusedElement = false;
if (focusedBrowsingContext->IsChrome()) {
@ -1734,11 +1735,12 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags,
}
}
if (!Blur(needToClearFocusedElement ? focusedBrowsingContext.get()
: nullptr,
commonAncestor ? commonAncestor.get() : nullptr,
focusMovesToDifferentBC, aAdjustWidget, aActionId,
elementToFocus)) {
// TODO: MOZ_KnownLive is required due to bug 1770680
if (!Blur(MOZ_KnownLive(needToClearFocusedElement
? focusedBrowsingContext.get()
: nullptr),
commonAncestor, focusMovesToDifferentBC, aAdjustWidget,
aActionId, elementToFocus)) {
return;
}
}
@ -1777,7 +1779,9 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags,
if (aFlags & FLAG_RAISE) {
if (newRootBrowsingContext) {
if (XRE_IsParentProcess() || newRootBrowsingContext->IsInProcess()) {
RaiseWindow(newRootBrowsingContext->GetDOMWindow(),
nsCOMPtr<nsPIDOMWindowOuter> outerWindow =
newRootBrowsingContext->GetDOMWindow();
RaiseWindow(outerWindow,
aFlags & FLAG_NONSYSTEMCALLER ? CallerType::NonSystem
: CallerType::System,
aActionId);
@ -2325,7 +2329,7 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear,
mFirstBlurEvent = element;
}
nsPresContext* focusedPresContext =
const RefPtr<nsPresContext> focusedPresContext =
GetActiveBrowsingContext() ? presShell->GetPresContext() : nullptr;
IMEStateManager::OnChangeFocus(focusedPresContext, nullptr,
GetFocusMoveActionCause(0));
@ -2434,13 +2438,15 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear,
SetFocusedWindowInternal(nullptr, aActionId);
mFocusedElement = nullptr;
Document* doc = window->GetExtantDoc();
RefPtr<Document> doc = window->GetExtantDoc();
if (doc) {
SendFocusOrBlurEvent(eBlur, presShell, doc, ToSupports(doc), false);
SendFocusOrBlurEvent(eBlur, presShell, doc,
MOZ_KnownLive(ToSupports(doc)), false);
}
if (!GetFocusedBrowsingContext()) {
SendFocusOrBlurEvent(eBlur, presShell, doc,
window->GetCurrentInnerWindow(), false);
nsCOMPtr<nsPIDOMWindowInner> innerWindow =
window->GetCurrentInnerWindow();
SendFocusOrBlurEvent(eBlur, presShell, doc, innerWindow, false);
}
// check if a different window was focused
@ -2496,7 +2502,7 @@ void nsFocusManager::Focus(
return;
}
RefPtr<PresShell> presShell = docShell->GetPresShell();
const RefPtr<PresShell> presShell = docShell->GetPresShell();
if (!presShell) {
return;
}
@ -2570,8 +2576,8 @@ void nsFocusManager::Focus(
// if this is a new document, update the parent chain of frames so that
// focus can be traversed from the top level down to the newly focused
// window.
AdjustWindowFocus(aWindow->GetBrowsingContext(), false,
IsWindowVisible(aWindow), aActionId);
RefPtr<BrowsingContext> bc = aWindow->GetBrowsingContext();
AdjustWindowFocus(bc, false, IsWindowVisible(aWindow), aActionId);
}
// indicate that the window has taken focus.
@ -2594,7 +2600,7 @@ void nsFocusManager::Focus(
// if switching to a new document, first fire the focus event on the
// document and then the window.
if (aIsNewDocument) {
Document* doc = aWindow->GetExtantDoc();
RefPtr<Document> doc = aWindow->GetExtantDoc();
// The focus change should be notified to IMEStateManager from here if:
// * the focused element is in design mode or
// * nobody gets focus and the document is in design mode
@ -2602,17 +2608,19 @@ void nsFocusManager::Focus(
// receive focus event.
if (doc && ((aElement && aElement->IsInDesignMode()) ||
(!aElement && doc->IsInDesignMode()))) {
IMEStateManager::OnChangeFocus(presShell->GetPresContext(), nullptr,
RefPtr<nsPresContext> presContext = presShell->GetPresContext();
IMEStateManager::OnChangeFocus(presContext, nullptr,
GetFocusMoveActionCause(aFlags));
}
if (doc && !focusInOtherContentProcess) {
SendFocusOrBlurEvent(eFocus, presShell, doc, ToSupports(doc),
aWindowRaised);
SendFocusOrBlurEvent(eFocus, presShell, doc,
MOZ_KnownLive(ToSupports(doc)), aWindowRaised);
}
if (GetFocusedBrowsingContext() == aWindow->GetBrowsingContext() &&
!mFocusedElement && !focusInOtherContentProcess) {
SendFocusOrBlurEvent(eFocus, presShell, doc,
aWindow->GetCurrentInnerWindow(), aWindowRaised);
nsCOMPtr<nsPIDOMWindowInner> innerWindow =
aWindow->GetCurrentInnerWindow();
SendFocusOrBlurEvent(eFocus, presShell, doc, innerWindow, aWindowRaised);
}
}
@ -2636,7 +2644,7 @@ void nsFocusManager::Focus(
if (aElement && aFocusChanged) {
ScrollIntoView(presShell, aElement, aFlags);
}
nsPresContext* presContext = presShell->GetPresContext();
const RefPtr<nsPresContext> presContext = presShell->GetPresContext();
if (sendFocusEvent) {
NotifyFocusStateChange(aElement, nullptr, aFlags,
/* aGettingFocus = */ true, shouldShowFocusRing);
@ -2659,11 +2667,11 @@ void nsFocusManager::Focus(
}
if (!focusInOtherContentProcess) {
SendFocusOrBlurEvent(eFocus, presShell, aElement->GetComposedDoc(),
aElement, aWindowRaised, isRefocus,
aBlurredElementInfo
? aBlurredElementInfo->mElement.get()
: nullptr);
RefPtr<Document> composedDocument = aElement->GetComposedDoc();
RefPtr<Element> relatedTargetElement =
aBlurredElementInfo ? aBlurredElementInfo->mElement.get() : nullptr;
SendFocusOrBlurEvent(eFocus, presShell, composedDocument, aElement,
aWindowRaised, isRefocus, relatedTargetElement);
}
} else {
IMEStateManager::OnChangeFocus(presContext, nullptr,
@ -2676,7 +2684,7 @@ void nsFocusManager::Focus(
if (!mFocusedElement) {
// When there is no focused element, IMEStateManager needs to adjust IME
// enabled state with the document.
nsPresContext* presContext = presShell->GetPresContext();
RefPtr<nsPresContext> presContext = presShell->GetPresContext();
IMEStateManager::OnChangeFocus(presContext, nullptr,
GetFocusMoveActionCause(aFlags));
}
@ -2692,9 +2700,11 @@ void nsFocusManager::Focus(
// needed. If this is a different document than was focused before, also
// update the caret's visibility. If this is the same document, the caret
// visibility should be the same as before so there is no need to update it.
if (mFocusedElement == aElement)
if (mFocusedElement == aElement) {
RefPtr<Element> focusedElement = mFocusedElement;
UpdateCaret(aFocusChanged && !(aFlags & FLAG_BYMOUSE), aIsNewDocument,
mFocusedElement);
focusedElement);
}
}
class FocusBlurEvent : public Runnable {
@ -2932,7 +2942,9 @@ void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow,
nsCOMPtr<nsPIDOMWindowOuter> window(aWindow);
RefPtr<nsFocusManager> self(this);
NS_DispatchToCurrentThread(NS_NewRunnableFunction(
"nsFocusManager::RaiseWindow", [self, window]() -> void {
"nsFocusManager::RaiseWindow",
// TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1770093)
[self, window]() MOZ_CAN_RUN_SCRIPT_BOUNDARY -> void {
self->WindowRaised(window, GenerateFocusActionId());
}));
return;
@ -2992,7 +3004,8 @@ void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow,
}
void nsFocusManager::UpdateCaretForCaretBrowsingMode() {
UpdateCaret(false, true, mFocusedElement);
RefPtr<Element> focusedElement = mFocusedElement;
UpdateCaret(false, true, focusedElement);
}
void nsFocusManager::UpdateCaret(bool aMoveCaretToFocus, bool aUpdateVisibility,
@ -3016,7 +3029,7 @@ void nsFocusManager::UpdateCaret(bool aMoveCaretToFocus, bool aUpdateVisibility,
bool browseWithCaret = Preferences::GetBool("accessibility.browsewithcaret");
RefPtr<PresShell> presShell = focusedDocShell->GetPresShell();
const RefPtr<PresShell> presShell = focusedDocShell->GetPresShell();
if (!presShell) {
return;
}
@ -3349,15 +3362,17 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
}
}
Element* rootContent = doc->GetRootElement();
NS_ENSURE_TRUE(rootContent, NS_OK);
// rootElement and presShell may be set to sub-document's ones so that they
// cannot be `const`.
RefPtr<Element> rootElement = doc->GetRootElement();
NS_ENSURE_TRUE(rootElement, NS_OK);
PresShell* presShell = doc->GetPresShell();
RefPtr<PresShell> presShell = doc->GetPresShell();
NS_ENSURE_TRUE(presShell, NS_OK);
if (aType == MOVEFOCUS_FIRST) {
if (!aStartContent) {
startContent = rootContent;
startContent = rootElement;
}
return GetNextTabbableContent(presShell, startContent, nullptr,
startContent, true, 1, false, false,
@ -3365,7 +3380,7 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
}
if (aType == MOVEFOCUS_LAST) {
if (!aStartContent) {
startContent = rootContent;
startContent = rootElement;
}
return GetNextTabbableContent(presShell, startContent, nullptr,
startContent, false, 0, false, false,
@ -3397,7 +3412,7 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
// so just treat this as the beginning of the tab order.
if (tabIndex < 0) {
tabIndex = 1;
if (startContent != rootContent) {
if (startContent != rootElement) {
ignoreTabIndex = true;
}
}
@ -3413,13 +3428,13 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
if (popupFrame && !forDocumentNavigation) {
// Don't navigate outside of a popup, so pretend that the
// root content is the popup itself
rootContent = popupFrame->GetContent()->AsElement();
NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
rootElement = popupFrame->GetContent()->AsElement();
NS_ASSERTION(rootElement, "Popup frame doesn't have a content node");
} else if (!forward) {
// If focus moves backward and when current focused node is root
// content or <body> element which is editable by contenteditable
// attribute, focus should move to its parent document.
if (startContent == rootContent) {
if (startContent == rootElement) {
doNavigation = false;
} else {
Document* doc = startContent->GetComposedDoc();
@ -3448,7 +3463,7 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
// When navigating by documents, we start at the popup but can navigate
// outside of it to look for other panels and documents.
if (!forDocumentNavigation) {
rootContent = startContent->AsElement();
rootElement = startContent->AsElement();
}
doc = startContent ? startContent->GetComposedDoc() : nullptr;
@ -3459,8 +3474,8 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
nsCOMPtr<nsIContent> endSelectionContent;
GetSelectionLocation(doc, presShell, getter_AddRefs(startContent),
getter_AddRefs(endSelectionContent));
// If the selection is on the rootContent, then there is no selection
if (startContent == rootContent) {
// If the selection is on the rootElement, then there is no selection
if (startContent == rootElement) {
startContent = nullptr;
}
@ -3485,7 +3500,7 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
if (!startContent) {
// otherwise, just use the root content as the starting point
startContent = rootContent;
startContent = rootElement;
NS_ENSURE_TRUE(startContent, NS_OK);
}
}
@ -3499,7 +3514,7 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
if (forDocumentNavigation && nsContentUtils::IsChromeDoc(doc)) {
nsAutoString retarget;
if (rootContent->GetAttr(kNameSpaceID_None,
if (rootElement->GetAttr(kNameSpaceID_None,
nsGkAtoms::retargetdocumentfocus, retarget)) {
nsIContent* retargetElement = doc->GetElementById(retarget);
// The common case here is the urlbar where focus is on the anonymous
@ -3510,7 +3525,7 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
(retargetElement == startContent ||
(!retargetElement->Contains(startContent) &&
startContent->IsInclusiveDescendantOf(retargetElement)))) {
startContent = rootContent;
startContent = rootElement;
}
}
}
@ -3529,7 +3544,7 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
// will never wrap around on its own, and can only return the original
// content when it is called a second time or later.
bool skipOriginalContentCheck = true;
nsIContent* originalStartContent = startContent;
const nsCOMPtr<nsIContent> originalStartContent = startContent;
LOGCONTENTNAVIGATION("Focus Navigation Start Content %s", startContent.get());
LOGFOCUSNAVIGATION((" Forward: %d Tabindex: %d Ignore: %d DocNav: %d",
@ -3539,9 +3554,11 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
while (doc) {
if (doNavigation) {
nsCOMPtr<nsIContent> nextFocus;
// TODO: MOZ_KnownLive is reruired due to bug 1770680
nsresult rv = GetNextTabbableContent(
presShell, rootContent,
skipOriginalContentCheck ? nullptr : originalStartContent,
presShell, rootElement,
MOZ_KnownLive(skipOriginalContentCheck ? nullptr
: originalStartContent.get()),
startContent, forward, tabIndex, ignoreTabIndex,
forDocumentNavigation, aNavigateByKey, getter_AddRefs(nextFocus));
NS_ENSURE_SUCCESS(rv, rv);
@ -3568,8 +3585,8 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
// in a popup, so start again from the beginning of the popup. However,
// if we already started at the beginning, then there isn't anything to
// focus, so just return
if (startContent != rootContent) {
startContent = rootContent;
if (startContent != rootElement) {
startContent = rootElement;
tabIndex = forward ? 1 : 0;
continue;
}
@ -3582,11 +3599,11 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
ignoreTabIndex = false;
if (aNoParentTraversal) {
if (startContent == rootContent) {
if (startContent == rootElement) {
return NS_OK;
}
startContent = rootContent;
startContent = rootElement;
tabIndex = forward ? 1 : 0;
continue;
}
@ -3607,7 +3624,7 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
doc = startContent->GetComposedDoc();
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
rootContent = doc->GetRootElement();
rootElement = doc->GetRootElement();
presShell = doc->GetPresShell();
// We can focus the root element now that we have moved to another
@ -3634,8 +3651,8 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
popupFrame = nsLayoutUtils::GetClosestFrameOfType(
frame, LayoutFrameType::MenuPopup);
if (popupFrame) {
rootContent = popupFrame->GetContent()->AsElement();
NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
rootElement = popupFrame->GetContent()->AsElement();
NS_ASSERTION(rootElement, "Popup frame doesn't have a content node");
}
}
} else {
@ -3646,10 +3663,9 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
docShell->TabToTreeOwner(forward, forDocumentNavigation, &tookFocus);
// If the tree owner took the focus, blur the current element.
if (tookFocus) {
if (GetFocusedBrowsingContext() &&
GetFocusedBrowsingContext()->IsInProcess()) {
Blur(GetFocusedBrowsingContext(), nullptr, true, true,
GenerateFocusActionId());
RefPtr<BrowsingContext> focusedBC = GetFocusedBrowsingContext();
if (focusedBC && focusedBC->IsInProcess()) {
Blur(focusedBC, nullptr, true, true, GenerateFocusActionId());
} else {
nsCOMPtr<nsPIDOMWindowOuter> window = docShell->GetWindow();
window->SetFocusedElement(nullptr);
@ -3671,8 +3687,9 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
// Chrome documents however cannot be focused directly, so instead we
// focus the first focusable element within the window.
// For example, the urlbar.
Element* root = GetRootForFocus(piWindow, doc, true, true);
return FocusFirst(root, aNextContent);
RefPtr<Element> rootElementForFocus =
GetRootForFocus(piWindow, doc, true, true);
return FocusFirst(rootElementForFocus, aNextContent);
}
// Once we have hit the top-level and have iterated to the end again, we
@ -3681,7 +3698,7 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
mayFocusRoot = true;
// reset the tab index and start again from the beginning or end
startContent = rootContent;
startContent = rootElement;
tabIndex = forward ? 1 : 0;
}
@ -4001,15 +4018,15 @@ nsIContent* nsFocusManager::GetNextTabbableContentInScope(
}
nsIContent* nsFocusManager::GetNextTabbableContentInAncestorScopes(
nsIContent* aStartOwner, nsIContent** aStartContent,
nsIContent* aStartOwner, nsCOMPtr<nsIContent>& aStartContent /* inout */,
nsIContent* aOriginalStartContent, bool aForward, int32_t* aCurrentTabIndex,
bool* aIgnoreTabIndex, bool aForDocumentNavigation, bool aNavigateByKey) {
MOZ_ASSERT(aStartOwner == FindScopeOwner(*aStartContent),
MOZ_ASSERT(aStartOwner == FindScopeOwner(aStartContent),
"aStartOWner should be the scope owner of aStartContent");
MOZ_ASSERT(IsHostOrSlot(aStartOwner), "scope owner should be host or slot");
nsIContent* owner = aStartOwner;
nsIContent* startContent = *aStartContent;
nsCOMPtr<nsIContent> owner = aStartOwner;
nsCOMPtr<nsIContent> startContent = aStartContent;
while (IsHostOrSlot(owner)) {
int32_t tabIndex = 0;
if (IsHostOrSlot(startContent)) {
@ -4033,7 +4050,7 @@ nsIContent* nsFocusManager::GetNextTabbableContentInAncestorScopes(
// If not found in shadow DOM, search from the top level shadow host in light
// DOM
*aStartContent = startContent;
aStartContent = startContent;
*aCurrentTabIndex = HostOrSlotTabIndexValue(startContent);
if (*aCurrentTabIndex < 0) {
@ -4074,8 +4091,9 @@ nsresult nsFocusManager::GetNextTabbableContent(
return NS_OK;
}
nsIContent* startContent = aStartContent;
nsIContent* currentTopLevelScopeOwner = GetTopLevelScopeOwner(startContent);
nsCOMPtr<nsIContent> startContent = aStartContent;
nsCOMPtr<nsIContent> currentTopLevelScopeOwner =
GetTopLevelScopeOwner(startContent);
LOGCONTENTNAVIGATION("GetNextTabbable: %s", startContent);
LOGFOCUSNAVIGATION((" tabindex: %d", aCurrentTabIndex));
@ -4095,9 +4113,9 @@ nsresult nsFocusManager::GetNextTabbableContent(
// If startContent is in a scope owned by Shadow DOM search from scope
// including startContent
if (nsIContent* owner = FindScopeOwner(startContent)) {
if (nsCOMPtr<nsIContent> owner = FindScopeOwner(startContent)) {
nsIContent* contentToFocus = GetNextTabbableContentInAncestorScopes(
owner, &startContent, aOriginalStartContent, aForward,
owner, startContent /* inout */, aOriginalStartContent, aForward,
&aCurrentTabIndex, &aIgnoreTabIndex, aForDocumentNavigation,
aNavigateByKey);
if (contentToFocus) {
@ -4192,7 +4210,7 @@ nsresult nsFocusManager::GetNextTabbableContent(
while (frame) {
// Try to find the topmost scope owner, since we want to skip the node
// that is not owned by document in frame traversal.
nsIContent* currentContent = frame->GetContent();
const nsCOMPtr<nsIContent> currentContent = frame->GetContent();
if (currentTopLevelScopeOwner) {
oldTopLevelScopeOwner = currentTopLevelScopeOwner;
}
@ -4456,15 +4474,16 @@ bool nsFocusManager::TryDocumentNavigation(nsIContent* aCurrentContent,
bool* aCheckSubDocument,
nsIContent** aResultContent) {
*aCheckSubDocument = true;
if (Element* docRoot = GetRootForChildDocument(aCurrentContent)) {
if (RefPtr<Element> rootElementForChildDocument =
GetRootForChildDocument(aCurrentContent)) {
// If GetRootForChildDocument returned something then call
// FocusFirst to find the root or first element to focus within
// the child document. If this is a frameset though, skip this and
// fall through to normal tab navigation to iterate into
// the frameset's frames and locate the first focusable frame.
if (!docRoot->IsHTMLElement(nsGkAtoms::frameset)) {
if (!rootElementForChildDocument->IsHTMLElement(nsGkAtoms::frameset)) {
*aCheckSubDocument = false;
Unused << FocusFirst(docRoot, aResultContent);
Unused << FocusFirst(rootElementForChildDocument, aResultContent);
return *aResultContent != nullptr;
}
} else {
@ -4495,16 +4514,16 @@ bool nsFocusManager::TryToMoveFocusToSubDocument(
}
}
}
Element* rootElement = subdoc->GetRootElement();
PresShell* subPresShell = subdoc->GetPresShell();
if (rootElement && subPresShell) {
nsresult rv = GetNextTabbableContent(
subPresShell, rootElement, aOriginalStartContent, rootElement,
aForward, (aForward ? 1 : 0), false, aForDocumentNavigation,
aNavigateByKey, aResultContent);
NS_ENSURE_SUCCESS(rv, false);
if (*aResultContent) {
return true;
if (RefPtr<Element> rootElement = subdoc->GetRootElement()) {
if (RefPtr<PresShell> subPresShell = subdoc->GetPresShell()) {
nsresult rv = GetNextTabbableContent(
subPresShell, rootElement, aOriginalStartContent, rootElement,
aForward, (aForward ? 1 : 0), false, aForDocumentNavigation,
aNavigateByKey, aResultContent);
NS_ENSURE_SUCCESS(rv, false);
if (*aResultContent) {
return true;
}
}
}
}
@ -4635,7 +4654,7 @@ nsresult nsFocusManager::FocusFirst(Element* aRootElement,
if (aRootElement->GetAttr(kNameSpaceID_None,
nsGkAtoms::retargetdocumentfocus, retarget)) {
nsCOMPtr<Element> element = doc->GetElementById(retarget);
RefPtr<Element> element = doc->GetElementById(retarget);
nsCOMPtr<nsIContent> retargetElement =
FlushAndCheckIfFocusable(element, 0);
if (retargetElement) {
@ -4650,8 +4669,7 @@ nsresult nsFocusManager::FocusFirst(Element* aRootElement,
// If the found content is in a chrome shell, navigate forward one
// tabbable item so that the first item is focused. Note that we
// always go forward and not back here.
PresShell* presShell = doc->GetPresShell();
if (presShell) {
if (RefPtr<PresShell> presShell = doc->GetPresShell()) {
return GetNextTabbableContent(presShell, aRootElement, nullptr,
aRootElement, true, 1, false, false, true,
aNextContent);
@ -5258,12 +5276,12 @@ void nsFocusManager::SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow,
}
}
void nsFocusManager::NotifyOfReFocus(nsIContent& aContent) {
nsPIDOMWindowOuter* window = GetCurrentWindow(&aContent);
void nsFocusManager::NotifyOfReFocus(Element& aElement) {
nsPIDOMWindowOuter* window = GetCurrentWindow(&aElement);
if (!window || window != mFocusedWindow) {
return;
}
if (!aContent.IsInComposedDoc() || IsNonFocusableRoot(&aContent)) {
if (!aElement.IsInComposedDoc() || IsNonFocusableRoot(&aElement)) {
return;
}
nsIDocShell* docShell = window->GetDocShell();
@ -5274,11 +5292,11 @@ void nsFocusManager::NotifyOfReFocus(nsIContent& aContent) {
if (!presShell) {
return;
}
nsPresContext* presContext = presShell->GetPresContext();
RefPtr<nsPresContext> presContext = presShell->GetPresContext();
if (!presContext) {
return;
}
IMEStateManager::OnReFocus(presContext, aContent);
IMEStateManager::OnReFocus(*presContext, aElement);
}
void nsFocusManager::MarkUncollectableForCCGeneration(uint32_t aGeneration) {

View file

@ -61,11 +61,12 @@ class nsFocusManager final : public nsIFocusManager,
// Simple helper to call SetFocusedWindow on the instance.
//
// This raises the window and switches to the tab as needed.
static void FocusWindow(nsPIDOMWindowOuter* aWindow,
mozilla::dom::CallerType aCallerType);
MOZ_CAN_RUN_SCRIPT static void FocusWindow(
nsPIDOMWindowOuter* aWindow, mozilla::dom::CallerType aCallerType);
static void PrefChanged(const char* aPref, void* aSelf);
void PrefChanged(const char* aPref);
MOZ_CAN_RUN_SCRIPT_BOUNDARY static void PrefChanged(const char* aPref,
void* aSelf);
MOZ_CAN_RUN_SCRIPT void PrefChanged(const char* aPref);
/**
* Retrieve the single focus manager.
@ -138,7 +139,8 @@ class nsFocusManager final : public nsIFocusManager,
/**
* Called when content has been removed.
*/
nsresult ContentRemoved(Document* aDocument, nsIContent* aContent);
MOZ_CAN_RUN_SCRIPT nsresult ContentRemoved(Document* aDocument,
nsIContent* aContent);
void NeedsFlushBeforeEventHandling(mozilla::dom::Element* aElement) {
if (mFocusedElement == aElement) {
@ -148,7 +150,8 @@ class nsFocusManager final : public nsIFocusManager,
bool CanSkipFocus(nsIContent* aContent);
void FlushBeforeEventHandlingIfNeeded(nsIContent* aContent) {
MOZ_CAN_RUN_SCRIPT void FlushBeforeEventHandlingIfNeeded(
nsIContent* aContent) {
if (mEventHandlingNeedsFlush) {
nsCOMPtr<Document> doc = aContent->GetComposedDoc();
if (doc) {
@ -161,7 +164,7 @@ class nsFocusManager final : public nsIFocusManager,
/**
* Update the caret with current mode (whether in caret browsing mode or not).
*/
void UpdateCaretForCaretBrowsingMode();
MOZ_CAN_RUN_SCRIPT void UpdateCaretForCaretBrowsingMode();
/** @see nsIFocusManager.getLastFocusMethod() */
uint32_t GetLastFocusMethod(nsPIDOMWindowOuter*) const;
@ -203,16 +206,14 @@ class nsFocusManager final : public nsIFocusManager,
* aNavigateByKey to move focus by keyboard as a side effect of computing the
* next target.
*/
nsresult DetermineElementToMoveFocus(nsPIDOMWindowOuter* aWindow,
nsIContent* aStart, int32_t aType,
bool aNoParentTraversal,
bool aNavigateByKey,
nsIContent** aNextContent);
MOZ_CAN_RUN_SCRIPT nsresult DetermineElementToMoveFocus(
nsPIDOMWindowOuter* aWindow, nsIContent* aStart, int32_t aType,
bool aNoParentTraversal, bool aNavigateByKey, nsIContent** aNextContent);
/**
* Setter for focusedWindow with CallerType
*/
MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult SetFocusedWindowWithCallerType(
MOZ_CAN_RUN_SCRIPT nsresult SetFocusedWindowWithCallerType(
mozIDOMWindowProxy* aWindowToFocus, mozilla::dom::CallerType aCallerType,
uint64_t aActionId);
@ -225,19 +226,21 @@ class nsFocusManager final : public nsIFocusManager,
/**
* Raises the top-level window aWindow at the widget level.
*/
void RaiseWindow(nsPIDOMWindowOuter* aWindow,
mozilla::dom::CallerType aCallerType, uint64_t aActionId);
MOZ_CAN_RUN_SCRIPT void RaiseWindow(nsPIDOMWindowOuter* aWindow,
mozilla::dom::CallerType aCallerType,
uint64_t aActionId);
/**
* Called when a window has been raised.
*/
void WindowRaised(mozIDOMWindowProxy* aWindow, uint64_t aActionId);
MOZ_CAN_RUN_SCRIPT void WindowRaised(mozIDOMWindowProxy* aWindow,
uint64_t aActionId);
/**
* Called when a window has been lowered.
*/
MOZ_CAN_RUN_SCRIPT_BOUNDARY void WindowLowered(mozIDOMWindowProxy* aWindow,
uint64_t aActionId);
MOZ_CAN_RUN_SCRIPT void WindowLowered(mozIDOMWindowProxy* aWindow,
uint64_t aActionId);
/**
* Called when a new document in a window is shown.
@ -245,29 +248,23 @@ class nsFocusManager final : public nsIFocusManager,
* If aNeedsFocus is true, then focus events are expected to be fired on the
* window if this window is in the focused window chain.
*/
void WindowShown(mozIDOMWindowProxy* aWindow, bool aNeedsFocus);
MOZ_CAN_RUN_SCRIPT void WindowShown(mozIDOMWindowProxy* aWindow,
bool aNeedsFocus);
/**
* Called when a document in a window has been hidden or otherwise can no
* longer accept focus.
*/
void WindowHidden(mozIDOMWindowProxy* aWindow, uint64_t aActionId);
MOZ_CAN_RUN_SCRIPT void WindowHidden(mozIDOMWindowProxy* aWindow,
uint64_t aActionId);
/**
* Fire any events that have been delayed due to synchronized actions.
*/
void FireDelayedEvents(Document* aDocument);
MOZ_CAN_RUN_SCRIPT void FireDelayedEvents(Document* aDocument);
void WasNuked(nsPIDOMWindowOuter* aWindow);
/**
* Indicate that a plugin wishes to take the focus. This is similar to a
* normal focus except that the widget focus is not changed. Updating the
* widget focus state is the responsibility of the caller.
*/
MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
FocusPlugin(mozilla::dom::Element* aPlugin);
static uint32_t ProgrammaticFocusFlags(
const mozilla::dom::FocusOptions& aOptions);
@ -289,11 +286,11 @@ class nsFocusManager final : public nsIFocusManager,
static InputContextAction::Cause GetFocusMoveActionCause(uint32_t aFlags);
/**
* Notify of re-focus to same content.
* Notify of re-focus to same element.
*
* aContent is focused content.
* aElement is focused element.
*/
void NotifyOfReFocus(nsIContent& aContent);
MOZ_CAN_RUN_SCRIPT void NotifyOfReFocus(mozilla::dom::Element& aElement);
static void MarkUncollectableForCCGeneration(uint32_t aGeneration);
@ -363,12 +360,12 @@ class nsFocusManager final : public nsIFocusManager,
* start at the active top-level window and navigate down the currently
* focused elements for each frame in the tree to get to aBrowsingContext.
*/
bool AdjustInProcessWindowFocus(
MOZ_CAN_RUN_SCRIPT bool AdjustInProcessWindowFocus(
mozilla::dom::BrowsingContext* aBrowsingContext, bool aCheckPermission,
bool aIsVisible, uint64_t aActionId);
MOZ_CAN_RUN_SCRIPT void AdjustWindowFocus(
mozilla::dom::BrowsingContext* aBrowsingContext, bool aCheckPermission,
bool aIsVisible, uint64_t aActionId);
void AdjustWindowFocus(mozilla::dom::BrowsingContext* aBrowsingContext,
bool aCheckPermission, bool aIsVisible,
uint64_t aActionId);
/**
* Returns true if aWindow is visible.
@ -398,7 +395,7 @@ class nsFocusManager final : public nsIFocusManager,
* frame, so only the IsFocusable method on the content node must be
* true.
*/
mozilla::dom::Element* FlushAndCheckIfFocusable(
MOZ_CAN_RUN_SCRIPT mozilla::dom::Element* FlushAndCheckIfFocusable(
mozilla::dom::Element* aElement, uint32_t aFlags);
/**
@ -422,23 +419,21 @@ class nsFocusManager final : public nsIFocusManager,
*
* If aAdjustWidget is false, don't change the widget focus state.
*/
// MOZ_CAN_RUN_SCRIPT_BOUNDARY for now, until we annotate callers.
MOZ_CAN_RUN_SCRIPT_BOUNDARY
bool Blur(mozilla::dom::BrowsingContext* aBrowsingContextToClear,
mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus,
bool aIsLeavingDocument, bool aAdjustWidget, uint64_t aActionId,
mozilla::dom::Element* aElementToFocus = nullptr);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
void BlurFromOtherProcess(
MOZ_CAN_RUN_SCRIPT bool Blur(
mozilla::dom::BrowsingContext* aBrowsingContextToClear,
mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus,
bool aIsLeavingDocument, bool aAdjustWidget, uint64_t aActionId,
mozilla::dom::Element* aElementToFocus = nullptr);
MOZ_CAN_RUN_SCRIPT void BlurFromOtherProcess(
mozilla::dom::BrowsingContext* aFocusedBrowsingContext,
mozilla::dom::BrowsingContext* aBrowsingContextToClear,
mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus,
bool aIsLeavingDocument, bool aAdjustWidget, uint64_t aActionId);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
bool BlurImpl(mozilla::dom::BrowsingContext* aBrowsingContextToClear,
mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus,
bool aIsLeavingDocument, bool aAdjustWidget,
mozilla::dom::Element* aElementToFocus, uint64_t aActionId);
MOZ_CAN_RUN_SCRIPT bool BlurImpl(
mozilla::dom::BrowsingContext* aBrowsingContextToClear,
mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus,
bool aIsLeavingDocument, bool aAdjustWidget,
mozilla::dom::Element* aElementToFocus, uint64_t aActionId);
/**
* Focus an element in the active window and child frame.
@ -466,11 +461,11 @@ class nsFocusManager final : public nsIFocusManager,
*
* If aAdjustWidget is false, don't change the widget focus state.
*/
MOZ_CAN_RUN_SCRIPT_BOUNDARY
void Focus(nsPIDOMWindowOuter* aWindow, mozilla::dom::Element* aContent,
uint32_t aFlags, bool aIsNewDocument, bool aFocusChanged,
bool aWindowRaised, bool aAdjustWidget, uint64_t aActionId,
const mozilla::Maybe<BlurredElementInfo>& = mozilla::Nothing());
MOZ_CAN_RUN_SCRIPT void Focus(
nsPIDOMWindowOuter* aWindow, mozilla::dom::Element* aContent,
uint32_t aFlags, bool aIsNewDocument, bool aFocusChanged,
bool aWindowRaised, bool aAdjustWidget, uint64_t aActionId,
const mozilla::Maybe<BlurredElementInfo>& = mozilla::Nothing());
/**
* Send a focus or blur event at aTarget. It may be added to the delayed
@ -480,7 +475,7 @@ class nsFocusManager final : public nsIFocusManager,
*
* aWindowRaised should only be true if called from WindowRaised.
*/
void SendFocusOrBlurEvent(
MOZ_CAN_RUN_SCRIPT void SendFocusOrBlurEvent(
mozilla::EventMessage aEventMessage, mozilla::PresShell* aPresShell,
Document* aDocument, nsISupports* aTarget, bool aWindowRaised,
bool aIsRefocus = false,
@ -493,7 +488,7 @@ class nsFocusManager final : public nsIFocusManager,
*
* aWindowRaised should only be true if called from WindowRaised.
*/
void FireFocusOrBlurEvent(
MOZ_CAN_RUN_SCRIPT void FireFocusOrBlurEvent(
mozilla::EventMessage aEventMessage, mozilla::PresShell* aPresShell,
nsISupports* aTarget, bool aWindowRaised, bool aIsRefocus = false,
mozilla::dom::EventTarget* aRelatedTarget = nullptr);
@ -515,7 +510,7 @@ class nsFocusManager final : public nsIFocusManager,
* aRelatedTarget is the content related to the event (the object
* losing focus for focusin, the object getting focus for focusout).
*/
void FireFocusInOrOutEvent(
MOZ_CAN_RUN_SCRIPT void FireFocusInOrOutEvent(
mozilla::EventMessage aEventMessage, mozilla::PresShell* aPresShell,
nsISupports* aTarget, nsPIDOMWindowOuter* aCurrentFocusedWindow,
nsIContent* aCurrentFocusedContent,
@ -536,14 +531,15 @@ class nsFocusManager final : public nsIFocusManager,
* aUpdateVisibility should be true to update whether the caret is
* visible or not.
*/
void UpdateCaret(bool aMoveCaretToFocus, bool aUpdateVisibility,
nsIContent* aContent);
MOZ_CAN_RUN_SCRIPT void UpdateCaret(bool aMoveCaretToFocus,
bool aUpdateVisibility,
nsIContent* aContent);
/**
* Helper method to move the caret to the focused element aContent.
*/
MOZ_CAN_RUN_SCRIPT_BOUNDARY void MoveCaretToFocus(
mozilla::PresShell* aPresShell, nsIContent* aContent);
MOZ_CAN_RUN_SCRIPT void MoveCaretToFocus(mozilla::PresShell* aPresShell,
nsIContent* aContent);
/**
* Makes the caret visible or not, depending on aVisible.
@ -591,7 +587,7 @@ class nsFocusManager final : public nsIFocusManager,
* Consider the method searches downwards in flattened subtree
* rooted at aOwner.
*/
nsIContent* GetNextTabbableContentInScope(
MOZ_CAN_RUN_SCRIPT nsIContent* GetNextTabbableContentInScope(
nsIContent* aOwner, nsIContent* aStartContent,
nsIContent* aOriginalStartContent, bool aForward,
int32_t aCurrentTabIndex, bool aIgnoreTabIndex,
@ -633,8 +629,8 @@ class nsFocusManager final : public nsIFocusManager,
* flattened subtrees that contains aStartContent as non-root, except
* the flattened subtree rooted at shadow host in light DOM.
*/
nsIContent* GetNextTabbableContentInAncestorScopes(
nsIContent* aStartOwner, nsIContent** aStartContent,
MOZ_CAN_RUN_SCRIPT nsIContent* GetNextTabbableContentInAncestorScopes(
nsIContent* aStartOwner, nsCOMPtr<nsIContent>& aStartContent /* inout */,
nsIContent* aOriginalStartContent, bool aForward,
int32_t* aCurrentTabIndex, bool* aIgnoreTabIndex,
bool aForDocumentNavigation, bool aNavigateByKey);
@ -670,7 +666,7 @@ class nsFocusManager final : public nsIFocusManager,
* aNavigateByKey to move focus by keyboard as a side effect of computing the
* next target.
*/
nsresult GetNextTabbableContent(
MOZ_CAN_RUN_SCRIPT nsresult GetNextTabbableContent(
mozilla::PresShell* aPresShell, nsIContent* aRootContent,
nsIContent* aOriginalStartContent, nsIContent* aStartContent,
bool aForward, int32_t aCurrentTabIndex, bool aIgnoreTabIndex,
@ -706,8 +702,8 @@ class nsFocusManager final : public nsIFocusManager,
* aRootContent. For content documents, this will be aRootContent itself, but
* for chrome documents, this will locate the next focusable content.
*/
nsresult FocusFirst(mozilla::dom::Element* aRootContent,
nsIContent** aNextContent);
MOZ_CAN_RUN_SCRIPT nsresult FocusFirst(mozilla::dom::Element* aRootContent,
nsIContent** aNextContent);
/**
* Retrieves and returns the root node from aDocument to be focused. Will
@ -760,15 +756,14 @@ class nsFocusManager final : public nsIFocusManager,
void SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow, uint64_t aActionId,
bool aSyncBrowsingContext = true);
bool TryDocumentNavigation(nsIContent* aCurrentContent,
bool* aCheckSubDocument,
nsIContent** aResultContent);
MOZ_CAN_RUN_SCRIPT bool TryDocumentNavigation(nsIContent* aCurrentContent,
bool* aCheckSubDocument,
nsIContent** aResultContent);
bool TryToMoveFocusToSubDocument(nsIContent* aCurrentContent,
nsIContent* aOriginalStartContent,
bool aForward, bool aForDocumentNavigation,
bool aNavigateByKey,
nsIContent** aResultContent);
MOZ_CAN_RUN_SCRIPT bool TryToMoveFocusToSubDocument(
nsIContent* aCurrentContent, nsIContent* aOriginalStartContent,
bool aForward, bool aForDocumentNavigation, bool aNavigateByKey,
nsIContent** aResultContent);
// Sets the focused BrowsingContext and, if appropriate, syncs it to
// other processes.

View file

@ -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)
@ -4571,9 +4562,9 @@ void nsGlobalWindowInner::SetReadyForFocus() {
bool oldNeedsFocus = mNeedsFocus;
mNeedsFocus = false;
nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (fm) {
fm->WindowShown(GetOuterWindow(), oldNeedsFocus);
if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
nsCOMPtr<nsPIDOMWindowOuter> outerWindow = GetOuterWindow();
fm->WindowShown(outerWindow, oldNeedsFocus);
}
}
@ -4582,9 +4573,9 @@ void nsGlobalWindowInner::PageHidden() {
// no longer valid. Use the persisted field to determine if the document
// is being destroyed.
nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (fm) {
fm->WindowHidden(GetOuterWindow(), nsFocusManager::GenerateFocusActionId());
if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
nsCOMPtr<nsPIDOMWindowOuter> outerWindow = GetOuterWindow();
fm->WindowHidden(outerWindow, nsFocusManager::GenerateFocusActionId());
}
mNeedsFocus = true;

View file

@ -468,8 +468,8 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
#endif
virtual bool TakeFocus(bool aFocus, uint32_t aFocusMethod) override;
virtual void SetReadyForFocus() override;
virtual void PageHidden() override;
MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual void SetReadyForFocus() override;
MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual void PageHidden() override;
virtual nsresult DispatchAsyncHashchange(nsIURI* aOldURI,
nsIURI* aNewURI) override;
virtual nsresult DispatchSyncPopState() override;

View file

@ -4986,8 +4986,8 @@ void nsGlobalWindowOuter::PromptOuter(const nsAString& aMessage,
void nsGlobalWindowOuter::FocusOuter(CallerType aCallerType,
bool aFromOtherProcess,
uint64_t aActionId) {
nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (!fm) {
RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
if (MOZ_UNLIKELY(!fm)) {
return;
}
@ -5027,7 +5027,8 @@ void nsGlobalWindowOuter::FocusOuter(CallerType aCallerType,
if (parent) {
if (!parent->IsInProcess()) {
if (isActive) {
fm->WindowRaised(this, aActionId);
OwningNonNull<nsGlobalWindowOuter> kungFuDeathGrip(*this);
fm->WindowRaised(kungFuDeathGrip, aActionId);
} else {
ContentChild* contentChild = ContentChild::GetSingleton();
MOZ_ASSERT(contentChild);
@ -5049,7 +5050,8 @@ void nsGlobalWindowOuter::FocusOuter(CallerType aCallerType,
// if there is no parent, this must be a toplevel window, so raise the
// window if canFocus is true. If this is a child process, the raise
// window request will get forwarded to the parent by the puppet widget.
fm->RaiseWindow(this, aCallerType, aActionId);
OwningNonNull<nsGlobalWindowOuter> kungFuDeathGrip(*this);
fm->RaiseWindow(kungFuDeathGrip, aCallerType, aActionId);
}
}
@ -5072,13 +5074,15 @@ void nsGlobalWindowOuter::BlurOuter(CallerType aCallerType) {
siteWindow->Blur();
// if the root is focused, clear the focus
nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (fm && mDoc) {
RefPtr<Element> element;
fm->GetFocusedElementForWindow(this, false, nullptr,
getter_AddRefs(element));
if (element == mDoc->GetRootElement()) {
fm->ClearFocus(this);
if (mDoc) {
if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
RefPtr<Element> element;
fm->GetFocusedElementForWindow(this, false, nullptr,
getter_AddRefs(element));
if (element == mDoc->GetRootElement()) {
OwningNonNull<nsGlobalWindowOuter> kungFuDeathGrip(*this);
fm->ClearFocus(kungFuDeathGrip);
}
}
}
}

View file

@ -537,10 +537,16 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget,
bool GetClosedOuter();
bool Closed() override;
void StopOuter(mozilla::ErrorResult& aError);
void FocusOuter(mozilla::dom::CallerType aCallerType, bool aFromOtherProcess,
uint64_t aActionId);
// TODO: Convert FocusOuter() to MOZ_CAN_RUN_SCRIPT and get rid of the
// kungFuDeathGrip in it.
MOZ_CAN_RUN_SCRIPT_BOUNDARY void FocusOuter(
mozilla::dom::CallerType aCallerType, bool aFromOtherProcess,
uint64_t aActionId);
nsresult Focus(mozilla::dom::CallerType aCallerType) override;
void BlurOuter(mozilla::dom::CallerType aCallerType);
// TODO: Convert BlurOuter() to MOZ_CAN_RUN_SCRIPT and get rid of the
// kungFuDeathGrip in it.
MOZ_CAN_RUN_SCRIPT_BOUNDARY void BlurOuter(
mozilla::dom::CallerType aCallerType);
mozilla::dom::WindowProxyHolder GetFramesOuter();
uint32_t Length();
mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> GetTopOuter();

View file

@ -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_) \

View file

@ -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;
}
}
}

View file

@ -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.

View file

@ -528,8 +528,7 @@ RefPtr<ClientOpPromise> ClientSource::Focus(const ClientFocusArgs& aArgs) {
rv.ThrowNotSupportedError("Not a Window client");
return ClientOpPromise::CreateAndReject(rv, __func__);
}
nsPIDOMWindowOuter* outer = nullptr;
nsCOMPtr<nsPIDOMWindowOuter> outer;
nsPIDOMWindowInner* inner = GetInnerWindow();
if (inner) {
outer = inner->GetOuterWindow();

View file

@ -139,7 +139,9 @@ class ClientSource final : public ClientThing<ClientSourceChild> {
// clients.
void NoteDOMContentLoaded();
RefPtr<ClientOpPromise> Focus(const ClientFocusArgs& aArgs);
// TODO: Convert Focus() to MOZ_CAN_RUN_SCRIPT
MOZ_CAN_RUN_SCRIPT_BOUNDARY RefPtr<ClientOpPromise> Focus(
const ClientFocusArgs& aArgs);
RefPtr<ClientOpPromise> PostMessage(const ClientPostMessageArgs& aArgs);

View file

@ -166,14 +166,16 @@ nsresult ContentEventHandler::RawRange::SetStartAndEnd(
}
nsresult ContentEventHandler::RawRange::SelectNodeContents(
nsINode* aNodeToSelectContents) {
nsINode* newRoot = RangeUtils::ComputeRootNode(aNodeToSelectContents);
const nsINode* aNodeToSelectContents) {
nsINode* const newRoot =
RangeUtils::ComputeRootNode(const_cast<nsINode*>(aNodeToSelectContents));
if (!newRoot) {
return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
}
mRoot = newRoot;
mStart = RawRangeBoundary(aNodeToSelectContents, nullptr);
mEnd = RawRangeBoundary(aNodeToSelectContents,
mStart =
RawRangeBoundary(const_cast<nsINode*>(aNodeToSelectContents), nullptr);
mEnd = RawRangeBoundary(const_cast<nsINode*>(aNodeToSelectContents),
aNodeToSelectContents->GetLastChild());
return NS_OK;
}
@ -265,10 +267,11 @@ nsresult ContentEventHandler::InitRootContent(
if (!aNormalSelection.RangeCount()) {
// If there is no selection range, we should compute the selection root
// from ancestor limiter or root content of the document.
mRootContent = aNormalSelection.GetAncestorLimiter();
if (!mRootContent) {
mRootContent = mDocument->GetRootElement();
if (NS_WARN_IF(!mRootContent)) {
mRootElement =
Element::FromNodeOrNull(aNormalSelection.GetAncestorLimiter());
if (!mRootElement) {
mRootElement = mDocument->GetRootElement();
if (NS_WARN_IF(!mRootElement)) {
return NS_ERROR_NOT_AVAILABLE;
}
}
@ -301,8 +304,9 @@ nsresult ContentEventHandler::InitRootContent(
"firstNormalSelectionRange crosses the document boundary");
RefPtr<PresShell> presShell = mDocument->GetPresShell();
mRootContent = startNode->GetSelectionRootContent(presShell);
if (NS_WARN_IF(!mRootContent)) {
mRootElement =
Element::FromNodeOrNull(startNode->GetSelectionRootContent(presShell));
if (NS_WARN_IF(!mRootElement)) {
return NS_ERROR_FAILURE;
}
@ -317,7 +321,7 @@ nsresult ContentEventHandler::InitCommon(EventMessage aEventMessage,
}
mSelection = nullptr;
mRootContent = nullptr;
mRootElement = nullptr;
mFirstSelectedRawRange.Clear();
nsresult rv = InitBasic(aRequireFlush);
@ -366,7 +370,7 @@ nsresult ContentEventHandler::InitCommon(EventMessage aEventMessage,
// But otherwise, we need to assume that there is a selection range at the
// beginning of the root content if aSelectionType is eNormal.
rv = mFirstSelectedRawRange.CollapseTo(RawRangeBoundary(mRootContent, 0u));
rv = mFirstSelectedRawRange.CollapseTo(RawRangeBoundary(mRootElement, 0u));
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_UNEXPECTED;
}
@ -429,14 +433,14 @@ nsresult ContentEventHandler::Init(WidgetQueryContentEvent* aEvent) {
// corresponding handler returns error.
aEvent->EmplaceReply();
aEvent->mReply->mContentsRoot = mRootContent.get();
aEvent->mReply->mContentsRoot = mRootElement.get();
aEvent->mReply->mIsEditableContent =
mRootContent && mRootContent->IsEditable();
mRootElement && mRootElement->IsEditable();
nsRect r;
nsIFrame* frame = nsCaret::GetGeometry(mSelection, &r);
if (!frame) {
frame = mRootContent->GetPrimaryFrame();
frame = mRootElement->GetPrimaryFrame();
if (NS_WARN_IF(!frame)) {
return NS_ERROR_FAILURE;
}
@ -637,10 +641,10 @@ static uint32_t ConvertToXPOffset(const Text& aTextNode,
}
/* static */
bool ContentEventHandler::ShouldBreakLineBefore(
const nsIContent& aContent, const nsINode* aRootNode /* = nullptr */) {
bool ContentEventHandler::ShouldBreakLineBefore(const nsIContent& aContent,
const Element* aRootElement) {
// We don't need to append linebreak at the start of the root element.
if (&aContent == aRootNode) {
if (&aContent == aRootElement) {
return false;
}
@ -683,11 +687,11 @@ bool ContentEventHandler::ShouldBreakLineBefore(
}
nsresult ContentEventHandler::GenerateFlatTextContent(
nsIContent* aContent, nsString& aString, LineBreakType aLineBreakType) {
const Element* aElement, nsString& aString, LineBreakType aLineBreakType) {
MOZ_ASSERT(aString.IsEmpty());
RawRange rawRange;
nsresult rv = rawRange.SelectNodeContents(aContent);
nsresult rv = rawRange.SelectNodeContents(aElement);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -740,7 +744,7 @@ nsresult ContentEventHandler::GenerateFlatTextContent(
} else {
AppendString(aString, *textNode);
}
} else if (ShouldBreakLineBefore(*node->AsContent(), mRootContent)) {
} else if (ShouldBreakLineBefore(*node->AsContent(), mRootElement)) {
aString.Append(char16_t('\n'));
}
}
@ -909,7 +913,7 @@ nsresult ContentEventHandler::GenerateFlatFontRanges(
endOffset, aLineBreakType);
baseOffset += GetTextLengthInRange(*textNode, startOffset, endOffset,
aLineBreakType);
} else if (ShouldBreakLineBefore(*content, mRootContent)) {
} else if (ShouldBreakLineBefore(*content, mRootElement)) {
if (aFontRanges.IsEmpty()) {
MOZ_ASSERT(baseOffset == 0);
FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
@ -1008,15 +1012,15 @@ nsresult ContentEventHandler::SetRawRangeFromFlatTextOffset(
}
// Special case like <br contenteditable>
if (!mRootContent->HasChildren()) {
nsresult rv = aRawRange->CollapseTo(RawRangeBoundary(mRootContent, 0u));
if (!mRootElement->HasChildren()) {
nsresult rv = aRawRange->CollapseTo(RawRangeBoundary(mRootElement, 0u));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
PreContentIterator preOrderIter;
nsresult rv = preOrderIter.Init(mRootContent);
nsresult rv = preOrderIter.Init(mRootElement);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -1029,8 +1033,8 @@ nsresult ContentEventHandler::SetRawRangeFromFlatTextOffset(
if (NS_WARN_IF(!node)) {
break;
}
// FYI: mRootContent shouldn't cause any text. So, we can skip it simply.
if (node == mRootContent || !node->IsContent()) {
// FYI: mRootElement shouldn't cause any text. So, we can skip it simply.
if (node == mRootElement || !node->IsContent()) {
continue;
}
nsIContent* const content = node->AsContent();
@ -1043,7 +1047,7 @@ nsresult ContentEventHandler::SetRawRangeFromFlatTextOffset(
uint32_t textLength = contentAsText
? GetTextLength(*contentAsText, aLineBreakType)
: (ShouldBreakLineBefore(*content, mRootContent)
: (ShouldBreakLineBefore(*content, mRootElement)
? GetBRLength(aLineBreakType)
: 0);
if (!textLength) {
@ -1174,7 +1178,7 @@ nsresult ContentEventHandler::SetRawRangeFromFlatTextOffset(
}
if (content->HasChildren() &&
ShouldBreakLineBefore(*content, mRootContent)) {
ShouldBreakLineBefore(*content, mRootElement)) {
// Rule #2.3: </element>]
rv = aRawRange->SetEnd(content, 0);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -1205,19 +1209,18 @@ nsresult ContentEventHandler::SetRawRangeFromFlatTextOffset(
}
if (!startSet) {
MOZ_ASSERT(!mRootContent->IsText());
if (!offset) {
// Rule #1.5: <root>[</root>
// When there are no nodes causing text, the start of the DOM range
// should be start of the root node since clicking on such editor (e.g.,
// <div contenteditable><span></span></div>) sets caret to the start of
// the editor (i.e., before <span> in the example).
rv = aRawRange->SetStart(mRootContent, 0);
rv = aRawRange->SetStart(mRootElement, 0);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!aLength) {
rv = aRawRange->SetEnd(mRootContent, 0);
rv = aRawRange->SetEnd(mRootElement, 0);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -1225,7 +1228,7 @@ nsresult ContentEventHandler::SetRawRangeFromFlatTextOffset(
}
} else {
// Rule #1.5: [</root>
rv = aRawRange->SetStart(mRootContent, mRootContent->GetChildCount());
rv = aRawRange->SetStart(mRootElement, mRootElement->GetChildCount());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -1235,7 +1238,7 @@ nsresult ContentEventHandler::SetRawRangeFromFlatTextOffset(
}
}
// Rule #2.5: ]</root>
rv = aRawRange->SetEnd(mRootContent, mRootContent->GetChildCount());
rv = aRawRange->SetEnd(mRootElement, mRootElement->GetChildCount());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -1339,8 +1342,8 @@ nsresult ContentEventHandler::OnQuerySelectedText(
nsINode* const endNode = mFirstSelectedRawRange.GetEndContainer();
// Make sure the selection is within the root content range.
if (!startNode->IsInclusiveDescendantOf(mRootContent) ||
!endNode->IsInclusiveDescendantOf(mRootContent)) {
if (!startNode->IsInclusiveDescendantOf(mRootElement) ||
!endNode->IsInclusiveDescendantOf(mRootElement)) {
return NS_ERROR_NOT_AVAILABLE;
}
@ -1505,7 +1508,7 @@ ContentEventHandler::GetFirstFrameInRangeForTextRect(
// If the element node causes a line break before it, it's the first
// node causing text.
if (ShouldBreakLineBefore(*node->AsContent(), mRootContent) ||
if (ShouldBreakLineBefore(*node->AsContent(), mRootElement) ||
IsPaddingBR(*node->AsContent())) {
nodePosition = {node, 0u};
}
@ -1593,7 +1596,7 @@ ContentEventHandler::GetLastFrameInRangeForTextRect(const RawRange& aRawRange) {
break;
}
if (ShouldBreakLineBefore(*node->AsContent(), mRootContent) ||
if (ShouldBreakLineBefore(*node->AsContent(), mRootElement) ||
IsPaddingBR(*node->AsContent())) {
nodePosition = {node, 0u};
break;
@ -1655,7 +1658,7 @@ ContentEventHandler::GetLineBreakerRectBefore(nsIFrame* aFrame) {
// open tag causes a line break or moz-<br> for computing empty last line's
// rect.
MOZ_ASSERT(aFrame->GetContent());
MOZ_ASSERT(ShouldBreakLineBefore(*aFrame->GetContent(), mRootContent) ||
MOZ_ASSERT(ShouldBreakLineBefore(*aFrame->GetContent(), mRootElement) ||
IsPaddingBR(*aFrame->GetContent()));
nsIFrame* frameForFontMetrics = aFrame;
@ -1846,7 +1849,7 @@ nsresult ContentEventHandler::OnQueryTextRectArray(
// the end of contents.
if (!firstFrame.IsValid()) {
nsAutoString allText;
rv = GenerateFlatTextContent(mRootContent, allText, lineBreakType);
rv = GenerateFlatTextContent(mRootElement, allText, lineBreakType);
// If the offset doesn't reach the end of contents yet but there is no
// frames for the node, that means that current offset's node is hidden
// by CSS or something. Ideally, we should handle it with the last
@ -1914,7 +1917,7 @@ nsresult ContentEventHandler::OnQueryTextRectArray(
// Note that moz-<br> element does not cause any text, however,
// it represents empty line at the last of current block. Therefore,
// we need to compute its rect too.
else if (ShouldBreakLineBefore(*firstContent, mRootContent) ||
else if (ShouldBreakLineBefore(*firstContent, mRootElement) ||
IsPaddingBR(*firstContent)) {
nsRect brRect;
// If the frame is not a <br> frame, we need to compute the caret rect
@ -2170,7 +2173,7 @@ nsresult ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent) {
// the end of contents.
if (!firstFrame.IsValid()) {
nsAutoString allText;
rv = GenerateFlatTextContent(mRootContent, allText, lineBreakType);
rv = GenerateFlatTextContent(mRootElement, allText, lineBreakType);
// If the offset doesn't reach the end of contents but there is no frames
// for the node, that means that current offset's node is hidden by CSS or
// something. Ideally, we should handle it with the last visible text
@ -2182,7 +2185,7 @@ nsresult ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent) {
}
// Look for the last frame which should be included text rects.
rv = rawRange.SelectNodeContents(mRootContent);
rv = rawRange.SelectNodeContents(mRootElement);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_UNEXPECTED;
}
@ -2226,10 +2229,10 @@ nsresult ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent) {
}
aEvent->mReply->mWritingMode = lastFrame->GetWritingMode();
}
// Otherwise, if there are no contents in mRootContent, guess caret rect in
// Otherwise, if there are no contents in mRootElement, guess caret rect in
// its frame (with its font height and content box).
else {
nsIFrame* rootContentFrame = mRootContent->GetPrimaryFrame();
nsIFrame* rootContentFrame = mRootElement->GetPrimaryFrame();
if (NS_WARN_IF(!rootContentFrame)) {
return NS_ERROR_FAILURE;
}
@ -2459,7 +2462,7 @@ nsresult ContentEventHandler::OnQueryEditorRect(
return rv;
}
if (NS_WARN_IF(NS_FAILED(QueryContentRect(mRootContent, aEvent)))) {
if (NS_WARN_IF(NS_FAILED(QueryContentRect(mRootElement, aEvent)))) {
return NS_ERROR_FAILURE;
}
@ -2613,7 +2616,7 @@ nsresult ContentEventHandler::OnQueryCharacterAtPoint(
nsIFrame* targetFrame =
nsLayoutUtils::GetFrameForPoint(RelativeTo{rootFrame}, ptInRoot);
if (!targetFrame || !targetFrame->GetContent() ||
!targetFrame->GetContent()->IsInclusiveDescendantOf(mRootContent)) {
!targetFrame->GetContent()->IsInclusiveDescendantOf(mRootElement)) {
// There is no character at the point.
MOZ_ASSERT(aEvent->Succeeded());
return NS_OK;
@ -2626,7 +2629,7 @@ nsresult ContentEventHandler::OnQueryCharacterAtPoint(
nsIFrame::ContentOffsets tentativeCaretOffsets =
targetFrame->GetContentOffsetsFromPoint(ptInTarget);
if (!tentativeCaretOffsets.content ||
!tentativeCaretOffsets.content->IsInclusiveDescendantOf(mRootContent)) {
!tentativeCaretOffsets.content->IsInclusiveDescendantOf(mRootElement)) {
// There is no character nor tentative caret point at the point.
MOZ_ASSERT(aEvent->Succeeded());
return NS_OK;
@ -2634,8 +2637,8 @@ nsresult ContentEventHandler::OnQueryCharacterAtPoint(
uint32_t tentativeCaretOffset = 0;
if (NS_WARN_IF(NS_FAILED(GetFlatTextLengthInRange(
NodePosition(mRootContent, 0u), NodePosition(tentativeCaretOffsets),
mRootContent, &tentativeCaretOffset, GetLineBreakType(aEvent))))) {
NodePosition(mRootElement, 0u), NodePosition(tentativeCaretOffsets),
mRootElement, &tentativeCaretOffset, GetLineBreakType(aEvent))))) {
return NS_ERROR_FAILURE;
}
@ -2652,8 +2655,8 @@ nsresult ContentEventHandler::OnQueryCharacterAtPoint(
NS_ENSURE_TRUE(contentOffsets.content, NS_ERROR_FAILURE);
uint32_t offset = 0;
if (NS_WARN_IF(NS_FAILED(GetFlatTextLengthInRange(
NodePosition(mRootContent, 0u), NodePosition(contentOffsets),
mRootContent, &offset, GetLineBreakType(aEvent))))) {
NodePosition(mRootElement, 0u), NodePosition(contentOffsets),
mRootElement, &offset, GetLineBreakType(aEvent))))) {
return NS_ERROR_FAILURE;
}
@ -2717,9 +2720,9 @@ nsresult ContentEventHandler::OnQueryDOMWidgetHittest(
/* static */
nsresult ContentEventHandler::GetFlatTextLengthInRange(
const NodePosition& aStartPosition, const NodePosition& aEndPosition,
nsIContent* aRootContent, uint32_t* aLength, LineBreakType aLineBreakType,
bool aIsRemovingNode /* = false */) {
if (NS_WARN_IF(!aRootContent) || NS_WARN_IF(!aStartPosition.IsSet()) ||
const Element* aRootElement, uint32_t* aLength,
LineBreakType aLineBreakType, bool aIsRemovingNode /* = false */) {
if (NS_WARN_IF(!aRootElement) || NS_WARN_IF(!aStartPosition.IsSet()) ||
NS_WARN_IF(!aEndPosition.IsSet()) || NS_WARN_IF(!aLength)) {
return NS_ERROR_INVALID_ARG;
}
@ -2769,7 +2772,7 @@ nsresult ContentEventHandler::GetFlatTextLengthInRange(
// When the end position is immediately after non-root element's open tag,
// we need to include a line break caused by the open tag.
if (endPosition.Container() != aRootContent &&
if (endPosition.Container() != aRootElement &&
endPosition.IsImmediatelyAfterOpenTag()) {
if (endPosition.Container()->HasChildren()) {
// When the end node has some children, move the end position to before
@ -2806,7 +2809,7 @@ nsresult ContentEventHandler::GetFlatTextLengthInRange(
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else if (endPosition.Container() != aRootContent) {
} else if (endPosition.Container() != aRootElement) {
// Offset is past node's length; set end of range to end of node
rv = prevRawRange.SetEndAfter(endPosition.Container());
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -2819,7 +2822,7 @@ nsresult ContentEventHandler::GetFlatTextLengthInRange(
}
} else {
// Offset is past the root node; set end of range to end of root node
rv = preOrderIter.Init(aRootContent);
rv = preOrderIter.Init(const_cast<Element*>(aRootElement));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -2849,7 +2852,7 @@ nsresult ContentEventHandler::GetFlatTextLengthInRange(
} else {
*aLength += GetTextLength(*textNode, aLineBreakType);
}
} else if (ShouldBreakLineBefore(*content, aRootContent)) {
} else if (ShouldBreakLineBefore(*content, aRootElement)) {
// If the start position is start of this node but doesn't include the
// open tag, don't append the line break length.
if (node == aStartPosition.Container() &&
@ -2893,8 +2896,8 @@ nsresult ContentEventHandler::GetStartOffset(const RawRange& aRawRange,
const NodePosition& startPos =
startIsContainer ? NodePosition(startNode, aRawRange.StartOffset())
: NodePositionBefore(startNode, aRawRange.StartOffset());
return GetFlatTextLengthInRange(NodePosition(mRootContent, 0u), startPos,
mRootContent, aOffset, aLineBreakType);
return GetFlatTextLengthInRange(NodePosition(mRootElement, 0u), startPos,
mRootElement, aOffset, aLineBreakType);
}
nsresult ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(
@ -2975,11 +2978,13 @@ nsresult ContentEventHandler::ConvertToRootRelativeOffset(nsIFrame* aFrame,
return NS_OK;
}
static void AdjustRangeForSelection(nsIContent* aRoot, nsINode** aNode,
static void AdjustRangeForSelection(const Element* aRootElement,
nsINode** aNode,
Maybe<uint32_t>* aNodeOffset) {
nsINode* node = *aNode;
Maybe<uint32_t> nodeOffset = *aNodeOffset;
if (aRoot == node || NS_WARN_IF(!node->GetParent()) || !node->IsText()) {
if (aRootElement == node || NS_WARN_IF(!node->GetParent()) ||
!node->IsText()) {
return;
}
@ -2993,15 +2998,15 @@ static void AdjustRangeForSelection(nsIContent* aRoot, nsINode** aNode,
return;
}
nsIContent* aRootParent = aRoot->GetParent();
if (NS_WARN_IF(!aRootParent)) {
Element* rootParentElement = aRootElement->GetParentElement();
if (NS_WARN_IF(!rootParentElement)) {
return;
}
// If the root node is not an anonymous div of <textarea>, we don't need to
// do this hack. If you did this, ContentEventHandler couldn't distinguish
// if the range includes open tag of the next node in some cases, e.g.,
// textNode]<p></p> vs. textNode<p>]</p>
if (!aRootParent->IsHTMLElement(nsGkAtoms::textarea)) {
if (!rootParentElement->IsHTMLElement(nsGkAtoms::textarea)) {
return;
}
@ -3028,10 +3033,8 @@ nsresult ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent) {
// Get selection to manipulate
// XXX why do we need to get them from ISM? This method should work fine
// without ISM.
RefPtr<Selection> sel;
nsresult rv = IMEStateManager::GetFocusSelectionAndRoot(
getter_AddRefs(sel), getter_AddRefs(mRootContent));
mSelection = sel;
nsresult rv = IMEStateManager::GetFocusSelectionAndRootElement(
getter_AddRefs(mSelection), getter_AddRefs(mRootElement));
if (rv != NS_ERROR_NOT_AVAILABLE) {
NS_ENSURE_SUCCESS(rv, rv);
} else {
@ -3050,8 +3053,8 @@ nsresult ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent) {
nsINode* endNode = rawRange.GetEndContainer();
Maybe<uint32_t> startNodeOffset = Some(rawRange.StartOffset());
Maybe<uint32_t> endNodeOffset = Some(rawRange.EndOffset());
AdjustRangeForSelection(mRootContent, &startNode, &startNodeOffset);
AdjustRangeForSelection(mRootContent, &endNode, &endNodeOffset);
AdjustRangeForSelection(mRootElement, &startNode, &startNodeOffset);
AdjustRangeForSelection(mRootElement, &endNode, &endNodeOffset);
if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode) ||
NS_WARN_IF(startNodeOffset.isNothing()) ||
NS_WARN_IF(endNodeOffset.isNothing())) {

View file

@ -21,6 +21,7 @@ struct nsRect;
namespace mozilla {
namespace dom {
class Element;
class Text;
} // namespace dom
@ -89,7 +90,7 @@ class MOZ_STACK_CLASS ContentEventHandler {
nsresult SetStartAndEnd(const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd);
nsresult SelectNodeContents(nsINode* aNodeToSelectContents);
nsresult SelectNodeContents(const nsINode* aNodeToSelectContents);
private:
inline void AssertStartIsBeforeOrEqualToEnd();
@ -101,6 +102,7 @@ class MOZ_STACK_CLASS ContentEventHandler {
};
public:
using Element = dom::Element;
using Selection = dom::Selection;
explicit ContentEventHandler(nsPresContext* aPresContext);
@ -150,7 +152,7 @@ class MOZ_STACK_CLASS ContentEventHandler {
// mFirstSelectedRawRange is initialized from the first range of mSelection,
// if it exists. Otherwise, it is reset by Clear().
RawRange mFirstSelectedRawRange;
nsCOMPtr<nsIContent> mRootContent;
RefPtr<Element> mRootElement;
MOZ_CAN_RUN_SCRIPT nsresult Init(WidgetQueryContentEvent* aEvent);
MOZ_CAN_RUN_SCRIPT nsresult Init(WidgetSelectionEvent* aEvent);
@ -227,8 +229,8 @@ class MOZ_STACK_CLASS ContentEventHandler {
// Get the flatten text length in the range.
// @param aStartPosition Start node and offset in the node of the range.
// @param aEndPosition End node and offset in the node of the range.
// @param aRootContent The root content of the editor or document.
// aRootContent won't cause any text including
// @param aRootElement The root element of the editor or document.
// aRootElement won't cause any text including
// line breaks.
// @param aLength The result of the flatten text length of the
// range.
@ -244,7 +246,7 @@ class MOZ_STACK_CLASS ContentEventHandler {
// be number of the children of mNode.
static nsresult GetFlatTextLengthInRange(const NodePosition& aStartPosition,
const NodePosition& aEndPosition,
nsIContent* aRootContent,
const Element* aRootElement,
uint32_t* aLength,
LineBreakType aLineBreakType,
bool aIsRemovingNode = false);
@ -268,12 +270,12 @@ class MOZ_STACK_CLASS ContentEventHandler {
uint32_t aXPStartOffset,
uint32_t aXPEndOffset,
LineBreakType aLineBreakType);
// Get the contents in aContent (meaning all children of aContent) as plain
// text. E.g., specifying mRootContent gets whole text in it.
// Get the contents in aElement (meaning all children of aElement) as plain
// text. E.g., specifying mRootElement gets whole text in it.
// Note that the result is not same as .textContent. The result is
// optimized for native IMEs. For example, <br> element and some block
// elements causes "\n" (or "\r\n"), see also ShouldBreakLineBefore().
nsresult GenerateFlatTextContent(nsIContent* aContent, nsString& aString,
nsresult GenerateFlatTextContent(const Element* aElement, nsString& aString,
LineBreakType aLineBreakType);
// Get the contents of aRange as plain text.
nsresult GenerateFlatTextContent(const RawRange& aRawRange, nsString& aString,
@ -287,7 +289,7 @@ class MOZ_STACK_CLASS ContentEventHandler {
// This should return false only when aContent is an html element which
// is typically used in a paragraph like <em>.
static bool ShouldBreakLineBefore(const nsIContent& aContent,
const nsINode* aRootNode = nullptr);
const Element* aRootElement);
// Get the line breaker length.
static inline uint32_t GetBRLength(LineBreakType aLineBreakType);
static LineBreakType GetLineBreakType(WidgetQueryContentEvent* aEvent);

View file

@ -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)

View file

@ -3501,10 +3501,11 @@ nsresult EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
// focused frame
EnsureDocument(mPresContext);
if (mDocument) {
nsCOMPtr<nsPIDOMWindowOuter> outerWindow = mDocument->GetWindow();
#ifdef XP_MACOSX
if (!activeContent || !activeContent->IsXULElement())
#endif
fm->ClearFocus(mDocument->GetWindow());
fm->ClearFocus(outerWindow);
// Prevent switch frame if we're already not in the foreground tab
// and we're in a content process.
// TODO: If we were inactive frame in this tab, and now in
@ -3514,7 +3515,7 @@ nsresult EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
// for doing this. Therefore, we should skip setting focus
// to clicked document for now.
if (XRE_IsParentProcess() || IsInActiveTab(mDocument)) {
fm->SetFocusedWindow(mDocument->GetWindow());
fm->SetFocusedWindow(outerWindow);
}
}
}
@ -3968,7 +3969,10 @@ bool EventStateManager::IsTargetCrossProcess(WidgetGUIEvent* aEvent) {
}
void EventStateManager::NotifyDestroyPresContext(nsPresContext* aPresContext) {
IMEStateManager::OnDestroyPresContext(aPresContext);
RefPtr<nsPresContext> presContext = aPresContext;
if (presContext) {
IMEStateManager::OnDestroyPresContext(*presContext);
}
if (mHoverContent) {
// Bug 70855: Presentation is going away, possibly for a reframe.
// Reset the hover state so that if we're recreating the presentation,
@ -3977,7 +3981,7 @@ void EventStateManager::NotifyDestroyPresContext(nsPresContext* aPresContext) {
SetContentState(nullptr, NS_EVENT_STATE_HOVER);
}
mPointersEnterLeaveHelper.Clear();
PointerEventHandler::NotifyDestroyPresContext(aPresContext);
PointerEventHandler::NotifyDestroyPresContext(presContext);
}
void EventStateManager::SetPresContext(nsPresContext* aPresContext) {
@ -5540,12 +5544,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,
@ -5823,12 +5821,18 @@ void EventStateManager::ContentRemoved(Document* aDocument,
element->LeaveLink(element->GetPresContext(Element::eForComposedDoc));
}
IMEStateManager::OnRemoveContent(mPresContext, aContent);
if (aContent->IsElement()) {
if (RefPtr<nsPresContext> presContext = mPresContext) {
IMEStateManager::OnRemoveContent(*presContext,
MOZ_KnownLive(*aContent->AsElement()));
}
}
// inform the focus manager that the content is being removed. If this
// content is focused, the focus will be removed without firing events.
nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (fm) fm->ContentRemoved(aDocument, aContent);
if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
fm->ContentRemoved(aDocument, aContent);
}
RemoveNodeFromChainIfNeeded(NS_EVENT_STATE_HOVER, aContent, true);
RemoveNodeFromChainIfNeeded(NS_EVENT_STATE_ACTIVE, aContent, true);

View file

@ -130,7 +130,8 @@ class EventStateManager : public nsSupportsWeakReference, public nsIObserver {
MOZ_CAN_RUN_SCRIPT_BOUNDARY void DispatchLegacyMouseScrollEvents(
nsIFrame* aTargetFrame, WidgetWheelEvent* aEvent, nsEventStatus* aStatus);
void NotifyDestroyPresContext(nsPresContext* aPresContext);
MOZ_CAN_RUN_SCRIPT_BOUNDARY void NotifyDestroyPresContext(
nsPresContext* aPresContext);
void SetPresContext(nsPresContext* aPresContext);
void ClearFrameRefs(nsIFrame* aFrame);
@ -158,7 +159,8 @@ class EventStateManager : public nsSupportsWeakReference, public nsIObserver {
bool SetContentState(nsIContent* aContent, EventStates aState);
void NativeAnonymousContentRemoved(nsIContent* aAnonContent);
void ContentRemoved(dom::Document* aDocument, nsIContent* aContent);
MOZ_CAN_RUN_SCRIPT_BOUNDARY void ContentRemoved(dom::Document* aDocument,
nsIContent* aContent);
/**
* Called when a native anonymous <div> element which is root element of
@ -286,9 +288,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);

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