mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 23:35:34 +00:00
Merge mozilla-central to autoland
This commit is contained in:
commit
51711c7e82
2
CLOBBER
2
CLOBBER
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 ?
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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|
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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", {});
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
||||
|
@ -104,7 +104,7 @@ FormAutofillParent.prototype = {
|
||||
document.getElementById("passwordsGroup");
|
||||
let insertBeforeNode = useOldOrganization ?
|
||||
document.getElementById("locationBarGroup") :
|
||||
null;
|
||||
document.getElementById("passwordGrid");
|
||||
parentNode.insertBefore(prefGroup, insertBeforeNode);
|
||||
break;
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
},
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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")) {
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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() },
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
15
extensions/permissions/moz.build
Normal file
15
extensions/permissions/moz.build
Normal 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')
|
384
extensions/permissions/nsContentBlocker.cpp
Normal file
384
extensions/permissions/nsContentBlocker.cpp
Normal 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;
|
||||
}
|
53
extensions/permissions/nsContentBlocker.h
Normal file
53
extensions/permissions/nsContentBlocker.h
Normal 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__ */
|
37
extensions/permissions/nsModuleFactory.cpp
Normal file
37
extensions/permissions/nsModuleFactory.cpp
Normal 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;
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 ||
|
||||
|
@ -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 {
|
||||
|
@ -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)) {
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
@ -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,
|
||||
|
@ -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 }
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,6 @@ UNIFIED_SOURCES += [
|
||||
'FrozenImage.cpp',
|
||||
'IDecodingTask.cpp',
|
||||
'Image.cpp',
|
||||
'ImageBlocker.cpp',
|
||||
'ImageCacheKey.cpp',
|
||||
'ImageFactory.cpp',
|
||||
'ImageOps.cpp',
|
||||
|
@ -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]
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
3
js/src/jit-test/tests/parser/bug-1161312.js
Normal file
3
js/src/jit-test/tests/parser/bug-1161312.js
Normal 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);
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
@ -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);
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
57
js/xpconnect/loader/AutoMemMap.cpp
Normal file
57
js/xpconnect/loader/AutoMemMap.cpp
Normal 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
|
67
js/xpconnect/loader/AutoMemMap.h
Normal file
67
js/xpconnect/loader/AutoMemMap.h
Normal 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
|
169
js/xpconnect/loader/ScriptPreloader-inl.h
Normal file
169
js/xpconnect/loader/ScriptPreloader-inl.h
Normal 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
|
737
js/xpconnect/loader/ScriptPreloader.cpp
Normal file
737
js/xpconnect/loader/ScriptPreloader.cpp
Normal 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
|
292
js/xpconnect/loader/ScriptPreloader.h
Normal file
292
js/xpconnect/loader/ScriptPreloader.h
Normal 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
|
@ -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',
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
9
layout/reftests/svg/smil/anim-targethref-10.svg
Normal file
9
layout/reftests/svg/smil/anim-targethref-10.svg
Normal 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 |
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user