mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
merge mozilla-inbound to mozilla-central. r=merge a=merge
MozReview-Commit-ID: Jduo3F6TzgF
This commit is contained in:
commit
484d2b7f51
@ -1,6 +1,7 @@
|
||||
[addon-manager.xpi]
|
||||
[author-email.xpi]
|
||||
[child_process.xpi]
|
||||
skip-if = true
|
||||
[chrome.xpi]
|
||||
[content-permissions.xpi]
|
||||
[content-script-messages-latency.xpi]
|
||||
|
@ -145,7 +145,6 @@ function testRegister(assert, text) {
|
||||
var channel = ios.newChannelFromURIWithLoadInfo(uri, aLoadInfo);
|
||||
|
||||
channel.originalURI = aURI;
|
||||
aLoadInfo.resultPrincipalURI = aURI;
|
||||
return channel;
|
||||
},
|
||||
getURIFlags: function(aURI) {
|
||||
|
@ -69,6 +69,8 @@ pref("extensions.screenshots.system-disabled", true);
|
||||
// Disable add-ons that are not installed by the user in all scopes by default.
|
||||
// See the SCOPE constants in AddonManager.jsm for values to use here.
|
||||
pref("extensions.autoDisableScopes", 15);
|
||||
// Scopes to scan for changes at startup.
|
||||
pref("extensions.startupScanScopes", 0);
|
||||
|
||||
// This is where the profiler WebExtension API will look for breakpad symbols.
|
||||
// NOTE: deliberately http right now since https://symbols.mozilla.org is not supported.
|
||||
|
@ -1143,6 +1143,11 @@ addEventListener("DOMContentLoaded", function onDCL() {
|
||||
gBrowser.updateBrowserRemoteness(initBrowser, gMultiProcessBrowser);
|
||||
});
|
||||
|
||||
let _resolveDelayedStartup;
|
||||
var delayedStartupPromise = new Promise(resolve => {
|
||||
_resolveDelayedStartup = resolve;
|
||||
});
|
||||
|
||||
var gBrowserInit = {
|
||||
delayedStartupFinished: false,
|
||||
|
||||
@ -1605,6 +1610,7 @@ var gBrowserInit = {
|
||||
|
||||
this.delayedStartupFinished = true;
|
||||
|
||||
_resolveDelayedStartup();
|
||||
Services.obs.notifyObservers(window, "browser-delayed-startup-finished");
|
||||
TelemetryTimestamps.add("delayedStartupFinished");
|
||||
},
|
||||
|
@ -186,7 +186,7 @@ var checkDTD = async function(aURISpec) {
|
||||
// https://hg.mozilla.org/mozilla-central/file/68c0b7d6f16ce5bb023e08050102b5f2fe4aacd8/python/compare-locales/compare_locales/parser.py#l233
|
||||
let entities = rawContents.match(/<!ENTITY\s+([\w\.]*)\s+("[^"]*"|'[^']*')\s*>/g);
|
||||
if (!entities) {
|
||||
// Some files, such as requestAutocomplete.dtd, have no entities defined.
|
||||
// Some files have no entities defined.
|
||||
return;
|
||||
}
|
||||
for (let entity of entities) {
|
||||
|
@ -1,155 +1,89 @@
|
||||
const {AddonManagerPrivate} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
|
||||
|
||||
// MockAddon mimics the AddonInternal interface and MockProvider implements
|
||||
// just enough of the AddonManager provider interface to make it look like
|
||||
// we have sideloaded webextensions so the sideloading flow can be tested.
|
||||
const {AddonTestUtils} = Cu.import("resource://testing-common/AddonTestUtils.jsm", {});
|
||||
|
||||
// MockAddon -> callback
|
||||
let setCallbacks = new Map();
|
||||
AddonTestUtils.initMochitest(this);
|
||||
|
||||
class MockAddon {
|
||||
constructor(props) {
|
||||
this._userDisabled = false;
|
||||
this.pendingOperations = 0;
|
||||
this.type = "extension";
|
||||
async function createWebExtension(details) {
|
||||
let options = {
|
||||
manifest: {
|
||||
applications: {gecko: {id: details.id}},
|
||||
|
||||
for (let name in props) {
|
||||
if (name == "userDisabled") {
|
||||
this._userDisabled = props[name];
|
||||
}
|
||||
this[name] = props[name];
|
||||
}
|
||||
name: details.name,
|
||||
|
||||
permissions: details.permissions,
|
||||
},
|
||||
};
|
||||
|
||||
if (details.iconURL) {
|
||||
options.manifest.icons = {"64": details.iconURL};
|
||||
}
|
||||
|
||||
markAsSeen() {
|
||||
this.seen = true;
|
||||
}
|
||||
let xpi = AddonTestUtils.createTempWebExtensionFile(options);
|
||||
|
||||
get userDisabled() {
|
||||
return this._userDisabled;
|
||||
}
|
||||
|
||||
set userDisabled(val) {
|
||||
this._userDisabled = val;
|
||||
AddonManagerPrivate.callAddonListeners(val ? "onDisabled" : "onEnabled", this);
|
||||
let fn = setCallbacks.get(this);
|
||||
if (fn) {
|
||||
setCallbacks.delete(this);
|
||||
fn(val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
get permissions() {
|
||||
return this._userDisabled ? AddonManager.PERM_CAN_ENABLE : AddonManager.PERM_CAN_DISABLE;
|
||||
}
|
||||
await AddonTestUtils.manuallyInstall(xpi);
|
||||
}
|
||||
|
||||
class MockProvider {
|
||||
constructor(...addons) {
|
||||
this.addons = new Set(addons);
|
||||
}
|
||||
|
||||
startup() { }
|
||||
shutdown() { }
|
||||
|
||||
getAddonByID(id, callback) {
|
||||
for (let addon of this.addons) {
|
||||
if (addon.id == id) {
|
||||
callback(addon);
|
||||
return;
|
||||
}
|
||||
}
|
||||
callback(null);
|
||||
}
|
||||
|
||||
getAddonsByTypes(types, callback) {
|
||||
let addons = [];
|
||||
if (!types || types.includes("extension")) {
|
||||
addons = [...this.addons];
|
||||
}
|
||||
callback(addons);
|
||||
}
|
||||
}
|
||||
|
||||
function promiseSetDisabled(addon) {
|
||||
return new Promise(resolve => {
|
||||
setCallbacks.set(addon, resolve);
|
||||
async function createXULExtension(details) {
|
||||
let xpi = AddonTestUtils.createTempXPIFile({
|
||||
"install.rdf": {
|
||||
id: details.id,
|
||||
name: details.name,
|
||||
version: "0.1",
|
||||
targetApplications: [{
|
||||
id: "toolkit@mozilla.org",
|
||||
minVersion: "0",
|
||||
maxVersion: "*",
|
||||
}],
|
||||
},
|
||||
});
|
||||
|
||||
await AddonTestUtils.manuallyInstall(xpi);
|
||||
}
|
||||
|
||||
let cleanup;
|
||||
|
||||
add_task(async function() {
|
||||
// ICON_URL wouldn't ever appear as an actual webextension icon, but
|
||||
// we're just mocking out the addon here, so all we care about is that
|
||||
// that it propagates correctly to the popup.
|
||||
const ICON_URL = "chrome://mozapps/skin/extensions/category-extensions.svg";
|
||||
const DEFAULT_ICON_URL = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["xpinstall.signatures.required", false],
|
||||
["extensions.autoDisableScopes", 15],
|
||||
["extensions.ui.ignoreUnsigned", true],
|
||||
],
|
||||
});
|
||||
|
||||
const ID1 = "addon1@tests.mozilla.org";
|
||||
let mock1 = new MockAddon({
|
||||
await createWebExtension({
|
||||
id: ID1,
|
||||
name: "Test 1",
|
||||
userDisabled: true,
|
||||
seen: false,
|
||||
userPermissions: {
|
||||
permissions: ["history"],
|
||||
origins: ["https://*/*"],
|
||||
},
|
||||
iconURL: ICON_URL,
|
||||
permissions: ["history", "https://*/*"],
|
||||
iconURL: "foo-icon.png",
|
||||
});
|
||||
|
||||
const ID2 = "addon2@tests.mozilla.org";
|
||||
let mock2 = new MockAddon({
|
||||
await createXULExtension({
|
||||
id: ID2,
|
||||
name: "Test 2",
|
||||
userDisabled: true,
|
||||
seen: false,
|
||||
userPermissions: {
|
||||
permissions: [],
|
||||
origins: [],
|
||||
},
|
||||
});
|
||||
|
||||
const ID3 = "addon3@tests.mozilla.org";
|
||||
let mock3 = new MockAddon({
|
||||
await createWebExtension({
|
||||
id: ID3,
|
||||
name: "Test 3",
|
||||
isWebExtension: true,
|
||||
userDisabled: true,
|
||||
seen: false,
|
||||
userPermissions: {
|
||||
permissions: [],
|
||||
origins: ["<all_urls>"],
|
||||
}
|
||||
permissions: ["<all_urls>"],
|
||||
});
|
||||
|
||||
const ID4 = "addon4@tests.mozilla.org";
|
||||
let mock4 = new MockAddon({
|
||||
await createWebExtension({
|
||||
id: ID4,
|
||||
name: "Test 4",
|
||||
isWebExtension: true,
|
||||
userDisabled: true,
|
||||
seen: false,
|
||||
userPermissions: {
|
||||
permissions: [],
|
||||
origins: ["<all_urls>"],
|
||||
}
|
||||
permissions: ["<all_urls>"],
|
||||
});
|
||||
|
||||
let provider = new MockProvider(mock1, mock2, mock3, mock4);
|
||||
AddonManagerPrivate.registerProvider(provider, [{
|
||||
id: "extension",
|
||||
name: "Extensions",
|
||||
uiPriority: 4000,
|
||||
flags: AddonManager.TYPE_UI_VIEW_LIST |
|
||||
AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL,
|
||||
}]);
|
||||
|
||||
testCleanup = async function() {
|
||||
AddonManagerPrivate.unregisterProvider(provider);
|
||||
|
||||
// clear out ExtensionsUI state about sideloaded extensions so
|
||||
// subsequent tests don't get confused.
|
||||
ExtensionsUI.sideloaded.clear();
|
||||
@ -203,17 +137,13 @@ add_task(async function() {
|
||||
is(win.gViewController.currentViewId, VIEW, "about:addons is at extensions list");
|
||||
|
||||
// Check the contents of the notification, then choose "Cancel"
|
||||
checkNotification(panel, ICON_URL, [
|
||||
checkNotification(panel, /\/foo-icon\.png$/, [
|
||||
["webextPerms.hostDescription.allUrls"],
|
||||
["webextPerms.description.history"],
|
||||
]);
|
||||
|
||||
let disablePromise = promiseSetDisabled(mock1);
|
||||
panel.secondaryButton.click();
|
||||
|
||||
let value = await disablePromise;
|
||||
is(value, true, "Addon should remain disabled");
|
||||
|
||||
let [addon1, addon2, addon3, addon4] = await AddonManager.getAddonsByIDs([ID1, ID2, ID3, ID4]);
|
||||
ok(addon1.seen, "Addon should be marked as seen");
|
||||
is(addon1.userDisabled, true, "Addon 1 should still be disabled");
|
||||
@ -245,12 +175,8 @@ add_task(async function() {
|
||||
checkNotification(panel, DEFAULT_ICON_URL, []);
|
||||
|
||||
// This time accept the install.
|
||||
disablePromise = promiseSetDisabled(mock2);
|
||||
panel.button.click();
|
||||
|
||||
value = await disablePromise;
|
||||
is(value, false, "Addon should be set to enabled");
|
||||
|
||||
[addon1, addon2, addon3, addon4] = await AddonManager.getAddonsByIDs([ID1, ID2, ID3, ID4]);
|
||||
is(addon1.userDisabled, true, "Addon 1 should still be disabled");
|
||||
is(addon2.userDisabled, false, "Addon 2 should now be enabled");
|
||||
@ -288,10 +214,7 @@ add_task(async function() {
|
||||
checkNotification(panel, DEFAULT_ICON_URL, [["webextPerms.hostDescription.allUrls"]]);
|
||||
|
||||
// Accept the permissions
|
||||
disablePromise = promiseSetDisabled(mock3);
|
||||
panel.button.click();
|
||||
value = await disablePromise;
|
||||
is(value, false, "userDisabled should be set on addon 3");
|
||||
|
||||
addon3 = await AddonManager.getAddonByID(ID3);
|
||||
is(addon3.userDisabled, false, "Addon 3 should be enabled");
|
||||
@ -316,10 +239,7 @@ add_task(async function() {
|
||||
checkNotification(panel, DEFAULT_ICON_URL, [["webextPerms.hostDescription.allUrls"]]);
|
||||
|
||||
// Accept the permissions
|
||||
disablePromise = promiseSetDisabled(mock4);
|
||||
panel.button.click();
|
||||
value = await disablePromise;
|
||||
is(value, false, "userDisabled should be set on addon 4");
|
||||
|
||||
addon4 = await AddonManager.getAddonByID(ID4);
|
||||
is(addon4.userDisabled, false, "Addon 4 should be enabled");
|
||||
@ -329,5 +249,11 @@ add_task(async function() {
|
||||
|
||||
isnot(menuButton.getAttribute("badge-status"), "addon-alert", "Should no longer have addon alert badge");
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
for (let addon of [addon1, addon2, addon3, addon4]) {
|
||||
addon.uninstall();
|
||||
}
|
||||
|
||||
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
});
|
||||
|
@ -218,7 +218,7 @@ function checkNotification(panel, checkIcon, permissions) {
|
||||
let header = document.getElementById("addon-webext-perm-intro");
|
||||
|
||||
if (checkIcon instanceof RegExp) {
|
||||
ok(checkIcon.test(icon), "Notification icon is correct");
|
||||
ok(checkIcon.test(icon), `Notification icon is correct ${JSON.stringify(icon)} ~= ${checkIcon}`);
|
||||
} else if (typeof checkIcon == "function") {
|
||||
ok(checkIcon(icon), "Notification icon is correct");
|
||||
} else {
|
||||
|
@ -172,23 +172,27 @@ AboutRedirector::NewChannel(nsIURI* aURI,
|
||||
rv = NS_NewURI(getter_AddRefs(tempURI), url);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If tempURI links to an internal URI (chrome://, resource://)
|
||||
// then set the result principal URL on the channel's load info.
|
||||
// Otherwise, we leave it null which forces the channel principal
|
||||
// to reflect the displayed URL rather than being the systemPrincipal.
|
||||
// If tempURI links to an external URI (i.e. something other than
|
||||
// chrome:// or resource://) then set the LOAD_REPLACE flag on the
|
||||
// channel which forces the channel owner to reflect the displayed
|
||||
// URL rather then being the systemPrincipal.
|
||||
bool isUIResource = false;
|
||||
rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
|
||||
&isUIResource);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsLoadFlags loadFlags = isUIResource
|
||||
? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
|
||||
: static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
|
||||
|
||||
rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
|
||||
tempURI,
|
||||
aLoadInfo);
|
||||
aLoadInfo,
|
||||
nullptr, // aLoadGroup
|
||||
nullptr, // aCallbacks
|
||||
loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (isUIResource) {
|
||||
aLoadInfo->SetResultPrincipalURI(aURI);
|
||||
}
|
||||
tempChannel->SetOriginalURI(aURI);
|
||||
|
||||
NS_ADDREF(*result = tempChannel);
|
||||
|
@ -253,7 +253,6 @@ FeedConverter.prototype = {
|
||||
let aboutFeedsURI = ios.newURI("about:feeds");
|
||||
chromeChannel = ios.newChannelFromURIWithLoadInfo(aboutFeedsURI, loadInfo);
|
||||
chromeChannel.originalURI = result.uri;
|
||||
loadInfo.resultPrincipalURI = result.uri;
|
||||
|
||||
// carry the origin attributes from the channel that loaded the feed.
|
||||
chromeChannel.owner =
|
||||
@ -561,12 +560,10 @@ GenericProtocolHandler.prototype = {
|
||||
const schemeId = this._getTelemetrySchemeId();
|
||||
Services.telemetry.getHistogramById("FEED_PROTOCOL_USAGE").add(schemeId);
|
||||
|
||||
if (channel instanceof Components.interfaces.nsIHttpChannel) {
|
||||
if (channel instanceof Components.interfaces.nsIHttpChannel)
|
||||
// Set this so we know this is supposed to be a feed
|
||||
channel.setRequestHeader("X-Moz-Is-Feed", "1", false);
|
||||
}
|
||||
channel.originalURI = aUri;
|
||||
aLoadInfo.resultPrincipalURI = aUri;
|
||||
return channel;
|
||||
},
|
||||
|
||||
|
@ -225,7 +225,7 @@ skip-if = true
|
||||
|
||||
# Disabled on OS X:
|
||||
[browser_625016.js]
|
||||
skip-if = os == "mac"
|
||||
skip-if = os == "mac" || (os == "linux" && debug) # linux, Bug 1348583
|
||||
|
||||
[browser_906076_lazy_tabs.js]
|
||||
[browser_911547.js]
|
||||
|
@ -26,7 +26,6 @@ let TestAboutPage = {
|
||||
let channel = Services.io.newChannelFromURIWithLoadInfo(newURI,
|
||||
aLoadInfo);
|
||||
channel.originalURI = aURI;
|
||||
aLoadInfo.resultPrincipalURI = aURI;
|
||||
return channel;
|
||||
},
|
||||
|
||||
|
@ -1003,7 +1003,6 @@ PdfStreamConverter.prototype = {
|
||||
|
||||
// Keep the URL the same so the browser sees it as the same.
|
||||
channel.originalURI = aRequest.URI;
|
||||
channel.loadInfo.resultPrincipalURI = aRequest.loadInfo.resultPrincipalURI;
|
||||
channel.loadGroup = aRequest.loadGroup;
|
||||
channel.loadInfo.originAttributes = aRequest.loadInfo.originAttributes;
|
||||
|
||||
|
@ -40,7 +40,6 @@ AboutPage.prototype = {
|
||||
let channel = Services.io.newChannelFromURIWithLoadInfo(newURI,
|
||||
aLoadInfo);
|
||||
channel.originalURI = aURI;
|
||||
aLoadInfo.resultPrincipalURI = aURI;
|
||||
|
||||
if (this.uriFlags & Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT) {
|
||||
let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(aURI);
|
||||
|
@ -312,7 +312,6 @@
|
||||
@RESPATH@/components/toolkit_asyncshutdown.xpt
|
||||
@RESPATH@/components/toolkit_filewatcher.xpt
|
||||
@RESPATH@/components/toolkit_finalizationwitness.xpt
|
||||
@RESPATH@/components/toolkit_formautofill.xpt
|
||||
@RESPATH@/components/toolkit_osfile.xpt
|
||||
@RESPATH@/components/toolkit_securityreporter.xpt
|
||||
@RESPATH@/components/toolkit_perfmonitoring.xpt
|
||||
@ -471,9 +470,6 @@
|
||||
@RESPATH@/components/nsFormAutoComplete.js
|
||||
@RESPATH@/components/FormHistoryStartup.js
|
||||
@RESPATH@/components/nsInputListAutoComplete.js
|
||||
@RESPATH@/components/formautofill.manifest
|
||||
@RESPATH@/components/FormAutofillContentService.js
|
||||
@RESPATH@/components/FormAutofillStartup.js
|
||||
@RESPATH@/components/contentAreaDropListener.manifest
|
||||
@RESPATH@/components/contentAreaDropListener.js
|
||||
@RESPATH@/browser/components/BrowserProfileMigrators.manifest
|
||||
|
@ -12,6 +12,8 @@ Cu.import("resource://gre/modules/EventEmitter.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
||||
"resource://gre/modules/PluralForm.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
|
||||
@ -35,7 +37,7 @@ this.ExtensionsUI = {
|
||||
sideloadListener: null,
|
||||
histogram: null,
|
||||
|
||||
init() {
|
||||
async init() {
|
||||
this.histogram = Services.telemetry.getHistogramById("EXTENSION_INSTALL_PROMPT_RESULT");
|
||||
|
||||
Services.obs.addObserver(this, "webextension-permission-prompt");
|
||||
@ -43,53 +45,55 @@ this.ExtensionsUI = {
|
||||
Services.obs.addObserver(this, "webextension-install-notify");
|
||||
Services.obs.addObserver(this, "webextension-optional-permission-prompt");
|
||||
|
||||
await RecentWindow.getMostRecentBrowserWindow().delayedStartupPromise;
|
||||
|
||||
this._checkForSideloaded();
|
||||
},
|
||||
|
||||
_checkForSideloaded() {
|
||||
AddonManager.getAllAddons(addons => {
|
||||
// Check for any side-loaded addons that the user is allowed
|
||||
// to enable.
|
||||
let sideloaded = addons.filter(
|
||||
addon => addon.seen === false && (addon.permissions & AddonManager.PERM_CAN_ENABLE));
|
||||
async _checkForSideloaded() {
|
||||
let sideloaded = await AddonManagerPrivate.getNewSideloads();
|
||||
|
||||
if (!sideloaded.length) {
|
||||
return;
|
||||
if (!sideloaded.length) {
|
||||
// No new side-loads. We're done.
|
||||
return;
|
||||
}
|
||||
|
||||
// The ordering shouldn't matter, but tests depend on notifications
|
||||
// happening in a specific order.
|
||||
sideloaded.sort((a, b) => a.id.localeCompare(b.id));
|
||||
|
||||
if (WEBEXT_PERMISSION_PROMPTS) {
|
||||
if (!this.sideloadListener) {
|
||||
this.sideloadListener = {
|
||||
onEnabled: addon => {
|
||||
if (!this.sideloaded.has(addon)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.sideloaded.delete(addon);
|
||||
this.emit("change");
|
||||
|
||||
if (this.sideloaded.size == 0) {
|
||||
AddonManager.removeAddonListener(this.sideloadListener);
|
||||
this.sideloadListener = null;
|
||||
}
|
||||
},
|
||||
};
|
||||
AddonManager.addAddonListener(this.sideloadListener);
|
||||
}
|
||||
|
||||
if (WEBEXT_PERMISSION_PROMPTS) {
|
||||
if (!this.sideloadListener) {
|
||||
this.sideloadListener = {
|
||||
onEnabled: addon => {
|
||||
if (!this.sideloaded.has(addon)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.sideloaded.delete(addon);
|
||||
this.emit("change");
|
||||
|
||||
if (this.sideloaded.size == 0) {
|
||||
AddonManager.removeAddonListener(this.sideloadListener);
|
||||
this.sideloadListener = null;
|
||||
}
|
||||
},
|
||||
};
|
||||
AddonManager.addAddonListener(this.sideloadListener);
|
||||
}
|
||||
|
||||
for (let addon of sideloaded) {
|
||||
this.sideloaded.add(addon);
|
||||
}
|
||||
this.emit("change");
|
||||
} else {
|
||||
// This and all the accompanying about:newaddon code can eventually
|
||||
// be removed. See bug 1331521.
|
||||
let win = RecentWindow.getMostRecentBrowserWindow();
|
||||
for (let addon of sideloaded) {
|
||||
win.openUILinkIn(`about:newaddon?id=${addon.id}`, "tab");
|
||||
}
|
||||
for (let addon of sideloaded) {
|
||||
this.sideloaded.add(addon);
|
||||
}
|
||||
});
|
||||
this.emit("change");
|
||||
} else {
|
||||
// This and all the accompanying about:newaddon code can eventually
|
||||
// be removed. See bug 1331521.
|
||||
let win = RecentWindow.getMostRecentBrowserWindow();
|
||||
for (let addon of sideloaded) {
|
||||
win.openUILinkIn(`about:newaddon?id=${addon.id}`, "tab");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
showAddonsManager(browser, strings, icon, histkey) {
|
||||
@ -146,6 +150,11 @@ this.ExtensionsUI = {
|
||||
}
|
||||
|
||||
info.unsigned = info.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING;
|
||||
if (info.unsigned && Cu.isInAutomation &&
|
||||
Services.prefs.getBoolPref("extensions.ui.ignoreUnsigned", false)) {
|
||||
info.unsigned = false;
|
||||
}
|
||||
|
||||
let strings = this._buildStrings(info);
|
||||
|
||||
// If this is an update with no promptable permissions, just apply it
|
||||
|
@ -168,6 +168,9 @@ def rust_triple_alias(host_or_target):
|
||||
('x86_64', 'WINNT'): 'x86_64-pc-windows-msvc',
|
||||
('x86', 'WINNT-MINGW'): 'i686-pc-windows-gnu',
|
||||
('x86_64', 'WINNT-MINGW'): 'x86_64-pc-windows-gnu',
|
||||
# Solaris
|
||||
('x86_64', 'SunOS'): 'x86_64-sun-solaris',
|
||||
('sparc64', 'SunOS'): 'sparcv9-sun-solaris',
|
||||
}.get((host_or_target.cpu, os_or_kernel), None)
|
||||
|
||||
if rustc_target is None:
|
||||
|
@ -168,11 +168,12 @@ nsChromeProtocolHandler::NewChannel2(nsIURI* aURI,
|
||||
|
||||
// Make sure that the channel remembers where it was
|
||||
// originally loaded from.
|
||||
nsLoadFlags loadFlags = 0;
|
||||
result->GetLoadFlags(&loadFlags);
|
||||
result->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
|
||||
rv = result->SetOriginalURI(aURI);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
aLoadInfo->SetResultPrincipalURI(aURI);
|
||||
|
||||
// Get a system principal for content files and set the owner
|
||||
// property of the result
|
||||
nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
|
||||
|
@ -59,6 +59,7 @@ support-files =
|
||||
[browser_dbg-iframes.js]
|
||||
[browser_dbg_keyboard_navigation.js]
|
||||
[browser_dbg_keyboard-shortcuts.js]
|
||||
skip-if = os == "linux" # bug 1351952
|
||||
[browser_dbg-pause-exceptions.js]
|
||||
[browser_dbg-navigation.js]
|
||||
[browser_dbg-pretty-print.js]
|
||||
|
@ -26,10 +26,6 @@ AboutURL.prototype = {
|
||||
newChannel: function (aURI, aLoadInfo) {
|
||||
let chan = Services.io.newChannelFromURIWithLoadInfo(this.uri, aLoadInfo);
|
||||
chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
|
||||
|
||||
// Must set the result principal URI _after_ we've created the channel
|
||||
// since the chrome protocol would overwrite it with a chrome:// URL.
|
||||
aLoadInfo.resultPrincipalURI = aURI;
|
||||
return chan;
|
||||
},
|
||||
|
||||
|
@ -106,6 +106,7 @@ skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32
|
||||
[browser_rules_cycle-angle.js]
|
||||
[browser_rules_cycle-color.js]
|
||||
[browser_rules_edit-display-grid-property.js]
|
||||
skip-if = (os == "linux") # Bug 1356214
|
||||
[browser_rules_edit-property-cancel.js]
|
||||
[browser_rules_edit-property-click.js]
|
||||
[browser_rules_edit-property-commit.js]
|
||||
|
@ -174,10 +174,10 @@ nsAboutRedirector::NewChannel(nsIURI* aURI,
|
||||
rv = NS_NewURI(getter_AddRefs(tempURI), kRedirMap[i].url);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If tempURI links to an internal URI (chrome://, resource://, about:)
|
||||
// then set the result principal URL on the channel's load info.
|
||||
// Otherwise, we leave it null which forces the channel principal
|
||||
// to reflect the displayed URL rather than being the systemPrincipal.
|
||||
// If tempURI links to an external URI (i.e. something other than
|
||||
// chrome:// or resource://) then set the LOAD_REPLACE flag on the
|
||||
// channel which forces the channel owner to reflect the displayed
|
||||
// URL rather then being the systemPrincipal.
|
||||
bool isUIResource = false;
|
||||
rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
|
||||
&isUIResource);
|
||||
@ -185,14 +185,18 @@ nsAboutRedirector::NewChannel(nsIURI* aURI,
|
||||
|
||||
bool isAboutBlank = NS_IsAboutBlank(tempURI);
|
||||
|
||||
nsLoadFlags loadFlags = isUIResource || isAboutBlank
|
||||
? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
|
||||
: static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
|
||||
|
||||
rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
|
||||
tempURI,
|
||||
aLoadInfo);
|
||||
aLoadInfo,
|
||||
nullptr, // aLoadGroup
|
||||
nullptr, // aCallbacks
|
||||
loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (isUIResource || isAboutBlank) {
|
||||
aLoadInfo->SetResultPrincipalURI(aURI);
|
||||
}
|
||||
tempChannel->SetOriginalURI(aURI);
|
||||
|
||||
tempChannel.forget(aResult);
|
||||
|
@ -11182,12 +11182,6 @@ nsDocShell::DoURILoad(nsIURI* aURI,
|
||||
|
||||
if (aOriginalURI) {
|
||||
channel->SetOriginalURI(aOriginalURI);
|
||||
// The LOAD_REPLACE flag and its handling here will be removed as part
|
||||
// of bug 1319110. For now preserve its restoration here to not break
|
||||
// any code expecting it being set specially on redirected channels.
|
||||
// If the flag has originally been set to change result of
|
||||
// NS_GetFinalChannelURI it won't have any effect and also won't cause
|
||||
// any harm.
|
||||
if (aLoadReplace) {
|
||||
uint32_t loadFlags;
|
||||
channel->GetLoadFlags(&loadFlags);
|
||||
|
@ -10511,10 +10511,13 @@ nsContentUtils::UserInteractionObserver::Init()
|
||||
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);
|
||||
// We can't register ourselves as an annotator yet, as the HangMonitor hasn't
|
||||
// started yet. It will have started by the time we have the chance to spin
|
||||
// the event loop.
|
||||
RefPtr<UserInteractionObserver> self = this;
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction([=] () {
|
||||
HangMonitor::RegisterAnnotator(*self);
|
||||
}));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1592,9 +1592,7 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JSScript*> script(cx);
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
script = ScriptPreloader::GetSingleton().GetCachedScript(cx, url);
|
||||
}
|
||||
script = ScriptPreloader::GetChildSingleton().GetCachedScript(cx, url);
|
||||
|
||||
if (!script) {
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
@ -1657,9 +1655,7 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
|
||||
uri->GetScheme(scheme);
|
||||
// We don't cache data: scripts!
|
||||
if (aShouldCache && !scheme.EqualsLiteral("data")) {
|
||||
if (XRE_IsParentProcess()) {
|
||||
ScriptPreloader::GetSingleton().NoteScript(url, url, script);
|
||||
}
|
||||
ScriptPreloader::GetChildSingleton().NoteScript(url, url, script);
|
||||
// Root the object also for caching.
|
||||
auto* holder = new nsMessageManagerScriptHolder(cx, script, aRunInGlobalScope);
|
||||
sCachedScripts->Put(aURL, holder);
|
||||
|
@ -7798,6 +7798,15 @@ class CGPerSignatureCall(CGThing):
|
||||
self.descriptor.interface.identifier.name,
|
||||
self.idlNode.identifier.name))
|
||||
if setSlot:
|
||||
if self.idlNode.isStatic():
|
||||
raise TypeError(
|
||||
"Attribute %s.%s is static, so we don't have a useful slot "
|
||||
"to cache it in, because we don't have support for that on "
|
||||
"interface objects. See "
|
||||
"https://bugzilla.mozilla.org/show_bug.cgi?id=1363870" %
|
||||
(self.descriptor.interface.identifier.name,
|
||||
self.idlNode.identifier.name))
|
||||
|
||||
# When using a slot on the Xray expando, we need to make sure that
|
||||
# our initial conversion to a JS::Value is done in the caller
|
||||
# compartment. When using a slot on our reflector, we want to do
|
||||
|
@ -883,8 +883,6 @@ nsHostObjectProtocolHandler::NewChannel2(nsIURI* uri,
|
||||
}
|
||||
|
||||
channel->SetOriginalURI(uri);
|
||||
aLoadInfo->SetResultPrincipalURI(uri);
|
||||
|
||||
channel->SetContentType(NS_ConvertUTF16toUTF8(contentType));
|
||||
channel->SetContentLength(size);
|
||||
|
||||
|
@ -1106,7 +1106,14 @@ FlyWebService::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
nsresult rv = wrapper->GetData(&innerID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Make a copy of mServers to iterate over, because closing a server
|
||||
// can remove entries from mServers.
|
||||
nsCOMArray<FlyWebPublishedServer> serversCopy;
|
||||
for (FlyWebPublishedServer* server : mServers) {
|
||||
serversCopy.AppendElement(server);
|
||||
}
|
||||
|
||||
for (FlyWebPublishedServer* server : serversCopy) {
|
||||
if (server->OwnerWindowID() == innerID) {
|
||||
server->Close();
|
||||
}
|
||||
|
@ -65,7 +65,9 @@ public:
|
||||
MOZ_ASSERT(valid.Length() == values.Length());
|
||||
|
||||
for (size_t i = 0; i < values.Length(); i++) {
|
||||
service->NewAxisMoveEvent(aID, i, values[i]);
|
||||
if (valid[i]) {
|
||||
service->NewAxisMoveEvent(aID, i, values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/EventStates.h"
|
||||
#include "mozilla/dom/AutocompleteErrorEvent.h"
|
||||
#include "mozilla/dom/nsCSPUtils.h"
|
||||
#include "mozilla/dom/nsCSPContext.h"
|
||||
#include "mozilla/dom/nsMixedContentBlocker.h"
|
||||
@ -32,7 +31,6 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsIFormAutofillContentService.h"
|
||||
#include "mozilla/BinarySearch.h"
|
||||
#include "nsQueryObject.h"
|
||||
|
||||
@ -286,31 +284,6 @@ HTMLFormElement::CheckValidity(bool* retVal)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLFormElement::RequestAutocomplete()
|
||||
{
|
||||
bool dummy;
|
||||
nsCOMPtr<nsIDOMWindow> window =
|
||||
do_QueryInterface(OwnerDoc()->GetScriptHandlingObject(dummy));
|
||||
nsCOMPtr<nsIFormAutofillContentService> formAutofillContentService =
|
||||
do_GetService("@mozilla.org/formautofill/content-service;1");
|
||||
|
||||
if (!formAutofillContentService || !window) {
|
||||
AutocompleteErrorEventInit init;
|
||||
init.mBubbles = true;
|
||||
init.mCancelable = false;
|
||||
init.mReason = AutoCompleteErrorReason::Disabled;
|
||||
|
||||
RefPtr<AutocompleteErrorEvent> event =
|
||||
AutocompleteErrorEvent::Constructor(this, NS_LITERAL_STRING("autocompleteerror"), init);
|
||||
|
||||
(new AsyncEventDispatcher(this, event))->PostDOMEvent();
|
||||
return;
|
||||
}
|
||||
|
||||
formAutofillContentService->RequestAutocomplete(this, window);
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLFormElement::ParseAttribute(int32_t aNamespaceID,
|
||||
nsIAtom* aAttribute,
|
||||
|
@ -416,8 +416,6 @@ public:
|
||||
|
||||
js::ExpandoAndGeneration mExpandoAndGeneration;
|
||||
|
||||
void RequestAutocomplete();
|
||||
|
||||
protected:
|
||||
virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
|
@ -2412,7 +2412,6 @@ nsHTMLDocument::CreateAndAddWyciwygChannel(void)
|
||||
channel->SetLoadFlags(loadFlags);
|
||||
|
||||
channel->SetOriginalURI(wcwgURI);
|
||||
loadInfo->SetResultPrincipalURI(wcwgURI);
|
||||
|
||||
rv = loadGroup->AddRequest(mWyciwygChannel, nullptr);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to add request to load group.");
|
||||
|
@ -59,6 +59,7 @@
|
||||
#include "mozilla/layers/ContentProcessController.h"
|
||||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
#include "mozilla/layout/RenderFrameChild.h"
|
||||
#include "mozilla/loader/ScriptCacheActors.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/net/CaptivePortalService.h"
|
||||
#include "mozilla/Omnijar.h"
|
||||
@ -235,6 +236,7 @@ using namespace mozilla::widget;
|
||||
using namespace mozilla::system;
|
||||
#endif
|
||||
using namespace mozilla::widget;
|
||||
using mozilla::loader::PScriptCacheChild;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -1820,6 +1822,31 @@ ContentChild::RecvPTestShellConstructor(PTestShellChild* actor)
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
PScriptCacheChild*
|
||||
ContentChild::AllocPScriptCacheChild(const FileDescOrError& cacheFile, const bool& wantCacheData)
|
||||
{
|
||||
return new loader::ScriptCacheChild();
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::DeallocPScriptCacheChild(PScriptCacheChild* cache)
|
||||
{
|
||||
delete static_cast<loader::ScriptCacheChild*>(cache);
|
||||
return true;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentChild::RecvPScriptCacheConstructor(PScriptCacheChild* actor, const FileDescOrError& cacheFile, const bool& wantCacheData)
|
||||
{
|
||||
Maybe<FileDescriptor> fd;
|
||||
if (cacheFile.type() == cacheFile.TFileDescriptor) {
|
||||
fd.emplace(cacheFile.get_FileDescriptor());
|
||||
}
|
||||
|
||||
static_cast<loader::ScriptCacheChild*>(actor)->Init(fd, wantCacheData);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
PNeckoChild*
|
||||
ContentChild::AllocPNeckoChild()
|
||||
{
|
||||
|
@ -36,6 +36,8 @@ struct LookAndFeelInt;
|
||||
namespace mozilla {
|
||||
class RemoteSpellcheckEngineChild;
|
||||
|
||||
using mozilla::loader::PScriptCacheChild;
|
||||
|
||||
namespace ipc {
|
||||
class OptionalURIParams;
|
||||
class URIParams;
|
||||
@ -241,6 +243,17 @@ public:
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvPTestShellConstructor(PTestShellChild*) override;
|
||||
|
||||
virtual PScriptCacheChild*
|
||||
AllocPScriptCacheChild(const FileDescOrError& cacheFile,
|
||||
const bool& wantCacheData) override;
|
||||
|
||||
virtual bool DeallocPScriptCacheChild(PScriptCacheChild*) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvPScriptCacheConstructor(PScriptCacheChild*,
|
||||
const FileDescOrError& cacheFile,
|
||||
const bool& wantCacheData) override;
|
||||
|
||||
jsipc::CPOWManager* GetCPOWManager() override;
|
||||
|
||||
virtual PNeckoChild* AllocPNeckoChild() override;
|
||||
|
@ -85,6 +85,7 @@
|
||||
#include "mozilla/layers/ImageBridgeParent.h"
|
||||
#include "mozilla/layers/LayerTreeOwnerTracker.h"
|
||||
#include "mozilla/layout/RenderFrameParent.h"
|
||||
#include "mozilla/loader/ScriptCacheActors.h"
|
||||
#include "mozilla/LookAndFeel.h"
|
||||
#include "mozilla/media/MediaParent.h"
|
||||
#include "mozilla/Move.h"
|
||||
@ -94,6 +95,7 @@
|
||||
#include "mozilla/ProcessHangMonitor.h"
|
||||
#include "mozilla/ProcessHangMonitorIPC.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/ScriptPreloader.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
@ -284,6 +286,7 @@ using namespace mozilla::net;
|
||||
using namespace mozilla::jsipc;
|
||||
using namespace mozilla::psm;
|
||||
using namespace mozilla::widget;
|
||||
using mozilla::loader::PScriptCacheParent;
|
||||
|
||||
// XXX Workaround for bug 986973 to maintain the existing broken semantics
|
||||
template<>
|
||||
@ -2265,15 +2268,17 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority,
|
||||
Unused << SendAppInfo(version, buildID, name, UAName, ID, vendor);
|
||||
}
|
||||
|
||||
// Initialize the message manager (and load delayed scripts) now that we
|
||||
// have established communications with the child.
|
||||
mMessageManager->InitWithCallback(this);
|
||||
|
||||
// Send the child its remote type. On Mac, this needs to be sent prior
|
||||
// to the message we send to enable the Sandbox (SendStartProcessSandbox)
|
||||
// because different remote types require different sandbox privileges.
|
||||
Unused << SendRemoteType(mRemoteType);
|
||||
|
||||
ScriptPreloader::InitContentChild(*this);
|
||||
|
||||
// Initialize the message manager (and load delayed scripts) now that we
|
||||
// have established communications with the child.
|
||||
mMessageManager->InitWithCallback(this);
|
||||
|
||||
// Set the subprocess's priority. We do this early on because we're likely
|
||||
// /lowering/ the process's CPU and memory priority, which it has inherited
|
||||
// from this process.
|
||||
@ -3121,6 +3126,19 @@ ContentParent::DeallocPTestShellParent(PTestShellParent* shell)
|
||||
return true;
|
||||
}
|
||||
|
||||
PScriptCacheParent*
|
||||
ContentParent::AllocPScriptCacheParent(const FileDescOrError& cacheFile, const bool& wantCacheData)
|
||||
{
|
||||
return new loader::ScriptCacheParent(wantCacheData);
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::DeallocPScriptCacheParent(PScriptCacheParent* cache)
|
||||
{
|
||||
delete static_cast<loader::ScriptCacheParent*>(cache);
|
||||
return true;
|
||||
}
|
||||
|
||||
PNeckoParent*
|
||||
ContentParent::AllocPNeckoParent()
|
||||
{
|
||||
|
@ -67,6 +67,8 @@ class SandboxBrokerPolicyFactory;
|
||||
|
||||
class PreallocatedProcessManagerImpl;
|
||||
|
||||
using mozilla::loader::PScriptCacheParent;
|
||||
|
||||
namespace embedding {
|
||||
class PrintingParent;
|
||||
}
|
||||
@ -911,6 +913,12 @@ private:
|
||||
|
||||
virtual bool DeallocPTestShellParent(PTestShellParent* shell) override;
|
||||
|
||||
virtual PScriptCacheParent*
|
||||
AllocPScriptCacheParent(const FileDescOrError& cacheFile,
|
||||
const bool& wantCacheData) override;
|
||||
|
||||
virtual bool DeallocPScriptCacheParent(PScriptCacheParent* shell) override;
|
||||
|
||||
virtual bool DeallocPNeckoParent(PNeckoParent* necko) override;
|
||||
|
||||
virtual PPSMContentDownloaderParent*
|
||||
|
@ -45,6 +45,7 @@ include protocol PURLClassifierLocal;
|
||||
include protocol PVRManager;
|
||||
include protocol PVideoDecoderManager;
|
||||
include protocol PFlyWebPublishedServer;
|
||||
include protocol PScriptCache;
|
||||
include DOMTypes;
|
||||
include JavaScriptTypes;
|
||||
include IPCBlob;
|
||||
@ -311,6 +312,7 @@ nested(upto inside_cpow) sync protocol PContent
|
||||
manages PFlyWebPublishedServer;
|
||||
manages PURLClassifier;
|
||||
manages PURLClassifierLocal;
|
||||
manages PScriptCache;
|
||||
|
||||
both:
|
||||
// Depending on exactly how the new browser is being created, it might be
|
||||
@ -406,6 +408,8 @@ child:
|
||||
|
||||
async PTestShell();
|
||||
|
||||
async PScriptCache(FileDescOrError cacheFile, bool wantCacheData);
|
||||
|
||||
async RegisterChrome(ChromePackage[] packages, SubstitutionMapping[] substitutions,
|
||||
OverrideMapping[] overrides, nsCString locale, bool reset);
|
||||
async RegisterChromeItem(ChromeRegistryItem item);
|
||||
|
@ -19,12 +19,16 @@ namespace mozilla {
|
||||
class MediaRawData;
|
||||
class ChromiumCDMProxy;
|
||||
|
||||
namespace eme {
|
||||
enum DecryptStatus {
|
||||
Ok = 0,
|
||||
GenericErr = 1,
|
||||
NoKeyErr = 2,
|
||||
AbortedErr = 3,
|
||||
};
|
||||
}
|
||||
|
||||
using eme::DecryptStatus;
|
||||
|
||||
struct DecryptResult {
|
||||
DecryptResult(DecryptStatus aStatus, MediaRawData* aSample)
|
||||
|
@ -18,6 +18,8 @@
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
||||
using namespace eme;
|
||||
|
||||
ChromiumCDMParent::ChromiumCDMParent(GMPContentParent* aContentParent,
|
||||
uint32_t aPluginId)
|
||||
: mPluginId(aPluginId)
|
||||
@ -1084,7 +1086,7 @@ ChromiumCDMParent::Shutdown()
|
||||
mProxy = nullptr;
|
||||
|
||||
for (RefPtr<DecryptJob>& decrypt : mDecrypts) {
|
||||
decrypt->PostResult(AbortedErr);
|
||||
decrypt->PostResult(eme::AbortedErr);
|
||||
}
|
||||
mDecrypts.Clear();
|
||||
|
||||
|
@ -557,7 +557,7 @@ ChromiumCDMProxy::Decrypt(MediaRawData* aSample)
|
||||
{
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
|
||||
if (!cdm) {
|
||||
return DecryptPromise::CreateAndReject(DecryptResult(AbortedErr, aSample),
|
||||
return DecryptPromise::CreateAndReject(DecryptResult(eme::AbortedErr, aSample),
|
||||
__func__);
|
||||
}
|
||||
RefPtr<MediaRawData> sample = aSample;
|
||||
|
@ -36,12 +36,12 @@ DecryptJob::PostResult(DecryptStatus aResult,
|
||||
if (aDecryptedData.Length() != mSample->Size()) {
|
||||
NS_WARNING("CDM returned incorrect number of decrypted bytes");
|
||||
}
|
||||
if (aResult == Ok) {
|
||||
if (aResult == eme::Ok) {
|
||||
UniquePtr<MediaRawDataWriter> writer(mSample->CreateWriter());
|
||||
PodCopy(writer->Data(),
|
||||
aDecryptedData.Elements(),
|
||||
std::min<size_t>(aDecryptedData.Length(), mSample->Size()));
|
||||
} else if (aResult == NoKeyErr) {
|
||||
} else if (aResult == eme::NoKeyErr) {
|
||||
NS_WARNING("CDM returned NoKeyErr");
|
||||
// We still have the encrypted sample, so we can re-enqueue it to be
|
||||
// decrypted again once the key is usable again.
|
||||
|
@ -500,7 +500,7 @@ GMPCDMProxy::gmp_Shutdown()
|
||||
// Abort any pending decrypt jobs, to awaken any clients waiting on a job.
|
||||
for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
|
||||
DecryptJob* job = mDecryptionJobs[i];
|
||||
job->PostResult(AbortedErr);
|
||||
job->PostResult(eme::AbortedErr);
|
||||
}
|
||||
mDecryptionJobs.Clear();
|
||||
|
||||
@ -696,7 +696,7 @@ GMPCDMProxy::gmp_Decrypt(RefPtr<DecryptJob> aJob)
|
||||
MOZ_ASSERT(IsOnOwnerThread());
|
||||
|
||||
if (!mCDM) {
|
||||
aJob->PostResult(AbortedErr);
|
||||
aJob->PostResult(eme::AbortedErr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -400,10 +400,10 @@ DecryptStatus
|
||||
ToDecryptStatus(GMPErr aError)
|
||||
{
|
||||
switch (aError) {
|
||||
case GMPNoErr: return Ok;
|
||||
case GMPNoKeyErr: return NoKeyErr;
|
||||
case GMPAbortedErr: return AbortedErr;
|
||||
default: return GenericErr;
|
||||
case GMPNoErr: return eme::Ok;
|
||||
case GMPNoKeyErr: return eme::NoKeyErr;
|
||||
case GMPAbortedErr: return eme::AbortedErr;
|
||||
default: return eme::GenericErr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,12 +127,12 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (aDecrypted.mStatus == NoKeyErr) {
|
||||
if (aDecrypted.mStatus == eme::NoKeyErr) {
|
||||
// Key became unusable after we sent the sample to CDM to decrypt.
|
||||
// Call Decode() again, so that the sample is enqueued for decryption
|
||||
// if the key becomes usable again.
|
||||
AttemptDecode(aDecrypted.mSample);
|
||||
} else if (aDecrypted.mStatus != Ok) {
|
||||
} else if (aDecrypted.mStatus != eme::Ok) {
|
||||
mDecodePromise.RejectIfExists(
|
||||
MediaResult(
|
||||
NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
|
@ -433,6 +433,7 @@ public:
|
||||
dom::SVGSVGElement* aRootElem
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: mRootElem(aRootElem)
|
||||
, mDidOverride(false)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
MOZ_ASSERT(mRootElem, "No SVG node to manage?");
|
||||
|
@ -1,23 +0,0 @@
|
||||
/* -*- Mode: IDL; 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/. */
|
||||
|
||||
enum AutoCompleteErrorReason {
|
||||
"",
|
||||
"cancel",
|
||||
"disabled",
|
||||
"invalid"
|
||||
};
|
||||
|
||||
[Pref="dom.forms.requestAutocomplete",
|
||||
Constructor(DOMString type, optional AutocompleteErrorEventInit eventInitDict)]
|
||||
interface AutocompleteErrorEvent : Event
|
||||
{
|
||||
readonly attribute AutoCompleteErrorReason reason;
|
||||
};
|
||||
|
||||
dictionary AutocompleteErrorEventInit : EventInit
|
||||
{
|
||||
AutoCompleteErrorReason reason = "";
|
||||
};
|
@ -46,7 +46,4 @@ interface HTMLFormElement : HTMLElement {
|
||||
void reset();
|
||||
boolean checkValidity();
|
||||
boolean reportValidity();
|
||||
|
||||
[Pref="dom.forms.requestAutocomplete"]
|
||||
void requestAutocomplete();
|
||||
};
|
||||
|
@ -1054,7 +1054,6 @@ if CONFIG['FUZZING']:
|
||||
GENERATED_EVENTS_WEBIDL_FILES = [
|
||||
'AddonEvent.webidl',
|
||||
'AnimationPlaybackEvent.webidl',
|
||||
'AutocompleteErrorEvent.webidl',
|
||||
'BlobEvent.webidl',
|
||||
'CaretStateChangedEvent.webidl',
|
||||
'CloseEvent.webidl',
|
||||
|
@ -2340,6 +2340,11 @@ gfxPlatform::InitWebRenderConfig()
|
||||
|
||||
if (prefEnabled) {
|
||||
featureWebRender.UserEnable("Enabled by pref");
|
||||
} else {
|
||||
const char* env = PR_GetEnv("MOZ_WEBRENDER");
|
||||
if (env && *env == '1') {
|
||||
featureWebRender.UserEnable("Enabled by envvar");
|
||||
}
|
||||
}
|
||||
|
||||
// WebRender relies on the GPU process when on Windows
|
||||
|
@ -31,9 +31,6 @@ gfxQuartzNativeDrawing::BeginNativeDrawing()
|
||||
|
||||
mNativeRect = transform.TransformBounds(mNativeRect);
|
||||
mNativeRect.RoundOut();
|
||||
// Quartz theme drawing often adjusts drawing rects, so make
|
||||
// sure our surface is big enough for that.
|
||||
mNativeRect.Inflate(5);
|
||||
if (mNativeRect.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -42,18 +39,31 @@ gfxQuartzNativeDrawing::BeginNativeDrawing()
|
||||
Factory::CreateDrawTarget(BackendType::SKIA,
|
||||
IntSize::Truncate(mNativeRect.width, mNativeRect.height),
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
|
||||
if (mTempDrawTarget) {
|
||||
transform.PostTranslate(-mNativeRect.x, -mNativeRect.y);
|
||||
mTempDrawTarget->SetTransform(transform);
|
||||
if (!mTempDrawTarget) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
transform.PostTranslate(-mNativeRect.x, -mNativeRect.y);
|
||||
mTempDrawTarget->SetTransform(transform);
|
||||
|
||||
dt = mTempDrawTarget;
|
||||
} else {
|
||||
// Clip the DT in case BorrowedCGContext needs to create a new layer.
|
||||
// This prevents it from creating a new layer the size of the window.
|
||||
mDrawTarget->PushClipRect(mNativeRect);
|
||||
}
|
||||
if (dt) {
|
||||
MOZ_ASSERT(dt->GetBackendType() == BackendType::SKIA);
|
||||
mCGContext = mBorrowedContext.Init(dt);
|
||||
MOZ_ASSERT(mCGContext);
|
||||
|
||||
MOZ_ASSERT(dt->GetBackendType() == BackendType::SKIA);
|
||||
mCGContext = mBorrowedContext.Init(dt);
|
||||
|
||||
if (NS_WARN_IF(!mCGContext)) {
|
||||
// Failed borrowing CG context, so we need to clean up.
|
||||
if (!mTempDrawTarget) {
|
||||
mDrawTarget->PopClip();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return mCGContext;
|
||||
}
|
||||
|
||||
@ -70,5 +80,7 @@ gfxQuartzNativeDrawing::EndNativeDrawing()
|
||||
mDrawTarget->SetTransform(Matrix());
|
||||
mDrawTarget->DrawSurface(source, mNativeRect,
|
||||
Rect(0, 0, mNativeRect.width, mNativeRect.height));
|
||||
} else {
|
||||
mDrawTarget->PopClip();
|
||||
}
|
||||
}
|
||||
|
@ -91,9 +91,6 @@ nsGIFDecoder2::nsGIFDecoder2(RasterImage* aImage)
|
||||
{
|
||||
// Clear out the structure, excluding the arrays.
|
||||
memset(&mGIFStruct, 0, sizeof(mGIFStruct));
|
||||
|
||||
// Initialize as "animate once" in case no NETSCAPE2.0 extension is found.
|
||||
mGIFStruct.loop_count = 1;
|
||||
}
|
||||
|
||||
nsGIFDecoder2::~nsGIFDecoder2()
|
||||
@ -111,7 +108,7 @@ nsGIFDecoder2::FinishInternal()
|
||||
if (mCurrentFrameIndex == mGIFStruct.images_decoded) {
|
||||
EndImageFrame();
|
||||
}
|
||||
PostDecodeDone(mGIFStruct.loop_count - 1);
|
||||
PostDecodeDone(mGIFStruct.loop_count);
|
||||
mGIFOpen = false;
|
||||
}
|
||||
|
||||
@ -746,6 +743,11 @@ nsGIFDecoder2::ReadNetscapeExtensionData(const char* aData)
|
||||
case NETSCAPE_LOOPING_EXTENSION_SUB_BLOCK_ID:
|
||||
// This is looping extension.
|
||||
mGIFStruct.loop_count = LittleEndian::readUint16(aData + 1);
|
||||
// Zero loop count is infinite animation loop request.
|
||||
if (mGIFStruct.loop_count == 0) {
|
||||
mGIFStruct.loop_count = -1;
|
||||
}
|
||||
|
||||
return Transition::To(State::NETSCAPE_EXTENSION_SUB_BLOCK,
|
||||
SUB_BLOCK_HEADER_LEN);
|
||||
|
||||
|
@ -323,6 +323,78 @@ GeckoChildProcessHost::GetUniqueID()
|
||||
return sNextUniqueID++;
|
||||
}
|
||||
|
||||
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
|
||||
|
||||
// This is pretty much a duplicate of the function in PluginProcessParent.
|
||||
// Simply copying for now due to uplift. I will address this duplication and for
|
||||
// example the similar code in the Constructor in bug 1339105.
|
||||
static void
|
||||
AddSandboxAllowedFile(std::vector<std::wstring>& aAllowedFiles,
|
||||
nsIProperties* aDirSvc, const char* aDirKey,
|
||||
const nsAString& aSuffix = EmptyString())
|
||||
{
|
||||
nsCOMPtr<nsIFile> ruleDir;
|
||||
nsresult rv =
|
||||
aDirSvc->Get(aDirKey, NS_GET_IID(nsIFile), getter_AddRefs(ruleDir));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString rulePath;
|
||||
rv = ruleDir->GetPath(rulePath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert network share path to format for sandbox policy.
|
||||
if (Substring(rulePath, 0, 2).Equals(L"\\\\")) {
|
||||
rulePath.InsertLiteral(u"??\\UNC", 1);
|
||||
}
|
||||
|
||||
if (!aSuffix.IsEmpty()) {
|
||||
rulePath.Append(aSuffix);
|
||||
}
|
||||
|
||||
aAllowedFiles.push_back(std::wstring(rulePath.get()));
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
AddContentSandboxAllowedFiles(int32_t aSandboxLevel,
|
||||
std::vector<std::wstring>& aAllowedFilesRead,
|
||||
std::vector<std::wstring>& aAllowedFilesReadWrite)
|
||||
{
|
||||
if (aSandboxLevel < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIProperties> dirSvc =
|
||||
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add rule to allow read / write access to content temp dir. If for some
|
||||
// reason the addition of the content temp failed, this will give write access
|
||||
// to the normal TEMP dir. However such failures should be pretty rare and
|
||||
// without this printing will not currently work.
|
||||
AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc,
|
||||
NS_APP_CONTENT_PROCESS_TEMP_DIR,
|
||||
NS_LITERAL_STRING("\\*"));
|
||||
|
||||
if (aSandboxLevel < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add rule to allow read access to installation directory. At less than
|
||||
// level 2 we already add a global read rule.
|
||||
AddSandboxAllowedFile(aAllowedFilesRead, dirSvc, NS_GRE_DIR,
|
||||
NS_LITERAL_STRING("\\*"));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
GeckoChildProcessHost::PrepareLaunch()
|
||||
{
|
||||
@ -343,6 +415,11 @@ GeckoChildProcessHost::PrepareLaunch()
|
||||
mSandboxLevel = Preferences::GetInt("security.sandbox.content.level");
|
||||
mEnableSandboxLogging =
|
||||
Preferences::GetBool("security.sandbox.logging.enabled");
|
||||
|
||||
// This calls the directory service, which can also cause issues if called
|
||||
// off main thread.
|
||||
AddContentSandboxAllowedFiles(mSandboxLevel, mAllowedFilesRead,
|
||||
mAllowedFilesReadWrite);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -663,50 +740,6 @@ AddAppDirToCommandLine(std::vector<std::string>& aCmdLine)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
|
||||
|
||||
static void
|
||||
AddContentSandboxAllowedFiles(int32_t aSandboxLevel,
|
||||
std::vector<std::wstring>& aAllowedFilesRead)
|
||||
{
|
||||
if (aSandboxLevel < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> binDir;
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(binDir));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString binDirPath;
|
||||
rv = binDir->GetPath(binDirPath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If bin directory is on a remote drive add read access.
|
||||
wchar_t volPath[MAX_PATH];
|
||||
if (!::GetVolumePathNameW(binDirPath.get(), volPath, MAX_PATH)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (::GetDriveTypeW(volPath) != DRIVE_REMOTE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert network share path to format for sandbox policy.
|
||||
if (Substring(binDirPath, 0, 2).Equals(L"\\\\")) {
|
||||
binDirPath.InsertLiteral(u"??\\UNC", 1);
|
||||
}
|
||||
|
||||
binDirPath.AppendLiteral(u"\\*");
|
||||
|
||||
aAllowedFilesRead.push_back(std::wstring(binDirPath.get()));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool
|
||||
GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts, base::ProcessArchitecture arch)
|
||||
{
|
||||
@ -1048,7 +1081,6 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
|
||||
mSandboxBroker.SetSecurityLevelForContentProcess(mSandboxLevel,
|
||||
mPrivileges);
|
||||
shouldSandboxCurrentProcess = true;
|
||||
AddContentSandboxAllowedFiles(mSandboxLevel, mAllowedFilesRead);
|
||||
}
|
||||
#endif // MOZ_CONTENT_SANDBOX
|
||||
break;
|
||||
|
@ -166,11 +166,14 @@ function treatAsSafeArgument(entry, varName, csuName)
|
||||
["Gecko_SetImageOrientationAsFromImage", "aVisibility", null],
|
||||
["Gecko_CopyImageOrientationFrom", "aDst", null],
|
||||
["Gecko_SetImageElement", "aImage", null],
|
||||
["Gecko_SetLayerImageImageValue", "aImage", null],
|
||||
["Gecko_SetUrlImageValue", "aImage", null],
|
||||
["Gecko_CopyImageValueFrom", "aImage", null],
|
||||
["Gecko_SetCursorArrayLength", "aStyleUI", null],
|
||||
["Gecko_CopyCursorArrayFrom", "aDest", null],
|
||||
["Gecko_SetCursorImageValue", "aCursor", null],
|
||||
["Gecko_SetCursorImage", "aCursor", null],
|
||||
["Gecko_SetListStyleImageImageValue", "aList", null],
|
||||
["Gecko_SetListStyleImageNone", "aList", null],
|
||||
["Gecko_SetListStyleImage", "aList", null],
|
||||
["Gecko_CopyListStyleImageFrom", "aList", null],
|
||||
@ -190,6 +193,7 @@ function treatAsSafeArgument(entry, varName, csuName)
|
||||
["Gecko_ClearAndResizeStyleContents", "aContent", null],
|
||||
[/Gecko_ClearAndResizeCounter/, "aContent", null],
|
||||
[/Gecko_CopyCounter.*?From/, "aContent", null],
|
||||
[/Gecko_SetContentDataImageValue/, "aList", null],
|
||||
[/Gecko_SetContentData/, "aContent", null],
|
||||
[/Gecko_EnsureStyle.*?ArrayLength/, "aArray", null],
|
||||
["Gecko_GetOrCreateKeyframeAtStart", "aKeyframes", null],
|
||||
@ -390,6 +394,7 @@ function ignoreContents(entry)
|
||||
"Gecko_AppendMozBorderColors",
|
||||
"Gecko_CopyMozBorderColors",
|
||||
"Gecko_SetJemallocThreadLocalArena",
|
||||
"Gecko_SetNullImageValue",
|
||||
|
||||
// Needs main thread assertions or other fixes.
|
||||
/UndisplayedMap::GetEntryFor/,
|
||||
|
38
js/src/jit-test/tests/SIMD/anyall.js
Normal file
38
js/src/jit-test/tests/SIMD/anyall.js
Normal file
@ -0,0 +1,38 @@
|
||||
load(libdir + 'simd.js');
|
||||
|
||||
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||
|
||||
function all(B, n) {
|
||||
var a = B.splat(true);
|
||||
for (var i = 0; i < n; i++) {
|
||||
var b = B.replaceLane(a, i, false);
|
||||
assertEq(B.allTrue(b), false);
|
||||
var c = B.replaceLane(b, i, true);
|
||||
assertEq(B.allTrue(c), true);
|
||||
}
|
||||
}
|
||||
|
||||
function any(B, n) {
|
||||
var a = B.splat(false);
|
||||
for (var i = 0; i < n; i++) {
|
||||
var b = B.replaceLane(a, i, true);
|
||||
assertEq(B.anyTrue(b), true);
|
||||
var c = B.replaceLane(b, i, false);
|
||||
assertEq(B.anyTrue(c), false);
|
||||
}
|
||||
}
|
||||
|
||||
function f() {
|
||||
for (var j = 0; j < 200; j++) {
|
||||
all(SIMD.Bool64x2, 2)
|
||||
any(SIMD.Bool64x2, 2)
|
||||
all(SIMD.Bool32x4, 4)
|
||||
any(SIMD.Bool32x4, 4)
|
||||
all(SIMD.Bool16x8, 8)
|
||||
any(SIMD.Bool16x8, 8)
|
||||
all(SIMD.Bool8x16, 16)
|
||||
any(SIMD.Bool8x16, 16)
|
||||
}
|
||||
}
|
||||
|
||||
f()
|
@ -799,8 +799,7 @@ BaselineCompiler::emitArgumentTypeChecks()
|
||||
frame.pushThis();
|
||||
frame.popRegsAndSync(1);
|
||||
|
||||
ICTypeMonitor_Fallback::Compiler compiler(cx, ICStubCompiler::Engine::Baseline,
|
||||
(uint32_t) 0);
|
||||
ICTypeMonitor_Fallback::Compiler compiler(cx, uint32_t(0));
|
||||
if (!emitNonOpIC(compiler.getStub(&stubSpace_)))
|
||||
return false;
|
||||
|
||||
@ -808,8 +807,7 @@ BaselineCompiler::emitArgumentTypeChecks()
|
||||
frame.pushArg(i);
|
||||
frame.popRegsAndSync(1);
|
||||
|
||||
ICTypeMonitor_Fallback::Compiler compiler(cx, ICStubCompiler::Engine::Baseline,
|
||||
i + 1);
|
||||
ICTypeMonitor_Fallback::Compiler compiler(cx, i + 1);
|
||||
if (!emitNonOpIC(compiler.getStub(&stubSpace_)))
|
||||
return false;
|
||||
}
|
||||
@ -2614,8 +2612,7 @@ BaselineCompiler::emit_JSOP_GETALIASEDVAR()
|
||||
|
||||
if (ionCompileable_) {
|
||||
// No need to monitor types if we know Ion can't compile this script.
|
||||
ICTypeMonitor_Fallback::Compiler compiler(cx, ICStubCompiler::Engine::Baseline,
|
||||
(ICMonitoredFallbackStub*) nullptr);
|
||||
ICTypeMonitor_Fallback::Compiler compiler(cx, nullptr);
|
||||
if (!emitOpIC(compiler.getStub(&stubSpace_)))
|
||||
return false;
|
||||
}
|
||||
@ -2764,8 +2761,7 @@ BaselineCompiler::emit_JSOP_GETIMPORT()
|
||||
|
||||
if (ionCompileable_) {
|
||||
// No need to monitor types if we know Ion can't compile this script.
|
||||
ICTypeMonitor_Fallback::Compiler compiler(cx, ICStubCompiler::Engine::Baseline,
|
||||
(ICMonitoredFallbackStub*) nullptr);
|
||||
ICTypeMonitor_Fallback::Compiler compiler(cx, nullptr);
|
||||
if (!emitOpIC(compiler.getStub(&stubSpace_)))
|
||||
return false;
|
||||
}
|
||||
|
@ -778,8 +778,6 @@ static bool
|
||||
DoGetElemFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_, HandleValue lhs,
|
||||
HandleValue rhs, MutableHandleValue res)
|
||||
{
|
||||
SharedStubInfo info(cx, frame, stub_->icEntry());
|
||||
|
||||
// This fallback stub may trigger debug mode toggling.
|
||||
DebugModeOSRVolatileStub<ICGetElem_Fallback*> stub(frame, stub_);
|
||||
|
||||
@ -814,8 +812,7 @@ DoGetElemFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_
|
||||
&isTemporarilyUnoptimizable, lhs, rhs, CanAttachGetter::Yes);
|
||||
if (gen.tryAttachStub()) {
|
||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||
engine, info.outerScript(cx), stub,
|
||||
&attached);
|
||||
engine, script, stub, &attached);
|
||||
if (newStub) {
|
||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||
if (gen.shouldNotePreliminaryObjectStub())
|
||||
@ -839,10 +836,8 @@ DoGetElemFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_
|
||||
return true;
|
||||
|
||||
// Add a type monitor stub for the resulting value.
|
||||
if (!stub->addMonitorStubForValue(cx, &info, res))
|
||||
{
|
||||
if (!stub->addMonitorStubForValue(cx, frame, res))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attached)
|
||||
return true;
|
||||
@ -1338,8 +1333,6 @@ static bool
|
||||
DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_,
|
||||
HandleObject envChain, MutableHandleValue res)
|
||||
{
|
||||
SharedStubInfo info(cx, frame, stub_->icEntry());
|
||||
|
||||
// This fallback stub may trigger debug mode toggling.
|
||||
DebugModeOSRVolatileStub<ICGetName_Fallback*> stub(frame, stub_);
|
||||
|
||||
@ -1361,8 +1354,7 @@ DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_
|
||||
GetNameIRGenerator gen(cx, script, pc, stub->state().mode(), envChain, name);
|
||||
if (gen.tryAttachStub()) {
|
||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||
engine, info.outerScript(cx), stub,
|
||||
&attached);
|
||||
engine, script, stub, &attached);
|
||||
if (newStub)
|
||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||
}
|
||||
@ -1387,7 +1379,7 @@ DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_
|
||||
return true;
|
||||
|
||||
// Add a type monitor stub for the resulting value.
|
||||
if (!stub->addMonitorStubForValue(cx, &info, res))
|
||||
if (!stub->addMonitorStubForValue(cx, frame, res))
|
||||
return false;
|
||||
|
||||
if (!attached)
|
||||
@ -2393,8 +2385,6 @@ static bool
|
||||
DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint32_t argc,
|
||||
Value* vp, MutableHandleValue res)
|
||||
{
|
||||
SharedStubInfo info(cx, frame, stub_->icEntry());
|
||||
|
||||
// This fallback stub may trigger debug mode toggling.
|
||||
DebugModeOSRVolatileStub<ICCall_Fallback*> stub(frame, stub_);
|
||||
|
||||
@ -2466,15 +2456,8 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint
|
||||
if (stub.invalid())
|
||||
return true;
|
||||
|
||||
// Attach a new TypeMonitor stub for this value.
|
||||
ICTypeMonitor_Fallback* typeMonFbStub = stub->fallbackMonitorStub();
|
||||
if (!typeMonFbStub->addMonitorStubForValue(cx, &info, res))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add a type monitor stub for the resulting value.
|
||||
if (!stub->addMonitorStubForValue(cx, &info, res))
|
||||
if (!stub->addMonitorStubForValue(cx, frame, res))
|
||||
return false;
|
||||
|
||||
// If 'callee' is a potential Call_StringSplit, try to attach an
|
||||
@ -2492,8 +2475,6 @@ static bool
|
||||
DoSpreadCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, Value* vp,
|
||||
MutableHandleValue res)
|
||||
{
|
||||
SharedStubInfo info(cx, frame, stub_->icEntry());
|
||||
|
||||
// This fallback stub may trigger debug mode toggling.
|
||||
DebugModeOSRVolatileStub<ICCall_Fallback*> stub(frame, stub_);
|
||||
|
||||
@ -2527,15 +2508,8 @@ DoSpreadCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_
|
||||
if (stub.invalid())
|
||||
return true;
|
||||
|
||||
// Attach a new TypeMonitor stub for this value.
|
||||
ICTypeMonitor_Fallback* typeMonFbStub = stub->fallbackMonitorStub();
|
||||
if (!typeMonFbStub->addMonitorStubForValue(cx, &info, res))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add a type monitor stub for the resulting value.
|
||||
if (!stub->addMonitorStubForValue(cx, &info, res))
|
||||
if (!stub->addMonitorStubForValue(cx, frame, res))
|
||||
return false;
|
||||
|
||||
if (!handled)
|
||||
|
@ -103,7 +103,7 @@ class ICTypeUpdate_PrimitiveSet : public TypeCheckPrimitiveSetStub
|
||||
public:
|
||||
Compiler(JSContext* cx, ICTypeUpdate_PrimitiveSet* existingStub, JSValueType type)
|
||||
: TypeCheckPrimitiveSetStub::Compiler(cx, TypeUpdate_PrimitiveSet,
|
||||
Engine::Baseline, existingStub, type)
|
||||
existingStub, type)
|
||||
{}
|
||||
|
||||
ICTypeUpdate_PrimitiveSet* updateStub() {
|
||||
@ -402,7 +402,7 @@ class ICGetElem_Fallback : public ICMonitoredFallbackStub
|
||||
ICGetElem_Fallback* stub = newStub<ICGetElem_Fallback>(space, getStubCode());
|
||||
if (!stub)
|
||||
return nullptr;
|
||||
if (!stub->initMonitoringChain(cx, space, engine_))
|
||||
if (!stub->initMonitoringChain(cx, space))
|
||||
return nullptr;
|
||||
return stub;
|
||||
}
|
||||
@ -531,7 +531,7 @@ class ICGetName_Fallback : public ICMonitoredFallbackStub
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
ICGetName_Fallback* stub = newStub<ICGetName_Fallback>(space, getStubCode());
|
||||
if (!stub || !stub->initMonitoringChain(cx, space, engine_))
|
||||
if (!stub || !stub->initMonitoringChain(cx, space))
|
||||
return nullptr;
|
||||
return stub;
|
||||
}
|
||||
@ -587,7 +587,7 @@ class ICGetIntrinsic_Fallback : public ICMonitoredFallbackStub
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
ICGetIntrinsic_Fallback* stub =
|
||||
newStub<ICGetIntrinsic_Fallback>(space, getStubCode());
|
||||
if (!stub || !stub->initMonitoringChain(cx, space, engine_))
|
||||
if (!stub || !stub->initMonitoringChain(cx, space))
|
||||
return nullptr;
|
||||
return stub;
|
||||
}
|
||||
@ -768,7 +768,7 @@ class ICCall_Fallback : public ICMonitoredFallbackStub
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
ICCall_Fallback* stub = newStub<ICCall_Fallback>(space, getStubCode());
|
||||
if (!stub || !stub->initMonitoringChain(cx, space, engine_))
|
||||
if (!stub || !stub->initMonitoringChain(cx, space))
|
||||
return nullptr;
|
||||
return stub;
|
||||
}
|
||||
|
@ -106,12 +106,111 @@ MoveResolver::findCycledMove(PendingMoveIterator* iter, PendingMoveIterator end,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef JS_CODEGEN_ARM
|
||||
static inline bool
|
||||
MoveIsDouble(const MoveOperand& move)
|
||||
{
|
||||
if (!move.isFloatReg())
|
||||
return false;
|
||||
return move.floatReg().isDouble();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef JS_CODEGEN_ARM
|
||||
static inline bool
|
||||
MoveIsSingle(const MoveOperand& move)
|
||||
{
|
||||
if (!move.isFloatReg())
|
||||
return false;
|
||||
return move.floatReg().isSingle();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef JS_CODEGEN_ARM
|
||||
bool
|
||||
MoveResolver::isDoubleAliasedAsSingle(const MoveOperand& move)
|
||||
{
|
||||
if (!MoveIsDouble(move))
|
||||
return false;
|
||||
|
||||
for (auto iter = pending_.begin(); iter != pending_.end(); ++iter) {
|
||||
PendingMove* other = *iter;
|
||||
if (other->from().aliases(move) && MoveIsSingle(other->from()))
|
||||
return true;
|
||||
if (other->to().aliases(move) && MoveIsSingle(other->to()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef JS_CODEGEN_ARM
|
||||
static MoveOperand
|
||||
SplitIntoLowerHalf(const MoveOperand& move)
|
||||
{
|
||||
if (MoveIsDouble(move)) {
|
||||
FloatRegister lowerSingle = move.floatReg().asSingle();
|
||||
return MoveOperand(lowerSingle);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(move.isMemoryOrEffectiveAddress());
|
||||
return move;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef JS_CODEGEN_ARM
|
||||
static MoveOperand
|
||||
SplitIntoUpperHalf(const MoveOperand& move)
|
||||
{
|
||||
if (MoveIsDouble(move)) {
|
||||
FloatRegister lowerSingle = move.floatReg().asSingle();
|
||||
FloatRegister upperSingle = VFPRegister(lowerSingle.code() + 1, VFPRegister::Single);
|
||||
return MoveOperand(upperSingle);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(move.isMemoryOrEffectiveAddress());
|
||||
return MoveOperand(move.base(), move.disp() + sizeof(float));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
MoveResolver::resolve()
|
||||
{
|
||||
resetState();
|
||||
orderedMoves_.clear();
|
||||
|
||||
#ifdef JS_CODEGEN_ARM
|
||||
// Some of ARM's double registers alias two of its single registers,
|
||||
// but the algorithm below assumes that every register can participate
|
||||
// in at most one cycle. To satisfy the algorithm, any double registers
|
||||
// that may conflict are split into their single-register halves.
|
||||
//
|
||||
// This logic is only applicable because ARM only uses registers d0-d15,
|
||||
// all of which alias s0-s31. Double registers d16-d31 are unused.
|
||||
// Therefore there is never a double move that cannot be split.
|
||||
// If this changes in the future, the algorithm will have to be fixed.
|
||||
for (auto iter = pending_.begin(); iter != pending_.end(); ++iter) {
|
||||
PendingMove* pm = *iter;
|
||||
|
||||
if (isDoubleAliasedAsSingle(pm->from()) || isDoubleAliasedAsSingle(pm->to())) {
|
||||
PendingMove* lower = movePool_.allocate();
|
||||
if (!lower)
|
||||
return false;
|
||||
|
||||
// Insert the new node before the current position to not affect iteration.
|
||||
MoveOperand fromLower = SplitIntoLowerHalf(pm->from());
|
||||
MoveOperand toLower = SplitIntoLowerHalf(pm->to());
|
||||
new (lower) PendingMove(fromLower, toLower, MoveOp::FLOAT32);
|
||||
pending_.insertBefore(pm, lower);
|
||||
|
||||
// Overwrite pm in place for the upper move. Iteration proceeds as normal.
|
||||
MoveOperand fromUpper = SplitIntoUpperHalf(pm->from());
|
||||
MoveOperand toUpper = SplitIntoUpperHalf(pm->to());
|
||||
pm->overwrite(fromUpper, toUpper, MoveOp::FLOAT32);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
InlineList<PendingMove> stack;
|
||||
|
||||
// This is a depth-first-search without recursion, which tries to find
|
||||
|
@ -252,6 +252,13 @@ class MoveOp
|
||||
bool aliases(const MoveOp& other) const {
|
||||
return aliases(other.from()) || aliases(other.to());
|
||||
}
|
||||
#ifdef JS_CODEGEN_ARM
|
||||
void overwrite(MoveOperand& from, MoveOperand& to, Type type) {
|
||||
from_ = from;
|
||||
to_ = to;
|
||||
type_ = type;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class MoveResolver
|
||||
@ -298,6 +305,10 @@ class MoveResolver
|
||||
// Internal reset function. Does not clear lists.
|
||||
void resetState();
|
||||
|
||||
#ifdef JS_CODEGEN_ARM
|
||||
bool isDoubleAliasedAsSingle(const MoveOperand& move);
|
||||
#endif
|
||||
|
||||
public:
|
||||
MoveResolver();
|
||||
|
||||
|
@ -442,12 +442,11 @@ ICMonitoredStub::ICMonitoredStub(Kind kind, JitCode* stubCode, ICStub* firstMoni
|
||||
}
|
||||
|
||||
bool
|
||||
ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx, ICStubSpace* space,
|
||||
ICStubCompiler::Engine engine)
|
||||
ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx, ICStubSpace* space)
|
||||
{
|
||||
MOZ_ASSERT(fallbackMonitorStub_ == nullptr);
|
||||
|
||||
ICTypeMonitor_Fallback::Compiler compiler(cx, engine, this);
|
||||
ICTypeMonitor_Fallback::Compiler compiler(cx, this);
|
||||
ICTypeMonitor_Fallback* stub = compiler.getStub(space);
|
||||
if (!stub)
|
||||
return false;
|
||||
@ -456,10 +455,10 @@ ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx, ICStubSpace* space,
|
||||
}
|
||||
|
||||
bool
|
||||
ICMonitoredFallbackStub::addMonitorStubForValue(JSContext* cx, SharedStubInfo* stub,
|
||||
ICMonitoredFallbackStub::addMonitorStubForValue(JSContext* cx, BaselineFrame* frame,
|
||||
HandleValue val)
|
||||
{
|
||||
return fallbackMonitorStub_->addMonitorStubForValue(cx, stub, val);
|
||||
return fallbackMonitorStub_->addMonitorStubForValue(cx, frame, val);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -2012,12 +2011,10 @@ static bool
|
||||
DoGetPropFallback(JSContext* cx, BaselineFrame* frame, ICGetProp_Fallback* stub_,
|
||||
MutableHandleValue val, MutableHandleValue res)
|
||||
{
|
||||
SharedStubInfo info(cx, frame, stub_->icEntry());
|
||||
HandleScript script = info.innerScript();
|
||||
|
||||
// This fallback stub may trigger debug mode toggling.
|
||||
DebugModeOSRVolatileStub<ICGetProp_Fallback*> stub(frame, stub_);
|
||||
|
||||
RootedScript script(cx, frame->script());
|
||||
jsbytecode* pc = stub_->icEntry()->pc(script);
|
||||
JSOp op = JSOp(*pc);
|
||||
FallbackICSpew(cx, stub, "GetProp(%s)", CodeName[op]);
|
||||
@ -2069,7 +2066,7 @@ DoGetPropFallback(JSContext* cx, BaselineFrame* frame, ICGetProp_Fallback* stub_
|
||||
return true;
|
||||
|
||||
// Add a type monitor stub for the resulting value.
|
||||
if (!stub->addMonitorStubForValue(cx, &info, res))
|
||||
if (!stub->addMonitorStubForValue(cx, frame, res))
|
||||
return false;
|
||||
|
||||
if (attached)
|
||||
@ -2176,7 +2173,7 @@ BaselineScript::noteAccessedGetter(uint32_t pcOffset)
|
||||
//
|
||||
|
||||
bool
|
||||
ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext* cx, SharedStubInfo* info, HandleValue val)
|
||||
ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext* cx, BaselineFrame* frame, HandleValue val)
|
||||
{
|
||||
bool wasDetachedMonitorChain = lastMonitorStubPtrAddr_ == nullptr;
|
||||
MOZ_ASSERT_IF(wasDetachedMonitorChain, numOptimizedMonitorStubs_ == 0);
|
||||
@ -2203,9 +2200,10 @@ ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext* cx, SharedStubInfo* in
|
||||
}
|
||||
}
|
||||
|
||||
ICTypeMonitor_PrimitiveSet::Compiler compiler(cx, info->engine(), existingStub, type);
|
||||
ICStub* stub = existingStub ? compiler.updateStub()
|
||||
: compiler.getStub(compiler.getStubSpace(info->outerScript(cx)));
|
||||
ICTypeMonitor_PrimitiveSet::Compiler compiler(cx, existingStub, type);
|
||||
ICStub* stub = existingStub
|
||||
? compiler.updateStub()
|
||||
: compiler.getStub(compiler.getStubSpace(frame->script()));
|
||||
if (!stub) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
@ -2232,7 +2230,7 @@ ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext* cx, SharedStubInfo* in
|
||||
}
|
||||
|
||||
ICTypeMonitor_SingleObject::Compiler compiler(cx, obj);
|
||||
ICStub* stub = compiler.getStub(compiler.getStubSpace(info->outerScript(cx)));
|
||||
ICStub* stub = compiler.getStub(compiler.getStubSpace(frame->script()));
|
||||
if (!stub) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
@ -2256,7 +2254,7 @@ ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext* cx, SharedStubInfo* in
|
||||
}
|
||||
|
||||
ICTypeMonitor_ObjectGroup::Compiler compiler(cx, group);
|
||||
ICStub* stub = compiler.getStub(compiler.getStubSpace(info->outerScript(cx)));
|
||||
ICStub* stub = compiler.getStub(compiler.getStubSpace(frame->script()));
|
||||
if (!stub) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
@ -2293,11 +2291,10 @@ ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext* cx, SharedStubInfo* in
|
||||
}
|
||||
|
||||
static bool
|
||||
DoTypeMonitorFallback(JSContext* cx, void* payload, ICTypeMonitor_Fallback* stub,
|
||||
DoTypeMonitorFallback(JSContext* cx, BaselineFrame* frame, ICTypeMonitor_Fallback* stub,
|
||||
HandleValue value, MutableHandleValue res)
|
||||
{
|
||||
SharedStubInfo info(cx, payload, stub->icEntry());
|
||||
HandleScript script = info.innerScript();
|
||||
JSScript* script = frame->script();
|
||||
jsbytecode* pc = stub->icEntry()->pc(script);
|
||||
TypeFallbackICSpew(cx, stub, "TypeMonitor");
|
||||
|
||||
@ -2316,7 +2313,7 @@ DoTypeMonitorFallback(JSContext* cx, void* payload, ICTypeMonitor_Fallback* stub
|
||||
// In derived class constructors (including nested arrows/eval), the
|
||||
// |this| argument or GETALIASEDVAR can return the magic TDZ value.
|
||||
MOZ_ASSERT(value.isMagic(JS_UNINITIALIZED_LEXICAL));
|
||||
MOZ_ASSERT(info.frame()->isFunctionFrame() || info.frame()->isEvalFrame());
|
||||
MOZ_ASSERT(frame->isFunctionFrame() || frame->isEvalFrame());
|
||||
MOZ_ASSERT(stub->monitorsThis() ||
|
||||
*GetNextPc(pc) == JSOP_CHECKTHIS ||
|
||||
*GetNextPc(pc) == JSOP_CHECKRETURN);
|
||||
@ -2340,7 +2337,7 @@ DoTypeMonitorFallback(JSContext* cx, void* payload, ICTypeMonitor_Fallback* stub
|
||||
TypeScript::Monitor(cx, script, pc, value);
|
||||
}
|
||||
|
||||
if (!stub->invalid() && !stub->addMonitorStubForValue(cx, &info, value))
|
||||
if (!stub->invalid() && !stub->addMonitorStubForValue(cx, frame, value))
|
||||
return false;
|
||||
|
||||
// Copy input value to res.
|
||||
@ -2348,7 +2345,7 @@ DoTypeMonitorFallback(JSContext* cx, void* payload, ICTypeMonitor_Fallback* stub
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*DoTypeMonitorFallbackFn)(JSContext*, void*, ICTypeMonitor_Fallback*,
|
||||
typedef bool (*DoTypeMonitorFallbackFn)(JSContext*, BaselineFrame*, ICTypeMonitor_Fallback*,
|
||||
HandleValue, MutableHandleValue);
|
||||
static const VMFunction DoTypeMonitorFallbackInfo =
|
||||
FunctionInfo<DoTypeMonitorFallbackFn>(DoTypeMonitorFallback, "DoTypeMonitorFallback",
|
||||
@ -2364,7 +2361,7 @@ ICTypeMonitor_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
|
||||
masm.pushValue(R0);
|
||||
masm.push(ICStubReg);
|
||||
pushStubPayload(masm, R0.scratchReg());
|
||||
masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
|
||||
return tailCallVM(DoTypeMonitorFallbackInfo, masm);
|
||||
}
|
||||
|
@ -1207,9 +1207,8 @@ class ICMonitoredFallbackStub : public ICFallbackStub
|
||||
fallbackMonitorStub_(nullptr) {}
|
||||
|
||||
public:
|
||||
MOZ_MUST_USE bool initMonitoringChain(JSContext* cx, ICStubSpace* space,
|
||||
ICStubCompiler::Engine engine);
|
||||
MOZ_MUST_USE bool addMonitorStubForValue(JSContext* cx, SharedStubInfo* info, HandleValue val);
|
||||
MOZ_MUST_USE bool initMonitoringChain(JSContext* cx, ICStubSpace* space);
|
||||
MOZ_MUST_USE bool addMonitorStubForValue(JSContext* cx, BaselineFrame* frame, HandleValue val);
|
||||
|
||||
inline ICTypeMonitor_Fallback* fallbackMonitorStub() const {
|
||||
return fallbackMonitorStub_;
|
||||
@ -1304,9 +1303,9 @@ class TypeCheckPrimitiveSetStub : public ICStub
|
||||
}
|
||||
|
||||
public:
|
||||
Compiler(JSContext* cx, Kind kind, Engine engine_, TypeCheckPrimitiveSetStub* existingStub,
|
||||
Compiler(JSContext* cx, Kind kind, TypeCheckPrimitiveSetStub* existingStub,
|
||||
JSValueType type)
|
||||
: ICStubCompiler(cx, kind, engine_),
|
||||
: ICStubCompiler(cx, kind, Engine::Baseline),
|
||||
existingStub_(existingStub),
|
||||
flags_((existingStub ? existingStub->typeFlags() : 0) | TypeToFlag(type))
|
||||
{
|
||||
@ -1469,7 +1468,7 @@ class ICTypeMonitor_Fallback : public ICStub
|
||||
|
||||
// Create a new monitor stub for the type of the given value, and
|
||||
// add it to this chain.
|
||||
MOZ_MUST_USE bool addMonitorStubForValue(JSContext* cx, SharedStubInfo* info, HandleValue val);
|
||||
MOZ_MUST_USE bool addMonitorStubForValue(JSContext* cx, BaselineFrame* frame, HandleValue val);
|
||||
|
||||
void resetMonitorStubChain(Zone* zone);
|
||||
|
||||
@ -1482,14 +1481,14 @@ class ICTypeMonitor_Fallback : public ICStub
|
||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
||||
|
||||
public:
|
||||
Compiler(JSContext* cx, Engine engine, ICMonitoredFallbackStub* mainFallbackStub)
|
||||
: ICStubCompiler(cx, ICStub::TypeMonitor_Fallback, engine),
|
||||
Compiler(JSContext* cx, ICMonitoredFallbackStub* mainFallbackStub)
|
||||
: ICStubCompiler(cx, ICStub::TypeMonitor_Fallback, Engine::Baseline),
|
||||
mainFallbackStub_(mainFallbackStub),
|
||||
argumentIndex_(BYTECODE_INDEX)
|
||||
{ }
|
||||
|
||||
Compiler(JSContext* cx, Engine engine, uint32_t argumentIndex)
|
||||
: ICStubCompiler(cx, ICStub::TypeMonitor_Fallback, engine),
|
||||
Compiler(JSContext* cx, uint32_t argumentIndex)
|
||||
: ICStubCompiler(cx, ICStub::TypeMonitor_Fallback, Engine::Baseline),
|
||||
mainFallbackStub_(nullptr),
|
||||
argumentIndex_(argumentIndex)
|
||||
{ }
|
||||
@ -1515,9 +1514,9 @@ class ICTypeMonitor_PrimitiveSet : public TypeCheckPrimitiveSetStub
|
||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
||||
|
||||
public:
|
||||
Compiler(JSContext* cx, Engine engine, ICTypeMonitor_PrimitiveSet* existingStub,
|
||||
Compiler(JSContext* cx, ICTypeMonitor_PrimitiveSet* existingStub,
|
||||
JSValueType type)
|
||||
: TypeCheckPrimitiveSetStub::Compiler(cx, TypeMonitor_PrimitiveSet, engine, existingStub,
|
||||
: TypeCheckPrimitiveSetStub::Compiler(cx, TypeMonitor_PrimitiveSet, existingStub,
|
||||
type)
|
||||
{}
|
||||
|
||||
@ -2362,7 +2361,7 @@ class ICGetProp_Fallback : public ICMonitoredFallbackStub
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
ICGetProp_Fallback* stub = newStub<ICGetProp_Fallback>(space, getStubCode());
|
||||
if (!stub || !stub->initMonitoringChain(cx, space, engine_))
|
||||
if (!stub || !stub->initMonitoringChain(cx, space))
|
||||
return nullptr;
|
||||
return stub;
|
||||
}
|
||||
|
@ -2296,6 +2296,10 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
masm.vmovmskps_rr(src.encoding(), dest.encoding());
|
||||
}
|
||||
void vpmovmskb(FloatRegister src, Register dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
masm.vpmovmskb_rr(src.encoding(), dest.encoding());
|
||||
}
|
||||
void vptest(FloatRegister rhs, FloatRegister lhs) {
|
||||
MOZ_ASSERT(HasSSE41());
|
||||
masm.vptest_rr(rhs.encoding(), lhs.encoding());
|
||||
|
@ -3090,6 +3090,11 @@ public:
|
||||
twoByteOpSimdInt32("vmovmskps", VEX_PS, OP2_MOVMSKPD_EdVd, src, dst);
|
||||
}
|
||||
|
||||
void vpmovmskb_rr(XMMRegisterID src, RegisterID dst)
|
||||
{
|
||||
twoByteOpSimdInt32("vpmovmskb", VEX_PD, OP2_PMOVMSKB_EdVd, src, dst);
|
||||
}
|
||||
|
||||
void vptest_rr(XMMRegisterID rhs, XMMRegisterID lhs) {
|
||||
threeByteOpSimd("vptest", VEX_PD, OP3_PTEST_VdVd, ESCAPE_38, rhs, invalid_xmm, lhs);
|
||||
}
|
||||
|
@ -2963,8 +2963,10 @@ CodeGeneratorX86Shared::visitSimdAllTrue(LSimdAllTrue* ins)
|
||||
FloatRegister input = ToFloatRegister(ins->input());
|
||||
Register output = ToRegister(ins->output());
|
||||
|
||||
masm.vmovmskps(input, output);
|
||||
masm.cmp32(output, Imm32(0xf));
|
||||
// We know that the input lanes are boolean, so they are either 0 or -1.
|
||||
// The all-true vector has all 128 bits set, no matter the lane geometry.
|
||||
masm.vpmovmskb(input, output);
|
||||
masm.cmp32(output, Imm32(0xffff));
|
||||
masm.emitSet(Assembler::Zero, output);
|
||||
}
|
||||
|
||||
@ -2974,7 +2976,7 @@ CodeGeneratorX86Shared::visitSimdAnyTrue(LSimdAnyTrue* ins)
|
||||
FloatRegister input = ToFloatRegister(ins->input());
|
||||
Register output = ToRegister(ins->output());
|
||||
|
||||
masm.vmovmskps(input, output);
|
||||
masm.vpmovmskb(input, output);
|
||||
masm.cmp32(output, Imm32(0x0));
|
||||
masm.emitSet(Assembler::NonZero, output);
|
||||
}
|
||||
|
@ -266,6 +266,7 @@ enum TwoByteOpcodeID {
|
||||
OP2_PSRLD_VdqWdq = 0xD2,
|
||||
OP2_PMULLW_VdqWdq = 0xD5,
|
||||
OP2_MOVQ_WdVd = 0xD6,
|
||||
OP2_PMOVMSKB_EdVd = 0xD7,
|
||||
OP2_PSUBUSB_VdqWdq = 0xD8,
|
||||
OP2_PSUBUSW_VdqWdq = 0xD9,
|
||||
OP2_PANDDQ_VdqWdq = 0xDB,
|
||||
|
@ -6,6 +6,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if defined(JS_SIMULATOR_ARM)
|
||||
|
||||
#include "jit/arm/Assembler-arm.h"
|
||||
#include "jit/arm/MoveEmitter-arm.h"
|
||||
#include "jit/arm/Simulator-arm.h"
|
||||
@ -528,5 +529,96 @@ BEGIN_TEST(testJitMoveEmitterCycles_autogen3)
|
||||
return true;
|
||||
}
|
||||
END_TEST(testJitMoveEmitterCycles_autogen3)
|
||||
BEGIN_TEST(testJitMoveEmitterCycles_bug1299147_1)
|
||||
{
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
|
||||
TempAllocator alloc(&lifo);
|
||||
JitContext jc(cx, &alloc);
|
||||
cx->runtime()->getJitRuntime(cx);
|
||||
MacroAssembler masm;
|
||||
MoveEmitter mover(masm);
|
||||
MoveResolver mr;
|
||||
mr.setAllocator(alloc);
|
||||
Simulator* sim = Simulator::Current();
|
||||
// S2 -> S0
|
||||
// S2 -> S6
|
||||
// S3 -> S1
|
||||
// S3 -> S7
|
||||
// D0 -> D1
|
||||
// D0 -> D2
|
||||
TRY(mr.addMove(MoveOperand(s2), MoveOperand(s0), MoveOp::FLOAT32));
|
||||
TRY(mr.addMove(MoveOperand(s2), MoveOperand(s6), MoveOp::FLOAT32));
|
||||
sim->set_s_register_from_float(2, 2);
|
||||
TRY(mr.addMove(MoveOperand(s3), MoveOperand(s1), MoveOp::FLOAT32));
|
||||
TRY(mr.addMove(MoveOperand(s3), MoveOperand(s7), MoveOp::FLOAT32));
|
||||
sim->set_s_register_from_float(3, 4);
|
||||
TRY(mr.addMove(MoveOperand(d0), MoveOperand(d1), MoveOp::FLOAT32));
|
||||
TRY(mr.addMove(MoveOperand(d0), MoveOperand(d2), MoveOp::FLOAT32));
|
||||
sim->set_d_register_from_double(0, 1);
|
||||
// don't explode!
|
||||
TRY(mr.resolve());
|
||||
mover.emit(mr);
|
||||
mover.finish();
|
||||
masm.abiret();
|
||||
JitCode* code = linkAndAllocate(cx, &masm);
|
||||
sim->call(code->raw(), 1, 1);
|
||||
float f;
|
||||
double d;
|
||||
sim->get_double_from_d_register(1, &d);
|
||||
CHECK(d == 1);
|
||||
sim->get_double_from_d_register(2, &d);
|
||||
CHECK(d == 1);
|
||||
sim->get_float_from_s_register(0, &f);
|
||||
CHECK(int(f) == 2);
|
||||
sim->get_float_from_s_register(6, &f);
|
||||
CHECK(int(f) == 2);
|
||||
sim->get_float_from_s_register(1, &f);
|
||||
CHECK(int(f) == 4);
|
||||
sim->get_float_from_s_register(7, &f);
|
||||
CHECK(int(f) == 4);
|
||||
return true;
|
||||
}
|
||||
END_TEST(testJitMoveEmitterCycles_bug1299147_1)
|
||||
BEGIN_TEST(testJitMoveEmitterCycles_bug1299147)
|
||||
{
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
|
||||
TempAllocator alloc(&lifo);
|
||||
JitContext jc(cx, &alloc);
|
||||
cx->runtime()->getJitRuntime(cx);
|
||||
MacroAssembler masm;
|
||||
MoveEmitter mover(masm);
|
||||
MoveResolver mr;
|
||||
mr.setAllocator(alloc);
|
||||
Simulator* sim = Simulator::Current();
|
||||
// S2 -> S5
|
||||
// S2 -> S6
|
||||
// D0 -> D1
|
||||
TRY(mr.addMove(MoveOperand(s2), MoveOperand(s5), MoveOp::FLOAT32));
|
||||
TRY(mr.addMove(MoveOperand(s2), MoveOperand(s6), MoveOp::FLOAT32));
|
||||
sim->set_s_register_from_float(2, 2);
|
||||
TRY(mr.addMove(MoveOperand(d0), MoveOperand(d1), MoveOp::FLOAT32));
|
||||
sim->set_d_register_from_double(0, 1);
|
||||
// don't explode!
|
||||
TRY(mr.resolve());
|
||||
mover.emit(mr);
|
||||
mover.finish();
|
||||
masm.abiret();
|
||||
JitCode* code = linkAndAllocate(cx, &masm);
|
||||
sim->call(code->raw(), 1, 1);
|
||||
float f;
|
||||
double d;
|
||||
sim->get_double_from_d_register(1, &d);
|
||||
CHECK(d == 1);
|
||||
sim->get_float_from_s_register(5, &f);
|
||||
CHECK(int(f) == 2);
|
||||
sim->get_float_from_s_register(6, &f);
|
||||
CHECK(int(f) == 2);
|
||||
return true;
|
||||
}
|
||||
END_TEST(testJitMoveEmitterCycles_bug1299147)
|
||||
|
||||
#endif
|
||||
#endif // JS_SIMULATOR_ARM
|
||||
|
@ -529,6 +529,12 @@ interface nsIXPCComponents_Utils : nsISupports
|
||||
[implicit_jscontext]
|
||||
attribute boolean ion;
|
||||
|
||||
// Returns true if we're running in automation and certain security
|
||||
// restrictions can be eased.
|
||||
readonly attribute boolean isInAutomation;
|
||||
|
||||
void crashIfNotInAutomation();
|
||||
|
||||
[implicit_jscontext]
|
||||
void setGCZeal(in long zeal);
|
||||
|
||||
|
@ -10,9 +10,13 @@
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsIFile.h"
|
||||
|
||||
#include <private/pprio.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace loader {
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
AutoMemMap::~AutoMemMap()
|
||||
{
|
||||
if (fileMap) {
|
||||
@ -26,26 +30,62 @@ AutoMemMap::~AutoMemMap()
|
||||
}
|
||||
}
|
||||
|
||||
FileDescriptor
|
||||
AutoMemMap::cloneFileDescriptor()
|
||||
{
|
||||
if (fd.get()) {
|
||||
auto handle = FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(fd.get()));
|
||||
return FileDescriptor(handle);
|
||||
}
|
||||
return FileDescriptor();
|
||||
}
|
||||
|
||||
Result<Ok, nsresult>
|
||||
AutoMemMap::init(nsIFile* file, int flags, int mode, PRFileMapProtect prot)
|
||||
{
|
||||
MOZ_ASSERT(!fd);
|
||||
|
||||
NS_TRY(file->OpenNSPRFileDesc(flags, mode, &fd.rwget()));
|
||||
|
||||
return initInternal(prot);
|
||||
}
|
||||
|
||||
Result<Ok, nsresult>
|
||||
AutoMemMap::init(const FileDescriptor& file)
|
||||
{
|
||||
MOZ_ASSERT(!fd);
|
||||
if (!file.IsValid()) {
|
||||
return Err(NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
auto handle = file.ClonePlatformHandle();
|
||||
|
||||
fd = PR_ImportFile(PROsfd(handle.get()));
|
||||
if (!fd) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
Unused << handle.release();
|
||||
|
||||
return initInternal();
|
||||
}
|
||||
|
||||
Result<Ok, nsresult>
|
||||
AutoMemMap::initInternal(PRFileMapProtect prot)
|
||||
{
|
||||
MOZ_ASSERT(!fileMap);
|
||||
MOZ_ASSERT(!addr);
|
||||
|
||||
int64_t fileSize;
|
||||
NS_TRY(file->GetFileSize(&fileSize));
|
||||
PRFileInfo64 fileInfo;
|
||||
NS_TRY(PR_GetOpenFileInfo64(fd.get(), &fileInfo));
|
||||
|
||||
if (fileSize > UINT32_MAX)
|
||||
if (fileInfo.size > 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;
|
||||
size_ = fileInfo.size;
|
||||
addr = PR_MemMap(fileMap, 0, size_);
|
||||
if (!addr)
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/RangedPtr.h"
|
||||
#include "mozilla/Result.h"
|
||||
#include "mozilla/ipc/FileDescriptor.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
|
||||
#include <prio.h>
|
||||
@ -19,6 +20,8 @@ class nsIFile;
|
||||
namespace mozilla {
|
||||
namespace loader {
|
||||
|
||||
using mozilla::ipc::FileDescriptor;
|
||||
|
||||
class AutoMemMap
|
||||
{
|
||||
public:
|
||||
@ -30,6 +33,9 @@ class AutoMemMap
|
||||
init(nsIFile* file, int flags = PR_RDONLY, int mode = 0,
|
||||
PRFileMapProtect prot = PR_PROT_READONLY);
|
||||
|
||||
Result<Ok, nsresult>
|
||||
init(const ipc::FileDescriptor& file);
|
||||
|
||||
bool initialized() { return addr; }
|
||||
|
||||
uint32_t size() const { MOZ_ASSERT(fd); return size_; }
|
||||
@ -50,7 +56,11 @@ class AutoMemMap
|
||||
|
||||
size_t nonHeapSizeOfExcludingThis() { return size_; }
|
||||
|
||||
FileDescriptor cloneFileDescriptor();
|
||||
|
||||
private:
|
||||
Result<Ok, nsresult> initInternal(PRFileMapProtect prot = PR_PROT_READONLY);
|
||||
|
||||
AutoFDClose fd;
|
||||
PRFileMap* fileMap = nullptr;
|
||||
|
||||
|
33
js/xpconnect/loader/PScriptCache.ipdl
Normal file
33
js/xpconnect/loader/PScriptCache.ipdl
Normal file
@ -0,0 +1,33 @@
|
||||
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
|
||||
/* 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 protocol PContent;
|
||||
|
||||
using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
|
||||
using mozilla::void_t from "ipc/IPCMessageUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace loader {
|
||||
|
||||
struct ScriptData {
|
||||
nsCString url;
|
||||
nsCString cachePath;
|
||||
TimeStamp loadTime;
|
||||
// This will be an empty array if script data is present in the previous
|
||||
// session's cache.
|
||||
uint8_t[] xdrData;
|
||||
};
|
||||
|
||||
protocol PScriptCache
|
||||
{
|
||||
manager PContent;
|
||||
|
||||
parent:
|
||||
async __delete__(ScriptData[] scripts);
|
||||
};
|
||||
|
||||
} // namespace loader
|
||||
} // namespace mozilla
|
100
js/xpconnect/loader/ScriptCacheActors.cpp
Normal file
100
js/xpconnect/loader/ScriptCacheActors.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
/* -*- 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/loader/ScriptCacheActors.h"
|
||||
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace loader {
|
||||
|
||||
void
|
||||
ScriptCacheChild::Init(const Maybe<FileDescriptor>& cacheFile, bool wantCacheData)
|
||||
{
|
||||
mWantCacheData = wantCacheData;
|
||||
|
||||
auto& cache = ScriptPreloader::GetChildSingleton();
|
||||
Unused << cache.InitCache(cacheFile, this);
|
||||
|
||||
if (!wantCacheData) {
|
||||
// If the parent process isn't expecting any cache data from us, we're
|
||||
// done.
|
||||
Send__delete__(this, AutoTArray<ScriptData, 0>());
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize the script cache for the content process, and send back data about
|
||||
// any scripts executed up to this point.
|
||||
void
|
||||
ScriptCacheChild::SendScriptsAndFinalize(ScriptPreloader::ScriptHash& scripts)
|
||||
{
|
||||
MOZ_ASSERT(mWantCacheData);
|
||||
|
||||
AutoSafeJSAPI jsapi;
|
||||
|
||||
auto matcher = ScriptPreloader::Match<ScriptPreloader::ScriptStatus::Saved>();
|
||||
|
||||
nsTArray<ScriptData> dataArray;
|
||||
for (auto& script : IterHash(scripts, matcher)) {
|
||||
if (!script->mSize && !script->XDREncode(jsapi.cx())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto data = dataArray.AppendElement();
|
||||
|
||||
data->url() = script->mURL;
|
||||
data->cachePath() = script->mCachePath;
|
||||
data->loadTime() = script->mLoadTime;
|
||||
|
||||
if (script->HasBuffer()) {
|
||||
auto& xdrData = script->Buffer();
|
||||
data->xdrData().AppendElements(xdrData.begin(), xdrData.length());
|
||||
script->FreeData();
|
||||
}
|
||||
}
|
||||
|
||||
Send__delete__(this, dataArray);
|
||||
}
|
||||
|
||||
void
|
||||
ScriptCacheChild::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
auto& cache = ScriptPreloader::GetChildSingleton();
|
||||
cache.mChildActor = nullptr;
|
||||
}
|
||||
|
||||
|
||||
IPCResult
|
||||
ScriptCacheParent::Recv__delete__(nsTArray<ScriptData>&& scripts)
|
||||
{
|
||||
if (!mWantCacheData && scripts.Length()) {
|
||||
return IPC_FAIL(this, "UnexpectedScriptData");
|
||||
}
|
||||
|
||||
// We don't want any more data from the process at this point.
|
||||
mWantCacheData = false;
|
||||
|
||||
// Merge the child's script data with the parent's.
|
||||
auto parent = static_cast<dom::ContentParent*>(Manager());
|
||||
auto processType = ScriptPreloader::GetChildProcessType(parent->GetRemoteType());
|
||||
|
||||
auto& cache = ScriptPreloader::GetChildSingleton();
|
||||
for (auto& script : scripts) {
|
||||
cache.NoteScript(script.url(), script.cachePath(), processType,
|
||||
Move(script.xdrData()), script.loadTime());
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
ScriptCacheParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{}
|
||||
|
||||
} // namespace loader
|
||||
} // namespace mozilla
|
62
js/xpconnect/loader/ScriptCacheActors.h
Normal file
62
js/xpconnect/loader/ScriptCacheActors.h
Normal file
@ -0,0 +1,62 @@
|
||||
/* -*- 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 ScriptCache_h
|
||||
#define ScriptCache_h
|
||||
|
||||
#include "mozilla/ScriptPreloader.h"
|
||||
#include "mozilla/loader/PScriptCacheChild.h"
|
||||
#include "mozilla/loader/PScriptCacheParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
class FileDescriptor;
|
||||
}
|
||||
|
||||
|
||||
namespace loader {
|
||||
|
||||
using mozilla::ipc::FileDescriptor;
|
||||
using mozilla::ipc::IPCResult;
|
||||
|
||||
class ScriptCacheParent final : public PScriptCacheParent
|
||||
{
|
||||
public:
|
||||
explicit ScriptCacheParent(bool wantCacheData)
|
||||
: mWantCacheData(wantCacheData)
|
||||
{}
|
||||
|
||||
protected:
|
||||
virtual IPCResult Recv__delete__(nsTArray<ScriptData>&& scripts) override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
private:
|
||||
bool mWantCacheData;
|
||||
};
|
||||
|
||||
class ScriptCacheChild final : public PScriptCacheChild
|
||||
{
|
||||
friend class mozilla::ScriptPreloader;
|
||||
|
||||
public:
|
||||
ScriptCacheChild() = default;
|
||||
|
||||
void Init(const Maybe<FileDescriptor>& cacheFile, bool wantCacheData);
|
||||
|
||||
protected:
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
void SendScriptsAndFinalize(ScriptPreloader::ScriptHash& scripts);
|
||||
|
||||
private:
|
||||
bool mWantCacheData = false;
|
||||
};
|
||||
|
||||
|
||||
} // namespace loader
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ScriptCache_h
|
@ -9,9 +9,11 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/EnumSet.h"
|
||||
#include "mozilla/Range.h"
|
||||
#include "mozilla/Result.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
@ -20,6 +22,13 @@
|
||||
namespace mozilla {
|
||||
namespace loader {
|
||||
|
||||
using mozilla::dom::AutoJSAPI;
|
||||
|
||||
struct MOZ_RAII AutoSafeJSAPI : public AutoJSAPI
|
||||
{
|
||||
AutoSafeJSAPI() { Init(); }
|
||||
};
|
||||
|
||||
static inline Result<Ok, nsresult>
|
||||
WrapNSResult(PRStatus aRv)
|
||||
{
|
||||
@ -55,6 +64,22 @@ public:
|
||||
return buf;
|
||||
}
|
||||
|
||||
void
|
||||
codeUint8(const uint8_t& val)
|
||||
{
|
||||
*write(sizeof val) = val;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
codeUint8(const EnumSet<T>& val)
|
||||
{
|
||||
// EnumSets are always represented as uint32_t values, so we need to
|
||||
// assert that the value actually fits in a uint8 before writing it.
|
||||
uint32_t value = val.serialize();
|
||||
codeUint8(CheckedUint8(value).value());
|
||||
}
|
||||
|
||||
void
|
||||
codeUint16(const uint16_t& val)
|
||||
{
|
||||
@ -105,6 +130,26 @@ public:
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool
|
||||
codeUint8(uint8_t& val)
|
||||
{
|
||||
if (checkCapacity(sizeof val)) {
|
||||
val = *read(sizeof val);
|
||||
}
|
||||
return !error_;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
codeUint8(EnumSet<T>& val)
|
||||
{
|
||||
uint8_t value;
|
||||
if (codeUint8(value)) {
|
||||
val.deserialize(value);
|
||||
}
|
||||
return !error_;
|
||||
}
|
||||
|
||||
bool
|
||||
codeUint16(uint16_t& val)
|
||||
{
|
||||
@ -163,6 +208,122 @@ public:
|
||||
size_t cursor_ = 0;
|
||||
};
|
||||
|
||||
|
||||
template <typename T> struct Matcher;
|
||||
|
||||
// Wraps the iterator for a nsTHashTable so that it may be used as a range
|
||||
// iterator. Each iterator result acts as a smart pointer to the hash element,
|
||||
// and has a Remove() method which will remove the element from the hash.
|
||||
//
|
||||
// It also accepts an optional Matcher instance against which to filter the
|
||||
// elements which should be iterated over.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// for (auto& elem : HashElemIter<HashType>(hash)) {
|
||||
// if (elem->IsDead()) {
|
||||
// elem.Remove();
|
||||
// }
|
||||
// }
|
||||
template <typename T>
|
||||
class HashElemIter
|
||||
{
|
||||
using Iterator = typename T::Iterator;
|
||||
using ElemType = typename T::UserDataType;
|
||||
|
||||
T& hash_;
|
||||
Matcher<ElemType>* matcher_;
|
||||
Maybe<Iterator> iter_;
|
||||
|
||||
public:
|
||||
explicit HashElemIter(T& hash, Matcher<ElemType>* matcher = nullptr)
|
||||
: hash_(hash), matcher_(matcher)
|
||||
{
|
||||
iter_.emplace(Move(hash.Iter()));
|
||||
}
|
||||
|
||||
class Elem
|
||||
{
|
||||
friend class HashElemIter<T>;
|
||||
|
||||
HashElemIter<T>& iter_;
|
||||
bool done_;
|
||||
|
||||
Elem(HashElemIter& iter, bool done)
|
||||
: iter_(iter), done_(done)
|
||||
{
|
||||
skipNonMatching();
|
||||
}
|
||||
|
||||
Iterator& iter() { return iter_.iter_.ref(); }
|
||||
|
||||
void skipNonMatching()
|
||||
{
|
||||
if (iter_.matcher_) {
|
||||
while (!done_ && !iter_.matcher_->Matches(get())) {
|
||||
iter().Next();
|
||||
done_ = iter().Done();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Elem& operator*() { return *this; }
|
||||
|
||||
ElemType get()
|
||||
{
|
||||
if (done_) {
|
||||
return nullptr;
|
||||
}
|
||||
return iter().Data();
|
||||
}
|
||||
|
||||
ElemType operator->() { return get(); }
|
||||
|
||||
operator ElemType() { return get(); }
|
||||
|
||||
void Remove() { iter().Remove(); }
|
||||
|
||||
Elem& operator++()
|
||||
{
|
||||
MOZ_ASSERT(!done_);
|
||||
|
||||
iter().Next();
|
||||
done_ = iter().Done();
|
||||
|
||||
skipNonMatching();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator!=(Elem& other)
|
||||
{
|
||||
return done_ != other.done_ || this->get() != other.get();
|
||||
}
|
||||
};
|
||||
|
||||
Elem begin() { return Elem(*this, iter_->Done()); }
|
||||
|
||||
Elem end() { return Elem(*this, true); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
HashElemIter<T> IterHash(T& hash, Matcher<typename T::UserDataType>* matcher = nullptr)
|
||||
{
|
||||
return HashElemIter<T>(hash, matcher);
|
||||
}
|
||||
|
||||
template <typename T, typename F>
|
||||
bool
|
||||
Find(T&& iter, F&& match)
|
||||
{
|
||||
for (auto& elem : iter) {
|
||||
if (match(elem)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}; // namespace loader
|
||||
}; // namespace mozilla
|
||||
|
||||
|
@ -6,14 +6,17 @@
|
||||
|
||||
#include "mozilla/ScriptPreloader.h"
|
||||
#include "ScriptPreloader-inl.h"
|
||||
#include "mozilla/loader/ScriptCacheActors.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
|
||||
#include "MainThreadUtils.h"
|
||||
#include "nsDebug.h"
|
||||
@ -27,6 +30,7 @@
|
||||
#include "xpcpublic.h"
|
||||
|
||||
#define DELAYED_STARTUP_TOPIC "browser-delayed-startup-finished"
|
||||
#define DOC_ELEM_INSERTED_TOPIC "document-element-inserted"
|
||||
#define CLEANUP_TOPIC "xpcom-shutdown"
|
||||
#define SHUTDOWN_TOPIC "quit-application-granted"
|
||||
#define CACHE_FLUSH_TOPIC "startupcache-invalidate"
|
||||
@ -39,21 +43,26 @@ static LazyLogModule gLog("ScriptPreloader");
|
||||
}
|
||||
|
||||
using mozilla::dom::AutoJSAPI;
|
||||
using mozilla::dom::ContentChild;
|
||||
using mozilla::dom::ContentParent;
|
||||
using namespace mozilla::loader;
|
||||
|
||||
ProcessType ScriptPreloader::sProcessType;
|
||||
|
||||
|
||||
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),
|
||||
SizeOfHashEntries<ScriptStatus::Saved>(mScripts, 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),
|
||||
SizeOfHashEntries<ScriptStatus::Restored>(mScripts, 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.");
|
||||
|
||||
@ -77,22 +86,102 @@ ScriptPreloader::GetSingleton()
|
||||
static RefPtr<ScriptPreloader> singleton;
|
||||
|
||||
if (!singleton) {
|
||||
singleton = new ScriptPreloader();
|
||||
if (XRE_IsParentProcess()) {
|
||||
singleton = new ScriptPreloader();
|
||||
singleton->mChildCache = &GetChildSingleton();
|
||||
Unused << singleton->InitCache();
|
||||
} else {
|
||||
singleton = &GetChildSingleton();
|
||||
}
|
||||
|
||||
ClearOnShutdown(&singleton);
|
||||
}
|
||||
|
||||
return *singleton;
|
||||
}
|
||||
|
||||
// The child singleton is available in all processes, including the parent, and
|
||||
// is used for scripts which are expected to be loaded into child processes
|
||||
// (such as process and frame scripts), or scripts that have already been loaded
|
||||
// into a child. The child caches are managed as follows:
|
||||
//
|
||||
// - Every startup, we open the cache file from the last session, move it to a
|
||||
// new location, and begin pre-loading the scripts that are stored in it. There
|
||||
// is a separate cache file for parent and content processes, but the parent
|
||||
// process opens both the parent and content cache files.
|
||||
//
|
||||
// - Once startup is complete, we write a new cache file for the next session,
|
||||
// containing only the scripts that were used during early startup, so we don't
|
||||
// waste pre-loading scripts that may not be needed.
|
||||
//
|
||||
// - For content processes, opening and writing the cache file is handled in the
|
||||
// parent process. The first content process of each type sends back the data
|
||||
// for scripts that were loaded in early startup, and the parent merges them and
|
||||
// writes them to a cache file.
|
||||
//
|
||||
// - Currently, content processes only benefit from the cache data written
|
||||
// during the *previous* session. Ideally, new content processes should probably
|
||||
// use the cache data written during this session if there was no previous cache
|
||||
// file, but I'd rather do that as a follow-up.
|
||||
ScriptPreloader&
|
||||
ScriptPreloader::GetChildSingleton()
|
||||
{
|
||||
static RefPtr<ScriptPreloader> singleton;
|
||||
|
||||
if (!singleton) {
|
||||
singleton = new ScriptPreloader();
|
||||
if (XRE_IsParentProcess()) {
|
||||
Unused << singleton->InitCache(NS_LITERAL_STRING("scriptCache-child"));
|
||||
}
|
||||
ClearOnShutdown(&singleton);
|
||||
}
|
||||
|
||||
return *singleton;
|
||||
}
|
||||
|
||||
void
|
||||
ScriptPreloader::InitContentChild(ContentParent& parent)
|
||||
{
|
||||
auto& cache = GetChildSingleton();
|
||||
|
||||
// We want startup script data from the first process of a given type.
|
||||
// That process sends back its script data before it executes any
|
||||
// untrusted code, and then we never accept further script data for that
|
||||
// type of process for the rest of the session.
|
||||
//
|
||||
// The script data from each process type is merged with the data from the
|
||||
// parent process's frame and process scripts, and shared between all
|
||||
// content process types in the next session.
|
||||
//
|
||||
// Note that if the first process of a given type crashes or shuts down
|
||||
// before sending us its script data, we silently ignore it, and data for
|
||||
// that process type is not included in the next session's cache. This
|
||||
// should be a sufficiently rare occurrence that it's not worth trying to
|
||||
// handle specially.
|
||||
auto processType = GetChildProcessType(parent.GetRemoteType());
|
||||
bool wantScriptData = !cache.mInitializedProcesses.contains(processType);
|
||||
cache.mInitializedProcesses += processType;
|
||||
|
||||
auto fd = cache.mCacheData.cloneFileDescriptor();
|
||||
if (fd.IsValid()) {
|
||||
Unused << parent.SendPScriptCacheConstructor(fd, wantScriptData);
|
||||
} else {
|
||||
Unused << parent.SendPScriptCacheConstructor(NS_ERROR_FILE_NOT_FOUND, wantScriptData);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessType
|
||||
ScriptPreloader::GetChildProcessType(const nsAString& remoteType)
|
||||
{
|
||||
if (remoteType.EqualsLiteral(EXTENSION_REMOTE_TYPE)) {
|
||||
return ProcessType::Extension;
|
||||
}
|
||||
return ProcessType::Web;
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
struct MOZ_RAII AutoSafeJSAPI : public AutoJSAPI
|
||||
{
|
||||
AutoSafeJSAPI() { Init(); }
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
TraceOp(JSTracer* trc, void* data)
|
||||
{
|
||||
@ -106,11 +195,7 @@ TraceOp(JSTracer* trc, void* data)
|
||||
void
|
||||
ScriptPreloader::Trace(JSTracer* trc)
|
||||
{
|
||||
for (auto script : mSavedScripts) {
|
||||
JS::TraceEdge(trc, &script->mScript, "ScriptPreloader::CachedScript.mScript");
|
||||
}
|
||||
|
||||
for (auto script : mRestoredScripts) {
|
||||
for (auto& script : IterHash(mScripts)) {
|
||||
JS::TraceEdge(trc, &script->mScript, "ScriptPreloader::CachedScript.mScript");
|
||||
}
|
||||
}
|
||||
@ -120,9 +205,26 @@ ScriptPreloader::ScriptPreloader()
|
||||
: mMonitor("[ScriptPreloader.mMonitor]")
|
||||
, mSaveMonitor("[ScriptPreloader.mSaveMonitor]")
|
||||
{
|
||||
if (XRE_IsParentProcess()) {
|
||||
sProcessType = ProcessType::Parent;
|
||||
} else {
|
||||
sProcessType = GetChildProcessType(dom::ContentChild::GetSingleton()->GetRemoteType());
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
MOZ_RELEASE_ASSERT(obs);
|
||||
obs->AddObserver(this, DELAYED_STARTUP_TOPIC, false);
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
// In the parent process, we want to freeze the script cache as soon
|
||||
// as delayed startup for the first browser window has completed.
|
||||
obs->AddObserver(this, DELAYED_STARTUP_TOPIC, false);
|
||||
} else {
|
||||
// In the child process, we need to freeze the script cache before any
|
||||
// untrusted code has been executed. The insertion of the first DOM
|
||||
// document element may sometimes be earlier than is ideal, but at
|
||||
// least it should always be safe.
|
||||
obs->AddObserver(this, DOC_ELEM_INSERTED_TOPIC, false);
|
||||
}
|
||||
obs->AddObserver(this, SHUTDOWN_TOPIC, false);
|
||||
obs->AddObserver(this, CLEANUP_TOPIC, false);
|
||||
obs->AddObserver(this, CACHE_FLUSH_TOPIC, false);
|
||||
@ -154,8 +256,7 @@ ScriptPreloader::Cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
mSavedScripts.clear();
|
||||
mRestoredScripts.clear();
|
||||
mScripts.Clear();
|
||||
|
||||
AutoSafeJSAPI jsapi;
|
||||
JS_RemoveExtraGCRootsTracer(jsapi.cx(), TraceOp, this);
|
||||
@ -164,37 +265,26 @@ ScriptPreloader::Cleanup()
|
||||
}
|
||||
|
||||
void
|
||||
ScriptPreloader::FlushScripts(LinkedList<CachedScript>& scripts)
|
||||
ScriptPreloader::FlushCache()
|
||||
{
|
||||
for (auto next = scripts.getFirst(); next; ) {
|
||||
auto script = next;
|
||||
next = script->getNext();
|
||||
MonitorAutoLock mal(mMonitor);
|
||||
|
||||
for (auto& script : IterHash(mScripts)) {
|
||||
// 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;
|
||||
script.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (mSaveComplete && mChildCache) {
|
||||
mSaveComplete = false;
|
||||
|
||||
Unused << NS_NewNamedThread("SaveScripts",
|
||||
@ -205,16 +295,28 @@ ScriptPreloader::FlushCache()
|
||||
nsresult
|
||||
ScriptPreloader::Observe(nsISupports* subject, const char* topic, const char16_t* data)
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (!strcmp(topic, DELAYED_STARTUP_TOPIC)) {
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
obs->RemoveObserver(this, DELAYED_STARTUP_TOPIC);
|
||||
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
mStartupFinished = true;
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
if (mChildCache) {
|
||||
Unused << NS_NewNamedThread("SaveScripts",
|
||||
getter_AddRefs(mSaveThread), this);
|
||||
}
|
||||
} else if (!strcmp(topic, DOC_ELEM_INSERTED_TOPIC)) {
|
||||
obs->RemoveObserver(this, DOC_ELEM_INSERTED_TOPIC);
|
||||
|
||||
MOZ_ASSERT(XRE_IsContentProcess());
|
||||
|
||||
mStartupFinished = true;
|
||||
|
||||
if (mChildActor) {
|
||||
mChildActor->SendScriptsAndFinalize(mScripts);
|
||||
}
|
||||
} else if (!strcmp(topic, SHUTDOWN_TOPIC)) {
|
||||
ForceWriteCacheFile();
|
||||
} else if (!strcmp(topic, CLEANUP_TOPIC)) {
|
||||
@ -228,7 +330,7 @@ ScriptPreloader::Observe(nsISupports* subject, const char* topic, const char16_t
|
||||
|
||||
|
||||
Result<nsCOMPtr<nsIFile>, nsresult>
|
||||
ScriptPreloader::GetCacheFile(const char* leafName)
|
||||
ScriptPreloader::GetCacheFile(const nsAString& suffix)
|
||||
{
|
||||
nsCOMPtr<nsIFile> cacheFile;
|
||||
NS_TRY(mProfD->Clone(getter_AddRefs(cacheFile)));
|
||||
@ -236,12 +338,12 @@ ScriptPreloader::GetCacheFile(const char* leafName)
|
||||
NS_TRY(cacheFile->AppendNative(NS_LITERAL_CSTRING("startupCache")));
|
||||
Unused << cacheFile->Create(nsIFile::DIRECTORY_TYPE, 0777);
|
||||
|
||||
NS_TRY(cacheFile->AppendNative(nsDependentCString(leafName)));
|
||||
NS_TRY(cacheFile->Append(mBaseName + suffix));
|
||||
|
||||
return Move(cacheFile);
|
||||
}
|
||||
|
||||
static const uint8_t MAGIC[] = "mozXDRcache";
|
||||
static const uint8_t MAGIC[] = "mozXDRcachev001";
|
||||
|
||||
Result<Ok, nsresult>
|
||||
ScriptPreloader::OpenCache()
|
||||
@ -249,14 +351,14 @@ ScriptPreloader::OpenCache()
|
||||
NS_TRY(NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(mProfD)));
|
||||
|
||||
nsCOMPtr<nsIFile> cacheFile;
|
||||
MOZ_TRY_VAR(cacheFile, GetCacheFile("scriptCache.bin"));
|
||||
MOZ_TRY_VAR(cacheFile, GetCacheFile(NS_LITERAL_STRING(".bin")));
|
||||
|
||||
bool exists;
|
||||
NS_TRY(cacheFile->Exists(&exists));
|
||||
if (exists) {
|
||||
NS_TRY(cacheFile->MoveTo(nullptr, NS_LITERAL_STRING("scriptCache-current.bin")));
|
||||
NS_TRY(cacheFile->MoveTo(nullptr, mBaseName + NS_LITERAL_STRING("-current.bin")));
|
||||
} else {
|
||||
NS_TRY(cacheFile->SetLeafName(NS_LITERAL_STRING("scriptCache-current.bin")));
|
||||
NS_TRY(cacheFile->SetLeafName(mBaseName + NS_LITERAL_STRING("-current.bin")));
|
||||
NS_TRY(cacheFile->Exists(&exists));
|
||||
if (!exists) {
|
||||
return Err(NS_ERROR_FILE_NOT_FOUND);
|
||||
@ -271,9 +373,10 @@ ScriptPreloader::OpenCache()
|
||||
// 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()
|
||||
ScriptPreloader::InitCache(const nsAString& basePath)
|
||||
{
|
||||
mCacheInitialized = true;
|
||||
mBaseName = basePath;
|
||||
|
||||
RegisterWeakMemoryReporter(this);
|
||||
|
||||
@ -283,6 +386,31 @@ ScriptPreloader::InitCache()
|
||||
|
||||
MOZ_TRY(OpenCache());
|
||||
|
||||
return InitCacheInternal();
|
||||
}
|
||||
|
||||
Result<Ok, nsresult>
|
||||
ScriptPreloader::InitCache(const Maybe<ipc::FileDescriptor>& cacheFile, ScriptCacheChild* cacheChild)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsContentProcess());
|
||||
|
||||
mCacheInitialized = true;
|
||||
mChildActor = cacheChild;
|
||||
|
||||
RegisterWeakMemoryReporter(this);
|
||||
|
||||
if (cacheFile.isNothing()){
|
||||
return Ok();
|
||||
}
|
||||
|
||||
MOZ_TRY(mCacheData.init(cacheFile.ref()));
|
||||
|
||||
return InitCacheInternal();
|
||||
}
|
||||
|
||||
Result<Ok, nsresult>
|
||||
ScriptPreloader::InitCacheInternal()
|
||||
{
|
||||
auto size = mCacheData.size();
|
||||
|
||||
uint32_t headerSize;
|
||||
@ -305,8 +433,12 @@ ScriptPreloader::InitCache()
|
||||
return Err(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
AutoTArray<CachedScript*, 256> scripts;
|
||||
|
||||
{
|
||||
AutoCleanLinkedList<CachedScript> scripts;
|
||||
auto cleanup = MakeScopeExit([&] () {
|
||||
mScripts.Clear();
|
||||
});
|
||||
|
||||
Range<uint8_t> header(data, data + headerSize);
|
||||
data += headerSize;
|
||||
@ -315,7 +447,8 @@ ScriptPreloader::InitCache()
|
||||
|
||||
size_t offset = 0;
|
||||
while (!buf.finished()) {
|
||||
auto script = MakeUnique<CachedScript>(buf);
|
||||
auto script = MakeUnique<CachedScript>(*this, buf);
|
||||
MOZ_RELEASE_ASSERT(script);
|
||||
|
||||
auto scriptData = data + script->mOffset;
|
||||
if (scriptData + script->mSize > end) {
|
||||
@ -331,17 +464,16 @@ ScriptPreloader::InitCache()
|
||||
|
||||
script->mXDRRange.emplace(scriptData, scriptData + script->mSize);
|
||||
|
||||
scripts.insertBack(script.release());
|
||||
scripts.AppendElement(script.get());
|
||||
mScripts.Put(script->mCachePath, script.get());
|
||||
Unused << script.release();
|
||||
}
|
||||
|
||||
if (buf.error()) {
|
||||
return Err(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
for (auto script : scripts) {
|
||||
mScripts.Put(script->mCachePath, script);
|
||||
}
|
||||
mRestoredScripts = Move(scripts);
|
||||
cleanup.release();
|
||||
}
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
@ -352,8 +484,11 @@ ScriptPreloader::InitCache()
|
||||
LOG(Info, "Off-thread decoding scripts...\n");
|
||||
|
||||
JS::CompileOptions options(cx, JSVERSION_LATEST);
|
||||
for (auto script : mRestoredScripts) {
|
||||
if (script->mSize > MIN_OFFTHREAD_SIZE &&
|
||||
|
||||
for (auto& script : scripts) {
|
||||
// Only async decode scripts which have been used in this process type.
|
||||
if (script->mProcessTypes.contains(CurrentProcessType()) &&
|
||||
script->AsyncDecodable() &&
|
||||
JS::CanCompileOffThread(cx, options, script->mSize)) {
|
||||
DecodeScriptOffThread(cx, script);
|
||||
} else {
|
||||
@ -381,52 +516,47 @@ ScriptPreloader::PrepareCacheWrite()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
auto cleanup = MakeScopeExit([&] () {
|
||||
if (mChildCache) {
|
||||
mChildCache->PrepareCacheWrite();
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
bool found = Find(IterHash(mScripts), [] (CachedScript* script) {
|
||||
return (script->mStatus == ScriptStatus::Restored ||
|
||||
!script->HasRange() || script->HasArray());
|
||||
});
|
||||
|
||||
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);
|
||||
for (auto& script : IterHash(mScripts, Match<ScriptStatus::Saved>())) {
|
||||
// Don't write any scripts that are also in the child cache. They'll be
|
||||
// loaded from the child cache in that case, so there's no need to write
|
||||
// them twice.
|
||||
CachedScript* childScript = mChildCache ? mChildCache->mScripts.Get(script->mCachePath) : nullptr;
|
||||
if (childScript) {
|
||||
if (childScript->mStatus == ScriptStatus::Saved) {
|
||||
childScript->UpdateLoadTime(script->mLoadTime);
|
||||
childScript->mProcessTypes += script->mProcessTypes;
|
||||
} else {
|
||||
childScript = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store async-decoded scripts contiguously, since they're loaded
|
||||
// immediately at startup.
|
||||
while (CachedScript* s = asyncScripts.popLast()) {
|
||||
mSavedScripts.insertFront(s);
|
||||
if (childScript || (!script->mSize && !script->XDREncode(jsapi.cx()))) {
|
||||
script.Remove();
|
||||
} else {
|
||||
script->mSize = script->Range().length();
|
||||
}
|
||||
}
|
||||
|
||||
mDataPrepared = true;
|
||||
@ -443,6 +573,7 @@ ScriptPreloader::PrepareCacheWrite()
|
||||
// - 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 bit field describing which process types the script is used in.
|
||||
//
|
||||
// - 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.
|
||||
@ -465,7 +596,7 @@ ScriptPreloader::WriteCache()
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> cacheFile;
|
||||
MOZ_TRY_VAR(cacheFile, GetCacheFile("scriptCache-new.bin"));
|
||||
MOZ_TRY_VAR(cacheFile, GetCacheFile(NS_LITERAL_STRING("-new.bin")));
|
||||
|
||||
bool exists;
|
||||
NS_TRY(cacheFile->Exists(&exists));
|
||||
@ -473,31 +604,45 @@ ScriptPreloader::WriteCache()
|
||||
NS_TRY(cacheFile->Remove(false));
|
||||
}
|
||||
|
||||
AutoFDClose fd;
|
||||
NS_TRY(cacheFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 0644, &fd.rwget()));
|
||||
{
|
||||
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);
|
||||
nsTArray<CachedScript*> scripts;
|
||||
for (auto& script : IterHash(mScripts, Match<ScriptStatus::Saved>())) {
|
||||
scripts.AppendElement(script);
|
||||
}
|
||||
|
||||
offset += script->mSize;
|
||||
// Sort scripts by load time, with async loaded scripts before sync scripts.
|
||||
// Since async scripts are always loaded immediately at startup, it helps to
|
||||
// have them stored contiguously.
|
||||
scripts.Sort(CachedScript::Comparator());
|
||||
|
||||
OutputBuffer buf;
|
||||
size_t offset = 0;
|
||||
for (auto script : scripts) {
|
||||
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 : scripts) {
|
||||
MOZ_TRY(Write(fd, script->Range().begin().get(), script->mSize));
|
||||
|
||||
if (script->mScript) {
|
||||
script->FreeData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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")));
|
||||
NS_TRY(cacheFile->MoveTo(nullptr, mBaseName + NS_LITERAL_STRING(".bin")));
|
||||
|
||||
return Ok();
|
||||
}
|
||||
@ -513,7 +658,11 @@ ScriptPreloader::Run()
|
||||
// during early startup.
|
||||
mal.Wait(10000);
|
||||
|
||||
Unused << WriteCache();
|
||||
auto result = WriteCache();
|
||||
Unused << NS_WARN_IF(result.isErr());
|
||||
|
||||
result = mChildCache->WriteCache();
|
||||
Unused << NS_WARN_IF(result.isErr());
|
||||
|
||||
mSaveComplete = true;
|
||||
NS_ReleaseOnMainThread(mSaveThread.forget());
|
||||
@ -522,27 +671,13 @@ ScriptPreloader::Run()
|
||||
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)
|
||||
JS::HandleScript jsscript)
|
||||
{
|
||||
if (mStartupFinished || !mCacheInitialized) {
|
||||
return;
|
||||
}
|
||||
// Don't bother trying to cache any URLs with cache-busting query
|
||||
// parameters.
|
||||
if (cachePath.FindChar('?') >= 0) {
|
||||
if (mStartupFinished || !mCacheInitialized || cachePath.FindChar('?') >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -552,30 +687,59 @@ ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
|
||||
return;
|
||||
}
|
||||
|
||||
bool exists = mScripts.Get(cachePath);
|
||||
auto script = mScripts.LookupOrAdd(cachePath, *this, url, cachePath, jsscript);
|
||||
|
||||
CachedScript* restored = nullptr;
|
||||
if (exists) {
|
||||
restored = FindScript(mRestoredScripts, cachePath);
|
||||
if (script->mStatus == ScriptStatus::Restored) {
|
||||
script->mStatus = ScriptStatus::Saved;
|
||||
|
||||
MOZ_ASSERT(jsscript);
|
||||
script->mScript = jsscript;
|
||||
script->mReadyToExecute = true;
|
||||
}
|
||||
|
||||
if (restored) {
|
||||
restored->remove();
|
||||
mSavedScripts.insertBack(restored);
|
||||
script->UpdateLoadTime(TimeStamp::Now());
|
||||
script->mProcessTypes += CurrentProcessType();
|
||||
}
|
||||
|
||||
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);
|
||||
void
|
||||
ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
|
||||
ProcessType processType, nsTArray<uint8_t>&& xdrData,
|
||||
TimeStamp loadTime)
|
||||
{
|
||||
auto script = mScripts.LookupOrAdd(cachePath, *this, url, cachePath, nullptr);
|
||||
|
||||
if (script->mStatus == ScriptStatus::Restored) {
|
||||
script->mStatus = ScriptStatus::Saved;
|
||||
|
||||
script->mReadyToExecute = true;
|
||||
} else {
|
||||
if (!script->HasRange()) {
|
||||
MOZ_ASSERT(!script->HasArray());
|
||||
|
||||
script->mSize = xdrData.Length();
|
||||
script->mXDRData.construct<nsTArray<uint8_t>>(Forward<nsTArray<uint8_t>>(xdrData));
|
||||
|
||||
auto& data = script->Array();
|
||||
script->mXDRRange.emplace(data.Elements(), data.Length());
|
||||
}
|
||||
}
|
||||
|
||||
script->UpdateLoadTime(loadTime);
|
||||
script->mProcessTypes += processType;
|
||||
}
|
||||
|
||||
JSScript*
|
||||
ScriptPreloader::GetCachedScript(JSContext* cx, const nsCString& path)
|
||||
{
|
||||
// If a script is used by both the parent and the child, it's stored only
|
||||
// in the child cache.
|
||||
if (mChildCache) {
|
||||
auto script = mChildCache->GetCachedScript(cx, path);
|
||||
if (script) {
|
||||
return script;
|
||||
}
|
||||
}
|
||||
|
||||
auto script = mScripts.Get(path);
|
||||
if (script) {
|
||||
return WaitForCachedScript(cx, script);
|
||||
@ -637,7 +801,7 @@ ScriptPreloader::OffThreadDecodeCallback(void* token, void* context)
|
||||
{
|
||||
auto script = static_cast<CachedScript*>(context);
|
||||
|
||||
MonitorAutoLock mal(GetSingleton().mMonitor);
|
||||
MonitorAutoLock mal(script->mCache.mMonitor);
|
||||
|
||||
if (script->mReadyToExecute) {
|
||||
// We've already executed this script on the main thread, and opted to
|
||||
@ -645,7 +809,7 @@ ScriptPreloader::OffThreadDecodeCallback(void* token, void* context)
|
||||
// finish. So just cancel the off-thread parse rather than completing
|
||||
// it.
|
||||
NS_DispatchToMainThread(
|
||||
NewRunnableMethod<void*>(&GetSingleton(),
|
||||
NewRunnableMethod<void*>(&script->mCache,
|
||||
&ScriptPreloader::CancelOffThreadParse,
|
||||
token));
|
||||
return;
|
||||
@ -657,8 +821,9 @@ ScriptPreloader::OffThreadDecodeCallback(void* token, void* context)
|
||||
mal.NotifyAll();
|
||||
}
|
||||
|
||||
inline
|
||||
ScriptPreloader::CachedScript::CachedScript(InputBuffer& buf)
|
||||
ScriptPreloader::CachedScript::CachedScript(ScriptPreloader& cache, InputBuffer& buf)
|
||||
: mCache(cache)
|
||||
, mStatus(ScriptStatus::Restored)
|
||||
{
|
||||
Code(buf);
|
||||
}
|
||||
@ -669,11 +834,11 @@ ScriptPreloader::CachedScript::XDREncode(JSContext* cx)
|
||||
JSAutoCompartment ac(cx, mScript);
|
||||
JS::RootedScript jsscript(cx, mScript);
|
||||
|
||||
mXDRData.emplace();
|
||||
mXDRData.construct<JS::TranscodeBuffer>();
|
||||
|
||||
JS::TranscodeResult code = JS::EncodeScript(cx, Data(), jsscript);
|
||||
JS::TranscodeResult code = JS::EncodeScript(cx, Buffer(), jsscript);
|
||||
if (code == JS::TranscodeResult_Ok) {
|
||||
mXDRRange.emplace(Data().begin(), Data().length());
|
||||
mXDRRange.emplace(Buffer().begin(), Buffer().length());
|
||||
return true;
|
||||
}
|
||||
JS_ClearPendingException(cx);
|
||||
@ -684,7 +849,7 @@ void
|
||||
ScriptPreloader::CachedScript::Cancel()
|
||||
{
|
||||
if (mToken) {
|
||||
GetSingleton().mMonitor.AssertCurrentThreadOwns();
|
||||
mCache.mMonitor.AssertCurrentThreadOwns();
|
||||
|
||||
AutoSafeJSAPI jsapi;
|
||||
JS::CancelOffThreadScriptDecoder(jsapi.cx(), mToken);
|
||||
@ -708,11 +873,15 @@ ScriptPreloader::CachedScript::GetJSScript(JSContext* cx)
|
||||
// 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());
|
||||
MOZ_ASSERT(HasRange());
|
||||
|
||||
JS::RootedScript script(cx);
|
||||
if (JS::DecodeScript(cx, Range(), &script)) {
|
||||
mScript = script;
|
||||
|
||||
if (mCache.mSaveComplete) {
|
||||
FreeData();
|
||||
}
|
||||
}
|
||||
|
||||
return mScript;
|
||||
|
@ -6,27 +6,50 @@
|
||||
#ifndef ScriptPreloader_h
|
||||
#define ScriptPreloader_h
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/EnumSet.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/MaybeOneOf.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 "nsClassHashtable.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIThread.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "js/GCAnnotations.h"
|
||||
|
||||
#include <prio.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class ContentParent;
|
||||
}
|
||||
namespace ipc {
|
||||
class FileDescriptor;
|
||||
}
|
||||
namespace loader {
|
||||
class InputBuffer;
|
||||
class ScriptCacheChild;
|
||||
|
||||
enum class ProcessType : uint8_t {
|
||||
Parent,
|
||||
Web,
|
||||
Extension,
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Matcher
|
||||
{
|
||||
virtual bool Matches(T) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
using namespace mozilla::loader;
|
||||
@ -37,6 +60,8 @@ class ScriptPreloader : public nsIObserver
|
||||
{
|
||||
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
|
||||
|
||||
friend class mozilla::loader::ScriptCacheChild;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
@ -44,6 +69,9 @@ public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
static ScriptPreloader& GetSingleton();
|
||||
static ScriptPreloader& GetChildSingleton();
|
||||
|
||||
static ProcessType GetChildProcessType(const nsAString& remoteType);
|
||||
|
||||
// Retrieves the script with the given cache key from the script cache.
|
||||
// Returns null if the script is not cached.
|
||||
@ -54,15 +82,37 @@ public:
|
||||
// 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 NoteScript(const nsCString& url, const nsCString& cachePath,
|
||||
ProcessType processType, nsTArray<uint8_t>&& xdrData,
|
||||
TimeStamp loadTime);
|
||||
|
||||
// Initializes the script cache from the startup script cache file.
|
||||
Result<Ok, nsresult> InitCache(const nsAString& = NS_LITERAL_STRING("scriptCache"));
|
||||
|
||||
Result<Ok, nsresult> InitCache(const Maybe<ipc::FileDescriptor>& cacheFile, ScriptCacheChild* cacheChild);
|
||||
|
||||
private:
|
||||
Result<Ok, nsresult> InitCacheInternal();
|
||||
|
||||
public:
|
||||
void Trace(JSTracer* trc);
|
||||
|
||||
static ProcessType CurrentProcessType()
|
||||
{
|
||||
return sProcessType;
|
||||
}
|
||||
|
||||
static void InitContentChild(dom::ContentParent& parent);
|
||||
|
||||
protected:
|
||||
virtual ~ScriptPreloader() = default;
|
||||
|
||||
private:
|
||||
enum class ScriptStatus {
|
||||
Restored,
|
||||
Saved,
|
||||
};
|
||||
|
||||
// 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.
|
||||
@ -87,32 +137,84 @@ private:
|
||||
// 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>
|
||||
class CachedScript
|
||||
{
|
||||
public:
|
||||
CachedScript(CachedScript&&) = default;
|
||||
|
||||
CachedScript(const nsCString& url, const nsCString& cachePath, JSScript* script)
|
||||
: mURL(url)
|
||||
CachedScript(ScriptPreloader& cache, const nsCString& url, const nsCString& cachePath, JSScript* script)
|
||||
: mCache(cache)
|
||||
, mURL(url)
|
||||
, mCachePath(cachePath)
|
||||
, mStatus(ScriptStatus::Saved)
|
||||
, mScript(script)
|
||||
, mReadyToExecute(true)
|
||||
{}
|
||||
|
||||
explicit inline CachedScript(InputBuffer& buf);
|
||||
inline CachedScript(ScriptPreloader& cache, InputBuffer& buf);
|
||||
|
||||
~CachedScript()
|
||||
~CachedScript() = default;
|
||||
|
||||
// For use with nsTArray::Sort.
|
||||
//
|
||||
// Orders scripts by:
|
||||
//
|
||||
// 1) Async-decoded scripts before sync-decoded scripts, since the
|
||||
// former are needed immediately at startup, and should be stored
|
||||
// contiguously.
|
||||
// 2) Script load time, so that scripts which are needed earlier are
|
||||
// stored earlier, and scripts needed at approximately the same
|
||||
// time are stored approximately contiguously.
|
||||
struct Comparator
|
||||
{
|
||||
auto& cache = GetSingleton();
|
||||
#ifdef DEBUG
|
||||
auto hashValue = cache.mScripts.Get(mCachePath);
|
||||
MOZ_ASSERT_IF(hashValue, hashValue == this);
|
||||
#endif
|
||||
cache.mScripts.Remove(mCachePath);
|
||||
}
|
||||
bool Equals(const CachedScript* a, const CachedScript* b) const
|
||||
{
|
||||
return (a->AsyncDecodable() == b->AsyncDecodable() &&
|
||||
a->mLoadTime == b->mLoadTime);
|
||||
}
|
||||
|
||||
bool LessThan(const CachedScript* a, const CachedScript* b) const
|
||||
{
|
||||
if (a->AsyncDecodable() != b->AsyncDecodable()) {
|
||||
return a->AsyncDecodable();
|
||||
}
|
||||
return a->mLoadTime < b->mLoadTime;
|
||||
}
|
||||
};
|
||||
|
||||
struct StatusMatcher final : public Matcher<CachedScript*>
|
||||
{
|
||||
explicit StatusMatcher(ScriptStatus status) : mStatus(status) {}
|
||||
|
||||
virtual bool Matches(CachedScript* script)
|
||||
{
|
||||
return script->mStatus == mStatus;
|
||||
}
|
||||
|
||||
const ScriptStatus mStatus;
|
||||
};
|
||||
|
||||
void Cancel();
|
||||
|
||||
void FreeData()
|
||||
{
|
||||
// If the script data isn't mmapped, we need to release both it
|
||||
// and the Range that points to it at the same time.
|
||||
if (!mXDRData.empty()) {
|
||||
mXDRRange.reset();
|
||||
mXDRData.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateLoadTime(const TimeStamp& loadTime)
|
||||
{
|
||||
if (mLoadTime.IsNull() || loadTime < mLoadTime) {
|
||||
mLoadTime = loadTime;
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncDecodable() const { return mSize > MIN_OFFTHREAD_SIZE; }
|
||||
|
||||
// Encodes this script into XDR data, and stores the result in mXDRData.
|
||||
// Returns true on success, false on failure.
|
||||
bool XDREncode(JSContext* cx);
|
||||
@ -126,36 +228,58 @@ private:
|
||||
buffer.codeString(mCachePath);
|
||||
buffer.codeUint32(mOffset);
|
||||
buffer.codeUint32(mSize);
|
||||
buffer.codeUint8(mProcessTypes);
|
||||
}
|
||||
|
||||
// Returns the XDR data generated for this script during this session. See
|
||||
// mXDRData.
|
||||
JS::TranscodeBuffer& Data()
|
||||
JS::TranscodeBuffer& Buffer()
|
||||
{
|
||||
MOZ_ASSERT(mXDRData.isSome());
|
||||
return mXDRData.ref();
|
||||
MOZ_ASSERT(HasBuffer());
|
||||
return mXDRData.ref<JS::TranscodeBuffer>();
|
||||
}
|
||||
|
||||
bool HasBuffer() { return mXDRData.constructed<JS::TranscodeBuffer>(); }
|
||||
|
||||
// Returns the read-only XDR data for this script. See mXDRRange.
|
||||
const JS::TranscodeRange& Range()
|
||||
{
|
||||
MOZ_ASSERT(mXDRRange.isSome());
|
||||
MOZ_ASSERT(HasRange());
|
||||
return mXDRRange.ref();
|
||||
}
|
||||
|
||||
bool HasRange() { return mXDRRange.isSome(); }
|
||||
|
||||
nsTArray<uint8_t>& Array()
|
||||
{
|
||||
MOZ_ASSERT(HasArray());
|
||||
return mXDRData.ref<nsTArray<uint8_t>>();
|
||||
}
|
||||
|
||||
bool HasArray() { return mXDRData.constructed<nsTArray<uint8_t>>(); }
|
||||
|
||||
|
||||
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));
|
||||
|
||||
if (HasArray()) {
|
||||
size += Array().ShallowSizeOfExcludingThis(mallocSizeOf);
|
||||
} else if (HasBuffer()) {
|
||||
size += Buffer().sizeOfExcludingThis(mallocSizeOf);
|
||||
} else {
|
||||
return size;
|
||||
}
|
||||
|
||||
size += (mURL.SizeOfExcludingThisEvenIfShared(mallocSizeOf) +
|
||||
mCachePath.SizeOfExcludingThisEvenIfShared(mallocSizeOf));
|
||||
return size;
|
||||
}
|
||||
|
||||
ScriptPreloader& mCache;
|
||||
|
||||
// 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
|
||||
@ -168,6 +292,10 @@ private:
|
||||
// The size of this script's encoded XDR data.
|
||||
uint32_t mSize = 0;
|
||||
|
||||
ScriptStatus mStatus;
|
||||
|
||||
TimeStamp mLoadTime{};
|
||||
|
||||
JS::Heap<JSScript*> mScript;
|
||||
|
||||
// True if this script is ready to be executed. This means that either the
|
||||
@ -180,6 +308,9 @@ private:
|
||||
// has not yet been finalized on the main thread.
|
||||
void* mToken = nullptr;
|
||||
|
||||
// The set of processes in which this script has been used.
|
||||
EnumSet<ProcessType> mProcessTypes{};
|
||||
|
||||
// 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.
|
||||
@ -187,8 +318,15 @@ private:
|
||||
|
||||
// XDR data which was generated from a script compiled during this
|
||||
// session, and will be written to the cache file.
|
||||
Maybe<JS::TranscodeBuffer> mXDRData;
|
||||
};
|
||||
MaybeOneOf<JS::TranscodeBuffer, nsTArray<uint8_t>> mXDRData;
|
||||
} JS_HAZ_NON_GC_POINTER;
|
||||
|
||||
template <ScriptStatus status>
|
||||
static Matcher<CachedScript*>* Match()
|
||||
{
|
||||
static CachedScript::StatusMatcher matcher{status};
|
||||
return &matcher;
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -213,7 +351,6 @@ private:
|
||||
void Cleanup();
|
||||
|
||||
void FlushCache();
|
||||
void FlushScripts(LinkedList<CachedScript>& scripts);
|
||||
|
||||
// Opens the cache file for reading.
|
||||
Result<Ok, nsresult> OpenCache();
|
||||
@ -228,9 +365,7 @@ private:
|
||||
// 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);
|
||||
GetCacheFile(const nsAString& suffix);
|
||||
|
||||
// Waits for the given cached script to finish compiling off-thread, or
|
||||
// decodes it synchronously on the main thread, as appropriate.
|
||||
@ -248,26 +383,19 @@ private:
|
||||
mallocSizeOf(mSaveThread.get()) + mallocSizeOf(mProfD.get()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static size_t SizeOfLinkedList(LinkedList<T>& list, mozilla::MallocSizeOf mallocSizeOf)
|
||||
using ScriptHash = nsClassHashtable<nsCStringHashKey, CachedScript>;
|
||||
|
||||
template<ScriptStatus status>
|
||||
static size_t SizeOfHashEntries(ScriptHash& scripts, mozilla::MallocSizeOf mallocSizeOf)
|
||||
{
|
||||
size_t size = 0;
|
||||
for (auto elem : list) {
|
||||
for (auto elem : IterHash(scripts, Match<status>())) {
|
||||
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;
|
||||
ScriptHash mScripts;
|
||||
|
||||
// True after we've shown the first window, and are no longer adding new
|
||||
// scripts to the cache.
|
||||
@ -277,6 +405,18 @@ private:
|
||||
bool mSaveComplete = false;
|
||||
bool mDataPrepared = false;
|
||||
|
||||
// The process type of the current process.
|
||||
static ProcessType sProcessType;
|
||||
|
||||
// The process types for which remote processes have been initialized, and
|
||||
// are expected to send back script data.
|
||||
EnumSet<ProcessType> mInitializedProcesses{};
|
||||
|
||||
RefPtr<ScriptPreloader> mChildCache;
|
||||
ScriptCacheChild* mChildActor = nullptr;
|
||||
|
||||
nsString mBaseName;
|
||||
|
||||
nsCOMPtr<nsIFile> mProfD;
|
||||
nsCOMPtr<nsIThread> mSaveThread;
|
||||
|
||||
|
@ -9,6 +9,7 @@ UNIFIED_SOURCES += [
|
||||
'ChromeScriptLoader.cpp',
|
||||
'mozJSLoaderUtils.cpp',
|
||||
'mozJSSubScriptLoader.cpp',
|
||||
'ScriptCacheActors.cpp',
|
||||
'ScriptPreloader.cpp',
|
||||
]
|
||||
|
||||
@ -18,6 +19,10 @@ SOURCES += [
|
||||
'mozJSComponentLoader.cpp'
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'PScriptCache.ipdl',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'ScriptPreloader.h',
|
||||
]
|
||||
@ -28,6 +33,7 @@ EXPORTS.mozilla.dom += [
|
||||
|
||||
EXPORTS.mozilla.loader += [
|
||||
'AutoMemMap.h',
|
||||
'ScriptCacheActors.h',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
@ -43,5 +49,7 @@ LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-shadow']
|
||||
|
@ -683,28 +683,26 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo,
|
||||
rv = PathifyURI(aInfo.URI(), cachePath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (cache) {
|
||||
if (!mReuseLoaderGlobal) {
|
||||
script = ScriptPreloader::GetSingleton().GetCachedScript(cx, cachePath);
|
||||
if (!script) {
|
||||
rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
|
||||
}
|
||||
} else {
|
||||
rv = ReadCachedFunction(cache, cachePath, cx, mSystemPrincipal,
|
||||
function.address());
|
||||
if (!mReuseLoaderGlobal) {
|
||||
script = ScriptPreloader::GetSingleton().GetCachedScript(cx, cachePath);
|
||||
if (!script && cache) {
|
||||
ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
|
||||
}
|
||||
} else if (cache) {
|
||||
ReadCachedFunction(cache, cachePath, cx, mSystemPrincipal,
|
||||
function.address());
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
|
||||
} else {
|
||||
// This is ok, it just means the script is not yet in the
|
||||
// cache. Could mean that the cache was corrupted and got removed,
|
||||
// but either way we're going to write this out.
|
||||
writeToCache = true;
|
||||
// ReadCachedScript and ReadCachedFunction may have set a pending
|
||||
// exception.
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
if (script || function) {
|
||||
LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
|
||||
} else if (cache) {
|
||||
// This is ok, it just means the script is not yet in the
|
||||
// cache. Could mean that the cache was corrupted and got removed,
|
||||
// but either way we're going to write this out.
|
||||
writeToCache = true;
|
||||
// ReadCachedScript and ReadCachedFunction may have set a pending
|
||||
// exception.
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
|
||||
if (!script && !function) {
|
||||
|
@ -697,10 +697,10 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
|
||||
|
||||
RootedFunction function(cx);
|
||||
RootedScript script(cx);
|
||||
if (cache && !options.ignoreCache) {
|
||||
if (!options.ignoreCache) {
|
||||
if (!options.wantReturnValue)
|
||||
script = ScriptPreloader::GetSingleton().GetCachedScript(cx, cachePath);
|
||||
if (!script)
|
||||
if (!script && cache)
|
||||
rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
|
||||
if (NS_FAILED(rv) || !script) {
|
||||
// ReadCachedScript may have set a pending exception.
|
||||
|
81
js/xpconnect/loader/script_cache.py
Executable file
81
js/xpconnect/loader/script_cache.py
Executable file
@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python
|
||||
import io
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
|
||||
MAGIC = b'mozXDRcachev001\0'
|
||||
|
||||
|
||||
def usage():
|
||||
print("""Usage: script_cache.py <file.bin> ...
|
||||
|
||||
Decodes and prints out the contents of a startup script cache file
|
||||
(e.g., startupCache/scriptCache.bin) in human-readable form.""")
|
||||
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class ProcessTypes:
|
||||
Default = 0
|
||||
Web = 1
|
||||
Extension = 2
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def __str__(self):
|
||||
res = []
|
||||
if self.val & (1 << self.Default):
|
||||
res.append('Parent')
|
||||
if self.val & (1 << self.Web):
|
||||
res.append('Web')
|
||||
if self.val & (1 << self.Extension):
|
||||
res.append('Extension')
|
||||
return '|'.join(res)
|
||||
|
||||
|
||||
class InputBuffer(object):
|
||||
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.offset = 0
|
||||
|
||||
@property
|
||||
def remaining(self):
|
||||
return len(self.data) - self.offset
|
||||
|
||||
def unpack(self, fmt):
|
||||
res = struct.unpack_from(fmt, self.data, self.offset)
|
||||
self.offset += struct.calcsize(fmt)
|
||||
return res
|
||||
|
||||
def unpack_str(self):
|
||||
size, = self.unpack('<H')
|
||||
res = self.data[self.offset:self.offset + size].decode('utf-8')
|
||||
self.offset += size
|
||||
return res
|
||||
|
||||
|
||||
if len(sys.argv) < 2 or not os.path.exists(sys.argv[1]):
|
||||
usage()
|
||||
|
||||
for filename in sys.argv[1:]:
|
||||
with io.open(filename, 'rb') as f:
|
||||
magic = f.read(len(MAGIC))
|
||||
if magic != MAGIC:
|
||||
raise Exception('Bad magic number')
|
||||
|
||||
hdrSize, = struct.unpack('<I', f.read(4))
|
||||
|
||||
hdr = InputBuffer(f.read(hdrSize))
|
||||
|
||||
i = 0
|
||||
while hdr.remaining:
|
||||
i += 1
|
||||
print('{}: {}'.format(i, hdr.unpack_str()))
|
||||
print(' Key: {}'.format(hdr.unpack_str()))
|
||||
print(' Offset: {:>9,}'.format(*hdr.unpack('<I')))
|
||||
print(' Size: {:>9,}'.format(*hdr.unpack('<I')))
|
||||
print(' Processes: {}'.format(ProcessTypes(*hdr.unpack('B'))))
|
||||
print('')
|
@ -2916,7 +2916,7 @@ nsXPCComponents_Utils::SetWantXrays(HandleValue vscope, JSContext* cx)
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::ForcePermissiveCOWs(JSContext* cx)
|
||||
{
|
||||
CrashIfNotInAutomation();
|
||||
xpc::CrashIfNotInAutomation();
|
||||
CompartmentPrivate::Get(CurrentGlobalOrNull(cx))->forcePermissiveCOWs = true;
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2927,7 +2927,7 @@ nsXPCComponents_Utils::ForcePrivilegedComponentsForScope(HandleValue vscope,
|
||||
{
|
||||
if (!vscope.isObject())
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
CrashIfNotInAutomation();
|
||||
xpc::CrashIfNotInAutomation();
|
||||
JSObject* scopeObj = js::UncheckedUnwrap(&vscope.toObject());
|
||||
XPCWrappedNativeScope* scope = ObjectScope(scopeObj);
|
||||
scope->ForcePrivilegedComponents();
|
||||
@ -3012,6 +3012,22 @@ nsXPCComponents_Utils::SetGCZeal(int32_t aValue, JSContext* cx)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::GetIsInAutomation(bool* aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aResult);
|
||||
|
||||
*aResult = xpc::IsInAutomation();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::CrashIfNotInAutomation()
|
||||
{
|
||||
xpc::CrashIfNotInAutomation();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::NukeSandbox(HandleValue obj, JSContext* cx)
|
||||
{
|
||||
|
@ -9323,6 +9323,22 @@ nsLayoutUtils::GetCumulativeApzCallbackTransform(nsIFrame* aFrame)
|
||||
return delta;
|
||||
}
|
||||
|
||||
/* static */ nsRect
|
||||
nsLayoutUtils::ComputePartialPrerenderArea(const nsRect& aDirtyRect,
|
||||
const nsRect& aOverflow,
|
||||
const nsSize& aPrerenderSize)
|
||||
{
|
||||
// Simple calculation for now: center the pre-render area on the dirty rect,
|
||||
// and clamp to the overflow area. Later we can do more advanced things like
|
||||
// redistributing from one axis to another, or from one side to another.
|
||||
nscoord xExcess = aPrerenderSize.width - aDirtyRect.width;
|
||||
nscoord yExcess = aPrerenderSize.height - aDirtyRect.height;
|
||||
nsRect result = aDirtyRect;
|
||||
result.Inflate(xExcess / 2, yExcess / 2);
|
||||
return result.MoveInsideAndClamp(aOverflow);
|
||||
}
|
||||
|
||||
|
||||
/* static */ bool
|
||||
nsLayoutUtils::SupportsServoStyleBackend(nsIDocument* aDocument)
|
||||
{
|
||||
|
@ -2891,6 +2891,19 @@ public:
|
||||
*/
|
||||
static CSSPoint GetCumulativeApzCallbackTransform(nsIFrame* aFrame);
|
||||
|
||||
/**
|
||||
* Compute a rect to pre-render in cases where we want to render more of
|
||||
* something than what is visible (usually to support async transformation).
|
||||
* @param aDirtyRect the area that's visible
|
||||
* @param aOverflow the total size of the thing we're rendering
|
||||
* @param aPrerenderSize how large of an area we're willing to render
|
||||
* @return A rectangle that includes |aDirtyRect|, is clamped to |aOverflow|,
|
||||
* and is no larger than |aPrerenderSize|.
|
||||
*/
|
||||
static nsRect ComputePartialPrerenderArea(const nsRect& aDirtyRect,
|
||||
const nsRect& aOverflow,
|
||||
const nsSize& aPrerenderSize);
|
||||
|
||||
/*
|
||||
* Returns whether the given document supports being rendered with a
|
||||
* Servo-backed style system. This checks whether Stylo is enabled
|
||||
|
11
layout/base/tests/bug1359411-ref.html
Normal file
11
layout/base/tests/bug1359411-ref.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head><style>
|
||||
i:before{content:"X"}
|
||||
</style></head><body onload="runTest()"><div><button type="button"><i></i></button><p></p></div><editor contenteditable="true">focus me, then press the UP key</editor>
|
||||
<script>
|
||||
function runTest() {
|
||||
document.body.offsetHeight;
|
||||
var e = document.querySelector('editor');
|
||||
e.focus()
|
||||
}
|
||||
</script>
|
12
layout/base/tests/bug1359411.html
Normal file
12
layout/base/tests/bug1359411.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head><script src="/tests/SimpleTest/EventUtils.js"></script><style>
|
||||
i:before{content:"X"}
|
||||
</style></head><body onload="runTest()"><div><button type="button"><i></i></button><p></p></div><editor contenteditable="true" onfocus="sendKey('UP')">focus me, then press the UP key</editor>
|
||||
<script>var e = document.querySelector('editor'); e.focus()</script>
|
||||
<script>
|
||||
function runTest() {
|
||||
document.body.offsetHeight;
|
||||
var e = document.querySelector('editor');
|
||||
e.focus()
|
||||
}
|
||||
</script>
|
@ -287,6 +287,8 @@ support-files =
|
||||
bug1354478-5-ref.html
|
||||
bug1354478-6.html
|
||||
bug1354478-6-ref.html
|
||||
bug1359411.html
|
||||
bug1359411-ref.html
|
||||
image_rgrg-256x256.png
|
||||
input-invalid-ref.html
|
||||
input-maxlength-invalid-change.html
|
||||
|
@ -192,6 +192,7 @@ var tests = [
|
||||
[ 'bug1354478-4.html' , 'bug1354478-4-ref.html'] ,
|
||||
[ 'bug1354478-5.html' , 'bug1354478-5-ref.html'] ,
|
||||
[ 'bug1354478-6.html' , 'bug1354478-6-ref.html'] ,
|
||||
[ 'bug1359411.html' , 'bug1359411-ref.html' ] ,
|
||||
function() {SpecialPowers.pushPrefEnv({'clear': [['layout.accessiblecaret.enabled']]}, nextTest);} ,
|
||||
];
|
||||
|
||||
|
@ -3093,13 +3093,33 @@ ScrollFrameHelper::AppendScrollPartsTo(nsDisplayListBuilder* aBuilder,
|
||||
appendToTopFlags |= APPEND_SCROLLBAR_CONTAINER;
|
||||
}
|
||||
|
||||
// The display port doesn't necessarily include the scrollbars, so just
|
||||
// include all of the scrollbars if we are in a RCD-RSF. We only do
|
||||
// this for the root scrollframe of the root content document, which is
|
||||
// zoomable, and where the scrollbar sizes are bounded by the widget.
|
||||
nsRect dirty = mIsRoot && mOuter->PresContext()->IsRootContentDocument()
|
||||
? scrollParts[i]->GetVisualOverflowRectRelativeToParent()
|
||||
: aDirtyRect;
|
||||
// The display port doesn't necessarily include the scrollbars.
|
||||
bool thumbGetsLayer = (scrollTargetId != FrameMetrics::NULL_SCROLL_ID);
|
||||
bool isRcdRsf = mIsRoot && mOuter->PresContext()->IsRootContentDocument();
|
||||
nsRect dirty = aDirtyRect;
|
||||
if (isRcdRsf || thumbGetsLayer) {
|
||||
nsRect overflow = scrollParts[i]->GetVisualOverflowRectRelativeToParent();
|
||||
if (isRcdRsf) {
|
||||
// For the root content document's root scroll frame (RCD-RSF), include
|
||||
// all of the scrollbars. We only do this for the RCD-RSF, which is
|
||||
// zoomable, and where the scrollbar sizes are bounded by the widget.
|
||||
dirty = overflow;
|
||||
} else {
|
||||
// For subframes, we still try to prerender parts of the scrollbar that
|
||||
// are not currently visible, because they might be brought into view
|
||||
// by async scrolling, but we bound the area to render by the size of
|
||||
// the root reference frame (because subframe scrollbars can be
|
||||
// arbitrary large).
|
||||
nsSize refSize = aBuilder->RootReferenceFrame()->GetSize();
|
||||
gfxSize scale = nsLayoutUtils::GetTransformToAncestorScale(mOuter);
|
||||
if (scale.width != 0 && scale.height != 0) {
|
||||
refSize.width /= scale.width;
|
||||
refSize.height /= scale.height;
|
||||
}
|
||||
dirty = nsLayoutUtils::ComputePartialPrerenderArea(dirty, overflow, refSize);
|
||||
}
|
||||
}
|
||||
|
||||
nsDisplayListBuilder::AutoBuildingDisplayList
|
||||
buildingForChild(aBuilder, scrollParts[i],
|
||||
dirty + mOuter->GetOffsetTo(scrollParts[i]), true);
|
||||
|
@ -12,19 +12,6 @@ nsMathMLSelectedFrame::~nsMathMLSelectedFrame()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
nsMathMLSelectedFrame::Init(nsIContent* aContent,
|
||||
nsContainerFrame* aParent,
|
||||
nsIFrame* aPrevInFlow)
|
||||
{
|
||||
// Init our local attributes
|
||||
mInvalidMarkup = false;
|
||||
mSelectedFrame = nullptr;
|
||||
|
||||
// Let the base class do the rest
|
||||
nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMathMLSelectedFrame::TransmitAutomaticData()
|
||||
{
|
||||
|
@ -10,11 +10,6 @@
|
||||
|
||||
class nsMathMLSelectedFrame : public nsMathMLContainerFrame {
|
||||
public:
|
||||
virtual void
|
||||
Init(nsIContent* aContent,
|
||||
nsContainerFrame* aParent,
|
||||
nsIFrame* aPrevInFlow) override;
|
||||
|
||||
NS_IMETHOD
|
||||
TransmitAutomaticData() override;
|
||||
|
||||
@ -54,7 +49,9 @@ public:
|
||||
|
||||
protected:
|
||||
explicit nsMathMLSelectedFrame(nsStyleContext* aContext) :
|
||||
nsMathMLContainerFrame(aContext) {}
|
||||
nsMathMLContainerFrame(aContext),
|
||||
mSelectedFrame(nullptr),
|
||||
mInvalidMarkup(false) {}
|
||||
virtual ~nsMathMLSelectedFrame();
|
||||
|
||||
virtual nsIFrame* GetSelectedFrame() = 0;
|
||||
|
@ -6291,15 +6291,15 @@ FrameLayerBuilder::GetMostRecentGeometry(nsDisplayItem* aItem)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gfx::Rect
|
||||
CalculateBounds(const nsTArray<DisplayItemClip::RoundedRect>& aRects, int32_t A2D)
|
||||
static gfx::Rect
|
||||
CalculateBounds(const nsTArray<DisplayItemClip::RoundedRect>& aRects, int32_t aAppUnitsPerDevPixel)
|
||||
{
|
||||
nsRect bounds = aRects[0].mRect;
|
||||
for (uint32_t i = 1; i < aRects.Length(); ++i) {
|
||||
bounds.UnionRect(bounds, aRects[i].mRect);
|
||||
}
|
||||
|
||||
return gfx::ToRect(nsLayoutUtils::RectToGfxRect(bounds, A2D));
|
||||
return gfx::Rect(bounds.ToNearestPixels(aAppUnitsPerDevPixel));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -6376,7 +6376,6 @@ ContainerState::CreateMaskLayer(Layer *aLayer,
|
||||
return maskLayer.forget();
|
||||
}
|
||||
|
||||
// calculate a more precise bounding rect
|
||||
gfx::Rect boundingRect = CalculateBounds(newData.mRoundedClipRects,
|
||||
newData.mAppUnitsPerDevPixel);
|
||||
boundingRect.Scale(mParameters.mXScale, mParameters.mYScale);
|
||||
|
@ -7317,20 +7317,7 @@ bool
|
||||
nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
|
||||
{
|
||||
return mAllowAsyncAnimation;
|
||||
}
|
||||
|
||||
static nsRect ComputePartialPrerenderArea(const nsRect& aDirtyRect,
|
||||
const nsRect& aOverflow,
|
||||
const nsSize& aPrerenderSize)
|
||||
{
|
||||
// Simple calculation for now: center the pre-render area on the dirty rect,
|
||||
// and clamp to the overflow area. Later we can do more advanced things like
|
||||
// redistributing from one axis to another, or from one side to another.
|
||||
nscoord xExcess = aPrerenderSize.width - aDirtyRect.width;
|
||||
nscoord yExcess = aPrerenderSize.height - aDirtyRect.height;
|
||||
nsRect result = aDirtyRect;
|
||||
result.Inflate(xExcess / 2, yExcess / 2);
|
||||
return result.MoveInsideAndClamp(aOverflow);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -7403,7 +7390,7 @@ nsDisplayTransform::ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBui
|
||||
*aDirtyRect = overflow;
|
||||
return FullPrerender;
|
||||
} else if (gfxPrefs::PartiallyPrerenderAnimatedContent()) {
|
||||
*aDirtyRect = ComputePartialPrerenderArea(*aDirtyRect, overflow, maxSize);
|
||||
*aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea(*aDirtyRect, overflow, maxSize);
|
||||
return PartialPrerender;
|
||||
}
|
||||
|
||||
|
@ -1175,9 +1175,40 @@ CreateStyleImageRequest(nsStyleImageRequest::Mode aModeFlags,
|
||||
|
||||
NS_IMPL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::ImageValue, ImageValue);
|
||||
|
||||
static already_AddRefed<nsStyleImageRequest>
|
||||
CreateStyleImageRequest(nsStyleImageRequest::Mode aModeFlags,
|
||||
mozilla::css::ImageValue* aImageValue)
|
||||
{
|
||||
RefPtr<nsStyleImageRequest> req =
|
||||
new nsStyleImageRequest(aModeFlags, aImageValue);
|
||||
return req.forget();
|
||||
}
|
||||
|
||||
mozilla::css::ImageValue*
|
||||
Gecko_ImageValue_Create(ServoBundledURI aURI)
|
||||
{
|
||||
NS_ConvertUTF8toUTF16 url(reinterpret_cast<const char*>(aURI.mURLString),
|
||||
aURI.mURLStringLength);
|
||||
|
||||
RefPtr<ImageValue> value(new ImageValue(url, do_AddRef(aURI.mExtraData)));
|
||||
return value.forget().take();
|
||||
}
|
||||
|
||||
void
|
||||
Gecko_SetLayerImageImageValue(nsStyleImage* aImage,
|
||||
mozilla::css::ImageValue* aImageValue)
|
||||
{
|
||||
MOZ_ASSERT(aImage && aImageValue);
|
||||
|
||||
RefPtr<nsStyleImageRequest> req =
|
||||
CreateStyleImageRequest(nsStyleImageRequest::Mode::Track, aImageValue);
|
||||
aImage->SetImageRequest(req.forget());
|
||||
}
|
||||
|
||||
void
|
||||
Gecko_SetUrlImageValue(nsStyleImage* aImage, ServoBundledURI aURI)
|
||||
{
|
||||
MOZ_ASSERT(aImage);
|
||||
RefPtr<nsStyleImageRequest> req =
|
||||
CreateStyleImageRequest(nsStyleImageRequest::Mode::Track, aURI);
|
||||
aImage->SetImageRequest(req.forget());
|
||||
@ -1212,6 +1243,16 @@ Gecko_SetCursorArrayLength(nsStyleUserInterface* aStyleUI, size_t aLen)
|
||||
aStyleUI->mCursorImages.SetLength(aLen);
|
||||
}
|
||||
|
||||
void
|
||||
Gecko_SetCursorImageValue(nsCursorImage* aCursor,
|
||||
mozilla::css::ImageValue* aImageValue)
|
||||
{
|
||||
MOZ_ASSERT(aCursor && aImageValue);
|
||||
|
||||
aCursor->mImage =
|
||||
CreateStyleImageRequest(nsStyleImageRequest::Mode::Discard, aImageValue);
|
||||
}
|
||||
|
||||
void
|
||||
Gecko_SetCursorImage(nsCursorImage* aCursor, ServoBundledURI aURI)
|
||||
{
|
||||
@ -1226,6 +1267,17 @@ Gecko_CopyCursorArrayFrom(nsStyleUserInterface* aDest,
|
||||
aDest->mCursorImages = aSrc->mCursorImages;
|
||||
}
|
||||
|
||||
void
|
||||
Gecko_SetContentDataImageValue(nsStyleContentData* aContent,
|
||||
mozilla::css::ImageValue* aImageValue)
|
||||
{
|
||||
MOZ_ASSERT(aContent && aImageValue);
|
||||
|
||||
RefPtr<nsStyleImageRequest> req =
|
||||
CreateStyleImageRequest(nsStyleImageRequest::Mode::Track, aImageValue);
|
||||
aContent->SetImageRequest(req.forget());
|
||||
}
|
||||
|
||||
void
|
||||
Gecko_SetContentDataImage(nsStyleContentData* aContent, ServoBundledURI aURI)
|
||||
{
|
||||
@ -1279,6 +1331,16 @@ Gecko_SetListStyleImageNone(nsStyleList* aList)
|
||||
aList->mListStyleImage = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Gecko_SetListStyleImageImageValue(nsStyleList* aList,
|
||||
mozilla::css::ImageValue* aImageValue)
|
||||
{
|
||||
MOZ_ASSERT(aList && aImageValue);
|
||||
|
||||
aList->mListStyleImage =
|
||||
CreateStyleImageRequest(nsStyleImageRequest::Mode(0), aImageValue);
|
||||
}
|
||||
|
||||
void
|
||||
Gecko_SetListStyleImage(nsStyleList* aList,
|
||||
ServoBundledURI aURI)
|
||||
|
@ -276,6 +276,11 @@ void Gecko_CopyListStyleTypeFrom(nsStyleList* dst, const nsStyleList* src);
|
||||
void Gecko_SetNullImageValue(nsStyleImage* image);
|
||||
void Gecko_SetGradientImageValue(nsStyleImage* image, nsStyleGradient* gradient);
|
||||
NS_DECL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::ImageValue, ImageValue);
|
||||
mozilla::css::ImageValue* Gecko_ImageValue_Create(ServoBundledURI aURI);
|
||||
void Gecko_SetLayerImageImageValue(nsStyleImage* image,
|
||||
mozilla::css::ImageValue* aImageValue);
|
||||
|
||||
// XXX cku: remove this function after gecko and stylo side are both ready
|
||||
void Gecko_SetUrlImageValue(nsStyleImage* image,
|
||||
ServoBundledURI uri);
|
||||
void Gecko_SetImageElement(nsStyleImage* image, nsIAtom* atom);
|
||||
@ -290,17 +295,26 @@ nsStyleGradient* Gecko_CreateGradient(uint8_t shape,
|
||||
|
||||
// list-style-image style.
|
||||
void Gecko_SetListStyleImageNone(nsStyleList* style_struct);
|
||||
void Gecko_SetListStyleImageImageValue(nsStyleList* style_struct,
|
||||
mozilla::css::ImageValue* aImageValue);
|
||||
// XXX cku: remove this function after gecko and stylo side are both ready
|
||||
void Gecko_SetListStyleImage(nsStyleList* style_struct,
|
||||
ServoBundledURI uri);
|
||||
void Gecko_CopyListStyleImageFrom(nsStyleList* dest, const nsStyleList* src);
|
||||
|
||||
// cursor style.
|
||||
void Gecko_SetCursorArrayLength(nsStyleUserInterface* ui, size_t len);
|
||||
void Gecko_SetCursorImageValue(nsCursorImage* aCursor,
|
||||
mozilla::css::ImageValue* aImageValue);
|
||||
// XXX cku: remove this function after gecko and stylo side are both ready
|
||||
void Gecko_SetCursorImage(nsCursorImage* cursor,
|
||||
ServoBundledURI uri);
|
||||
void Gecko_CopyCursorArrayFrom(nsStyleUserInterface* dest,
|
||||
const nsStyleUserInterface* src);
|
||||
|
||||
void Gecko_SetContentDataImageValue(nsStyleContentData* aList,
|
||||
mozilla::css::ImageValue* aImageValue);
|
||||
// XXX cku: remove this function after gecko and stylo side are both ready
|
||||
void Gecko_SetContentDataImage(nsStyleContentData* content_data, ServoBundledURI uri);
|
||||
void Gecko_SetContentDataArray(nsStyleContentData* content_data, nsStyleContentType type, uint32_t len);
|
||||
|
||||
|
@ -3043,7 +3043,6 @@ void
|
||||
css::ImageValue::Initialize(nsIDocument* aDocument)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mInitialized);
|
||||
|
||||
// NB: If aDocument is not the original document, we may not be able to load
|
||||
// images from aDocument. Instead we do the image load from the original doc
|
||||
@ -3053,17 +3052,15 @@ css::ImageValue::Initialize(nsIDocument* aDocument)
|
||||
loadingDoc = aDocument;
|
||||
}
|
||||
|
||||
loadingDoc->StyleImageLoader()->LoadImage(GetURI(),
|
||||
mExtraData->GetPrincipal(),
|
||||
mExtraData->GetReferrer(), this);
|
||||
if (!mLoadedImage) {
|
||||
loadingDoc->StyleImageLoader()->LoadImage(GetURI(),
|
||||
mExtraData->GetPrincipal(),
|
||||
mExtraData->GetReferrer(), this);
|
||||
|
||||
if (loadingDoc != aDocument) {
|
||||
aDocument->StyleImageLoader()->MaybeRegisterCSSImage(this);
|
||||
mLoadedImage = true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
mInitialized = true;
|
||||
#endif
|
||||
aDocument->StyleImageLoader()->MaybeRegisterCSSImage(this);
|
||||
}
|
||||
|
||||
css::ImageValue::~ImageValue()
|
||||
|
@ -230,9 +230,7 @@ public:
|
||||
nsRefPtrHashtable<nsPtrHashKey<nsIDocument>, imgRequestProxy> mRequests;
|
||||
|
||||
private:
|
||||
#ifdef DEBUG
|
||||
bool mInitialized = false;
|
||||
#endif
|
||||
bool mLoadedImage = false;
|
||||
};
|
||||
|
||||
struct GridNamedArea {
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "nsIURI.h"
|
||||
#include "nsIDocument.h"
|
||||
#include <algorithm>
|
||||
#include "ImageLoader.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
@ -2020,6 +2021,15 @@ nsStyleImageRequest::nsStyleImageRequest(
|
||||
{
|
||||
}
|
||||
|
||||
nsStyleImageRequest::nsStyleImageRequest(
|
||||
Mode aModeFlags,
|
||||
mozilla::css::ImageValue* aImageValue)
|
||||
: mImageValue(aImageValue)
|
||||
, mModeFlags(aModeFlags)
|
||||
, mResolved(false)
|
||||
{
|
||||
}
|
||||
|
||||
nsStyleImageRequest::~nsStyleImageRequest()
|
||||
{
|
||||
// We may or may not be being destroyed on the main thread. To clean
|
||||
@ -2031,13 +2041,16 @@ nsStyleImageRequest::~nsStyleImageRequest()
|
||||
mRequestProxy.forget(),
|
||||
mImageValue.forget(),
|
||||
mImageTracker.forget());
|
||||
if (NS_IsMainThread() || !IsResolved()) {
|
||||
if (NS_IsMainThread()) {
|
||||
task->Run();
|
||||
} else {
|
||||
MOZ_ASSERT(IsResolved() == bool(mDocGroup),
|
||||
"We forgot to cache mDocGroup in Resolve()?");
|
||||
mDocGroup->Dispatch("StyleImageRequestCleanupTask",
|
||||
TaskCategory::Other, task.forget());
|
||||
if (mDocGroup) {
|
||||
mDocGroup->Dispatch("StyleImageRequestCleanupTask",
|
||||
TaskCategory::Other, task.forget());
|
||||
} else {
|
||||
// if Resolve was not called at some point, mDocGroup is not set.
|
||||
NS_DispatchToMainThread(task.forget());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -347,6 +347,12 @@ public:
|
||||
const nsAString& aURL,
|
||||
already_AddRefed<mozilla::URLExtraData> aExtraData);
|
||||
|
||||
// Can be called from any thread, but Resolve() must be called later
|
||||
// on the main thread before get() can be used.
|
||||
nsStyleImageRequest(
|
||||
Mode aModeFlags,
|
||||
mozilla::css::ImageValue* aImageValue);
|
||||
|
||||
bool Resolve(nsPresContext* aPresContext);
|
||||
bool IsResolved() const { return mResolved; }
|
||||
|
||||
|
@ -123,7 +123,6 @@ AboutRedirector.prototype = {
|
||||
}
|
||||
|
||||
channel.originalURI = aURI;
|
||||
aLoadInfo.resultPrincipalURI = aURI;
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ AboutFlyWeb.prototype = Object.freeze({
|
||||
let uri = Services.io.newURI("chrome://flyweb/content/aboutFlyWeb.xhtml");
|
||||
let channel = Services.io.newChannelFromURIWithLoadInfo(uri, aLoadInfo);
|
||||
channel.originalURI = aURI;
|
||||
aLoadInfo.resultPrincipalURI = aURI;
|
||||
return channel;
|
||||
}
|
||||
});
|
||||
|
@ -129,11 +129,11 @@ public class AndroidGamepadManager {
|
||||
}
|
||||
}
|
||||
|
||||
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko_priority")
|
||||
@WrapForJNI(calledFrom = "ui")
|
||||
private static native void onGamepadChange(int id, boolean added);
|
||||
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko_priority")
|
||||
@WrapForJNI(calledFrom = "ui")
|
||||
private static native void onButtonChange(int id, int button, boolean pressed, float value);
|
||||
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko_priority")
|
||||
@WrapForJNI(calledFrom = "ui")
|
||||
private static native void onAxisChange(int id, boolean[] valid, float[] values);
|
||||
|
||||
private static boolean sStarted;
|
||||
@ -184,7 +184,7 @@ public class AndroidGamepadManager {
|
||||
}
|
||||
}
|
||||
|
||||
@WrapForJNI(calledFrom = "gecko")
|
||||
@WrapForJNI
|
||||
private static void onGamepadAdded(final int device_id, final int service_id) {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
|
@ -223,7 +223,6 @@
|
||||
@BINPATH@/components/toolkit_asyncshutdown.xpt
|
||||
@BINPATH@/components/toolkit_filewatcher.xpt
|
||||
@BINPATH@/components/toolkit_finalizationwitness.xpt
|
||||
@BINPATH@/components/toolkit_formautofill.xpt
|
||||
@BINPATH@/components/toolkit_osfile.xpt
|
||||
@BINPATH@/components/toolkit_securityreporter.xpt
|
||||
@BINPATH@/components/toolkit_perfmonitoring.xpt
|
||||
@ -348,9 +347,6 @@
|
||||
@BINPATH@/components/nsFormAutoComplete.js
|
||||
@BINPATH@/components/FormHistoryStartup.js
|
||||
@BINPATH@/components/nsInputListAutoComplete.js
|
||||
@BINPATH@/components/formautofill.manifest
|
||||
@BINPATH@/components/FormAutofillContentService.js
|
||||
@BINPATH@/components/FormAutofillStartup.js
|
||||
@BINPATH@/components/contentAreaDropListener.manifest
|
||||
@BINPATH@/components/contentAreaDropListener.js
|
||||
@BINPATH@/components/messageWakeupService.js
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user