merge mozilla-inbound to mozilla-central. r=merge a=merge

MozReview-Commit-ID: Jduo3F6TzgF
This commit is contained in:
Sebastian Hengst 2017-05-14 18:04:29 +02:00
commit 484d2b7f51
259 changed files with 4903 additions and 6409 deletions

View File

@ -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]

View File

@ -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) {

View File

@ -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.

View File

@ -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");
},

View File

@ -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) {

View File

@ -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);
});

View File

@ -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 {

View File

@ -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);

View File

@ -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;
},

View File

@ -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]

View File

@ -26,7 +26,6 @@ let TestAboutPage = {
let channel = Services.io.newChannelFromURIWithLoadInfo(newURI,
aLoadInfo);
channel.originalURI = aURI;
aLoadInfo.resultPrincipalURI = aURI;
return channel;
},

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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);

View File

@ -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]

View File

@ -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;
},

View File

@ -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]

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -883,8 +883,6 @@ nsHostObjectProtocolHandler::NewChannel2(nsIURI* uri,
}
channel->SetOriginalURI(uri);
aLoadInfo->SetResultPrincipalURI(uri);
channel->SetContentType(NS_ConvertUTF16toUTF8(contentType));
channel->SetContentLength(size);

View File

@ -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();
}

View File

@ -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]);
}
}
}
};

View File

@ -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,

View File

@ -416,8 +416,6 @@ public:
js::ExpandoAndGeneration mExpandoAndGeneration;
void RequestAutocomplete();
protected:
virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;

View File

@ -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.");

View File

@ -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()
{

View File

@ -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;

View File

@ -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()
{

View File

@ -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*

View File

@ -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);

View File

@ -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)

View File

@ -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();

View File

@ -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;

View File

@ -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.

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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?");

View File

@ -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 = "";
};

View File

@ -46,7 +46,4 @@ interface HTMLFormElement : HTMLElement {
void reset();
boolean checkValidity();
boolean reportValidity();
[Pref="dom.forms.requestAutocomplete"]
void requestAutocomplete();
};

View File

@ -1054,7 +1054,6 @@ if CONFIG['FUZZING']:
GENERATED_EVENTS_WEBIDL_FILES = [
'AddonEvent.webidl',
'AnimationPlaybackEvent.webidl',
'AutocompleteErrorEvent.webidl',
'BlobEvent.webidl',
'CaretStateChangedEvent.webidl',
'CloseEvent.webidl',

View File

@ -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

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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/,

View 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()

View File

@ -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;
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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

View File

@ -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();

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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());

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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;

View 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

View 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

View 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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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']

View File

@ -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) {

View File

@ -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.

View 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('')

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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

View 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>

View 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>

View File

@ -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

View File

@ -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);} ,
];

View File

@ -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);

View File

@ -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()
{

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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)

View File

@ -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);

View File

@ -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()

View File

@ -230,9 +230,7 @@ public:
nsRefPtrHashtable<nsPtrHashKey<nsIDocument>, imgRequestProxy> mRequests;
private:
#ifdef DEBUG
bool mInitialized = false;
#endif
bool mLoadedImage = false;
};
struct GridNamedArea {

View File

@ -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());
}
}
}

View File

@ -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; }

View File

@ -123,7 +123,6 @@ AboutRedirector.prototype = {
}
channel.originalURI = aURI;
aLoadInfo.resultPrincipalURI = aURI;
return channel;
}

View File

@ -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;
}
});

View File

@ -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

View File

@ -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