Merge mozilla-central to autoland

This commit is contained in:
Iris Hsiao 2017-05-08 11:19:04 +08:00
commit 51711c7e82
125 changed files with 20960 additions and 18647 deletions

View File

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 1357107 - Removing a directory from extensions/ requires re-running configure to update MOZ_EXTENSIONS
Bug 1356927 - Mac builds in automation require a clobber for a change in which ranlib they use

View File

@ -1027,7 +1027,7 @@ pref("security.sandbox.windows.log.stackTraceDepth", 0);
// For information on what the level number means, see
// SetSecurityLevelForGPUProcess() in
// security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
pref("security.sandbox.gpu.level", 1);
pref("security.sandbox.gpu.level", 0);
#endif
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)

View File

@ -327,39 +327,39 @@ toolbarpaletteitem > toolbaritem[sdkstylewidget="true"][cui-areatype="toolbar"]
@media not all and (min-resolution: 1.1dppx) {
.webextension-browser-action {
list-style-image: var(--webextension-toolbar-image);
list-style-image: var(--webextension-toolbar-image, inherit);
}
.webextension-browser-action[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > .webextension-browser-action {
list-style-image: var(--webextension-menupanel-image);
list-style-image: var(--webextension-menupanel-image, inherit);
}
.webextension-page-action {
list-style-image: var(--webextension-urlbar-image);
list-style-image: var(--webextension-urlbar-image, inherit);
}
.webextension-menuitem {
list-style-image: var(--webextension-menuitem-image);
list-style-image: var(--webextension-menuitem-image, inherit);
}
}
@media (min-resolution: 1.1dppx) {
.webextension-browser-action {
list-style-image: var(--webextension-toolbar-image-2x);
list-style-image: var(--webextension-toolbar-image-2x, inherit);
}
.webextension-browser-action[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > .webextension-browser-action {
list-style-image: var(--webextension-menupanel-image-2x);
list-style-image: var(--webextension-menupanel-image-2x, inherit);
}
.webextension-page-action {
list-style-image: var(--webextension-urlbar-image-2x);
list-style-image: var(--webextension-urlbar-image-2x, inherit);
}
.webextension-menuitem {
list-style-image: var(--webextension-menuitem-image-2x);
list-style-image: var(--webextension-menuitem-image-2x, inherit);
}
}

View File

@ -3489,10 +3489,7 @@ var PrintPreviewListener = {
_lastRequestedPrintPreviewTab: null,
_createPPBrowser() {
if (!this._tabBeforePrintPreview) {
this._tabBeforePrintPreview = gBrowser.selectedTab;
}
let browser = this._tabBeforePrintPreview.linkedBrowser;
let browser = this.getSourceBrowser();
let preferredRemoteType = browser.remoteType;
return gBrowser.loadOneTab("about:printpreview", {
inBackground: true,
@ -3519,7 +3516,7 @@ var PrintPreviewListener = {
return gBrowser.getBrowserForTab(this._simplifiedPrintPreviewTab);
},
createSimplifiedBrowser() {
let browser = this._tabBeforePrintPreview.linkedBrowser;
let browser = this.getSourceBrowser();
this._simplifyPageTab = gBrowser.loadOneTab("about:printpreview", {
inBackground: true,
sameProcessAsFrameLoader: browser.frameLoader
@ -3527,8 +3524,10 @@ var PrintPreviewListener = {
return this.getSimplifiedSourceBrowser();
},
getSourceBrowser() {
return this._tabBeforePrintPreview ?
this._tabBeforePrintPreview.linkedBrowser : gBrowser.selectedBrowser;
if (!this._tabBeforePrintPreview) {
this._tabBeforePrintPreview = gBrowser.selectedTab;
}
return this._tabBeforePrintPreview.linkedBrowser;
},
getSimplifiedSourceBrowser() {
return this._simplifyPageTab ?

View File

@ -1,12 +1,6 @@
const kURL1 = "data:text/html,Should I stay or should I go?";
const kURL2 = "data:text/html,I shouldn't be here!";
add_task(function* setup() {
yield SpecialPowers.pushPrefEnv({
set: [["dom.ipc.processCount", 1]]
});
});
/**
* Verify that if we open a new tab and try to make it the selected tab while
* print preview is up, that doesn't happen.

View File

@ -19,6 +19,7 @@ Cu.import("resource://gre/modules/EventEmitter.jsm");
Cu.import("resource://gre/modules/Task.jsm");
var {
DefaultWeakMap,
IconDetails,
} = ExtensionUtils;
@ -57,6 +58,8 @@ this.browserAction = class extends ExtensionAPI {
let options = extension.manifest.browser_action;
this.iconData = new DefaultWeakMap(icons => this.getIconData(icons));
let widgetId = makeWidgetId(extension.id);
this.id = `${widgetId}-browser-action`;
this.viewId = `PanelUI-webext-${widgetId}-browser-action-view`;
@ -367,58 +370,74 @@ this.browserAction = class extends ExtensionAPI {
// in |tabData|.
updateButton(node, tabData) {
let title = tabData.title || this.extension.name;
node.setAttribute("tooltiptext", title);
node.setAttribute("label", title);
if (tabData.badgeText) {
node.setAttribute("badge", tabData.badgeText);
} else {
node.removeAttribute("badge");
}
node.ownerGlobal.requestAnimationFrame(() => {
node.setAttribute("tooltiptext", title);
node.setAttribute("label", title);
if (tabData.enabled) {
node.removeAttribute("disabled");
} else {
node.setAttribute("disabled", "true");
}
let badgeNode = node.ownerDocument.getAnonymousElementByAttribute(node,
"class", "toolbarbutton-badge");
if (badgeNode) {
let color = tabData.badgeBackgroundColor;
if (color) {
color = `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3] / 255})`;
if (tabData.badgeText) {
node.setAttribute("badge", tabData.badgeText);
} else {
node.removeAttribute("badge");
}
badgeNode.style.backgroundColor = color || "";
}
const LEGACY_CLASS = "toolbarbutton-legacy-addon";
node.classList.remove(LEGACY_CLASS);
if (tabData.enabled) {
node.removeAttribute("disabled");
} else {
node.setAttribute("disabled", "true");
}
let badgeNode = node.ownerDocument.getAnonymousElementByAttribute(node,
"class", "toolbarbutton-badge");
if (badgeNode) {
let color = tabData.badgeBackgroundColor;
if (color) {
color = `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3] / 255})`;
}
badgeNode.style.backgroundColor = color || "";
}
let {style, legacy} = this.iconData.get(tabData.icon);
const LEGACY_CLASS = "toolbarbutton-legacy-addon";
if (legacy) {
node.classList.add(LEGACY_CLASS);
} else {
node.classList.remove(LEGACY_CLASS);
}
node.setAttribute("style", style);
});
}
getIconData(icons) {
let baseSize = 16;
let {icon, size} = IconDetails.getPreferredIcon(tabData.icon, this.extension, baseSize);
let {icon, size} = IconDetails.getPreferredIcon(icons, this.extension, baseSize);
let legacy = false;
// If the best available icon size is not divisible by 16, check if we have
// an 18px icon to fall back to, and trim off the padding instead.
if (size % 16 && !icon.endsWith(".svg")) {
let result = IconDetails.getPreferredIcon(tabData.icon, this.extension, 18);
let result = IconDetails.getPreferredIcon(icons, this.extension, 18);
if (result.size % 18 == 0) {
baseSize = 18;
icon = result.icon;
node.classList.add(LEGACY_CLASS);
legacy = true;
}
}
let getIcon = size => IconDetails.escapeUrl(
IconDetails.getPreferredIcon(tabData.icon, this.extension, size).icon);
IconDetails.getPreferredIcon(icons, this.extension, size).icon);
node.setAttribute("style", `
let style = `
--webextension-menupanel-image: url("${getIcon(32)}");
--webextension-menupanel-image-2x: url("${getIcon(64)}");
--webextension-toolbar-image: url("${IconDetails.escapeUrl(icon)}");
--webextension-toolbar-image-2x: url("${getIcon(baseSize * 2)}");
`);
`;
return {style, legacy};
}
// Update the toolbar button for a given window.

View File

@ -8,6 +8,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelPopup",
Cu.import("resource://gre/modules/Task.jsm");
var {
DefaultWeakMap,
IconDetails,
} = ExtensionUtils;
@ -23,6 +24,8 @@ this.pageAction = class extends ExtensionAPI {
let {extension} = this;
let options = extension.manifest.page_action;
this.iconData = new DefaultWeakMap(icons => this.getIconData(icons));
this.id = makeWidgetId(extension.id) + "-page-action";
this.tabManager = extension.tabManager;
@ -104,31 +107,40 @@ this.pageAction = class extends ExtensionAPI {
return;
}
let button = this.getButton(window);
window.requestAnimationFrame(() => {
let button = this.getButton(window);
if (tabData.show) {
// Update the title and icon only if the button is visible.
if (tabData.show) {
// Update the title and icon only if the button is visible.
let title = tabData.title || this.extension.name;
button.setAttribute("tooltiptext", title);
button.setAttribute("aria-label", title);
let title = tabData.title || this.extension.name;
button.setAttribute("tooltiptext", title);
button.setAttribute("aria-label", title);
button.classList.add("webextension-page-action");
// These URLs should already be properly escaped, but make doubly sure CSS
// string escape characters are escaped here, since they could lead to a
// sandbox break.
let escape = str => str.replace(/[\\\s"]/g, encodeURIComponent);
let {style} = this.iconData.get(tabData.icon);
let getIcon = size => escape(IconDetails.getPreferredIcon(tabData.icon, this.extension, size).icon);
button.setAttribute("style", style);
}
button.setAttribute("style", `
--webextension-urlbar-image: url("${getIcon(16)}");
--webextension-urlbar-image-2x: url("${getIcon(32)}");
`);
button.hidden = !tabData.show;
});
}
button.classList.add("webextension-page-action");
}
getIconData(icons) {
// These URLs should already be properly escaped, but make doubly sure CSS
// string escape characters are escaped here, since they could lead to a
// sandbox break.
let escape = str => str.replace(/[\\\s"]/g, encodeURIComponent);
button.hidden = !tabData.show;
let getIcon = size => escape(IconDetails.getPreferredIcon(icons, this.extension, size).icon);
let style = `
--webextension-urlbar-image: url("${getIcon(16)}");
--webextension-urlbar-image-2x: url("${getIcon(32)}");
`;
return {style};
}
// Create an |image| node and add it to the |urlbar-icons|

View File

@ -124,7 +124,9 @@ function* runTests(options) {
}
let awaitFinish = new Promise(resolve => {
extension.onMessage("nextTest", (expecting, testsRemaining) => {
extension.onMessage("nextTest", async (expecting, testsRemaining) => {
await promiseAnimationFrame();
checkDetails(expecting);
if (testsRemaining) {

View File

@ -268,6 +268,8 @@ add_task(function* testDetailsObjects() {
extension.sendMessage("setIcon", test);
yield extension.awaitMessage("iconSet");
yield promiseAnimationFrame();
let browserActionButton = browserActionWidget.forWindow(window).node;
let pageActionImage = document.getElementById(pageActionId);

View File

@ -105,6 +105,8 @@ add_task(function* testDefaultDetails() {
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
let pageActionId = makeWidgetId(extension.id) + "-page-action";
yield promiseAnimationFrame();
let browserActionButton = document.getElementById(browserActionId);
let image = getListStyleImage(browserActionButton);

View File

@ -112,6 +112,7 @@ add_task(function* testBrowserActionDisabled() {
yield extension.startup();
yield extension.awaitMessage("browserAction-disabled");
yield promiseAnimationFrame();
const {GlobalManager, Management: {global: {browserActionFor}}} = Cu.import("resource://gre/modules/Extension.jsm", {});

View File

@ -18,6 +18,7 @@
* awaitExtensionPanel awaitPopupResize
* promiseContentDimensions alterContent
* promisePrefChangeObserved openContextMenuInFrame
* promiseAnimationFrame
*/
const {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm", {});
@ -83,6 +84,13 @@ function getListStyleImage(button) {
return match && match[1];
}
async function promiseAnimationFrame(win = window) {
await new Promise(resolve => win.requestAnimationFrame(resolve));
let {mainThread} = Services.tm;
return new Promise(resolve => mainThread.dispatch(resolve, mainThread.DISPATCH_NORMAL));
}
function promisePopupShown(popup) {
return new Promise(resolve => {
if (popup.state == "open") {
@ -217,6 +225,7 @@ var showBrowserAction = Task.async(function* (extension, win = window) {
});
var clickBrowserAction = Task.async(function* (extension, win = window) {
yield promiseAnimationFrame(win);
yield showBrowserAction(extension, win);
let widget = getBrowserActionWidget(extension).forWindow(win);
@ -279,8 +288,8 @@ async function closeContextMenu(contextMenu) {
await popupHiddenPromise;
}
function* openExtensionContextMenu(selector = "#img1") {
let contextMenu = yield openContextMenu(selector);
async function openExtensionContextMenu(selector = "#img1") {
let contextMenu = await openContextMenu(selector);
let topLevelMenu = contextMenu.getElementsByAttribute("ext-type", "top-level-menu");
// Return null if the extension only has one item and therefore no extension menu.
@ -291,23 +300,23 @@ function* openExtensionContextMenu(selector = "#img1") {
let extensionMenu = topLevelMenu[0].childNodes[0];
let popupShownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
EventUtils.synthesizeMouseAtCenter(extensionMenu, {});
yield popupShownPromise;
await popupShownPromise;
return extensionMenu;
}
function* closeExtensionContextMenu(itemToSelect, modifiers = {}) {
async function closeExtensionContextMenu(itemToSelect, modifiers = {}) {
let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
EventUtils.synthesizeMouseAtCenter(itemToSelect, modifiers);
yield popupHiddenPromise;
await popupHiddenPromise;
}
function* openChromeContextMenu(menuId, target, win = window) {
async function openChromeContextMenu(menuId, target, win = window) {
const node = win.document.querySelector(target);
const menu = win.document.getElementById(menuId);
const shown = BrowserTestUtils.waitForEvent(menu, "popupshown");
EventUtils.synthesizeMouseAtCenter(node, {type: "contextmenu"}, win);
yield shown;
await shown;
return menu;
}
@ -330,9 +339,10 @@ function closeChromeContextMenu(menuId, itemToSelect, win = window) {
return hidden;
}
function openActionContextMenu(extension, kind, win = window) {
async function openActionContextMenu(extension, kind, win = window) {
// See comment from clickPageAction below.
SetPageProxyState("valid");
await promiseAnimationFrame(win);
const id = `#${makeWidgetId(extension.id)}-${kind}-action`;
return openChromeContextMenu("toolbar-context-menu", id, win);
}
@ -354,7 +364,7 @@ function getPageActionPopup(extension, win = window) {
return win.document.getElementById(panelId);
}
function clickPageAction(extension, win = window) {
async function clickPageAction(extension, win = window) {
// This would normally be set automatically on navigation, and cleared
// when the user types a value into the URL bar, to show and hide page
// identity info and icons such as page action buttons.
@ -364,6 +374,8 @@ function clickPageAction(extension, win = window) {
/* globals SetPageProxyState */
SetPageProxyState("valid");
await promiseAnimationFrame(win);
let pageActionId = makeWidgetId(extension.id) + "-page-action";
let elem = win.document.getElementById(pageActionId);

View File

@ -3,7 +3,7 @@
"use strict";
/* exported runTests */
/* globals getListStyleImage */
/* globals getListStyleImage, promiseAnimationFrame */
function* runTests(options) {
function background(getTests) {
@ -108,11 +108,13 @@ function* runTests(options) {
let testNewWindows = 1;
let awaitFinish = new Promise(resolve => {
extension.onMessage("nextTest", (expecting, testsRemaining) => {
extension.onMessage("nextTest", async (expecting, testsRemaining) => {
if (!pageActionId) {
pageActionId = `${makeWidgetId(extension.id)}-page-action`;
}
await promiseAnimationFrame();
checkDetails(expecting);
if (testsRemaining) {

View File

@ -1,3 +1,8 @@
if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1}
MOZ_AUTOMATION_UPDATE_PACKAGING=1
fi
. "$topsrcdir/browser/config/mozconfigs/linux32/common-opt"
# Add-on signing is not required for DevEdition

View File

@ -7,6 +7,7 @@ ac_add_options --disable-jemalloc
ac_add_options --disable-crashreporter
ac_add_options --disable-elf-hack
ac_add_options --enable-debug
ac_add_options --disable-sandbox
MOZ_CODE_COVERAGE=1
export CFLAGS="--coverage"

View File

@ -1,3 +1,8 @@
if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1}
MOZ_AUTOMATION_UPDATE_PACKAGING=1
fi
. "$topsrcdir/browser/config/mozconfigs/linux64/common-opt"
# Add-on signing is not required for DevEdition

View File

@ -1,3 +1,8 @@
if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
MOZ_AUTOMATION_UPDATE_PACKAGING=1
fi
. "$topsrcdir/browser/config/mozconfigs/macosx64/common-opt"
# Add-on signing is not required for DevEdition

View File

@ -1,3 +1,8 @@
if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
MOZ_AUTOMATION_UPDATE_PACKAGING=1
fi
. "$topsrcdir/build/mozconfig.win-common"
. "$topsrcdir/browser/config/mozconfigs/win32/common-opt"

View File

@ -1,3 +1,8 @@
if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
MOZ_AUTOMATION_UPDATE_PACKAGING=1
fi
. "$topsrcdir/build/mozconfig.win-common"
. "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
. "$topsrcdir/browser/config/mozconfigs/win64/common-opt"

View File

@ -18,6 +18,7 @@ if test "$OS_ARCH" = "WINNT"; then
if ! test "$HAVE_64BIT_BUILD"; then
if test "$MOZ_UPDATE_CHANNEL" = "nightly" -o \
"$MOZ_UPDATE_CHANNEL" = "aurora" -o \
"$MOZ_UPDATE_CHANNEL" = "aurora-dev" -o \
"$MOZ_UPDATE_CHANNEL" = "beta" -o \
"$MOZ_UPDATE_CHANNEL" = "beta-dev" -o \
"$MOZ_UPDATE_CHANNEL" = "release" -o \
@ -47,12 +48,17 @@ MOZ_APP_VERSION_DISPLAY=$FIREFOX_VERSION_DISPLAY
MOZ_BRANDING_DIRECTORY=browser/branding/unofficial
MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/branding/official
MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
# This should usually be the same as the value MAR_CHANNEL_ID.
# ACCEPTED_MAR_CHANNEL_IDS should usually be the same as the value MAR_CHANNEL_ID.
# If more than one ID is needed, then you should use a comma separated list
# of values.
ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
# The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
MAR_CHANNEL_ID=firefox-mozilla-central
if test "$MOZ_UPDATE_CHANNEL" = "aurora"; then
ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-aurora
MAR_CHANNEL_ID=firefox-mozilla-aurora
else
ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
MAR_CHANNEL_ID=firefox-mozilla-central
fi
MOZ_PROFILE_MIGRATOR=1
MOZ_JSDOWNLOADS=1

View File

@ -104,7 +104,7 @@ FormAutofillParent.prototype = {
document.getElementById("passwordsGroup");
let insertBeforeNode = useOldOrganization ?
document.getElementById("locationBarGroup") :
null;
document.getElementById("passwordGrid");
parentNode.insertBefore(prefGroup, insertBeforeNode);
break;
}

View File

@ -84,6 +84,7 @@ FormAutofillPreferences.prototype = {
captionLabel.textContent = this.bundle.GetStringFromName("preferenceGroupTitle");
} else {
formAutofillGroup = document.createElementNS(XUL_NS, "vbox");
savedProfilesBtn.className = "accessory-button";
}
this.refs = {

View File

@ -16,17 +16,17 @@
</head>
<body>
<form>
<label id="first-name-container">
<label id="given-name-container">
<span>First Name</span>
<input id="first-name" type="text"/>
<input id="given-name" type="text"/>
</label>
<label id="middle-name-container">
<label id="additional-name-container">
<span>Middle Name</span>
<input id="middle-name" type="text"/>
<input id="additional-name" type="text"/>
</label>
<label id="last-name-container">
<label id="family-name-container">
<span>Last Name</span>
<input id="last-name" type="text"/>
<input id="family-name" type="text"/>
</label>
<label id="organization-container">
<span>Company</span>

View File

@ -139,6 +139,7 @@ ManageProfileDialog.prototype = {
// as option text. Possibly improve the algorithm in
// ProfileAutoCompleteResult.jsm and reuse it here.
const fieldOrder = [
"name",
"street-address", // Street address
"address-level2", // City/Town
"organization", // Company or organization name
@ -169,7 +170,7 @@ ManageProfileDialog.prototype = {
*/
openEditDialog(profile) {
window.openDialog(EDIT_PROFILE_URL, null,
"chrome,centerscreen,modal,width=600,height=370",
"chrome,centerscreen,modal,width=600,height=450",
profile);
},

View File

@ -52,15 +52,15 @@ button {
width: 15em;
}
#first-name-container,
#middle-name-container,
#given-name-container,
#additional-name-container,
#address-level1-container,
#postal-code-container,
#country-container {
flex: 0 1 50%;
}
#last-name-container,
#family-name-container,
#organization-container,
#street-address-container,
#address-level2-container,
@ -74,7 +74,7 @@ button {
justify-content: end;
}
#last-name,
#family-name,
#organization,
#address-level2,
#tel{
@ -86,10 +86,3 @@ button {
#email {
flex: 1 0 auto;
}
#first-name-container,
#middle-name-container,
#last-name-container {
/* Hide until we support names */
display: none;
}

View File

@ -29,6 +29,12 @@ add_task(function* test_saveProfile() {
resolve();
}, {once: true});
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1["given-name"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1["additional-name"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1["family-name"], {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1.organization, {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey(TEST_PROFILE_1["street-address"], {}, win);
@ -46,20 +52,17 @@ add_task(function* test_saveProfile() {
EventUtils.synthesizeKey(TEST_PROFILE_1.tel, {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
info("saving profile");
EventUtils.synthesizeKey("VK_RETURN", {}, win);
}, {once: true});
});
let profiles = yield getProfiles();
is(profiles.length, 1, "only one profile is in storage");
is(profiles[0].organization, TEST_PROFILE_1.organization, "match organization");
is(profiles[0]["street-address"], TEST_PROFILE_1["street-address"], "match street-address");
is(profiles[0]["address-level2"], TEST_PROFILE_1["address-level2"], "match address-level2");
is(profiles[0]["address-level1"], TEST_PROFILE_1["address-level1"], "match address-level1");
is(profiles[0]["postal-code"], TEST_PROFILE_1["postal-code"], "match postal-code");
is(profiles[0].country, TEST_PROFILE_1.country, "match country");
is(profiles[0].email, TEST_PROFILE_1.email, "match email");
is(profiles[0].tel, TEST_PROFILE_1.tel, "match tel");
is(Object.keys(TEST_PROFILE_1).length, 11, "Sanity check number of properties");
for (let [fieldName, fieldValue] of Object.entries(TEST_PROFILE_1)) {
is(profiles[0][fieldName], fieldValue, "check " + fieldName);
}
});
add_task(function* test_editProfile() {
@ -79,7 +82,7 @@ add_task(function* test_editProfile() {
profiles = yield getProfiles();
is(profiles.length, 1, "only one profile is in storage");
is(profiles[0].organization, TEST_PROFILE_1.organization + "test", "organization changed");
is(profiles[0]["given-name"], TEST_PROFILE_1["given-name"] + "test", "given-name changed");
yield removeProfiles([profiles[0].guid]);
profiles = yield getProfiles();

View File

@ -8,6 +8,9 @@ const MANAGE_PROFILES_DIALOG_URL = "chrome://formautofill/content/manageProfiles
const EDIT_PROFILE_DIALOG_URL = "chrome://formautofill/content/editProfile.xhtml";
const TEST_PROFILE_1 = {
"given-name": "John",
"additional-name": "R.",
"family-name": "Smith",
organization: "World Wide Web Consortium",
"street-address": "32 Vassar Street\nMIT Room 32-G524",
"address-level2": "Cambridge",

View File

@ -993,13 +993,15 @@ cargo_linker_env_var := CARGO_TARGET_$(RUST_TARGET_ENV_NAME)_LINKER
# have to pass in any special linker options on Windows.
ifneq (WINNT,$(OS_ARCH))
# Defining all of this for ASan builds results in crashes while running
# Defining all of this for ASan/TSan builds results in crashes while running
# some crates's build scripts (!), so disable it for now.
ifndef MOZ_ASAN
ifndef MOZ_TSAN
target_cargo_env_vars := \
MOZ_CARGO_WRAP_LDFLAGS="$(LDFLAGS)" \
MOZ_CARGO_WRAP_LD="$(CC)" \
$(cargo_linker_env_var)=$(topsrcdir)/build/cargo-linker
endif # MOZ_TSAN
endif # MOZ_ASAN
endif # ifneq WINNT

View File

@ -47,8 +47,9 @@ const { addDebuggerToGlobal } = Cu.import("resource://gre/modules/jsdebugger.jsm
const systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
.createInstance(Ci.nsIPrincipal);
var { loadSubScript } = Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader);
var { loadSubScript, loadSubScriptWithOptions } =
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader);
/**
* Initializes any test that needs to work with add-ons.

View File

@ -7,7 +7,7 @@ function run_test() {
do_test_pending();
let global = testGlobal("test");
loadSubScript(SOURCE_URL, global);
loadSubScriptWithOptions(SOURCE_URL, {target: global, ignoreCache: true});
Cu.forceGC(); Cu.forceGC(); Cu.forceGC();
DebuggerServer.registerModule("xpcshell-test/testactors");
@ -33,7 +33,7 @@ function run_test() {
packet = yield executeOnNextTickAndWaitForPause(function () {
reload(tabClient).then(function () {
loadSubScript(SOURCE_URL, global);
loadSubScriptWithOptions(SOURCE_URL, {target: global, ignoreCache: true});
});
}, client);
do_check_eq(packet.type, "paused");

View File

@ -7,7 +7,7 @@ function run_test() {
do_test_pending();
let global = createTestGlobal("test");
loadSubScript(SOURCE_URL, global);
loadSubScriptWithOptions(SOURCE_URL, {target: global, ignoreCache: true});
Cu.forceGC(); Cu.forceGC(); Cu.forceGC();
DebuggerServer.registerModule("xpcshell-test/testactors");
@ -33,7 +33,7 @@ function run_test() {
packet = yield executeOnNextTickAndWaitForPause(function () {
reload(tabClient).then(function () {
loadSubScript(SOURCE_URL, global);
loadSubScriptWithOptions(SOURCE_URL, {target: global, ignoreCache: true});
});
}, client);
do_check_eq(packet.type, "paused");

View File

@ -7,7 +7,7 @@ function run_test() {
do_test_pending();
let global = createTestGlobal("test");
loadSubScript(SOURCE_URL, global);
loadSubScriptWithOptions(SOURCE_URL, {target: global, ignoreCache: true});
Cu.forceGC(); Cu.forceGC(); Cu.forceGC();
DebuggerServer.registerModule("xpcshell-test/testactors");
@ -34,7 +34,7 @@ function run_test() {
packet = yield executeOnNextTickAndWaitForPause(function () {
reload(tabClient).then(function () {
loadSubScript(SOURCE_URL, global);
loadSubScriptWithOptions(SOURCE_URL, {target: global, ignoreCache: true});
});
}, client);
do_check_eq(packet.type, "paused");

View File

@ -9853,8 +9853,12 @@ nsDocShell::InternalLoad(nsIURI* aURI,
if (!aWindowTarget.IsEmpty()) {
// Locate the target DocShell.
nsCOMPtr<nsIDocShellTreeItem> targetItem;
// Only _self, _parent, and _top are supported in noopener case.
if (!(aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) ||
// Only _self, _parent, and _top are supported in noopener case. But we
// have to be careful to not apply that to the noreferrer case. See bug
// 1358469.
bool allowNamedTarget = !(aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) ||
(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
if (allowNamedTarget ||
aWindowTarget.LowerCaseEqualsLiteral("_self") ||
aWindowTarget.LowerCaseEqualsLiteral("_parent") ||
aWindowTarget.LowerCaseEqualsLiteral("_top")) {

View File

@ -115,7 +115,7 @@ public:
virtual size_t
SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
bool ElementHasHref() const;
virtual bool ElementHasHref() const;
// This is called by HTMLAnchorElement.
void TryDNSPrefetch();

View File

@ -219,6 +219,7 @@
#include "mozilla/dom/TabGroup.h"
#include "nsIWebNavigationInfo.h"
#include "nsPluginHost.h"
#include "mozilla/HangAnnotations.h"
#include "nsIBidiKeyboard.h"
@ -302,6 +303,9 @@ bool nsContentUtils::sRequestIdleCallbackEnabled = false;
int32_t nsContentUtils::sPrivacyMaxInnerWidth = 1000;
int32_t nsContentUtils::sPrivacyMaxInnerHeight = 1000;
nsContentUtils::UserInteractionObserver*
nsContentUtils::sUserInteractionObserver = nullptr;
uint32_t nsContentUtils::sHandlingInputTimeout = 1000;
uint32_t nsContentUtils::sCookiesLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
@ -493,6 +497,32 @@ private:
} // namespace
/**
* This class is used to determine whether or not the user is currently
* interacting with the browser. It listens to observer events to toggle the
* value of the sUserActive static.
*
* This class is an internal implementation detail.
* nsContentUtils::GetUserIsInteracting() should be used to access current
* user interaction status.
*/
class nsContentUtils::UserInteractionObserver final : public nsIObserver
, public HangMonitor::Annotator
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
void Init();
void Shutdown();
virtual void AnnotateHang(HangMonitor::HangAnnotations& aAnnotations) override;
static Atomic<bool> sUserActive;
private:
~UserInteractionObserver() {}
};
/* static */
TimeDuration
nsContentUtils::HandlingUserInputTimeout()
@ -661,6 +691,10 @@ nsContentUtils::Init()
}
uuidGenerator.forget(&sUUIDGenerator);
RefPtr<UserInteractionObserver> uio = new UserInteractionObserver();
uio->Init();
uio.forget(&sUserInteractionObserver);
sInitialized = true;
return NS_OK;
@ -2027,6 +2061,11 @@ nsContentUtils::Shutdown()
NS_IF_RELEASE(sSameOriginChecker);
if (sUserInteractionObserver) {
sUserInteractionObserver->Shutdown();
NS_RELEASE(sUserInteractionObserver);
}
HTMLInputElement::Shutdown();
nsMappedAttributes::Shutdown();
}
@ -10382,5 +10421,71 @@ nsContentUtils::GenerateTabId()
uint64_t tabBits = tabId & ((uint64_t(1) << kTabIdTabBits) - 1);
return (processBits << kTabIdTabBits) | tabBits;
}
/* static */ bool
nsContentUtils::GetUserIsInteracting()
{
return UserInteractionObserver::sUserActive;
}
static const char* kUserInteractionInactive = "user-interaction-inactive";
static const char* kUserInteractionActive = "user-interaction-active";
void
nsContentUtils::UserInteractionObserver::Init()
{
// Listen for the observer messages from EventStateManager which are telling
// us whether or not the user is interacting.
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->AddObserver(this, kUserInteractionInactive, false);
obs->AddObserver(this, kUserInteractionActive, false);
// Register ourselves as an annotator for the Background Hang Reporter, so
// that hang stacks are annotated with whether or not the user was
// interacting with the browser when the hang occurred.
HangMonitor::RegisterAnnotator(*this);
}
void
nsContentUtils::UserInteractionObserver::Shutdown()
{
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->RemoveObserver(this, kUserInteractionInactive);
obs->RemoveObserver(this, kUserInteractionActive);
}
HangMonitor::UnregisterAnnotator(*this);
}
/**
* NB: This function is always called by the HangMonitor thread.
* Plan accordingly
*/
void
nsContentUtils::UserInteractionObserver::AnnotateHang(HangMonitor::HangAnnotations& aAnnotations)
{
// NOTE: Only annotate the hang report if the user is known to be interacting.
if (sUserActive) {
aAnnotations.AddAnnotation(NS_LITERAL_STRING("UserInteracting"), true);
}
}
NS_IMETHODIMP
nsContentUtils::UserInteractionObserver::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
if (!strcmp(aTopic, kUserInteractionInactive)) {
sUserActive = false;
} else if (!strcmp(aTopic, kUserInteractionActive)) {
sUserActive = true;
} else {
NS_WARNING("Unexpected observer notification");
}
return NS_OK;
}
Atomic<bool> nsContentUtils::UserInteractionObserver::sUserActive(false);
NS_IMPL_ISUPPORTS(nsContentUtils::UserInteractionObserver, nsIObserver)

View File

@ -2937,6 +2937,13 @@ public:
static bool
SkipCursorMoveForSameValueSet() { return sSkipCursorMoveForSameValueSet; }
/**
* Determine whether or not the user is currently interacting with the web
* browser. This method is safe to call from off of the main thread.
*/
static bool
GetUserIsInteracting();
private:
static bool InitializeEventTable();
@ -3068,6 +3075,9 @@ private:
static int32_t sPrivacyMaxInnerWidth;
static int32_t sPrivacyMaxInnerHeight;
class UserInteractionObserver;
static UserInteractionObserver* sUserInteractionObserver;
static nsHtml5StringParser* sHTMLFragmentParser;
static nsIParser* sXMLFragmentParser;
static nsIFragmentContentSink* sXMLFragmentSink;

View File

@ -4195,6 +4195,7 @@ struct StateTableEntry
static constexpr StateTableEntry kManuallyManagedStates[] = {
{ "-moz-autofill", NS_EVENT_STATE_AUTOFILL },
{ "-moz-autofill-preview", NS_EVENT_STATE_AUTOFILL_PREVIEW },
{ nullptr, EventStates() },
};

View File

@ -32,6 +32,7 @@
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/IntentionalCrash.h"
#include "mozilla/Preferences.h"
#include "mozilla/ScriptPreloader.h"
#include "mozilla/Telemetry.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/MessagePort.h"
@ -1598,53 +1599,62 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
return;
}
nsCOMPtr<nsIChannel> channel;
NS_NewChannel(getter_AddRefs(channel),
uri,
nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_OTHER);
if (!channel) {
// Compile the script in the compilation scope instead of the current global
// to avoid keeping the current compartment alive.
AutoJSAPI jsapi;
if (!jsapi.Init(xpc::CompilationScope())) {
return;
}
JSContext* cx = jsapi.cx();
JS::Rooted<JSScript*> script(cx);
nsCOMPtr<nsIInputStream> input;
rv = channel->Open2(getter_AddRefs(input));
NS_ENSURE_SUCCESS_VOID(rv);
nsString dataString;
char16_t* dataStringBuf = nullptr;
size_t dataStringLength = 0;
uint64_t avail64 = 0;
if (input && NS_SUCCEEDED(input->Available(&avail64)) && avail64) {
if (avail64 > UINT32_MAX) {
return;
}
nsCString buffer;
uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)UINT32_MAX);
if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, avail))) {
return;
}
nsScriptLoader::ConvertToUTF16(channel, (uint8_t*)buffer.get(), avail,
EmptyString(), nullptr,
dataStringBuf, dataStringLength);
if (XRE_IsParentProcess()) {
script = ScriptPreloader::GetSingleton().GetCachedScript(cx, url);
}
JS::SourceBufferHolder srcBuf(dataStringBuf, dataStringLength,
JS::SourceBufferHolder::GiveOwnership);
if (!script) {
nsCOMPtr<nsIChannel> channel;
NS_NewChannel(getter_AddRefs(channel),
uri,
nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_OTHER);
if (dataStringBuf && dataStringLength > 0) {
// Compile the script in the compilation scope instead of the current global
// to avoid keeping the current compartment alive.
AutoJSAPI jsapi;
if (!jsapi.Init(xpc::CompilationScope())) {
if (!channel) {
return;
}
JSContext* cx = jsapi.cx();
nsCOMPtr<nsIInputStream> input;
rv = channel->Open2(getter_AddRefs(input));
NS_ENSURE_SUCCESS_VOID(rv);
nsString dataString;
char16_t* dataStringBuf = nullptr;
size_t dataStringLength = 0;
uint64_t avail64 = 0;
if (input && NS_SUCCEEDED(input->Available(&avail64)) && avail64) {
if (avail64 > UINT32_MAX) {
return;
}
nsCString buffer;
uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)UINT32_MAX);
if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, avail))) {
return;
}
nsScriptLoader::ConvertToUTF16(channel, (uint8_t*)buffer.get(), avail,
EmptyString(), nullptr,
dataStringBuf, dataStringLength);
}
JS::SourceBufferHolder srcBuf(dataStringBuf, dataStringLength,
JS::SourceBufferHolder::GiveOwnership);
if (!dataStringBuf || dataStringLength == 0) {
return;
}
JS::CompileOptions options(cx, JSVERSION_LATEST);
options.setFileAndLine(url.get(), 1);
options.setNoScriptRval(true);
JS::Rooted<JSScript*> script(cx);
if (aRunInGlobalScope) {
if (!JS::Compile(cx, options, srcBuf, &script)) {
@ -1654,18 +1664,21 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
} else if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) {
return;
}
}
MOZ_ASSERT(script);
aScriptp.set(script);
MOZ_ASSERT(script);
aScriptp.set(script);
nsAutoCString scheme;
uri->GetScheme(scheme);
// We don't cache data: scripts!
if (aShouldCache && !scheme.EqualsLiteral("data")) {
// Root the object also for caching.
auto* holder = new nsMessageManagerScriptHolder(cx, script, aRunInGlobalScope);
sCachedScripts->Put(aURL, holder);
nsAutoCString scheme;
uri->GetScheme(scheme);
// We don't cache data: scripts!
if (aShouldCache && !scheme.EqualsLiteral("data")) {
if (XRE_IsParentProcess()) {
ScriptPreloader::GetSingleton().NoteScript(url, url, script);
}
// Root the object also for caching.
auto* holder = new nsMessageManagerScriptHolder(cx, script, aRunInGlobalScope);
sCachedScripts->Put(aURL, holder);
}
}

View File

@ -189,7 +189,6 @@ static bool sNeedsFullGC = false;
static bool sNeedsGCAfterCC = false;
static bool sIncrementalCC = false;
static bool sDidPaintAfterPreviousICCSlice = false;
static bool sUserActive = false;
static int32_t sActiveIntersliceGCBudget = 0; // ms;
static nsScriptNameSpaceManager *gNameSpaceManager;
@ -350,12 +349,10 @@ nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic,
}
}
} else if (!nsCRT::strcmp(aTopic, "user-interaction-inactive")) {
sUserActive = false;
if (sCompactOnUserInactive) {
nsJSContext::PokeShrinkingGC();
}
} else if (!nsCRT::strcmp(aTopic, "user-interaction-active")) {
sUserActive = true;
nsJSContext::KillShrinkingGCTimer();
if (sIsCompactingOnUserInactive) {
JS::AbortIncrementalGC(sContext);
@ -1744,7 +1741,7 @@ InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure)
{
nsJSContext::KillInterSliceGCTimer();
bool e10sParent = XRE_IsParentProcess() && BrowserTabsRemoteAutostart();
int64_t budget = e10sParent && sUserActive && sActiveIntersliceGCBudget ?
int64_t budget = e10sParent && nsContentUtils::GetUserIsInteracting() && sActiveIntersliceGCBudget ?
sActiveIntersliceGCBudget : NS_INTERSLICE_GC_BUDGET;
nsJSContext::GarbageCollectNow(JS::gcreason::INTER_SLICE_GC,
nsJSContext::IncrementalGC,

View File

@ -290,6 +290,8 @@ private:
#define NS_EVENT_STATE_RTL NS_DEFINE_EVENT_STATE_MACRO(45)
// Element is filled by Autofill feature.
#define NS_EVENT_STATE_AUTOFILL NS_DEFINE_EVENT_STATE_MACRO(50)
// Element is filled with preview data by Autofill feature.
#define NS_EVENT_STATE_AUTOFILL_PREVIEW NS_DEFINE_EVENT_STATE_MACRO(51)
// Event state that is used for values that need to be parsed but do nothing.
#define NS_EVENT_STATE_IGNORE NS_DEFINE_EVENT_STATE_MACRO(63)
@ -308,7 +310,8 @@ private:
// document (e.g. in BindToTree and UnbindFromTree), if that is an
// appropriate thing to do for your state bit.
#define MANUALLY_MANAGED_STATES ( \
NS_EVENT_STATE_AUTOFILL \
NS_EVENT_STATE_AUTOFILL | \
NS_EVENT_STATE_AUTOFILL_PREVIEW \
)
// Event states that are managed externally to an element (by the

View File

@ -12,6 +12,7 @@ interface nsIDOMMutationEvent : nsISupports
const unsigned short MODIFICATION = 1;
const unsigned short ADDITION = 2;
const unsigned short REMOVAL = 3;
const unsigned short SMIL = 4;
readonly attribute nsIDOMNode relatedNode;
readonly attribute DOMString prevValue;

View File

@ -35,6 +35,7 @@
#include "mozilla/layers/IAPZCTreeManager.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/layers/InputAPZContext.h"
#include "mozilla/layers/PLayerTransactionChild.h"
#include "mozilla/layers/ShadowLayers.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/layout/RenderFrameChild.h"
@ -2713,11 +2714,9 @@ TabChild::InitRenderingState(const TextureFactoryIdentifier& aTextureFactoryIden
if (lf) {
nsTArray<LayersBackend> backends;
backends.AppendElement(mTextureFactoryIdentifier.mParentBackend);
bool success;
PLayerTransactionChild* shadowManager =
compositorChild->SendPLayerTransactionConstructor(backends,
aLayersId, &mTextureFactoryIdentifier, &success);
if (shadowManager && success) {
compositorChild->SendPLayerTransactionConstructor(backends, aLayersId);
if (shadowManager) {
lf->SetShadowManager(shadowManager);
lf->IdentifyTextureHost(mTextureFactoryIdentifier);
ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
@ -3100,20 +3099,20 @@ TabChild::ReinitRendering()
wr::AsPipelineId(mLayersId),
&mTextureFactoryIdentifier);
} else {
bool success;
bool success = false;
nsTArray<LayersBackend> ignored;
PLayerTransactionChild* shadowManager =
cb->SendPLayerTransactionConstructor(ignored, LayersId(), &mTextureFactoryIdentifier, &success);
PLayerTransactionChild* shadowManager = cb->SendPLayerTransactionConstructor(ignored, LayersId());
if (shadowManager &&
shadowManager->SendGetTextureFactoryIdentifier(&mTextureFactoryIdentifier) &&
mTextureFactoryIdentifier.mParentBackend != LayersBackend::LAYERS_NONE)
{
success = true;
}
if (!success) {
NS_WARNING("failed to re-allocate layer transaction");
return;
}
if (!shadowManager) {
NS_WARNING("failed to re-construct LayersChild");
return;
}
RefPtr<LayerManager> lm = mPuppetWidget->RecreateLayerManager(shadowManager);
ShadowLayerForwarder* lf = lm->AsShadowForwarder();
lf->IdentifyTextureHost(mTextureFactoryIdentifier);

View File

@ -80,6 +80,16 @@ SVGAElement::Href()
: mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);
}
//----------------------------------------------------------------------
// Link methods
bool
SVGAElement::ElementHasHref() const
{
return mStringAttributes[HREF].IsExplicitlySet() ||
mStringAttributes[XLINK_HREF].IsExplicitlySet();
}
//----------------------------------------------------------------------
// nsINode methods
@ -277,13 +287,9 @@ SVGAElement::IsLink(nsIURI** aURI) const
{ &nsGkAtoms::_empty, &nsGkAtoms::onRequest, nullptr };
// Optimization: check for href first for early return
bool useXLink = !HasAttr(kNameSpaceID_None, nsGkAtoms::href);
const nsAttrValue* href =
useXLink
? mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_XLink)
: mAttrsAndChildren.GetAttr(nsGkAtoms::href, kNameSpaceID_None);
bool useBareHref = mStringAttributes[HREF].IsExplicitlySet();
if (href &&
if ((useBareHref || mStringAttributes[XLINK_HREF].IsExplicitlySet()) &&
FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::type,
sTypeVals, eCaseMatters) !=
nsIContent::ATTR_VALUE_NO_MATCH &&
@ -296,7 +302,7 @@ SVGAElement::IsLink(nsIURI** aURI) const
nsCOMPtr<nsIURI> baseURI = GetBaseURI();
// Get absolute URI
nsAutoString str;
const uint8_t idx = useXLink ? XLINK_HREF : HREF;
const uint8_t idx = useBareHref ? HREF : XLINK_HREF;
mStringAttributes[idx].GetAnimValue(str, this);
nsContentUtils::NewURIWithDocumentCharset(aURI, str, OwnerDoc(), baseURI);
// must promise out param is non-null if we return true
@ -373,9 +379,7 @@ SVGAElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr,
if (aAttr == nsGkAtoms::href &&
(aNameSpaceID == kNameSpaceID_XLink ||
aNameSpaceID == kNameSpaceID_None)) {
bool hasHref = HasAttr(kNameSpaceID_None, nsGkAtoms::href) ||
HasAttr(kNameSpaceID_XLink, nsGkAtoms::href);
Link::ResetLinkState(!!aNotify, hasHref);
Link::ResetLinkState(!!aNotify, Link::ElementHasHref());
}
return rv;

View File

@ -68,6 +68,9 @@ public:
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify) override;
// Link
virtual bool ElementHasHref() const override;
// WebIDL
already_AddRefed<SVGAnimatedString> Href();
already_AddRefed<SVGAnimatedString> Target();

View File

@ -125,16 +125,10 @@ SVGFEImageElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
(aNamespaceID == kNameSpaceID_XLink ||
aNamespaceID == kNameSpaceID_None)) {
// If there isn't a frame we still need to load the image in case
// the frame is created later e.g. by attaching to a document.
// If there is a frame then it should deal with loading as the image
// url may be animated.
if (!GetPrimaryFrame()) {
if (aValue) {
LoadSVGImage(true, aNotify);
} else {
CancelImageRequests(aNotify);
}
if (aValue) {
LoadSVGImage(true, aNotify);
} else {
CancelImageRequests(aNotify);
}
}

View File

@ -156,16 +156,10 @@ SVGImageElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
(aNamespaceID == kNameSpaceID_None ||
aNamespaceID == kNameSpaceID_XLink)) {
// If there isn't a frame we still need to load the image in case
// the frame is created later e.g. by attaching to a document.
// If there is a frame then it should deal with loading as the image
// url may be animated
if (!GetPrimaryFrame()) {
if (aValue) {
LoadSVGImage(true, aNotify);
} else {
CancelImageRequests(aNotify);
}
if (aValue) {
LoadSVGImage(true, aNotify);
} else {
CancelImageRequests(aNotify);
}
}
return SVGImageElementBase::AfterSetAttr(aNamespaceID, aName,

View File

@ -1612,7 +1612,7 @@ nsSVGElement::DidAnimateLength(uint8_t aAttrEnum)
LengthAttributesInfo info = GetLengthInfo();
frame->AttributeChanged(kNameSpaceID_None,
*info.mLengthInfo[aAttrEnum].mName,
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}
@ -1709,7 +1709,7 @@ nsSVGElement::DidAnimateLengthList(uint8_t aAttrEnum)
LengthListAttributesInfo info = GetLengthListInfo();
frame->AttributeChanged(kNameSpaceID_None,
*info.mLengthListInfo[aAttrEnum].mName,
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}
@ -1797,7 +1797,7 @@ nsSVGElement::DidAnimateNumberList(uint8_t aAttrEnum)
frame->AttributeChanged(kNameSpaceID_None,
*info.mNumberListInfo[aAttrEnum].mName,
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}
@ -1858,7 +1858,7 @@ nsSVGElement::DidAnimatePointList()
if (frame) {
frame->AttributeChanged(kNameSpaceID_None,
GetPointListAttrName(),
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}
@ -1895,7 +1895,7 @@ nsSVGElement::DidAnimatePathSegList()
if (frame) {
frame->AttributeChanged(kNameSpaceID_None,
GetPathDataAttrName(),
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}
@ -1936,7 +1936,7 @@ nsSVGElement::DidAnimateNumber(uint8_t aAttrEnum)
NumberAttributesInfo info = GetNumberInfo();
frame->AttributeChanged(kNameSpaceID_None,
*info.mNumberInfo[aAttrEnum].mName,
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}
@ -2006,7 +2006,7 @@ nsSVGElement::DidAnimateNumberPair(uint8_t aAttrEnum)
NumberPairAttributesInfo info = GetNumberPairInfo();
frame->AttributeChanged(kNameSpaceID_None,
*info.mNumberPairInfo[aAttrEnum].mName,
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}
@ -2047,7 +2047,7 @@ nsSVGElement::DidAnimateInteger(uint8_t aAttrEnum)
IntegerAttributesInfo info = GetIntegerInfo();
frame->AttributeChanged(kNameSpaceID_None,
*info.mIntegerInfo[aAttrEnum].mName,
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}
@ -2118,7 +2118,7 @@ nsSVGElement::DidAnimateIntegerPair(uint8_t aAttrEnum)
IntegerPairAttributesInfo info = GetIntegerPairInfo();
frame->AttributeChanged(kNameSpaceID_None,
*info.mIntegerPairInfo[aAttrEnum].mName,
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}
@ -2166,7 +2166,7 @@ nsSVGElement::DidAnimateAngle(uint8_t aAttrEnum)
AngleAttributesInfo info = GetAngleInfo();
frame->AttributeChanged(kNameSpaceID_None,
*info.mAngleInfo[aAttrEnum].mName,
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}
@ -2205,7 +2205,7 @@ nsSVGElement::DidAnimateBoolean(uint8_t aAttrEnum)
BooleanAttributesInfo info = GetBooleanInfo();
frame->AttributeChanged(kNameSpaceID_None,
*info.mBooleanInfo[aAttrEnum].mName,
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}
@ -2244,7 +2244,7 @@ nsSVGElement::DidAnimateEnum(uint8_t aAttrEnum)
EnumAttributesInfo info = GetEnumInfo();
frame->AttributeChanged(kNameSpaceID_None,
*info.mEnumInfo[aAttrEnum].mName,
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}
@ -2281,7 +2281,7 @@ nsSVGElement::DidAnimateViewBox()
if (frame) {
frame->AttributeChanged(kNameSpaceID_None,
nsGkAtoms::viewBox,
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}
@ -2321,7 +2321,7 @@ nsSVGElement::DidAnimatePreserveAspectRatio()
if (frame) {
frame->AttributeChanged(kNameSpaceID_None,
nsGkAtoms::preserveAspectRatio,
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}
@ -2419,7 +2419,7 @@ nsSVGElement::DidAnimateString(uint8_t aAttrEnum)
StringAttributesInfo info = GetStringInfo();
frame->AttributeChanged(info.mStringInfo[aAttrEnum].mNamespaceID,
*info.mStringInfo[aAttrEnum].mName,
nsIDOMMutationEvent::MODIFICATION);
nsIDOMMutationEvent::SMIL);
}
}

View File

@ -0,0 +1,15 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
UNIFIED_SOURCES += [
'nsContentBlocker.cpp',
'nsModuleFactory.cpp',
]
FINAL_LIBRARY = 'xul'
with Files('**'):
BUG_COMPONENT = ('Core', 'DOM')

View File

@ -0,0 +1,384 @@
/* 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 "nsContentBlocker.h"
#include "nsIContent.h"
#include "nsIURI.h"
#include "nsIServiceManager.h"
#include "nsIDocShellTreeItem.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIDocShell.h"
#include "nsString.h"
#include "nsContentPolicyUtils.h"
#include "nsIObjectLoadingContent.h"
#include "mozilla/ArrayUtils.h"
#include "nsContentUtils.h"
// Possible behavior pref values
// Those map to the nsIPermissionManager values where possible
#define BEHAVIOR_ACCEPT nsIPermissionManager::ALLOW_ACTION
#define BEHAVIOR_REJECT nsIPermissionManager::DENY_ACTION
#define BEHAVIOR_NOFOREIGN 3
// From nsIContentPolicy
static const char *kTypeString[] = {
"other",
"script",
"image",
"stylesheet",
"object",
"document",
"subdocument",
"refresh",
"xbl",
"ping",
"xmlhttprequest",
"objectsubrequest",
"dtd",
"font",
"media",
"websocket",
"csp_report",
"xslt",
"beacon",
"fetch",
"image",
"manifest",
"", // TYPE_INTERNAL_SCRIPT
"", // TYPE_INTERNAL_WORKER
"", // TYPE_INTERNAL_SHARED_WORKER
"", // TYPE_INTERNAL_EMBED
"", // TYPE_INTERNAL_OBJECT
"", // TYPE_INTERNAL_FRAME
"", // TYPE_INTERNAL_IFRAME
"", // TYPE_INTERNAL_AUDIO
"", // TYPE_INTERNAL_VIDEO
"", // TYPE_INTERNAL_TRACK
"", // TYPE_INTERNAL_XMLHTTPREQUEST
"", // TYPE_INTERNAL_EVENTSOURCE
"", // TYPE_INTERNAL_SERVICE_WORKER
"", // TYPE_INTERNAL_SCRIPT_PRELOAD
"", // TYPE_INTERNAL_IMAGE
"", // TYPE_INTERNAL_IMAGE_PRELOAD
"", // TYPE_INTERNAL_STYLESHEET
"", // TYPE_INTERNAL_STYLESHEET_PRELOAD
"", // TYPE_INTERNAL_IMAGE_FAVICON
"", // TYPE_INTERNAL_WORKERS_IMPORT_SCRIPTS
};
#define NUMBER_OF_TYPES MOZ_ARRAY_LENGTH(kTypeString)
uint8_t nsContentBlocker::mBehaviorPref[NUMBER_OF_TYPES];
NS_IMPL_ISUPPORTS(nsContentBlocker,
nsIContentPolicy,
nsIObserver,
nsISupportsWeakReference)
nsContentBlocker::nsContentBlocker()
{
memset(mBehaviorPref, BEHAVIOR_ACCEPT, NUMBER_OF_TYPES);
}
nsresult
nsContentBlocker::Init()
{
nsresult rv;
mPermissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrefService> prefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrefBranch> prefBranch;
rv = prefService->GetBranch("permissions.default.", getter_AddRefs(prefBranch));
NS_ENSURE_SUCCESS(rv, rv);
// Migrate old image blocker pref
nsCOMPtr<nsIPrefBranch> oldPrefBranch;
oldPrefBranch = do_QueryInterface(prefService);
int32_t oldPref;
rv = oldPrefBranch->GetIntPref("network.image.imageBehavior", &oldPref);
if (NS_SUCCEEDED(rv) && oldPref) {
int32_t newPref;
switch (oldPref) {
default:
newPref = BEHAVIOR_ACCEPT;
break;
case 1:
newPref = BEHAVIOR_NOFOREIGN;
break;
case 2:
newPref = BEHAVIOR_REJECT;
break;
}
prefBranch->SetIntPref("image", newPref);
oldPrefBranch->ClearUserPref("network.image.imageBehavior");
}
// The branch is not a copy of the prefservice, but a new object, because
// it is a non-default branch. Adding obeservers to it will only work if
// we make sure that the object doesn't die. So, keep a reference to it.
mPrefBranchInternal = do_QueryInterface(prefBranch, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = mPrefBranchInternal->AddObserver("", this, true);
PrefChanged(prefBranch, nullptr);
return rv;
}
#undef LIMIT
#define LIMIT(x, low, high, default) ((x) >= (low) && (x) <= (high) ? (x) : (default))
void
nsContentBlocker::PrefChanged(nsIPrefBranch *aPrefBranch,
const char *aPref)
{
int32_t val;
#define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P))
for(uint32_t i = 0; i < NUMBER_OF_TYPES; ++i) {
if (*kTypeString[i] &&
PREF_CHANGED(kTypeString[i]) &&
NS_SUCCEEDED(aPrefBranch->GetIntPref(kTypeString[i], &val)))
mBehaviorPref[i] = LIMIT(val, 1, 3, 1);
}
}
// nsIContentPolicy Implementation
NS_IMETHODIMP
nsContentBlocker::ShouldLoad(uint32_t aContentType,
nsIURI *aContentLocation,
nsIURI *aRequestingLocation,
nsISupports *aRequestingContext,
const nsACString &aMimeGuess,
nsISupports *aExtra,
nsIPrincipal *aRequestPrincipal,
int16_t *aDecision)
{
MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType),
"We should only see external content policy types here.");
*aDecision = nsIContentPolicy::ACCEPT;
nsresult rv;
// Ony support NUMBER_OF_TYPES content types. that all there is at the
// moment, but you never know...
if (aContentType > NUMBER_OF_TYPES)
return NS_OK;
// we can't do anything without this
if (!aContentLocation)
return NS_OK;
// The final type of an object tag may mutate before it reaches
// shouldProcess, so we cannot make any sane blocking decisions here
if (aContentType == nsIContentPolicy::TYPE_OBJECT)
return NS_OK;
// we only want to check http, https, ftp
// for chrome:// and resources and others, no need to check.
nsAutoCString scheme;
aContentLocation->GetScheme(scheme);
if (!scheme.LowerCaseEqualsLiteral("ftp") &&
!scheme.LowerCaseEqualsLiteral("http") &&
!scheme.LowerCaseEqualsLiteral("https"))
return NS_OK;
bool shouldLoad, fromPrefs;
rv = TestPermission(aContentLocation, aRequestingLocation, aContentType,
&shouldLoad, &fromPrefs);
NS_ENSURE_SUCCESS(rv, rv);
if (!shouldLoad) {
if (fromPrefs) {
*aDecision = nsIContentPolicy::REJECT_TYPE;
} else {
*aDecision = nsIContentPolicy::REJECT_SERVER;
}
}
return NS_OK;
}
NS_IMETHODIMP
nsContentBlocker::ShouldProcess(uint32_t aContentType,
nsIURI *aContentLocation,
nsIURI *aRequestingLocation,
nsISupports *aRequestingContext,
const nsACString &aMimeGuess,
nsISupports *aExtra,
nsIPrincipal *aRequestPrincipal,
int16_t *aDecision)
{
MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType),
"We should only see external content policy types here.");
// For loads where aRequestingContext is chrome, we should just
// accept. Those are most likely toplevel loads in windows, and
// chrome generally knows what it's doing anyway.
nsCOMPtr<nsIDocShellTreeItem> item =
do_QueryInterface(NS_CP_GetDocShellFromContext(aRequestingContext));
if (item && item->ItemType() == nsIDocShellTreeItem::typeChrome) {
*aDecision = nsIContentPolicy::ACCEPT;
return NS_OK;
}
// For objects, we only check policy in shouldProcess, as the final type isn't
// determined until the channel is open -- We don't want to block images in
// object tags because plugins are disallowed.
// NOTE that this bypasses the aContentLocation checks in ShouldLoad - this is
// intentional, as aContentLocation may be null for plugins that load by type
// (e.g. java)
if (aContentType == nsIContentPolicy::TYPE_OBJECT) {
*aDecision = nsIContentPolicy::ACCEPT;
bool shouldLoad, fromPrefs;
nsresult rv = TestPermission(aContentLocation, aRequestingLocation,
aContentType, &shouldLoad, &fromPrefs);
NS_ENSURE_SUCCESS(rv, rv);
if (!shouldLoad) {
if (fromPrefs) {
*aDecision = nsIContentPolicy::REJECT_TYPE;
} else {
*aDecision = nsIContentPolicy::REJECT_SERVER;
}
}
return NS_OK;
}
// This isn't a load from chrome or an object tag - Just do a ShouldLoad()
// check -- we want the same answer here
return ShouldLoad(aContentType, aContentLocation, aRequestingLocation,
aRequestingContext, aMimeGuess, aExtra, aRequestPrincipal,
aDecision);
}
nsresult
nsContentBlocker::TestPermission(nsIURI *aCurrentURI,
nsIURI *aFirstURI,
int32_t aContentType,
bool *aPermission,
bool *aFromPrefs)
{
*aFromPrefs = false;
if (!*kTypeString[aContentType - 1]) {
// Disallow internal content policy types, they should not be used here.
*aPermission = false;
return NS_OK;
}
// This default will also get used if there is an unknown value in the
// permission list, or if the permission manager returns unknown values.
*aPermission = true;
// check the permission list first; if we find an entry, it overrides
// default prefs.
// Don't forget the aContentType ranges from 1..8, while the
// array is indexed 0..7
uint32_t permission;
nsresult rv = mPermissionManager->TestPermission(aCurrentURI,
kTypeString[aContentType - 1],
&permission);
NS_ENSURE_SUCCESS(rv, rv);
// If there is nothing on the list, use the default.
if (!permission) {
permission = mBehaviorPref[aContentType - 1];
*aFromPrefs = true;
}
// Use the fact that the nsIPermissionManager values map to
// the BEHAVIOR_* values above.
switch (permission) {
case BEHAVIOR_ACCEPT:
*aPermission = true;
break;
case BEHAVIOR_REJECT:
*aPermission = false;
break;
case BEHAVIOR_NOFOREIGN:
// Third party checking
// Need a requesting uri for third party checks to work.
if (!aFirstURI)
return NS_OK;
bool trustedSource = false;
rv = aFirstURI->SchemeIs("chrome", &trustedSource);
NS_ENSURE_SUCCESS(rv,rv);
if (!trustedSource) {
rv = aFirstURI->SchemeIs("resource", &trustedSource);
NS_ENSURE_SUCCESS(rv,rv);
}
if (trustedSource)
return NS_OK;
// compare tails of names checking to see if they have a common domain
// we do this by comparing the tails of both names where each tail
// includes at least one dot
// A more generic method somewhere would be nice
nsAutoCString currentHost;
rv = aCurrentURI->GetAsciiHost(currentHost);
NS_ENSURE_SUCCESS(rv, rv);
// Search for two dots, starting at the end.
// If there are no two dots found, ++dot will turn to zero,
// that will return the entire string.
int32_t dot = currentHost.RFindChar('.');
dot = currentHost.RFindChar('.', dot-1);
++dot;
// Get the domain, ie the last part of the host (www.domain.com -> domain.com)
// This will break on co.uk
const nsCSubstring &tail =
Substring(currentHost, dot, currentHost.Length() - dot);
nsAutoCString firstHost;
rv = aFirstURI->GetAsciiHost(firstHost);
NS_ENSURE_SUCCESS(rv, rv);
// If the tail is longer then the whole firstHost, it will never match
if (firstHost.Length() < tail.Length()) {
*aPermission = false;
return NS_OK;
}
// Get the last part of the firstUri with the same length as |tail|
const nsCSubstring &firstTail =
Substring(firstHost, firstHost.Length() - tail.Length(), tail.Length());
// Check that both tails are the same, and that just before the tail in
// |firstUri| there is a dot. That means both url are in the same domain
if ((firstHost.Length() > tail.Length() &&
firstHost.CharAt(firstHost.Length() - tail.Length() - 1) != '.') ||
!tail.Equals(firstTail)) {
*aPermission = false;
}
break;
}
return NS_OK;
}
NS_IMETHODIMP
nsContentBlocker::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData)
{
NS_ASSERTION(!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic),
"unexpected topic - we only deal with pref changes!");
if (mPrefBranchInternal)
PrefChanged(mPrefBranchInternal, NS_LossyConvertUTF16toASCII(aData).get());
return NS_OK;
}

View File

@ -0,0 +1,53 @@
/* 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 nsContentBlocker_h__
#define nsContentBlocker_h__
#include "nsIContentPolicy.h"
#include "nsIObserver.h"
#include "nsWeakReference.h"
#include "nsIPermissionManager.h"
#include "nsIPrefBranch.h"
#include "mozilla/Attributes.h"
class nsIPrefBranch;
////////////////////////////////////////////////////////////////////////////////
class nsContentBlocker final : public nsIContentPolicy,
public nsIObserver,
public nsSupportsWeakReference
{
public:
// nsISupports
NS_DECL_ISUPPORTS
NS_DECL_NSICONTENTPOLICY
NS_DECL_NSIOBSERVER
nsContentBlocker();
nsresult Init();
private:
~nsContentBlocker() {}
void PrefChanged(nsIPrefBranch *, const char *);
nsresult TestPermission(nsIURI *aCurrentURI,
nsIURI *aFirstURI,
int32_t aContentType,
bool *aPermission,
bool *aFromPrefs);
nsCOMPtr<nsIPermissionManager> mPermissionManager;
nsCOMPtr<nsIPrefBranch> mPrefBranchInternal;
static uint8_t mBehaviorPref[];
};
#define NS_CONTENTBLOCKER_CID \
{ 0x4ca6b67b, 0x5cc7, 0x4e71, \
{ 0xa9, 0x8a, 0x97, 0xaf, 0x1c, 0x13, 0x48, 0x62 } }
#define NS_CONTENTBLOCKER_CONTRACTID "@mozilla.org/permissions/contentblocker;1"
#endif /* nsContentBlocker_h__ */

View File

@ -0,0 +1,37 @@
/* 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 "mozilla/ModuleUtils.h"
#include "nsIServiceManager.h"
#include "nsContentBlocker.h"
#include "nsXPIDLString.h"
// Define the constructor function for the objects
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsContentBlocker, Init)
NS_DEFINE_NAMED_CID(NS_CONTENTBLOCKER_CID);
static const mozilla::Module::CIDEntry kPermissionsCIDs[] = {
{ &kNS_CONTENTBLOCKER_CID, false, nullptr, nsContentBlockerConstructor },
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kPermissionsContracts[] = {
{ NS_CONTENTBLOCKER_CONTRACTID, &kNS_CONTENTBLOCKER_CID },
{ nullptr }
};
static const mozilla::Module::CategoryEntry kPermissionsCategories[] = {
{ "content-policy", NS_CONTENTBLOCKER_CONTRACTID, NS_CONTENTBLOCKER_CONTRACTID },
{ nullptr }
};
static const mozilla::Module kPermissionsModule = {
mozilla::Module::kVersion,
kPermissionsCIDs,
kPermissionsContracts,
kPermissionsCategories
};
NSMODULE_DEFN(nsPermissionsModule) = &kPermissionsModule;

View File

@ -330,10 +330,7 @@ CompositorBridgeChild::CompositorIsInGPUProcess()
}
PLayerTransactionChild*
CompositorBridgeChild::AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints,
const uint64_t& aId,
TextureFactoryIdentifier*,
bool*)
CompositorBridgeChild::AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints, const uint64_t& aId)
{
LayerTransactionChild* c = new LayerTransactionChild(aId);
c->AddIPDLReference();

View File

@ -249,9 +249,7 @@ private:
virtual PLayerTransactionChild*
AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints,
const uint64_t& aId,
TextureFactoryIdentifier* aTextureFactoryIdentifier,
bool* aSuccess) override;
const uint64_t& aId) override;
virtual bool DeallocPLayerTransactionChild(PLayerTransactionChild *aChild) override;

View File

@ -1443,9 +1443,7 @@ CompositorBridgeParent::NewCompositor(const nsTArray<LayersBackend>& aBackendHin
PLayerTransactionParent*
CompositorBridgeParent::AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
const uint64_t& aId,
TextureFactoryIdentifier* aTextureFactoryIdentifier,
bool *aSuccess)
const uint64_t& aId)
{
MOZ_ASSERT(aId == 0);
@ -1453,16 +1451,13 @@ CompositorBridgeParent::AllocPLayerTransactionParent(const nsTArray<LayersBacken
if (!mLayerManager) {
NS_WARNING("Failed to initialise Compositor");
*aSuccess = false;
LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, 0);
p->AddIPDLReference();
return p;
}
mCompositionManager = new AsyncCompositionManager(this, mLayerManager);
*aSuccess = true;
*aTextureFactoryIdentifier = mLayerManager->GetTextureFactoryIdentifier();
LayerTransactionParent* p = new LayerTransactionParent(mLayerManager, this, 0);
p->AddIPDLReference();
return p;

View File

@ -511,9 +511,7 @@ protected:
virtual PLayerTransactionParent*
AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
const uint64_t& aId,
TextureFactoryIdentifier* aTextureFactoryIdentifier,
bool* aSuccess) override;
const uint64_t& aId) override;
virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) override;
virtual void ScheduleTask(already_AddRefed<CancelableRunnable>, int);

View File

@ -62,9 +62,7 @@ CrossProcessCompositorBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
PLayerTransactionParent*
CrossProcessCompositorBridgeParent::AllocPLayerTransactionParent(
const nsTArray<LayersBackend>&,
const uint64_t& aId,
TextureFactoryIdentifier* aTextureFactoryIdentifier,
bool *aSuccess)
const uint64_t& aId)
{
MOZ_ASSERT(aId != 0);
@ -85,8 +83,6 @@ CrossProcessCompositorBridgeParent::AllocPLayerTransactionParent(
if (state && state->mLayerManager) {
state->mCrossProcessParent = this;
HostLayerManager* lm = state->mLayerManager;
*aTextureFactoryIdentifier = lm->GetCompositor()->GetTextureFactoryIdentifier();
*aSuccess = true;
LayerTransactionParent* p = new LayerTransactionParent(lm, this, aId);
p->AddIPDLReference();
sIndirectLayerTrees[aId].mLayerTree = p;
@ -97,7 +93,6 @@ CrossProcessCompositorBridgeParent::AllocPLayerTransactionParent(
}
NS_WARNING("Created child without a matching parent?");
*aSuccess = false;
LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, aId);
p->AddIPDLReference();
return p;

View File

@ -92,9 +92,7 @@ public:
virtual PLayerTransactionParent*
AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aBackendHints,
const uint64_t& aId,
TextureFactoryIdentifier* aTextureFactoryIdentifier,
bool *aSuccess) override;
const uint64_t& aId) override;
virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) override;

View File

@ -1053,5 +1053,17 @@ LayerTransactionParent::RecvRecordPaintTimes(const PaintTiming& aTiming)
return IPC_OK();
}
mozilla::ipc::IPCResult
LayerTransactionParent::RecvGetTextureFactoryIdentifier(TextureFactoryIdentifier* aIdentifier)
{
if (!mLayerManager) {
// Default constructor sets mParentBackend to LAYERS_NONE.
return IPC_OK();
}
*aIdentifier = mLayerManager->GetTextureFactoryIdentifier();
return IPC_OK();
}
} // namespace layers
} // namespace mozilla

View File

@ -141,6 +141,7 @@ protected:
virtual mozilla::ipc::IPCResult RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
nsTArray<ScrollableLayerGuid>&& aTargets) override;
virtual mozilla::ipc::IPCResult RecvRecordPaintTimes(const PaintTiming& aTiming) override;
virtual mozilla::ipc::IPCResult RecvGetTextureFactoryIdentifier(TextureFactoryIdentifier* aIdentifier) override;
bool SetLayerAttributes(const OpSetLayerAttributes& aOp);

View File

@ -222,8 +222,7 @@ parent:
// layersBackendHints is an ordered list of preffered backends where
// layersBackendHints[0] is the best backend. If any hints are LayersBackend::LAYERS_NONE
// that hint is ignored.
sync PLayerTransaction(LayersBackend[] layersBackendHints, uint64_t id)
returns (TextureFactoryIdentifier textureFactoryIdentifier, bool success);
async PLayerTransaction(LayersBackend[] layersBackendHints, uint64_t id);
// Notify the compositor that a region of the screen has been invalidated.
async NotifyRegionInvalidated(nsIntRegion region);

View File

@ -18,6 +18,7 @@ using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
using class mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
using mozilla::layers::LayerHandle from "mozilla/layers/LayersTypes.h";
using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
@ -118,6 +119,9 @@ parent:
// Query a named property from the last frame
sync RequestProperty(nsString property) returns (float value);
// Return the TextureFactoryIdentifier for this compositor.
sync GetTextureFactoryIdentifier() returns (TextureFactoryIdentifier aIdentifier);
// Tell the compositor to notify APZ that a layer has been confirmed for an
// input event.
async SetConfirmedTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets);

View File

@ -850,6 +850,15 @@ ChooseFontSize(gfxFontconfigFontEntry* aEntry,
bestSize = size;
}
}
// If the font has bitmaps but wants to be scaled, then let it scale.
if (bestSize >= 0.0) {
FcBool scalable;
if (FcPatternGetBool(aEntry->GetPattern(),
FC_SCALABLE, 0, &scalable) == FcResultMatch &&
scalable) {
return requestedSize;
}
}
return bestSize;
}
@ -1012,6 +1021,12 @@ gfxFontconfigFontFamily::AddFontPattern(FcPattern* aFontPattern)
if (FcPatternGetBool(aFontPattern, FC_OUTLINE, 0, &outline) != FcResultMatch ||
!outline) {
mHasNonScalableFaces = true;
FcBool scalable;
if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) == FcResultMatch &&
scalable) {
mForceScalable = true;
}
}
nsCountedRef<FcPattern> pattern(aFontPattern);
@ -1023,7 +1038,9 @@ static const double kRejectDistance = 10000.0;
// Calculate a distance score representing the size disparity between the
// requested style's size and the font entry's size.
static double
SizeDistance(gfxFontconfigFontEntry* aEntry, const gfxFontStyle& aStyle)
SizeDistance(gfxFontconfigFontEntry* aEntry,
const gfxFontStyle& aStyle,
bool aForceScalable)
{
double requestedSize = SizeForStyle(aEntry, aStyle);
double bestDist = -1.0;
@ -1040,7 +1057,7 @@ SizeDistance(gfxFontconfigFontEntry* aEntry, const gfxFontStyle& aStyle)
if (bestDist < 0.0) {
// No size means scalable
return -1.0;
} else if (5.0 * bestDist < requestedSize) {
} else if (aForceScalable || 5.0 * bestDist < requestedSize) {
// fontconfig prefers a matching family or lang to pixelsize of bitmap
// fonts. CSS suggests a tolerance of 20% on pixelsize.
return bestDist;
@ -1074,7 +1091,7 @@ gfxFontconfigFontFamily::FindAllFontsForStyle(const gfxFontStyle& aFontStyle,
for (size_t i = 0; i < aFontEntryList.Length(); i++) {
gfxFontconfigFontEntry* entry =
static_cast<gfxFontconfigFontEntry*>(aFontEntryList[i]);
double dist = SizeDistance(entry, aFontStyle);
double dist = SizeDistance(entry, aFontStyle, mForceScalable);
// If the entry is scalable or has a style that does not match
// the group of unscalable fonts, then start a new group.
if (dist < 0.0 ||

View File

@ -195,7 +195,8 @@ public:
explicit gfxFontconfigFontFamily(const nsAString& aName) :
gfxFontFamily(aName),
mContainsAppFonts(false),
mHasNonScalableFaces(false)
mHasNonScalableFaces(false),
mForceScalable(false)
{ }
void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) override;
@ -221,6 +222,7 @@ protected:
bool mContainsAppFonts;
bool mHasNonScalableFaces;
bool mForceScalable;
};
class gfxFontconfigFont : public gfxFontconfigFontBase {

View File

@ -48,6 +48,7 @@ gfxHarfBuzzShaper::gfxHarfBuzzShaper(gfxFont *aFont)
: gfxFontShaper(aFont),
mHBFace(aFont->GetFontEntry()->GetHBFace()),
mHBFont(nullptr),
mBuffer(nullptr),
mKernTable(nullptr),
mHmtxTable(nullptr),
mVmtxTable(nullptr),
@ -98,6 +99,9 @@ gfxHarfBuzzShaper::~gfxHarfBuzzShaper()
if (mHBFace) {
hb_face_destroy(mHBFace);
}
if (mBuffer) {
hb_buffer_destroy(mBuffer);
}
}
#define UNICODE_BMP_LIMIT 0x10000
@ -1289,6 +1293,11 @@ gfxHarfBuzzShaper::Initialize()
}
}
mBuffer = hb_buffer_create();
hb_buffer_set_unicode_funcs(mBuffer, sHBUnicodeFuncs);
hb_buffer_set_cluster_level(mBuffer,
HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
mHBFont = hb_font_create(mHBFace);
hb_font_set_funcs(mHBFont, sHBFontFuncs, &mCallbackData, nullptr);
hb_font_set_ppem(mHBFont, mFont->GetAdjustedSize(), mFont->GetAdjustedSize());
@ -1474,10 +1483,8 @@ gfxHarfBuzzShaper::ShapeText(DrawTarget *aDrawTarget,
&features);
bool isRightToLeft = aShapedText->IsRightToLeft();
hb_buffer_t *buffer = hb_buffer_create();
hb_buffer_set_unicode_funcs(buffer, sHBUnicodeFuncs);
hb_buffer_set_direction(buffer,
hb_buffer_set_direction(mBuffer,
aVertical ? HB_DIRECTION_TTB :
(isRightToLeft ? HB_DIRECTION_RTL :
HB_DIRECTION_LTR));
@ -1487,7 +1494,7 @@ gfxHarfBuzzShaper::ShapeText(DrawTarget *aDrawTarget,
} else {
scriptTag = GetHBScriptUsedForShaping(aScript);
}
hb_buffer_set_script(buffer, scriptTag);
hb_buffer_set_script(mBuffer, scriptTag);
hb_language_t language;
if (style->languageOverride) {
@ -1502,27 +1509,25 @@ gfxHarfBuzzShaper::ShapeText(DrawTarget *aDrawTarget,
} else {
language = hb_ot_tag_to_language(HB_OT_TAG_DEFAULT_LANGUAGE);
}
hb_buffer_set_language(buffer, language);
hb_buffer_set_language(mBuffer, language);
uint32_t length = aLength;
hb_buffer_add_utf16(buffer,
hb_buffer_add_utf16(mBuffer,
reinterpret_cast<const uint16_t*>(aText),
length, 0, length);
hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
hb_shape(mHBFont, buffer, features.Elements(), features.Length());
hb_shape(mHBFont, mBuffer, features.Elements(), features.Length());
if (isRightToLeft) {
hb_buffer_reverse(buffer);
hb_buffer_reverse(mBuffer);
}
nsresult rv = SetGlyphsFromRun(aShapedText, aOffset, aLength,
aText, buffer, aVertical, aRounding);
aText, aVertical, aRounding);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"failed to store glyphs into gfxShapedWord");
hb_buffer_destroy(buffer);
hb_buffer_clear_contents(mBuffer);
return NS_SUCCEEDED(rv);
}
@ -1536,12 +1541,11 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
uint32_t aOffset,
uint32_t aLength,
const char16_t *aText,
hb_buffer_t *aBuffer,
bool aVertical,
RoundingFlags aRounding)
{
uint32_t numGlyphs;
const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(aBuffer, &numGlyphs);
const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
if (numGlyphs == 0) {
return NS_OK;
}
@ -1603,7 +1607,7 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
nscoord bPos = 0;
const hb_glyph_position_t *posInfo =
hb_buffer_get_glyph_positions(aBuffer, nullptr);
hb_buffer_get_glyph_positions(mBuffer, nullptr);
while (glyphStart < int32_t(numGlyphs)) {

View File

@ -100,14 +100,12 @@ protected:
uint32_t aOffset,
uint32_t aLength,
const char16_t *aText,
hb_buffer_t *aBuffer,
bool aVertical,
RoundingFlags aRounding);
// retrieve glyph positions, applying advance adjustments and attachments
// returns results in appUnits
nscoord GetGlyphPositions(gfxContext *aContext,
hb_buffer_t *aBuffer,
nsTArray<nsPoint>& aPositions,
uint32_t aAppUnitsPerDevUnit);
@ -132,6 +130,9 @@ protected:
// size-specific font object, owned by the gfxHarfBuzzShaper
hb_font_t *mHBFont;
// harfbuzz buffer for the shaping process
hb_buffer_t *mBuffer;
FontCallbackData mCallbackData;
// Following table references etc are declared "mutable" because the

View File

@ -425,6 +425,7 @@ FrameAnimator::RequestRefresh(AnimationState& aState,
// Advanced to the correct frame, the composited frame is now valid to be drawn.
if (*currentFrameEndTime > aTime) {
aState.mCompositedFrameInvalid = false;
ret.mDirtyRect = IntRect(IntPoint(0,0), mSize);
}
MOZ_ASSERT(!aState.mIsCurrentlyDecoded || !aState.mCompositedFrameInvalid);

View File

@ -1,82 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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 "ImageBlocker.h"
#include "nsIPermissionManager.h"
#include "nsContentUtils.h"
#include "mozilla/Unused.h"
using namespace mozilla;
using namespace mozilla::image;
bool ImageBlocker::sInitialized = false;
int32_t ImageBlocker::sImagePermission = nsIPermissionManager::ALLOW_ACTION;
NS_IMPL_ISUPPORTS(ImageBlocker, nsIContentPolicy)
ImageBlocker::ImageBlocker()
{
if (!sInitialized) {
Preferences::AddIntVarCache(&sImagePermission,
"permissions.default.image");
sInitialized = true;
}
}
NS_IMETHODIMP
ImageBlocker::ShouldLoad(uint32_t aContentType,
nsIURI* aContentLocation,
nsIURI* aRequestOrigin,
nsISupports* aContext,
const nsACString& aMimeTypeGuess,
nsISupports* aExtra,
nsIPrincipal* aRequestPrincipal,
int16_t* aShouldLoad)
{
MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType),
"We should only see external content policy types here.");
*aShouldLoad = nsIContentPolicy::ACCEPT;
if (!aContentLocation) {
return NS_OK;
}
nsAutoCString scheme;
Unused << aContentLocation->GetScheme(scheme);
if (!scheme.LowerCaseEqualsLiteral("ftp") &&
!scheme.LowerCaseEqualsLiteral("http") &&
!scheme.LowerCaseEqualsLiteral("https")) {
return NS_OK;
}
// Block loading images depending on the permissions.default.image pref.
if (aContentType == nsIContentPolicy::TYPE_IMAGE ||
aContentType == nsIContentPolicy::TYPE_IMAGESET) {
*aShouldLoad = (sImagePermission == nsIPermissionManager::ALLOW_ACTION) ?
nsIContentPolicy::ACCEPT :
nsIContentPolicy::REJECT_TYPE;
}
return NS_OK;
}
NS_IMETHODIMP
ImageBlocker::ShouldProcess(uint32_t aContentType,
nsIURI* aContentLocation,
nsIURI* aRequestOrigin,
nsISupports* aRequestingContext,
const nsACString& aMimeTypeGuess,
nsISupports* aExtra,
nsIPrincipal* aRequestPrincipal,
int16_t* aShouldProcess)
{
MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType),
"We should only see external content policy types here.");
return ShouldLoad(aContentType, aContentLocation, aRequestOrigin,
aRequestingContext, aMimeTypeGuess, aExtra,
aRequestPrincipal, aShouldProcess);
}

View File

@ -1,42 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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_image_ImageBlocker_h
#define mozilla_image_ImageBlocker_h
#include "nsIContentPolicy.h"
namespace mozilla {
namespace image {
#define IMAGEBLOCKER_CONTRACTID "@mozilla.org/image-blocker-content-policy;1"
#define IMAGEBLOCKER_CID \
{ /* f6fcd651-164b-4416-b001-9c8c393fd93b */ \
0xf6fcd651, \
0x164b, \
0x4416, \
{0xb0, 0x01, 0x9c, 0x8c, 0x39, 0x3f, 0xd9, 0x3b} \
}
class ImageBlocker final : public nsIContentPolicy
{
~ImageBlocker() = default;
public:
ImageBlocker();
NS_DECL_ISUPPORTS
NS_DECL_NSICONTENTPOLICY
private:
static bool sInitialized;
static int32_t sImagePermission;
};
}
}
#endif // mozilla_image_ImageBlocker_h

View File

@ -1262,17 +1262,20 @@ RasterImage::Decode(const IntSize& aSize,
mSourceBuffer, mSize,
decoderFlags, surfaceFlags);
// We may not be able to send an invalidation right here because of async
// notifications but that's not a problem because the first frame
// invalidation (when it comes) will invalidate for us. So we can ignore
// the return value of UpdateState. This also handles the invalidation
// from setting the composited frame as valid below.
mAnimationState->UpdateState(mAnimationFinished, this, mSize);
// If the animation is finished we can draw right away because we just draw
// the final frame all the time from now on. See comment in
// AnimationState::UpdateState.
if (mAnimationFinished) {
mAnimationState->SetCompositedFrameInvalid(false);
}
// notifications but that shouldn't be a problem because we shouldn't be
// getting a non-empty rect back from UpdateState. This is because UpdateState
// will only return a non-empty rect if we are currently decoded, or the
// animation is finished. We can't be decoded because we are creating a decoder
// here. If the animation is finished then the composited frame would have
// been valid when the animation finished, and it's not possible to mark
// the composited frame as invalid when the animation is finished. So
// the composited frame can't change from invalid to valid in this UpdateState
// call, and hence no rect can be returned.
#ifdef DEBUG
gfx::IntRect rect =
#endif
mAnimationState->UpdateState(mAnimationFinished, this, mSize);
MOZ_ASSERT(rect.IsEmpty());
} else {
task = DecoderFactory::CreateDecoder(mDecoderType, WrapNotNull(this),
mSourceBuffer, mSize, aSize,

View File

@ -10,7 +10,6 @@
#include "nsMimeTypes.h"
#include "DecodePool.h"
#include "ImageBlocker.h"
#include "ImageFactory.h"
#include "ShutdownTracker.h"
#include "SurfaceCache.h"
@ -39,7 +38,6 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsICOEncoder)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsJPEGEncoder)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsPNGEncoder)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsBMPEncoder)
NS_GENERIC_FACTORY_CONSTRUCTOR(ImageBlocker)
NS_DEFINE_NAMED_CID(NS_IMGLOADER_CID);
NS_DEFINE_NAMED_CID(NS_IMGREQUESTPROXY_CID);
NS_DEFINE_NAMED_CID(NS_IMGTOOLS_CID);
@ -47,7 +45,6 @@ NS_DEFINE_NAMED_CID(NS_ICOENCODER_CID);
NS_DEFINE_NAMED_CID(NS_JPEGENCODER_CID);
NS_DEFINE_NAMED_CID(NS_PNGENCODER_CID);
NS_DEFINE_NAMED_CID(NS_BMPENCODER_CID);
NS_DEFINE_NAMED_CID(IMAGEBLOCKER_CID);
static const mozilla::Module::CIDEntry kImageCIDs[] = {
{ &kNS_IMGLOADER_CID, false, nullptr, imgLoaderConstructor, },
@ -57,7 +54,6 @@ static const mozilla::Module::CIDEntry kImageCIDs[] = {
{ &kNS_JPEGENCODER_CID, false, nullptr, nsJPEGEncoderConstructor, },
{ &kNS_PNGENCODER_CID, false, nullptr, nsPNGEncoderConstructor, },
{ &kNS_BMPENCODER_CID, false, nullptr, nsBMPEncoderConstructor, },
{ &kIMAGEBLOCKER_CID, false, nullptr, ImageBlockerConstructor, },
{ nullptr }
};
@ -70,7 +66,6 @@ static const mozilla::Module::ContractIDEntry kImageContracts[] = {
{ "@mozilla.org/image/encoder;2?type=" IMAGE_JPEG, &kNS_JPEGENCODER_CID },
{ "@mozilla.org/image/encoder;2?type=" IMAGE_PNG, &kNS_PNGENCODER_CID },
{ "@mozilla.org/image/encoder;2?type=" IMAGE_BMP, &kNS_BMPENCODER_CID },
{ IMAGEBLOCKER_CONTRACTID, &kIMAGEBLOCKER_CID },
{ nullptr }
};
@ -88,7 +83,6 @@ static const mozilla::Module::CategoryEntry kImageCategories[] = {
{ "Gecko-Content-Viewers", IMAGE_APNG, "@mozilla.org/content/document-loader-factory;1" },
{ "Gecko-Content-Viewers", IMAGE_X_PNG, "@mozilla.org/content/document-loader-factory;1" },
{ "content-sniffing-services", "@mozilla.org/image/loader;1", "@mozilla.org/image/loader;1" },
{ "content-policy", IMAGEBLOCKER_CONTRACTID, IMAGEBLOCKER_CONTRACTID },
{ nullptr }
};

View File

@ -617,7 +617,7 @@ imgRequestProxy* NewStaticProxy(imgRequestProxy* aThis)
{
nsCOMPtr<nsIPrincipal> currentPrincipal;
aThis->GetImagePrincipal(getter_AddRefs(currentPrincipal));
RefPtr<Image> image = aThis->GetImage();
RefPtr<image::Image> image = aThis->GetImage();
return new imgRequestProxyStatic(image, currentPrincipal);
}

View File

@ -63,7 +63,6 @@ UNIFIED_SOURCES += [
'FrozenImage.cpp',
'IDecodingTask.cpp',
'Image.cpp',
'ImageBlocker.cpp',
'ImageCacheKey.cpp',
'ImageFactory.cpp',
'ImageOps.cpp',

View File

@ -1001,8 +1001,6 @@ description =
description =
[PCompositorBridge::StopFrameTimeRecording]
description =
[PCompositorBridge::PLayerTransaction]
description =
[PCompositorBridge::SyncWithCompositor]
description =
[PCompositorBridge::PWebRenderBridge]
@ -1036,6 +1034,8 @@ description =
description =
[PLayerTransaction::RequestProperty]
description =
[PLayerTransaction::GetTextureFactoryIdentifier]
description = bug 1350634
[PUiCompositorController::Pause]
description =
[PUiCompositorController::Resume]

View File

@ -681,8 +681,8 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying());
MOZ_ASSERT(!lazy->isLegacyGenerator());
ParseNode* pn = parser.standaloneLazyFunction(fun, lazy->strict(), lazy->generatorKind(),
lazy->asyncKind());
ParseNode* pn = parser.standaloneLazyFunction(fun, lazy->toStringStart() + lazy->column(),
lazy->strict(), lazy->generatorKind(), lazy->asyncKind());
if (!pn)
return false;

View File

@ -169,26 +169,60 @@ ParseContext::Scope::dump(ParseContext* pc)
fprintf(stdout, "\n");
}
/* static */ void
ParseContext::Scope::removeVarForAnnexBLexicalFunction(ParseContext* pc, JSAtom* name)
bool
ParseContext::Scope::addPossibleAnnexBFunctionBox(ParseContext* pc, FunctionBox* funbox)
{
// Local strict mode is allowed, e.g., a class binding removing a
// synthesized Annex B binding.
MOZ_ASSERT(!pc->sc()->strictScript);
if (!possibleAnnexBFunctionBoxes_) {
if (!possibleAnnexBFunctionBoxes_.acquire(pc->sc()->context))
return false;
}
for (ParseContext::Scope* scope = pc->innermostScope();
scope != pc->varScope().enclosing();
scope = scope->enclosing())
return possibleAnnexBFunctionBoxes_->append(funbox);
}
bool
ParseContext::Scope::propagateAndMarkAnnexBFunctionBoxes(ParseContext* pc)
{
// Strict mode doesn't have wack Annex B function semantics.
if (pc->sc()->strict() ||
!possibleAnnexBFunctionBoxes_ ||
possibleAnnexBFunctionBoxes_->empty())
{
if (DeclaredNamePtr p = scope->declared_->lookup(name)) {
if (p->value()->kind() == DeclarationKind::VarForAnnexBLexicalFunction)
scope->declared_->remove(p);
return true;
}
if (this == &pc->varScope()) {
// Base case: actually declare the Annex B vars and mark applicable
// function boxes as Annex B.
RootedPropertyName name(pc->sc()->context);
Maybe<DeclarationKind> redeclaredKind;
uint32_t unused;
for (FunctionBox* funbox : *possibleAnnexBFunctionBoxes_) {
if (pc->annexBAppliesToLexicalFunctionInInnermostScope(funbox)) {
name = funbox->function()->explicitName()->asPropertyName();
if (!pc->tryDeclareVar(name,
DeclarationKind::VarForAnnexBLexicalFunction,
DeclaredNameInfo::npos, &redeclaredKind, &unused))
{
return false;
}
MOZ_ASSERT(!redeclaredKind);
funbox->isAnnexB = true;
}
}
} else {
// Inner scope case: propagate still applicable function boxes to the
// enclosing scope.
for (FunctionBox* funbox : *possibleAnnexBFunctionBoxes_) {
if (pc->annexBAppliesToLexicalFunctionInInnermostScope(funbox)) {
if (!enclosing()->addPossibleAnnexBFunctionBox(pc, funbox))
return false;
}
}
}
// Annex B semantics no longer applies to any functions with this name, as
// an early error would have occurred.
pc->removeInnerFunctionBoxesForAnnexB(name);
return true;
}
static bool
@ -367,59 +401,9 @@ ParseContext::init()
if (!closedOverBindingsForLazy_.acquire(cx))
return false;
if (!sc()->strict()) {
if (!innerFunctionBoxesForAnnexB_.acquire(cx))
return false;
}
return true;
}
bool
ParseContext::addInnerFunctionBoxForAnnexB(FunctionBox* funbox)
{
for (uint32_t i = 0; i < innerFunctionBoxesForAnnexB_->length(); i++) {
if (!innerFunctionBoxesForAnnexB_[i]) {
innerFunctionBoxesForAnnexB_[i] = funbox;
return true;
}
}
return innerFunctionBoxesForAnnexB_->append(funbox);
}
void
ParseContext::removeInnerFunctionBoxesForAnnexB(JSAtom* name)
{
for (uint32_t i = 0; i < innerFunctionBoxesForAnnexB_->length(); i++) {
if (FunctionBox* funbox = innerFunctionBoxesForAnnexB_[i]) {
if (funbox->function()->explicitName() == name)
innerFunctionBoxesForAnnexB_[i] = nullptr;
}
}
}
void
ParseContext::finishInnerFunctionBoxesForAnnexB()
{
// Strict mode doesn't have wack Annex B function semantics. Or we
// could've failed to initialize ParseContext.
if (sc()->strict() || !innerFunctionBoxesForAnnexB_)
return;
for (uint32_t i = 0; i < innerFunctionBoxesForAnnexB_->length(); i++) {
if (FunctionBox* funbox = innerFunctionBoxesForAnnexB_[i])
funbox->isAnnexB = true;
}
}
ParseContext::~ParseContext()
{
// Any funboxes still in the list at the end of parsing means no early
// error would have occurred for declaring a binding in the nearest var
// scope. Mark them as needing extra assignments to this var binding.
finishInnerFunctionBoxesForAnnexB();
}
bool
UsedNameTracker::noteUse(JSContext* cx, JSAtom* name, uint32_t scriptId, uint32_t scopeId)
{
@ -916,11 +900,9 @@ template <template <typename CharT> class ParseHandler, typename CharT>
FunctionBox*
Parser<ParseHandler, CharT>::newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
Directives inheritedDirectives,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
bool tryAnnexB)
GeneratorKind generatorKind, FunctionAsyncKind asyncKind)
{
MOZ_ASSERT(fun);
MOZ_ASSERT_IF(tryAnnexB, !pc->sc()->strict());
/*
* We use JSContext.tempLifoAlloc to allocate parsed objects and place them
@ -942,9 +924,6 @@ Parser<ParseHandler, CharT>::newFunctionBox(Node fn, JSFunction* fun, uint32_t t
if (fn)
handler.setFunctionBox(fn, funbox);
if (tryAnnexB && !pc->addInnerFunctionBoxForAnnexB(funbox))
return nullptr;
return funbox;
}
@ -1200,12 +1179,11 @@ DeclarationKindIsVar(DeclarationKind kind)
kind == DeclarationKind::ForOfVar;
}
template <template <typename CharT> class ParseHandler, typename CharT>
Maybe<DeclarationKind>
Parser<ParseHandler, CharT>::isVarRedeclaredInEval(HandlePropertyName name, DeclarationKind kind)
ParseContext::isVarRedeclaredInEval(HandlePropertyName name, DeclarationKind kind)
{
MOZ_ASSERT(DeclarationKindIsVar(kind));
MOZ_ASSERT(pc->sc()->isEvalContext());
MOZ_ASSERT(sc()->isEvalContext());
// In the case of eval, we also need to check enclosing VM scopes to see
// if the var declaration is allowed in the context.
@ -1213,8 +1191,8 @@ Parser<ParseHandler, CharT>::isVarRedeclaredInEval(HandlePropertyName name, Decl
// This check is necessary in addition to
// js::CheckEvalDeclarationConflicts because we only know during parsing
// if a var is bound by for-of.
Scope* enclosingScope = pc->sc()->compilationEnclosingScope();
Scope* varScope = EvalScope::nearestVarScopeForDirectEval(enclosingScope);
js::Scope* enclosingScope = sc()->compilationEnclosingScope();
js::Scope* varScope = EvalScope::nearestVarScopeForDirectEval(enclosingScope);
MOZ_ASSERT(varScope);
for (ScopeIter si(enclosingScope); si; si++) {
for (js::BindingIter bi(si.scope()); bi; bi++) {
@ -1254,6 +1232,25 @@ Parser<ParseHandler, CharT>::isVarRedeclaredInEval(HandlePropertyName name, Decl
return Nothing();
}
Maybe<DeclarationKind>
ParseContext::isVarRedeclaredInInnermostScope(HandlePropertyName name, DeclarationKind kind)
{
Maybe<DeclarationKind> redeclaredKind;
uint32_t unused;
MOZ_ALWAYS_TRUE(tryDeclareVarHelper<DryRunInnermostScopeOnly>(name, kind,
DeclaredNameInfo::npos,
&redeclaredKind, &unused));
return redeclaredKind;
}
bool
ParseContext::tryDeclareVar(HandlePropertyName name, DeclarationKind kind,
uint32_t beginPos, Maybe<DeclarationKind>* redeclaredKind,
uint32_t* prevPos)
{
return tryDeclareVarHelper<NotDryRun>(name, kind, beginPos, redeclaredKind, prevPos);
}
static bool
DeclarationKindIsParameter(DeclarationKind kind)
{
@ -1261,12 +1258,11 @@ DeclarationKindIsParameter(DeclarationKind kind)
kind == DeclarationKind::FormalParameter;
}
template <template <typename CharT> class ParseHandler, typename CharT>
template <ParseContext::DryRunOption dryRunOption>
bool
Parser<ParseHandler, CharT>::tryDeclareVar(HandlePropertyName name, DeclarationKind kind,
uint32_t beginPos,
Maybe<DeclarationKind>* redeclaredKind,
uint32_t* prevPos)
ParseContext::tryDeclareVarHelper(HandlePropertyName name, DeclarationKind kind,
uint32_t beginPos, Maybe<DeclarationKind>* redeclaredKind,
uint32_t* prevPos)
{
MOZ_ASSERT(DeclarationKindIsVar(kind));
@ -1282,8 +1278,8 @@ Parser<ParseHandler, CharT>::tryDeclareVar(HandlePropertyName name, DeclarationK
// { var x; var x; }
// { { let x; } var x; }
for (ParseContext::Scope* scope = pc->innermostScope();
scope != pc->varScope().enclosing();
for (ParseContext::Scope* scope = innermostScope();
scope != varScope().enclosing();
scope = scope->enclosing())
{
if (AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name)) {
@ -1308,23 +1304,20 @@ Parser<ParseHandler, CharT>::tryDeclareVar(HandlePropertyName name, DeclarationK
// the kind to BodyLevelFunction. See
// declareFunctionArgumentsObject.
//
// For a var previously declared as
// VarForAnnexBLexicalFunction, this previous DeclarationKind
// is used so that vars synthesized solely for Annex B.3.3 may
// be removed if an early error would occur. If a synthesized
// Annex B.3.3 var has the same name as a body-level function,
// this is not a redeclaration, and indeed, because the
// body-level function binds the name, this name should not be
// removed should a redeclaration occur in the future. Thus it
// is also correct to alter the kind to BodyLevelFunction.
// VarForAnnexBLexicalFunction declarations are declared when
// the var scope exits. It is not possible for a var to be
// previously declared as VarForAnnexBLexicalFunction and
// checked for redeclaration.
//
// [1] ES 15.1.11
// [2] ES 18.2.1.3
// [3] ES 8.1.1.4.15
// [4] ES 8.1.1.4.16
// [5] ES 9.2.12
if (kind == DeclarationKind::BodyLevelFunction)
if (dryRunOption == NotDryRun && kind == DeclarationKind::BodyLevelFunction) {
MOZ_ASSERT(declaredKind != DeclarationKind::VarForAnnexBLexicalFunction);
p->value()->alterKind(kind);
}
} else if (!DeclarationKindIsParameter(declaredKind)) {
// Annex B.3.5 allows redeclaring simple (non-destructured)
// catch parameters with var declarations, except when it
@ -1335,7 +1328,7 @@ Parser<ParseHandler, CharT>::tryDeclareVar(HandlePropertyName name, DeclarationK
// Annex B.3.3 allows redeclaring functions in the same block.
bool annexB33Allowance = declaredKind == DeclarationKind::LexicalFunction &&
kind == DeclarationKind::VarForAnnexBLexicalFunction &&
scope == pc->innermostScope();
scope == innermostScope();
if (!annexB35Allowance && !annexB33Allowance) {
*redeclaredKind = Some(declaredKind);
@ -1351,13 +1344,23 @@ Parser<ParseHandler, CharT>::tryDeclareVar(HandlePropertyName name, DeclarationK
*redeclaredKind = Some(declaredKind);
return true;
}
} else {
if (!scope->addDeclaredName(pc, p, name, kind, beginPos))
} else if (dryRunOption == NotDryRun) {
if (!scope->addDeclaredName(this, p, name, kind, beginPos))
return false;
}
// DryRunOption is used for propagating Annex B functions: we don't
// want to declare the synthesized Annex B vars until we exit the var
// scope and know that no early errors would have occurred. In order
// to avoid quadratic search, we only check for var redeclarations in
// the innermost scope when doing a dry run.
if (dryRunOption == DryRunInnermostScopeOnly)
break;
}
if (!pc->sc()->strict() && pc->sc()->isEvalContext()) {
if (!sc()->strict() && sc()->isEvalContext() &&
(dryRunOption == NotDryRun || innermostScope() == &varScope()))
{
*redeclaredKind = isVarRedeclaredInEval(name, kind);
// We don't have position information at runtime.
*prevPos = DeclaredNameInfo::npos;
@ -1366,49 +1369,36 @@ Parser<ParseHandler, CharT>::tryDeclareVar(HandlePropertyName name, DeclarationK
return true;
}
template <template <typename CharT> class ParseHandler, typename CharT>
bool
Parser<ParseHandler, CharT>::tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name,
uint32_t beginPos,
bool* tryAnnexB)
ParseContext::annexBAppliesToLexicalFunctionInInnermostScope(FunctionBox* funbox)
{
Maybe<DeclarationKind> redeclaredKind;
uint32_t unused;
if (!tryDeclareVar(name, DeclarationKind::VarForAnnexBLexicalFunction, beginPos,
&redeclaredKind, &unused))
{
return false;
}
MOZ_ASSERT(!sc()->strict());
if (!redeclaredKind && pc->isFunctionBox()) {
ParseContext::Scope& funScope = pc->functionScope();
ParseContext::Scope& varScope = pc->varScope();
if (&funScope != &varScope) {
RootedPropertyName name(sc()->context, funbox->function()->explicitName()->asPropertyName());
Maybe<DeclarationKind> redeclaredKind =
isVarRedeclaredInInnermostScope(name, DeclarationKind::VarForAnnexBLexicalFunction);
if (!redeclaredKind && isFunctionBox()) {
Scope& funScope = functionScope();
if (&funScope != &varScope()) {
// Annex B.3.3.1 disallows redeclaring parameter names. In the
// presence of parameter expressions, parameter names are on the
// function scope, which encloses the var scope. This means
// tryDeclareVar call above would not catch this case, so test it
// manually.
// function scope, which encloses the var scope. This means the
// isVarRedeclaredInInnermostScope call above would not catch this
// case, so test it manually.
if (AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(name)) {
DeclarationKind declaredKind = p->value()->kind();
if (DeclarationKindIsParameter(declaredKind))
redeclaredKind = Some(declaredKind);
else
MOZ_ASSERT(FunctionScope::isSpecialName(context, name));
MOZ_ASSERT(FunctionScope::isSpecialName(sc()->context, name));
}
}
}
if (redeclaredKind) {
// If an early error would have occurred, undo all the
// VarForAnnexBLexicalFunction declarations.
*tryAnnexB = false;
ParseContext::Scope::removeVarForAnnexBLexicalFunction(pc, name);
} else {
*tryAnnexB = true;
}
return true;
// If an early error would have occurred already, this function should not
// exhibit Annex B.3.3 semantics.
return !redeclaredKind;
}
template <template <typename CharT> class ParseHandler, typename CharT>
@ -1451,7 +1441,7 @@ Parser<ParseHandler, CharT>::noteDeclaredName(HandlePropertyName name, Declarati
case DeclarationKind::ForOfVar: {
Maybe<DeclarationKind> redeclaredKind;
uint32_t prevPos;
if (!tryDeclareVar(name, kind, pos.begin, &redeclaredKind, &prevPos))
if (!pc->tryDeclareVar(name, kind, pos.begin, &redeclaredKind, &prevPos))
return false;
if (redeclaredKind) {
@ -1509,17 +1499,10 @@ Parser<ParseHandler, CharT>::noteDeclaredName(HandlePropertyName name, Declarati
//
// In sloppy mode, lexical functions may redeclare other lexical
// functions for web compatibility reasons.
if (pc->sc()->strict() ||
(p->value()->kind() != DeclarationKind::LexicalFunction &&
p->value()->kind() != DeclarationKind::VarForAnnexBLexicalFunction))
{
if (pc->sc()->strict() || p->value()->kind() != DeclarationKind::LexicalFunction) {
reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
// Update the DeclarationKind to make a LexicalFunction
// declaration that shadows the VarForAnnexBLexicalFunction.
p->value()->alterKind(kind);
} else {
if (!scope->addDeclaredName(pc, p, name, kind, pos.begin))
return false;
@ -1570,20 +1553,11 @@ Parser<ParseHandler, CharT>::noteDeclaredName(HandlePropertyName name, Declarati
// name in the same scope.
AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name);
if (p) {
// If the early error would have occurred due to Annex B.3.3
// semantics, remove the synthesized Annex B var declaration, do
// not report the redeclaration, and declare the lexical name.
if (p->value()->kind() == DeclarationKind::VarForAnnexBLexicalFunction) {
ParseContext::Scope::removeVarForAnnexBLexicalFunction(pc, name);
p = scope->lookupDeclaredNameForAdd(name);
MOZ_ASSERT(!p);
} else {
reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
if (!p && !scope->addDeclaredName(pc, p, name, kind, pos.begin))
if (!scope->addDeclaredName(pc, p, name, kind, pos.begin))
return false;
break;
@ -1645,6 +1619,11 @@ template <template <typename CharT> class ParseHandler, typename CharT>
bool
Parser<ParseHandler, CharT>::propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope)
{
// Now that we have all the declared names in the scope, check which
// functions should exhibit Annex B semantics.
if (!scope.propagateAndMarkAnnexBFunctionBoxes(pc))
return false;
if (handler.canSkipLazyClosedOverBindings()) {
// Scopes are nullptr-delimited in the LazyScript closed over bindings
// array.
@ -2173,6 +2152,12 @@ Parser<FullParseHandler, char16_t>::evalBody(EvalSharedContext* evalsc)
if (!FoldConstants(context, &body, this))
return nullptr;
// For eval scripts, since all bindings are automatically considered
// closed over, we don't need to call propagateFreeNamesAndMarkClosed-
// OverBindings. However, Annex B.3.3 functions still need to be marked.
if (!varScope.propagateAndMarkAnnexBFunctionBoxes(pc))
return nullptr;
Maybe<EvalScope::Data*> bindings = newEvalScopeData(pc->varScope());
if (!bindings)
return nullptr;
@ -2203,6 +2188,12 @@ Parser<FullParseHandler, char16_t>::globalBody(GlobalSharedContext* globalsc)
if (!FoldConstants(context, &body, this))
return nullptr;
// For global scripts, whether bindings are closed over or not doesn't
// matter, so no need to call propagateFreeNamesAndMarkClosedOver-
// Bindings. However, Annex B.3.3 functions still need to be marked.
if (!varScope.propagateAndMarkAnnexBFunctionBoxes(pc))
return nullptr;
Maybe<GlobalScope::Data*> bindings = newGlobalScopeData(pc->varScope());
if (!bindings)
return nullptr;
@ -2546,7 +2537,7 @@ Parser<FullParseHandler, char16_t>::standaloneFunction(HandleFunction fun,
fn->pn_body = argsbody;
FunctionBox* funbox = newFunctionBox(fn, fun, /* toStringStart = */ 0, inheritedDirectives,
generatorKind, asyncKind, /* tryAnnexB = */ false);
generatorKind, asyncKind);
if (!funbox)
return null();
funbox->initStandaloneFunction(enclosingScope);
@ -3219,7 +3210,7 @@ Parser<FullParseHandler, char16_t>::skipLazyInnerFunction(ParseNode* pn, uint32_
RootedFunction fun(context, handler.nextLazyInnerFunction());
MOZ_ASSERT(!fun->isLegacyGenerator());
FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, Directives(/* strict = */ false),
fun->generatorKind(), fun->asyncKind(), tryAnnexB);
fun->generatorKind(), fun->asyncKind());
if (!funbox)
return false;
@ -3250,6 +3241,10 @@ Parser<FullParseHandler, char16_t>::skipLazyInnerFunction(ParseNode* pn, uint32_
}
#endif
// Append possible Annex B function box only upon successfully parsing.
if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
return false;
return true;
}
@ -3446,7 +3441,7 @@ Parser<FullParseHandler, char16_t>::trySyntaxParseInnerFunction(ParseNode* pn, H
// still expects a FunctionBox to be attached to it during BCE, and
// the syntax parser cannot attach one to it.
FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives,
generatorKind, asyncKind, tryAnnexB);
generatorKind, asyncKind);
if (!funbox)
return false;
funbox->initWithEnclosingParseContext(pc, kind);
@ -3475,6 +3470,11 @@ Parser<FullParseHandler, char16_t>::trySyntaxParseInnerFunction(ParseNode* pn, H
// Update the end position of the parse node.
pn->pn_pos.end = tokenStream.currentToken().pos.end;
// Append possible Annex B function box only upon successfully parsing.
if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
return false;
return true;
} while (false);
@ -3543,13 +3543,22 @@ Parser<ParseHandler, CharT>::innerFunction(Node pn, ParseContext* outerpc, Handl
// instead of the current top of the stack of the syntax parser.
FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives,
generatorKind, asyncKind, tryAnnexB);
generatorKind, asyncKind);
if (!funbox)
return false;
funbox->initWithEnclosingParseContext(outerpc, kind);
return innerFunction(pn, outerpc, funbox, toStringStart, inHandling, yieldHandling, kind,
inheritedDirectives, newDirectives);
if (!innerFunction(pn, outerpc, funbox, toStringStart, inHandling, yieldHandling, kind,
inheritedDirectives, newDirectives))
{
return false;
}
// Append possible Annex B function box only upon successfully parsing.
if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
return false;
return true;
}
template <template <typename CharT> class ParseHandler, typename CharT>
@ -3573,7 +3582,9 @@ Parser<ParseHandler, CharT>::appendToCallSiteObj(Node callSiteObj)
template <>
ParseNode*
Parser<FullParseHandler, char16_t>::standaloneLazyFunction(HandleFunction fun, bool strict,
Parser<FullParseHandler, char16_t>::standaloneLazyFunction(HandleFunction fun,
uint32_t toStringStart,
bool strict,
GeneratorKind generatorKind,
FunctionAsyncKind asyncKind)
{
@ -3584,8 +3595,8 @@ Parser<FullParseHandler, char16_t>::standaloneLazyFunction(HandleFunction fun, b
return null();
Directives directives(strict);
FunctionBox* funbox = newFunctionBox(pn, fun, /* toStringStart = */ 0, directives,
generatorKind, asyncKind, /* tryAnnexB = */ false);
FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, directives, generatorKind,
asyncKind);
if (!funbox)
return null();
funbox->initFromLazyFunction();
@ -3759,14 +3770,14 @@ Parser<ParseHandler, CharT>::functionFormalParametersAndBody(InHandling inHandli
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
JSMSG_CURLY_OPENED, openedPos));
funbox->setEnd(pos().end);
funbox->setEnd(tokenStream);
} else {
#if !JS_HAS_EXPR_CLOSURES
MOZ_ASSERT(kind == Arrow);
#endif
if (tokenStream.hadError())
return false;
funbox->setEnd(pos().end);
funbox->setEnd(tokenStream);
if (kind == Statement && !matchOrInsertSemicolonAfterExpression())
return false;
}
@ -3854,8 +3865,10 @@ Parser<ParseHandler, CharT>::functionStmt(uint32_t toStringStart, YieldHandling
// early error, do so. This 'var' binding would be assigned
// the function object when its declaration is reached, not at
// the start of the block.
if (!tryDeclareVarForAnnexBLexicalFunction(name, pos().begin, &tryAnnexB))
return null();
//
// This semantics is implemented upon Scope exit in
// Scope::propagateAndMarkAnnexBFunctionBoxes.
tryAnnexB = true;
}
if (!noteDeclaredName(name, DeclarationKind::LexicalFunction, pos()))
@ -8594,7 +8607,7 @@ Parser<ParseHandler, CharT>::generatorComprehensionLambda(unsigned begin)
// Create box for fun->object early to root it.
Directives directives(/* strict = */ outerpc->sc()->strict());
FunctionBox* genFunbox = newFunctionBox(genfn, fun, /* toStringStart = */ 0, directives,
StarGenerator, SyncFunction, /* tryAnnexB = */ false);
StarGenerator, SyncFunction);
if (!genFunbox)
return null();
genFunbox->isGenexpLambda = true;
@ -8629,7 +8642,7 @@ Parser<ParseHandler, CharT>::generatorComprehensionLambda(unsigned begin)
uint32_t end = pos().end;
handler.setBeginPosition(comp, begin);
handler.setEndPosition(comp, end);
genFunbox->setEnd(end);
genFunbox->setEnd(tokenStream);
handler.addStatementToList(body, comp);
handler.setEndPosition(body, end);
handler.setBeginPosition(genfn, begin);

View File

@ -118,6 +118,11 @@ class ParseContext : public Nestable<ParseContext>
// the scope in which it is declared.
PooledMapPtr<DeclaredNameMap> declared_;
// FunctionBoxes in this scope that need to be considered for Annex
// B.3.3 semantics. This is checked on Scope exit, as by then we have
// all the declared names and would know if Annex B.3.3 is applicable.
PooledVectorPtr<FunctionBoxVector> possibleAnnexBFunctionBoxes_;
// Monotonically increasing id.
uint32_t id_;
@ -164,9 +169,12 @@ class ParseContext : public Nestable<ParseContext>
return maybeReportOOM(pc, declared_->add(p, name, DeclaredNameInfo(kind, pos)));
}
// Remove all VarForAnnexBLexicalFunction declarations of a certain
// name from all scopes in pc's scope stack.
static void removeVarForAnnexBLexicalFunction(ParseContext* pc, JSAtom* name);
// Add a FunctionBox as a possible candidate for Annex B.3.3 semantics.
MOZ_MUST_USE bool addPossibleAnnexBFunctionBox(ParseContext* pc, FunctionBox* funbox);
// Check if the candidate function boxes for Annex B.3.3 should in
// fact get Annex B semantics. Checked on Scope exit.
MOZ_MUST_USE bool propagateAndMarkAnnexBFunctionBoxes(ParseContext* pc);
// Add and remove catch parameter names. Used to implement the odd
// semantics of catch bodies.
@ -297,10 +305,6 @@ class ParseContext : public Nestable<ParseContext>
// expressions.
Scope* varScope_;
// Inner function boxes in this context to try Annex B.3.3 semantics
// on. Only used when full parsing.
PooledVectorPtr<FunctionBoxVector> innerFunctionBoxesForAnnexB_;
// Simple formal parameter names, in order of appearance. Only used when
// isFunctionBox().
PooledVectorPtr<AtomVector> positionalFormalParameterNames_;
@ -361,7 +365,6 @@ class ParseContext : public Nestable<ParseContext>
innermostStatement_(nullptr),
innermostScope_(nullptr),
varScope_(nullptr),
innerFunctionBoxesForAnnexB_(prs->context->frontendCollectionPool()),
positionalFormalParameterNames_(prs->context->frontendCollectionPool()),
closedOverBindingsForLazy_(prs->context->frontendCollectionPool()),
scriptId_(prs->usedNames.nextScriptId()),
@ -381,8 +384,6 @@ class ParseContext : public Nestable<ParseContext>
}
}
~ParseContext();
MOZ_MUST_USE bool init();
SharedContext* sc() {
@ -450,10 +451,6 @@ class ParseContext : public Nestable<ParseContext>
return *closedOverBindingsForLazy_;
}
MOZ_MUST_USE bool addInnerFunctionBoxForAnnexB(FunctionBox* funbox);
void removeInnerFunctionBoxesForAnnexB(JSAtom* name);
void finishInnerFunctionBoxesForAnnexB();
// True if we are at the topmost level of a entire script or function body.
// For example, while parsing this code we would encounter f1 and f2 at
// body level, but we would not encounter f3 or f4 at body level:
@ -533,6 +530,23 @@ class ParseContext : public Nestable<ParseContext>
uint32_t scriptId() const {
return scriptId_;
}
bool annexBAppliesToLexicalFunctionInInnermostScope(FunctionBox* funbox);
bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
mozilla::Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos);
private:
mozilla::Maybe<DeclarationKind> isVarRedeclaredInInnermostScope(HandlePropertyName name,
DeclarationKind kind);
mozilla::Maybe<DeclarationKind> isVarRedeclaredInEval(HandlePropertyName name,
DeclarationKind kind);
enum DryRunOption { NotDryRun, DryRunInnermostScopeOnly };
template <DryRunOption dryRunOption>
bool tryDeclareVarHelper(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
mozilla::Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos);
};
template <>
@ -963,6 +977,7 @@ inline
ParseContext::Scope::Scope(ParserBase* parser)
: Nestable<Scope>(&parser->pc->innermostScope_),
declared_(parser->context->frontendCollectionPool()),
possibleAnnexBFunctionBoxes_(parser->context->frontendCollectionPool()),
id_(parser->usedNames.nextScopeId())
{ }
@ -1133,8 +1148,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
Directives directives,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
bool tryAnnexB);
GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
void trace(JSTracer* trc);
@ -1185,7 +1199,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
// Parse a function, given only its arguments and body. Used for lazily
// parsed functions.
Node standaloneLazyFunction(HandleFunction fun, bool strict,
Node standaloneLazyFunction(HandleFunction fun, uint32_t toStringStart, bool strict,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
// Parse an inner function given an enclosing ParseContext and a
@ -1512,12 +1526,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
bool notePositionalFormalParameter(Node fn, HandlePropertyName name, uint32_t beginPos,
bool disallowDuplicateParams, bool* duplicatedParam);
bool noteDestructuredPositionalFormalParameter(Node fn, Node destruct);
mozilla::Maybe<DeclarationKind> isVarRedeclaredInEval(HandlePropertyName name,
DeclarationKind kind);
bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
mozilla::Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos);
bool tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name, uint32_t beginPos,
bool* tryAnnexB);
bool checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
DeclarationKind kind, TokenPos pos);
bool noteDeclaredName(HandlePropertyName name, DeclarationKind kind, TokenPos pos);

View File

@ -561,16 +561,28 @@ class FunctionBox : public ObjectBox, public SharedContext
}
void setStart(const TokenStream& tokenStream) {
bufStart = tokenStream.currentToken().pos.begin;
tokenStream.srcCoords.lineNumAndColumnIndex(bufStart, &startLine, &startColumn);
// Token positions are already offset from the start column in
// CompileOptions. bufStart and toStringStart, however, refer to
// absolute positions within the ScriptSource buffer, and need to
// de-offset from the starting column.
uint32_t offset = tokenStream.currentToken().pos.begin;
MOZ_ASSERT(offset >= tokenStream.options().column);
MOZ_ASSERT(toStringStart >= tokenStream.options().column);
toStringStart -= tokenStream.options().column;
bufStart = offset - tokenStream.options().column;
tokenStream.srcCoords.lineNumAndColumnIndex(offset, &startLine, &startColumn);
}
void setEnd(uint32_t end) {
void setEnd(const TokenStream& tokenStream) {
// For all functions except class constructors, the buffer and
// toString ending positions are the same. Class constructors override
// the toString ending position with the end of the class definition.
bufEnd = end;
toStringEnd = end;
//
// Offsets are de-offset for the same reason as in setStart above.
uint32_t offset = tokenStream.currentToken().pos.end;
MOZ_ASSERT(offset >= tokenStream.options().column);
bufEnd = offset - tokenStream.options().column;
toStringEnd = bufEnd;
}
void trace(JSTracer* trc) override;

View File

@ -0,0 +1,3 @@
// Test that lazy scripts can handle OOB column numbers.
assertEq(evaluate(`var f = x=>saveStack().column; f()`, { columnNumber: 1729 }), 1741);

View File

@ -4209,8 +4209,18 @@ JS::DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
mozilla::Vector<uint8_t>& buffer /* TranscodeBuffer& */, size_t cursor,
OffThreadCompileCallback callback, void* callbackData)
{
MOZ_ASSERT(CanCompileOffThread(cx, options, buffer.length() - cursor));
return StartOffThreadDecodeScript(cx, options, buffer, cursor, callback, callbackData);
JS::TranscodeRange range(buffer.begin() + cursor, buffer.length() - cursor);
MOZ_ASSERT(CanCompileOffThread(cx, options, range.length()));
return StartOffThreadDecodeScript(cx, options, range, callback, callbackData);
}
JS_PUBLIC_API(bool)
JS::DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
const mozilla::Range<uint8_t>& range /* TranscodeRange& */,
OffThreadCompileCallback callback, void* callbackData)
{
MOZ_ASSERT(CanCompileOffThread(cx, options, range.length()));
return StartOffThreadDecodeScript(cx, options, range, callback, callbackData);
}
JS_PUBLIC_API(JSScript*)
@ -4580,6 +4590,23 @@ JS::CloneAndExecuteScript(JSContext* cx, HandleScript scriptArg,
return ExecuteScript(cx, globalLexical, script, rval.address());
}
JS_PUBLIC_API(bool)
JS::CloneAndExecuteScript(JSContext* cx, JS::AutoObjectVector& envChain,
HandleScript scriptArg,
JS::MutableHandleValue rval)
{
CHECK_REQUEST(cx);
RootedScript script(cx, scriptArg);
if (script->compartment() != cx->compartment()) {
script = CloneGlobalScript(cx, ScopeKind::NonSyntactic, script);
if (!script)
return false;
js::Debugger::onNewScript(cx, script);
}
return ExecuteScript(cx, envChain, script, rval.address());
}
static const unsigned LARGE_SCRIPT_LENGTH = 500*1024;
static bool
@ -7030,6 +7057,15 @@ JS::DecodeScript(JSContext* cx, TranscodeBuffer& buffer, JS::MutableHandleScript
return decoder.resultCode();
}
JS_PUBLIC_API(JS::TranscodeResult)
JS::DecodeScript(JSContext* cx, const TranscodeRange& range, JS::MutableHandleScript scriptp)
{
XDRDecoder decoder(cx, range);
decoder.codeScript(scriptp);
MOZ_ASSERT(bool(scriptp) == (decoder.resultCode() == TranscodeResult_Ok));
return decoder.resultCode();
}
JS_PUBLIC_API(JS::TranscodeResult)
JS::DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer,
JS::MutableHandleFunction funp,

View File

@ -4278,6 +4278,11 @@ DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
mozilla::Vector<uint8_t>& buffer /* TranscodeBuffer& */, size_t cursor,
OffThreadCompileCallback callback, void* callbackData);
extern JS_PUBLIC_API(bool)
DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
const mozilla::Range<uint8_t>& range /* TranscodeRange& */,
OffThreadCompileCallback callback, void* callbackData);
extern JS_PUBLIC_API(JSScript*)
FinishOffThreadScriptDecoder(JSContext* cx, void* token);
@ -4378,6 +4383,15 @@ extern JS_PUBLIC_API(bool)
CloneAndExecuteScript(JSContext* cx, JS::Handle<JSScript*> script,
JS::MutableHandleValue rval);
/**
* Like CloneAndExecuteScript above, but allows executing under a non-syntactic
* environment chain.
*/
extern JS_PUBLIC_API(bool)
CloneAndExecuteScript(JSContext* cx, JS::AutoObjectVector& envChain,
JS::Handle<JSScript*> script,
JS::MutableHandleValue rval);
} /* namespace JS */
namespace JS {
@ -6159,6 +6173,7 @@ class MOZ_RAII AutoHideScriptedCaller
*/
typedef mozilla::Vector<uint8_t> TranscodeBuffer;
typedef mozilla::Range<uint8_t> TranscodeRange;
enum TranscodeResult
{
@ -6188,6 +6203,9 @@ extern JS_PUBLIC_API(TranscodeResult)
DecodeScript(JSContext* cx, TranscodeBuffer& buffer, JS::MutableHandleScript scriptp,
size_t cursorIndex = 0);
extern JS_PUBLIC_API(TranscodeResult)
DecodeScript(JSContext* cx, const TranscodeRange& range, JS::MutableHandleScript scriptp);
extern JS_PUBLIC_API(TranscodeResult)
DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer, JS::MutableHandleFunction funp,
size_t cursorIndex = 0);

View File

@ -1449,6 +1449,9 @@ JSContext::findVersion()
if (compartment() && compartment()->behaviors().version() != JSVERSION_UNKNOWN)
return compartment()->behaviors().version();
if (!CurrentThreadCanAccessRuntime(runtime()))
return JSVERSION_DEFAULT;
return runtime()->defaultVersion();
}

View File

@ -0,0 +1,10 @@
// Test that Annex B function interaction with 'arguments'.
(function() {
assertEq(typeof arguments, "object");
{ function arguments() {} }
assertEq(typeof arguments, "function");
})();
if (typeof reportCompare === "function")
reportCompare(0, 0);

View File

@ -0,0 +1,14 @@
// Test that functions in block that do not exhibit Annex B do not override
// previous functions that do exhibit Annex B.
function f() {
var outerX;
{ function x() {1} outerX = x; }
{ { function x() {2}; } let x; }
{ let x; { function x() {3}; } }
assertEq(x, outerX);
}
f();
if (typeof reportCompare === "function")
reportCompare(0, 0);

View File

@ -300,25 +300,27 @@ static const JSClass parseTaskGlobalClass = {
ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData)
: kind(kind), options(cx), chars(chars), length(length),
: kind(kind), options(cx),
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
parseGlobal(parseGlobal),
callback(callback), callbackData(callbackData),
script(nullptr), sourceObject(nullptr),
overRecursed(false), outOfMemory(false)
{
data.construct<TwoByteChars>(chars, length);
}
ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
JS::TranscodeBuffer& buffer, size_t cursor,
const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData)
: kind(kind), options(cx), buffer(&buffer), cursor(cursor),
: kind(kind), options(cx),
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
parseGlobal(parseGlobal),
callback(callback), callbackData(callbackData),
script(nullptr), sourceObject(nullptr),
overRecursed(false), outOfMemory(false)
{
data.construct<const JS::TranscodeRange>(range);
}
bool
@ -385,7 +387,8 @@ ScriptParseTask::ScriptParseTask(JSContext* cx, JSObject* parseGlobal,
void
ScriptParseTask::parse(JSContext* cx)
{
SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
auto& range = data.ref<TwoByteChars>();
SourceBufferHolder srcBuf(range.begin().get(), range.length(), SourceBufferHolder::NoOwnership);
script = frontend::CompileGlobalScript(cx, alloc, ScopeKind::Global,
options, srcBuf,
/* sourceObjectOut = */ &sourceObject);
@ -402,17 +405,18 @@ ModuleParseTask::ModuleParseTask(JSContext* cx, JSObject* parseGlobal,
void
ModuleParseTask::parse(JSContext* cx)
{
SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
auto& range = data.ref<TwoByteChars>();
SourceBufferHolder srcBuf(range.begin().get(), range.length(), SourceBufferHolder::NoOwnership);
ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject);
if (module)
script = module->script();
}
ScriptDecodeTask::ScriptDecodeTask(JSContext* cx, JSObject* parseGlobal,
JS::TranscodeBuffer& buffer, size_t cursor,
const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData)
: ParseTask(ParseTaskKind::ScriptDecode, cx, parseGlobal,
buffer, cursor, callback, callbackData)
range, callback, callbackData)
{
}
@ -421,7 +425,7 @@ ScriptDecodeTask::parse(JSContext* cx)
{
RootedScript resultScript(cx);
XDROffThreadDecoder decoder(cx, alloc, &options, /* sourceObjectOut = */ &sourceObject,
*buffer, cursor);
data.ref<const JS::TranscodeRange>());
decoder.codeScript(&resultScript);
MOZ_ASSERT(bool(resultScript) == (decoder.resultCode() == JS::TranscodeResult_Ok));
if (decoder.resultCode() == JS::TranscodeResult_Ok) {
@ -653,12 +657,11 @@ js::StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& optio
bool
js::StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
JS::TranscodeBuffer& buffer, size_t cursor,
const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData)
{
auto functor = [&](JSObject* global) -> ScriptDecodeTask* {
return cx->new_<ScriptDecodeTask>(cx, global, buffer, cursor,
callback, callbackData);
return cx->new_<ScriptDecodeTask>(cx, global, range, callback, callbackData);
};
return StartOffThreadParseTask(cx, options, ParseTaskKind::ScriptDecode, functor);
}

View File

@ -15,6 +15,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/MaybeOneOf.h"
#include "mozilla/PodOperations.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Variant.h"
@ -534,7 +535,7 @@ StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
bool
StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
JS::TranscodeBuffer& buffer, size_t cursor,
const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData);
/*
@ -593,21 +594,9 @@ struct ParseTask
{
ParseTaskKind kind;
OwningCompileOptions options;
// Anonymous union, the only correct interpretation is provided by the
// ParseTaskKind value, or from the virtual parse function.
union {
struct {
const char16_t* chars;
size_t length;
};
struct {
// This should be a reference, but C++ prevents us from using union
// with references as it assumes the reference constness might be
// violated.
JS::TranscodeBuffer* const buffer;
size_t cursor;
};
};
mozilla::MaybeOneOf<const JS::TranscodeRange, JS::TwoByteChars> data;
LifoAlloc alloc;
// Rooted pointer to the global object to use while parsing.
@ -635,7 +624,7 @@ struct ParseTask
const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData);
ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
JS::TranscodeBuffer& buffer, size_t cursor,
const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData);
bool init(JSContext* cx, const ReadOnlyCompileOptions& options);
@ -671,7 +660,7 @@ struct ModuleParseTask : public ParseTask
struct ScriptDecodeTask : public ParseTask
{
ScriptDecodeTask(JSContext* cx, JSObject* parseGlobal,
JS::TranscodeBuffer& buffer, size_t cursor,
const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData);
void parse(JSContext* cx) override;
};

View File

@ -15,30 +15,35 @@
namespace js {
class XDRBuffer {
class XDRBufferBase
{
public:
XDRBuffer(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
: context_(cx), buffer_(buffer), cursor_(cursor) { }
explicit XDRBufferBase(JSContext* cx, size_t cursor = 0)
: context_(cx), cursor_(cursor) { }
JSContext* cx() const {
return context_;
}
const uint8_t* read(size_t n) {
MOZ_ASSERT(cursor_ < buffer_.length());
uint8_t* ptr = &buffer_[cursor_];
cursor_ += n;
return ptr;
size_t cursor() const {
return cursor_;
}
const char* readCString() {
char* ptr = reinterpret_cast<char*>(&buffer_[cursor_]);
uint8_t* end = reinterpret_cast<uint8_t*>(strchr(ptr, '\0')) + 1;
MOZ_ASSERT(buffer_.begin() < end);
MOZ_ASSERT(end <= buffer_.end());
cursor_ = end - buffer_.begin();
return ptr;
}
protected:
JSContext* const context_;
size_t cursor_;
};
template <XDRMode mode>
class XDRBuffer;
template <>
class XDRBuffer<XDR_ENCODE> : public XDRBufferBase
{
public:
XDRBuffer(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
: XDRBufferBase(cx, cursor),
buffer_(buffer) { }
uint8_t* write(size_t n) {
MOZ_ASSERT(n != 0);
@ -51,14 +56,55 @@ class XDRBuffer {
return ptr;
}
size_t cursor() const {
return cursor_;
const char* readCString() {
MOZ_CRASH("Should never read in encode mode");
return nullptr;
}
const uint8_t* read(size_t n) {
MOZ_CRASH("Should never read in encode mode");
return nullptr;
}
private:
JSContext* const context_;
JS::TranscodeBuffer& buffer_;
size_t cursor_;
};
template <>
class XDRBuffer<XDR_DECODE> : public XDRBufferBase
{
public:
XDRBuffer(JSContext* cx, const JS::TranscodeRange& range)
: XDRBufferBase(cx),
buffer_(range) { }
XDRBuffer(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
: XDRBufferBase(cx, cursor),
buffer_(buffer.begin(), buffer.length()) { }
const char* readCString() {
char* ptr = reinterpret_cast<char*>(&buffer_[cursor_]);
uint8_t* end = reinterpret_cast<uint8_t*>(strchr(ptr, '\0')) + 1;
MOZ_ASSERT(buffer_.begin().get() < end);
MOZ_ASSERT(end <= buffer_.end().get());
cursor_ = end - buffer_.begin().get();
return ptr;
}
const uint8_t* read(size_t n) {
MOZ_ASSERT(cursor_ < buffer_.length());
uint8_t* ptr = &buffer_[cursor_];
cursor_ += n;
return ptr;
}
uint8_t* write(size_t n) {
MOZ_CRASH("Should never write in decode mode");
return nullptr;
}
private:
const JS::TranscodeRange buffer_;
};
class XDRCoderBase;
@ -124,7 +170,7 @@ template <XDRMode mode>
class XDRState : public XDRCoderBase
{
public:
XDRBuffer buf;
XDRBuffer<mode> buf;
private:
JS::TranscodeResult resultCode_;
@ -135,6 +181,13 @@ class XDRState : public XDRCoderBase
{
}
template <typename RangeType>
XDRState(JSContext* cx, const RangeType& range)
: buf(cx, range),
resultCode_(JS::TranscodeResult_Ok)
{
}
virtual ~XDRState() {};
JSContext* cx() const {
@ -307,8 +360,8 @@ class XDROffThreadDecoder : public XDRDecoder
XDROffThreadDecoder(JSContext* cx, LifoAlloc& alloc,
const ReadOnlyCompileOptions* options,
ScriptSourceObject** sourceObjectOut,
JS::TranscodeBuffer& buffer, size_t cursor = 0)
: XDRDecoder(cx, buffer, cursor),
const JS::TranscodeRange& range)
: XDRDecoder(cx, range),
options_(options),
sourceObjectOut_(sourceObjectOut),
alloc_(alloc)

View File

@ -7098,8 +7098,7 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
ParseContext* outerpc = m.parser().pc;
Directives directives(outerpc);
FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, toStringStart, directives,
NotGenerator, SyncFunction,
/* tryAnnexB = */ false);
NotGenerator, SyncFunction);
if (!funbox)
return false;
funbox->initWithEnclosingParseContext(outerpc, frontend::Statement);

View File

@ -0,0 +1,57 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 "AutoMemMap.h"
#include "ScriptPreloader-inl.h"
#include "mozilla/Unused.h"
#include "nsIFile.h"
namespace mozilla {
namespace loader {
AutoMemMap::~AutoMemMap()
{
if (fileMap) {
if (addr) {
Unused << NS_WARN_IF(PR_MemUnmap(addr, size()) != PR_SUCCESS);
addr = nullptr;
}
Unused << NS_WARN_IF(PR_CloseFileMap(fileMap) != PR_SUCCESS);
fileMap = nullptr;
}
}
Result<Ok, nsresult>
AutoMemMap::init(nsIFile* file, int flags, int mode, PRFileMapProtect prot)
{
MOZ_ASSERT(!fd);
MOZ_ASSERT(!fileMap);
MOZ_ASSERT(!addr);
int64_t fileSize;
NS_TRY(file->GetFileSize(&fileSize));
if (fileSize > UINT32_MAX)
return Err(NS_ERROR_INVALID_ARG);
NS_TRY(file->OpenNSPRFileDesc(flags, mode, &fd.rwget()));
fileMap = PR_CreateFileMap(fd, 0, prot);
if (!fileMap)
return Err(NS_ERROR_FAILURE);
size_ = fileSize;
addr = PR_MemMap(fileMap, 0, size_);
if (!addr)
return Err(NS_ERROR_FAILURE);
return Ok();
}
} // namespace loader
} // namespace mozilla

View File

@ -0,0 +1,67 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
/* 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 loader_AutoMemMap_h
#define loader_AutoMemMap_h
#include "mozilla/FileUtils.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/RangedPtr.h"
#include "mozilla/Result.h"
#include "nsIMemoryReporter.h"
#include <prio.h>
class nsIFile;
namespace mozilla {
namespace loader {
class AutoMemMap
{
public:
AutoMemMap() = default;
~AutoMemMap();
Result<Ok, nsresult>
init(nsIFile* file, int flags = PR_RDONLY, int mode = 0,
PRFileMapProtect prot = PR_PROT_READONLY);
bool initialized() { return addr; }
uint32_t size() const { MOZ_ASSERT(fd); return size_; }
template<typename T = void>
const RangedPtr<T> get()
{
MOZ_ASSERT(addr);
return { static_cast<T*>(addr), size_ };
}
template<typename T = void>
const RangedPtr<T> get() const
{
MOZ_ASSERT(addr);
return { static_cast<T*>(addr), size_ };
}
size_t nonHeapSizeOfExcludingThis() { return size_; }
private:
AutoFDClose fd;
PRFileMap* fileMap = nullptr;
uint32_t size_ = 0;
void* addr = nullptr;
AutoMemMap(const AutoMemMap&) = delete;
void operator=(const AutoMemMap&) = delete;
};
} // namespace loader
} // namespace mozilla
#endif // loader_AutoMemMap_h

View File

@ -0,0 +1,169 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
/* 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 ScriptPreloader_inl_h
#define ScriptPreloader_inl_h
#include "mozilla/Attributes.h"
#include "mozilla/Assertions.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/Range.h"
#include "mozilla/Result.h"
#include "mozilla/Unused.h"
#include "nsString.h"
#include "nsTArray.h"
#include <prio.h>
namespace mozilla {
namespace loader {
static inline Result<Ok, nsresult>
WrapNSResult(PRStatus aRv)
{
if (aRv != PR_SUCCESS) {
return Err(NS_ERROR_FAILURE);
}
return Ok();
}
static inline Result<Ok, nsresult>
WrapNSResult(nsresult aRv)
{
if (NS_FAILED(aRv)) {
return Err(aRv);
}
return Ok();
}
#define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr))
class OutputBuffer
{
public:
OutputBuffer()
{}
uint8_t*
write(size_t size)
{
auto buf = data.AppendElements(size);
cursor_ += size;
return buf;
}
void
codeUint16(const uint16_t& val)
{
LittleEndian::writeUint16(write(sizeof val), val);
}
void
codeUint32(const uint32_t& val)
{
LittleEndian::writeUint32(write(sizeof val), val);
}
void
codeString(const nsCString& str)
{
auto len = CheckedUint16(str.Length()).value();
codeUint16(len);
memcpy(write(len), str.BeginReading(), len);
}
size_t cursor() const { return cursor_; }
uint8_t* Get() { return data.Elements(); }
const uint8_t* Get() const { return data.Elements(); }
private:
nsTArray<uint8_t> data;
size_t cursor_ = 0;
};
class InputBuffer
{
public:
explicit InputBuffer(const Range<uint8_t>& buffer)
: data(buffer)
{}
const uint8_t*
read(size_t size)
{
MOZ_ASSERT(checkCapacity(size));
auto buf = &data[cursor_];
cursor_ += size;
return buf;
}
bool
codeUint16(uint16_t& val)
{
if (checkCapacity(sizeof val)) {
val = LittleEndian::readUint16(read(sizeof val));
}
return !error_;
}
bool
codeUint32(uint32_t& val)
{
if (checkCapacity(sizeof val)) {
val = LittleEndian::readUint32(read(sizeof val));
}
return !error_;
}
bool
codeString(nsCString& str)
{
uint16_t len;
if (codeUint16(len)) {
if (checkCapacity(len)) {
str.SetLength(len);
memcpy(str.BeginWriting(), read(len), len);
}
}
return !error_;
}
bool error() { return error_; }
bool finished() { return error_ || !remainingCapacity(); }
size_t remainingCapacity() { return data.length() - cursor_; }
size_t cursor() const { return cursor_; }
const uint8_t* Get() const { return data.begin().get(); }
private:
bool
checkCapacity(size_t size)
{
if (size > remainingCapacity()) {
error_ = true;
}
return !error_;
}
bool error_ = false;
public:
const Range<uint8_t>& data;
size_t cursor_ = 0;
};
}; // namespace loader
}; // namespace mozilla
#endif // ScriptPreloader_inl_h

View File

@ -0,0 +1,737 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 "mozilla/ScriptPreloader.h"
#include "ScriptPreloader-inl.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/FileUtils.h"
#include "mozilla/Logging.h"
#include "mozilla/Services.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/ScriptSettings.h"
#include "MainThreadUtils.h"
#include "nsDebug.h"
#include "nsDirectoryServiceUtils.h"
#include "nsIFile.h"
#include "nsIObserverService.h"
#include "nsJSUtils.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "xpcpublic.h"
#define DELAYED_STARTUP_TOPIC "browser-delayed-startup-finished"
#define CLEANUP_TOPIC "xpcom-shutdown"
#define SHUTDOWN_TOPIC "quit-application-granted"
#define CACHE_FLUSH_TOPIC "startupcache-invalidate"
namespace mozilla {
namespace {
static LazyLogModule gLog("ScriptPreloader");
#define LOG(level, ...) MOZ_LOG(gLog, LogLevel::level, (__VA_ARGS__))
}
using mozilla::dom::AutoJSAPI;
using namespace mozilla::loader;
nsresult
ScriptPreloader::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize)
{
MOZ_COLLECT_REPORT(
"explicit/script-preloader/heap/saved-scripts", KIND_HEAP, UNITS_BYTES,
SizeOfLinkedList(mSavedScripts, MallocSizeOf),
"Memory used to hold the scripts which have been executed in this "
"session, and will be written to the startup script cache file.");
MOZ_COLLECT_REPORT(
"explicit/script-preloader/heap/restored-scripts", KIND_HEAP, UNITS_BYTES,
SizeOfLinkedList(mRestoredScripts, MallocSizeOf),
"Memory used to hold the scripts which have been restored from the "
"startup script cache file, but have not been executed in this session.");
MOZ_COLLECT_REPORT(
"explicit/script-preloader/heap/other", KIND_HEAP, UNITS_BYTES,
ShallowHeapSizeOfIncludingThis(MallocSizeOf),
"Memory used by the script cache service itself.");
MOZ_COLLECT_REPORT(
"explicit/script-preloader/non-heap/memmapped-cache", KIND_NONHEAP, UNITS_BYTES,
mCacheData.nonHeapSizeOfExcludingThis(),
"The memory-mapped startup script cache file.");
return NS_OK;
}
ScriptPreloader&
ScriptPreloader::GetSingleton()
{
static RefPtr<ScriptPreloader> singleton;
if (!singleton) {
singleton = new ScriptPreloader();
ClearOnShutdown(&singleton);
}
return *singleton;
}
namespace {
struct MOZ_RAII AutoSafeJSAPI : public AutoJSAPI
{
AutoSafeJSAPI() { Init(); }
};
static void
TraceOp(JSTracer* trc, void* data)
{
auto preloader = static_cast<ScriptPreloader*>(data);
preloader->Trace(trc);
}
} // anonymous namespace
void
ScriptPreloader::Trace(JSTracer* trc)
{
for (auto script : mSavedScripts) {
JS::TraceEdge(trc, &script->mScript, "ScriptPreloader::CachedScript.mScript");
}
for (auto script : mRestoredScripts) {
JS::TraceEdge(trc, &script->mScript, "ScriptPreloader::CachedScript.mScript");
}
}
ScriptPreloader::ScriptPreloader()
: mMonitor("[ScriptPreloader.mMonitor]")
, mSaveMonitor("[ScriptPreloader.mSaveMonitor]")
{
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
MOZ_RELEASE_ASSERT(obs);
obs->AddObserver(this, DELAYED_STARTUP_TOPIC, false);
obs->AddObserver(this, SHUTDOWN_TOPIC, false);
obs->AddObserver(this, CLEANUP_TOPIC, false);
obs->AddObserver(this, CACHE_FLUSH_TOPIC, false);
AutoSafeJSAPI jsapi;
JS_AddExtraGCRootsTracer(jsapi.cx(), TraceOp, this);
}
void
ScriptPreloader::ForceWriteCacheFile()
{
if (mSaveThread) {
MonitorAutoLock mal(mSaveMonitor);
// Unblock the save thread, so it can start saving before we get to
// XPCOM shutdown.
mal.Notify();
}
}
void
ScriptPreloader::Cleanup()
{
if (mSaveThread) {
MonitorAutoLock mal(mSaveMonitor);
while (!mSaveComplete && mSaveThread) {
mal.Wait();
}
}
mSavedScripts.clear();
mRestoredScripts.clear();
AutoSafeJSAPI jsapi;
JS_RemoveExtraGCRootsTracer(jsapi.cx(), TraceOp, this);
UnregisterWeakMemoryReporter(this);
}
void
ScriptPreloader::FlushScripts(LinkedList<CachedScript>& scripts)
{
for (auto next = scripts.getFirst(); next; ) {
auto script = next;
next = script->getNext();
// We can only purge finished scripts here. Async scripts that are
// still being parsed off-thread have a non-refcounted reference to
// this script, which needs to stay alive until they finish parsing.
if (script->mReadyToExecute) {
script->Cancel();
script->remove();
delete script;
}
}
}
void
ScriptPreloader::FlushCache()
{
MonitorAutoLock mal(mMonitor);
FlushScripts(mSavedScripts);
FlushScripts(mRestoredScripts);
// If we've already finished saving the cache at this point, start a new
// delayed save operation. This will write out an empty cache file in place
// of any cache file we've already written out this session, which will
// prevent us from falling back to the current session's cache file on the
// next startup.
if (mSaveComplete) {
mSaveComplete = false;
Unused << NS_NewNamedThread("SaveScripts",
getter_AddRefs(mSaveThread), this);
}
}
nsresult
ScriptPreloader::Observe(nsISupports* subject, const char* topic, const char16_t* data)
{
if (!strcmp(topic, DELAYED_STARTUP_TOPIC)) {
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
obs->RemoveObserver(this, DELAYED_STARTUP_TOPIC);
mStartupFinished = true;
if (XRE_IsParentProcess()) {
Unused << NS_NewNamedThread("SaveScripts",
getter_AddRefs(mSaveThread), this);
}
} else if (!strcmp(topic, SHUTDOWN_TOPIC)) {
ForceWriteCacheFile();
} else if (!strcmp(topic, CLEANUP_TOPIC)) {
Cleanup();
} else if (!strcmp(topic, CACHE_FLUSH_TOPIC)) {
FlushCache();
}
return NS_OK;
}
Result<nsCOMPtr<nsIFile>, nsresult>
ScriptPreloader::GetCacheFile(const char* leafName)
{
nsCOMPtr<nsIFile> cacheFile;
NS_TRY(mProfD->Clone(getter_AddRefs(cacheFile)));
NS_TRY(cacheFile->AppendNative(NS_LITERAL_CSTRING("startupCache")));
Unused << cacheFile->Create(nsIFile::DIRECTORY_TYPE, 0777);
NS_TRY(cacheFile->AppendNative(nsDependentCString(leafName)));
return Move(cacheFile);
}
static const uint8_t MAGIC[] = "mozXDRcache";
Result<Ok, nsresult>
ScriptPreloader::OpenCache()
{
NS_TRY(NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(mProfD)));
nsCOMPtr<nsIFile> cacheFile;
MOZ_TRY_VAR(cacheFile, GetCacheFile("scriptCache.bin"));
bool exists;
NS_TRY(cacheFile->Exists(&exists));
if (exists) {
NS_TRY(cacheFile->MoveTo(nullptr, NS_LITERAL_STRING("scriptCache-current.bin")));
} else {
NS_TRY(cacheFile->SetLeafName(NS_LITERAL_STRING("scriptCache-current.bin")));
NS_TRY(cacheFile->Exists(&exists));
if (!exists) {
return Err(NS_ERROR_FILE_NOT_FOUND);
}
}
MOZ_TRY(mCacheData.init(cacheFile));
return Ok();
}
// Opens the script cache file for this session, and initializes the script
// cache based on its contents. See WriteCache for details of the cache file.
Result<Ok, nsresult>
ScriptPreloader::InitCache()
{
mCacheInitialized = true;
RegisterWeakMemoryReporter(this);
if (!XRE_IsParentProcess()) {
return Ok();
}
MOZ_TRY(OpenCache());
auto size = mCacheData.size();
uint32_t headerSize;
if (size < sizeof(MAGIC) + sizeof(headerSize)) {
return Err(NS_ERROR_UNEXPECTED);
}
auto data = mCacheData.get<uint8_t>();
auto end = data + size;
if (memcmp(MAGIC, data.get(), sizeof(MAGIC))) {
return Err(NS_ERROR_UNEXPECTED);
}
data += sizeof(MAGIC);
headerSize = LittleEndian::readUint32(data.get());
data += sizeof(headerSize);
if (data + headerSize > end) {
return Err(NS_ERROR_UNEXPECTED);
}
{
AutoCleanLinkedList<CachedScript> scripts;
Range<uint8_t> header(data, data + headerSize);
data += headerSize;
InputBuffer buf(header);
size_t offset = 0;
while (!buf.finished()) {
auto script = MakeUnique<CachedScript>(buf);
auto scriptData = data + script->mOffset;
if (scriptData + script->mSize > end) {
return Err(NS_ERROR_UNEXPECTED);
}
// Make sure offsets match what we'd expect based on script ordering and
// size, as a basic sanity check.
if (script->mOffset != offset) {
return Err(NS_ERROR_UNEXPECTED);
}
offset += script->mSize;
script->mXDRRange.emplace(scriptData, scriptData + script->mSize);
scripts.insertBack(script.release());
}
if (buf.error()) {
return Err(NS_ERROR_UNEXPECTED);
}
for (auto script : scripts) {
mScripts.Put(script->mCachePath, script);
}
mRestoredScripts = Move(scripts);
}
AutoJSAPI jsapi;
MOZ_RELEASE_ASSERT(jsapi.Init(xpc::CompilationScope()));
JSContext* cx = jsapi.cx();
auto start = TimeStamp::Now();
LOG(Info, "Off-thread decoding scripts...\n");
JS::CompileOptions options(cx, JSVERSION_LATEST);
for (auto script : mRestoredScripts) {
if (script->mSize > MIN_OFFTHREAD_SIZE &&
JS::CanCompileOffThread(cx, options, script->mSize)) {
DecodeScriptOffThread(cx, script);
} else {
script->mReadyToExecute = true;
}
}
LOG(Info, "Initialized decoding in %fms\n",
(TimeStamp::Now() - start).ToMilliseconds());
return Ok();
}
static inline Result<Ok, nsresult>
Write(PRFileDesc* fd, const void* data, int32_t len)
{
if (PR_Write(fd, data, len) != len) {
return Err(NS_ERROR_FAILURE);
}
return Ok();
}
void
ScriptPreloader::PrepareCacheWrite()
{
MOZ_ASSERT(NS_IsMainThread());
if (mDataPrepared) {
return;
}
if (mRestoredScripts.isEmpty()) {
// Check for any new scripts that we need to save. If there aren't
// any, and there aren't any saved scripts that we need to remove,
// don't bother writing out a new cache file.
bool found = false;
for (auto script : mSavedScripts) {
if (script->mXDRRange.isNothing()) {
found = true;
break;
}
}
if (!found) {
mSaveComplete = true;
return;
}
}
AutoSafeJSAPI jsapi;
LinkedList<CachedScript> asyncScripts;
for (CachedScript* next = mSavedScripts.getFirst(); next; ) {
CachedScript* script = next;
next = script->getNext();
if (!script->mSize && !script->XDREncode(jsapi.cx())) {
script->remove();
delete script;
} else {
script->mSize = script->Range().length();
if (script->mSize > MIN_OFFTHREAD_SIZE) {
script->remove();
asyncScripts.insertBack(script);
}
}
}
// Store async-decoded scripts contiguously, since they're loaded
// immediately at startup.
while (CachedScript* s = asyncScripts.popLast()) {
mSavedScripts.insertFront(s);
}
mDataPrepared = true;
}
// Writes out a script cache file for the scripts accessed during early
// startup in this session. The cache file is a little-endian binary file with
// the following format:
//
// - A uint32 containing the size of the header block.
//
// - A header entry for each file stored in the cache containing:
// - The URL that the script was originally read from.
// - Its cache key.
// - The offset of its XDR data within the XDR data block.
// - The size of its XDR data in the XDR data block.
//
// - A block of XDR data for the encoded scripts, with each script's data at
// an offset from the start of the block, as specified above.
Result<Ok, nsresult>
ScriptPreloader::WriteCache()
{
MOZ_ASSERT(!NS_IsMainThread());
if (!mDataPrepared && !mSaveComplete) {
MonitorAutoUnlock mau(mSaveMonitor);
NS_DispatchToMainThread(
NewRunnableMethod(this, &ScriptPreloader::PrepareCacheWrite),
NS_DISPATCH_SYNC);
}
if (mSaveComplete) {
// If we don't have anything we need to save, we're done.
return Ok();
}
nsCOMPtr<nsIFile> cacheFile;
MOZ_TRY_VAR(cacheFile, GetCacheFile("scriptCache-new.bin"));
bool exists;
NS_TRY(cacheFile->Exists(&exists));
if (exists) {
NS_TRY(cacheFile->Remove(false));
}
AutoFDClose fd;
NS_TRY(cacheFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 0644, &fd.rwget()));
OutputBuffer buf;
size_t offset = 0;
for (auto script : mSavedScripts) {
script->mOffset = offset;
script->Code(buf);
offset += script->mSize;
}
uint8_t headerSize[4];
LittleEndian::writeUint32(headerSize, buf.cursor());
MOZ_TRY(Write(fd, MAGIC, sizeof(MAGIC)));
MOZ_TRY(Write(fd, headerSize, sizeof(headerSize)));
MOZ_TRY(Write(fd, buf.Get(), buf.cursor()));
for (auto script : mSavedScripts) {
MOZ_TRY(Write(fd, script->Range().begin().get(), script->mSize));
script->mXDRData.reset();
}
NS_TRY(cacheFile->MoveTo(nullptr, NS_LITERAL_STRING("scriptCache.bin")));
return Ok();
}
// Runs in the mSaveThread thread, and writes out the cache file for the next
// session after a reasonable delay.
nsresult
ScriptPreloader::Run()
{
MonitorAutoLock mal(mSaveMonitor);
// Ideally wait about 10 seconds before saving, to avoid unnecessary IO
// during early startup.
mal.Wait(10000);
Unused << WriteCache();
mSaveComplete = true;
NS_ReleaseOnMainThread(mSaveThread.forget());
mal.NotifyAll();
return NS_OK;
}
/* static */ ScriptPreloader::CachedScript*
ScriptPreloader::FindScript(LinkedList<CachedScript>& scripts, const nsCString& cachePath)
{
for (auto script : scripts) {
if (script->mCachePath == cachePath) {
return script;
}
}
return nullptr;
}
void
ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
JS::HandleScript script)
{
if (mStartupFinished || !mCacheInitialized) {
return;
}
// Don't bother trying to cache any URLs with cache-busting query
// parameters.
if (cachePath.FindChar('?') >= 0) {
return;
}
// Don't bother caching files that belong to the mochitest harness.
NS_NAMED_LITERAL_CSTRING(mochikitPrefix, "chrome://mochikit/");
if (StringHead(url, mochikitPrefix.Length()) == mochikitPrefix) {
return;
}
bool exists = mScripts.Get(cachePath);
CachedScript* restored = nullptr;
if (exists) {
restored = FindScript(mRestoredScripts, cachePath);
}
if (restored) {
restored->remove();
mSavedScripts.insertBack(restored);
MOZ_ASSERT(script);
restored->mScript = script;
restored->mReadyToExecute = true;
} else if (!exists) {
auto cachedScript = new CachedScript(url, cachePath, script);
mSavedScripts.insertBack(cachedScript);
mScripts.Put(cachePath, cachedScript);
}
}
JSScript*
ScriptPreloader::GetCachedScript(JSContext* cx, const nsCString& path)
{
auto script = mScripts.Get(path);
if (script) {
return WaitForCachedScript(cx, script);
}
return nullptr;
}
JSScript*
ScriptPreloader::WaitForCachedScript(JSContext* cx, CachedScript* script)
{
if (!script->mReadyToExecute) {
LOG(Info, "Must wait for async script load: %s\n", script->mURL.get());
auto start = TimeStamp::Now();
MonitorAutoLock mal(mMonitor);
if (!script->mReadyToExecute && script->mSize < MAX_MAINTHREAD_DECODE_SIZE) {
LOG(Info, "Script is small enough to recompile on main thread\n");
script->mReadyToExecute = true;
} else {
while (!script->mReadyToExecute) {
mal.Wait();
}
}
LOG(Info, "Waited %fms\n", (TimeStamp::Now() - start).ToMilliseconds());
}
return script->GetJSScript(cx);
}
void
ScriptPreloader::DecodeScriptOffThread(JSContext* cx, CachedScript* script)
{
JS::CompileOptions options(cx, JSVERSION_LATEST);
options.setNoScriptRval(true)
.setFileAndLine(script->mURL.get(), 1);
if (!JS::DecodeOffThreadScript(cx, options, script->Range(),
OffThreadDecodeCallback,
static_cast<void*>(script))) {
script->mReadyToExecute = true;
}
}
void
ScriptPreloader::CancelOffThreadParse(void* token)
{
AutoSafeJSAPI jsapi;
JS::CancelOffThreadScriptDecoder(jsapi.cx(), token);
}
/* static */ void
ScriptPreloader::OffThreadDecodeCallback(void* token, void* context)
{
auto script = static_cast<CachedScript*>(context);
MonitorAutoLock mal(GetSingleton().mMonitor);
if (script->mReadyToExecute) {
// We've already executed this script on the main thread, and opted to
// main thread decode it rather waiting for off-thread decoding to
// finish. So just cancel the off-thread parse rather than completing
// it.
NS_DispatchToMainThread(
NewRunnableMethod<void*>(&GetSingleton(),
&ScriptPreloader::CancelOffThreadParse,
token));
return;
}
script->mToken = token;
script->mReadyToExecute = true;
mal.NotifyAll();
}
inline
ScriptPreloader::CachedScript::CachedScript(InputBuffer& buf)
{
Code(buf);
}
bool
ScriptPreloader::CachedScript::XDREncode(JSContext* cx)
{
JSAutoCompartment ac(cx, mScript);
JS::RootedScript jsscript(cx, mScript);
mXDRData.emplace();
JS::TranscodeResult code = JS::EncodeScript(cx, Data(), jsscript);
if (code == JS::TranscodeResult_Ok) {
mXDRRange.emplace(Data().begin(), Data().length());
return true;
}
JS_ClearPendingException(cx);
return false;
}
void
ScriptPreloader::CachedScript::Cancel()
{
if (mToken) {
GetSingleton().mMonitor.AssertCurrentThreadOwns();
AutoSafeJSAPI jsapi;
JS::CancelOffThreadScriptDecoder(jsapi.cx(), mToken);
mReadyToExecute = true;
mToken = nullptr;
}
}
JSScript*
ScriptPreloader::CachedScript::GetJSScript(JSContext* cx)
{
MOZ_ASSERT(mReadyToExecute);
if (mScript) {
return mScript;
}
// If we have no token at this point, the script was too small to decode
// off-thread, or it was needed before the off-thread compilation was
// finished, and is small enough to decode on the main thread rather than
// wait for the off-thread decoding to finish. In either case, we decode
// it synchronously the first time it's needed.
if (!mToken) {
MOZ_ASSERT(mXDRRange.isSome());
JS::RootedScript script(cx);
if (JS::DecodeScript(cx, Range(), &script)) {
mScript = script;
}
return mScript;
}
Maybe<JSAutoCompartment> ac;
if (JS::CompartmentCreationOptionsRef(cx).addonIdOrNull()) {
// Make sure we never try to finish the parse in a compartment with an
// add-on ID, it wasn't started in one.
ac.emplace(cx, xpc::CompilationScope());
}
mScript = JS::FinishOffThreadScriptDecoder(cx, mToken);
mToken = nullptr;
return mScript;
}
NS_IMPL_ISUPPORTS(ScriptPreloader, nsIObserver, nsIRunnable, nsIMemoryReporter)
#undef LOG
} // namespace mozilla

View File

@ -0,0 +1,292 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
/* 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 ScriptPreloader_h
#define ScriptPreloader_h
#include "mozilla/LinkedList.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Maybe.h"
#include "mozilla/Monitor.h"
#include "mozilla/Range.h"
#include "mozilla/Vector.h"
#include "mozilla/Result.h"
#include "mozilla/loader/AutoMemMap.h"
#include "nsDataHashtable.h"
#include "nsIFile.h"
#include "nsIMemoryReporter.h"
#include "nsIObserver.h"
#include "nsIThread.h"
#include "jsapi.h"
#include <prio.h>
namespace mozilla {
namespace loader {
class InputBuffer;
}
using namespace mozilla::loader;
class ScriptPreloader : public nsIObserver
, public nsIMemoryReporter
, public nsIRunnable
{
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSIMEMORYREPORTER
NS_DECL_NSIRUNNABLE
static ScriptPreloader& GetSingleton();
// Retrieves the script with the given cache key from the script cache.
// Returns null if the script is not cached.
JSScript* GetCachedScript(JSContext* cx, const nsCString& name);
// Notes the execution of a script with the given URL and cache key.
// Depending on the stage of startup, the script may be serialized and
// stored to the startup script cache.
void NoteScript(const nsCString& url, const nsCString& cachePath, JS::HandleScript script);
// Initializes the script cache from the startup script cache file.
Result<Ok, nsresult> InitCache();
void Trace(JSTracer* trc);
protected:
virtual ~ScriptPreloader() = default;
private:
// Represents a cached JS script, either initially read from the script
// cache file, to be added to the next session's script cache file, or
// both.
//
// A script which was read from the cache file may be in any of the
// following states:
//
// - Read from the cache, and being compiled off thread. In this case,
// mReadyToExecute is false, and mToken is null.
// - Off-thread compilation has finished, but the script has not yet been
// executed. In this case, mReadyToExecute is true, and mToken has a non-null
// value.
// - Read from the cache, but too small or needed to immediately to be
// compiled off-thread. In this case, mReadyToExecute is true, and both mToken
// and mScript are null.
// - Fully decoded, and ready to be added to the next session's cache
// file. In this case, mReadyToExecute is true, and mScript is non-null.
//
// A script to be added to the next session's cache file always has a
// non-null mScript value. If it was read from the last session's cache
// file, it also has a non-empty mXDRRange range, which will be stored in
// the next session's cache file. If it was compiled in this session, its
// mXDRRange will initially be empty, and its mXDRData buffer will be
// populated just before it is written to the cache file.
class CachedScript : public LinkedListElement<CachedScript>
{
public:
CachedScript(CachedScript&&) = default;
CachedScript(const nsCString& url, const nsCString& cachePath, JSScript* script)
: mURL(url)
, mCachePath(cachePath)
, mScript(script)
, mReadyToExecute(true)
{}
explicit inline CachedScript(InputBuffer& buf);
~CachedScript()
{
auto& cache = GetSingleton();
#ifdef DEBUG
auto hashValue = cache.mScripts.Get(mCachePath);
MOZ_ASSERT_IF(hashValue, hashValue == this);
#endif
cache.mScripts.Remove(mCachePath);
}
void Cancel();
// Encodes this script into XDR data, and stores the result in mXDRData.
// Returns true on success, false on failure.
bool XDREncode(JSContext* cx);
// Encodes or decodes this script, in the storage format required by the
// script cache file.
template<typename Buffer>
void Code(Buffer& buffer)
{
buffer.codeString(mURL);
buffer.codeString(mCachePath);
buffer.codeUint32(mOffset);
buffer.codeUint32(mSize);
}
// Returns the XDR data generated for this script during this session. See
// mXDRData.
JS::TranscodeBuffer& Data()
{
MOZ_ASSERT(mXDRData.isSome());
return mXDRData.ref();
}
// Returns the read-only XDR data for this script. See mXDRRange.
const JS::TranscodeRange& Range()
{
MOZ_ASSERT(mXDRRange.isSome());
return mXDRRange.ref();
}
JSScript* GetJSScript(JSContext* cx);
size_t HeapSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
{
auto size = mallocSizeOf(this);
if (mXDRData.isSome()) {
size += (mXDRData->sizeOfExcludingThis(mallocSizeOf) +
mURL.SizeOfExcludingThisEvenIfShared(mallocSizeOf) +
mCachePath.SizeOfExcludingThisEvenIfShared(mallocSizeOf));
}
return size;
}
// The URL from which this script was initially read and compiled.
nsCString mURL;
// A unique identifier for this script's filesystem location, used as a
// primary cache lookup value.
nsCString mCachePath;
// The offset of this script in the cache file, from the start of the XDR
// data block.
uint32_t mOffset = 0;
// The size of this script's encoded XDR data.
uint32_t mSize = 0;
JS::Heap<JSScript*> mScript;
// True if this script is ready to be executed. This means that either the
// off-thread portion of an off-thread decode has finished, or the script
// is too small to be decoded off-thread, and may be immediately decoded
// whenever it is first executed.
bool mReadyToExecute = false;
// The off-thread decode token for a completed off-thread decode, which
// has not yet been finalized on the main thread.
void* mToken = nullptr;
// The read-only XDR data for this script, which was either read from an
// existing cache file, or generated by encoding a script which was
// compiled during this session.
Maybe<JS::TranscodeRange> mXDRRange;
// XDR data which was generated from a script compiled during this
// session, and will be written to the cache file.
Maybe<JS::TranscodeBuffer> mXDRData;
};
// There's a trade-off between the time it takes to setup an off-thread
// decode and the time we save by doing the decode off-thread. At this
// point, the setup is quite expensive, and 20K is about where we start to
// see an improvement rather than a regression.
//
// This also means that we get much better performance loading one big
// script than several small scripts, since the setup is per-script, and the
// OMT compile is almost always complete by the time we need a given script.
static constexpr int MIN_OFFTHREAD_SIZE = 20 * 1024;
// The maximum size of scripts to re-decode on the main thread if off-thread
// decoding hasn't finished yet. In practice, we don't hit this very often,
// but when we do, re-decoding some smaller scripts on the main thread gives
// the background decoding a chance to catch up without blocking the main
// thread for quite as long.
static constexpr int MAX_MAINTHREAD_DECODE_SIZE = 50 * 1024;
ScriptPreloader();
void ForceWriteCacheFile();
void Cleanup();
void FlushCache();
void FlushScripts(LinkedList<CachedScript>& scripts);
// Opens the cache file for reading.
Result<Ok, nsresult> OpenCache();
// Writes a new cache file to disk. Must not be called on the main thread.
Result<Ok, nsresult> WriteCache();
// Prepares scripts for writing to the cache, serializing new scripts to
// XDR, and calculating their size-based offsets.
void PrepareCacheWrite();
// Returns a file pointer for the cache file with the given name in the
// current profile.
Result<nsCOMPtr<nsIFile>, nsresult>
GetCacheFile(const char* leafName);
static CachedScript* FindScript(LinkedList<CachedScript>& scripts, const nsCString& cachePath);
// Waits for the given cached script to finish compiling off-thread, or
// decodes it synchronously on the main thread, as appropriate.
JSScript* WaitForCachedScript(JSContext* cx, CachedScript* script);
// Begins decoding the given script in a background thread.
void DecodeScriptOffThread(JSContext* cx, CachedScript* script);
static void OffThreadDecodeCallback(void* token, void* context);
void CancelOffThreadParse(void* token);
size_t ShallowHeapSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
{
return (mallocSizeOf(this) + mScripts.ShallowSizeOfExcludingThis(mallocSizeOf) +
mallocSizeOf(mSaveThread.get()) + mallocSizeOf(mProfD.get()));
}
template<typename T>
static size_t SizeOfLinkedList(LinkedList<T>& list, mozilla::MallocSizeOf mallocSizeOf)
{
size_t size = 0;
for (auto elem : list) {
size += elem->HeapSizeOfIncludingThis(mallocSizeOf);
}
return size;
}
// The list of scripts executed during this session, and being saved for
// potential reuse, and to be written to the next session's cache file.
AutoCleanLinkedList<CachedScript> mSavedScripts;
// The list of scripts restored from the cache file at the start of this
// session. Scripts are removed from this list and moved to mSavedScripts
// the first time they're used during this session.
AutoCleanLinkedList<CachedScript> mRestoredScripts;
nsDataHashtable<nsCStringHashKey, CachedScript*> mScripts;
// True after we've shown the first window, and are no longer adding new
// scripts to the cache.
bool mStartupFinished = false;
bool mCacheInitialized = false;
bool mSaveComplete = false;
bool mDataPrepared = false;
nsCOMPtr<nsIFile> mProfD;
nsCOMPtr<nsIThread> mSaveThread;
// The mmapped cache data from this session's cache file.
AutoMemMap mCacheData;
Monitor mMonitor;
Monitor mSaveMonitor;
};
} // namespace mozilla
#endif // ScriptPreloader_h

View File

@ -5,9 +5,11 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
UNIFIED_SOURCES += [
'AutoMemMap.cpp',
'ChromeScriptLoader.cpp',
'mozJSLoaderUtils.cpp',
'mozJSSubScriptLoader.cpp',
'ScriptPreloader.cpp',
]
# mozJSComponentLoader.cpp cannot be built in unified mode because it uses
@ -16,10 +18,18 @@ SOURCES += [
'mozJSComponentLoader.cpp'
]
EXPORTS.mozilla += [
'ScriptPreloader.h',
]
EXPORTS.mozilla.dom += [
'PrecompiledScript.h',
]
EXPORTS.mozilla.loader += [
'AutoMemMap.h',
]
EXTRA_JS_MODULES += [
'ISO8601DateUtils.jsm',
'XPCOMUtils.jsm',

View File

@ -44,6 +44,7 @@
#include "mozilla/scache/StartupCacheUtils.h"
#include "mozilla/MacroForEach.h"
#include "mozilla/Preferences.h"
#include "mozilla/ScriptPreloader.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/Unused.h"
@ -684,7 +685,10 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo,
if (cache) {
if (!mReuseLoaderGlobal) {
rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
script = ScriptPreloader::GetSingleton().GetCachedScript(cx, cachePath);
if (!script) {
rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
}
} else {
rv = ReadCachedFunction(cache, cachePath, cx, mSystemPrincipal,
function.address());
@ -848,6 +852,10 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo,
MOZ_ASSERT(!!script != !!function);
MOZ_ASSERT(!!script == JS_IsGlobalObject(obj));
if (script) {
ScriptPreloader::GetSingleton().NoteScript(nativePath, cachePath, script);
}
if (writeToCache) {
// We successfully compiled the script, so cache it.
if (script) {
@ -890,7 +898,8 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo,
JSContext* aescx = aes.cx();
bool ok;
if (script) {
ok = JS_ExecuteScript(aescx, script);
JS::RootedValue rval(cx);
ok = JS::CloneAndExecuteScript(aescx, script, &rval);
} else {
RootedValue rval(cx);
ok = JS_CallFunction(aescx, obj, function,

View File

@ -28,6 +28,7 @@
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/HoldDropJSObjects.h"
#include "mozilla/ScriptPreloader.h"
#include "mozilla/scache/StartupCache.h"
#include "mozilla/scache/StartupCacheUtils.h"
#include "mozilla/Unused.h"
@ -193,7 +194,8 @@ EvalScript(JSContext* cx,
HandleObject targetObj,
MutableHandleValue retval,
nsIURI* uri,
bool cache,
bool startupCache,
bool preloadCache,
MutableHandleScript script,
HandleFunction function)
{
@ -207,7 +209,7 @@ EvalScript(JSContext* cx,
}
} else {
if (JS_IsGlobalObject(targetObj)) {
if (!JS_ExecuteScript(cx, script, retval)) {
if (!JS::CloneAndExecuteScript(cx, script, retval)) {
return false;
}
} else {
@ -215,7 +217,7 @@ EvalScript(JSContext* cx,
if (!envChain.append(targetObj)) {
return false;
}
if (!JS_ExecuteScript(cx, envChain, script, retval)) {
if (!JS::CloneAndExecuteScript(cx, envChain, script, retval)) {
return false;
}
}
@ -226,7 +228,7 @@ EvalScript(JSContext* cx,
return false;
}
if (cache && !!script) {
if (script && (startupCache || preloadCache)) {
nsAutoCString cachePath;
JSVersion version = JS_GetVersion(cx);
cachePath.AppendPrintf("jssubloader/%d", version);
@ -245,8 +247,16 @@ EvalScript(JSContext* cx,
return false;
}
WriteCachedScript(StartupCache::GetSingleton(),
cachePath, cx, principal, script);
nsCString uriStr;
if (preloadCache && NS_SUCCEEDED(uri->GetSpec(uriStr))) {
ScriptPreloader::GetSingleton().NoteScript(uriStr, cachePath, script);
}
if (startupCache) {
JSAutoCompartment ac(cx, script);
WriteCachedScript(StartupCache::GetSingleton(),
cachePath, cx, principal, script);
}
}
return true;
@ -400,7 +410,9 @@ AsyncScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
}
JS::Rooted<JS::Value> retval(cx);
if (EvalScript(cx, targetObj, &retval, uri, mCache, &script, function)) {
if (EvalScript(cx, targetObj, &retval, uri, mCache,
mCache && !mWantReturnValue,
&script, function)) {
autoPromise.ResolvePromise(retval);
}
@ -631,9 +643,9 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
JSAutoCompartment ac(cx, targetObj);
// Suppress caching if we're compiling as content.
StartupCache* cache = (principal == mSystemPrincipal)
? StartupCache::GetSingleton()
: nullptr;
bool ignoreCache = options.ignoreCache || principal != mSystemPrincipal;
StartupCache* cache = ignoreCache ? nullptr : StartupCache::GetSingleton();
nsCOMPtr<nsIIOService> serv = do_GetService(NS_IOSERVICE_CONTRACTID);
if (!serv) {
ReportError(cx, NS_LITERAL_CSTRING(LOAD_ERROR_NOSERVICE));
@ -686,8 +698,11 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
RootedFunction function(cx);
RootedScript script(cx);
if (cache && !options.ignoreCache) {
rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
if (NS_FAILED(rv)) {
if (!options.wantReturnValue)
script = ScriptPreloader::GetSingleton().GetCachedScript(cx, cachePath);
if (!script)
rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
if (NS_FAILED(rv) || !script) {
// ReadCachedScript may have set a pending exception.
JS_ClearPendingException(cx);
}
@ -712,7 +727,9 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
cache = nullptr;
}
Unused << EvalScript(cx, targetObj, retval, uri, !!cache, &script, function);
Unused << EvalScript(cx, targetObj, retval, uri, !!cache,
!ignoreCache && !options.wantReturnValue,
&script, function);
return NS_OK;
}

View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg">
<animate href="#x" attributeName="href" values="https://www.google.com"/>
<a id="x" fill="red"><rect x="15" y="15" width="200" height="200"/></a>
<style>
a:any-link {
fill: blue;
}
</style>
</svg>

After

Width:  |  Height:  |  Size: 260 B

View File

@ -205,6 +205,7 @@ fails-if(!stylo) == anim-strokecolor-1.svg anim-standard-ref.svg # bug 436296
== anim-targethref-7.svg anim-standard-ref.svg
== anim-targethref-8.svg anim-standard-ref.svg
== anim-targethref-9.svg anim-standard-ref.svg
== anim-targethref-10.svg anim-standard-ref.svg
== anim-text-rotate-01.svg anim-text-rotate-01-ref.svg
== anim-feFuncR-tableValues-01.svg anim-feFuncR-tableValues-01-ref.svg

View File

@ -211,6 +211,9 @@ CSS_STATE_PSEUDO_CLASS(mozMathIncrementScriptLevel,
CSS_STATE_PSEUDO_CLASS(mozAutofill, ":-moz-autofill",
CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "",
NS_EVENT_STATE_AUTOFILL)
CSS_STATE_PSEUDO_CLASS(mozAutofillPreview, ":-moz-autofill-preview",
CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "",
NS_EVENT_STATE_AUTOFILL_PREVIEW)
// CSS 3 UI
// http://www.w3.org/TR/2004/CR-css3-ui-20040511/#pseudo-classes

View File

@ -1204,6 +1204,9 @@ input[type="number"] > div > div > div:hover {
background-color: lightblue;
}
:-moz-autofill {
:-moz-autofill, :-moz-autofill-preview {
filter: grayscale(21%) brightness(88%) contrast(161%) invert(10%) sepia(40%) saturate(206%);
}
:-moz-autofill-preview {
color: GrayText;
}

View File

@ -8,6 +8,7 @@
#include "nsContentUtils.h"
#include "nsFrame.h"
#include "nsGkAtoms.h"
#include "nsIDOMMutationEvent.h"
#include "nsLiteralString.h"
#include "nsSVGEffects.h"
#include "nsSVGFilters.h"
@ -120,15 +121,20 @@ SVGFEImageFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType)
{
SVGFEImageElement *element = static_cast<SVGFEImageElement*>(mContent);
SVGFEImageElement* element = static_cast<SVGFEImageElement*>(mContent);
if (element->AttributeAffectsRendering(aNameSpaceID, aAttribute)) {
MOZ_ASSERT(GetParent()->IsSVGFilterFrame(),
"Observers observe the filter, so that's what we must invalidate");
nsSVGEffects::InvalidateDirectRenderingObservers(GetParent());
}
if ((aNameSpaceID == kNameSpaceID_XLink ||
aNameSpaceID == kNameSpaceID_None) &&
aAttribute == nsGkAtoms::href) {
// Currently our SMIL implementation does not modify the DOM attributes. Once
// we implement the SVG 2 SMIL behaviour this can be removed
// SVGFEImageElement::AfterSetAttr's implementation will be sufficient.
if (aModType == nsIDOMMutationEvent::SMIL &&
aAttribute == nsGkAtoms::href &&
(aNameSpaceID == kNameSpaceID_XLink ||
aNameSpaceID == kNameSpaceID_None)) {
bool hrefIsSet =
element->mStringAttributes[SVGFEImageElement::HREF].IsExplicitlySet() ||
element->mStringAttributes[SVGFEImageElement::XLINK_HREF]

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