Update On Thu May 26 20:31:23 CEST 2022
This commit is contained in:
parent
9d52f5e324
commit
f0bf5d908a
412 changed files with 18315 additions and 13152 deletions
|
@ -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 {
|
||||
|
|
|
@ -2,6 +2,4 @@
|
|||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!ENTITY brandShorterName "Firefox">
|
||||
<!ENTITY brandShortName "Firefox Developer Edition">
|
||||
<!ENTITY brandFullName "Firefox Developer Edition">
|
||||
|
|
|
@ -2,6 +2,4 @@
|
|||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!ENTITY brandShorterName "Nightly">
|
||||
<!ENTITY brandShortName "Nightly">
|
||||
<!ENTITY brandFullName "Firefox Nightly">
|
||||
|
|
|
@ -2,6 +2,4 @@
|
|||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!ENTITY brandShorterName "Firefox">
|
||||
<!ENTITY brandShortName "Firefox">
|
||||
<!ENTITY brandFullName "Mozilla Firefox">
|
||||
|
|
|
@ -2,6 +2,4 @@
|
|||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!ENTITY brandShorterName "Nightly">
|
||||
<!ENTITY brandShortName "Nightly">
|
||||
<!ENTITY brandFullName "Nightly">
|
||||
|
|
|
@ -44,10 +44,12 @@ class ColorwaySelector extends HTMLFieldSetElement {
|
|||
for (let input of this.children) {
|
||||
if (input.value == this.activeTheme.id) {
|
||||
input.classList.add("active");
|
||||
input.setAttribute("aria-current", true);
|
||||
this.updateName(this.selectedTheme.name);
|
||||
this.updateDescription(input.value);
|
||||
} else {
|
||||
input.classList.remove("active");
|
||||
input.setAttribute("aria-current", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +59,7 @@ class ColorwaySelector extends HTMLFieldSetElement {
|
|||
input.type = "radio";
|
||||
input.name = "colorway";
|
||||
input.value = theme.id;
|
||||
input.setAttribute("title", theme.name);
|
||||
input.style.setProperty("--colorway-icon", `url(${theme.iconURL})`);
|
||||
input.onclick = () => {
|
||||
this.selectedTheme = theme;
|
||||
|
|
|
@ -40,6 +40,24 @@ body > header {
|
|||
padding: .2em 1em;
|
||||
}
|
||||
|
||||
#use-fx-home-controls:not(.success) > .success-prompt,
|
||||
#use-fx-home-controls.success > .reset-prompt {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#use-fx-home-controls > .success-prompt::before {
|
||||
display: inline-block;
|
||||
content: "";
|
||||
background: var(--green-50) url('chrome://global/skin/icons/check.svg') center center no-repeat;
|
||||
-moz-context-properties: fill;
|
||||
fill: white;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 15px;
|
||||
vertical-align: middle;
|
||||
margin-inline-end: 0.5em;
|
||||
}
|
||||
|
||||
body > section {
|
||||
grid-area: main;
|
||||
}
|
||||
|
|
|
@ -4,3 +4,7 @@
|
|||
|
||||
colorway-collection-life-in-color = Life In Color
|
||||
colorway-collection-true-colors = True Colors
|
||||
colorway-fx-home-link = Use { -brand-product-name } Home for colorful new tabs
|
||||
colorway-fx-home-link-success = { -brand-product-name } Home is now your home page
|
||||
colorway-fx-home-apply-button = Apply
|
||||
colorway-fx-home-undo-button = Undo
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
href="chrome://global/skin/in-content/common.css">
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://browser/content/colorwaycloset.css">
|
||||
<link rel="localization" href="browser/colorways.ftl"/>
|
||||
<link rel="localization" href="branding/brand.ftl">
|
||||
<link rel="localization" href="preview/colorwaycloset.ftl">
|
||||
<script src="chrome://browser/content/colorwaycloset.js" defer="async"></script>
|
||||
<script type="module" src="chrome://browser/content/ColorwayClosetSelector.js"></script>
|
||||
</head>
|
||||
|
@ -30,6 +31,16 @@
|
|||
<p id="colorway-description"></p>
|
||||
</div>
|
||||
</section>
|
||||
<section id="use-fx-home-controls" hidden>
|
||||
<div class="reset-prompt">
|
||||
<span data-l10n-id="colorway-fx-home-link"></span>
|
||||
<button data-l10n-id="colorway-fx-home-apply-button"></button>
|
||||
</div>
|
||||
<div class="success-prompt">
|
||||
<span data-l10n-id="colorway-fx-home-link-success"></span>
|
||||
<button data-l10n-id="colorway-fx-home-undo-button"></button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -5,19 +5,40 @@
|
|||
const { BuiltInThemes } = ChromeUtils.import(
|
||||
"resource:///modules/BuiltInThemes.jsm"
|
||||
);
|
||||
const { HomePage } = ChromeUtils.import("resource:///modules/HomePage.jsm");
|
||||
|
||||
function showUseFXHomeControls(fluentStrings) {
|
||||
let homeState;
|
||||
const useFXHomeControls = document.getElementById("use-fx-home-controls");
|
||||
useFXHomeControls.hidden = HomePage.isDefault;
|
||||
if (!HomePage.isDefault) {
|
||||
useFXHomeControls
|
||||
.querySelector(".reset-prompt > button")
|
||||
.addEventListener("click", () => {
|
||||
homeState = HomePage.get();
|
||||
HomePage.reset();
|
||||
useFXHomeControls.classList.add("success");
|
||||
});
|
||||
useFXHomeControls
|
||||
.querySelector(".success-prompt > button")
|
||||
.addEventListener("click", () => {
|
||||
HomePage.set(homeState);
|
||||
useFXHomeControls.classList.remove("success");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const collection = BuiltInThemes.findActiveColorwayCollection();
|
||||
if (collection) {
|
||||
const { expiry, l10nId } = collection;
|
||||
const fluentStrings = new Localization(["preview/colorwaycloset.ftl"], true);
|
||||
const formatter = new Intl.DateTimeFormat("default", {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
});
|
||||
document.getElementById(
|
||||
"collection-title"
|
||||
).innerText = fluentStrings.formatValueSync(l10nId);
|
||||
const collectionTitle = document.getElementById("collection-title");
|
||||
document.l10n.setAttributes(collectionTitle, l10nId);
|
||||
document.querySelector(
|
||||
"#collection-expiry-date > span"
|
||||
).innerText = formatter.format(expiry);
|
||||
showUseFXHomeControls();
|
||||
}
|
||||
|
|
|
@ -31,6 +31,18 @@ add_task(async function about_colorwaycloset_smoke_test() {
|
|||
document.getElementById("colorway-description"),
|
||||
"colorway description exists"
|
||||
);
|
||||
|
||||
const useFXHomeControls = document.getElementById("use-fx-home-controls");
|
||||
ok(useFXHomeControls, "firefox home controls exists");
|
||||
useFXHomeControls.toggleAttribute("hidden", false);
|
||||
ok(
|
||||
document.querySelector("#use-fx-home-controls > .reset-prompt"),
|
||||
"firefox home controls reset prompt exists"
|
||||
);
|
||||
ok(
|
||||
document.querySelector("#use-fx-home-controls > .success-prompt"),
|
||||
"firefox home controls reset prompt exists"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -339,9 +339,10 @@ this.chrome_settings_overrides = class extends ExtensionAPI {
|
|||
extension.startupReason
|
||||
);
|
||||
}
|
||||
// Ensure the item is disabled. If addSetting was called above,
|
||||
// Item may be null, and enabled may be undefined.
|
||||
if (disable && item?.enabled !== false) {
|
||||
|
||||
// Ensure the item is disabled (either if exists and is not default or if it does not
|
||||
// exist yet).
|
||||
if (disable) {
|
||||
item = await ExtensionSettingsStore.disable(
|
||||
extension.id,
|
||||
DEFAULT_SEARCH_STORE_TYPE,
|
||||
|
@ -466,6 +467,37 @@ this.chrome_settings_overrides = class extends ExtensionAPI {
|
|||
DEFAULT_SEARCH_STORE_TYPE,
|
||||
DEFAULT_SEARCH_SETTING_NAME
|
||||
);
|
||||
|
||||
// Check for an inconsistency between the value returned by getLevelOfcontrol
|
||||
// and the current engine actually set.
|
||||
if (
|
||||
control === "controlled_by_this_extension" &&
|
||||
Services.search.defaultEngine.name !== engineName
|
||||
) {
|
||||
// Check for and fix any inconsistency between the extensions settings storage
|
||||
// and the current engine actually set. If settings claims the extension is default
|
||||
// but the search service claims otherwise, select what the search service claims
|
||||
// (See Bug 1767550).
|
||||
const allSettings = ExtensionSettingsStore.getAllSettings(
|
||||
DEFAULT_SEARCH_STORE_TYPE,
|
||||
DEFAULT_SEARCH_SETTING_NAME
|
||||
);
|
||||
for (const setting of allSettings) {
|
||||
if (setting.value !== Services.search.defaultEngine.name) {
|
||||
await ExtensionSettingsStore.disable(
|
||||
setting.id,
|
||||
DEFAULT_SEARCH_STORE_TYPE,
|
||||
DEFAULT_SEARCH_SETTING_NAME
|
||||
);
|
||||
}
|
||||
}
|
||||
control = await ExtensionSettingsStore.getLevelOfControl(
|
||||
extension.id,
|
||||
DEFAULT_SEARCH_STORE_TYPE,
|
||||
DEFAULT_SEARCH_SETTING_NAME
|
||||
);
|
||||
}
|
||||
|
||||
if (control === "controlled_by_this_extension") {
|
||||
await Services.search.setDefault(
|
||||
Services.search.getEngineByName(engineName)
|
||||
|
|
|
@ -337,6 +337,47 @@ add_task(async function test_overrides_update_homepage_change() {
|
|||
await extension.unload();
|
||||
});
|
||||
|
||||
async function withHandlingDefaultSearchPrompt({ extensionId, respond }, cb) {
|
||||
const promptResponseHandled = TestUtils.topicObserved(
|
||||
"webextension-defaultsearch-prompt-response"
|
||||
);
|
||||
const prompted = TestUtils.topicObserved(
|
||||
"webextension-defaultsearch-prompt",
|
||||
(subject, message) => {
|
||||
if (subject.wrappedJSObject.id == extensionId) {
|
||||
return subject.wrappedJSObject.respond(respond);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
await Promise.all([cb(), prompted, promptResponseHandled]);
|
||||
}
|
||||
|
||||
async function assertUpdateDoNotPrompt(extension, updateExtensionInfo) {
|
||||
let deferredUpgradePrompt = topicObservable(
|
||||
"webextension-defaultsearch-prompt",
|
||||
(subject, message) => {
|
||||
if (subject.wrappedJSObject.id == extension.id) {
|
||||
ok(false, "should not prompt on update");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
await Promise.race([
|
||||
extension.upgrade(updateExtensionInfo),
|
||||
deferredUpgradePrompt.promise,
|
||||
]);
|
||||
deferredUpgradePrompt.resolve();
|
||||
|
||||
await AddonTestUtils.waitForSearchProviderStartup(extension);
|
||||
|
||||
equal(
|
||||
extension.version,
|
||||
updateExtensionInfo.manifest.version,
|
||||
"The updated addon has the expected version."
|
||||
);
|
||||
}
|
||||
|
||||
add_task(async function test_default_search_prompts() {
|
||||
/* This tests the scenario where an addon did not gain
|
||||
* default search during install, and later upgrades.
|
||||
|
@ -368,22 +409,15 @@ add_task(async function test_default_search_prompts() {
|
|||
|
||||
let extension = ExtensionTestUtils.loadExtension(extensionInfo);
|
||||
|
||||
// Mock a response from the default search prompt where we
|
||||
// say no to setting this as the default when installing.
|
||||
let prompted = TestUtils.topicObserved(
|
||||
"webextension-defaultsearch-prompt",
|
||||
(subject, message) => {
|
||||
if (subject.wrappedJSObject.id == extension.id) {
|
||||
return subject.wrappedJSObject.respond(false);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let defaultEngineName = (await Services.search.getDefault()).name;
|
||||
ok(defaultEngineName !== "Example", "Search is not Example.");
|
||||
|
||||
await extension.startup();
|
||||
await prompted;
|
||||
// Mock a response from the default search prompt where we
|
||||
// say no to setting this as the default when installing.
|
||||
await withHandlingDefaultSearchPrompt(
|
||||
{ extensionId: EXTENSION_ID, respond: false },
|
||||
() => extension.startup()
|
||||
);
|
||||
|
||||
equal(
|
||||
extension.version,
|
||||
|
@ -396,70 +430,354 @@ add_task(async function test_default_search_prompts() {
|
|||
"Default engine is the default after startup."
|
||||
);
|
||||
|
||||
extensionInfo.manifest = {
|
||||
version: "2.0",
|
||||
applications: {
|
||||
gecko: {
|
||||
id: EXTENSION_ID,
|
||||
},
|
||||
},
|
||||
chrome_settings_overrides: {
|
||||
search_provider: {
|
||||
name: "Example",
|
||||
search_url: "https://example.com/?q={searchTerms}",
|
||||
is_default: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let deferredUpgradePrompt = topicObservable(
|
||||
"webextension-defaultsearch-prompt",
|
||||
(subject, message) => {
|
||||
if (subject.wrappedJSObject.id == extension.id) {
|
||||
ok(false, "should not prompt on update");
|
||||
}
|
||||
}
|
||||
info(
|
||||
"Verify that updating the extension does not prompt and does not take over the default engine"
|
||||
);
|
||||
|
||||
await Promise.race([
|
||||
extension.upgrade(extensionInfo),
|
||||
deferredUpgradePrompt.promise,
|
||||
]);
|
||||
deferredUpgradePrompt.resolve();
|
||||
|
||||
await AddonTestUtils.waitForSearchProviderStartup(extension);
|
||||
|
||||
equal(
|
||||
extension.version,
|
||||
"2.0",
|
||||
"The updated addon has the expected version."
|
||||
);
|
||||
// An upgraded extension does not become the default engine.
|
||||
extensionInfo.manifest.version = "2.0";
|
||||
await assertUpdateDoNotPrompt(extension, extensionInfo);
|
||||
equal(
|
||||
(await Services.search.getDefault()).name,
|
||||
defaultEngineName,
|
||||
"Default engine is still the default after startup."
|
||||
"Default engine is still the default after update."
|
||||
);
|
||||
|
||||
info("Verify that disable/enable the extension does prompt the user");
|
||||
|
||||
let addon = await AddonManager.getAddonByID(EXTENSION_ID);
|
||||
await addon.disable();
|
||||
|
||||
prompted = TestUtils.topicObserved(
|
||||
"webextension-defaultsearch-prompt",
|
||||
(subject, message) => {
|
||||
if (subject.wrappedJSObject.id == extension.id) {
|
||||
return subject.wrappedJSObject.respond(false);
|
||||
}
|
||||
await withHandlingDefaultSearchPrompt(
|
||||
{ extensionId: EXTENSION_ID, respond: false },
|
||||
async () => {
|
||||
await addon.disable();
|
||||
await addon.enable();
|
||||
}
|
||||
);
|
||||
await Promise.all([addon.enable(), prompted]);
|
||||
|
||||
// we still said no.
|
||||
equal(
|
||||
(await Services.search.getDefault()).name,
|
||||
defaultEngineName,
|
||||
"Default engine is the default after startup."
|
||||
"Default engine is the default after being disabling/enabling."
|
||||
);
|
||||
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
async function test_default_search_on_updating_addons_installed_before_bug1757760({
|
||||
builtinAsInitialDefault,
|
||||
}) {
|
||||
/* This tests covers a scenario similar to the previous test but with an extension-settings.json file
|
||||
content like the one that would be available in the profile if the add-on was installed on firefox
|
||||
versions that didn't include the changes from Bug 1757760 (See Bug 1767550).
|
||||
*/
|
||||
|
||||
const EXTENSION_ID = `test_old_addon@tests.mozilla.org`;
|
||||
const EXTENSION_ID2 = `test_old_addon2@tests.mozilla.org`;
|
||||
|
||||
const extensionInfo = {
|
||||
useAddonManager: "permanent",
|
||||
manifest: {
|
||||
version: "1.1",
|
||||
browser_specific_settings: {
|
||||
gecko: {
|
||||
id: EXTENSION_ID,
|
||||
},
|
||||
},
|
||||
chrome_settings_overrides: {
|
||||
search_provider: {
|
||||
name: "Test SearchEngine",
|
||||
search_url: "https://example.com/?q={searchTerms}",
|
||||
is_default: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const extensionInfo2 = {
|
||||
useAddonManager: "permanent",
|
||||
manifest: {
|
||||
version: "1.2",
|
||||
browser_specific_settings: {
|
||||
gecko: {
|
||||
id: EXTENSION_ID2,
|
||||
},
|
||||
},
|
||||
chrome_settings_overrides: {
|
||||
search_provider: {
|
||||
name: "Test SearchEngine2",
|
||||
search_url: "https://example.com/?q={searchTerms}",
|
||||
is_default: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const { ExtensionSettingsStore } = ChromeUtils.import(
|
||||
"resource://gre/modules/ExtensionSettingsStore.jsm"
|
||||
);
|
||||
|
||||
async function assertExtensionSettingsStore(
|
||||
extensionInfo,
|
||||
expectedLevelOfControl
|
||||
) {
|
||||
const { id } = extensionInfo.manifest.browser_specific_settings.gecko;
|
||||
info(`Asserting ExtensionSettingsStore for ${id}`);
|
||||
const item = ExtensionSettingsStore.getSetting(
|
||||
"default_search",
|
||||
"defaultSearch",
|
||||
id
|
||||
);
|
||||
equal(
|
||||
item.value,
|
||||
extensionInfo.manifest.chrome_settings_overrides.search_provider.name,
|
||||
"Got the expected item returned by ExtensionSettingsStore.getSetting"
|
||||
);
|
||||
const control = await ExtensionSettingsStore.getLevelOfControl(
|
||||
id,
|
||||
"default_search",
|
||||
"defaultSearch"
|
||||
);
|
||||
equal(
|
||||
control,
|
||||
expectedLevelOfControl,
|
||||
`Got expected levelOfControl for ${id}`
|
||||
);
|
||||
}
|
||||
|
||||
info("Install test extensions without opt-in to the related search engines");
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension(extensionInfo);
|
||||
let extension2 = ExtensionTestUtils.loadExtension(extensionInfo2);
|
||||
|
||||
// Mock a response from the default search prompt where we
|
||||
// say no to setting this as the default when installing.
|
||||
await withHandlingDefaultSearchPrompt(
|
||||
{ extensionId: EXTENSION_ID, respond: false },
|
||||
() => extension.startup()
|
||||
);
|
||||
|
||||
equal(
|
||||
extension.version,
|
||||
"1.1",
|
||||
"first installed addon has the expected version."
|
||||
);
|
||||
|
||||
// Mock a response from the default search prompt where we
|
||||
// say no to setting this as the default when installing.
|
||||
await withHandlingDefaultSearchPrompt(
|
||||
{ extensionId: EXTENSION_ID2, respond: false },
|
||||
() => extension2.startup()
|
||||
);
|
||||
|
||||
equal(
|
||||
extension2.version,
|
||||
"1.2",
|
||||
"second installed addon has the expected version."
|
||||
);
|
||||
|
||||
info("Setup preconditions (set the initial default search engine)");
|
||||
|
||||
// Sanity check to be sure the initial engine expected as precondition
|
||||
// for the scenario covered by the current test case.
|
||||
let initialEngine;
|
||||
if (builtinAsInitialDefault) {
|
||||
initialEngine = Services.search.originalDefaultEngine;
|
||||
} else {
|
||||
initialEngine = Services.search.getEngineByName(
|
||||
extensionInfo.manifest.chrome_settings_overrides.search_provider.name
|
||||
);
|
||||
}
|
||||
await Services.search.setDefault(initialEngine);
|
||||
|
||||
let defaultEngineName = (await Services.search.getDefault()).name;
|
||||
Assert.equal(
|
||||
defaultEngineName,
|
||||
initialEngine.name,
|
||||
`initial default search engine expected to be ${
|
||||
builtinAsInitialDefault ? "app-provided" : EXTENSION_ID
|
||||
}`
|
||||
);
|
||||
Assert.notEqual(
|
||||
defaultEngineName,
|
||||
extensionInfo2.manifest.chrome_settings_overrides.search_provider.name,
|
||||
"initial default search engine name should not be the same as the second extension search_provider"
|
||||
);
|
||||
|
||||
equal(
|
||||
(await Services.search.getDefault()).name,
|
||||
initialEngine.name,
|
||||
`Default engine should still be set to the ${
|
||||
builtinAsInitialDefault ? "app-provided" : EXTENSION_ID
|
||||
}.`
|
||||
);
|
||||
|
||||
// Mock an update from settings stored as in an older Firefox version where Bug 1757760 was not landed yet.
|
||||
info(
|
||||
"Setup preconditions (inject mock extension-settings.json data and assert on the expected setting and levelOfControl)"
|
||||
);
|
||||
|
||||
let addon = await AddonManager.getAddonByID(EXTENSION_ID);
|
||||
let addon2 = await AddonManager.getAddonByID(EXTENSION_ID2);
|
||||
|
||||
const extensionSettingsData = {
|
||||
version: 2,
|
||||
url_overrides: {},
|
||||
prefs: {},
|
||||
homepageNotification: {},
|
||||
tabHideNotification: {},
|
||||
default_search: {
|
||||
defaultSearch: {
|
||||
initialValue: Services.search.originalDefaultEngine.name,
|
||||
precedenceList: [
|
||||
{
|
||||
id: EXTENSION_ID2,
|
||||
// The install dates are used in ExtensionSettingsStore.getLevelOfControl
|
||||
// and to recreate the expected preconditions the last extension installed
|
||||
// should have a installDate timestamp > then the first one.
|
||||
installDate: addon2.installDate.getTime() + 1000,
|
||||
value:
|
||||
extensionInfo2.manifest.chrome_settings_overrides.search_provider
|
||||
.name,
|
||||
// When an addon with a default search engine override is installed in Firefox versions
|
||||
// without the changes landed from Bug 1757760, `enabled` will be set to true in all cases
|
||||
// (Prompt never answered, or when No or Yes is selected by the user).
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
id: EXTENSION_ID,
|
||||
installDate: addon.installDate.getTime(),
|
||||
value:
|
||||
extensionInfo.manifest.chrome_settings_overrides.search_provider
|
||||
.name,
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
newTabNotification: {},
|
||||
commands: {},
|
||||
};
|
||||
|
||||
const file = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
file.append("extension-settings.json");
|
||||
|
||||
info(`writing mock settings data into ${file.path}`);
|
||||
await IOUtils.writeJSON(file.path, extensionSettingsData);
|
||||
await ExtensionSettingsStore._reloadFile(false);
|
||||
|
||||
equal(
|
||||
(await Services.search.getDefault()).name,
|
||||
initialEngine.name,
|
||||
"Default engine is still set to the initial one."
|
||||
);
|
||||
|
||||
// The following assertions verify that the migration applied from ExtensionSettingsStore
|
||||
// fixed the inconsistent state and kept the search engine unchanged.
|
||||
//
|
||||
// - With the fixed settings we expect both to be resolved to "controllable_by_this_extension".
|
||||
// - Without the fix applied during the migration the levelOfControl resolved would be:
|
||||
// - for the last installed: "controlled_by_this_extension"
|
||||
// - for the first installed: "controlled_by_other_extensions"
|
||||
await assertExtensionSettingsStore(
|
||||
extensionInfo2,
|
||||
"controlled_by_this_extension"
|
||||
);
|
||||
await assertExtensionSettingsStore(
|
||||
extensionInfo,
|
||||
"controlled_by_other_extensions"
|
||||
);
|
||||
|
||||
info(
|
||||
"Verify that updating the extension does not prompt and does not take over the default engine"
|
||||
);
|
||||
|
||||
extensionInfo2.manifest.version = "2.2";
|
||||
await assertUpdateDoNotPrompt(extension2, extensionInfo2);
|
||||
|
||||
extensionInfo.manifest.version = "2.1";
|
||||
await assertUpdateDoNotPrompt(extension, extensionInfo);
|
||||
|
||||
equal(
|
||||
(await Services.search.getDefault()).name,
|
||||
initialEngine.name,
|
||||
"Default engine is still the same after updating both the test extensions."
|
||||
);
|
||||
|
||||
// After both the extensions have been updated and their inconsistent state
|
||||
// updated internally, both extensions should have levelOfControl "controllable_*".
|
||||
await assertExtensionSettingsStore(
|
||||
extensionInfo2,
|
||||
"controllable_by_this_extension"
|
||||
);
|
||||
await assertExtensionSettingsStore(
|
||||
extensionInfo,
|
||||
// We expect levelOfControl to be controlled_by_this_extension if the test case
|
||||
// is expecting the third party extension to stay set as default.
|
||||
builtinAsInitialDefault
|
||||
? "controllable_by_this_extension"
|
||||
: "controlled_by_this_extension"
|
||||
);
|
||||
|
||||
info("Verify that disable/enable the extension does prompt the user");
|
||||
|
||||
await withHandlingDefaultSearchPrompt(
|
||||
{ extensionId: EXTENSION_ID2, respond: false },
|
||||
async () => {
|
||||
await addon2.disable();
|
||||
await addon2.enable();
|
||||
}
|
||||
);
|
||||
|
||||
// we said no.
|
||||
equal(
|
||||
(await Services.search.getDefault()).name,
|
||||
initialEngine.name,
|
||||
`Default engine should still be the same after disabling/enabling ${EXTENSION_ID2}.`
|
||||
);
|
||||
|
||||
await withHandlingDefaultSearchPrompt(
|
||||
{ extensionId: EXTENSION_ID, respond: false },
|
||||
async () => {
|
||||
await addon.disable();
|
||||
await addon.enable();
|
||||
}
|
||||
);
|
||||
|
||||
// we said no.
|
||||
equal(
|
||||
(await Services.search.getDefault()).name,
|
||||
Services.search.originalDefaultEngine.name,
|
||||
`Default engine should be set to the original default after disabling/enabling ${EXTENSION_ID}.`
|
||||
);
|
||||
|
||||
await withHandlingDefaultSearchPrompt(
|
||||
{ extensionId: EXTENSION_ID, respond: true },
|
||||
async () => {
|
||||
await addon.disable();
|
||||
await addon.enable();
|
||||
}
|
||||
);
|
||||
|
||||
// we responded yes.
|
||||
equal(
|
||||
(await Services.search.getDefault()).name,
|
||||
extensionInfo.manifest.chrome_settings_overrides.search_provider.name,
|
||||
"Default engine should be set to the one opted-in from the last prompt."
|
||||
);
|
||||
|
||||
await extension.unload();
|
||||
await extension2.unload();
|
||||
}
|
||||
|
||||
add_task(function test_builtin_default_search_after_updating_old_addons() {
|
||||
return test_default_search_on_updating_addons_installed_before_bug1757760({
|
||||
builtinAsInitialDefault: true,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function test_third_party_default_search_after_updating_old_addons() {
|
||||
return test_default_search_on_updating_addons_installed_before_bug1757760({
|
||||
builtinAsInitialDefault: false,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,12 +6,13 @@ body {
|
|||
display: flex;
|
||||
align-items: stretch;
|
||||
padding-block: 40px 80px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Ubuntu", "Helvetica Neue", sans-serif;
|
||||
font: message-box;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
font-weight: 500;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
body > nav {
|
||||
|
@ -77,6 +78,7 @@ body > main > aside {
|
|||
|
||||
.page-section-header > .section-description {
|
||||
grid-area: desc;
|
||||
color: var(--in-content-deemphasized-text)
|
||||
}
|
||||
|
||||
.setup-step > h2 {
|
||||
|
@ -101,7 +103,7 @@ body > main > aside {
|
|||
|
||||
.closed-tab-li {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
grid-template-columns: min-content repeat(8, 1fr);
|
||||
column-gap: 16px;
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
|
@ -119,6 +121,7 @@ body > main > aside {
|
|||
.closed-tab-li-title {
|
||||
grid-column: span 5;
|
||||
padding-inline-start: 2px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.closed-tab-li-url {
|
||||
|
@ -130,14 +133,23 @@ body > main > aside {
|
|||
text-align: end;
|
||||
}
|
||||
|
||||
.closed-tab-li-url, .closed-tab-li-time {
|
||||
color: var(--in-content-deemphasized-text);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.closed-tab-li-title, .closed-tab-li-url {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon {
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
|
@ -152,3 +164,13 @@ body > main > aside {
|
|||
.icon.history {
|
||||
background-image: url('chrome://browser/skin/history.svg');
|
||||
}
|
||||
|
||||
.favicon {
|
||||
background-size: cover;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.favicon, .icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src chrome:; object-src 'none'">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src chrome:; object-src 'none'; img-src data: chrome:;">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<title data-l10n-id="firefoxview-page-title"></title>
|
||||
<link rel="localization" href="branding/brand.ftl">
|
||||
|
|
|
@ -29,4 +29,5 @@ window.addEventListener("load", () => {
|
|||
|
||||
window.addEventListener("unload", () => {
|
||||
tabsSetupFlowManager?.uninit();
|
||||
document.getElementById("recently-closed-tabs-container").cleanup();
|
||||
});
|
||||
|
|
|
@ -14,8 +14,19 @@ XPCOMUtils.defineLazyModuleGetters(globalThis, {
|
|||
});
|
||||
|
||||
const relativeTimeFormat = new Services.intl.RelativeTimeFormat(undefined, {});
|
||||
const SS_NOTIFY_CLOSED_OBJECTS_CHANGED = "sessionstore-closed-objects-changed";
|
||||
|
||||
function getWindow() {
|
||||
return window.browsingContext.embedderWindowGlobal.browsingContext.window;
|
||||
}
|
||||
|
||||
class RecentlyClosedTabsList extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.maxTabsLength = 25;
|
||||
this.closedTabsData = [];
|
||||
}
|
||||
|
||||
get tabsList() {
|
||||
return this.querySelector("ol");
|
||||
}
|
||||
|
@ -29,20 +40,18 @@ class RecentlyClosedTabsList extends HTMLElement {
|
|||
|
||||
connectedCallback() {
|
||||
this.addEventListener("click", this);
|
||||
this.addEventListener("keydown", this);
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
if (event.type == "click") {
|
||||
const item = event.target.closest(".closed-tab-li");
|
||||
event.preventDefault();
|
||||
this.openTab(item.dataset.targetURI);
|
||||
if (
|
||||
event.type == "click" ||
|
||||
(event.type == "keydown" && event.keyCode == KeyEvent.DOM_VK_RETURN)
|
||||
) {
|
||||
this.openTabAndUpdate(event);
|
||||
}
|
||||
}
|
||||
|
||||
getWindow() {
|
||||
return window.windowRoot.ownerGlobal;
|
||||
}
|
||||
|
||||
convertTimestamp(timestamp) {
|
||||
const elapsed = Date.now() - timestamp;
|
||||
const nowThresholdMs = 91000;
|
||||
|
@ -79,66 +88,145 @@ class RecentlyClosedTabsList extends HTMLElement {
|
|||
return displayHost.length ? displayHost : uriString;
|
||||
}
|
||||
|
||||
getTargetURI(tab) {
|
||||
let targetURI = "";
|
||||
getTabStateValue(tab, key) {
|
||||
let value = "";
|
||||
const tabEntries = tab.state.entries;
|
||||
const activeIndex = tabEntries.length - 1;
|
||||
|
||||
if (activeIndex >= 0 && tabEntries[activeIndex]) {
|
||||
targetURI = tabEntries[activeIndex].url;
|
||||
value = tabEntries[activeIndex][key];
|
||||
}
|
||||
|
||||
return targetURI;
|
||||
return value;
|
||||
}
|
||||
|
||||
openTab(targetURI) {
|
||||
window.open(targetURI, "_blank");
|
||||
openTabAndUpdate(event) {
|
||||
event.preventDefault();
|
||||
const item = event.target.closest(".closed-tab-li");
|
||||
const index = [...this.tabsList.children].indexOf(item);
|
||||
|
||||
SessionStore.undoCloseTab(getWindow(), index);
|
||||
this.tabsList.removeChild(item);
|
||||
}
|
||||
|
||||
generateTabs() {
|
||||
let closedTabs = SessionStore.getClosedTabData(this.getWindow());
|
||||
closedTabs = closedTabs.slice(0, 25);
|
||||
initiateTabsList() {
|
||||
let closedTabs = SessionStore.getClosedTabData(getWindow());
|
||||
closedTabs = closedTabs.slice(0, this.maxTabsLength);
|
||||
this.closedTabsData = closedTabs;
|
||||
|
||||
for (const tab of closedTabs) {
|
||||
let li = document.createElement("li");
|
||||
li.classList.add("closed-tab-li");
|
||||
|
||||
if (tab.image) {
|
||||
// TODO - figure out how to render this properly
|
||||
PlacesUIUtils.setImage(tab, li);
|
||||
}
|
||||
|
||||
let title = document.createElement("span");
|
||||
title.textContent = `${tab.title}`;
|
||||
title.classList.add("closed-tab-li-title");
|
||||
|
||||
const targetURI = this.getTargetURI(tab);
|
||||
li.dataset.targetURI = targetURI;
|
||||
document.l10n.setAttributes(li, "firefoxview-closed-tabs-tab-button", {
|
||||
targetURI,
|
||||
});
|
||||
|
||||
let url = document.createElement("span");
|
||||
|
||||
if (targetURI) {
|
||||
url.textContent = this.formatURIForDisplay(targetURI);
|
||||
url.classList.add("closed-tab-li-url");
|
||||
}
|
||||
|
||||
let time = document.createElement("span");
|
||||
time.textContent = this.convertTimestamp(tab.closedAt);
|
||||
time.classList.add("closed-tab-li-time");
|
||||
|
||||
li.append(title, url, time);
|
||||
this.tabsList.appendChild(li);
|
||||
const li = this.generateListItem(tab);
|
||||
this.tabsList.append(li);
|
||||
}
|
||||
this.tabsList.hidden = false;
|
||||
}
|
||||
|
||||
updateTabsList() {
|
||||
let newClosedTabs = SessionStore.getClosedTabData(getWindow());
|
||||
newClosedTabs = newClosedTabs.slice(0, this.maxTabsLength);
|
||||
|
||||
if (this.closedTabsData.length && !newClosedTabs.length) {
|
||||
// if a user purges history, clear the list
|
||||
[...this.tabsList.children].forEach(node =>
|
||||
this.tabsList.removeChild(node)
|
||||
);
|
||||
document
|
||||
.getElementById("recently-closed-tabs-container")
|
||||
.togglePlaceholderVisibility(true);
|
||||
this.tabsList.hidden = true;
|
||||
this.closedTabsData = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const tabsToAdd = newClosedTabs.filter(
|
||||
newTab =>
|
||||
!this.closedTabsData.some(tab => {
|
||||
return (
|
||||
this.getTabStateValue(tab, "ID") ==
|
||||
this.getTabStateValue(newTab, "ID")
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
if (!tabsToAdd.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let tab of tabsToAdd.reverse()) {
|
||||
if (this.tabsList.children.length == this.maxTabsLength) {
|
||||
this.tabsList.lastChild.remove();
|
||||
}
|
||||
let li = this.generateListItem(tab);
|
||||
this.tabsList.prepend(li);
|
||||
}
|
||||
|
||||
this.closedTabsData = newClosedTabs;
|
||||
|
||||
// for situations where the tab list will initially be empty (such as
|
||||
// with new profiles or automatic session restore is disabled) and
|
||||
// this.initiateTabsList won't be called
|
||||
if (this.tabsList.hidden) {
|
||||
this.tabsList.hidden = false;
|
||||
document
|
||||
.getElementById("recently-closed-tabs-container")
|
||||
.togglePlaceholderVisibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
setFavicon(tab) {
|
||||
const imageUrl = tab.image
|
||||
? PlacesUIUtils.getImageURL(tab)
|
||||
: "chrome://global/skin/icons/defaultFavicon.svg";
|
||||
let favicon = document.createElement("div");
|
||||
|
||||
favicon.style.backgroundImage = `url('${imageUrl}')`;
|
||||
favicon.classList.add("favicon");
|
||||
favicon.setAttribute("role", "presentation");
|
||||
return favicon;
|
||||
}
|
||||
|
||||
generateListItem(tab) {
|
||||
const li = document.createElement("li");
|
||||
li.classList.add("closed-tab-li");
|
||||
li.setAttribute("tabindex", 0);
|
||||
li.setAttribute("role", "button");
|
||||
|
||||
const title = document.createElement("span");
|
||||
title.textContent = `${tab.title}`;
|
||||
title.classList.add("closed-tab-li-title");
|
||||
|
||||
const favicon = this.setFavicon(tab);
|
||||
li.append(favicon);
|
||||
|
||||
const targetURI = this.getTabStateValue(tab, "url");
|
||||
li.dataset.targetURI = targetURI;
|
||||
document.l10n.setAttributes(li, "firefoxview-closed-tabs-tab-button", {
|
||||
targetURI,
|
||||
});
|
||||
|
||||
const url = document.createElement("span");
|
||||
|
||||
if (targetURI) {
|
||||
url.textContent = this.formatURIForDisplay(targetURI);
|
||||
url.classList.add("closed-tab-li-url");
|
||||
}
|
||||
|
||||
const time = document.createElement("span");
|
||||
time.textContent = this.convertTimestamp(tab.closedAt);
|
||||
time.classList.add("closed-tab-li-time");
|
||||
|
||||
li.append(title, url, time);
|
||||
return li;
|
||||
}
|
||||
}
|
||||
customElements.define("recently-closed-tabs-list", RecentlyClosedTabsList);
|
||||
|
||||
class RecentlyClosedTabsContainer extends HTMLElement {
|
||||
getWindow = () => window.windowRoot.ownerGlobal;
|
||||
constructor() {
|
||||
super();
|
||||
this.observerAdded = false;
|
||||
this.boundObserve = (...args) => this.observe(...args);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.noTabsElement = this.querySelector(
|
||||
|
@ -149,28 +237,77 @@ class RecentlyClosedTabsContainer extends HTMLElement {
|
|||
"#collapsible-tabs-container"
|
||||
);
|
||||
this.collapsibleButton = this.querySelector("#collapsible-tabs-button");
|
||||
|
||||
this.collapsibleButton.addEventListener("click", this);
|
||||
|
||||
getWindow().gBrowser.tabContainer.addEventListener("TabSelect", this);
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
getWindow().gBrowser.tabContainer.removeEventListener("TabSelect", this);
|
||||
|
||||
if (this.observerAdded) {
|
||||
Services.obs.removeObserver(
|
||||
this.boundObserve,
|
||||
SS_NOTIFY_CLOSED_OBJECTS_CHANGED
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// we observe when a tab closes but since this notification fires more frequently and on
|
||||
// all windows, we remove the observer when another tab is selected; we check for changes
|
||||
// to the session store once the user return to this tab.
|
||||
handleObservers(contentDocument) {
|
||||
if (
|
||||
!this.observerAdded &&
|
||||
contentDocument &&
|
||||
contentDocument.URL == "about:firefoxview"
|
||||
) {
|
||||
Services.obs.addObserver(
|
||||
this.boundObserve,
|
||||
SS_NOTIFY_CLOSED_OBJECTS_CHANGED
|
||||
);
|
||||
this.observerAdded = true;
|
||||
this.list.updateTabsList();
|
||||
} else if (this.observerAdded) {
|
||||
Services.obs.removeObserver(
|
||||
this.boundObserve,
|
||||
SS_NOTIFY_CLOSED_OBJECTS_CHANGED
|
||||
);
|
||||
this.observerAdded = false;
|
||||
}
|
||||
}
|
||||
|
||||
observe = () => this.list.updateTabsList();
|
||||
|
||||
onLoad() {
|
||||
if (this.getClosedTabCount() == 0) {
|
||||
this.noTabsElement.hidden = false;
|
||||
this.collapsibleContainer.classList.add("empty-container");
|
||||
this.togglePlaceholderVisibility(true);
|
||||
} else {
|
||||
this.list.generateTabs();
|
||||
this.list.initiateTabsList();
|
||||
}
|
||||
Services.obs.addObserver(
|
||||
this.boundObserve,
|
||||
SS_NOTIFY_CLOSED_OBJECTS_CHANGED
|
||||
);
|
||||
this.observerAdded = true;
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
if (event.type == "click" && event.target == this.collapsibleButton) {
|
||||
this.toggleTabs();
|
||||
} else if (event.type == "TabSelect") {
|
||||
this.handleObservers(event.target.linkedBrowser.contentDocument);
|
||||
}
|
||||
}
|
||||
|
||||
togglePlaceholderVisibility(visible) {
|
||||
this.noTabsElement.toggleAttribute("hidden", !visible);
|
||||
this.collapsibleContainer.classList.toggle("empty-container", visible);
|
||||
}
|
||||
|
||||
getClosedTabCount = () => {
|
||||
try {
|
||||
return SessionStore.getClosedTabCount(this.getWindow());
|
||||
return SessionStore.getClosedTabCount(getWindow());
|
||||
} catch (ex) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[DEFAULT]
|
||||
run-if = nightly_build # about:firefoxview is only enabled on Nightly
|
||||
|
||||
support-files = head.js
|
||||
|
||||
[browser_firefoxview.js]
|
||||
[browser_firefoxview_tab.js]
|
||||
[browser_recently_closed_tabs.js]
|
||||
[browser_setup_state.js]
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(globalThis, {
|
||||
SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
|
||||
});
|
||||
|
||||
const URLs = [
|
||||
"http://mochi.test:8888/browser/",
|
||||
"http://www.example.com/",
|
||||
"http://example.net",
|
||||
"http://example.org",
|
||||
];
|
||||
|
||||
async function add_new_tab(URL) {
|
||||
let tab = BrowserTestUtils.addTab(gBrowser, URL);
|
||||
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
return tab;
|
||||
}
|
||||
|
||||
async function close_tab(tab) {
|
||||
const sessionStorePromise = BrowserTestUtils.waitForSessionStoreUpdate(tab);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
await sessionStorePromise;
|
||||
}
|
||||
|
||||
function clearHistory() {
|
||||
Services.obs.notifyObservers(null, "browser:purge-session-history");
|
||||
}
|
||||
|
||||
add_task(async function test_empty_list() {
|
||||
clearHistory();
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "about:firefoxview",
|
||||
},
|
||||
async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
const closedObjectsChanged = TestUtils.topicObserved(
|
||||
"sessionstore-closed-objects-changed"
|
||||
);
|
||||
|
||||
ok(
|
||||
document
|
||||
.querySelector("#collapsible-tabs-container")
|
||||
.classList.contains("empty-container"),
|
||||
"collapsible container should have correct styling when the list is empty"
|
||||
);
|
||||
|
||||
testVisibility(browser, {
|
||||
expectedVisible: {
|
||||
"#recently-closed-tabs-placeholder": true,
|
||||
"ol.closed-tabs-list": false,
|
||||
},
|
||||
});
|
||||
|
||||
const tab1 = await add_new_tab(URLs[0]);
|
||||
|
||||
await close_tab(tab1);
|
||||
await closedObjectsChanged;
|
||||
|
||||
ok(
|
||||
!document
|
||||
.querySelector("#collapsible-tabs-container")
|
||||
.classList.contains("empty-container"),
|
||||
"collapsible container should have correct styling when the list is not empty"
|
||||
);
|
||||
|
||||
testVisibility(browser, {
|
||||
expectedVisible: {
|
||||
"#recently-closed-tabs-placeholder": false,
|
||||
"ol.closed-tabs-list": true,
|
||||
},
|
||||
});
|
||||
|
||||
ok(
|
||||
document.querySelector("ol.closed-tabs-list").children.length === 1,
|
||||
"recently-closed-tabs-list should have one list item"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_list_ordering() {
|
||||
const existingData = SessionStore.getClosedTabCount(window);
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "about:firefoxview",
|
||||
},
|
||||
async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
const closedObjectsChanged = TestUtils.topicObserved(
|
||||
"sessionstore-closed-objects-changed"
|
||||
);
|
||||
|
||||
const tab1 = await add_new_tab(URLs[0]);
|
||||
const tab2 = await add_new_tab(URLs[1]);
|
||||
const tab3 = await add_new_tab(URLs[2]);
|
||||
|
||||
gBrowser.selectedTab = tab3;
|
||||
|
||||
await close_tab(tab3);
|
||||
await closedObjectsChanged;
|
||||
|
||||
await close_tab(tab2);
|
||||
await closedObjectsChanged;
|
||||
|
||||
await close_tab(tab1);
|
||||
await closedObjectsChanged;
|
||||
|
||||
const tabsList = document.querySelector("ol.closed-tabs-list");
|
||||
await BrowserTestUtils.waitForMutationCondition(
|
||||
tabsList,
|
||||
{ childList: true },
|
||||
() => tabsList.children.length > 1
|
||||
);
|
||||
|
||||
ok(
|
||||
document.querySelector("ol.closed-tabs-list").children.length ===
|
||||
3 + existingData,
|
||||
"recently-closed-tabs-list should have one list item"
|
||||
);
|
||||
|
||||
// check that the ordering is correct when user navigates to another tab, and then closes multiple tabs.
|
||||
ok(
|
||||
document
|
||||
.querySelector("ol.closed-tabs-list")
|
||||
.firstChild.textContent.includes("mochi.test"),
|
||||
"first list item in recently-closed-tabs-list is in the correct order"
|
||||
);
|
||||
|
||||
ok(
|
||||
document
|
||||
.querySelector("ol.closed-tabs-list")
|
||||
.children[2].textContent.includes("example.net"),
|
||||
"last list item in recently-closed-tabs-list is in the correct order"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_max_list_items() {
|
||||
// the tabs opened from the previous test provide seed data
|
||||
const mockMaxTabsLength = SessionStore.getClosedTabCount(window);
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "about:firefoxview",
|
||||
},
|
||||
async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
|
||||
// override this value for testing purposes
|
||||
document.querySelector(
|
||||
"recently-closed-tabs-list"
|
||||
).maxTabsLength = mockMaxTabsLength;
|
||||
|
||||
ok(
|
||||
!document
|
||||
.querySelector("#collapsible-tabs-container")
|
||||
.classList.contains("empty-container"),
|
||||
"collapsible container should have correct styling when the list is not empty"
|
||||
);
|
||||
|
||||
testVisibility(browser, {
|
||||
expectedVisible: {
|
||||
"#recently-closed-tabs-placeholder": false,
|
||||
"ol.closed-tabs-list": true,
|
||||
},
|
||||
});
|
||||
|
||||
ok(
|
||||
document.querySelector("ol.closed-tabs-list").childNodes.length ===
|
||||
mockMaxTabsLength,
|
||||
`recently-closed-tabs-list should have ${mockMaxTabsLength} list items`
|
||||
);
|
||||
|
||||
ok(
|
||||
document
|
||||
.querySelector("ol.closed-tabs-list")
|
||||
.firstChild.textContent.includes("about:firefoxview"),
|
||||
"first list item in recently-closed-tabs-list is from previous test (session store)"
|
||||
);
|
||||
|
||||
const closedObjectsChanged = TestUtils.topicObserved(
|
||||
"sessionstore-closed-objects-changed"
|
||||
);
|
||||
// add another tab
|
||||
const tab = await add_new_tab(URLs[3]);
|
||||
await close_tab(tab);
|
||||
await closedObjectsChanged;
|
||||
|
||||
ok(
|
||||
document
|
||||
.querySelector("ol.closed-tabs-list")
|
||||
.firstChild.textContent.includes("example.org"),
|
||||
"first list item in recently-closed-tabs-list should have been updated"
|
||||
);
|
||||
|
||||
ok(
|
||||
document.querySelector("ol.closed-tabs-list").childNodes.length ===
|
||||
mockMaxTabsLength,
|
||||
`recently-closed-tabs-list should still have ${mockMaxTabsLength} list items`
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
20
browser/components/firefoxview/tests/browser/head.js
Normal file
20
browser/components/firefoxview/tests/browser/head.js
Normal 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`);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1806,14 +1806,13 @@ var PlacesUIUtils = {
|
|||
}
|
||||
},
|
||||
|
||||
setImage(aItem, aElement) {
|
||||
getImageURL(aItem) {
|
||||
let iconURL = aItem.image;
|
||||
// don't initiate a connection just to fetch a favicon (see bug 467828)
|
||||
if (/^https?:/.test(iconURL)) {
|
||||
iconURL = "moz-anno:favicon:" + iconURL;
|
||||
}
|
||||
|
||||
aElement.setAttribute("image", iconURL);
|
||||
return iconURL;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,7 @@ support-files =
|
|||
|
||||
[browser_privatebrowsing_DownloadLastDirWithCPS.js]
|
||||
[browser_privatebrowsing_about_default_promo.js]
|
||||
[browser_privatebrowsing_about_focus_promo.js]
|
||||
[browser_privatebrowsing_about_nimbus.js]
|
||||
[browser_privatebrowsing_about_nimbus_messaging.js]
|
||||
[browser_privatebrowsing_about_nimbus_impressions.js]
|
||||
|
@ -44,7 +45,6 @@ skip-if = verify
|
|||
[browser_privatebrowsing_downloadLastDir_c.js]
|
||||
[browser_privatebrowsing_downloadLastDir_toggle.js]
|
||||
[browser_privatebrowsing_favicon.js]
|
||||
[browser_privatebrowsing_focus_promo.js]
|
||||
[browser_privatebrowsing_history_shift_click.js]
|
||||
[browser_privatebrowsing_last_private_browsing_context_exited.js]
|
||||
[browser_privatebrowsing_lastpbcontextexited.js]
|
||||
|
|
|
@ -57,7 +57,7 @@ add_task(async function test_focus_promo_in_disallowed_region() {
|
|||
|
||||
add_task(
|
||||
async function test_klar_promo_in_certain_regions_with_English_locale() {
|
||||
const testLocale = "en-GB"; // British English
|
||||
const testLocale = "en-US"; // US English
|
||||
setLocale(testLocale);
|
||||
|
||||
const testRegion = async region => {
|
|
@ -192,7 +192,8 @@ function createEntry(
|
|||
|
||||
element.setAttribute("label", aMenuLabel);
|
||||
if (aClosedTab.image) {
|
||||
PlacesUIUtils.setImage(aClosedTab, element);
|
||||
const iconURL = PlacesUIUtils.getImageURL(aClosedTab);
|
||||
element.setAttribute("image", iconURL);
|
||||
}
|
||||
if (!aIsWindowsFragment) {
|
||||
element.setAttribute("value", aIndex);
|
||||
|
|
|
@ -1081,6 +1081,7 @@ var SessionStoreInternal = {
|
|||
// Non-SHIP code calls this when the frame script is unloaded.
|
||||
this.onFinalTabStateUpdateComplete(aSubject);
|
||||
}
|
||||
this._notifyOfClosedObjectsChange();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -41,7 +41,11 @@ CONTENT_WIN.addEventListener("DOMContentLoaded", function onDCL(evt) {
|
|||
|
||||
case "childList": {
|
||||
// We really only care about elements appending inside pages.
|
||||
if (!mutation.addedNodes || !mutation.target.closest(".page")) {
|
||||
let parent =
|
||||
mutation.target instanceof HTMLDocument
|
||||
? mutation.target.documentElement
|
||||
: mutation.target;
|
||||
if (!mutation.addedNodes || !parent.closest(".page")) {
|
||||
break;
|
||||
}
|
||||
FormAutofillUtils.localizeMarkup(mutation.target);
|
||||
|
|
|
@ -64,6 +64,12 @@ let AVAILABLE_PIP_OVERRIDES;
|
|||
},
|
||||
},
|
||||
|
||||
hulu: {
|
||||
"https://www.hulu.com/watch/*": {
|
||||
videoWrapperScriptPath: "video-wrappers/hulu.js",
|
||||
},
|
||||
},
|
||||
|
||||
instagram: {
|
||||
"https://www.instagram.com/*": { policy: TOGGLE_POLICIES.ONE_QUARTER },
|
||||
},
|
||||
|
|
|
@ -32,6 +32,7 @@ FINAL_TARGET_FILES.features["pictureinpicture@mozilla.org"]["video-wrappers"] +=
|
|||
"video-wrappers/dailymotion.js",
|
||||
"video-wrappers/funimation.js",
|
||||
"video-wrappers/hotstar.js",
|
||||
"video-wrappers/hulu.js",
|
||||
"video-wrappers/mock-wrapper.js",
|
||||
"video-wrappers/netflix.js",
|
||||
"video-wrappers/piped.js",
|
||||
|
|
48
browser/extensions/pictureinpicture/video-wrappers/hulu.js
Normal file
48
browser/extensions/pictureinpicture/video-wrappers/hulu.js
Normal 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;
|
|
@ -2,7 +2,7 @@
|
|||
"manifest_version": 2,
|
||||
"name": "Web Compatibility Interventions",
|
||||
"description": "Urgent post-release fixes for web compatibility.",
|
||||
"version": "101.7.0",
|
||||
"version": "101.8.0",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "webcompat@mozilla.org",
|
||||
|
|
|
@ -9,13 +9,56 @@
|
|||
*
|
||||
* Some sites rely on Maxmind's GeoIP library which gets blocked by ETP's
|
||||
* fingerprinter blocking. With the library window global not being defined
|
||||
* functionality may break or the site does not render at all. This shim adds a
|
||||
* dummy object which returns errors for any request to mitigate the breakage.
|
||||
* functionality may break or the site does not render at all. This shim
|
||||
* has it return the United States as the location for all users.
|
||||
*/
|
||||
|
||||
if (!window.geoip2) {
|
||||
const callback = (_, onError) => {
|
||||
onError("");
|
||||
const continent = {
|
||||
code: "NA",
|
||||
geoname_id: 6255149,
|
||||
names: {
|
||||
de: "Nordamerika",
|
||||
en: "North America",
|
||||
es: "Norteamérica",
|
||||
fr: "Amérique du Nord",
|
||||
ja: "北アメリカ",
|
||||
"pt-BR": "América do Norte",
|
||||
ru: "Северная Америка",
|
||||
"zh-CN": "北美洲",
|
||||
},
|
||||
};
|
||||
|
||||
const country = {
|
||||
geoname_id: 6252001,
|
||||
iso_code: "US",
|
||||
names: {
|
||||
de: "USA",
|
||||
en: "United States",
|
||||
es: "Estados Unidos",
|
||||
fr: "États-Unis",
|
||||
ja: "アメリカ合衆国",
|
||||
"pt-BR": "Estados Unidos",
|
||||
ru: "США",
|
||||
"zh-CN": "美国",
|
||||
},
|
||||
};
|
||||
|
||||
const city = {
|
||||
names: {
|
||||
en: "",
|
||||
},
|
||||
};
|
||||
|
||||
const callback = onSuccess => {
|
||||
requestAnimationFrame(() => {
|
||||
onSuccess({
|
||||
city,
|
||||
continent,
|
||||
country,
|
||||
registered_country: country,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
window.geoip2 = {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
@ -119,6 +119,7 @@ https://sub2.test1.example.com:443 privileged
|
|||
https://sub2.test2.example.com:443 privileged
|
||||
https://example.net:443 privileged
|
||||
https://nocert.example.com:443 privileged,nocert
|
||||
https://nocert.example.org:443 privileged,nocert
|
||||
https://self-signed.example.com:443 privileged,cert=selfsigned
|
||||
https://untrusted.example.com:443 privileged,cert=untrusted
|
||||
https://expired.example.com:443 privileged,cert=expired
|
||||
|
|
|
@ -1208,7 +1208,22 @@
|
|||
"../node_modules/babel-loader/lib/index.js??ref--1!../packages/devtools-source-map/src/utils/network-request.js": 1056,
|
||||
"../node_modules/babel-loader/lib/index.js??ref--1!../../shared/worker-dispatcher.js": 1057,
|
||||
"../node_modules/babel-loader/lib/index.js??ref--1!../packages/devtools-source-map/src/utils/privileged-network-request.js": 1058,
|
||||
"../node_modules/babel-loader/lib/index.js??ref--1!../../shared/worker-utils.js": 1059
|
||||
"../node_modules/babel-loader/lib/index.js??ref--1!../../shared/worker-utils.js": 1059,
|
||||
"../packages/devtools-source-map/node_modules/whatwg-url/lib/url-state-machine.js": 1060,
|
||||
"../packages/devtools-source-map/node_modules/whatwg-url/lib/urlencoded.js": 1061,
|
||||
"../packages/devtools-source-map/node_modules/webidl-conversions/lib/index.js": 1062,
|
||||
"../packages/devtools-source-map/node_modules/whatwg-url/lib/utils.js": 1063,
|
||||
"../node_modules/node-libs-browser/node_modules/punycode/punycode.js": 1064,
|
||||
"../packages/devtools-source-map/node_modules/whatwg-url/lib/infra.js": 1065,
|
||||
"../packages/devtools-source-map/node_modules/whatwg-url/lib/URLSearchParams.js": 1066,
|
||||
"../packages/devtools-source-map/node_modules/whatwg-url/lib/public-api.js": 1067,
|
||||
"../packages/devtools-source-map/node_modules/whatwg-url/lib/URL.js": 1068,
|
||||
"../packages/devtools-source-map/node_modules/whatwg-url/lib/URL-impl.js": 1069,
|
||||
"../packages/devtools-source-map/node_modules/tr46/index.js": 1070,
|
||||
"../packages/devtools-source-map/node_modules/tr46/lib/regexes.js": 1071,
|
||||
"../node_modules/json-loader/index.js!../packages/devtools-source-map/node_modules/tr46/lib/mappingTable.json": 1072,
|
||||
"../packages/devtools-source-map/node_modules/whatwg-url/lib/URLSearchParams-impl.js": 1073,
|
||||
"../node_modules/lodash.sortby/index.js": 1074
|
||||
},
|
||||
"usedIds": {
|
||||
"0": 0,
|
||||
|
@ -2270,7 +2285,22 @@
|
|||
"1056": 1056,
|
||||
"1057": 1057,
|
||||
"1058": 1058,
|
||||
"1059": 1059
|
||||
"1059": 1059,
|
||||
"1060": 1060,
|
||||
"1061": 1061,
|
||||
"1062": 1062,
|
||||
"1063": 1063,
|
||||
"1064": 1064,
|
||||
"1065": 1065,
|
||||
"1066": 1066,
|
||||
"1067": 1067,
|
||||
"1068": 1068,
|
||||
"1069": 1069,
|
||||
"1070": 1070,
|
||||
"1071": 1071,
|
||||
"1072": 1072,
|
||||
"1073": 1073,
|
||||
"1074": 1074
|
||||
}
|
||||
},
|
||||
"chunks": {
|
||||
|
@ -2415,7 +2445,7 @@
|
|||
"byName": {},
|
||||
"byBlocks": {},
|
||||
"usedIds": {
|
||||
"0": 0
|
||||
"1": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2436,7 +2466,7 @@
|
|||
"byName": {},
|
||||
"byBlocks": {},
|
||||
"usedIds": {
|
||||
"0": 0
|
||||
"1": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,49 +2,6 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
const whatwgUrl = `
|
||||
(() => {
|
||||
let factory;
|
||||
function define(...args) {
|
||||
if (factory) {
|
||||
throw new Error("expected a single define call");
|
||||
}
|
||||
|
||||
if (
|
||||
args.length !== 2 ||
|
||||
!Array.isArray(args[0]) ||
|
||||
args[0].length !== 0 ||
|
||||
typeof args[1] !== "function"
|
||||
) {
|
||||
throw new Error("whatwg-url had unexpected factory arguments.");
|
||||
}
|
||||
|
||||
factory = args[1];
|
||||
}
|
||||
define.amd = true;
|
||||
|
||||
const existingDefine = Object.getOwnPropertyDescriptor(globalThis, "define");
|
||||
globalThis.define = define;
|
||||
let err;
|
||||
try {
|
||||
importScripts("resource://devtools/client/shared/vendor/whatwg-url.js");
|
||||
|
||||
if (!factory) {
|
||||
throw new Error("Failed to load whatwg-url factory");
|
||||
}
|
||||
} finally {
|
||||
if (existingDefine) {
|
||||
Object.defineProperty(globalThis, "define", existingDefine);
|
||||
} else {
|
||||
delete globalThis.define;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return factory();
|
||||
})()
|
||||
`;
|
||||
|
||||
module.exports = {
|
||||
"./source-editor": "devtools/client/sourceeditor/editor",
|
||||
"../editor/source-editor": "devtools/client/sourceeditor/editor",
|
||||
|
@ -59,5 +16,4 @@ module.exports = {
|
|||
"devtools-services": "Services",
|
||||
"wasmparser/dist/cjs/WasmParser": "devtools/client/shared/vendor/WasmParser",
|
||||
"wasmparser/dist/cjs/WasmDis": "devtools/client/shared/vendor/WasmDis",
|
||||
"whatwg-url": `var ${whatwgUrl}`,
|
||||
};
|
||||
|
|
|
@ -25,6 +25,7 @@ Array [
|
|||
"thread": "FakeThread",
|
||||
},
|
||||
],
|
||||
"filename": "a",
|
||||
"source": Object {
|
||||
"extensionName": null,
|
||||
"id": "a",
|
||||
|
@ -42,12 +43,8 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`breakpoints should not re-add a breakpoint 1`] = `Array []`;
|
||||
|
||||
exports[`breakpoints should not show a breakpoint that does not have text 1`] = `Array []`;
|
||||
|
||||
exports[`breakpoints should not show a breakpoint that does not have text 2`] = `Array []`;
|
||||
|
||||
exports[`breakpoints should remap breakpoints on pretty print 1`] = `
|
||||
Object {
|
||||
"disabled": false,
|
||||
|
@ -96,6 +93,7 @@ Array [
|
|||
"thread": "FakeThread",
|
||||
},
|
||||
],
|
||||
"filename": "a",
|
||||
"source": Object {
|
||||
"extensionName": null,
|
||||
"id": "a",
|
||||
|
|
|
@ -42,7 +42,10 @@ class EmptyLines extends Component {
|
|||
shouldComponentUpdate(nextProps) {
|
||||
const { breakableLines, selectedSource } = this.props;
|
||||
return (
|
||||
breakableLines != nextProps.breakableLines ||
|
||||
// Breakable lines are something that evolves over time,
|
||||
// but we either have them loaded or not. So only compare the size
|
||||
// as sometimes we always get a blank new empty Set instance.
|
||||
breakableLines.size != nextProps.breakableLines.size ||
|
||||
selectedSource.id != nextProps.selectedSource.id
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,10 +15,7 @@ import actions from "../../../actions";
|
|||
import { getSelectedLocation } from "../../../utils/selected-location";
|
||||
import { createHeadlessEditor } from "../../../utils/editor/create-editor";
|
||||
|
||||
import {
|
||||
makeBreakpointId,
|
||||
sortSelectedBreakpoints,
|
||||
} from "../../../utils/breakpoint";
|
||||
import { makeBreakpointId } from "../../../utils/breakpoint";
|
||||
|
||||
import { getSelectedSource, getBreakpointSources } from "../../../selectors";
|
||||
|
||||
|
@ -88,23 +85,18 @@ class Breakpoints extends Component {
|
|||
}
|
||||
|
||||
const editor = this.getEditor();
|
||||
const sources = [...breakpointSources.map(({ source }) => source)];
|
||||
const sources = breakpointSources.map(({ source }) => source);
|
||||
|
||||
return (
|
||||
<div className="pane breakpoints-list">
|
||||
{breakpointSources.map(({ source, breakpoints }) => {
|
||||
const sortedBreakpoints = sortSelectedBreakpoints(
|
||||
breakpoints,
|
||||
selectedSource
|
||||
);
|
||||
|
||||
return [
|
||||
<BreakpointHeading
|
||||
key={source.id}
|
||||
source={source}
|
||||
sources={sources}
|
||||
/>,
|
||||
...sortedBreakpoints.map(breakpoint => (
|
||||
breakpoints.map(breakpoint => (
|
||||
<Breakpoint
|
||||
breakpoint={breakpoint}
|
||||
source={source}
|
||||
|
|
|
@ -3,55 +3,83 @@
|
|||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
import { createSelector } from "reselect";
|
||||
import { getSelectedSource, getSourceFromId } from "./sources";
|
||||
import { getSelectedSource, getSourcesMap } from "./sources";
|
||||
import { getBreakpointsList } from "./breakpoints";
|
||||
import { getFilename } from "../utils/source";
|
||||
import { getSelectedLocation } from "../utils/selected-location";
|
||||
import { sortSelectedBreakpoints } from "../utils/breakpoint";
|
||||
|
||||
function getBreakpointsForSource(source, selectedSource, breakpoints) {
|
||||
return sortSelectedBreakpoints(breakpoints, selectedSource)
|
||||
.filter(
|
||||
bp =>
|
||||
!bp.options.hidden &&
|
||||
(bp.text || bp.originalText || bp.options.condition || bp.disabled)
|
||||
)
|
||||
.filter(
|
||||
bp => getSelectedLocation(bp, selectedSource).sourceId == source.id
|
||||
);
|
||||
// Returns all the breakpoints for the given selected source
|
||||
// Depending on the selected source, this will match original or generated
|
||||
// location of the given selected source.
|
||||
function _getBreakpointsForSource(visibleBreakpoints, source, selectedSource) {
|
||||
return visibleBreakpoints.filter(
|
||||
bp => getSelectedLocation(bp, selectedSource).sourceId == source.id
|
||||
);
|
||||
}
|
||||
|
||||
const getSourcesForBreakpoints = state => {
|
||||
const selectedSource = getSelectedSource(state);
|
||||
const breakpointSourceIds = getBreakpointsList(state).map(
|
||||
// Returns a sorted list of sources for which we have breakpoints
|
||||
// We will return generated or original source IDs based on the currently selected source.
|
||||
const _getSourcesForBreakpoints = (breakpoints, sourcesMap, selectedSource) => {
|
||||
const breakpointSourceIds = breakpoints.map(
|
||||
breakpoint => getSelectedLocation(breakpoint, selectedSource).sourceId
|
||||
);
|
||||
|
||||
return [...new Set(breakpointSourceIds)]
|
||||
.map(sourceId => {
|
||||
const source = getSourceFromId(state, sourceId);
|
||||
const filename = getFilename(source);
|
||||
return { source, filename };
|
||||
})
|
||||
.filter(({ source }) => source && !source.isBlackBoxed)
|
||||
.sort((a, b) => a.filename - b.filename)
|
||||
.map(({ source }) => source);
|
||||
const sources = [];
|
||||
// We may have more than one breakpoint per sourceId,
|
||||
// so use a Set to have a unique list of source IDs.
|
||||
for (const sourceId of [...new Set(breakpointSourceIds)]) {
|
||||
const source = sourcesMap.get(sourceId);
|
||||
|
||||
// Ignore any source that is no longer in the sources reducer
|
||||
// or blackboxed sources.
|
||||
if (!source || source.isBlackBoxed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bps = _getBreakpointsForSource(breakpoints, source, selectedSource);
|
||||
|
||||
// Ignore sources which have no breakpoints
|
||||
if (bps.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sources.push({
|
||||
source,
|
||||
breakpoints: bps,
|
||||
filename: getFilename(source),
|
||||
});
|
||||
}
|
||||
|
||||
return sources.sort((a, b) => a.filename.localeCompare(b.filename));
|
||||
};
|
||||
|
||||
// Returns a list of sources with their related breakpoints:
|
||||
// [{ source, breakpoints [breakpoint1, ...] }, ...]
|
||||
//
|
||||
// This only returns sources for which we have a visible breakpoint.
|
||||
// This will return either generated or original source based on the currently
|
||||
// selected source.
|
||||
export const getBreakpointSources = createSelector(
|
||||
getBreakpointsList,
|
||||
getSourcesForBreakpoints,
|
||||
getSourcesMap,
|
||||
getSelectedSource,
|
||||
(breakpoints, sources, selectedSource) => {
|
||||
return sources
|
||||
.map(source => ({
|
||||
source,
|
||||
breakpoints: getBreakpointsForSource(
|
||||
source,
|
||||
selectedSource,
|
||||
breakpoints
|
||||
),
|
||||
}))
|
||||
.filter(({ breakpoints: bps }) => bps.length > 0);
|
||||
(breakpoints, sourcesMap, selectedSource) => {
|
||||
const visibleBreakpoints = breakpoints.filter(
|
||||
bp =>
|
||||
!bp.options.hidden &&
|
||||
(bp.text || bp.originalText || bp.options.condition || bp.disabled)
|
||||
);
|
||||
|
||||
const sortedVisibleBreakpoints = sortSelectedBreakpoints(
|
||||
visibleBreakpoints,
|
||||
selectedSource
|
||||
);
|
||||
|
||||
return _getSourcesForBreakpoints(
|
||||
sortedVisibleBreakpoints,
|
||||
sourcesMap,
|
||||
selectedSource
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -90,15 +90,24 @@ export function getSourceActorBreakableLines(state, id) {
|
|||
* @param {Object} state
|
||||
* @param {Array<String>} ids
|
||||
* List of Source Actor IDs
|
||||
* @return {AsyncValue<Array<Number>>}
|
||||
* @param {Boolean} isHTML
|
||||
* True, if we are fetching the breakable lines for an HTML source.
|
||||
* For them, we have to aggregate the lines of each source actors.
|
||||
* Otherwise, we might still have many source actors, but one per thread.
|
||||
* In this case, we simply return the first source actor to have the lines ready.
|
||||
* @return {Array<Number>}
|
||||
* List of all the breakable lines.
|
||||
*/
|
||||
export function getBreakableLinesForSourceActors(state, ids) {
|
||||
export function getBreakableLinesForSourceActors(state, ids, isHTML) {
|
||||
const allBreakableLines = [];
|
||||
for (const id of ids) {
|
||||
const { breakableLines } = getSourceActor(state, id);
|
||||
if (breakableLines && breakableLines.state == "fulfilled") {
|
||||
allBreakableLines.push(...breakableLines.value);
|
||||
if (isHTML) {
|
||||
allBreakableLines.push(...breakableLines.value);
|
||||
} else {
|
||||
return breakableLines.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return allBreakableLines;
|
||||
|
|
|
@ -140,7 +140,7 @@ export function getHasSiblingOfSameName(state, source) {
|
|||
return getSourcesUrlsInSources(state, source.url).length > 1;
|
||||
}
|
||||
|
||||
// This is only used externaly by tabs selectors
|
||||
// This is only used externaly by tabs and breakpointSources selectors
|
||||
export function getSourcesMap(state) {
|
||||
return state.sources.sources;
|
||||
}
|
||||
|
@ -377,7 +377,7 @@ export function getBreakableLines(state, sourceId) {
|
|||
|
||||
// We pull generated file breakable lines directly from the source actors
|
||||
// so that breakable lines can be added as new source actors on HTML loads.
|
||||
return getBreakableLinesForSourceActors(state, sourceActorIDs);
|
||||
return getBreakableLinesForSourceActors(state, sourceActorIDs, source.isHTML);
|
||||
}
|
||||
|
||||
export const getSelectedBreakableLines = createSelector(
|
||||
|
|
|
@ -113,11 +113,7 @@ describe("sources-tree", () => {
|
|||
expect(base.name).toBe("webpack://");
|
||||
expect(base.contents).toHaveLength(1);
|
||||
|
||||
const emptyNode = base.contents[0];
|
||||
expect(emptyNode.name).toBe("");
|
||||
expect(emptyNode.contents).toHaveLength(1);
|
||||
|
||||
const userNode = emptyNode.contents[0];
|
||||
const userNode = base.contents[0];
|
||||
expect(userNode.name).toBe("Users");
|
||||
expect(userNode.contents).toHaveLength(1);
|
||||
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
import { URL as URLParser } from "whatwg-url";
|
||||
|
||||
const defaultUrl = {
|
||||
hash: "",
|
||||
host: "",
|
||||
|
@ -57,7 +55,7 @@ export function parse(url) {
|
|||
|
||||
let urlObj;
|
||||
try {
|
||||
urlObj = new URLParser(url);
|
||||
urlObj = new URL(url);
|
||||
} catch (err) {
|
||||
urlObj = { ...defaultUrl };
|
||||
// If we're given simply a filename...
|
||||
|
@ -89,6 +87,10 @@ export function parse(url) {
|
|||
urlObj.pathname = url;
|
||||
}
|
||||
}
|
||||
// When provided a special URL like "webpack:///webpack/foo",
|
||||
// prevents passing the three slashes in the path, and pass only onea.
|
||||
// This will prevent displaying modules in empty-name sub folders.
|
||||
urlObj.pathname = urlObj.pathname.replace(/\/+/, "/");
|
||||
urlObj.path = urlObj.pathname + urlObj.search;
|
||||
|
||||
// Cache the result
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
|
@ -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,
|
||||
|
|
|
@ -10,6 +10,7 @@ DevToolsModules(
|
|||
"RequestListColumnFile.js",
|
||||
"RequestListColumnInitiator.js",
|
||||
"RequestListColumnMethod.js",
|
||||
"RequestListColumnPriority.js",
|
||||
"RequestListColumnProtocol.js",
|
||||
"RequestListColumnRemoteIP.js",
|
||||
"RequestListColumnResponseHeader.js",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -43,6 +43,7 @@ const cols = {
|
|||
setCookies: false,
|
||||
transferred: true,
|
||||
contentSize: true,
|
||||
priority: false,
|
||||
startTime: false,
|
||||
endTime: false,
|
||||
responseTime: false,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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:",
|
||||
|
|
|
@ -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);
|
||||
});
|
|
@ -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
1
devtools/client/shared/vendor/moz.build
vendored
1
devtools/client/shared/vendor/moz.build
vendored
|
@ -23,7 +23,6 @@ DevToolsModules(
|
|||
'seamless-immutable.js',
|
||||
'WasmDis.js',
|
||||
'WasmParser.js',
|
||||
'whatwg-url.js',
|
||||
)
|
||||
|
||||
# react dev versions are used if enable-debug-js-modules is set in .mozconfig.
|
||||
|
|
8851
devtools/client/shared/vendor/whatwg-url.js
vendored
8851
devtools/client/shared/vendor/whatwg-url.js
vendored
File diff suppressed because one or more lines are too long
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -3561,6 +3561,7 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
|
|||
const char* errorDescriptionID = nullptr;
|
||||
AutoTArray<nsString, 3> formatStrs;
|
||||
bool addHostPort = false;
|
||||
bool isBadStsCertError = false;
|
||||
nsresult rv = NS_OK;
|
||||
nsAutoString messageStr;
|
||||
nsAutoCString cssClass;
|
||||
|
@ -3710,6 +3711,7 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
|
|||
// In the future we should differentiate between an HSTS host and a
|
||||
// pinned host and display a more informative message to the user.
|
||||
if (isStsHost || isPinnedHost) {
|
||||
isBadStsCertError = true;
|
||||
cssClass.AssignLiteral("badStsCert");
|
||||
}
|
||||
|
||||
|
@ -3870,19 +3872,21 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
|
|||
}
|
||||
}
|
||||
|
||||
nsresult delegateErrorCode = aError;
|
||||
// If the HTTPS-Only Mode upgraded this request and the upgrade might have
|
||||
// caused this error, we replace the error-page with about:httpsonlyerror
|
||||
bool isHttpsOnlyError =
|
||||
nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(aFailedChannel, aError);
|
||||
if (isHttpsOnlyError) {
|
||||
if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(aFailedChannel, aError)) {
|
||||
errorPage.AssignLiteral("httpsonlyerror");
|
||||
delegateErrorCode = NS_ERROR_HTTPS_ONLY;
|
||||
} else if (isBadStsCertError) {
|
||||
delegateErrorCode = NS_ERROR_BAD_HSTS_CERT;
|
||||
}
|
||||
|
||||
if (nsCOMPtr<nsILoadURIDelegate> loadURIDelegate = GetLoadURIDelegate()) {
|
||||
nsresult code = isHttpsOnlyError ? NS_ERROR_HTTPS_ONLY : aError;
|
||||
nsCOMPtr<nsIURI> errorPageURI;
|
||||
rv = loadURIDelegate->HandleLoadError(aURI, code, NS_ERROR_GET_MODULE(code),
|
||||
getter_AddRefs(errorPageURI));
|
||||
rv = loadURIDelegate->HandleLoadError(
|
||||
aURI, delegateErrorCode, NS_ERROR_GET_MODULE(delegateErrorCode),
|
||||
getter_AddRefs(errorPageURI));
|
||||
// If the docshell is going away there's no point in showing an error page.
|
||||
if (NS_FAILED(rv) || mIsBeingDestroyed) {
|
||||
*aDisplayedErrorPage = false;
|
||||
|
|
|
@ -15,20 +15,9 @@
|
|||
|
||||
namespace mozilla::dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(AbortController)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbortController)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mSignal)
|
||||
tmp->mReason.setUndefined();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbortController)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mSignal)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AbortController)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReason)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(AbortController,
|
||||
(mGlobal, mSignal),
|
||||
(mReason))
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AbortController)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AbortController)
|
||||
|
|
|
@ -566,38 +566,6 @@ void ChromeUtils::Import(const GlobalObject& aGlobal,
|
|||
aRetval.set(exports);
|
||||
}
|
||||
|
||||
/* static */
|
||||
void ChromeUtils::ImportModule(const GlobalObject& aGlobal,
|
||||
const nsAString& aResourceURI,
|
||||
JS::MutableHandle<JSObject*> aRetval,
|
||||
ErrorResult& aRv) {
|
||||
RefPtr<mozJSComponentLoader> moduleloader = mozJSComponentLoader::Get();
|
||||
MOZ_ASSERT(moduleloader);
|
||||
|
||||
NS_ConvertUTF16toUTF8 registryLocation(aResourceURI);
|
||||
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE(
|
||||
"ChromeUtils::ImportModule", OTHER, registryLocation);
|
||||
|
||||
JSContext* cx = aGlobal.Context();
|
||||
|
||||
JS::RootedObject moduleNamespace(cx);
|
||||
nsresult rv =
|
||||
moduleloader->ImportModule(cx, registryLocation, &moduleNamespace);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
||||
|
||||
if (!JS_WrapObject(cx, &moduleNamespace)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
aRetval.set(moduleNamespace);
|
||||
}
|
||||
|
||||
namespace module_getter {
|
||||
static const size_t SLOT_ID = 0;
|
||||
static const size_t SLOT_URI = 1;
|
||||
|
|
|
@ -196,11 +196,6 @@ class ChromeUtils {
|
|||
const Optional<JS::Handle<JSObject*>>& aTargetObj,
|
||||
JS::MutableHandle<JSObject*> aRetval, ErrorResult& aRv);
|
||||
|
||||
static void ImportModule(const GlobalObject& aGlobal,
|
||||
const nsAString& aResourceURI,
|
||||
JS::MutableHandle<JSObject*> aRetval,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static void DefineModuleGetter(const GlobalObject& global,
|
||||
JS::Handle<JSObject*> target,
|
||||
const nsAString& id,
|
||||
|
|
|
@ -147,24 +147,9 @@ NS_INTERFACE_MAP_END
|
|||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Exception)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Exception)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(Exception)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Exception)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Exception)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mThrownJSVal)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Exception)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mData)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
tmp->mThrownJSVal.setNull();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(Exception,
|
||||
(mLocation, mData),
|
||||
(mThrownJSVal))
|
||||
|
||||
Exception::Exception(const nsACString& aMessage, nsresult aResult,
|
||||
const nsACString& aName, nsIStackFrame* aLocation,
|
||||
|
|
|
@ -316,7 +316,8 @@ static BrowsingContextOrigin SimilarOrigin(const Element& aTarget,
|
|||
|
||||
// NOTE: This returns nullptr if |aDocument| is in another process from the top
|
||||
// level content document.
|
||||
static Document* GetTopLevelContentDocumentInThisProcess(Document& aDocument) {
|
||||
static const Document* GetTopLevelContentDocumentInThisProcess(
|
||||
const Document& aDocument) {
|
||||
auto* wc = aDocument.GetTopLevelWindowContext();
|
||||
return wc ? wc->GetExtantDoc() : nullptr;
|
||||
}
|
||||
|
@ -462,9 +463,9 @@ struct OopIframeMetrics {
|
|||
nsRect mRemoteDocumentVisibleRect;
|
||||
};
|
||||
|
||||
static Maybe<OopIframeMetrics> GetOopIframeMetrics(Document& aDocument,
|
||||
Document* aRootDocument) {
|
||||
Document* rootDoc =
|
||||
static Maybe<OopIframeMetrics> GetOopIframeMetrics(
|
||||
const Document& aDocument, const Document* aRootDocument) {
|
||||
const Document* rootDoc =
|
||||
nsContentUtils::GetInProcessSubtreeRootDocument(&aDocument);
|
||||
MOZ_ASSERT(rootDoc);
|
||||
|
||||
|
@ -522,9 +523,10 @@ static Maybe<OopIframeMetrics> GetOopIframeMetrics(Document& aDocument,
|
|||
}
|
||||
|
||||
// https://w3c.github.io/IntersectionObserver/#update-intersection-observations-algo
|
||||
// (step 2)
|
||||
void DOMIntersectionObserver::Update(Document* aDocument,
|
||||
DOMHighResTimeStamp time) {
|
||||
// step 2.1
|
||||
IntersectionInput DOMIntersectionObserver::ComputeInput(
|
||||
const Document& aDocument, const nsINode* aRoot,
|
||||
const StyleRect<LengthPercentage>* aRootMargin) {
|
||||
// 1 - Let rootBounds be observer's root intersection rectangle.
|
||||
// ... but since the intersection rectangle depends on the target, we defer
|
||||
// the inflation until later.
|
||||
|
@ -533,10 +535,11 @@ void DOMIntersectionObserver::Update(Document* aDocument,
|
|||
// document.
|
||||
nsRect rootRect;
|
||||
nsIFrame* rootFrame = nullptr;
|
||||
nsINode* root = mRoot;
|
||||
const nsINode* root = aRoot;
|
||||
const bool isImplicitRoot = !aRoot;
|
||||
Maybe<nsRect> remoteDocumentVisibleRect;
|
||||
if (mRoot && mRoot->IsElement()) {
|
||||
if ((rootFrame = mRoot->AsElement()->GetPrimaryFrame())) {
|
||||
if (aRoot && aRoot->IsElement()) {
|
||||
if ((rootFrame = aRoot->AsElement()->GetPrimaryFrame())) {
|
||||
nsRect rootRectRelativeToRootFrame;
|
||||
if (nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame)) {
|
||||
// rootRectRelativeToRootFrame should be the content rect of rootFrame,
|
||||
|
@ -552,10 +555,10 @@ void DOMIntersectionObserver::Update(Document* aDocument,
|
|||
rootFrame, rootRectRelativeToRootFrame, containingBlock);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(!mRoot || mRoot->IsDocument());
|
||||
Document* rootDocument =
|
||||
mRoot ? mRoot->AsDocument()
|
||||
: GetTopLevelContentDocumentInThisProcess(*aDocument);
|
||||
MOZ_ASSERT(!aRoot || aRoot->IsDocument());
|
||||
const Document* rootDocument =
|
||||
aRoot ? aRoot->AsDocument()
|
||||
: GetTopLevelContentDocumentInThisProcess(aDocument);
|
||||
root = rootDocument;
|
||||
|
||||
if (rootDocument) {
|
||||
|
@ -581,7 +584,7 @@ void DOMIntersectionObserver::Update(Document* aDocument,
|
|||
}
|
||||
|
||||
if (Maybe<OopIframeMetrics> metrics =
|
||||
GetOopIframeMetrics(*aDocument, rootDocument)) {
|
||||
GetOopIframeMetrics(aDocument, rootDocument)) {
|
||||
rootFrame = metrics->mInProcessRootFrame;
|
||||
if (!rootDocument) {
|
||||
rootRect = metrics->mInProcessRootRect;
|
||||
|
@ -592,101 +595,104 @@ void DOMIntersectionObserver::Update(Document* aDocument,
|
|||
|
||||
nsMargin rootMargin; // This root margin is NOT applied in `implicit root`
|
||||
// case, e.g. in out-of-process iframes.
|
||||
for (const auto side : mozilla::AllPhysicalSides()) {
|
||||
nscoord basis = side == eSideTop || side == eSideBottom ? rootRect.Height()
|
||||
: rootRect.Width();
|
||||
rootMargin.Side(side) = mRootMargin.Get(side).Resolve(
|
||||
basis, static_cast<nscoord (*)(float)>(NSToCoordRoundWithClamp));
|
||||
if (aRootMargin) {
|
||||
for (const auto side : mozilla::AllPhysicalSides()) {
|
||||
nscoord basis = side == eSideTop || side == eSideBottom
|
||||
? rootRect.Height()
|
||||
: rootRect.Width();
|
||||
rootMargin.Side(side) = aRootMargin->Get(side).Resolve(
|
||||
basis, static_cast<nscoord (*)(float)>(NSToCoordRoundWithClamp));
|
||||
}
|
||||
}
|
||||
return {isImplicitRoot, root, rootFrame,
|
||||
rootRect, rootMargin, remoteDocumentVisibleRect};
|
||||
}
|
||||
|
||||
// https://w3c.github.io/IntersectionObserver/#update-intersection-observations-algo
|
||||
// (steps 2.1 - 2.5)
|
||||
IntersectionOutput DOMIntersectionObserver::Intersect(
|
||||
const IntersectionInput& aInput, Element& aTarget) {
|
||||
const bool isSimilarOrigin = SimilarOrigin(aTarget, aInput.mRootNode) ==
|
||||
BrowsingContextOrigin::Similar;
|
||||
nsIFrame* targetFrame = aTarget.GetPrimaryFrame();
|
||||
if (!targetFrame || !aInput.mRootFrame) {
|
||||
return {isSimilarOrigin};
|
||||
}
|
||||
|
||||
// "From the perspective of an IntersectionObserver, the skipped contents
|
||||
// of an element are never intersecting the intersection root. This is
|
||||
// true even if both the root and the target elements are in the skipped
|
||||
// contents."
|
||||
// https://drafts.csswg.org/css-contain/#cv-notes
|
||||
if (targetFrame->AncestorHidesContent()) {
|
||||
return {isSimilarOrigin};
|
||||
}
|
||||
|
||||
// 2.2. If the intersection root is not the implicit root, and target is
|
||||
// not in the same Document as the intersection root, skip to step 11.
|
||||
if (!aInput.mIsImplicitRoot &&
|
||||
aInput.mRootNode->OwnerDoc() != aTarget.OwnerDoc()) {
|
||||
return {isSimilarOrigin};
|
||||
}
|
||||
|
||||
// 2.3. If the intersection root is an element and target is not a descendant
|
||||
// of the intersection root in the containing block chain, skip to step 11.
|
||||
//
|
||||
// NOTE(emilio): We also do this if target is the implicit root, pending
|
||||
// clarification in
|
||||
// https://github.com/w3c/IntersectionObserver/issues/456.
|
||||
if (aInput.mRootFrame == targetFrame ||
|
||||
!nsLayoutUtils::IsAncestorFrameCrossDocInProcess(aInput.mRootFrame,
|
||||
targetFrame)) {
|
||||
return {isSimilarOrigin};
|
||||
}
|
||||
|
||||
nsRect rootBounds = aInput.mRootRect;
|
||||
if (isSimilarOrigin) {
|
||||
rootBounds.Inflate(aInput.mRootMargin);
|
||||
}
|
||||
|
||||
// 2.4. Set targetRect to the DOMRectReadOnly obtained by running the
|
||||
// getBoundingClientRect() algorithm on target.
|
||||
nsRect targetRect = targetFrame->GetBoundingClientRect();
|
||||
|
||||
// 2.5. Let intersectionRect be the result of running the compute the
|
||||
// intersection algorithm on target and observer’s 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 observer’s 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 targetRect’s 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 intersectionRect’s area.
|
||||
int64_t intersectionArea = !intersectionRect
|
||||
? 0
|
||||
: (int64_t)intersectionRect->Width() *
|
||||
(int64_t)intersectionRect->Height();
|
||||
int64_t intersectionArea =
|
||||
!output.mIntersectionRect
|
||||
? 0
|
||||
: (int64_t)output.mIntersectionRect->Width() *
|
||||
(int64_t)output.mIntersectionRect->Height();
|
||||
|
||||
// 2.7. Let isIntersecting be true if targetRect and rootBounds intersect or
|
||||
// are edge-adjacent, even if the intersection has zero area (because
|
||||
// rootBounds or targetRect have zero area); otherwise, let isIntersecting
|
||||
// be false.
|
||||
const bool isIntersecting = intersectionRect.isSome();
|
||||
const bool isIntersecting = output.Intersects();
|
||||
|
||||
// 2.8. If targetArea is non-zero, let intersectionRatio be intersectionArea
|
||||
// divided by targetArea. Otherwise, let intersectionRatio be 1 if
|
||||
|
@ -729,9 +735,9 @@ void DOMIntersectionObserver::Update(Document* aDocument,
|
|||
// entry's isIntersecting value.
|
||||
QueueIntersectionObserverEntry(
|
||||
target, time,
|
||||
origin == BrowsingContextOrigin::Similar ? Some(rootBounds)
|
||||
: Nothing(),
|
||||
targetRect, intersectionRect, thresholdIndex > 0, intersectionRatio);
|
||||
output.mIsSimilarOrigin ? Some(output.mRootBounds) : Nothing(),
|
||||
output.mTargetRect, output.mIntersectionRect, thresholdIndex > 0,
|
||||
intersectionRatio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,32 @@ class DOMIntersectionObserverEntry final : public nsISupports,
|
|||
} \
|
||||
}
|
||||
|
||||
// An input suitable to compute intersections with multiple targets.
|
||||
struct IntersectionInput {
|
||||
// Whether the root is implicit (null, originally).
|
||||
const bool mIsImplicitRoot = false;
|
||||
// The computed root node. For the implicit root, this will be the in-process
|
||||
// root document we can compute coordinates against (along with the remote
|
||||
// document visible rect if appropriate).
|
||||
const nsINode* mRootNode = nullptr;
|
||||
nsIFrame* mRootFrame = nullptr;
|
||||
// The rect of mRootFrame in client coordinates.
|
||||
nsRect mRootRect;
|
||||
// The root margin computed against the root rect.
|
||||
nsMargin mRootMargin;
|
||||
// If this is in an OOP iframe, the visible rect of the OOP frame.
|
||||
Maybe<nsRect> mRemoteDocumentVisibleRect;
|
||||
};
|
||||
|
||||
struct IntersectionOutput {
|
||||
const bool mIsSimilarOrigin;
|
||||
const nsRect mRootBounds;
|
||||
const nsRect mTargetRect;
|
||||
const Maybe<nsRect> mIntersectionRect;
|
||||
|
||||
bool Intersects() const { return mIntersectionRect.isSome(); }
|
||||
};
|
||||
|
||||
class DOMIntersectionObserver final : public nsISupports,
|
||||
public nsWrapperCache {
|
||||
virtual ~DOMIntersectionObserver() { Disconnect(); }
|
||||
|
@ -122,6 +148,11 @@ class DOMIntersectionObserver final : public nsISupports,
|
|||
|
||||
void TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal);
|
||||
|
||||
static IntersectionInput ComputeInput(
|
||||
const Document& aDocument, const nsINode* aRoot,
|
||||
const StyleRect<LengthPercentage>* aRootMargin);
|
||||
static IntersectionOutput Intersect(const IntersectionInput&, Element&);
|
||||
|
||||
void Update(Document* aDocument, DOMHighResTimeStamp time);
|
||||
MOZ_CAN_RUN_SCRIPT void Notify();
|
||||
|
||||
|
|
|
@ -36,26 +36,10 @@ DOMRequest::DOMRequest(nsIGlobalObject* aGlobal)
|
|||
|
||||
DOMRequest::~DOMRequest() { mozilla::DropJSObjects(this); }
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(DOMRequest)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMRequest,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMRequest,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
|
||||
tmp->mResult.setUndefined();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest, DOMEventTargetHelper)
|
||||
// Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
|
||||
// DOMEventTargetHelper does it for us.
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResult)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED_WITH_JS_MEMBERS(DOMRequest,
|
||||
DOMEventTargetHelper,
|
||||
(mError, mPromise),
|
||||
(mResult))
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMRequest)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -12,32 +12,10 @@
|
|||
|
||||
namespace mozilla::dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(Pose)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Pose)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
tmp->mPosition = nullptr;
|
||||
tmp->mLinearVelocity = nullptr;
|
||||
tmp->mLinearAcceleration = nullptr;
|
||||
tmp->mOrientation = nullptr;
|
||||
tmp->mAngularVelocity = nullptr;
|
||||
tmp->mAngularAcceleration = nullptr;
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Pose)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Pose)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPosition)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLinearVelocity)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLinearAcceleration)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOrientation)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAngularVelocity)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAngularAcceleration)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(
|
||||
Pose, (mParent),
|
||||
(mPosition, mLinearVelocity, mLinearAcceleration, mOrientation,
|
||||
mAngularVelocity, mAngularAcceleration))
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Pose, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Pose, Release)
|
||||
|
|
|
@ -6991,11 +6991,11 @@ void nsContentUtils::FireMutationEventsForDirectParsing(
|
|||
}
|
||||
|
||||
/* static */
|
||||
Document* nsContentUtils::GetInProcessSubtreeRootDocument(Document* aDoc) {
|
||||
const Document* nsContentUtils::GetInProcessSubtreeRootDocument(const Document* aDoc) {
|
||||
if (!aDoc) {
|
||||
return nullptr;
|
||||
}
|
||||
Document* doc = aDoc;
|
||||
const Document* doc = aDoc;
|
||||
while (doc->GetInProcessParentDocument()) {
|
||||
doc = doc->GetInProcessParentDocument();
|
||||
}
|
||||
|
@ -7589,9 +7589,17 @@ nsresult nsContentUtils::IPCTransferableToTransferable(
|
|||
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
const nsString& text = item.data().get_nsString();
|
||||
rv = dataWrapper->SetData(text);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (item.data().type() == IPCDataTransferData::TShmem) {
|
||||
Shmem itemData = item.data().get_Shmem();
|
||||
const nsDependentSubstring text(itemData.get<char16_t>(),
|
||||
itemData.Size<char16_t>());
|
||||
rv = dataWrapper->SetData(text);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
const nsString& text = item.data().get_nsString();
|
||||
rv = dataWrapper->SetData(text);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -7673,6 +7681,13 @@ nsresult nsContentUtils::IPCTransferableItemToVariant(
|
|||
});
|
||||
|
||||
if (aDataTransferItem.dataType() == TransferableDataType::String) {
|
||||
if (aDataTransferItem.data().type() == IPCDataTransferData::TShmem) {
|
||||
Shmem data = aDataTransferItem.data().get_Shmem();
|
||||
aVariant->SetAsAString(
|
||||
nsDependentSubstring(data.get<char16_t>(), data.Size<char16_t>()));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const nsString& data = aDataTransferItem.data().get_nsString();
|
||||
aVariant->SetAsAString(data);
|
||||
return NS_OK;
|
||||
|
@ -7851,17 +7866,23 @@ bool nsContentUtils::IsFlavorImage(const nsACString& aFlavor) {
|
|||
aFlavor.EqualsLiteral(kGIFImageMime);
|
||||
}
|
||||
|
||||
static Shmem ConvertToShmem(mozilla::dom::ContentChild* aChild,
|
||||
mozilla::dom::ContentParent* aParent,
|
||||
const nsACString& aInput) {
|
||||
static bool AllocateShmem(mozilla::dom::ContentChild* aChild,
|
||||
mozilla::dom::ContentParent* aParent, size_t aSize,
|
||||
mozilla::ipc::Shmem* aShmem) {
|
||||
MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
|
||||
MOZ_ASSERT(aShmem);
|
||||
|
||||
IShmemAllocator* allocator = aChild ? static_cast<IShmemAllocator*>(aChild)
|
||||
: static_cast<IShmemAllocator*>(aParent);
|
||||
|
||||
return allocator->AllocShmem(aSize, SharedMemory::TYPE_BASIC, aShmem);
|
||||
}
|
||||
|
||||
static Shmem ConvertToShmem(mozilla::dom::ContentChild* aChild,
|
||||
mozilla::dom::ContentParent* aParent,
|
||||
const nsACString& aInput) {
|
||||
Shmem result;
|
||||
if (!allocator->AllocShmem(aInput.Length(), SharedMemory::TYPE_BASIC,
|
||||
&result)) {
|
||||
if (!AllocateShmem(aChild, aParent, aInput.Length(), &result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -7870,6 +7891,20 @@ static Shmem ConvertToShmem(mozilla::dom::ContentChild* aChild,
|
|||
return result;
|
||||
}
|
||||
|
||||
static Shmem ConvertToShmem(mozilla::dom::ContentChild* aChild,
|
||||
mozilla::dom::ContentParent* aParent,
|
||||
const nsAString& aInput) {
|
||||
Shmem result;
|
||||
uint32_t size = aInput.Length() * sizeof(char16_t);
|
||||
if (!AllocateShmem(aChild, aParent, size, &result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
memcpy(result.get<char>(), aInput.BeginReading(), size);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void nsContentUtils::TransferableToIPCTransferable(
|
||||
nsITransferable* aTransferable, IPCDataTransfer* aIPCDataTransfer,
|
||||
bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
|
||||
|
@ -7919,9 +7954,31 @@ void nsContentUtils::TransferableToIPCTransferable(
|
|||
if (nsCOMPtr<nsISupportsString> text = do_QueryInterface(data)) {
|
||||
nsAutoString dataAsString;
|
||||
text->GetData(dataAsString);
|
||||
|
||||
Maybe<Shmem> dataAsShmem;
|
||||
uint32_t size = dataAsString.Length() * sizeof(char16_t);
|
||||
// XXX IPCDataTransfer could contain multiple items, we give each item
|
||||
// same bucket size. The IPC message includes more than data payload, so
|
||||
// subtract 10 KB to make the total size within the bucket size. It
|
||||
// would be nice if we could have a smarter way to decide when to use
|
||||
// Shmem.
|
||||
uint32_t threshold =
|
||||
(IPC::Channel::kMaximumMessageSize / flavorList.Length()) -
|
||||
(10 * 1024);
|
||||
if (size > threshold) {
|
||||
dataAsShmem.emplace(ConvertToShmem(aChild, aParent, dataAsString));
|
||||
if (!dataAsShmem->IsReadable() || !dataAsShmem->Size<char16_t>()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
|
||||
item->flavor() = flavorStr;
|
||||
item->data() = dataAsString;
|
||||
if (dataAsShmem) {
|
||||
item->data() = dataAsShmem.value();
|
||||
} else {
|
||||
item->data() = dataAsString;
|
||||
}
|
||||
item->dataType() = TransferableDataType::String;
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -495,6 +495,7 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID)
|
|||
class_, native_members_, js_members_) \
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(class_) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(class_) \
|
||||
using ::ImplCycleCollectionUnlink; \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK( \
|
||||
MOZ_FOR_EACH_EXPAND_HELPER native_members_) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(MOZ_FOR_EACH_EXPAND_HELPER js_members_) \
|
||||
|
|
|
@ -145,9 +145,22 @@ bool OffscreenCanvasDisplayHelper::CommitFrameToCompositor(
|
|||
} else {
|
||||
surface = aContext->GetFrontBufferSnapshot(/* requireAlphaPremult */ false);
|
||||
if (surface) {
|
||||
auto surfaceImage = MakeRefPtr<layers::SourceSurfaceImage>(surface);
|
||||
surfaceImage->SetTextureFlags(flags);
|
||||
image = surfaceImage;
|
||||
bool usable = true;
|
||||
if (surface->GetType() == gfx::SurfaceType::WEBGL) {
|
||||
// Ensure we can map in the surface. If we get a SourceSurfaceWebgl
|
||||
// surface, then it may not be backed by raw pixels yet. We need to map
|
||||
// it on the owning thread rather than the ImageBridge thread.
|
||||
gfx::DataSourceSurface::ScopedMap map(
|
||||
static_cast<gfx::DataSourceSurface*>(surface.get()),
|
||||
gfx::DataSourceSurface::READ);
|
||||
usable = map.IsMapped();
|
||||
}
|
||||
|
||||
if (usable) {
|
||||
auto surfaceImage = MakeRefPtr<layers::SourceSurfaceImage>(surface);
|
||||
surfaceImage->SetTextureFlags(flags);
|
||||
image = surfaceImage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -428,20 +428,6 @@ partial namespace ChromeUtils {
|
|||
[Throws]
|
||||
object import(UTF8String aResourceURI, optional object aTargetObj);
|
||||
|
||||
/**
|
||||
* Synchronously loads and evaluates the JS module source located at
|
||||
* 'aResourceURI'.
|
||||
*
|
||||
* @param aResourceURI A resource:// URI string to load the module from.
|
||||
* @returns the module's namespace object.
|
||||
*
|
||||
* The implementation maintains a hash of aResourceURI->global obj.
|
||||
* Subsequent invocations of import with 'aResourceURI' pointing to
|
||||
* the same file will not cause the module to be re-evaluated.
|
||||
*/
|
||||
[Throws]
|
||||
object importModule(DOMString aResourceURI);
|
||||
|
||||
/**
|
||||
* Defines a property on the given target which lazily imports a JavaScript
|
||||
* module when accessed.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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())) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue