Merge mozilla-central to inbound. a=merge CLOSED TREE

--HG--
rename : browser/components/payments/test/browser/browser_payments_onboarding_wizard.js => browser/components/payments/test/browser/browser_onboarding_wizard.js
This commit is contained in:
Narcis Beleuzu 2018-10-19 16:34:08 +03:00
commit 516255f90e
65 changed files with 2361 additions and 2018 deletions

View File

@ -1519,15 +1519,11 @@ pref("browser.contentblocking.global-toggle.enabled", true);
pref("browser.contentblocking.global-toggle.enabled", false);
#endif
// Define a set of default features for the Content Blocking UI
#ifdef EARLY_BETA_OR_EARLIER
pref("browser.contentblocking.fastblock.ui.enabled", true);
pref("browser.contentblocking.fastblock.control-center.ui.enabled", true);
#else
// Disable the UI for FastBlock in product.
pref("browser.contentblocking.fastblock.ui.enabled", false);
pref("browser.contentblocking.fastblock.control-center.ui.enabled", false);
#endif
// Define a set of default features for the Content Blocking UI.
pref("browser.contentblocking.trackingprotection.ui.enabled", true);
pref("browser.contentblocking.trackingprotection.control-center.ui.enabled", true);
pref("browser.contentblocking.rejecttrackers.ui.enabled", true);
@ -1779,8 +1775,3 @@ pref("prio.publicKeyB", "26E6674E65425B823F1F1D5F96E3BB3EF9E406EC7FBA7DEF8B08A35
#if defined(NIGHTLY_BUILD) && defined(MOZ_LIBPRIO)
pref("prio.enabled", true);
#endif
#ifdef NIGHTLY_BUILD
pref("browser.fastblock.enabled", true);
#endif

View File

@ -39,9 +39,10 @@ add_task(async function setup() {
let scalars = Services.telemetry.snapshotScalars(
Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT, false).parent;
is(scalars["contentblocking.enabled"], true, "CB was enabled at startup");
is(scalars["contentblocking.fastblock_enabled"], AppConstants.NIGHTLY_BUILD,
"FB is enabled in Nightly");
is(scalars["contentblocking.enabled"], Services.prefs.getBoolPref("browser.contentblocking.enabled"),
"CB enabled status was recorded at startup");
is(scalars["contentblocking.fastblock_enabled"], Services.prefs.getBoolPref("browser.fastblock.enabled"),
"FB enabled status was recorded at startup");
is(scalars["contentblocking.exceptions"], 0, "no CB exceptions at startup");
});

View File

@ -34,13 +34,8 @@ var Pocket = {
Pocket._initPanelView(window);
},
onPanelViewShowing(event) {
Pocket._initPanelView(event.target.ownerGlobal);
},
_initPanelView(window) {
let document = window.document;
let iframe = window.pktUI.getPanelFrame();
let libraryButton = document.getElementById("library-button");
if (libraryButton) {
@ -59,36 +54,9 @@ var Pocket = {
} else {
window.pktUI.tryToSaveCurrentPage();
}
// pocketPanelDidHide in main.js set iframe to about:blank when it was
// hidden, make sure we're loading the save panel.
if (iframe.contentDocument &&
iframe.contentDocument.readyState == "complete" &&
iframe.contentDocument.documentURI != "about:blank") {
window.pktUI.pocketPanelDidShow();
} else {
// iframe didn't load yet. This seems to always be the case when in
// the toolbar panel, but never the case for a subview.
// XXX this only being fired when it's a _capturing_ listener!
iframe.addEventListener("load", Pocket.onFrameLoaded, true);
}
}, 0);
},
onFrameLoaded(event) {
let document = event.currentTarget.ownerDocument;
let window = document.defaultView;
let iframe = window.pktUI.getPanelFrame();
iframe.removeEventListener("load", Pocket.onFrameLoaded, true);
window.pktUI.pocketPanelDidShow();
},
onPanelViewHiding(event) {
let window = event.target.ownerGlobal;
window.pktUI.pocketPanelDidHide(event);
},
_urlToSave: null,
_titleToSave: null,
savePage(browser, url, title) {

View File

@ -55,8 +55,6 @@ ChromeUtils.defineModuleGetter(this, "pktApi",
var pktUI = (function() {
// -- Initialization (on startup and new windows) -- //
var _currentPanelDidShow;
var _currentPanelDidHide;
// Init panel id at 0. The first actual panel id will have the number 1 so
// in case at some point any panel has the id 0 we know there is something
@ -68,56 +66,6 @@ var pktUI = (function() {
var savePanelWidth = 350;
var savePanelHeights = {collapsed: 153, expanded: 272};
var _lastAddSucceeded = false;
// -- Event Handling -- //
/**
* Event handler when Pocket toolbar button is pressed
*/
function pocketPanelDidShow(event) {
if (_currentPanelDidShow) {
_currentPanelDidShow(event);
}
}
function pocketPanelDidHide(event) {
if (_currentPanelDidHide) {
_currentPanelDidHide(event);
}
// clear the panel
getPanelFrame().setAttribute("src", "about:blank");
if (_lastAddSucceeded) {
var libraryButton = document.getElementById("library-button");
if (!Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled") ||
!libraryButton ||
libraryButton.getAttribute("cui-areatype") == "menu-panel" ||
libraryButton.getAttribute("overflowedItem") == "true" ||
!libraryButton.closest("#nav-bar")) {
return;
}
libraryButton.removeAttribute("fade");
libraryButton.setAttribute("animate", "pocket");
libraryButton.addEventListener("animationend", onLibraryButtonAnimationEnd);
}
}
function onLibraryButtonAnimationEnd(event) {
let doc = event.target.ownerDocument;
let libraryButton = doc.getElementById("library-button");
if (event.animationName.startsWith("library-pocket-animation")) {
libraryButton.setAttribute("fade", "true");
} else if (event.animationName == "library-pocket-fade") {
libraryButton.removeEventListener("animationend", onLibraryButtonAnimationEnd);
libraryButton.removeAttribute("animate");
libraryButton.removeAttribute("fade");
}
}
// -- Communication to API -- //
/**
@ -128,7 +76,6 @@ var pktUI = (function() {
}
function tryToSaveUrl(url, title) {
// If the user is logged in, go ahead and save the current page
if (pktApi.isUserLoggedIn()) {
saveAndShowConfirmation(url, title);
@ -195,9 +142,6 @@ var pktUI = (function() {
+ inOverflowMenu
+ "&locale="
+ getUILocale(), {
onShow() {
},
onHide: panelDidHide,
width: inOverflowMenu ? overflowMenuWidth : 300,
height: startheight,
});
@ -231,7 +175,6 @@ var pktUI = (function() {
+ "&locale=" + getUILocale(), {
onShow() {
var saveLinkMessageId = "saveLink";
_lastAddSucceeded = false;
getPanelFrame().setAttribute("itemAdded", "false");
// Send error message for invalid url
@ -270,7 +213,6 @@ var pktUI = (function() {
ho2,
};
pktUIMessaging.sendMessageToPanel(panelId, saveLinkMessageId, successResponse);
_lastAddSucceeded = true;
getPanelFrame().setAttribute("itemAdded", "true");
},
error(error, request) {
@ -298,7 +240,6 @@ var pktUI = (function() {
// Send the link
pktApi.addLink(url, options);
},
onHide: panelDidHide,
width: inOverflowMenu ? overflowMenuWidth : savePanelWidth,
height: startheight,
});
@ -309,7 +250,6 @@ var pktUI = (function() {
* Open a generic panel
*/
function showPanel(url, options) {
// Add new panel id
_panelId += 1;
url += ("&panelId=" + _panelId);
@ -318,6 +258,7 @@ var pktUI = (function() {
// as if the user tries to click again on the toolbar button the overlay
// will close instead of the button will be clicked
var iframe = getPanelFrame();
options.onShow = options.onShow || (() => {});
// Register event handlers
registerEventMessages();
@ -325,15 +266,16 @@ var pktUI = (function() {
// Load the iframe
iframe.setAttribute("src", url);
// Uncomment to leave panel open -- for debugging
// panel.setAttribute('noautohide', true);
// panel.setAttribute('consumeoutsideclicks', false);
//
// For some reason setting onpopupshown and onpopuphidden on the panel directly didn't work, so
// do it this hacky way for now
_currentPanelDidShow = options.onShow;
_currentPanelDidHide = options.onHide;
if (iframe.contentDocument &&
iframe.contentDocument.readyState == "complete" &&
iframe.contentDocument.documentURI != "about:blank") {
options.onShow();
} else {
// iframe didn't load yet. This seems to always be the case when in
// the toolbar panel, but never the case for a subview.
// XXX this only being fired when it's a _capturing_ listener!
iframe.addEventListener("load", options.onShow, { once: true, capture: true });
}
resizePanel({
width: options.width,
@ -358,15 +300,6 @@ var pktUI = (function() {
iframe.style.height = options.height + "px";
}
/**
* Called when the signup and saved panel was hidden
*/
function panelDidHide() {
// clear the onShow and onHide values
_currentPanelDidShow = null;
_currentPanelDidHide = null;
}
/**
* Register all of the messages needed for the panels
*/
@ -517,7 +450,6 @@ var pktUI = (function() {
success(data, response) {
var successResponse = {status: "success"};
pktUIMessaging.sendResponseMessageToPanel(panelId, _deleteItemMessageId, successResponse);
_lastAddSucceeded = false;
getPanelFrame().setAttribute("itemAdded", "false");
},
error(error, response) {
@ -636,9 +568,6 @@ var pktUI = (function() {
openTabWithUrl,
pocketPanelDidShow,
pocketPanelDidHide,
tryToSaveUrl,
tryToSaveCurrentPage,
};

View File

@ -44,6 +44,7 @@ let extensionControlledContentIds = {
"privacy.containers": "browserContainersExtensionContent",
"homepage_override": "browserHomePageExtensionContent",
"newTabURL": "browserNewTabExtensionContent",
"webNotificationsDisabled": "browserNotificationsPermissionExtensionContent",
"defaultSearch": "browserDefaultSearchExtensionContent",
"proxy.settings": "proxyExtensionContent",
get "websites.trackingProtectionMode"() {
@ -61,6 +62,7 @@ let extensionControlledContentIds = {
const extensionControlledL10nKeys = {
"homepage_override": "homepage-override",
"newTabURL": "new-tab-url",
"webNotificationsDisabled": "web-notifications",
"defaultSearch": "default-search",
"privacy.containers": "privacy-containers",
"websites.trackingProtectionMode": contentBlockingUiEnabled ?

View File

@ -11,6 +11,8 @@ XPCOMUtils.defineLazyPreferenceGetter(this, "proxyType", PROXY_PREF);
const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
const CHROME_URL_ROOT = TEST_DIR + "/";
const PERMISSIONS_URL = "chrome://browser/content/preferences/sitePermissions.xul";
let sitePermissionsDialog;
function getSupportsFile(path) {
let cr = Cc["@mozilla.org/chrome/chrome-registry;1"]
@ -68,6 +70,19 @@ function waitForMessageContent(messageId, l10nId, doc) {
{ childList: true });
}
async function openNotificationsPermissionDialog() {
let dialogOpened = promiseLoadSubDialog(PERMISSIONS_URL);
await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
let doc = content.document;
let settingsButton = doc.getElementById("notificationSettingsButton");
settingsButton.click();
});
sitePermissionsDialog = await dialogOpened;
await sitePermissionsDialog.document.mozSubdialogReady;
}
add_task(async function testExtensionControlledHomepage() {
await openPreferencesViaOpenPreferencesAPI("paneHome", {leaveOpen: true});
// eslint-disable-next-line mozilla/no-cpows-in-tests
@ -339,6 +354,75 @@ add_task(async function testExtensionControlledNewTab() {
await addon.uninstall();
});
add_task(async function testExtensionControlledWebNotificationsPermission() {
let manifest = {
manifest_version: 2,
name: "TestExtension",
version: "1.0",
description: "Testing WebNotificationsDisable",
applications: {gecko: {id: "@web_notifications_disable"}},
permissions: [
"browserSettings",
],
browser_action: {
default_title: "Testing",
},
};
await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
await openNotificationsPermissionDialog();
let doc = sitePermissionsDialog.document;
let extensionControlledContent = doc.getElementById("browserNotificationsPermissionExtensionContent");
// Test that extension content is initially hidden.
ok(extensionControlledContent.hidden, "Extension content is initially hidden");
// Install an extension that will disable web notifications permission.
let messageShown = waitForMessageShown("browserNotificationsPermissionExtensionContent", doc);
let extension = ExtensionTestUtils.loadExtension({
manifest,
useAddonManager: "permanent",
background() {
browser.browserSettings.webNotificationsDisabled.set({value: true});
browser.test.sendMessage("load-extension");
},
});
await extension.startup();
await extension.awaitMessage("load-extension");
await messageShown;
let controlledDesc = extensionControlledContent.querySelector("description");
Assert.deepEqual(doc.l10n.getAttributes(controlledDesc), {
id: "extension-controlled-web-notifications",
args: {
name: "TestExtension",
},
}, "The user is notified that an extension is controlling the web notifications permission");
is(extensionControlledContent.hidden, false, "The extension controlled row is not hidden");
// Disable the extension.
doc.getElementById("disableNotificationsPermissionExtension").click();
// Verify the user is notified how to enable the extension.
await waitForEnableMessage(extensionControlledContent.id, doc);
is(doc.l10n.getAttributes(controlledDesc.querySelector("label")).id,
"extension-controlled-enable",
"The user is notified of how to enable the extension again");
// Verify the enable message can be dismissed.
let hidden = waitForMessageHidden(extensionControlledContent.id, doc);
let dismissButton = controlledDesc.querySelector("image:last-of-type");
dismissButton.click();
await hidden;
// Verify that the extension controlled content in hidden again.
is(extensionControlledContent.hidden, true, "The extension controlled row is now hidden");
await extension.unload();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
add_task(async function testExtensionControlledDefaultSearch() {
await openPreferencesViaOpenPreferencesAPI("paneSearch", {leaveOpen: true});
let doc = gBrowser.contentDocument;

View File

@ -23,8 +23,12 @@
margin-inline-end: 5px;
}
#browserNotificationsPermissionExtensionContent,
#permissionsDisableDescription {
margin-inline-start: 32px;
}
#permissionsDisableDescription {
color: #737373;
line-height: 110%;
}

View File

@ -2,6 +2,8 @@
* 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-globals-from in-content/extensionControlled.js */
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource:///modules/SitePermissions.jsm");
@ -42,6 +44,8 @@ function Permission(principal, type, capability, l10nId) {
}
const PERMISSION_STATES = [SitePermissions.ALLOW, SitePermissions.BLOCK, SitePermissions.PROMPT];
const NOTIFICATIONS_PERMISSION_OVERRIDE_KEY = "webNotificationsDisabled";
const NOTIFICATIONS_PERMISSION_PREF = "permissions.default.desktop-notification";
var gSitePermissionsManager = {
_type: "",
@ -74,42 +78,28 @@ var gSitePermissionsManager = {
this._removeAllButton = document.getElementById("removeAllPermissions");
this._searchBox = document.getElementById("searchBox");
this._checkbox = document.getElementById("permissionsDisableCheckbox");
this._disableExtensionButton = document.getElementById("disableNotificationsPermissionExtension");
this._permissionsDisableDescription = document.getElementById("permissionsDisableDescription");
let permissionsDisableDescription = document.getElementById("permissionsDisableDescription");
let permissionsText = document.getElementById("permissionsText");
let l10n = sitePermissionsL10n[this._type];
document.l10n.setAttributes(permissionsText, l10n.description);
document.l10n.setAttributes(this._checkbox, l10n.disableLabel);
document.l10n.setAttributes(permissionsDisableDescription, l10n.disableDescription);
document.l10n.setAttributes(this._permissionsDisableDescription, l10n.disableDescription);
document.l10n.setAttributes(document.documentElement, l10n.window);
await document.l10n.translateElements([
permissionsText,
this._checkbox,
permissionsDisableDescription,
this._permissionsDisableDescription,
document.documentElement,
]);
// Initialize the checkbox state.
// Initialize the checkbox state and handle showing notification permission UI
// when it is disabled by an extension.
this._defaultPermissionStatePrefName = "permissions.default." + this._type;
let pref = Services.prefs.getPrefType(this._defaultPermissionStatePrefName);
if (pref != Services.prefs.PREF_INVALID) {
this._currentDefaultPermissionsState = Services.prefs.getIntPref(this._defaultPermissionStatePrefName);
}
if (this._currentDefaultPermissionsState === null) {
this._checkbox.setAttribute("hidden", true);
permissionsDisableDescription.setAttribute("hidden", true);
} else if (this._currentDefaultPermissionsState == SitePermissions.BLOCK) {
this._checkbox.checked = true;
} else {
this._checkbox.checked = false;
}
if (Services.prefs.prefIsLocked(this._defaultPermissionStatePrefName)) {
this._checkbox.disabled = true;
}
this._watchPermissionPrefChange();
this._loadPermissions();
this.buildPermissionsList();
@ -155,6 +145,68 @@ var gSitePermissionsManager = {
menulist.getElementsByAttribute("value", perm.capability)[0];
},
_handleCheckboxUIUpdates() {
let pref = Services.prefs.getPrefType(this._defaultPermissionStatePrefName);
if (pref != Services.prefs.PREF_INVALID) {
this._currentDefaultPermissionsState = Services.prefs.getIntPref(this._defaultPermissionStatePrefName);
}
if (this._currentDefaultPermissionsState === null) {
this._checkbox.setAttribute("hidden", true);
this._permissionsDisableDescription.setAttribute("hidden", true);
} else if (this._currentDefaultPermissionsState == SitePermissions.BLOCK) {
this._checkbox.checked = true;
} else {
this._checkbox.checked = false;
}
if (Services.prefs.prefIsLocked(this._defaultPermissionStatePrefName)) {
this._checkbox.disabled = true;
}
},
/**
* Listen for changes to the permissions.default.* pref and make
* necessary changes to the UI.
*/
_watchPermissionPrefChange() {
this._handleCheckboxUIUpdates();
if (this._type == "desktop-notification") {
this._handleWebNotificationsDisable();
this._disableExtensionButton.addEventListener(
"command",
makeDisableControllingExtension(PREF_SETTING_TYPE, NOTIFICATIONS_PERMISSION_OVERRIDE_KEY)
);
}
let observer = () => {
this._handleCheckboxUIUpdates();
if (this._type == "desktop-notification") {
this._handleWebNotificationsDisable();
}
};
Services.prefs.addObserver(this._defaultPermissionStatePrefName, observer);
window.addEventListener("unload", () => {
Services.prefs.removeObserver(this._defaultPermissionStatePrefName, observer);
});
},
/**
* Handles the UI update for web notifications disable by extensions.
*/
async _handleWebNotificationsDisable() {
let prefLocked = Services.prefs.prefIsLocked(NOTIFICATIONS_PERMISSION_PREF);
if (prefLocked) {
// An extension can't control these settings if they're locked.
hideControllingExtension(NOTIFICATIONS_PERMISSION_OVERRIDE_KEY);
} else {
let isControlled = await handleControllingExtension(PREF_SETTING_TYPE, NOTIFICATIONS_PERMISSION_OVERRIDE_KEY);
this._checkbox.disabled = isControlled;
}
},
_getCapabilityString(capability) {
let stringKey = null;
switch (capability) {

View File

@ -19,10 +19,12 @@
onkeypress="gSitePermissionsManager.onWindowKeyPress(event);">
<linkset>
<link rel="localization" href="browser/preferences/preferences.ftl"/>
<link rel="localization" href="browser/preferences/permissions.ftl"/>
</linkset>
<script src="chrome://browser/content/preferences/sitePermissions.js"/>
<script type="application/javascript" src="chrome://browser/content/preferences/in-content/extensionControlled.js"/>
<keyset>
<key data-l10n-id="permissions-close-key" modifiers="accel" oncommand="window.close();"/>
@ -63,6 +65,13 @@
<checkbox id="permissionsDisableCheckbox"/>
<description id="permissionsDisableDescription"/>
<spacer flex="1"/>
<hbox id="browserNotificationsPermissionExtensionContent"
class="extension-controlled" align="center" hidden="true">
<description control="disableNotificationsPermissionExtension" flex="1"/>
<button id="disableNotificationsPermissionExtension"
class="extension-controlled-button accessory-button"
data-l10n-id="disable-extension"/>
</hbox>
<hbox class="actionButtons" align="right" flex="1">
<button oncommand="close();" icon="close" id="cancel"
data-l10n-id="permissions-button-cancel" />

View File

@ -97,6 +97,10 @@ extension-controlled-homepage-override = An extension, <img data-l10n-name="icon
# is being controlled by an extension.
extension-controlled-new-tab-url = An extension, <img data-l10n-name="icon"/> { $name }, is controlling your New Tab page.
# This string is shown to notify the user that their notifications permission
# is being controlled by an extension.
extension-controlled-web-notifications= An extension, <img data-l10n-name="icon"/> { $name }, is controlling this setting.
# This string is shown to notify the user that the default search engine
# is being controlled by an extension.
extension-controlled-default-search = An extension, <img data-l10n-name="icon"/> { $name }, has set your default search engine.

View File

@ -38,24 +38,19 @@ NS_IMPL_CI_INTERFACE_GETTER(NullPrincipal,
/* static */ already_AddRefed<NullPrincipal>
NullPrincipal::CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom)
{
MOZ_ASSERT(aInheritFrom);
return CreateWithInheritedAttributes(Cast(aInheritFrom)->OriginAttributesRef(), false);
RefPtr<NullPrincipal> nullPrin = new NullPrincipal();
nsresult rv = nullPrin->Init(Cast(aInheritFrom)->OriginAttributesRef());
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
return nullPrin.forget();
}
/* static */ already_AddRefed<NullPrincipal>
NullPrincipal::CreateWithInheritedAttributes(nsIDocShell* aDocShell, bool aIsFirstParty)
{
MOZ_ASSERT(aDocShell);
OriginAttributes attrs = nsDocShell::Cast(aDocShell)->GetOriginAttributes();
return CreateWithInheritedAttributes(attrs, aIsFirstParty);
}
/* static */ already_AddRefed<NullPrincipal>
NullPrincipal::CreateWithInheritedAttributes(const OriginAttributes& aOriginAttributes, bool aIsFirstParty)
{
RefPtr<NullPrincipal> nullPrin = new NullPrincipal();
nsresult rv = nullPrin->Init(aOriginAttributes, aIsFirstParty);
nsresult rv = nullPrin->Init(attrs, aIsFirstParty);
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
return nullPrin.forget();
}

View File

@ -56,19 +56,14 @@ public:
NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
NS_IMETHOD GetAddonId(nsAString& aAddonId) override;
static already_AddRefed<NullPrincipal>
CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom);
static already_AddRefed<NullPrincipal> CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom);
// Create NullPrincipal with origin attributes from docshell.
// If aIsFirstParty is true, and the pref 'privacy.firstparty.isolate' is also
// enabled, the mFirstPartyDomain value of the origin attributes will be set
// to an unique value.
static already_AddRefed<NullPrincipal>
CreateWithInheritedAttributes(nsIDocShell* aDocShell,
bool aIsFirstParty = false);
static already_AddRefed<NullPrincipal>
CreateWithInheritedAttributes(const OriginAttributes& aOriginAttributes,
bool aIsFirstParty = false);
CreateWithInheritedAttributes(nsIDocShell* aDocShell, bool aIsFirstParty = false);
static already_AddRefed<NullPrincipal>
Create(const OriginAttributes& aOriginAttributes,
@ -90,8 +85,7 @@ public:
protected:
virtual ~NullPrincipal() = default;
bool SubsumesInternal(nsIPrincipal* aOther,
DocumentDomainConsideration aConsideration) override
bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override
{
return aOther == this;
}

View File

@ -205,6 +205,7 @@ skip-if = verify
[browser_jsterm_autocomplete_return_key.js]
[browser_jsterm_autocomplete_width.js]
[browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js]
[browser_jsterm_await_concurrent.js]
[browser_jsterm_await_error.js]
[browser_jsterm_await_helper_dollar_underscore.js]
[browser_jsterm_await_paused.js]

View File

@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that top-level await expression work as expected.
// Test that top-level await expressions work as expected.
"use strict";
@ -21,7 +21,6 @@ add_task(async function() {
async function performTests() {
const hud = await openNewTabAndConsole(TEST_URI);
const {jsterm} = hud;
const executeAndWaitForResultMessage = (input, expectedOutput) =>
executeAndWaitForMessage(hud, input, expectedOutput, ".result");
@ -34,8 +33,8 @@ async function performTests() {
);
// Check that the resulting promise of the async iife is not displayed.
let messages = hud.ui.outputNode.querySelectorAll(".message .message-body");
let messagesText = Array.from(messages).map(n => n.textContent).join(" - ");
const messages = hud.ui.outputNode.querySelectorAll(".message .message-body");
const messagesText = Array.from(messages).map(n => n.textContent).join(" - ");
is(messagesText, `${simpleAwait} - Array [ "await1" ]`,
"The output contains the the expected messages");
@ -62,31 +61,6 @@ async function performTests() {
);
ok(message.node, "`x` was assigned as expected");
info("Check that concurrent await expression work fine");
hud.ui.clearOutput();
const delays = [1000, 500, 2000, 1500];
const inputs = delays.map(delay => `await new Promise(
r => setTimeout(() => r("await-concurrent-" + ${delay}), ${delay}))`);
// Let's wait for the message that sould be displayed last.
const onMessage = waitForMessage(hud, "await-concurrent-2000", ".message.result");
for (const input of inputs) {
jsterm.execute(input);
}
await onMessage;
messages = hud.ui.outputNode.querySelectorAll(".message .message-body");
messagesText = Array.from(messages).map(n => n.textContent);
const expectedMessages = [
...inputs,
`"await-concurrent-500"`,
`"await-concurrent-1000"`,
`"await-concurrent-1500"`,
`"await-concurrent-2000"`,
];
is(JSON.stringify(messagesText, null, 2), JSON.stringify(expectedMessages, null, 2),
"The output contains the the expected messages, in the expected order");
info("Check that a logged promise is still displayed as a promise");
message = await executeAndWaitForResultMessage(
`new Promise(r => setTimeout(() => r(1), 1000))`,

View File

@ -0,0 +1,49 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that multiple concurrent top-level await expressions work as expected.
"use strict";
const TEST_URI = "data:text/html;charset=utf-8,Web Console test top-level await";
add_task(async function() {
// Enable await mapping.
await pushPref("devtools.debugger.features.map-await-expression", true);
// Run test with legacy JsTerm
await pushPref("devtools.webconsole.jsterm.codeMirror", false);
await performTests();
// And then run it with the CodeMirror-powered one.
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
await performTests();
});
async function performTests() {
const hud = await openNewTabAndConsole(TEST_URI);
const {jsterm} = hud;
hud.ui.clearOutput();
const delays = [3000, 500, 9000, 6000];
const inputs = delays.map(delay => `await new Promise(
r => setTimeout(() => r("await-concurrent-" + ${delay}), ${delay}))`);
// Let's wait for the message that sould be displayed last.
const onMessage = waitForMessage(hud, "await-concurrent-9000", ".message.result");
for (const input of inputs) {
jsterm.execute(input);
}
await onMessage;
const messages = hud.ui.outputNode.querySelectorAll(".message .message-body");
const messagesText = Array.from(messages).map(n => n.textContent);
const expectedMessages = [
...inputs,
`"await-concurrent-500"`,
`"await-concurrent-3000"`,
`"await-concurrent-6000"`,
`"await-concurrent-9000"`,
];
is(JSON.stringify(messagesText, null, 2), JSON.stringify(expectedMessages, null, 2),
"The output contains the the expected messages, in the expected order");
}

View File

@ -66,7 +66,7 @@ WebConsoleOutputWrapper.prototype = {
hud.owner.openLink(url, e);
},
canRewind: () => {
if (!hud.owner.target.activeTab) {
if (!(hud.owner && hud.owner.target && hud.owner.target.activeTab)) {
return false;
}

View File

@ -61,7 +61,7 @@ XPIDL_MODULE = 'docshell'
EXPORTS += [
'nsCTooltipTextProvider.h',
'nsDocShell.h',
'nsDocShellLoadState.h',
'nsDocShellLoadInfo.h',
'nsDocShellLoadTypes.h',
'nsDocShellTreeOwner.h',
'nsILinkHandler.h',
@ -88,7 +88,7 @@ UNIFIED_SOURCES += [
'nsDocShell.cpp',
'nsDocShellEditorData.cpp',
'nsDocShellEnumerator.cpp',
'nsDocShellLoadState.cpp',
'nsDocShellLoadInfo.cpp',
'nsDocShellTreeOwner.cpp',
'nsDSURIContentListener.cpp',
'nsPingListener.cpp',

View File

@ -169,7 +169,7 @@
#include "nsDocShellCID.h"
#include "nsDocShellEditorData.h"
#include "nsDocShellEnumerator.h"
#include "nsDocShellLoadState.h"
#include "nsDocShellLoadInfo.h"
#include "nsDocShellLoadTypes.h"
#include "nsDOMCID.h"
#include "nsDOMNavigationTiming.h"
@ -643,12 +643,14 @@ nsDocShell::GetInterface(const nsIID& aIID, void** aSink)
}
NS_IMETHODIMP
nsDocShell::LoadURI(nsDocShellLoadState* aLoadState)
nsDocShell::LoadURI(nsIURI* aURI,
nsDocShellLoadInfo* aLoadInfo,
uint32_t aLoadFlags,
bool aFirstParty)
{
MOZ_ASSERT(aLoadState, "Must have a valid load state!");
MOZ_ASSERT((aLoadState->LoadFlags() & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0,
"Should not have these flags set");
MOZ_ASSERT(aLoadState->URI(), "Should have a valid URI to load");
MOZ_ASSERT(aLoadInfo || (aLoadFlags & EXTRA_LOAD_FLAGS) == 0,
"Unexpected flags");
MOZ_ASSERT((aLoadFlags & 0xf) == 0, "Should not have these flags set");
// Note: we allow loads to get through here even if mFiredUnloadEvent is
// true; that case will get handled in LoadInternal or LoadHistoryEntry,
@ -660,121 +662,86 @@ nsDocShell::LoadURI(nsDocShellLoadState* aLoadState)
return NS_OK; // JS may not handle returning of an error code
}
nsCOMPtr<nsIURI> referrer;
nsCOMPtr<nsIURI> originalURI;
Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
bool keepResultPrincipalURIIfSet = false;
bool loadReplace = false;
nsCOMPtr<nsIInputStream> postStream;
nsCOMPtr<nsIInputStream> headersStream;
nsCOMPtr<nsIPrincipal> triggeringPrincipal;
bool inheritPrincipal = false;
bool principalIsExplicit = false;
bool sendReferrer = true;
uint32_t referrerPolicy = RP_Unset;
bool isSrcdoc = false;
nsCOMPtr<nsISHEntry> shEntry;
nsString target;
nsAutoString srcdoc;
bool forceAllowDataURI = false;
bool originalFrameSrc = false;
nsCOMPtr<nsIDocShell> sourceDocShell;
nsCOMPtr<nsIURI> baseURI;
uint32_t loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
NS_ENSURE_ARG(aURI);
if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
mItemType == typeContent && !NS_IsAboutBlank(aLoadState->URI())) {
mItemType == typeContent && !NS_IsAboutBlank(aURI)) {
StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
}
// Extract the info from the DocShellLoadInfo struct...
if (aLoadInfo) {
referrer = aLoadInfo->Referrer();
originalURI = aLoadInfo->OriginalURI();
aLoadInfo->GetMaybeResultPrincipalURI(resultPrincipalURI);
keepResultPrincipalURIIfSet = aLoadInfo->KeepResultPrincipalURIIfSet();
loadReplace = aLoadInfo->LoadReplace();
// Get the appropriate loadType from nsIDocShellLoadInfo type
loadType = aLoadInfo->LoadType();
triggeringPrincipal = aLoadInfo->TriggeringPrincipal();
inheritPrincipal = aLoadInfo->InheritPrincipal();
principalIsExplicit = aLoadInfo->PrincipalIsExplicit();
shEntry = aLoadInfo->SHEntry();
aLoadInfo->GetTarget(target);
postStream = aLoadInfo->PostDataStream();
headersStream = aLoadInfo->HeadersStream();
sendReferrer = aLoadInfo->SendReferrer();
referrerPolicy = aLoadInfo->ReferrerPolicy();
isSrcdoc = aLoadInfo->IsSrcdocLoad();
aLoadInfo->GetSrcdocData(srcdoc);
sourceDocShell = aLoadInfo->SourceDocShell();
baseURI = aLoadInfo->BaseURI();
forceAllowDataURI = aLoadInfo->ForceAllowDataURI();
originalFrameSrc = aLoadInfo->OriginalFrameSrc();
}
MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
("nsDocShell[%p]: loading %s with flags 0x%08x",
this, aLoadState->URI()->GetSpecOrDefault().get(),
aLoadState->LoadFlags()));
this, aURI->GetSpecOrDefault().get(), aLoadFlags));
if (!aLoadState->SHEntry() &&
!LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
LOAD_FLAGS_REPLACE_HISTORY)) {
// This is possibly a subframe, so handle it accordingly.
//
// If history exists, it will be loaded into the aLoadState object, and the
// LoadType will be changed.
MaybeHandleSubframeHistory(aLoadState);
}
if (aLoadState->SHEntry()) {
#ifdef DEBUG
MOZ_LOG(gDocShellLog, LogLevel::Debug,
("nsDocShell[%p]: loading from session history", this));
#endif
return LoadHistoryEntry(aLoadState->SHEntry(), aLoadState->LoadType());
}
// On history navigation via Back/Forward buttons, don't execute
// automatic JavaScript redirection such as |location.href = ...| or
// |window.open()|
//
// LOAD_NORMAL: window.open(...) etc.
// LOAD_STOP_CONTENT: location.href = ..., location.assign(...)
if ((aLoadState->LoadType() == LOAD_NORMAL ||
aLoadState->LoadType() == LOAD_STOP_CONTENT) &&
ShouldBlockLoadingForBackButton()) {
return NS_OK;
}
// Set up the inheriting principal in LoadState.
nsresult rv = aLoadState->SetupInheritingPrincipal(mItemType, mOriginAttributes);
NS_ENSURE_SUCCESS(rv, rv);
rv = aLoadState->SetupTriggeringPrincipal(mOriginAttributes);
NS_ENSURE_SUCCESS(rv, rv);
aLoadState->CalculateDocShellInternalLoadFlags();
mozilla::Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
aLoadState->GetMaybeResultPrincipalURI(resultPrincipalURI);
MOZ_ASSERT(aLoadState->TypeHint().IsVoid(),
"Typehint should be null when calling InternalLoad from LoadURI");
MOZ_ASSERT(aLoadState->FileName().IsVoid(),
"FileName should be null when calling InternalLoad from LoadURI");
MOZ_ASSERT(aLoadState->SHEntry() == nullptr,
"SHEntry should be null when calling InternalLoad from LoadURI");
return InternalLoad(aLoadState->URI(),
aLoadState->OriginalURI(),
resultPrincipalURI,
aLoadState->KeepResultPrincipalURIIfSet(),
aLoadState->LoadReplace(),
aLoadState->Referrer(),
aLoadState->ReferrerPolicy(),
aLoadState->TriggeringPrincipal(),
aLoadState->PrincipalToInherit(),
aLoadState->DocShellInternalLoadFlags(),
aLoadState->Target(),
aLoadState->TypeHint(),
aLoadState->FileName(),
aLoadState->PostDataStream(),
aLoadState->HeadersStream(),
aLoadState->LoadType(),
aLoadState->SHEntry(),
aLoadState->FirstParty(),
aLoadState->SrcdocData(),
aLoadState->SourceDocShell(),
aLoadState->BaseURI(),
nullptr, // no nsIDocShell
nullptr); // no nsIRequest
}
void
nsDocShell::MaybeHandleSubframeHistory(nsDocShellLoadState* aLoadState)
{
// First, verify if this is a subframe.
if (!shEntry &&
!LOAD_TYPE_HAS_FLAGS(loadType, LOAD_FLAGS_REPLACE_HISTORY)) {
// First verify if this is a subframe.
nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
GetSameTypeParent(getter_AddRefs(parentAsItem));
nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
uint32_t parentLoadType;
if (!parentDS || parentDS == static_cast<nsIDocShell*>(this)) {
// This is the root docshell. If we got here while
// executing an onLoad Handler,this load will not go
// into session history.
bool inOnLoadHandler = false;
GetIsExecutingOnLoadHandler(&inOnLoadHandler);
if (inOnLoadHandler) {
aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
}
return;
}
/* OK. It is a subframe. Checkout the parent's loadtype. If the parent was
* loaded through a history mechanism, then get the SH entry for the child from
* the parent. This is done to restore frameset navigation while going
* back/forward. If the parent was loaded through any other loadType, set the
* child's loadType too accordingly, so that session history does not get
* confused.
if (parentDS && parentDS != static_cast<nsIDocShell*>(this)) {
/* OK. It is a subframe. Checkout the
* parent's loadtype. If the parent was loaded thro' a history
* mechanism, then get the SH entry for the child from the parent.
* This is done to restore frameset navigation while going back/forward.
* If the parent was loaded through any other loadType, set the
* child's loadType too accordingly, so that session history does not
* get confused.
*/
// Get the parent's load type
uint32_t parentLoadType;
parentDS->GetLoadType(&parentLoadType);
// Get the ShEntry for the child from the parent
@ -782,17 +749,13 @@ nsDocShell::MaybeHandleSubframeHistory(nsDocShellLoadState* aLoadState)
bool oshe = false;
parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
bool dynamicallyAddedChild = mDynamicallyCreated;
if (!dynamicallyAddedChild && !oshe && currentSH) {
currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
}
if (!dynamicallyAddedChild) {
// Only use the old SHEntry, if we're sure enough that
// it wasn't originally for some other frame.
nsCOMPtr<nsISHEntry> shEntry;
parentDS->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
aLoadState->SetSHEntry(shEntry);
}
// Make some decisions on the child frame's loadType based on the
@ -805,13 +768,58 @@ nsDocShell::MaybeHandleSubframeHistory(nsDocShellLoadState* aLoadState)
// currentSHEntry is null.
nsCOMPtr<nsISHEntry> currentChildEntry;
GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe);
if (mCurrentURI && (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry)) {
if (!mCurrentURI || (NS_IsAboutBlank(mCurrentURI) && !currentChildEntry)) {
// This is a newly created frame. Check for exception cases first.
// By default the subframe will inherit the parent's loadType.
if (shEntry && (parentLoadType == LOAD_NORMAL ||
parentLoadType == LOAD_LINK ||
parentLoadType == LOAD_NORMAL_EXTERNAL)) {
// The parent was loaded normally. In this case, this *brand new*
// child really shouldn't have a SHEntry. If it does, it could be
// because the parent is replacing an existing frame with a new frame,
// in the onLoadHandler. We don't want this url to get into session
// history. Clear off shEntry, and set load type to
// LOAD_BYPASS_HISTORY.
bool inOnLoadHandler = false;
parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
if (inOnLoadHandler) {
loadType = LOAD_NORMAL_REPLACE;
shEntry = nullptr;
}
} else if (parentLoadType == LOAD_REFRESH) {
// Clear shEntry. For refresh loads, we have to load
// what comes thro' the pipe, not what's in history.
shEntry = nullptr;
} else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
(shEntry &&
((parentLoadType & LOAD_CMD_HISTORY) ||
(parentLoadType == LOAD_RELOAD_NORMAL) ||
(parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) ||
(parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) ||
(parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) {
// If the parent url, bypassed history or was loaded from
// history, pass on the parent's loadType to the new child
// frame too, so that the child frame will also
// avoid getting into history.
loadType = parentLoadType;
} else if (parentLoadType == LOAD_ERROR_PAGE) {
// If the parent document is an error page, we don't
// want to update global/session history. However,
// this child frame is not an error page.
loadType = LOAD_BYPASS_HISTORY;
} else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
(parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
(parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
// the new frame should inherit the parent's load type so that it also
// bypasses the cache and/or proxy
loadType = parentLoadType;
}
} else {
// This is a pre-existing subframe. If
// 1. The load of this frame was not originally initiated by session
// history directly (i.e. (!shEntry) condition succeeded, but it can
// still be a history load on parent which causes this frame being
// loaded), which we checked with the above assert, and
// loaded), and
// 2. mCurrentURI is not null, nor the initial about:blank,
// it is possible that a parent's onLoadHandler or even self's
// onLoadHandler is loading a new page in this child. Check parent's and
@ -823,57 +831,187 @@ nsDocShell::MaybeHandleSubframeHistory(nsDocShellLoadState* aLoadState)
GetBusyFlags(&selfBusy);
if (parentBusy & BUSY_FLAGS_BUSY ||
selfBusy & BUSY_FLAGS_BUSY) {
aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
aLoadState->SetSHEntry(nullptr);
loadType = LOAD_NORMAL_REPLACE;
shEntry = nullptr;
}
}
} // parentDS
else {
// This is the root docshell. If we got here while
// executing an onLoad Handler,this load will not go
// into session history.
bool inOnLoadHandler = false;
GetIsExecutingOnLoadHandler(&inOnLoadHandler);
if (inOnLoadHandler) {
loadType = LOAD_NORMAL_REPLACE;
}
}
return;
} // !shEntry
if (shEntry) {
#ifdef DEBUG
MOZ_LOG(gDocShellLog, LogLevel::Debug,
("nsDocShell[%p]: loading from session history", this));
#endif
return LoadHistoryEntry(shEntry, loadType);
}
// This is a newly created frame. Check for exception cases first.
// By default the subframe will inherit the parent's loadType.
if (aLoadState->SHEntry() && (parentLoadType == LOAD_NORMAL ||
parentLoadType == LOAD_LINK ||
parentLoadType == LOAD_NORMAL_EXTERNAL)) {
// The parent was loaded normally. In this case, this *brand new*
// child really shouldn't have a SHEntry. If it does, it could be
// because the parent is replacing an existing frame with a new frame,
// in the onLoadHandler. We don't want this url to get into session
// history. Clear off shEntry, and set load type to
// LOAD_BYPASS_HISTORY.
bool inOnLoadHandler = false;
parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
if (inOnLoadHandler) {
aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
aLoadState->SetSHEntry(nullptr);
}
} else if (parentLoadType == LOAD_REFRESH) {
// Clear shEntry. For refresh loads, we have to load
// what comes through the pipe, not what's in history.
aLoadState->SetSHEntry(nullptr);
} else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
(aLoadState->SHEntry() &&
((parentLoadType & LOAD_CMD_HISTORY) ||
(parentLoadType == LOAD_RELOAD_NORMAL) ||
(parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) ||
(parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) ||
(parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) {
// If the parent url, bypassed history or was loaded from
// history, pass on the parent's loadType to the new child
// frame too, so that the child frame will also
// avoid getting into history.
aLoadState->SetLoadType(parentLoadType);
} else if (parentLoadType == LOAD_ERROR_PAGE) {
// If the parent document is an error page, we don't
// want to update global/session history. However,
// this child frame is not an error page.
aLoadState->SetLoadType(LOAD_BYPASS_HISTORY);
} else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
(parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
(parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
// the new frame should inherit the parent's load type so that it also
// bypasses the cache and/or proxy
aLoadState->SetLoadType(parentLoadType);
// On history navigation via Back/Forward buttons, don't execute
// automatic JavaScript redirection such as |location.href = ...| or
// |window.open()|
//
// LOAD_NORMAL: window.open(...) etc.
// LOAD_STOP_CONTENT: location.href = ..., location.assign(...)
if ((loadType == LOAD_NORMAL || loadType == LOAD_STOP_CONTENT) &&
ShouldBlockLoadingForBackButton()) {
return NS_OK;
}
// Perform the load...
// We need a principalToInherit.
//
// If principalIsExplicit is not set there are 4 possibilities:
// (1) If the system principal or an expanded principal was passed
// in and we're a typeContent docshell, inherit the principal
// from the current document instead.
// (2) In all other cases when the principal passed in is not null,
// use that principal.
// (3) If the caller has allowed inheriting from the current document,
// or if we're being called from system code (eg chrome JS or pure
// C++) then inheritPrincipal should be true and InternalLoad will get
// a principal from the current document. If none of these things are
// true, then
// (4) we don't pass a principal into the channel, and a principal will be
// created later from the channel's internal data.
//
// If principalIsExplicit *is* set, there are 4 possibilities
// (1) If the system principal or an expanded principal was passed in
// and we're a typeContent docshell, return an error.
// (2) In all other cases when the principal passed in is not null,
// use that principal.
// (3) If the caller has allowed inheriting from the current document,
// then inheritPrincipal should be true and InternalLoad will get
// a principal from the current document. If none of these things are
// true, then
// (4) we dont' pass a principal into the channel, and a principal will be
// created later from the channel's internal data.
nsCOMPtr<nsIPrincipal> principalToInherit = triggeringPrincipal;
if (principalToInherit && mItemType != typeChrome) {
if (nsContentUtils::IsSystemPrincipal(principalToInherit)) {
if (principalIsExplicit) {
return NS_ERROR_DOM_SECURITY_ERR;
}
principalToInherit = nullptr;
inheritPrincipal = true;
} else if (nsContentUtils::IsExpandedPrincipal(principalToInherit)) {
if (principalIsExplicit) {
return NS_ERROR_DOM_SECURITY_ERR;
}
// Don't inherit from the current page. Just do the safe thing
// and pretend that we were loaded by a nullprincipal.
//
// We didn't inherit OriginAttributes here as ExpandedPrincipal doesn't
// have origin attributes.
principalToInherit = NullPrincipal::CreateWithInheritedAttributes(this);
inheritPrincipal = false;
}
}
if (!principalToInherit && !inheritPrincipal && !principalIsExplicit) {
// See if there's system or chrome JS code running
inheritPrincipal = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
}
if (aLoadFlags & LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL) {
inheritPrincipal = false;
// If aFirstParty is true and the pref 'privacy.firstparty.isolate' is
// enabled, we will set firstPartyDomain on the origin attributes.
principalToInherit = NullPrincipal::CreateWithInheritedAttributes(this, aFirstParty);
}
// If the triggeringPrincipal is not passed explicitly, we first try to create
// a principal from the referrer, since the referrer URI reflects the web origin
// that triggered the load. If there is no referrer URI, we fall back to using
// the SystemPrincipal. It's safe to assume that no provided triggeringPrincipal
// and no referrer simulate a load that was triggered by the system.
// It's important to note that this block of code needs to appear *after* the block
// where we munge the principalToInherit, because otherwise we would never enter
// code blocks checking if the principalToInherit is null and we will end up with
// a wrong inheritPrincipal flag.
if (!triggeringPrincipal) {
if (referrer) {
nsresult rv = CreatePrincipalFromReferrer(referrer,
getter_AddRefs(triggeringPrincipal));
NS_ENSURE_SUCCESS(rv, rv);
}
else {
triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
}
}
uint32_t flags = 0;
if (inheritPrincipal) {
MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(principalToInherit), "Should not inherit SystemPrincipal");
flags |= INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
}
if (!sendReferrer) {
flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
}
if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
}
if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD) {
flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
}
if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER) {
flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
}
if (aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_COOKIES) {
flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
}
if (isSrcdoc) {
flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
}
if (forceAllowDataURI) {
flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
}
if (originalFrameSrc) {
flags |= INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC;
}
return InternalLoad(aURI,
originalURI,
resultPrincipalURI,
keepResultPrincipalURIIfSet,
loadReplace,
referrer,
referrerPolicy,
triggeringPrincipal,
principalToInherit,
flags,
target,
nullptr, // No type hint
VoidString(), // No forced download
postStream,
headersStream,
loadType,
nullptr, // No SHEntry
aFirstParty,
srcdoc,
sourceDocShell,
baseURI,
nullptr, // No nsIDocShell
nullptr); // No nsIRequest
}
/*
@ -4088,7 +4226,7 @@ nsDocShell::LoadURIWithOptions(const nsAString& aURI,
nsIURI* aBaseURI,
nsIPrincipal* aTriggeringPrincipal)
{
NS_ASSERTION((aLoadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0, "Unexpected flags");
NS_ASSERTION((aLoadFlags & 0xf) == 0, "Unexpected flags");
if (!IsNavigationAllowed()) {
return NS_OK; // JS may not handle returning of an error code
@ -4183,28 +4321,27 @@ nsDocShell::LoadURIWithOptions(const nsAString& aURI,
uint32_t extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS);
aLoadFlags &= ~EXTRA_LOAD_FLAGS;
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState();
RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
/*
* If the user "Disables Protection on This Page", we have to make sure to
* remember the users decision when opening links in child tabs [Bug 906190]
*/
uint32_t loadType;
if (aLoadFlags & LOAD_FLAGS_ALLOW_MIXED_CONTENT) {
loadState->SetLoadType(MAKE_LOAD_TYPE(LOAD_NORMAL_ALLOW_MIXED_CONTENT, aLoadFlags));
loadType = MAKE_LOAD_TYPE(LOAD_NORMAL_ALLOW_MIXED_CONTENT, aLoadFlags);
} else {
loadState->SetLoadType(MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags));
loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
}
loadState->SetURI(uri);
loadState->SetLoadFlags(extraFlags);
loadState->SetFirstParty(true);
loadState->SetPostDataStream(postStream);
loadState->SetReferrer(aReferringURI);
loadState->SetReferrerPolicy((mozilla::net::ReferrerPolicy)aReferrerPolicy);
loadState->SetHeadersStream(aHeaderStream);
loadState->SetBaseURI(aBaseURI);
loadState->SetTriggeringPrincipal(aTriggeringPrincipal);
loadState->SetForceAllowDataURI(forceAllowDataURI);
loadInfo->SetLoadType(loadType);
loadInfo->SetPostDataStream(postStream);
loadInfo->SetReferrer(aReferringURI);
loadInfo->SetReferrerPolicy((mozilla::net::ReferrerPolicy)aReferrerPolicy);
loadInfo->SetHeadersStream(aHeaderStream);
loadInfo->SetBaseURI(aBaseURI);
loadInfo->SetTriggeringPrincipal(aTriggeringPrincipal);
loadInfo->SetForceAllowDataURI(forceAllowDataURI);
if (fixupInfo) {
nsAutoString searchProvider, keyword;
@ -4213,7 +4350,7 @@ nsDocShell::LoadURIWithOptions(const nsAString& aURI,
MaybeNotifyKeywordSearchLoading(searchProvider, keyword);
}
rv = LoadURI(loadState);
rv = LoadURI(uri, loadInfo, extraFlags, true);
// Save URI string in case it's needed later when
// sending to search engine service in EndPageLoad()
@ -4781,7 +4918,7 @@ nsDocShell::LoadErrorPage(nsIURI* aErrorURI, nsIURI* aFailedURI, nsIChannel* aFa
return InternalLoad(aErrorURI, nullptr, Nothing(), false, false, nullptr, RP_Unset,
nsContentUtils::GetSystemPrincipal(), nullptr,
INTERNAL_LOAD_FLAGS_NONE, EmptyString(),
VoidCString(), VoidString(), nullptr, nullptr,
nullptr, VoidString(), nullptr, nullptr,
LOAD_ERROR_PAGE, nullptr, true, VoidString(), this,
nullptr, nullptr, nullptr);
}
@ -4793,7 +4930,7 @@ nsDocShell::Reload(uint32_t aReloadFlags)
return NS_OK; // JS may not handle returning of an error code
}
nsresult rv;
NS_ASSERTION(((aReloadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0),
NS_ASSERTION(((aReloadFlags & 0xf) == 0),
"Reload command not updated to use load flags!");
NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
"Don't pass these flags to Reload");
@ -4881,7 +5018,7 @@ nsDocShell::Reload(uint32_t aReloadFlags)
triggeringPrincipal,
flags,
EmptyString(), // No window target
NS_LossyConvertUTF16toASCII(contentTypeHint),
NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
VoidString(), // No forced download
nullptr, // No post data
nullptr, // No headers data
@ -6133,22 +6270,22 @@ nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal, int32_t aDel
{
NS_ENSURE_ARG(aURI);
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState();
RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
/* We do need to pass in a referrer, but we don't want it to
* be sent to the server.
*/
loadState->SetSendReferrer(false);
loadInfo->SetSendReferrer(false);
/* for most refreshes the current URI is an appropriate
* internal referrer
*/
loadState->SetReferrer(mCurrentURI);
loadInfo->SetReferrer(mCurrentURI);
loadState->SetOriginalURI(mCurrentURI);
loadState->SetResultPrincipalURI(aURI);
loadState->SetResultPrincipalURIIsSome(true);
loadState->SetKeepResultPrincipalURIIfSet(true);
loadInfo->SetOriginalURI(mCurrentURI);
loadInfo->SetResultPrincipalURI(aURI);
loadInfo->SetResultPrincipalURIIsSome(true);
loadInfo->SetKeepResultPrincipalURIIfSet(true);
// Set the triggering pricipal to aPrincipal if available, or current
// document's principal otherwise.
@ -6160,8 +6297,8 @@ nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal, int32_t aDel
}
principal = doc->NodePrincipal();
}
loadState->SetTriggeringPrincipal(principal);
loadState->SetPrincipalIsExplicit(true);
loadInfo->SetTriggeringPrincipal(principal);
loadInfo->SetPrincipalIsExplicit(true);
/* Check if this META refresh causes a redirection
* to another site.
@ -6174,7 +6311,7 @@ nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal, int32_t aDel
* we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
* Pass a REPLACE flag to LoadURI().
*/
loadState->SetLoadType(LOAD_NORMAL_REPLACE);
loadInfo->SetLoadType(LOAD_NORMAL_REPLACE);
/* for redirects we mimic HTTP, which passes the
* original referrer
@ -6182,21 +6319,17 @@ nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal, int32_t aDel
nsCOMPtr<nsIURI> internalReferrer;
GetReferringURI(getter_AddRefs(internalReferrer));
if (internalReferrer) {
loadState->SetReferrer(internalReferrer);
loadInfo->SetReferrer(internalReferrer);
}
} else {
loadState->SetLoadType(LOAD_REFRESH);
loadInfo->SetLoadType(LOAD_REFRESH);
}
loadState->SetURI(aURI);
loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL);
loadState->SetFirstParty(true);
/*
* LoadURI(...) will cancel all refresh timers... This causes the
* Timer and its refreshData instance to be released...
*/
LoadURI(loadState);
LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL, true);
return NS_OK;
}
@ -8962,7 +9095,7 @@ public:
nsIPrincipal* aTriggeringPrincipal,
nsIPrincipal* aPrincipalToInherit,
uint32_t aFlags,
const nsACString& aTypeHint,
const char* aTypeHint,
nsIInputStream* aPostData,
nsIInputStream* aHeadersData,
uint32_t aLoadType,
@ -8972,7 +9105,6 @@ public:
nsIDocShell* aSourceDocShell,
nsIURI* aBaseURI)
: mozilla::Runnable("InternalLoadEvent")
, mTypeHint(aTypeHint)
, mSrcdoc(aSrcdoc)
, mDocShell(aDocShell)
, mURI(aURI)
@ -8993,6 +9125,12 @@ public:
, mSourceDocShell(aSourceDocShell)
, mBaseURI(aBaseURI)
{
// Make sure to keep null things null as needed
if (aTypeHint) {
mTypeHint = aTypeHint;
} else {
mTypeHint.SetIsVoid(true);
}
}
NS_IMETHOD
@ -9005,7 +9143,8 @@ public:
mReferrerPolicy,
mTriggeringPrincipal, mPrincipalToInherit,
mFlags, EmptyString(),
mTypeHint,
mTypeHint.IsVoid() ? nullptr
: mTypeHint.get(),
VoidString(), mPostData,
mHeadersData, mLoadType, mSHEntry,
mFirstParty, mSrcdoc, mSourceDocShell,
@ -9076,7 +9215,7 @@ nsDocShell::InternalLoad(nsIURI* aURI,
nsIPrincipal* aPrincipalToInherit,
uint32_t aFlags,
const nsAString& aWindowTarget,
const nsACString& aTypeHint,
const char* aTypeHint,
const nsAString& aFileName,
nsIInputStream* aPostData,
nsIInputStream* aHeadersData,
@ -9357,31 +9496,31 @@ nsDocShell::InternalLoad(nsIURI* aURI,
MOZ_ASSERT(!aSHEntry);
MOZ_ASSERT(aFirstParty); // Windowwatcher will assume this.
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState();
RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
// Set up our loadinfo so it will do the load as much like we would have
// as possible.
loadState->SetReferrer(aReferrer);
loadState->SetReferrerPolicy((mozilla::net::ReferrerPolicy)aReferrerPolicy);
loadState->SetSendReferrer(!(aFlags &
loadInfo->SetReferrer(aReferrer);
loadInfo->SetReferrerPolicy((mozilla::net::ReferrerPolicy)aReferrerPolicy);
loadInfo->SetSendReferrer(!(aFlags &
INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
loadState->SetOriginalURI(aOriginalURI);
loadState->SetMaybeResultPrincipalURI(aResultPrincipalURI);
loadState->SetKeepResultPrincipalURIIfSet(aKeepResultPrincipalURIIfSet);
loadState->SetLoadReplace(aLoadReplace);
loadState->SetTriggeringPrincipal(aTriggeringPrincipal);
loadState->SetInheritPrincipal(
loadInfo->SetOriginalURI(aOriginalURI);
loadInfo->SetMaybeResultPrincipalURI(aResultPrincipalURI);
loadInfo->SetKeepResultPrincipalURIIfSet(aKeepResultPrincipalURIIfSet);
loadInfo->SetLoadReplace(aLoadReplace);
loadInfo->SetTriggeringPrincipal(aTriggeringPrincipal);
loadInfo->SetInheritPrincipal(
aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
// Explicit principal because we do not want any guesses as to what the
// principal to inherit is: it should be aTriggeringPrincipal.
loadState->SetPrincipalIsExplicit(true);
loadState->SetLoadType(LOAD_LINK);
loadState->SetForceAllowDataURI(aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI);
loadInfo->SetPrincipalIsExplicit(true);
loadInfo->SetLoadType(LOAD_LINK);
loadInfo->SetForceAllowDataURI(aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI);
rv = win->Open(NS_ConvertUTF8toUTF16(spec),
aWindowTarget, // window name
EmptyString(), // Features
loadState,
loadInfo,
true, // aForceNoOpener
getter_AddRefs(newWin));
MOZ_ASSERT(!newWin);
@ -10192,7 +10331,7 @@ nsDocShell::DoURILoad(nsIURI* aURI,
uint32_t aReferrerPolicy,
nsIPrincipal* aTriggeringPrincipal,
nsIPrincipal* aPrincipalToInherit,
const nsACString& aTypeHint,
const char* aTypeHint,
const nsAString& aFileName,
nsIInputStream* aPostData,
nsIInputStream* aHeadersData,
@ -10541,8 +10680,8 @@ nsDocShell::DoURILoad(nsIURI* aURI,
loadInfo->SetResultPrincipalURI(aResultPrincipalURI.ref());
}
if (!aTypeHint.IsVoid()) {
channel->SetContentType(aTypeHint);
if (aTypeHint && *aTypeHint) {
channel->SetContentType(nsDependentCString(aTypeHint));
mContentTypeHint = aTypeHint;
} else {
mContentTypeHint.Truncate();
@ -12096,7 +12235,7 @@ nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType)
principalToInherit,
flags,
EmptyString(), // No window target
contentType, // Type hint
contentType.get(), // Type hint
VoidString(), // No forced file download
postData, // Post data stream
nullptr, // No headers stream
@ -13284,7 +13423,7 @@ nsDocShell::OnLinkClickSync(nsIContent* aContent,
aContent->NodePrincipal(),
flags,
target, // Window target
NS_LossyConvertUTF16toASCII(typeHint),
NS_LossyConvertUTF16toASCII(typeHint).get(),
aFileName, // Download as file
aPostDataStream, // Post data stream
aHeadersDataStream, // Headers stream

View File

@ -534,7 +534,7 @@ private: // member functions
uint32_t aReferrerPolicy,
nsIPrincipal* aTriggeringPrincipal,
nsIPrincipal* aPrincipalToInherit,
const nsACString& aTypeHint,
const char* aTypeHint,
const nsAString& aFileName,
nsIInputStream* aPostData,
nsIInputStream* aHeadersData,
@ -905,14 +905,6 @@ private: // member functions
return mCSSErrorReportingEnabled;
}
// Handles retrieval of subframe session history for nsDocShell::LoadURI. If a
// load is requested in a subframe of the current DocShell, the subframe
// loadType may need to reflect the loadType of the parent document, or in
// some cases (like reloads), the history load may need to be cancelled. See
// function comments for in-depth logic descriptions.
void
MaybeHandleSubframeHistory(nsDocShellLoadState* aLoadState);
private: // data members
static nsIURIFixup* sURIFixup;

View File

@ -0,0 +1,318 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "nsDocShellLoadInfo.h"
#include "nsISHEntry.h"
#include "nsIInputStream.h"
#include "nsIURI.h"
#include "nsIDocShell.h"
#include "mozilla/net/ReferrerPolicy.h"
#include "mozilla/Unused.h"
#include "mozilla/Maybe.h"
namespace mozilla {
} // mozilla
nsDocShellLoadInfo::nsDocShellLoadInfo()
: mResultPrincipalURIIsSome(false)
, mKeepResultPrincipalURIIfSet(false)
, mLoadReplace(false)
, mInheritPrincipal(false)
, mPrincipalIsExplicit(false)
, mForceAllowDataURI(false)
, mOriginalFrameSrc(false)
, mSendReferrer(true)
, mReferrerPolicy(mozilla::net::RP_Unset)
, mLoadType(LOAD_NORMAL)
, mIsSrcdocLoad(false)
{
}
nsDocShellLoadInfo::~nsDocShellLoadInfo()
{
}
nsIURI*
nsDocShellLoadInfo::Referrer() const
{
return mReferrer;
}
void
nsDocShellLoadInfo::SetReferrer(nsIURI* aReferrer)
{
mReferrer = aReferrer;
}
nsIURI*
nsDocShellLoadInfo::OriginalURI() const
{
return mOriginalURI;
}
void
nsDocShellLoadInfo::SetOriginalURI(nsIURI* aOriginalURI)
{
mOriginalURI = aOriginalURI;
}
nsIURI*
nsDocShellLoadInfo::ResultPrincipalURI() const
{
return mResultPrincipalURI;
}
void
nsDocShellLoadInfo::SetResultPrincipalURI(nsIURI* aResultPrincipalURI)
{
mResultPrincipalURI = aResultPrincipalURI;
}
bool
nsDocShellLoadInfo::ResultPrincipalURIIsSome() const
{
return mResultPrincipalURIIsSome;
}
void
nsDocShellLoadInfo::SetResultPrincipalURIIsSome(bool aIsSome)
{
mResultPrincipalURIIsSome = aIsSome;
}
bool
nsDocShellLoadInfo::KeepResultPrincipalURIIfSet() const
{
return mKeepResultPrincipalURIIfSet;
}
void
nsDocShellLoadInfo::SetKeepResultPrincipalURIIfSet(bool aKeep)
{
mKeepResultPrincipalURIIfSet = aKeep;
}
bool
nsDocShellLoadInfo::LoadReplace() const
{
return mLoadReplace;
}
void
nsDocShellLoadInfo::SetLoadReplace(bool aLoadReplace)
{
mLoadReplace = aLoadReplace;
}
nsIPrincipal*
nsDocShellLoadInfo::TriggeringPrincipal() const
{
return mTriggeringPrincipal;
}
void
nsDocShellLoadInfo::SetTriggeringPrincipal(nsIPrincipal* aTriggeringPrincipal)
{
mTriggeringPrincipal = aTriggeringPrincipal;
}
bool
nsDocShellLoadInfo::InheritPrincipal() const
{
return mInheritPrincipal;
}
void
nsDocShellLoadInfo::SetInheritPrincipal(bool aInheritPrincipal)
{
mInheritPrincipal = aInheritPrincipal;
}
bool
nsDocShellLoadInfo::PrincipalIsExplicit() const
{
return mPrincipalIsExplicit;
}
void
nsDocShellLoadInfo::SetPrincipalIsExplicit(bool aPrincipalIsExplicit)
{
mPrincipalIsExplicit = aPrincipalIsExplicit;
}
bool
nsDocShellLoadInfo::ForceAllowDataURI() const
{
return mForceAllowDataURI;
}
void
nsDocShellLoadInfo::SetForceAllowDataURI(bool aForceAllowDataURI)
{
mForceAllowDataURI = aForceAllowDataURI;
}
bool
nsDocShellLoadInfo::OriginalFrameSrc() const
{
return mOriginalFrameSrc;
}
void
nsDocShellLoadInfo::SetOriginalFrameSrc(bool aOriginalFrameSrc)
{
mOriginalFrameSrc = aOriginalFrameSrc;
}
uint32_t
nsDocShellLoadInfo::LoadType() const
{
return mLoadType;
}
void
nsDocShellLoadInfo::SetLoadType(uint32_t aLoadType)
{
mLoadType = aLoadType;
}
nsISHEntry*
nsDocShellLoadInfo::SHEntry() const
{
return mSHEntry;
}
void
nsDocShellLoadInfo::SetSHEntry(nsISHEntry* aSHEntry)
{
mSHEntry = aSHEntry;
}
void
nsDocShellLoadInfo::GetTarget(nsAString& aTarget) const
{
aTarget = mTarget;
}
void
nsDocShellLoadInfo::SetTarget(const nsAString& aTarget)
{
mTarget = aTarget;
}
nsIInputStream*
nsDocShellLoadInfo::PostDataStream() const
{
return mPostDataStream;
}
void
nsDocShellLoadInfo::SetPostDataStream(nsIInputStream* aStream)
{
mPostDataStream = aStream;
}
nsIInputStream*
nsDocShellLoadInfo::HeadersStream() const
{
return mHeadersStream;
}
void
nsDocShellLoadInfo::SetHeadersStream(nsIInputStream* aHeadersStream)
{
mHeadersStream = aHeadersStream;
}
bool
nsDocShellLoadInfo::SendReferrer() const
{
return mSendReferrer;
}
void
nsDocShellLoadInfo::SetSendReferrer(bool aSendReferrer)
{
mSendReferrer = aSendReferrer;
}
uint32_t
nsDocShellLoadInfo::ReferrerPolicy() const
{
return mReferrerPolicy;
}
void
nsDocShellLoadInfo::SetReferrerPolicy(mozilla::net::ReferrerPolicy aReferrerPolicy)
{
mReferrerPolicy = aReferrerPolicy;
}
bool
nsDocShellLoadInfo::IsSrcdocLoad() const
{
return mIsSrcdocLoad;
}
void
nsDocShellLoadInfo::GetSrcdocData(nsAString& aSrcdocData) const
{
aSrcdocData = mSrcdocData;
}
void
nsDocShellLoadInfo::SetSrcdocData(const nsAString& aSrcdocData)
{
mSrcdocData = aSrcdocData;
mIsSrcdocLoad = true;
}
nsIDocShell*
nsDocShellLoadInfo::SourceDocShell() const
{
return mSourceDocShell;
}
void
nsDocShellLoadInfo::SetSourceDocShell(nsIDocShell* aSourceDocShell)
{
mSourceDocShell = aSourceDocShell;
}
nsIURI*
nsDocShellLoadInfo::BaseURI() const
{
return mBaseURI;
}
void
nsDocShellLoadInfo::SetBaseURI(nsIURI* aBaseURI)
{
mBaseURI = aBaseURI;
}
void
nsDocShellLoadInfo::GetMaybeResultPrincipalURI(mozilla::Maybe<nsCOMPtr<nsIURI>>& aRPURI) const
{
bool isSome = ResultPrincipalURIIsSome();
aRPURI.reset();
if (!isSome) {
return;
}
nsCOMPtr<nsIURI> uri = ResultPrincipalURI();
aRPURI.emplace(std::move(uri));
}
void
nsDocShellLoadInfo::SetMaybeResultPrincipalURI(mozilla::Maybe<nsCOMPtr<nsIURI>> const& aRPURI)
{
SetResultPrincipalURI(aRPURI.refOr(nullptr));
SetResultPrincipalURIIsSome(aRPURI.isSome());
}

View File

@ -4,8 +4,8 @@
* 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/. */
#ifndef nsDocShellLoadState_h__
#define nsDocShellLoadState_h__
#ifndef nsDocShellLoadInfo_h__
#define nsDocShellLoadInfo_h__
// Helper Classes
#include "nsCOMPtr.h"
@ -17,29 +17,22 @@ class nsIInputStream;
class nsISHEntry;
class nsIURI;
class nsIDocShell;
class OriginAttibutes;
/**
* nsDocShellLoadState contains setup information used in a nsIDocShell::loadURI
* nsDocShellLoadInfo contains setup information used in a nsIDocShell::loadURI
* call.
*/
class nsDocShellLoadState final
class nsDocShellLoadInfo
{
public:
NS_INLINE_DECL_REFCOUNTING(nsDocShellLoadState);
NS_INLINE_DECL_REFCOUNTING(nsDocShellLoadInfo);
nsDocShellLoadState();
// Getters and Setters
nsDocShellLoadInfo();
nsIURI* Referrer() const;
void SetReferrer(nsIURI* aReferrer);
nsIURI* URI() const;
void SetURI(nsIURI* aURI);
nsIURI* OriginalURI() const;
void SetOriginalURI(nsIURI* aOriginalURI);
@ -56,10 +49,6 @@ public:
void SetKeepResultPrincipalURIIfSet(bool aKeep);
nsIPrincipal* PrincipalToInherit() const;
void SetPrincipalToInherit(nsIPrincipal* aPrincipalToInherit);
bool LoadReplace() const;
void SetLoadReplace(bool aLoadReplace);
@ -92,7 +81,7 @@ public:
void SetSHEntry(nsISHEntry* aSHEntry);
const nsString& Target() const;
void GetTarget(nsAString& aTarget) const;
void SetTarget(const nsAString& aTarget);
@ -114,7 +103,7 @@ public:
bool IsSrcdocLoad() const;
const nsString& SrcdocData() const;
void GetSrcdocData(nsAString& aSrcdocData) const;
void SetSrcdocData(const nsAString& aSrcdocData);
@ -134,54 +123,13 @@ public:
void
SetMaybeResultPrincipalURI(mozilla::Maybe<nsCOMPtr<nsIURI>> const& aRPURI);
uint32_t LoadFlags() const;
void SetLoadFlags(uint32_t aFlags);
bool FirstParty() const;
void SetFirstParty(bool aFirstParty);
const nsCString& TypeHint() const;
void SetTypeHint(const nsCString& aTypeHint);
const nsString& FileName() const;
void SetFileName(const nsAString& aFileName);
uint32_t DocShellInternalLoadFlags() const;
void SetDocShellInternalLoadFlags(uint32_t aFlags);
// Give the type of DocShell we're loading into (chrome/content/etc) and
// origin attributes for the URI we're loading, figure out if we should
// inherit our principal from the document the load was requested from, or
// else if the principal should be set up later in the process (after loads).
// See comments in function for more info on principal selection algorithm
nsresult SetupInheritingPrincipal(uint32_t aItemType, const mozilla::OriginAttributes& aOriginAttributes);
// If no triggering principal exists at the moment, create one using referrer
// information and origin attributes.
nsresult SetupTriggeringPrincipal(const mozilla::OriginAttributes& aOriginAttributes);
// When loading a document through nsDocShell::LoadURI(), a special set of
// flags needs to be set based on other values in nsDocShellLoadState. This
// function calculates those flags, before the LoadState is passed to
// nsDocShell::InternalLoad.
void CalculateDocShellInternalLoadFlags();
protected:
// Destructor can't be defaulted or inlined, as header doesn't have all type
// includes it needs to do so.
~nsDocShellLoadState();
virtual ~nsDocShellLoadInfo();
protected:
// This is the referrer for the load.
nsCOMPtr<nsIURI> mReferrer;
// The URI we are navigating to. Will not be null once set.
nsCOMPtr<nsIURI> mURI;
// The originalURI to be passed to nsIDocShell.internalLoad. May be null.
nsCOMPtr<nsIURI> mOriginalURI;
@ -214,12 +162,6 @@ protected:
// for a content docshell the load fails.
bool mPrincipalIsExplicit;
// Principal we're inheriting. If null, this means the principal should be
// inherited from the current document. If set to NullPrincipal, the channel
// will fill in principal information later in the load. See internal function
// comments for more info.
nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
// If this attribute is true, then a top-level navigation
// to a data URI will be allowed.
bool mForceAllowDataURI;
@ -267,27 +209,6 @@ protected:
// Used for srcdoc loads to give view-source knowledge of the load's base URI
// as this information isn't embedded in the load's URI.
nsCOMPtr<nsIURI> mBaseURI;
// Set of Load Flags, taken from nsDocShellLoadTypes.h
uint32_t mLoadFlags;
// Is this a First Party Load?
bool mFirstParty;
// A hint as to the content-type of the resulting data. If no hint, IsVoid()
// should return true.
nsCString mTypeHint;
// Non-void when the link should be downloaded as the given filename.
// mFileName being non-void but empty means that no filename hint was
// specified, but link should still trigger a download. If not a download,
// mFileName.IsVoid() should return true.
nsString mFileName;
// LoadFlags calculated in nsDocShell::LoadURI and passed to
// nsDocShell::InternalLoad, taken from the INTERNAL_LOAD consts in
// nsIDocShell.idl
uint32_t mDocShellInternalLoadFlags;
};
#endif /* nsDocShellLoadState_h__ */
#endif /* nsDocShellLoadInfo_h__ */

View File

@ -1,554 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "nsDocShellLoadState.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsIScriptSecurityManager.h"
#include "nsIWebNavigation.h"
#include "mozilla/OriginAttributes.h"
#include "mozilla/NullPrincipal.h"
nsDocShellLoadState::nsDocShellLoadState()
: mResultPrincipalURIIsSome(false)
, mKeepResultPrincipalURIIfSet(false)
, mLoadReplace(false)
, mInheritPrincipal(false)
, mPrincipalIsExplicit(false)
, mForceAllowDataURI(false)
, mOriginalFrameSrc(false)
, mSendReferrer(true)
, mReferrerPolicy(mozilla::net::RP_Unset)
, mLoadType(LOAD_NORMAL)
, mIsSrcdocLoad(false)
, mLoadFlags(0)
, mFirstParty(false)
, mTypeHint(VoidCString())
, mFileName(VoidString())
, mDocShellInternalLoadFlags(0)
{
}
nsDocShellLoadState::~nsDocShellLoadState()
{
}
nsIURI*
nsDocShellLoadState::Referrer() const
{
return mReferrer;
}
void
nsDocShellLoadState::SetReferrer(nsIURI* aReferrer)
{
mReferrer = aReferrer;
}
nsIURI*
nsDocShellLoadState::URI() const
{
return mURI;
}
void
nsDocShellLoadState::SetURI(nsIURI* aURI)
{
mURI = aURI;
}
nsIURI*
nsDocShellLoadState::OriginalURI() const
{
return mOriginalURI;
}
void
nsDocShellLoadState::SetOriginalURI(nsIURI* aOriginalURI)
{
mOriginalURI = aOriginalURI;
}
nsIURI*
nsDocShellLoadState::ResultPrincipalURI() const
{
return mResultPrincipalURI;
}
void
nsDocShellLoadState::SetResultPrincipalURI(nsIURI* aResultPrincipalURI)
{
mResultPrincipalURI = aResultPrincipalURI;
}
bool
nsDocShellLoadState::ResultPrincipalURIIsSome() const
{
return mResultPrincipalURIIsSome;
}
void
nsDocShellLoadState::SetResultPrincipalURIIsSome(bool aIsSome)
{
mResultPrincipalURIIsSome = aIsSome;
}
bool
nsDocShellLoadState::KeepResultPrincipalURIIfSet() const
{
return mKeepResultPrincipalURIIfSet;
}
void
nsDocShellLoadState::SetKeepResultPrincipalURIIfSet(bool aKeep)
{
mKeepResultPrincipalURIIfSet = aKeep;
}
bool
nsDocShellLoadState::LoadReplace() const
{
return mLoadReplace;
}
void
nsDocShellLoadState::SetLoadReplace(bool aLoadReplace)
{
mLoadReplace = aLoadReplace;
}
nsIPrincipal*
nsDocShellLoadState::TriggeringPrincipal() const
{
return mTriggeringPrincipal;
}
void
nsDocShellLoadState::SetTriggeringPrincipal(nsIPrincipal* aTriggeringPrincipal)
{
mTriggeringPrincipal = aTriggeringPrincipal;
}
nsIPrincipal*
nsDocShellLoadState::PrincipalToInherit() const
{
return mPrincipalToInherit;
}
void
nsDocShellLoadState::SetPrincipalToInherit(nsIPrincipal* aPrincipalToInherit)
{
mPrincipalToInherit = aPrincipalToInherit;
}
bool
nsDocShellLoadState::InheritPrincipal() const
{
return mInheritPrincipal;
}
void
nsDocShellLoadState::SetInheritPrincipal(bool aInheritPrincipal)
{
mInheritPrincipal = aInheritPrincipal;
}
bool
nsDocShellLoadState::PrincipalIsExplicit() const
{
return mPrincipalIsExplicit;
}
void
nsDocShellLoadState::SetPrincipalIsExplicit(bool aPrincipalIsExplicit)
{
mPrincipalIsExplicit = aPrincipalIsExplicit;
}
bool
nsDocShellLoadState::ForceAllowDataURI() const
{
return mForceAllowDataURI;
}
void
nsDocShellLoadState::SetForceAllowDataURI(bool aForceAllowDataURI)
{
mForceAllowDataURI = aForceAllowDataURI;
}
bool
nsDocShellLoadState::OriginalFrameSrc() const
{
return mOriginalFrameSrc;
}
void
nsDocShellLoadState::SetOriginalFrameSrc(bool aOriginalFrameSrc)
{
mOriginalFrameSrc = aOriginalFrameSrc;
}
uint32_t
nsDocShellLoadState::LoadType() const
{
return mLoadType;
}
void
nsDocShellLoadState::SetLoadType(uint32_t aLoadType)
{
mLoadType = aLoadType;
}
nsISHEntry*
nsDocShellLoadState::SHEntry() const
{
return mSHEntry;
}
void
nsDocShellLoadState::SetSHEntry(nsISHEntry* aSHEntry)
{
mSHEntry = aSHEntry;
}
const nsString&
nsDocShellLoadState::Target() const
{
return mTarget;
}
void
nsDocShellLoadState::SetTarget(const nsAString& aTarget)
{
mTarget = aTarget;
}
nsIInputStream*
nsDocShellLoadState::PostDataStream() const
{
return mPostDataStream;
}
void
nsDocShellLoadState::SetPostDataStream(nsIInputStream* aStream)
{
mPostDataStream = aStream;
}
nsIInputStream*
nsDocShellLoadState::HeadersStream() const
{
return mHeadersStream;
}
void
nsDocShellLoadState::SetHeadersStream(nsIInputStream* aHeadersStream)
{
mHeadersStream = aHeadersStream;
}
bool
nsDocShellLoadState::SendReferrer() const
{
return mSendReferrer;
}
void
nsDocShellLoadState::SetSendReferrer(bool aSendReferrer)
{
mSendReferrer = aSendReferrer;
}
uint32_t
nsDocShellLoadState::ReferrerPolicy() const
{
return mReferrerPolicy;
}
void
nsDocShellLoadState::SetReferrerPolicy(mozilla::net::ReferrerPolicy aReferrerPolicy)
{
mReferrerPolicy = aReferrerPolicy;
}
bool
nsDocShellLoadState::IsSrcdocLoad() const
{
return mIsSrcdocLoad;
}
const nsString&
nsDocShellLoadState::SrcdocData() const
{
return mSrcdocData;
}
void
nsDocShellLoadState::SetSrcdocData(const nsAString& aSrcdocData)
{
mSrcdocData = aSrcdocData;
mIsSrcdocLoad = true;
}
nsIDocShell*
nsDocShellLoadState::SourceDocShell() const
{
return mSourceDocShell;
}
void
nsDocShellLoadState::SetSourceDocShell(nsIDocShell* aSourceDocShell)
{
mSourceDocShell = aSourceDocShell;
}
nsIURI*
nsDocShellLoadState::BaseURI() const
{
return mBaseURI;
}
void
nsDocShellLoadState::SetBaseURI(nsIURI* aBaseURI)
{
mBaseURI = aBaseURI;
}
void
nsDocShellLoadState::GetMaybeResultPrincipalURI(mozilla::Maybe<nsCOMPtr<nsIURI>>& aRPURI) const
{
bool isSome = ResultPrincipalURIIsSome();
aRPURI.reset();
if (!isSome) {
return;
}
nsCOMPtr<nsIURI> uri = ResultPrincipalURI();
aRPURI.emplace(std::move(uri));
}
void
nsDocShellLoadState::SetMaybeResultPrincipalURI(mozilla::Maybe<nsCOMPtr<nsIURI>> const& aRPURI)
{
SetResultPrincipalURI(aRPURI.refOr(nullptr));
SetResultPrincipalURIIsSome(aRPURI.isSome());
}
uint32_t
nsDocShellLoadState::LoadFlags() const
{
return mLoadFlags;
}
void
nsDocShellLoadState::SetLoadFlags(uint32_t aLoadFlags)
{
mLoadFlags = aLoadFlags;
}
bool
nsDocShellLoadState::FirstParty() const
{
return mFirstParty;
}
void
nsDocShellLoadState::SetFirstParty(bool aFirstParty)
{
mFirstParty = aFirstParty;
}
const nsCString&
nsDocShellLoadState::TypeHint() const
{
return mTypeHint;
}
void
nsDocShellLoadState::SetTypeHint(const nsCString& aTypeHint)
{
mTypeHint = aTypeHint;
}
const nsString&
nsDocShellLoadState::FileName() const
{
return mFileName;
}
void
nsDocShellLoadState::SetFileName(const nsAString& aFileName)
{
mFileName = aFileName;
}
uint32_t
nsDocShellLoadState::DocShellInternalLoadFlags() const
{
return mDocShellInternalLoadFlags;
}
void
nsDocShellLoadState::SetDocShellInternalLoadFlags(uint32_t aFlags)
{
mDocShellInternalLoadFlags = aFlags;
}
nsresult
nsDocShellLoadState::SetupInheritingPrincipal(uint32_t aItemType,
const mozilla::OriginAttributes& aOriginAttributes)
{
// We need a principalToInherit.
//
// If principalIsExplicit is not set there are 4 possibilities:
// (1) If the system principal or an expanded principal was passed
// in and we're a typeContent docshell, inherit the principal
// from the current document instead.
// (2) In all other cases when the principal passed in is not null,
// use that principal.
// (3) If the caller has allowed inheriting from the current document,
// or if we're being called from system code (eg chrome JS or pure
// C++) then inheritPrincipal should be true and InternalLoad will get
// a principal from the current document. If none of these things are
// true, then
// (4) we don't pass a principal into the channel, and a principal will be
// created later from the channel's internal data.
//
// If principalIsExplicit *is* set, there are 4 possibilities
// (1) If the system principal or an expanded principal was passed in
// and we're a typeContent docshell, return an error.
// (2) In all other cases when the principal passed in is not null,
// use that principal.
// (3) If the caller has allowed inheriting from the current document,
// then inheritPrincipal should be true and InternalLoad will get
// a principal from the current document. If none of these things are
// true, then
// (4) we dont' pass a principal into the channel, and a principal will be
// created later from the channel's internal data.
mPrincipalToInherit = mTriggeringPrincipal;
if (mPrincipalToInherit && aItemType != nsIDocShellTreeItem::typeChrome) {
if (nsContentUtils::IsSystemPrincipal(mPrincipalToInherit)) {
if (mPrincipalIsExplicit) {
return NS_ERROR_DOM_SECURITY_ERR;
}
mPrincipalToInherit = nullptr;
mInheritPrincipal = true;
} else if (nsContentUtils::IsExpandedPrincipal(mPrincipalToInherit)) {
if (mPrincipalIsExplicit) {
return NS_ERROR_DOM_SECURITY_ERR;
}
// Don't inherit from the current page. Just do the safe thing
// and pretend that we were loaded by a nullprincipal.
//
// We didn't inherit OriginAttributes here as ExpandedPrincipal doesn't
// have origin attributes.
mPrincipalToInherit =
NullPrincipal::CreateWithInheritedAttributes(aOriginAttributes,
false);
mInheritPrincipal = false;
}
}
if (!mPrincipalToInherit && !mInheritPrincipal && !mPrincipalIsExplicit) {
// See if there's system or chrome JS code running
mInheritPrincipal = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
}
if (mLoadFlags & nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL) {
mInheritPrincipal = false;
// If mFirstParty is true and the pref 'privacy.firstparty.isolate' is
// enabled, we will set firstPartyDomain on the origin attributes.
mPrincipalToInherit =
NullPrincipal::CreateWithInheritedAttributes(aOriginAttributes,
mFirstParty);
}
return NS_OK;
}
nsresult
nsDocShellLoadState::SetupTriggeringPrincipal(const mozilla::OriginAttributes& aOriginAttributes)
{
// If the triggeringPrincipal is not set, we first try to create a principal
// from the referrer, since the referrer URI reflects the web origin that
// triggered the load. If there is no referrer URI, we fall back to using the
// SystemPrincipal. It's safe to assume that no provided triggeringPrincipal
// and no referrer simulate a load that was triggered by the system. It's
// important to note that this block of code needs to appear *after* the block
// where we munge the principalToInherit, because otherwise we would never
// enter code blocks checking if the principalToInherit is null and we will
// end up with a wrong inheritPrincipal flag.
if (!mTriggeringPrincipal) {
if (mReferrer) {
mTriggeringPrincipal =
BasePrincipal::CreateCodebasePrincipal(mReferrer, aOriginAttributes);
if (!mTriggeringPrincipal) {
return NS_ERROR_FAILURE;
}
} else {
mTriggeringPrincipal = nsContentUtils::GetSystemPrincipal();
}
}
return NS_OK;
}
void
nsDocShellLoadState::CalculateDocShellInternalLoadFlags()
{
MOZ_ASSERT(mDocShellInternalLoadFlags == 0,
"Shouldn't have any load flags set at this point.");
if (mInheritPrincipal) {
MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(mPrincipalToInherit),
"Should not inherit SystemPrincipal");
mDocShellInternalLoadFlags |=
nsIDocShell::INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
}
if (!mSendReferrer) {
mDocShellInternalLoadFlags |=
nsIDocShell::INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
}
if (mDocShellInternalLoadFlags & nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
mDocShellInternalLoadFlags |=
nsIDocShell::INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
}
if (mDocShellInternalLoadFlags & nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD) {
mDocShellInternalLoadFlags |= nsIDocShell::INTERNAL_LOAD_FLAGS_FIRST_LOAD;
}
if (mDocShellInternalLoadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_CLASSIFIER) {
mDocShellInternalLoadFlags |=
nsIDocShell::INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
}
if (mDocShellInternalLoadFlags & nsIWebNavigation::LOAD_FLAGS_FORCE_ALLOW_COOKIES) {
mDocShellInternalLoadFlags |=
nsIDocShell::INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
}
if (mIsSrcdocLoad) {
mDocShellInternalLoadFlags |= nsIDocShell::INTERNAL_LOAD_FLAGS_IS_SRCDOC;
}
if (mForceAllowDataURI) {
mDocShellInternalLoadFlags |=
nsIDocShell::INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
}
if (mOriginalFrameSrc) {
mDocShellInternalLoadFlags |=
nsIDocShell::INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC;
}
}

View File

@ -11,6 +11,7 @@
#include "nsDOMNavigationTiming.h"
#include "nsIDocShell.h"
#include "nsDocShellLoadInfo.h"
#include "nsIWebNavigation.h"
/**

View File

@ -17,7 +17,7 @@
#include "nsIURI.h"
class nsPresContext;
class nsIPresShell;
class nsDocShellLoadState;
class nsDocShellLoadInfo;
namespace mozilla {
class Encoding;
class HTMLEditor;
@ -63,7 +63,7 @@ interface nsICommandManager;
interface nsICommandParams;
interface nsILoadURIDelegate;
native TabChildRef(already_AddRefed<nsITabChild>);
native nsDocShellLoadStatePtr(nsDocShellLoadState*);
native nsDocShellLoadInfoPtr(nsDocShellLoadInfo*);
webidl BrowsingContext;
webidl ContentFrameMessageManager;
@ -78,9 +78,23 @@ interface nsIDocShell : nsIDocShellTreeItem
* however, the URL dispatcher will go through its normal process of content
* loading.
*
* @param loadState - This is the extended load info for this load.
* @param uri - The URI to load.
* @param loadInfo - This is the extended load info for this load. This
* most often will be null, but if you need to do
* additional setup for this load you can get a loadInfo
* object by calling createLoadInfo. Once you have this
* object you can set the needed properties on it and
* then pass it to loadURI.
* @param aLoadFlags - Flags to modify load behaviour. Flags are defined in
* nsIWebNavigation. Note that using flags outside
* LOAD_FLAGS_MASK is only allowed if passing in a
* non-null loadInfo. And even some of those might not
* be allowed. Use at your own risk.
*/
[noscript]void loadURI(in nsDocShellLoadStatePtr loadState);
[noscript]void loadURI(in nsIURI uri,
in nsDocShellLoadInfoPtr loadInfo,
in unsigned long aLoadFlags,
in boolean firstParty);
const long INTERNAL_LOAD_FLAGS_NONE = 0x0;
const long INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL = 0x1;
@ -91,12 +105,6 @@ interface nsIDocShell : nsIDocShellTreeItem
// @see nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD
const long INTERNAL_LOAD_FLAGS_FIRST_LOAD = 0x8;
// The set of flags that should not be set before calling into
// nsDocShell::LoadURI and other nsDocShell loading functions.
const long INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS = 0xf;
const long INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER = 0x10;
const long INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES = 0x20;
@ -190,7 +198,7 @@ interface nsIDocShell : nsIDocShellTreeItem
in nsIPrincipal aPrincipalToInherit,
in uint32_t aFlags,
in AString aWindowTarget,
in ACString aTypeHint,
in string aTypeHint,
in AString aFileName,
in nsIInputStream aPostDataStream,
in nsIInputStream aHeadersStream,

View File

@ -9,8 +9,8 @@
#include <algorithm>
#include "nsDocShellEditorData.h"
#include "nsDocShellLoadTypes.h"
#include "nsIContentViewer.h"
#include "nsDocShellLoadInfo.h"
#include "nsIDocShellTreeItem.h"
#include "nsIInputStream.h"
#include "nsILayoutHistoryState.h"

View File

@ -13,7 +13,7 @@
#include "nsDocShell.h"
#include "nsIContentViewer.h"
#include "nsIDocShell.h"
#include "nsDocShellLoadState.h"
#include "nsDocShellLoadInfo.h"
#include "nsIDocShellTreeItem.h"
#include "nsILayoutHistoryState.h"
#include "nsIObserverService.h"
@ -1575,7 +1575,7 @@ nsSHistory::InitiateLoad(nsISHEntry* aFrameEntry, nsIDocShell* aFrameDS,
{
NS_ENSURE_STATE(aFrameDS && aFrameEntry);
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState();
RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
/* Set the loadType in the SHEntry too to what was passed on.
* This will be passed on to child subframes later in nsDocShell,
@ -1583,21 +1583,19 @@ nsSHistory::InitiateLoad(nsISHEntry* aFrameEntry, nsIDocShell* aFrameDS,
*/
aFrameEntry->SetLoadType(aLoadType);
loadState->SetLoadType(aLoadType);
loadState->SetSHEntry(aFrameEntry);
loadInfo->SetLoadType(aLoadType);
loadInfo->SetSHEntry(aFrameEntry);
nsCOMPtr<nsIURI> originalURI = aFrameEntry->GetOriginalURI();
loadState->SetOriginalURI(originalURI);
loadInfo->SetOriginalURI(originalURI);
loadState->SetLoadReplace(aFrameEntry->GetLoadReplace());
nsCOMPtr<nsIURI> newURI = aFrameEntry->GetURI();
loadState->SetURI(newURI);
loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
loadState->SetFirstParty(false);
loadInfo->SetLoadReplace(aFrameEntry->GetLoadReplace());
nsCOMPtr<nsIURI> nextURI = aFrameEntry->GetURI();
// Time to initiate a document load
return aFrameDS->LoadURI(loadState);
return aFrameDS->LoadURI(nextURI, loadInfo,
nsIWebNavigation::LOAD_FLAGS_NONE, false);
}
NS_IMETHODIMP_(void)

View File

@ -37,7 +37,7 @@ test(function(t) {
interpolated_matrix,
'the expected matrix from interpolatematrix(' +
'translateX(100px), rotate(90deg), 0.5) to none at 50%');
}, 'Test interpolation from interpolatmatrix to none at 50%');
}, 'Test interpolation from interpolatematrix to none at 50%');
</script>
</html>

View File

@ -9,7 +9,7 @@
#include "nsIScriptObjectPrincipal.h"
#include "nsIScriptContext.h"
#include "nsIDocShell.h"
#include "nsDocShellLoadState.h"
#include "nsDocShellLoadInfo.h"
#include "nsIWebNavigation.h"
#include "nsCDefaultURIFixup.h"
#include "nsIURIFixup.h"
@ -61,7 +61,7 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Location, mInnerWindow)
NS_IMPL_CYCLE_COLLECTING_ADDREF(Location)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Location)
already_AddRefed<nsDocShellLoadState>
already_AddRefed<nsDocShellLoadInfo>
Location::CheckURL(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
@ -152,16 +152,16 @@ Location::CheckURL(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal,
}
// Create load info
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState();
RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
loadState->SetTriggeringPrincipal(triggeringPrincipal);
loadInfo->SetTriggeringPrincipal(triggeringPrincipal);
if (sourceURI) {
loadState->SetReferrer(sourceURI);
loadState->SetReferrerPolicy(referrerPolicy);
loadInfo->SetReferrer(sourceURI);
loadInfo->SetReferrerPolicy(referrerPolicy);
}
return loadState.forget();
return loadInfo.forget();
}
nsresult
@ -212,31 +212,27 @@ Location::SetURI(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal,
{
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
if (docShell) {
RefPtr<nsDocShellLoadState> loadState =
RefPtr<nsDocShellLoadInfo> loadInfo =
CheckURL(aURI, aSubjectPrincipal, aRv);
if (aRv.Failed()) {
return;
}
if (aReplace) {
loadState->SetLoadType(LOAD_STOP_CONTENT_AND_REPLACE);
loadInfo->SetLoadType(LOAD_STOP_CONTENT_AND_REPLACE);
} else {
loadState->SetLoadType(LOAD_STOP_CONTENT);
loadInfo->SetLoadType(LOAD_STOP_CONTENT);
}
// Get the incumbent script's browsing context to set as source.
nsCOMPtr<nsPIDOMWindowInner> sourceWindow =
do_QueryInterface(mozilla::dom::GetIncumbentGlobal());
if (sourceWindow) {
loadState->SetSourceDocShell(sourceWindow->GetDocShell());
loadInfo->SetSourceDocShell(sourceWindow->GetDocShell());
}
loadState->SetURI(aURI);
loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
loadState->SetFirstParty(true);
nsresult rv = docShell->LoadURI(loadState);
nsresult rv = docShell->LoadURI(aURI, loadInfo,
nsIWebNavigation::LOAD_FLAGS_NONE, true);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
}

View File

@ -17,7 +17,6 @@
class nsIDocShell;
class nsIURI;
class nsDocShellLoadState;
namespace mozilla {
namespace dom {
@ -168,7 +167,6 @@ protected:
// Note, this method can return NS_OK with a null value for aURL. This happens
// if the docShell is null.
nsresult GetURI(nsIURI** aURL, bool aGetInnermostURI = false);
void SetURI(nsIURI* aURL, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv, bool aReplace = false);
void SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
@ -186,7 +184,7 @@ protected:
// Check whether it's OK to load the given url with the given subject
// principal, and if so construct the right nsDocShellLoadInfo for the load
// and return it.
already_AddRefed<nsDocShellLoadState> CheckURL(nsIURI *url,
already_AddRefed<nsDocShellLoadInfo> CheckURL(nsIURI *url,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv);

View File

@ -258,6 +258,7 @@
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/TabGroup.h"
#ifdef MOZ_XUL
#include "mozilla/dom/XULBroadcastManager.h"
#include "mozilla/dom/TreeBoxObject.h"
#include "nsIXULWindow.h"
#include "nsXULCommandDispatcher.h"
@ -1745,6 +1746,10 @@ nsDocument::~nsDocument()
mStyleImageLoader->DropDocumentReference();
}
if (mXULBroadcastManager) {
mXULBroadcastManager->DropDocumentReference();
}
delete mHeaderData;
ClearAllBoxObjects();
@ -5113,6 +5118,9 @@ nsDocument::EndUpdate()
MaybeEndOutermostXBLUpdate();
MaybeInitializeFinalizeFrameLoaders();
if (mXULBroadcastManager) {
mXULBroadcastManager->MaybeBroadcast();
}
}
void
@ -10247,6 +10255,15 @@ nsIDocument::GetCommandDispatcher()
return mCommandDispatcher;
}
void
nsIDocument::InitializeXULBroadcastManager()
{
if (mXULBroadcastManager) {
return;
}
mXULBroadcastManager = new XULBroadcastManager(this);
}
static JSObject*
GetScopeObjectOfNode(nsINode* node)
{

View File

@ -25,7 +25,7 @@
#include "nsIWebProgress.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeOwner.h"
#include "nsDocShellLoadState.h"
#include "nsDocShellLoadInfo.h"
#include "nsIBaseWindow.h"
#include "nsIBrowser.h"
#include "nsContentUtils.h"
@ -390,9 +390,9 @@ nsFrameLoader::ReallyStartLoadingInternal()
rv = CheckURILoad(mURIToLoad, mTriggeringPrincipal);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState();
RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
loadState->SetOriginalFrameSrc(mLoadingOriginalSrc);
loadInfo->SetOriginalFrameSrc(mLoadingOriginalSrc);
mLoadingOriginalSrc = false;
// If this frame is sandboxed with respect to origin we will set it up with
@ -403,9 +403,9 @@ nsFrameLoader::ReallyStartLoadingInternal()
// is very important; needed to prevent XSS attacks on documents loaded in
// subframes!
if (mTriggeringPrincipal) {
loadState->SetTriggeringPrincipal(mTriggeringPrincipal);
loadInfo->SetTriggeringPrincipal(mTriggeringPrincipal);
} else {
loadState->SetTriggeringPrincipal(mOwnerContent->NodePrincipal());
loadInfo->SetTriggeringPrincipal(mOwnerContent->NodePrincipal());
}
nsCOMPtr<nsIURI> referrer;
@ -420,9 +420,9 @@ nsFrameLoader::ReallyStartLoadingInternal()
mOwnerContent->OwnerDoc()->GetReferrer(referrerStr);
rv = NS_NewURI(getter_AddRefs(referrer), referrerStr);
loadState->SetSrcdocData(srcdoc);
loadInfo->SetSrcdocData(srcdoc);
nsCOMPtr<nsIURI> baseURI = mOwnerContent->GetBaseURI();
loadState->SetBaseURI(baseURI);
loadInfo->SetBaseURI(baseURI);
}
else {
rv = mOwnerContent->NodePrincipal()->GetURI(getter_AddRefs(referrer));
@ -437,7 +437,7 @@ nsFrameLoader::ReallyStartLoadingInternal()
bool isNullPrincipalScheme;
rv = referrer->SchemeIs(NS_NULLPRINCIPAL_SCHEME, &isNullPrincipalScheme);
if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
loadState->SetReferrer(referrer);
loadInfo->SetReferrer(referrer);
}
}
@ -453,7 +453,7 @@ nsFrameLoader::ReallyStartLoadingInternal()
referrerPolicy = iframeReferrerPolicy;
}
}
loadState->SetReferrerPolicy(referrerPolicy);
loadInfo->SetReferrerPolicy(referrerPolicy);
// Default flags:
int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE;
@ -467,10 +467,8 @@ nsFrameLoader::ReallyStartLoadingInternal()
// Kick off the load...
bool tmpState = mNeedsAsyncDestroy;
mNeedsAsyncDestroy = true;
loadState->SetURI(mURIToLoad);
loadState->SetLoadFlags(flags);
loadState->SetFirstParty(false);
rv = mDocShell->LoadURI(loadState);
nsCOMPtr<nsIURI> uriToLoad = mURIToLoad;
rv = mDocShell->LoadURI(uriToLoad, loadInfo, flags, false);
mNeedsAsyncDestroy = tmpState;
mURIToLoad = nullptr;
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -65,7 +65,7 @@
#include "nsPrintfCString.h"
#include "mozilla/intl/LocaleService.h"
#include "WindowDestroyedEvent.h"
#include "nsDocShellLoadState.h"
#include "nsDocShellLoadInfo.h"
// Helper Classes
#include "nsJSUtils.h"
@ -5520,7 +5520,7 @@ nsGlobalWindowOuter::OpenOuter(const nsAString& aUrl, const nsAString& aName,
nsresult
nsGlobalWindowOuter::Open(const nsAString& aUrl, const nsAString& aName,
const nsAString& aOptions, nsDocShellLoadState* aLoadState,
const nsAString& aOptions, nsDocShellLoadInfo* aLoadInfo,
bool aForceNoOpener, nsPIDOMWindowOuter **_retval)
{
return OpenInternal(aUrl, aName, aOptions,
@ -5530,7 +5530,7 @@ nsGlobalWindowOuter::Open(const nsAString& aUrl, const nsAString& aName,
false, // aDoJSFixups
true, // aNavigate
nullptr, nullptr, // No args
aLoadState,
aLoadInfo,
aForceNoOpener,
_retval);
}
@ -5546,7 +5546,7 @@ nsGlobalWindowOuter::OpenJS(const nsAString& aUrl, const nsAString& aName,
true, // aDoJSFixups
true, // aNavigate
nullptr, nullptr, // No args
nullptr, // aLoadState
nullptr, // aLoadInfo
false, // aForceNoOpener
_retval);
}
@ -5566,7 +5566,7 @@ nsGlobalWindowOuter::OpenDialog(const nsAString& aUrl, const nsAString& aName,
false, // aDoJSFixups
true, // aNavigate
nullptr, aExtraArgument, // Arguments
nullptr, // aLoadState
nullptr, // aLoadInfo
false, // aForceNoOpener
_retval);
}
@ -5585,7 +5585,7 @@ nsGlobalWindowOuter::OpenNoNavigate(const nsAString& aUrl,
false, // aDoJSFixups
false, // aNavigate
nullptr, nullptr, // No args
nullptr, // aLoadState
nullptr, // aLoadInfo
false, // aForceNoOpener
_retval);
@ -5613,7 +5613,7 @@ nsGlobalWindowOuter::OpenDialogOuter(JSContext* aCx, const nsAString& aUrl,
false, // aDoJSFixups
true, // aNavigate
argvArray, nullptr, // Arguments
nullptr, // aLoadState
nullptr, // aLoadInfo
false, // aForceNoOpener
getter_AddRefs(dialog));
return dialog.forget();
@ -6937,7 +6937,7 @@ nsGlobalWindowOuter::OpenInternal(const nsAString& aUrl, const nsAString& aName,
bool aDoJSFixups, bool aNavigate,
nsIArray *argv,
nsISupports *aExtraArgument,
nsDocShellLoadState* aLoadState,
nsDocShellLoadInfo* aLoadInfo,
bool aForceNoOpener,
nsPIDOMWindowOuter **aReturn)
{
@ -7086,7 +7086,7 @@ nsGlobalWindowOuter::OpenInternal(const nsAString& aUrl, const nsAString& aName,
aDialog, aNavigate, argv,
isPopupSpamWindow,
forceNoOpener,
aLoadState,
aLoadInfo,
getter_AddRefs(domReturn));
} else {
// Force a system caller here so that the window watcher won't screw us
@ -7109,7 +7109,7 @@ nsGlobalWindowOuter::OpenInternal(const nsAString& aUrl, const nsAString& aName,
aDialog, aNavigate, aExtraArgument,
isPopupSpamWindow,
forceNoOpener,
aLoadState,
aLoadInfo,
getter_AddRefs(domReturn));
}

View File

@ -72,7 +72,7 @@ class nsITimeoutHandler;
class nsIWebBrowserChrome;
class mozIDOMWindowProxy;
class nsDocShellLoadState;
class nsDocShellLoadInfo;
class nsDOMWindowList;
class nsScreen;
class nsHistory;
@ -607,7 +607,7 @@ public:
mozilla::ErrorResult& aError);
nsresult Open(const nsAString& aUrl, const nsAString& aName,
const nsAString& aOptions,
nsDocShellLoadState* aLoadState,
nsDocShellLoadInfo* aLoadInfo,
bool aForceNoOpener,
nsPIDOMWindowOuter **_retval) override;
mozilla::dom::Navigator* GetNavigator() override;
@ -879,7 +879,7 @@ private:
* @param aExtraArgument Another way to pass arguments in. This is mutually
* exclusive with the argv approach.
*
* @param aLoadState to be passed on along to the windowwatcher.
* @param aLoadInfo to be passed on along to the windowwatcher.
*
* @param aForceNoOpener if true, will act as if "noopener" were passed in
* aOptions, but without affecting any other window
@ -901,7 +901,7 @@ private:
bool aNavigate,
nsIArray *argv,
nsISupports *aExtraArgument,
nsDocShellLoadState* aLoadState,
nsDocShellLoadInfo* aLoadInfo,
bool aForceNoOpener,
nsPIDOMWindowOuter **aReturn);

View File

@ -148,6 +148,7 @@ class Animation;
class AnonymousContent;
class Attr;
class BoxObject;
class XULBroadcastManager;
class ClientInfo;
class ClientState;
class CDATASection;
@ -3442,6 +3443,15 @@ public:
mozilla::dom::Promise* GetDocumentReadyForIdle(mozilla::ErrorResult& aRv);
nsIDOMXULCommandDispatcher* GetCommandDispatcher();
bool HasXULBroadcastManager() const
{
return mXULBroadcastManager;
};
void InitializeXULBroadcastManager();
mozilla::dom::XULBroadcastManager* GetXULBroadcastManager() const
{
return mXULBroadcastManager;
}
already_AddRefed<nsINode> GetPopupNode();
void SetPopupNode(nsINode* aNode);
nsINode* GetPopupRangeParent(ErrorResult& aRv);
@ -4739,6 +4749,8 @@ protected:
nsCOMPtr<nsIDOMXULCommandDispatcher> mCommandDispatcher; // [OWNER] of the focus tracker
RefPtr<mozilla::dom::XULBroadcastManager> mXULBroadcastManager;
// At the moment, trackers might be blocked by Tracking Protection or FastBlock.
// In order to know the numbers of trackers detected and blocked, we add
// these two values here and those are shared by TP and FB.

View File

@ -31,7 +31,7 @@ class nsIChannel;
class nsIContent;
class nsICSSDeclaration;
class nsIDocShell;
class nsDocShellLoadState;
class nsDocShellLoadInfo;
class nsIDocument;
class nsIPrincipal;
class nsIScriptTimeoutHandler;
@ -1121,12 +1121,12 @@ public:
virtual nsDOMWindowList* GetFrames() = 0;
// aLoadState will be passed on through to the windowwatcher.
// aLoadInfo will be passed on through to the windowwatcher.
// aForceNoOpener will act just like a "noopener" feature in aOptions except
// will not affect any other window features.
virtual nsresult Open(const nsAString& aUrl, const nsAString& aName,
const nsAString& aOptions,
nsDocShellLoadState* aLoadState,
nsDocShellLoadInfo* aLoadInfo,
bool aForceNoOpener,
nsPIDOMWindowOuter **_retval) = 0;
virtual nsresult OpenDialog(const nsAString& aUrl, const nsAString& aName,

View File

@ -9,7 +9,7 @@
#include "ClientState.h"
#include "mozilla/Unused.h"
#include "nsIDocShell.h"
#include "nsDocShellLoadState.h"
#include "nsDocShellLoadInfo.h"
#include "nsIWebNavigation.h"
#include "nsIWebProgress.h"
#include "nsIWebProgressListener.h"
@ -236,16 +236,13 @@ ClientNavigateOpChild::DoNavigate(const ClientNavigateOpConstructorArgs& aArgs)
return ref.forget();
}
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState();
RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
loadState->SetTriggeringPrincipal(principal);
loadState->SetReferrerPolicy(doc->GetReferrerPolicy());
loadState->SetLoadType(LOAD_STOP_CONTENT);
loadState->SetSourceDocShell(docShell);
loadState->SetURI(url);
loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
loadState->SetFirstParty(true);
rv = docShell->LoadURI(loadState);
loadInfo->SetTriggeringPrincipal(principal);
loadInfo->SetReferrerPolicy(doc->GetReferrerPolicy());
loadInfo->SetLoadType(LOAD_STOP_CONTENT);
loadInfo->SetSourceDocShell(docShell);
rv = docShell->LoadURI(url, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, true);
if (NS_FAILED(rv)) {
ref = ClientOpPromise::CreateAndReject(rv, __func__);
return ref.forget();

View File

@ -9,9 +9,12 @@
display: flex;
background-color: grey;
font: 14px sans-serif;
width: 800px;
height: 50px;
}
#flex-sanity {
/* This just needs to be large enough so that no shrinking is required. */
width: 1600px;
}
.base { align-self: baseline; }
.lastbase { align-self: last baseline; }
@ -23,6 +26,7 @@
.yellow { background: yellow; }
.orange { background: orange; }
.pink { background: pink; }
.tan { background: tan; }
.white { background: white; }
.crossMinMax { min-height: 40px;
@ -32,6 +36,11 @@
max-width: 500px; }
.flexGrow { flex-grow: 1; }
.spacer150 { width: 150px;
box-sizing: border-box;
height: 10px;
border: 1px solid teal; }
</style>
<script>
@ -46,10 +55,6 @@ function testItemMatchesExpectedValues(item, values, index) {
is(item.node, values.node, "Item index " + index + " has expected node.");
}
if (typeof(values.node_todo) != "undefined") {
todo_is(item.node, values.node_todo, "Item index " + index + " has expected node.");
}
if (typeof(values.mainBaseSize) != "undefined") {
is(item.mainBaseSize, values.mainBaseSize, "Item index " + index + " has expected mainBaseSize.");
}
@ -85,8 +90,9 @@ function testItemMatchesExpectedValues(item, values, index) {
}
}
function runTests() {
let container = document.getElementById("wrapper");
// Test for items in "flex-sanity" flex container:
function testFlexSanity() {
let container = document.getElementById("flex-sanity");
let flex = container.getAsFlexContainer();
let lines = flex.getLines();
is(lines.length, 1, "Container should have expected number of lines.");
@ -118,7 +124,7 @@ function runTests() {
let expectedValues = [
{ crossMinSize: 0 },
{ mainBaseSize: lbElemBoundingRect.width,
{ mainBaseSize: 100,
mainDeltaSize: 0 },
{ crossMinSize: 40,
crossMaxSize: 120,
@ -126,6 +132,18 @@ function runTests() {
{ mainMinSize: 120,
mainMaxSize: 500,
mainDeltaSize: 0 },
{ mainMinSize: 120,
mainMaxSize: 500,
mainBaseSize: 150,
mainDeltaSize: 0 },
{ mainBaseSize: 10,
mainMaxSize: 5,
mainDeltaSize: 0 },
{ mainBaseSize: 10,
mainMinSize: 15,
mainDeltaSize: 0 },
{ mainBaseSize: 50,
mainMaxSize: 10 },
{ mainDeltaSize: 0 },
{ /* final item is anonymous flex item */ },
];
@ -161,22 +179,127 @@ function runTests() {
// actual size minus the base size.
isfuzzy(items[0].mainDeltaSize, firstRect.width - items[0].mainBaseSize, 1e-4,
"flex-grow item should have expected mainDeltaSize.");
}
// Test for items in "flex-growing" flex container:
function testFlexGrowing() {
let expectedValues = [
{ mainBaseSize: 10,
mainDeltaSize: 10,
mainMinSize: 35 },
{ mainBaseSize: 20,
mainDeltaSize: 5,
mainMinSize: 28 },
{ mainBaseSize: 30,
mainDeltaSize: 7 },
{ mainBaseSize: 0,
mainDeltaSize: 48,
mainMaxSize: 20 },
];
let container = document.getElementById("flex-growing");
let items = container.getAsFlexContainer().getLines()[0].getItems();
is(items.length, container.children.length,
"Line should have as many items as the flex container has child elems");
for (let i = 0; i < items.length; ++i) {
let item = items[i];
let values = expectedValues[i];
testItemMatchesExpectedValues(item, values, i);
}
}
function runTests() {
testFlexSanity();
testFlexGrowing();
SimpleTest.finish();
}
</script>
</head>
<body onLoad="runTests();">
<div id="wrapper" class="container">
<!-- First flex container to be tested: "flex-sanity".
We test a few general things, e.g.:
- accuracy of reported baselines.
- accuracy of reported flex base size, min/max sizes, & trivial deltas.
- flex items formation for display:contents subtrees & text nodes.
-->
<div id="flex-sanity" class="container">
<div class="lime base flexGrow">one line (first)</div>
<div class="yellow lastbase" style="width: 100px">one line (last)</div>
<div class="orange offset lastbase crossMinMax">two<br/>lines and offset (last)</div>
<div class="pink offset base mainMinMax">offset (first)</div>
<!-- Inflexible item w/ content-derived flex base size, which has min/max
but doesn't violate them: -->
<div class="tan mainMinMax">
<div class="spacer150"></div>
</div>
<!-- Inflexible item that is trivially clamped to smaller max-width: -->
<div style="flex: 0 0 10px; max-width: 5px"></div>
<!-- Inflexible item that is trivially clamped to larger min-width: -->
<div style="flex: 0 0 10px; min-width: 15px"></div>
<!-- Item that wants to grow but is trivially clamped to max-width
below base size: -->
<div style="flex: 1 1 50px; max-width: 10px"></div>
<div style="display:contents">
<div class="white">replaced</div>
</div>
anonymous text node
anon item for text
</div>
<!-- Second flex container to be tested, with items that grow by specific
predictable amounts. This ends up triggering 4 passes of the main
flexbox layout algorithm loop - and note that for each item, we only
report (via 'mainDeltaSize') the delta that it wanted in the *last pass
of the loop before that item was frozen*.
Here's what goes on in each pass (and the tentative deltas)
* PASS 1
- Available space = 120 - 10 - 20 - 30 - 0 = 60px.
- Sum of flex values = 1+1+2+16 = 20. So 1 "share" is 60px/20 = 3px.
- Deltas (space distributed): +3px, +3px, +6px, +48px
VIOLATIONS:
item0 is now 10+3 = 13px, which under its min
item1 is now 20+3 = 23px, which under its min
item3 is now 0+48 = 48px, which over its max
- the magnitude of max-violations (how far we're out of bounds) exceeds
magnitude of min-violations, so we prioritize the max-violations.
- So we freeze item3 at its max-width, 20px, leaving its final "desired"
mainDeltaSize at +48px from this pass.
* PASS 2
- Available space = 120 - 10 - 20 - 30 - 20 = 40px.
- Sum of flex values = 1+1+2 = 4. So 1 "share" is 40px/4 = 10px.
- Deltas (space distributed): +10px, +10px, +20px, +0px.
VIOLATIONS:
item0 is now 10+10 = 20px, which is under its min
- So we freeze item0 at its min-width, 35px, leaving its final "desired"
mainDeltaSize at +10px from this pass.
* PASS 3
- Available space = 120 - 35 - 20 - 30 - 20 = 15px.
- Sum of flex values = 1+2 = 3. So 1 "share" is 15px/3 = 5px.
- Deltas (space distributed): +0px, +5px, +10px, +0px.
VIOLATIONS:
item1 is now 20+5 = 25px, which is under its min
- So we freeze item1 at its min-width, 28px, leaving its final "desired"
mainDeltaSize at +5px from this pass.
* PASS 4
- Available space = 120 - 35 - 28 - 30 - 20 = 7px.
- Sum of flex values = 2. So 1 "share" is 7px/2 = 3.5px
- Deltas (space distributed): +0px, +0px, +7px, +0px.
VIOLATIONS:
None! (So, we'll be done after this pass!)
- So we freeze item2 (the only unfrozen item) at its flexed size, 37px,
and set its final "desired" mainDeltaSize to +7px from this pass.
- And we're done!
-->
<div id="flex-growing" class="container" style="width: 120px">
<div style="flex: 1 10px; min-width: 35px"></div>
<div style="flex: 1 20px; min-width: 28px"></div>
<div style="flex: 2 30px; min-width: 0"></div>
<div style="flex: 16 0px; max-width: 20px"></div>
</div>
</body>
</html>

View File

@ -119,7 +119,7 @@
#include "mozInlineSpellChecker.h"
#include "nsDocShell.h"
#include "nsDocShellLoadState.h"
#include "nsDocShellLoadInfo.h"
#include "nsIConsoleListener.h"
#include "nsIContentViewer.h"
#include "nsICycleCollectorListener.h"
@ -774,19 +774,19 @@ ContentChild::ProvideWindow(mozIDOMWindowProxy* aParent,
const nsAString& aName,
const nsACString& aFeatures,
bool aForceNoOpener,
nsDocShellLoadState* aLoadState,
nsDocShellLoadInfo* aLoadInfo,
bool* aWindowIsNew,
mozIDOMWindowProxy** aReturn)
{
return ProvideWindowCommon(nullptr, aParent, false, aChromeFlags,
aCalledFromJS, aPositionSpecified,
aSizeSpecified, aURI, aName, aFeatures,
aForceNoOpener, aLoadState, aWindowIsNew, aReturn);
aForceNoOpener, aLoadInfo, aWindowIsNew, aReturn);
}
static nsresult
GetCreateWindowParams(mozIDOMWindowProxy* aParent,
nsDocShellLoadState* aLoadState,
nsDocShellLoadInfo* aLoadInfo,
nsACString& aBaseURIString, float* aFullZoom,
uint32_t* aReferrerPolicy,
nsIPrincipal** aTriggeringPrincipal)
@ -814,11 +814,11 @@ GetCreateWindowParams(mozIDOMWindowProxy* aParent,
baseURI->GetSpec(aBaseURIString);
if (aLoadState) {
if (!aLoadState->SendReferrer()) {
if (aLoadInfo) {
if (!aLoadInfo->SendReferrer()) {
*aReferrerPolicy = mozilla::net::RP_No_Referrer;
} else {
*aReferrerPolicy = aLoadState->ReferrerPolicy();
*aReferrerPolicy = aLoadInfo->ReferrerPolicy();
}
}
@ -849,7 +849,7 @@ ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
const nsAString& aName,
const nsACString& aFeatures,
bool aForceNoOpener,
nsDocShellLoadState* aLoadState,
nsDocShellLoadInfo* aLoadInfo,
bool* aWindowIsNew,
mozIDOMWindowProxy** aReturn)
{
@ -896,7 +896,7 @@ ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
float fullZoom;
nsCOMPtr<nsIPrincipal> triggeringPrincipal;
uint32_t referrerPolicy = mozilla::net::RP_Unset;
rv = GetCreateWindowParams(aParent, aLoadState, baseURIString, &fullZoom,
rv = GetCreateWindowParams(aParent, aLoadInfo, baseURIString, &fullZoom,
&referrerPolicy,
getter_AddRefs(triggeringPrincipal));
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -1110,7 +1110,7 @@ ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
float fullZoom;
nsCOMPtr<nsIPrincipal> triggeringPrincipal;
uint32_t referrerPolicy = mozilla::net::RP_Unset;
rv = GetCreateWindowParams(aParent, aLoadState, baseURIString, &fullZoom,
rv = GetCreateWindowParams(aParent, aLoadInfo, baseURIString, &fullZoom,
&referrerPolicy,
getter_AddRefs(triggeringPrincipal));
if (NS_WARN_IF(NS_FAILED(rv))) {

View File

@ -34,7 +34,7 @@ struct OverrideMapping;
class nsIDomainPolicy;
class nsIURIClassifierCallback;
struct LookAndFeelInt;
class nsDocShellLoadState;
class nsDocShellLoadInfo;
namespace mozilla {
class RemoteSpellcheckEngineChild;
@ -119,7 +119,7 @@ public:
const nsAString& aName,
const nsACString& aFeatures,
bool aForceNoOpener,
nsDocShellLoadState* aLoadState,
nsDocShellLoadInfo* aLoadInfo,
bool* aWindowIsNew,
mozIDOMWindowProxy** aReturn);

View File

@ -124,7 +124,7 @@
#include "nsString.h"
#include "nsISupportsPrimitives.h"
#include "mozilla/Telemetry.h"
#include "nsDocShellLoadState.h"
#include "nsDocShellLoadInfo.h"
#include "nsWebBrowser.h"
#ifdef XP_WIN
@ -960,7 +960,7 @@ TabChild::ProvideWindow(mozIDOMWindowProxy* aParent,
bool aPositionSpecified, bool aSizeSpecified,
nsIURI* aURI, const nsAString& aName,
const nsACString& aFeatures, bool aForceNoOpener,
nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
nsDocShellLoadInfo* aLoadInfo, bool* aWindowIsNew,
mozIDOMWindowProxy** aReturn)
{
*aReturn = nullptr;
@ -1004,7 +1004,7 @@ TabChild::ProvideWindow(mozIDOMWindowProxy* aParent,
aName,
aFeatures,
aForceNoOpener,
aLoadState,
aLoadInfo,
aWindowIsNew,
aReturn);
}

View File

@ -0,0 +1,614 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=4 sw=4 et tw=80: */
/* 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/. */
#include "XULBroadcastManager.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "mozilla/EventDispatcher.h"
#include "nsXULElement.h"
#include "mozilla/Logging.h"
struct BroadcastListener {
nsWeakPtr mListener;
RefPtr<nsAtom> mAttribute;
};
struct BroadcasterMapEntry : public PLDHashEntryHdr
{
Element* mBroadcaster; // [WEAK]
nsTArray<BroadcastListener*> mListeners; // [OWNING] of BroadcastListener objects
};
struct nsAttrNameInfo
{
nsAttrNameInfo(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix) :
mNamespaceID(aNamespaceID), mName(aName), mPrefix(aPrefix) {}
nsAttrNameInfo(const nsAttrNameInfo& aOther) :
mNamespaceID(aOther.mNamespaceID), mName(aOther.mName),
mPrefix(aOther.mPrefix) {}
int32_t mNamespaceID;
RefPtr<nsAtom> mName;
RefPtr<nsAtom> mPrefix;
};
static void
ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
{
BroadcasterMapEntry* entry =
static_cast<BroadcasterMapEntry*>(aEntry);
for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
delete entry->mListeners[i];
}
entry->mListeners.Clear();
// N.B. that we need to manually run the dtor because we
// constructed the nsTArray object in-place.
entry->mListeners.~nsTArray<BroadcastListener*>();
}
static bool
CanBroadcast(int32_t aNameSpaceID, nsAtom* aAttribute)
{
// Don't push changes to the |id|, |persist|, |command| or
// |observes| attribute.
if (aNameSpaceID == kNameSpaceID_None) {
if ((aAttribute == nsGkAtoms::id) ||
(aAttribute == nsGkAtoms::persist) ||
(aAttribute == nsGkAtoms::command) ||
(aAttribute == nsGkAtoms::observes)) {
return false;
}
}
return true;
}
namespace mozilla {
namespace dom {
static LazyLogModule sXULBroadCastManager("XULBroadcastManager");
/* static */
bool
XULBroadcastManager::MayNeedListener(const Element& aElement) {
if (aElement.NodeInfo()->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) {
return true;
}
if (aElement.HasAttr(nsGkAtoms::observes)) {
return true;
}
if (aElement.HasAttr(nsGkAtoms::command) &&
!(aElement.NodeInfo()->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL) ||
aElement.NodeInfo()->Equals(nsGkAtoms::key, kNameSpaceID_XUL))) {
return true;
}
return false;
}
XULBroadcastManager::XULBroadcastManager(nsIDocument* aDocument)
: mDocument(aDocument),
mBroadcasterMap(nullptr),
mHandlingDelayedAttrChange(false),
mHandlingDelayedBroadcasters(false)
{
}
XULBroadcastManager::~XULBroadcastManager()
{
delete mBroadcasterMap;
}
void
XULBroadcastManager::DropDocumentReference(void)
{
mDocument = nullptr;
}
void
XULBroadcastManager::SynchronizeBroadcastListener(Element *aBroadcaster,
Element *aListener,
const nsAString &aAttr)
{
if (!nsContentUtils::IsSafeToRunScript()) {
nsDelayedBroadcastUpdate delayedUpdate(aBroadcaster, aListener,
aAttr);
mDelayedBroadcasters.AppendElement(delayedUpdate);
MaybeBroadcast();
return;
}
bool notify = mHandlingDelayedBroadcasters;
if (aAttr.EqualsLiteral("*")) {
uint32_t count = aBroadcaster->GetAttrCount();
nsTArray<nsAttrNameInfo> attributes(count);
for (uint32_t i = 0; i < count; ++i) {
const nsAttrName* attrName = aBroadcaster->GetAttrNameAt(i);
int32_t nameSpaceID = attrName->NamespaceID();
nsAtom* name = attrName->LocalName();
// _Don't_ push the |id|, |ref|, or |persist| attribute's value!
if (! CanBroadcast(nameSpaceID, name))
continue;
attributes.AppendElement(nsAttrNameInfo(nameSpaceID, name,
attrName->GetPrefix()));
}
count = attributes.Length();
while (count-- > 0) {
int32_t nameSpaceID = attributes[count].mNamespaceID;
nsAtom* name = attributes[count].mName;
nsAutoString value;
if (aBroadcaster->GetAttr(nameSpaceID, name, value)) {
aListener->SetAttr(nameSpaceID, name, attributes[count].mPrefix,
value, notify);
}
#if 0
// XXX we don't fire the |onbroadcast| handler during
// initial hookup: doing so would potentially run the
// |onbroadcast| handler before the |onload| handler,
// which could define JS properties that mask XBL
// properties, etc.
ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
#endif
}
}
else {
// Find out if the attribute is even present at all.
RefPtr<nsAtom> name = NS_Atomize(aAttr);
nsAutoString value;
if (aBroadcaster->GetAttr(kNameSpaceID_None, name, value)) {
aListener->SetAttr(kNameSpaceID_None, name, value, notify);
} else {
aListener->UnsetAttr(kNameSpaceID_None, name, notify);
}
#if 0
// XXX we don't fire the |onbroadcast| handler during initial
// hookup: doing so would potentially run the |onbroadcast|
// handler before the |onload| handler, which could define JS
// properties that mask XBL properties, etc.
ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
#endif
}
}
void
XULBroadcastManager::AddListenerFor(Element& aBroadcaster, Element& aListener,
const nsAString& aAttr, ErrorResult& aRv)
{
if (!mDocument) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
nsresult rv =
nsContentUtils::CheckSameOrigin(mDocument, &aBroadcaster);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
rv = nsContentUtils::CheckSameOrigin(mDocument, &aListener);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
static const PLDHashTableOps gOps = {
PLDHashTable::HashVoidPtrKeyStub,
PLDHashTable::MatchEntryStub,
PLDHashTable::MoveEntryStub,
ClearBroadcasterMapEntry,
nullptr
};
if (! mBroadcasterMap) {
mBroadcasterMap = new PLDHashTable(&gOps, sizeof(BroadcasterMapEntry));
}
auto entry = static_cast<BroadcasterMapEntry*>
(mBroadcasterMap->Search(&aBroadcaster));
if (!entry) {
entry = static_cast<BroadcasterMapEntry*>
(mBroadcasterMap->Add(&aBroadcaster, fallible));
if (! entry) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
entry->mBroadcaster = &aBroadcaster;
// N.B. placement new to construct the nsTArray object in-place
new (&entry->mListeners) nsTArray<BroadcastListener*>();
}
// Only add the listener if it's not there already!
RefPtr<nsAtom> attr = NS_Atomize(aAttr);
for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
BroadcastListener* bl = entry->mListeners[i];
nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
if (blListener == &aListener && bl->mAttribute == attr)
return;
}
BroadcastListener* bl = new BroadcastListener;
bl->mListener = do_GetWeakReference(&aListener);
bl->mAttribute = attr;
entry->mListeners.AppendElement(bl);
SynchronizeBroadcastListener(&aBroadcaster, &aListener, aAttr);
}
void
XULBroadcastManager::RemoveListenerFor(Element& aBroadcaster,
Element& aListener,
const nsAString& aAttr)
{
// If we haven't added any broadcast listeners, then there sure
// aren't any to remove.
if (! mBroadcasterMap)
return;
auto entry = static_cast<BroadcasterMapEntry*>
(mBroadcasterMap->Search(&aBroadcaster));
if (entry) {
RefPtr<nsAtom> attr = NS_Atomize(aAttr);
for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
BroadcastListener* bl = entry->mListeners[i];
nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
if (blListener == &aListener && bl->mAttribute == attr) {
entry->mListeners.RemoveElementAt(i);
delete bl;
if (entry->mListeners.IsEmpty())
mBroadcasterMap->RemoveEntry(entry);
break;
}
}
}
}
nsresult
XULBroadcastManager::ExecuteOnBroadcastHandlerFor(Element* aBroadcaster,
Element* aListener,
nsAtom* aAttr)
{
if (!mDocument) {
return NS_OK;
}
// Now we execute the onchange handler in the context of the
// observer. We need to find the observer in order to
// execute the handler.
for (nsIContent* child = aListener->GetFirstChild();
child;
child = child->GetNextSibling()) {
// Look for an <observes> element beneath the listener. This
// ought to have an |element| attribute that refers to
// aBroadcaster, and an |attribute| element that tells us what
// attriubtes we're listening for.
if (!child->IsXULElement(nsGkAtoms::observes))
continue;
// Is this the element that was listening to us?
nsAutoString listeningToID;
child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::element, listeningToID);
nsAutoString broadcasterID;
aBroadcaster->GetAttr(kNameSpaceID_None, nsGkAtoms::id, broadcasterID);
if (listeningToID != broadcasterID)
continue;
// We are observing the broadcaster, but is this the right
// attribute?
nsAutoString listeningToAttribute;
child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute,
listeningToAttribute);
if (!aAttr->Equals(listeningToAttribute) &&
!listeningToAttribute.EqualsLiteral("*")) {
continue;
}
// This is the right <observes> element. Execute the
// |onbroadcast| event handler
WidgetEvent event(true, eXULBroadcast);
RefPtr<nsPresContext> presContext = mDocument->GetPresContext();
if (presContext) {
// Handle the DOM event
nsEventStatus status = nsEventStatus_eIgnore;
EventDispatcher::Dispatch(child, presContext, &event, nullptr,
&status);
}
}
return NS_OK;
}
void
XULBroadcastManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
nsAtom* aAttribute)
{
if (!mDocument) {
return;
}
NS_ASSERTION(aElement->OwnerDoc() == mDocument, "unexpected doc");
// Synchronize broadcast listeners
if (mBroadcasterMap &&
CanBroadcast(aNameSpaceID, aAttribute)) {
auto entry = static_cast<BroadcasterMapEntry*>
(mBroadcasterMap->Search(aElement));
if (entry) {
// We've got listeners: push the value.
nsAutoString value;
bool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value);
for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
BroadcastListener* bl = entry->mListeners[i];
if ((bl->mAttribute == aAttribute) ||
(bl->mAttribute == nsGkAtoms::_asterisk)) {
nsCOMPtr<Element> listenerEl
= do_QueryReferent(bl->mListener);
if (listenerEl) {
nsAutoString currentValue;
bool hasAttr = listenerEl->GetAttr(kNameSpaceID_None,
aAttribute,
currentValue);
// We need to update listener only if we're
// (1) removing an existing attribute,
// (2) adding a new attribute or
// (3) changing the value of an attribute.
bool needsAttrChange =
attrSet != hasAttr || !value.Equals(currentValue);
nsDelayedBroadcastUpdate delayedUpdate(aElement,
listenerEl,
aAttribute,
value,
attrSet,
needsAttrChange);
size_t index =
mDelayedAttrChangeBroadcasts.IndexOf(delayedUpdate,
0, nsDelayedBroadcastUpdate::Comparator());
if (index != mDelayedAttrChangeBroadcasts.NoIndex) {
if (mHandlingDelayedAttrChange) {
NS_WARNING("Broadcasting loop!");
continue;
}
mDelayedAttrChangeBroadcasts.RemoveElementAt(index);
}
mDelayedAttrChangeBroadcasts.AppendElement(delayedUpdate);
}
}
}
}
}
}
void
XULBroadcastManager::MaybeBroadcast()
{
// Only broadcast when not in an update and when safe to run scripts.
if (mDocument && mDocument->UpdateNestingLevel() == 0 &&
(mDelayedAttrChangeBroadcasts.Length() ||
mDelayedBroadcasters.Length())) {
if (!nsContentUtils::IsSafeToRunScript()) {
if (mDocument) {
nsContentUtils::AddScriptRunner(
NewRunnableMethod("dom::XULBroadcastManager::MaybeBroadcast",
this,
&XULBroadcastManager::MaybeBroadcast));
}
return;
}
if (!mHandlingDelayedAttrChange) {
mHandlingDelayedAttrChange = true;
for (uint32_t i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) {
nsAtom* attrName = mDelayedAttrChangeBroadcasts[i].mAttrName;
if (mDelayedAttrChangeBroadcasts[i].mNeedsAttrChange) {
nsCOMPtr<Element> listener = mDelayedAttrChangeBroadcasts[i].mListener;
const nsString& value = mDelayedAttrChangeBroadcasts[i].mAttr;
if (mDelayedAttrChangeBroadcasts[i].mSetAttr) {
listener->SetAttr(kNameSpaceID_None, attrName, value,
true);
} else {
listener->UnsetAttr(kNameSpaceID_None, attrName,
true);
}
}
ExecuteOnBroadcastHandlerFor(mDelayedAttrChangeBroadcasts[i].mBroadcaster,
mDelayedAttrChangeBroadcasts[i].mListener,
attrName);
}
mDelayedAttrChangeBroadcasts.Clear();
mHandlingDelayedAttrChange = false;
}
uint32_t length = mDelayedBroadcasters.Length();
if (length) {
bool oldValue = mHandlingDelayedBroadcasters;
mHandlingDelayedBroadcasters = true;
nsTArray<nsDelayedBroadcastUpdate> delayedBroadcasters;
mDelayedBroadcasters.SwapElements(delayedBroadcasters);
for (uint32_t i = 0; i < length; ++i) {
SynchronizeBroadcastListener(delayedBroadcasters[i].mBroadcaster,
delayedBroadcasters[i].mListener,
delayedBroadcasters[i].mAttr);
}
mHandlingDelayedBroadcasters = oldValue;
}
}
}
nsresult
XULBroadcastManager::FindBroadcaster(Element* aElement,
Element** aListener,
nsString& aBroadcasterID,
nsString& aAttribute,
Element** aBroadcaster)
{
NodeInfo *ni = aElement->NodeInfo();
*aListener = nullptr;
*aBroadcaster = nullptr;
if (ni->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) {
// It's an <observes> element, which means that the actual
// listener is the _parent_ node. This element should have an
// 'element' attribute that specifies the ID of the
// broadcaster element, and an 'attribute' element, which
// specifies the name of the attribute to observe.
nsIContent* parent = aElement->GetParent();
if (!parent) {
// <observes> is the root element
return NS_FINDBROADCASTER_NOT_FOUND;
}
*aListener = Element::FromNode(parent);
NS_IF_ADDREF(*aListener);
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, aBroadcasterID);
if (aBroadcasterID.IsEmpty()) {
return NS_FINDBROADCASTER_NOT_FOUND;
}
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, aAttribute);
}
else {
// It's a generic element, which means that we'll use the
// value of the 'observes' attribute to determine the ID of
// the broadcaster element, and we'll watch _all_ of its
// values.
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, aBroadcasterID);
// Bail if there's no aBroadcasterID
if (aBroadcasterID.IsEmpty()) {
// Try the command attribute next.
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::command, aBroadcasterID);
if (!aBroadcasterID.IsEmpty()) {
// We've got something in the command attribute. We
// only treat this as a normal broadcaster if we are
// not a menuitem or a key.
if (ni->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL) ||
ni->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) {
return NS_FINDBROADCASTER_NOT_FOUND;
}
}
else {
return NS_FINDBROADCASTER_NOT_FOUND;
}
}
*aListener = aElement;
NS_ADDREF(*aListener);
aAttribute.Assign('*');
}
// Make sure we got a valid listener.
NS_ENSURE_TRUE(*aListener, NS_ERROR_UNEXPECTED);
// Try to find the broadcaster element in the document.
nsIDocument* doc = aElement->GetComposedDoc();
if (doc) {
*aBroadcaster = doc->GetElementById(aBroadcasterID);
}
// The broadcaster element is missing.
if (! *aBroadcaster) {
return NS_FINDBROADCASTER_NOT_FOUND;
}
NS_ADDREF(*aBroadcaster);
return NS_FINDBROADCASTER_FOUND;
}
nsresult
XULBroadcastManager::UpdateListenerHookup(Element* aElement, HookupAction aAction)
{
// Resolve a broadcaster hookup. Look at the element that we're
// trying to resolve: it could be an '<observes>' element, or just
// a vanilla element with an 'observes' attribute on it.
nsresult rv;
nsCOMPtr<Element> listener;
nsAutoString broadcasterID;
nsAutoString attribute;
nsCOMPtr<Element> broadcaster;
rv = FindBroadcaster(aElement, getter_AddRefs(listener),
broadcasterID, attribute, getter_AddRefs(broadcaster));
switch (rv) {
case NS_FINDBROADCASTER_NOT_FOUND:
return NS_OK;
case NS_FINDBROADCASTER_FOUND:
break;
default:
return rv;
}
NS_ENSURE_ARG(broadcaster && listener);
if (aAction == eHookupAdd) {
ErrorResult domRv;
AddListenerFor(*broadcaster, *listener, attribute, domRv);
if (domRv.Failed()) {
return domRv.StealNSResult();
}
} else {
RemoveListenerFor(*broadcaster, *listener, attribute);
}
// Tell the world we succeeded
if (MOZ_LOG_TEST(sXULBroadCastManager, LogLevel::Debug)) {
nsCOMPtr<nsIContent> content = listener;
NS_ASSERTION(content != nullptr, "not an nsIContent");
if (!content) {
return rv;
}
nsAutoCString attributeC,broadcasteridC;
LossyCopyUTF16toASCII(attribute, attributeC);
LossyCopyUTF16toASCII(broadcasterID, broadcasteridC);
MOZ_LOG(sXULBroadCastManager, LogLevel::Debug,
("xul: broadcaster hookup <%s attribute='%s'> to %s",
nsAtomCString(content->NodeInfo()->NameAtom()).get(),
attributeC.get(),
broadcasteridC.get()));
}
return NS_OK;
}
nsresult
XULBroadcastManager::AddListener(Element* aElement)
{
return UpdateListenerHookup(aElement, eHookupAdd);
}
nsresult
XULBroadcastManager::RemoveListener(Element* aElement)
{
return UpdateListenerHookup(aElement, eHookupRemove);
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,140 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_dom_XULBroadcastManager_h
#define mozilla_dom_XULBroadcastManager_h
#include "mozilla/dom/Element.h"
#include "nsAtom.h"
class nsXULElement;
namespace mozilla {
namespace dom {
class XULBroadcastManager final {
public:
typedef mozilla::dom::Element Element;
explicit XULBroadcastManager(nsIDocument* aDocument);
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(XULBroadcastManager)
/**
* Checks whether an element uses any of the special broadcaster attributes
* or is an observes element. This mimics the logic in FindBroadcaster, but
* is intended to be a lighter weight check and doesn't actually guarantee
* that the element will need a listener.
*/
static bool MayNeedListener(const Element& aElement);
nsresult AddListener(Element* aElement);
nsresult RemoveListener(Element* aElement);
void AttributeChanged(Element* aElement, int32_t aNameSpaceID,
nsAtom* aAttribute);
void MaybeBroadcast();
void DropDocumentReference(); // notification that doc is going away
protected:
enum HookupAction {
eHookupAdd = 0,
eHookupRemove
};
nsresult UpdateListenerHookup(Element* aElement, HookupAction aAction);
void RemoveListenerFor(Element& aBroadcaster, Element& aListener,
const nsAString& aAttr);
void AddListenerFor(Element& aBroadcaster, Element& aListener,
const nsAString& aAttr, ErrorResult& aRv);
nsresult
ExecuteOnBroadcastHandlerFor(Element* aBroadcaster,
Element* aListener,
nsAtom* aAttr);
// The out params of FindBroadcaster only have values that make sense when
// the method returns NS_FINDBROADCASTER_FOUND. In all other cases, the
// values of the out params should not be relied on (though *aListener and
// *aBroadcaster do need to be released if non-null, of course).
nsresult
FindBroadcaster(Element* aElement,
Element** aListener,
nsString& aBroadcasterID,
nsString& aAttribute,
Element** aBroadcaster);
void
SynchronizeBroadcastListener(Element *aBroadcaster,
Element *aListener,
const nsAString &aAttr);
// This reference is nulled by the Document in it's destructor through
// DropDocumentReference().
nsIDocument* MOZ_NON_OWNING_REF mDocument;
/**
* A map from a broadcaster element to a list of listener elements.
*/
PLDHashTable* mBroadcasterMap;
class nsDelayedBroadcastUpdate
{
public:
nsDelayedBroadcastUpdate(Element* aBroadcaster,
Element* aListener,
const nsAString &aAttr)
: mBroadcaster(aBroadcaster), mListener(aListener), mAttr(aAttr),
mSetAttr(false), mNeedsAttrChange(false) {}
nsDelayedBroadcastUpdate(Element* aBroadcaster,
Element* aListener,
nsAtom* aAttrName,
const nsAString &aAttr,
bool aSetAttr,
bool aNeedsAttrChange)
: mBroadcaster(aBroadcaster), mListener(aListener), mAttr(aAttr),
mAttrName(aAttrName), mSetAttr(aSetAttr),
mNeedsAttrChange(aNeedsAttrChange) {}
nsDelayedBroadcastUpdate(const nsDelayedBroadcastUpdate& aOther)
: mBroadcaster(aOther.mBroadcaster), mListener(aOther.mListener),
mAttr(aOther.mAttr), mAttrName(aOther.mAttrName),
mSetAttr(aOther.mSetAttr), mNeedsAttrChange(aOther.mNeedsAttrChange) {}
nsCOMPtr<Element> mBroadcaster;
nsCOMPtr<Element> mListener;
// Note if mAttrName isn't used, this is the name of the attr, otherwise
// this is the value of the attribute.
nsString mAttr;
RefPtr<nsAtom> mAttrName;
bool mSetAttr;
bool mNeedsAttrChange;
class Comparator {
public:
static bool Equals(const nsDelayedBroadcastUpdate& a, const nsDelayedBroadcastUpdate& b) {
return a.mBroadcaster == b.mBroadcaster && a.mListener == b.mListener && a.mAttrName == b.mAttrName;
}
};
};
nsTArray<nsDelayedBroadcastUpdate> mDelayedBroadcasters;
nsTArray<nsDelayedBroadcastUpdate> mDelayedAttrChangeBroadcasts;
bool mHandlingDelayedAttrChange;
bool mHandlingDelayedBroadcasters;
private:
~XULBroadcastManager();
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_XULBroadcastManager_h

View File

@ -116,19 +116,6 @@ int32_t XULDocument::gRefCnt = 0;
LazyLogModule XULDocument::gXULLog("XULDocument");
//----------------------------------------------------------------------
struct BroadcastListener {
nsWeakPtr mListener;
RefPtr<nsAtom> mAttribute;
};
struct BroadcasterMapEntry : public PLDHashEntryHdr
{
Element* mBroadcaster; // [WEAK]
nsTArray<BroadcastListener*> mListeners; // [OWNING] of BroadcastListener objects
};
//----------------------------------------------------------------------
//
// ctors & dtors
@ -149,10 +136,7 @@ XULDocument::XULDocument(void)
mOffThreadCompiling(false),
mOffThreadCompileStringBuf(nullptr),
mOffThreadCompileStringLength(0),
mBroadcasterMap(nullptr),
mInitialLayoutComplete(false),
mHandlingDelayedAttrChange(false),
mHandlingDelayedBroadcasters(false)
mInitialLayoutComplete(false)
{
// Override the default in nsDocument
mCharacterSet = UTF_8_ENCODING;
@ -170,9 +154,6 @@ XULDocument::~XULDocument()
NS_ASSERTION(mNextSrcLoadWaiter == nullptr,
"unreferenced document still waiting for script source to load?");
// Destroy our broadcaster map.
delete mBroadcasterMap;
Preferences::UnregisterCallback(XULDocument::DirectionChanged,
"intl.uidirection", this);
@ -442,276 +423,6 @@ XULDocument::OnPrototypeLoadDone(bool aResumeWalk)
return rv;
}
static void
ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
{
BroadcasterMapEntry* entry =
static_cast<BroadcasterMapEntry*>(aEntry);
for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
delete entry->mListeners[i];
}
entry->mListeners.Clear();
// N.B. that we need to manually run the dtor because we
// constructed the nsTArray object in-place.
entry->mListeners.~nsTArray<BroadcastListener*>();
}
static bool
CanBroadcast(int32_t aNameSpaceID, nsAtom* aAttribute)
{
// Don't push changes to the |id|, |persist|, |command| or
// |observes| attribute.
if (aNameSpaceID == kNameSpaceID_None) {
if ((aAttribute == nsGkAtoms::id) ||
(aAttribute == nsGkAtoms::persist) ||
(aAttribute == nsGkAtoms::command) ||
(aAttribute == nsGkAtoms::observes)) {
return false;
}
}
return true;
}
struct nsAttrNameInfo
{
nsAttrNameInfo(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix) :
mNamespaceID(aNamespaceID), mName(aName), mPrefix(aPrefix) {}
nsAttrNameInfo(const nsAttrNameInfo& aOther) :
mNamespaceID(aOther.mNamespaceID), mName(aOther.mName),
mPrefix(aOther.mPrefix) {}
int32_t mNamespaceID;
RefPtr<nsAtom> mName;
RefPtr<nsAtom> mPrefix;
};
void
XULDocument::SynchronizeBroadcastListener(Element *aBroadcaster,
Element *aListener,
const nsAString &aAttr)
{
if (!nsContentUtils::IsSafeToRunScript()) {
nsDelayedBroadcastUpdate delayedUpdate(aBroadcaster, aListener,
aAttr);
mDelayedBroadcasters.AppendElement(delayedUpdate);
MaybeBroadcast();
return;
}
bool notify = mDocumentLoaded || mHandlingDelayedBroadcasters;
if (aAttr.EqualsLiteral("*")) {
uint32_t count = aBroadcaster->GetAttrCount();
nsTArray<nsAttrNameInfo> attributes(count);
for (uint32_t i = 0; i < count; ++i) {
const nsAttrName* attrName = aBroadcaster->GetAttrNameAt(i);
int32_t nameSpaceID = attrName->NamespaceID();
nsAtom* name = attrName->LocalName();
// _Don't_ push the |id|, |ref|, or |persist| attribute's value!
if (! CanBroadcast(nameSpaceID, name))
continue;
attributes.AppendElement(nsAttrNameInfo(nameSpaceID, name,
attrName->GetPrefix()));
}
count = attributes.Length();
while (count-- > 0) {
int32_t nameSpaceID = attributes[count].mNamespaceID;
nsAtom* name = attributes[count].mName;
nsAutoString value;
if (aBroadcaster->GetAttr(nameSpaceID, name, value)) {
aListener->SetAttr(nameSpaceID, name, attributes[count].mPrefix,
value, notify);
}
#if 0
// XXX we don't fire the |onbroadcast| handler during
// initial hookup: doing so would potentially run the
// |onbroadcast| handler before the |onload| handler,
// which could define JS properties that mask XBL
// properties, etc.
ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
#endif
}
}
else {
// Find out if the attribute is even present at all.
RefPtr<nsAtom> name = NS_Atomize(aAttr);
nsAutoString value;
if (aBroadcaster->GetAttr(kNameSpaceID_None, name, value)) {
aListener->SetAttr(kNameSpaceID_None, name, value, notify);
} else {
aListener->UnsetAttr(kNameSpaceID_None, name, notify);
}
#if 0
// XXX we don't fire the |onbroadcast| handler during initial
// hookup: doing so would potentially run the |onbroadcast|
// handler before the |onload| handler, which could define JS
// properties that mask XBL properties, etc.
ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
#endif
}
}
void
XULDocument::AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
const nsAString& aAttr, ErrorResult& aRv)
{
nsresult rv =
nsContentUtils::CheckSameOrigin(this, &aBroadcaster);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
rv = nsContentUtils::CheckSameOrigin(this, &aListener);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
static const PLDHashTableOps gOps = {
PLDHashTable::HashVoidPtrKeyStub,
PLDHashTable::MatchEntryStub,
PLDHashTable::MoveEntryStub,
ClearBroadcasterMapEntry,
nullptr
};
if (! mBroadcasterMap) {
mBroadcasterMap = new PLDHashTable(&gOps, sizeof(BroadcasterMapEntry));
}
auto entry = static_cast<BroadcasterMapEntry*>
(mBroadcasterMap->Search(&aBroadcaster));
if (!entry) {
entry = static_cast<BroadcasterMapEntry*>
(mBroadcasterMap->Add(&aBroadcaster, fallible));
if (! entry) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
entry->mBroadcaster = &aBroadcaster;
// N.B. placement new to construct the nsTArray object in-place
new (&entry->mListeners) nsTArray<BroadcastListener*>();
}
// Only add the listener if it's not there already!
RefPtr<nsAtom> attr = NS_Atomize(aAttr);
for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
BroadcastListener* bl = entry->mListeners[i];
nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
if (blListener == &aListener && bl->mAttribute == attr)
return;
}
BroadcastListener* bl = new BroadcastListener;
bl->mListener = do_GetWeakReference(&aListener);
bl->mAttribute = attr;
entry->mListeners.AppendElement(bl);
SynchronizeBroadcastListener(&aBroadcaster, &aListener, aAttr);
}
void
XULDocument::RemoveBroadcastListenerFor(Element& aBroadcaster,
Element& aListener,
const nsAString& aAttr)
{
// If we haven't added any broadcast listeners, then there sure
// aren't any to remove.
if (! mBroadcasterMap)
return;
auto entry = static_cast<BroadcasterMapEntry*>
(mBroadcasterMap->Search(&aBroadcaster));
if (entry) {
RefPtr<nsAtom> attr = NS_Atomize(aAttr);
for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
BroadcastListener* bl = entry->mListeners[i];
nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
if (blListener == &aListener && bl->mAttribute == attr) {
entry->mListeners.RemoveElementAt(i);
delete bl;
if (entry->mListeners.IsEmpty())
mBroadcasterMap->RemoveEntry(entry);
break;
}
}
}
}
nsresult
XULDocument::ExecuteOnBroadcastHandlerFor(Element* aBroadcaster,
Element* aListener,
nsAtom* aAttr)
{
// Now we execute the onchange handler in the context of the
// observer. We need to find the observer in order to
// execute the handler.
for (nsIContent* child = aListener->GetFirstChild();
child;
child = child->GetNextSibling()) {
// Look for an <observes> element beneath the listener. This
// ought to have an |element| attribute that refers to
// aBroadcaster, and an |attribute| element that tells us what
// attriubtes we're listening for.
if (!child->IsXULElement(nsGkAtoms::observes))
continue;
// Is this the element that was listening to us?
nsAutoString listeningToID;
child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::element, listeningToID);
nsAutoString broadcasterID;
aBroadcaster->GetAttr(kNameSpaceID_None, nsGkAtoms::id, broadcasterID);
if (listeningToID != broadcasterID)
continue;
// We are observing the broadcaster, but is this the right
// attribute?
nsAutoString listeningToAttribute;
child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute,
listeningToAttribute);
if (!aAttr->Equals(listeningToAttribute) &&
!listeningToAttribute.EqualsLiteral("*")) {
continue;
}
// This is the right <observes> element. Execute the
// |onbroadcast| event handler
WidgetEvent event(true, eXULBroadcast);
RefPtr<nsPresContext> presContext = GetPresContext();
if (presContext) {
// Handle the DOM event
nsEventStatus status = nsEventStatus_eIgnore;
EventDispatcher::Dispatch(child, presContext, &event, nullptr,
&status);
}
}
return NS_OK;
}
static bool
ShouldPersistAttribute(Element* aElement, nsAtom* aAttribute)
{
@ -744,62 +455,6 @@ XULDocument::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
// Might not need this, but be safe for now.
nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
// Synchronize broadcast listeners
if (mBroadcasterMap &&
CanBroadcast(aNameSpaceID, aAttribute)) {
auto entry = static_cast<BroadcasterMapEntry*>
(mBroadcasterMap->Search(aElement));
if (entry) {
// We've got listeners: push the value.
nsAutoString value;
bool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value);
for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
BroadcastListener* bl = entry->mListeners[i];
if ((bl->mAttribute == aAttribute) ||
(bl->mAttribute == nsGkAtoms::_asterisk)) {
nsCOMPtr<Element> listenerEl
= do_QueryReferent(bl->mListener);
if (listenerEl) {
nsAutoString currentValue;
bool hasAttr = listenerEl->GetAttr(kNameSpaceID_None,
aAttribute,
currentValue);
// We need to update listener only if we're
// (1) removing an existing attribute,
// (2) adding a new attribute or
// (3) changing the value of an attribute.
bool needsAttrChange =
attrSet != hasAttr || !value.Equals(currentValue);
nsDelayedBroadcastUpdate delayedUpdate(aElement,
listenerEl,
aAttribute,
value,
attrSet,
needsAttrChange);
size_t index =
mDelayedAttrChangeBroadcasts.IndexOf(delayedUpdate,
0, nsDelayedBroadcastUpdate::Comparator());
if (index != mDelayedAttrChangeBroadcasts.NoIndex) {
if (mHandlingDelayedAttrChange) {
NS_WARNING("Broadcasting loop!");
continue;
}
mDelayedAttrChangeBroadcasts.RemoveElementAt(index);
}
mDelayedAttrChangeBroadcasts.AppendElement(delayedUpdate);
}
}
}
}
}
// checks for modifications in broadcasters
CheckBroadcasterHookup(aElement);
// See if there is anything we need to persist in the localstore.
//
// XXX Namespace handling broken :-(
@ -920,7 +575,6 @@ XULDocument::AddElementToDocumentPre(Element* aElement)
{
// Do a bunch of work that's necessary when an element gets added
// to the XUL Document.
nsresult rv;
// 1. Add the element to the id map, since it seems this can be
// called when creating elements from prototypes.
@ -931,11 +585,6 @@ XULDocument::AddElementToDocumentPre(Element* aElement)
AddToIdTable(aElement, id);
}
// 2. Check for a broadcaster hookup attribute, in which case
// we'll hook the node up as a listener on a broadcaster.
rv = CheckBroadcasterHookup(aElement);
if (NS_FAILED(rv)) return rv;
return NS_OK;
}
@ -1015,16 +664,6 @@ XULDocument::RemoveSubtreeFromDocument(nsIContent* aContent)
RemoveFromIdTable(aElement, id);
}
// Remove the element from our broadcaster map, since it is no longer
// in the document.
nsCOMPtr<Element> broadcaster, listener;
nsAutoString attribute, broadcasterID;
rv = FindBroadcaster(aElement, getter_AddRefs(listener),
broadcasterID, attribute, getter_AddRefs(broadcaster));
if (rv == NS_FINDBROADCASTER_FOUND) {
RemoveBroadcastListenerFor(*broadcaster, *listener, attribute);
}
return NS_OK;
}
@ -1801,66 +1440,10 @@ XULDocument::StyleSheetLoaded(StyleSheet* aSheet,
return NS_OK;
}
void
XULDocument::MaybeBroadcast()
{
// Only broadcast when not in an update and when safe to run scripts.
if (mUpdateNestLevel == 0 &&
(mDelayedAttrChangeBroadcasts.Length() ||
mDelayedBroadcasters.Length())) {
if (!nsContentUtils::IsSafeToRunScript()) {
if (!mInDestructor) {
nsContentUtils::AddScriptRunner(
NewRunnableMethod("dom::XULDocument::MaybeBroadcast",
this,
&XULDocument::MaybeBroadcast));
}
return;
}
if (!mHandlingDelayedAttrChange) {
mHandlingDelayedAttrChange = true;
for (uint32_t i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) {
nsAtom* attrName = mDelayedAttrChangeBroadcasts[i].mAttrName;
if (mDelayedAttrChangeBroadcasts[i].mNeedsAttrChange) {
nsCOMPtr<Element> listener = mDelayedAttrChangeBroadcasts[i].mListener;
const nsString& value = mDelayedAttrChangeBroadcasts[i].mAttr;
if (mDelayedAttrChangeBroadcasts[i].mSetAttr) {
listener->SetAttr(kNameSpaceID_None, attrName, value,
true);
} else {
listener->UnsetAttr(kNameSpaceID_None, attrName,
true);
}
}
ExecuteOnBroadcastHandlerFor(mDelayedAttrChangeBroadcasts[i].mBroadcaster,
mDelayedAttrChangeBroadcasts[i].mListener,
attrName);
}
mDelayedAttrChangeBroadcasts.Clear();
mHandlingDelayedAttrChange = false;
}
uint32_t length = mDelayedBroadcasters.Length();
if (length) {
bool oldValue = mHandlingDelayedBroadcasters;
mHandlingDelayedBroadcasters = true;
nsTArray<nsDelayedBroadcastUpdate> delayedBroadcasters;
mDelayedBroadcasters.SwapElements(delayedBroadcasters);
for (uint32_t i = 0; i < length; ++i) {
SynchronizeBroadcastListener(delayedBroadcasters[i].mBroadcaster,
delayedBroadcasters[i].mListener,
delayedBroadcasters[i].mAttr);
}
mHandlingDelayedBroadcasters = oldValue;
}
}
}
void
XULDocument::EndUpdate()
{
XMLDocument::EndUpdate();
MaybeBroadcast();
}
nsresult
@ -2248,141 +1831,6 @@ XULDocument::AddAttributes(nsXULPrototypeElement* aPrototype,
return NS_OK;
}
//----------------------------------------------------------------------
nsresult
XULDocument::FindBroadcaster(Element* aElement,
Element** aListener,
nsString& aBroadcasterID,
nsString& aAttribute,
Element** aBroadcaster)
{
mozilla::dom::NodeInfo *ni = aElement->NodeInfo();
*aListener = nullptr;
*aBroadcaster = nullptr;
if (ni->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) {
// It's an <observes> element, which means that the actual
// listener is the _parent_ node. This element should have an
// 'element' attribute that specifies the ID of the
// broadcaster element, and an 'attribute' element, which
// specifies the name of the attribute to observe.
nsIContent* parent = aElement->GetParent();
if (!parent) {
// <observes> is the root element
return NS_FINDBROADCASTER_NOT_FOUND;
}
*aListener = Element::FromNode(parent);
NS_IF_ADDREF(*aListener);
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, aBroadcasterID);
if (aBroadcasterID.IsEmpty()) {
return NS_FINDBROADCASTER_NOT_FOUND;
}
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, aAttribute);
}
else {
// It's a generic element, which means that we'll use the
// value of the 'observes' attribute to determine the ID of
// the broadcaster element, and we'll watch _all_ of its
// values.
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, aBroadcasterID);
// Bail if there's no aBroadcasterID
if (aBroadcasterID.IsEmpty()) {
// Try the command attribute next.
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::command, aBroadcasterID);
if (!aBroadcasterID.IsEmpty()) {
// We've got something in the command attribute. We
// only treat this as a normal broadcaster if we are
// not a menuitem or a key.
if (ni->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL) ||
ni->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) {
return NS_FINDBROADCASTER_NOT_FOUND;
}
}
else {
return NS_FINDBROADCASTER_NOT_FOUND;
}
}
*aListener = aElement;
NS_ADDREF(*aListener);
aAttribute.Assign('*');
}
// Make sure we got a valid listener.
NS_ENSURE_TRUE(*aListener, NS_ERROR_UNEXPECTED);
// Try to find the broadcaster element in the document.
*aBroadcaster = GetElementById(aBroadcasterID);
// The broadcaster element is missing.
if (! *aBroadcaster) {
return NS_FINDBROADCASTER_NOT_FOUND;
}
NS_ADDREF(*aBroadcaster);
return NS_FINDBROADCASTER_FOUND;
}
nsresult
XULDocument::CheckBroadcasterHookup(Element* aElement)
{
// Resolve a broadcaster hookup. Look at the element that we're
// trying to resolve: it could be an '<observes>' element, or just
// a vanilla element with an 'observes' attribute on it.
nsresult rv;
nsCOMPtr<Element> listener;
nsAutoString broadcasterID;
nsAutoString attribute;
nsCOMPtr<Element> broadcaster;
rv = FindBroadcaster(aElement, getter_AddRefs(listener),
broadcasterID, attribute, getter_AddRefs(broadcaster));
switch (rv) {
case NS_FINDBROADCASTER_NOT_FOUND:
return NS_OK;
case NS_FINDBROADCASTER_FOUND:
break;
default:
return rv;
}
NS_ENSURE_ARG(broadcaster && listener);
ErrorResult domRv;
AddBroadcastListenerFor(*broadcaster, *listener, attribute, domRv);
if (domRv.Failed()) {
return domRv.StealNSResult();
}
// Tell the world we succeeded
if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
nsCOMPtr<nsIContent> content = listener;
NS_ASSERTION(content != nullptr, "not an nsIContent");
if (!content) {
return rv;
}
nsAutoCString attributeC,broadcasteridC;
LossyCopyUTF16toASCII(attribute, attributeC);
LossyCopyUTF16toASCII(broadcasterID, broadcasteridC);
MOZ_LOG(gXULLog, LogLevel::Debug,
("xul: broadcaster hookup <%s attribute='%s'> to %s",
nsAtomCString(content->NodeInfo()->NameAtom()).get(),
attributeC.get(),
broadcasteridC.get()));
}
return NS_OK;
}
//----------------------------------------------------------------------
//
// CachedChromeStreamListener

View File

@ -128,9 +128,6 @@ public:
void TraceProtos(JSTracer* aTrc);
void RemoveBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
const nsAString& aAttr);
protected:
virtual ~XULDocument();
@ -164,14 +161,6 @@ protected:
nsresult
AddElementToDocumentPost(Element* aElement);
void AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
const nsAString& aAttr, ErrorResult& aRv);
nsresult
ExecuteOnBroadcastHandlerFor(Element* aBroadcaster,
Element* aListener,
nsAtom* aAttr);
static void DirectionChanged(const char* aPrefName, XULDocument* aData);
// pseudo constants
@ -292,25 +281,6 @@ protected:
protected:
// The out params of FindBroadcaster only have values that make sense when
// the method returns NS_FINDBROADCASTER_FOUND. In all other cases, the
// values of the out params should not be relied on (though *aListener and
// *aBroadcaster do need to be released if non-null, of course).
nsresult
FindBroadcaster(Element* aElement,
Element** aListener,
nsString& aBroadcasterID,
nsString& aAttribute,
Element** aBroadcaster);
nsresult
CheckBroadcasterHookup(Element* aElement);
void
SynchronizeBroadcastListener(Element *aBroadcaster,
Element *aListener,
const nsAString &aAttr);
/**
* The current prototype that we are walking to construct the
* content model.
@ -382,60 +352,8 @@ protected:
friend class CachedChromeStreamListener;
/**
* A map from a broadcaster element to a list of listener elements.
*/
PLDHashTable* mBroadcasterMap;
bool mInitialLayoutComplete;
class nsDelayedBroadcastUpdate
{
public:
nsDelayedBroadcastUpdate(Element* aBroadcaster,
Element* aListener,
const nsAString &aAttr)
: mBroadcaster(aBroadcaster), mListener(aListener), mAttr(aAttr),
mSetAttr(false), mNeedsAttrChange(false) {}
nsDelayedBroadcastUpdate(Element* aBroadcaster,
Element* aListener,
nsAtom* aAttrName,
const nsAString &aAttr,
bool aSetAttr,
bool aNeedsAttrChange)
: mBroadcaster(aBroadcaster), mListener(aListener), mAttr(aAttr),
mAttrName(aAttrName), mSetAttr(aSetAttr),
mNeedsAttrChange(aNeedsAttrChange) {}
nsDelayedBroadcastUpdate(const nsDelayedBroadcastUpdate& aOther)
: mBroadcaster(aOther.mBroadcaster), mListener(aOther.mListener),
mAttr(aOther.mAttr), mAttrName(aOther.mAttrName),
mSetAttr(aOther.mSetAttr), mNeedsAttrChange(aOther.mNeedsAttrChange) {}
nsCOMPtr<Element> mBroadcaster;
nsCOMPtr<Element> mListener;
// Note if mAttrName isn't used, this is the name of the attr, otherwise
// this is the value of the attribute.
nsString mAttr;
RefPtr<nsAtom> mAttrName;
bool mSetAttr;
bool mNeedsAttrChange;
class Comparator {
public:
static bool Equals(const nsDelayedBroadcastUpdate& a, const nsDelayedBroadcastUpdate& b) {
return a.mBroadcaster == b.mBroadcaster && a.mListener == b.mListener && a.mAttrName == b.mAttrName;
}
};
};
nsTArray<nsDelayedBroadcastUpdate> mDelayedBroadcasters;
nsTArray<nsDelayedBroadcastUpdate> mDelayedAttrChangeBroadcasts;
bool mHandlingDelayedAttrChange;
bool mHandlingDelayedBroadcasters;
void MaybeBroadcast();
private:
// helpers

View File

@ -22,6 +22,7 @@ if CONFIG['MOZ_XUL']:
]
EXPORTS.mozilla.dom += [
'XULBroadcastManager.h',
'XULFrameElement.h',
'XULMenuElement.h',
'XULPopupElement.h',
@ -39,6 +40,7 @@ if CONFIG['MOZ_XUL']:
'nsXULPrototypeCache.cpp',
'nsXULPrototypeDocument.cpp',
'nsXULSortService.cpp',
'XULBroadcastManager.cpp',
'XULDocument.cpp',
'XULFrameElement.cpp',
'XULMenuElement.cpp',

View File

@ -81,6 +81,7 @@
#include "mozilla/dom/XULElementBinding.h"
#include "mozilla/dom/BoxObject.h"
#include "mozilla/dom/XULBroadcastManager.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/dom/MutationEventBinding.h"
#include "mozilla/dom/XULCommandEvent.h"
@ -754,6 +755,14 @@ nsXULElement::BindToTree(nsIDocument* aDocument,
AddTooltipSupport();
}
if (doc && XULBroadcastManager::MayNeedListener(*this)) {
if (!doc->HasXULBroadcastManager()) {
doc->InitializeXULBroadcastManager();
}
XULBroadcastManager* broadcastManager = doc->GetXULBroadcastManager();
broadcastManager->AddListener(this);
}
return rv;
}
@ -768,6 +777,13 @@ nsXULElement::UnbindFromTree(bool aDeep, bool aNullParent)
RemoveTooltipSupport();
}
nsIDocument* doc = GetComposedDoc();
if (doc && doc->HasXULBroadcastManager() &&
XULBroadcastManager::MayNeedListener(*this)) {
RefPtr<XULBroadcastManager> broadcastManager = doc->GetXULBroadcastManager();
broadcastManager->RemoveListener(this);
}
// mControllers can own objects that are implemented
// in JavaScript (such as some implementations of
// nsIControllers. These objects prevent their global
@ -830,14 +846,18 @@ nsXULElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
(aName == nsGkAtoms::command || aName == nsGkAtoms::observes) &&
IsInUncomposedDoc()) {
// XXX sXBL/XBL2 issue! Owner or current document?
// XXX Why does this not also remove broadcast listeners if the
// "element" attribute was changed on an <observer>?
nsAutoString oldValue;
GetAttr(kNameSpaceID_None, nsGkAtoms::observes, oldValue);
if (oldValue.IsEmpty()) {
GetAttr(kNameSpaceID_None, nsGkAtoms::command, oldValue);
}
if (!oldValue.IsEmpty()) {
RemoveBroadcaster(oldValue);
nsIDocument* doc = GetUncomposedDoc();
if (!oldValue.IsEmpty() && doc->HasXULBroadcastManager()) {
RefPtr<XULBroadcastManager> broadcastManager = doc->GetXULBroadcastManager();
broadcastManager->RemoveListener(this);
}
} else if (aNamespaceID == kNameSpaceID_None &&
aValue &&
@ -965,6 +985,19 @@ nsXULElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
}
}
}
nsIDocument* doc = GetComposedDoc();
if (doc && doc->HasXULBroadcastManager()) {
RefPtr<XULBroadcastManager> broadcastManager = doc->GetXULBroadcastManager();
broadcastManager->AttributeChanged(this, aNamespaceID, aName);
}
if (doc && XULBroadcastManager::MayNeedListener(*this)) {
if (!doc->HasXULBroadcastManager()) {
doc->InitializeXULBroadcastManager();
}
XULBroadcastManager* broadcastManager = doc->GetXULBroadcastManager();
broadcastManager->AddListener(this);
}
// XXX need to check if they're changing an event handler: if
// so, then we need to unhook the old one. Or something.
}
@ -1012,19 +1045,6 @@ nsXULElement::ParseAttribute(int32_t aNamespaceID,
return true;
}
void
nsXULElement::RemoveBroadcaster(const nsAString & broadcasterId)
{
nsIDocument* doc = OwnerDoc();
if (!doc->IsXULDocument()) {
return;
}
if (Element* broadcaster = doc->GetElementById(broadcasterId)) {
doc->AsXULDocument()->RemoveBroadcastListenerFor(
*broadcaster, *this, NS_LITERAL_STRING("*"));
}
}
void
nsXULElement::DestroyContent()
{

View File

@ -693,8 +693,6 @@ protected:
void SetDrawsTitle(bool aState);
void UpdateBrightTitlebarForeground(nsIDocument* aDocument);
void RemoveBroadcaster(const nsAString & broadcasterId);
protected:
void AddTooltipSupport();
void RemoveTooltipSupport();

View File

@ -16,8 +16,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=445177
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=445177">Mozilla Bug 445177</a>
<hbox id="b1" value="foo"/>
<hbox id="o1" observes="b1"/>
<xul:hbox id="b1" value="foo"/>
<xul:hbox id="o1" observes="b1"/>
<pre id="test">
<script class="testbody" type="text/javascript">

View File

@ -1079,7 +1079,8 @@ public:
private:
// Helpers for ResolveFlexibleLengths():
void FreezeItemsEarly(bool aIsUsingFlexGrow);
void FreezeItemsEarly(bool aIsUsingFlexGrow,
ComputedFlexLineInfo* aLineInfo);
void FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation,
bool aIsFinalIteration);
@ -2503,7 +2504,8 @@ nsFlexContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
}
void
FlexLine::FreezeItemsEarly(bool aIsUsingFlexGrow)
FlexLine::FreezeItemsEarly(bool aIsUsingFlexGrow,
ComputedFlexLineInfo* aLineInfo)
{
// After we've established the type of flexing we're doing (growing vs.
// shrinking), and before we try to flex any items, we freeze items that
@ -2532,7 +2534,13 @@ FlexLine::FreezeItemsEarly(bool aIsUsingFlexGrow)
if (!item->IsFrozen()) {
numUnfrozenItemsToBeSeen--;
bool shouldFreeze = (0.0f == item->GetFlexFactor(aIsUsingFlexGrow));
if (!shouldFreeze) {
// NOTE: We skip the "could flex but base size out of range"
// early-freezing if flex devtools are active, so that we can let the
// first run of the main flex layout loop compute how much this item
// wants to flex. (This skipping shouldn't impact results, because
// any affected items will just immediately be caught & frozen as min/max
// violations in that first loop, and that'll trigger another loop.)
if (!shouldFreeze && !aLineInfo) {
if (aIsUsingFlexGrow) {
if (item->GetFlexBaseSize() > item->GetMainSize()) {
shouldFreeze = true;
@ -2618,13 +2626,26 @@ FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize,
{
MOZ_LOG(gFlexContainerLog, LogLevel::Debug, ("ResolveFlexibleLengths\n"));
// Before we start resolving sizes: if we have an aLineInfo structure to fill
// out, we inform it of each item's base size, and we initialize the "delta"
// for each item to 0. (And if the flex algorithm wants to grow or shrink the
// item, we'll update this delta further down.)
if (aLineInfo) {
uint32_t itemIndex = 0;
for (FlexItem* item = mItems.getFirst(); item; item = item->getNext(),
++itemIndex) {
aLineInfo->mItems[itemIndex].mMainBaseSize = item->GetFlexBaseSize();
aLineInfo->mItems[itemIndex].mMainDeltaSize = 0;
}
}
// Determine whether we're going to be growing or shrinking items.
const bool isUsingFlexGrow =
(mTotalOuterHypotheticalMainSize < aFlexContainerMainSize);
// Do an "early freeze" for flex items that obviously can't flex in the
// direction we've chosen:
FreezeItemsEarly(isUsingFlexGrow);
FreezeItemsEarly(isUsingFlexGrow, aLineInfo);
if ((mNumFrozenItems == mNumItems) && !aLineInfo) {
// All our items are frozen, so we have no flexible lengths to resolve,
@ -2663,21 +2684,6 @@ FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize,
availableFreeSpace -= item->GetMainSize();
}
// If we have an aLineInfo structure to fill out, and this is the
// first time through the loop, capture these sizes as mainBaseSizes.
// We only care about the first iteration, because additional
// iterations will only reset item base sizes to these values.
// We also set a 0 mainDeltaSize. This will be modified later if
// the item is stretched or shrunk.
if (aLineInfo && (iterationCounter == 0)) {
uint32_t itemIndex = 0;
for (FlexItem* item = mItems.getFirst(); item; item = item->getNext(),
++itemIndex) {
aLineInfo->mItems[itemIndex].mMainBaseSize = item->GetMainSize();
aLineInfo->mItems[itemIndex].mMainDeltaSize = 0;
}
}
MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
(" available free space = %d\n", availableFreeSpace));
@ -2852,37 +2858,44 @@ FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize,
uint32_t itemIndex = 0;
for (FlexItem* item = mItems.getFirst(); item; item = item->getNext(),
++itemIndex) {
// Calculate a deltaSize that represents how much the
// flex sizing algorithm "wants" to stretch or shrink this
// item during this pass through the algorithm. Later
// passes through the algorithm may overwrite this value.
// Also, this value may not reflect how much the size of
// the item is actually changed, since the size of the
// item will be clamped to min and max values later in
// this pass. That's intentional, since we want to report
// the value that the sizing algorithm tried to stretch
// or shrink the item.
nscoord deltaSize = item->GetMainSize() -
aLineInfo->mItems[itemIndex].mMainBaseSize;
if (!item->IsFrozen()) {
// Calculate a deltaSize that represents how much the flex sizing
// algorithm "wants" to stretch or shrink this item during this
// pass through the algorithm. Later passes through the algorithm
// may overwrite this, until this item is frozen. Note that this
// value may not reflect how much the size of the item is
// actually changed, since the size of the item will be clamped
// to min and max values later in this pass. That's intentional,
// since we want to report the value that the sizing algorithm
// tried to stretch or shrink the item.
nscoord deltaSize = item->GetMainSize() -
aLineInfo->mItems[itemIndex].mMainBaseSize;
aLineInfo->mItems[itemIndex].mMainDeltaSize = deltaSize;
// If any item on the line is growing, mark the aLineInfo
// structure; likewise if any item is shrinking. Items in
// a line can't be both growing and shrinking.
if (deltaSize > 0) {
MOZ_ASSERT(item->IsFrozen() || isUsingFlexGrow,
"Unfrozen items shouldn't grow without isUsingFlexGrow.");
MOZ_ASSERT(aLineInfo->mGrowthState !=
ComputedFlexLineInfo::GrowthState::SHRINKING);
aLineInfo->mGrowthState =
ComputedFlexLineInfo::GrowthState::GROWING;
} else if (deltaSize < 0) {
MOZ_ASSERT(item->IsFrozen() || !isUsingFlexGrow,
"Unfrozen items shouldn't shrink with isUsingFlexGrow.");
MOZ_ASSERT(aLineInfo->mGrowthState !=
ComputedFlexLineInfo::GrowthState::GROWING);
aLineInfo->mGrowthState =
ComputedFlexLineInfo::GrowthState::SHRINKING;
aLineInfo->mItems[itemIndex].mMainDeltaSize = deltaSize;
// If any (unfrozen) item on the line is growing, we mark the
// aLineInfo structure; likewise if any item is shrinking.
// (Note: a line can't contain a mix of items that are growing
// and shrinking. Also, the sign of any delta should match the
// type of flex factor we're using [grow vs shrink].)
if (deltaSize > 0) {
MOZ_ASSERT(isUsingFlexGrow,
"Unfrozen items can only grow if we're "
"distributing (positive) space with flex-grow");
MOZ_ASSERT(aLineInfo->mGrowthState !=
ComputedFlexLineInfo::GrowthState::SHRINKING,
"shouldn't flip flop from shrinking to growing");
aLineInfo->mGrowthState =
ComputedFlexLineInfo::GrowthState::GROWING;
} else if (deltaSize < 0) {
MOZ_ASSERT(!isUsingFlexGrow,
"Unfrozen items can only shrink if we're "
"distributing (negative) space with flex-shrink");
MOZ_ASSERT(aLineInfo->mGrowthState !=
ComputedFlexLineInfo::GrowthState::GROWING,
"shouldn't flip flop from growing to shrinking");
aLineInfo->mGrowthState =
ComputedFlexLineInfo::GrowthState::SHRINKING;
}
}
}
}

View File

@ -458,11 +458,11 @@ var transformTests = [
expected_uncomputed: 'rotate(-22.5deg) translate(5%, 5%) rotate(22.5deg)',
round_error_ok: true },
// test percent translation using matrix decomposition
{ start: 'rotate(45deg) rotate(-45deg)',
{ start: 'matrix(1, 0, 0, 1, 0, 0)',
end: 'rotate(90deg) translate(20%, 20%) rotate(-90deg)',
expected: 'matrix(1, 0, 0, 1, -2.5, 15)',
round_error_ok: true },
{ start: 'rotate(45deg) rotate(-45deg)',
{ start: 'matrix(1, 0, 0, 1, 0, 0)',
end: 'rotate(-90deg) translate(20%, 20%) rotate(90deg)',
expected: 'matrix(1, 0, 0, 1, 2.5, -15)',
round_error_ok: true },

View File

@ -5913,11 +5913,7 @@ pref("dom.event.default_to_passive_touch_listeners", true);
pref("browser.fastblock.timeout", 5000);
// The amount of time (ms) since navigation start after which
// we'll stop blocking tracker connections (0 = no limit).
#ifdef NIGHTLY_BUILD
pref("browser.fastblock.limit", 20000);
#else
pref("browser.fastblock.limit", 0);
#endif
// Enable clipboard readText() and writeText() by default
pref("dom.events.asyncClipboard", true);

View File

@ -1356,11 +1356,11 @@ fn is_matched_operation(first: &ComputedTransformOperation, second: &ComputedTra
&TransformOperation::RotateZ(..)) |
(&TransformOperation::Perspective(..),
&TransformOperation::Perspective(..)) => true,
// we animate scale and translate operations against each other
// Match functions that have the same primitive transform function
(a, b) if a.is_translate() && b.is_translate() => true,
(a, b) if a.is_scale() && b.is_scale() => true,
(a, b) if a.is_rotate() && b.is_rotate() => true,
// InterpolateMatrix and AccumulateMatrix are for mismatched transform.
// InterpolateMatrix and AccumulateMatrix are for mismatched transforms
_ => false
}
}
@ -2468,79 +2468,112 @@ impl Animate for ComputedTransform {
return Ok(Transform(result));
}
// https://drafts.csswg.org/css-transforms-1/#transform-transform-neutral-extend-animation
fn match_operations_if_possible<'a>(
this: &mut Cow<'a, Vec<ComputedTransformOperation>>,
other: &mut Cow<'a, Vec<ComputedTransformOperation>>,
) -> bool {
if !this.iter().zip(other.iter()).all(|(this, other)| is_matched_operation(this, other)) {
return false;
}
let this = Cow::Borrowed(&self.0);
let other = Cow::Borrowed(&other.0);
if this.len() == other.len() {
return true;
}
// Interpolate the common prefix
let mut result = this
.iter()
.zip(other.iter())
.take_while(|(this, other)| is_matched_operation(this, other))
.map(|(this, other)| this.animate(other, procedure))
.collect::<Result<Vec<_>, _>>()?;
let (shorter, longer) =
if this.len() < other.len() {
(this.to_mut(), other)
} else {
(other.to_mut(), this)
};
// Deal with the remainders
let this_remainder = if this.len() > result.len() {
Some(&this[result.len()..])
} else {
None
};
let other_remainder = if other.len() > result.len() {
Some(&other[result.len()..])
} else {
None
};
shorter.reserve(longer.len());
for op in longer.iter().skip(shorter.len()) {
shorter.push(op.to_animated_zero().unwrap());
}
// The resulting operations won't be matched regardless if the
// extended component is already InterpolateMatrix /
// AccumulateMatrix.
//
// Otherwise they should be matching operations all the time.
let already_mismatched = matches!(
longer[0],
TransformOperation::InterpolateMatrix { .. } |
TransformOperation::AccumulateMatrix { .. }
);
debug_assert_eq!(
!already_mismatched,
longer.iter().zip(shorter.iter()).all(|(this, other)| is_matched_operation(this, other)),
"ToAnimatedZero should generate matched operations"
);
!already_mismatched
}
let mut this = Cow::Borrowed(&self.0);
let mut other = Cow::Borrowed(&other.0);
if match_operations_if_possible(&mut this, &mut other) {
return Ok(Transform(
this.iter().zip(other.iter())
.map(|(this, other)| this.animate(other, procedure))
.collect::<Result<Vec<_>, _>>()?
));
}
match procedure {
Procedure::Add => Err(()),
Procedure::Interpolate { progress } => {
Ok(Transform(vec![TransformOperation::InterpolateMatrix {
from_list: Transform(this.into_owned()),
to_list: Transform(other.into_owned()),
progress: Percentage(progress as f32),
}]))
},
Procedure::Accumulate { count } => {
Ok(Transform(vec![TransformOperation::AccumulateMatrix {
from_list: Transform(this.into_owned()),
to_list: Transform(other.into_owned()),
count: cmp::min(count, i32::max_value() as u64) as i32,
}]))
match (this_remainder, other_remainder) {
// If there is a remainder from *both* lists we must have had mismatched functions.
// => Add the remainders to a suitable ___Matrix function.
(Some(this_remainder), Some(other_remainder)) => match procedure {
Procedure::Add => {
debug_assert!(false, "Should have already dealt with add by the point");
return Err(());
}
Procedure::Interpolate { progress } => {
result.push(TransformOperation::InterpolateMatrix {
from_list: Transform(this_remainder.to_vec()),
to_list: Transform(other_remainder.to_vec()),
progress: Percentage(progress as f32),
});
}
Procedure::Accumulate { count } => {
result.push(TransformOperation::AccumulateMatrix {
from_list: Transform(this_remainder.to_vec()),
to_list: Transform(other_remainder.to_vec()),
count: cmp::min(count, i32::max_value() as u64) as i32,
});
}
},
// If there is a remainder from just one list, then one list must be shorter but
// completely match the type of the corresponding functions in the longer list.
// => Interpolate the remainder with identity transforms.
(Some(remainder), None) | (None, Some(remainder)) => {
let fill_right = this_remainder.is_some();
result.append(
&mut remainder
.iter()
.map(|transform| {
let identity = transform.to_animated_zero().unwrap();
match transform {
// We can't interpolate/accumulate ___Matrix types directly with a
// matrix. Instead we need to wrap it in another ___Matrix type.
TransformOperation::AccumulateMatrix { .. }
| TransformOperation::InterpolateMatrix { .. } => {
let transform_list = Transform(vec![transform.clone()]);
let identity_list = Transform(vec![identity]);
let (from_list, to_list) = if fill_right {
(transform_list, identity_list)
} else {
(identity_list, transform_list)
};
match procedure {
Procedure::Add => Err(()),
Procedure::Interpolate { progress } => {
Ok(TransformOperation::InterpolateMatrix {
from_list,
to_list,
progress: Percentage(progress as f32),
})
}
Procedure::Accumulate { count } => {
Ok(TransformOperation::AccumulateMatrix {
from_list,
to_list,
count: cmp::min(count, i32::max_value() as u64)
as i32,
})
}
}
}
_ => {
let (lhs, rhs) = if fill_right {
(transform, &identity)
} else {
(&identity, transform)
};
lhs.animate(rhs, procedure)
}
}
})
.collect::<Result<Vec<_>, _>>()?,
);
}
(None, None) => {}
}
Ok(Transform(result))
}
}

View File

@ -23,6 +23,7 @@ config = {
"vcs_share_base": "/builds/hg-shared",
"version_path": "browser/config/version.txt",
"status_path": ".l10n_bumper_status",
"bump_configs": [{
"path": "mobile/locales/l10n-changesets.json",

View File

@ -22,6 +22,7 @@ config = {
"vcs_share_base": "/builds/hg-shared",
"version_path": "browser/config/version.txt",
"status_path": ".l10n_bumper_status",
"bump_configs": [{
"path": "mobile/locales/l10n-changesets.json",

View File

@ -22,6 +22,7 @@ config = {
"vcs_share_base": "/builds/hg-shared",
"version_path": "browser/config/version.txt",
"status_path": ".l10n_bumper_status",
"bump_configs": [{
"path": "mobile/locales/l10n-changesets.json",

View File

@ -334,6 +334,11 @@ class L10nBumper(VCSScript):
else:
self.fatal("Didn't complete successfully (hit max_retries)")
# touch status file for nagios
dirs = self.query_abs_dirs()
status_path = os.path.join(dirs['base_work_dir'], self.config['status_path'])
self._touch_file(status_path)
# __main__ {{{1
if __name__ == '__main__':

View File

@ -0,0 +1,120 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Transform list interpolation</title>
<link rel="help" href="https://drafts.csswg.org/css-transforms-1/#interpolation-of-transforms">
<meta name="assert" content="Interpolation of transform function lists is performed as follows">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/interpolation-testcommon.js"></script>
</head>
<body>
<script>
// none -> none
test_interpolation(
{
property: 'transform',
from: 'none',
to: 'none',
},
[{ at: 0.25, expect: 'none' }]
);
// none -> something
test_interpolation(
{
property: 'transform',
from: 'none',
to: 'translate(200px) rotate(720deg)',
},
[{ at: 0.25, expect: 'translate(50px) rotate(180deg)' }]
);
// something -> none
test_interpolation(
{
property: 'transform',
from: 'translate(200px) rotate(720deg)',
to: 'none',
},
[{ at: 0.25, expect: 'translate(150px) rotate(540deg)' }]
);
// Mismatched lengths (from is shorter), common part matches
test_interpolation(
{
property: 'transform',
from: 'translate(100px)',
to: 'translate(200px) rotate(720deg)',
},
[{ at: 0.25, expect: 'translate(125px) rotate(180deg)' }]
);
// Mismatched lengths (to is shorter), common part matches
test_interpolation(
{
property: 'transform',
from: 'translate(100px) rotate(720deg)',
to: 'translate(200px)',
},
[{ at: 0.25, expect: 'translate(125px) rotate(540deg)' }]
);
// Perfect match
test_interpolation(
{
property: 'transform',
from: 'scale(2) rotate(360deg) translate(100px) matrix(1, 0, 0, 1, 100, 0) skew(0deg)',
to: 'scale(3) rotate(1080deg) translate(200px) matrix(1, 0, 0, 1, 0, 200) skew(720deg)',
},
[
{
at: 0.25,
expect: 'scale(2.25) rotate(540deg) translate(125px) matrix(1, 0, 0, 1, 75, 50) skew(180deg)',
},
]
);
// Matches on primitives
test_interpolation(
{
property: 'transform',
from: 'translateX(100px) scaleX(3) translate(500px) scale(2)',
to: 'translateY(200px) scale(5) translateX(100px) scaleY(3)',
},
[{ at: 0.25, expect: 'translate(75px, 50px) scale(3.5, 2) translate(400px, 0px) scale(1.75, 2.25)' }]
);
// Common prefix
test_interpolation(
{
property: 'transform',
from: 'rotate(0deg) translate(100px)',
to: 'rotate(720deg) scale(2) translate(200px)',
},
[{ at: 0.25, expect: 'rotate(180deg) matrix(1.25, 0, 0, 1.25, 175, 0)' }]
);
// Complete mismatch (except length)
test_interpolation(
{
property: 'transform',
from: 'scale(2) rotate(0deg) translate(100px)',
to: 'rotate(720deg) scale(2) translate(200px)',
},
[{ at: 0.25, expect: 'matrix(2, 0, 0, 2, 250, 0)' }]
);
// Complete mismatch including length
test_interpolation(
{
property: 'transform',
from: 'scale(2) rotate(0deg)',
to: 'rotate(720deg) scale(2) translate(200px)',
},
[{ at: 0.25, expect: 'matrix(2, 0, 0, 2, 100, 0)' }]
);
</script>
</body>
</html>

View File

@ -1473,7 +1473,7 @@ const transformListType = {
Math.cos(Math.PI / 2),
100 * Math.cos(Math.PI / 2),
100 * Math.sin(Math.PI / 2) ] }]);
}, `${property}: rotate on roate and translate`);
}, `${property}: rotate on rotate and translate`);
test(t => {
const idlName = propertyToIDL(property);

View File

@ -14,12 +14,12 @@
#include "nsISupports.idl"
%{ C++
class nsDocShellLoadState;
class nsDocShellLoadInfo;
%}
interface mozIDOMWindowProxy;
interface nsIURI;
native nsDocShellLoadStatePtr(nsDocShellLoadState*);
native nsDocShellLoadInfoPtr(nsDocShellLoadInfo*);
/**
* The nsIWindowProvider interface exists so that the window watcher's default
@ -76,7 +76,7 @@ interface nsIWindowProvider : nsISupports
* the feature string to the window it returns in any way it sees fit.
* See the nsIWindowWatcher interface for details on feature strings.
*
* @param aLoadState Specify setup information of the load in the new window
* @param aLoadInfo Specify setup information of the load in the new window
*
* @param aWindowIsNew [out] Whether the window being returned was just
* created by the window provider implementation. This can be used by
@ -106,6 +106,6 @@ interface nsIWindowProvider : nsISupports
in AString aName,
in AUTF8String aFeatures,
in boolean aForceNoOpener,
in nsDocShellLoadStatePtr aLoadState,
in nsDocShellLoadInfoPtr aLoadInfo,
out boolean aWindowIsNew);
};

View File

@ -11,7 +11,7 @@
#include "nsISupports.idl"
%{ C++
class nsDocShellLoadState;
class nsDocShellLoadInfo;
%}
interface mozIDOMWindowProxy;
@ -21,7 +21,7 @@ interface nsIWebBrowserChrome;
interface nsIDocShellTreeItem;
interface nsIArray;
interface nsITabParent;
native nsDocShellLoadStatePtr(nsDocShellLoadState*);
native nsDocShellLoadInfoPtr(nsDocShellLoadInfo*);
[uuid(d162f9c4-19d5-4723-931f-f1e51bfa9f68)]
@ -66,8 +66,8 @@ interface nsPIWindowWatcher : nsISupports
looking for existing windows with the given name,
not setting an opener on the newly opened window,
and returning null from this method.
@param aLoadState if aNavigate is true, this allows the caller to pass in
an nsIDocShellLoadState to use for the navigation.
@param aLoadInfo if aNavigate is true, this allows the caller to pass in
an nsIDocShellLoadInfo to use for the navigation.
Callers can pass in null if they want the windowwatcher
to just construct a loadinfo itself. If aNavigate is
false, this argument is ignored.
@ -90,7 +90,7 @@ interface nsPIWindowWatcher : nsISupports
in nsISupports aArgs,
in boolean aIsPopupSpam,
in boolean aForceNoOpener,
in nsDocShellLoadStatePtr aLoadState);
in nsDocShellLoadInfoPtr aLoadInfo);
/**
* Opens a new window so that the window that aOpeningTab belongs to

View File

@ -22,7 +22,7 @@
#include "nsIBaseWindow.h"
#include "nsIBrowserDOMWindow.h"
#include "nsIDocShell.h"
#include "nsDocShellLoadState.h"
#include "nsDocShellLoadInfo.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIDocumentLoader.h"
@ -325,7 +325,7 @@ nsWindowWatcher::OpenWindow(mozIDOMWindowProxy* aParent,
/* navigate = */ true, argv,
/* aIsPopupSpam = */ false,
/* aForceNoOpener = */ false,
/* aLoadState = */ nullptr,
/* aLoadInfo = */ nullptr,
aResult);
}
@ -391,7 +391,7 @@ nsWindowWatcher::OpenWindow2(mozIDOMWindowProxy* aParent,
nsISupports* aArguments,
bool aIsPopupSpam,
bool aForceNoOpener,
nsDocShellLoadState* aLoadState,
nsDocShellLoadInfo* aLoadInfo,
mozIDOMWindowProxy** aResult)
{
nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
@ -412,7 +412,7 @@ nsWindowWatcher::OpenWindow2(mozIDOMWindowProxy* aParent,
return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
aCalledFromScript, dialog,
aNavigate, argv, aIsPopupSpam,
aForceNoOpener, aLoadState, aResult);
aForceNoOpener, aLoadInfo, aResult);
}
// This static function checks if the aDocShell uses an UserContextId equal to
@ -637,7 +637,7 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent,
nsIArray* aArgv,
bool aIsPopupSpam,
bool aForceNoOpener,
nsDocShellLoadState* aLoadState,
nsDocShellLoadInfo* aLoadInfo,
mozIDOMWindowProxy** aResult)
{
nsresult rv = NS_OK;
@ -821,7 +821,7 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent,
sizeSpec.PositionSpecified(),
sizeSpec.SizeSpecified(),
uriToLoad, name, features, aForceNoOpener,
aLoadState, &windowIsNew,
aLoadInfo, &windowIsNew,
getter_AddRefs(newWindow));
if (NS_SUCCEEDED(rv)) {
@ -1118,12 +1118,12 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent,
}
}
RefPtr<nsDocShellLoadState> loadState = aLoadState;
if (uriToLoad && aNavigate && !loadState) {
loadState = new nsDocShellLoadState();
RefPtr<nsDocShellLoadInfo> loadInfo = aLoadInfo;
if (uriToLoad && aNavigate && !loadInfo) {
loadInfo = new nsDocShellLoadInfo();
if (subjectPrincipal) {
loadState->SetTriggeringPrincipal(subjectPrincipal);
loadInfo->SetTriggeringPrincipal(subjectPrincipal);
}
/* use the URL from the *extant* document, if any. The usual accessor
@ -1138,8 +1138,8 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent,
}
if (doc) {
// Set the referrer
loadState->SetReferrer(doc->GetDocumentURI());
loadState->SetReferrerPolicy(doc->GetReferrerPolicy());
loadInfo->SetReferrer(doc->GetDocumentURI());
loadInfo->SetReferrerPolicy(doc->GetReferrerPolicy());
}
}
@ -1181,13 +1181,13 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent,
}
if (uriToLoad && aNavigate) {
loadState->SetURI(uriToLoad);
loadState->SetLoadFlags(windowIsNew ?
newDocShell->LoadURI(
uriToLoad,
loadInfo,
windowIsNew ?
static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD) :
static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_NONE));
loadState->SetFirstParty(true);
// Should this pay attention to errors returned by LoadURI?
newDocShell->LoadURI(loadState);
static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_NONE),
true);
}
// Copy the current session storage for the current domain. Don't perform the

View File

@ -88,7 +88,7 @@ protected:
nsIArray* aArgv,
bool aIsPopupSpam,
bool aForceNoOpener,
nsDocShellLoadState* aLoadState,
nsDocShellLoadInfo* aLoadInfo,
mozIDOMWindowProxy** aResult);
static nsresult URIfromURL(const char* aURL,

View File

@ -257,11 +257,14 @@ public:
// null-terminated.
bool NS_FASTCALL EqualsASCII(const char* aData) const;
// EqualsLiteral must ONLY be applied to an actual literal string, or
// a char array *constant* declared without an explicit size.
// Do not attempt to use it with a regular char* pointer, or with a
// non-constant char array variable. Use EqualsASCII for them.
// The template trick to acquire the array length at compile time without
// EqualsLiteral must ONLY be called with an actual literal string, or
// a char array *constant* declared without an explicit size and with an
// initializer that is a string literal or is otherwise null-terminated.
// Use EqualsASCII for other char array variables.
// (Although this method may happen to produce expected results for other
// char arrays that have bound one greater than the sequence of interest,
// such use is discouraged for reasons of readability and maintainability.)
// The template trick to acquire the array bound at compile time without
// using a macro is due to Corey Kosak, with much thanks.
template<int N>
inline bool EqualsLiteral(const char (&aStr)[N]) const
@ -279,11 +282,13 @@ public:
size_type aLen) const;
bool NS_FASTCALL LowerCaseEqualsASCII(const char* aData) const;
// LowerCaseEqualsLiteral must ONLY be applied to an actual
// literal string, or a char array *constant* declared without an
// explicit size. Do not attempt to use it with a regular char*
// pointer, or with a non-constant char array variable. Use
// LowerCaseEqualsASCII for them.
// LowerCaseEqualsLiteral must ONLY be called with an actual literal string,
// or a char array *constant* declared without an explicit size and with an
// initializer that is a string literal or is otherwise null-terminated.
// Use LowerCaseEqualsASCII for other char array variables.
// (Although this method may happen to produce expected results for other
// char arrays that have bound one greater than the sequence of interest,
// such use is discouraged for reasons of readability and maintainability.)
template<int N>
bool LowerCaseEqualsLiteral(const char (&aStr)[N]) const
{

View File

@ -473,10 +473,11 @@ public:
aFallible);
}
// AssignLiteral must ONLY be applied to an actual literal string, or
// a character array *constant* declared without an explicit size.
// Do not attempt to use it with a regular character pointer, or with a
// non-constant chararacter array variable. Use AssignASCII for those.
// AssignLiteral must ONLY be called with an actual literal string, or
// a character array *constant* of static storage duration declared
// without an explicit size and with an initializer that is a string
// literal or is otherwise null-terminated.
// Use Assign or AssignASCII for other character array variables.
//
// This method does not need a fallible version, because it uses the
// POD buffer of the literal as the string's buffer without allocating.
@ -489,10 +490,10 @@ public:
AssignLiteral(aStr, N - 1);
}
// AssignLiteral must ONLY be applied to an actual literal string, or
// a character array *constant* declared without an explicit size.
// Do not attempt to use it with a regular character pointer, or with a
// non-constant chararacter array variable. Use AssignASCII for those.
// AssignLiteral must ONLY be called with an actual literal string, or
// a char array *constant* declared without an explicit size and with an
// initializer that is a string literal or is otherwise null-terminated.
// Use AssignASCII for other char array variables.
//
// This method takes an 8-bit (ASCII-only!) string that is expanded
// into a 16-bit string at run time causing a run-time allocation.
@ -592,9 +593,11 @@ public:
size_type aLength,
const fallible_t&);
// ReplaceLiteral must ONLY be applied to an actual literal string.
// Do not attempt to use it with a regular char* pointer, or with a char
// array variable. Use Replace or ReplaceASCII for those.
// ReplaceLiteral must ONLY be called with an actual literal string, or
// a character array *constant* of static storage duration declared
// without an explicit size and with an initializer that is a string
// literal or is otherwise null-terminated.
// Use Replace or ReplaceASCII for other character array variables.
template<int N>
void ReplaceLiteral(index_type aCutStart, size_type aCutLength,
const char_type (&aStr)[N])
@ -640,9 +643,11 @@ public:
// Appends a literal string ("" literal in the 8-bit case and u"" literal
// in the 16-bit case) to the string.
//
// AppendLiteral must ONLY be applied to an actual literal string.
// Do not attempt to use it with a regular character pointer, or with a
// character array variable. Use Append or AppendASCII for those.
// AppendLiteral must ONLY be called with an actual literal string, or
// a character array *constant* of static storage duration declared
// without an explicit size and with an initializer that is a string
// literal or is otherwise null-terminated.
// Use Append or AppendASCII for other character array variables.
template<int N>
void AppendLiteral(const char_type (&aStr)[N])
{
@ -801,9 +806,11 @@ public:
Replace(aPos, 0, aTuple);
}
// InsertLiteral must ONLY be applied to an actual literal string.
// Do not attempt to use it with a regular char* pointer, or with a char
// array variable. Use Insert for those.
// InsertLiteral must ONLY be called with an actual literal string, or
// a character array *constant* of static storage duration declared
// without an explicit size and with an initializer that is a string
// literal or is otherwise null-terminated.
// Use Insert for other character array variables.
template<int N>
void InsertLiteral(const char_type (&aStr)[N], index_type aPos)
{

View File

@ -33,7 +33,7 @@
#include "nsWindowWatcher.h"
#include "mozilla/BrowserElementParent.h"
#include "mozilla/NullPrincipal.h"
#include "nsDocShellLoadState.h"
#include "nsDocShellLoadInfo.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIURI.h"
@ -766,7 +766,7 @@ nsContentTreeOwner::ProvideWindow(mozIDOMWindowProxy* aParent,
const nsAString& aName,
const nsACString& aFeatures,
bool aForceNoOpener,
nsDocShellLoadState* aLoadState,
nsDocShellLoadInfo* aLoadInfo,
bool* aWindowIsNew,
mozIDOMWindowProxy** aReturn)
{