--- a/.cron.yml
+++ b/.cron.yml
@@ -20,8 +20,18 @@ jobs:
treeherder-symbol: Na
triggered-by: nightly
target-tasks-method: nightly_fennec
run-on-projects:
- mozilla-central
- date
when: [] # never (temporary)
+ - name: nightly-mochitest-valgrind
+ job:
+ type: decision-task
+ treeherder-symbol: Vg
+ target-tasks-method: mochitest_valgrind
+ run-on-projects:
+ - mozilla-central
+ when:
+ - {hour: 16, minute: 0}
+ - {hour: 4, minute: 0}
--- a/.eslintignore
+++ b/.eslintignore
@@ -110,17 +110,16 @@ devtools/server/actors/object.js
devtools/server/actors/script.js
devtools/server/actors/styleeditor.js
devtools/server/actors/stylesheets.js
devtools/server/tests/browser/**
!devtools/server/tests/browser/browser_webextension_inspected_window.js
devtools/server/tests/mochitest/**
devtools/server/tests/unit/**
devtools/shared/heapsnapshot/**
-devtools/shared/transport/tests/unit/**
devtools/shared/webconsole/test/**
# Ignore devtools pre-processed files
devtools/client/framework/toolbox-process-window.js
devtools/client/performance/system.js
devtools/client/webide/webide-prefs.js
devtools/client/preferences/**
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -698,16 +698,17 @@
aria-label="&urlbar.viewSiteInfo.label;"
onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
ondragstart="gIdentityHandler.onDragStart(event);">
<image id="identity-icon"
consumeanchor="identity-box"
onclick="PageProxyClickHandler(event);"/>
<image id="sharing-icon" mousethrough="always"/>
+ <image id="tracking-protection-icon"/>
<box id="blocked-permissions-container" align="center">
<image data-permission-id="geo" class="blocked-permission-icon geo-icon" role="button"
tooltiptext="&urlbar.geolocationBlocked.tooltip;"/>
<image data-permission-id="desktop-notification" class="blocked-permission-icon desktop-notification-icon" role="button"
tooltiptext="&urlbar.webNotificationsBlocked.tooltip;"/>
<image data-permission-id="camera" class="blocked-permission-icon camera-icon" role="button"
tooltiptext="&urlbar.cameraBlocked.tooltip;"/>
<image data-permission-id="indexedDB" class="blocked-permission-icon indexedDB-icon" role="button"
@@ -746,17 +747,16 @@
tooltiptext="&urlbar.servicesNotificationAnchor.tooltip;"/>
<image id="translate-notification-icon" class="notification-anchor-icon translation-icon" role="button"
tooltiptext="&urlbar.translateNotificationAnchor.tooltip;"/>
<image id="translated-notification-icon" class="notification-anchor-icon translation-icon in-use" role="button"
tooltiptext="&urlbar.translatedNotificationAnchor.tooltip;"/>
<image id="eme-notification-icon" class="notification-anchor-icon drm-icon" role="button"
tooltiptext="&urlbar.emeNotificationAnchor.tooltip;"/>
</box>
- <image id="tracking-protection-icon"/>
<image id="connection-icon"/>
<hbox id="identity-icon-labels">
<label id="identity-icon-label" class="plain" flex="1"/>
<label id="identity-icon-country-label" class="plain"/>
</hbox>
</box>
<box id="urlbar-display-box" align="center">
<label id="switchtab" class="urlbar-display urlbar-display-switchtab" value="&urlbar.switchToTab.label;"/>
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_screen.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_screen.js
@@ -316,17 +316,17 @@ var gTests = [
yield checkSharingUI({audio: true, screen: "Screen"});
yield closeStream();
}
},
{
desc: "getUserMedia screen: clicking through without selecting a screen denies",
- run: function* checkReloading() {
+ run: function* checkClickThroughDenies() {
let promise = promisePopupNotificationShown("webRTC-shareDevices");
yield promiseRequestDevice(false, true, null, "screen");
yield promise;
yield expectObserverCalled("getUserMedia:request");
checkDeviceSelectors(false, false, true);
yield promiseMessage(permissionError, () => {
PopupNotifications.panel.firstChild.button.click();
--- a/browser/base/content/test/webrtc/head.js
+++ b/browser/base/content/test/webrtc/head.js
@@ -402,22 +402,24 @@ function* closeStream(aAlreadyClosed, aF
if (promises)
yield Promise.all(promises);
yield* assertWebRTCIndicatorStatus(null);
}
function* reloadAndAssertClosedStreams() {
info("reloading the web page");
- let promise = promiseObserverCalled("recording-device-events");
+ let promises = [
+ promiseObserverCalled("recording-device-events"),
+ promiseObserverCalled("recording-window-ended")
+ ];
yield ContentTask.spawn(gBrowser.selectedBrowser, null,
"() => content.location.reload()");
- yield promise;
+ yield Promise.all(promises);
- yield expectObserverCalled("recording-window-ended");
yield expectNoObserverCalled();
yield checkNotSharing();
}
function checkDeviceSelectors(aAudio, aVideo, aScreen) {
let micSelector = document.getElementById("webRTC-selectMicrophone");
if (aAudio)
ok(!micSelector.hidden, "microphone selector visible");
--- a/browser/modules/E10SUtils.jsm
+++ b/browser/modules/E10SUtils.jsm
@@ -8,18 +8,22 @@ this.EXPORTED_SYMBOLS = ["E10SUtils"];
const {interfaces: Ci, utils: Cu, classes: Cc} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyPreferenceGetter(this, "useRemoteWebExtensions",
"extensions.webextensions.remote", false);
+XPCOMUtils.defineLazyPreferenceGetter(this, "useSeparateFileUriProcess",
+ "browser.tabs.remote.separateFileUriProcess", false);
XPCOMUtils.defineLazyModuleGetter(this, "Utils",
"resource://gre/modules/sessionstore/Utils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "console",
+ "resource://gre/modules/Console.jsm");
function getAboutModule(aURL) {
// Needs to match NS_GetAboutModuleName
let moduleName = aURL.path.replace(/[#?].*/, "").toLowerCase();
let contract = "@mozilla.org/network/protocol/about;1?what=" + moduleName;
try {
return Cc[contract].getService(Ci.nsIAboutModule);
} catch (e) {
@@ -51,112 +55,125 @@ this.E10SUtils = {
canLoadURIInProcess(aURL, aProcess) {
let remoteType = aProcess == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT
? DEFAULT_REMOTE_TYPE : NOT_REMOTE;
return remoteType == this.getRemoteTypeForURI(aURL, true, remoteType);
},
getRemoteTypeForURI(aURL, aMultiProcess,
- aPreferredRemoteType = DEFAULT_REMOTE_TYPE) {
+ aPreferredRemoteType = DEFAULT_REMOTE_TYPE) {
if (!aMultiProcess) {
return NOT_REMOTE;
}
// loadURI in browser.xml treats null as about:blank
if (!aURL) {
aURL = "about:blank";
}
- // Javascript urls can load in any process, they apply to the current document
- if (aURL.startsWith("javascript:")) {
- return aPreferredRemoteType;
- }
-
- // We need data: URI's to load in a remote process, because some of our
- // tests rely on this. For blob: URI's, load them in their originating
- // process unless it is non-remote. In that case, favor a remote (sandboxed)
- // process with fewer privileges to limit exposure.
- if (aURL.startsWith("data:") || aURL.startsWith("blob:")) {
- return aPreferredRemoteType == NOT_REMOTE ? DEFAULT_REMOTE_TYPE
- : aPreferredRemoteType;
- }
-
- if (aURL.startsWith("file:")) {
- return Services.prefs.getBoolPref("browser.tabs.remote.separateFileUriProcess")
- ? FILE_REMOTE_TYPE : DEFAULT_REMOTE_TYPE;
+ let uri;
+ try {
+ uri = Services.io.newURI(aURL);
+ } catch (e) {
+ // If we have an invalid URI, it's still possible that it might get
+ // fixed-up into a valid URI later on. However, we don't want to return
+ // aPreferredRemoteType here, in case the URI gets fixed-up into
+ // something that wouldn't normally run in that process.
+ return DEFAULT_REMOTE_TYPE;
}
- if (aURL.startsWith("about:")) {
- // We need to special case about:blank because it needs to load in any.
- if (aURL == "about:blank") {
- return aPreferredRemoteType;
- }
+ return this.getRemoteTypeForURIObject(uri, aMultiProcess,
+ aPreferredRemoteType);
+ },
- let url = Services.io.newURI(aURL);
- let module = getAboutModule(url);
- // If the module doesn't exist then an error page will be loading, that
- // should be ok to load in any process
- if (!module) {
- return aPreferredRemoteType;
- }
-
- let flags = module.getURIFlags(url);
- if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD) {
- return DEFAULT_REMOTE_TYPE;
- }
-
- // If the about page can load in parent or child, it should be safe to
- // load in any remote type.
- if (flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD) {
- return aPreferredRemoteType;
- }
-
+ getRemoteTypeForURIObject(aURI, aMultiProcess,
+ aPreferredRemoteType = DEFAULT_REMOTE_TYPE) {
+ if (!aMultiProcess) {
return NOT_REMOTE;
}
- if (aURL.startsWith("chrome:")) {
- let url;
- try {
- // This can fail for invalid Chrome URIs, in which case we will end up
- // not loading anything anyway.
- url = Services.io.newURI(aURL);
- } catch (ex) {
+ switch (aURI.scheme) {
+ case "javascript":
+ // javascript URIs can load in any, they apply to the current document.
return aPreferredRemoteType;
- }
+
+ case "data":
+ case "blob":
+ // We need data: and blob: URIs to load in any remote process, because
+ // they need to be able to load in whatever is the current process
+ // unless it is non-remote. In that case we don't want to load them in
+ // the parent process, so we load them in the default remote process,
+ // which is sandboxed and limits any risk.
+ return aPreferredRemoteType == NOT_REMOTE ? DEFAULT_REMOTE_TYPE
+ : aPreferredRemoteType;
- let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
- getService(Ci.nsIXULChromeRegistry);
- if (chromeReg.mustLoadURLRemotely(url)) {
- return DEFAULT_REMOTE_TYPE;
- }
+ case "file":
+ return useSeparateFileUriProcess ? FILE_REMOTE_TYPE
+ : DEFAULT_REMOTE_TYPE;
+
+ case "about":
+ let module = getAboutModule(aURI);
+ // If the module doesn't exist then an error page will be loading, that
+ // should be ok to load in any process
+ if (!module) {
+ return aPreferredRemoteType;
+ }
+
+ let flags = module.getURIFlags(aURI);
+ if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD) {
+ return DEFAULT_REMOTE_TYPE;
+ }
- if (chromeReg.canLoadURLRemotely(url) &&
- aPreferredRemoteType != NOT_REMOTE) {
- return DEFAULT_REMOTE_TYPE;
- }
+ // If the about page can load in parent or child, it should be safe to
+ // load in any remote type.
+ if (flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD) {
+ return aPreferredRemoteType;
+ }
+
+ return NOT_REMOTE;
- return NOT_REMOTE;
- }
+ case "chrome":
+ let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
+ getService(Ci.nsIXULChromeRegistry);
+ if (chromeReg.mustLoadURLRemotely(aURI)) {
+ return DEFAULT_REMOTE_TYPE;
+ }
+
+ if (chromeReg.canLoadURLRemotely(aURI) &&
+ aPreferredRemoteType != NOT_REMOTE) {
+ return DEFAULT_REMOTE_TYPE;
+ }
- if (aURL.startsWith("moz-extension:")) {
- return useRemoteWebExtensions ? EXTENSION_REMOTE_TYPE : NOT_REMOTE;
- }
+ return NOT_REMOTE;
+
+ case "moz-extension":
+ return useRemoteWebExtensions ? EXTENSION_REMOTE_TYPE : NOT_REMOTE;
- if (aURL.startsWith("view-source:")) {
- return this.getRemoteTypeForURI(aURL.substr("view-source:".length),
- aMultiProcess, aPreferredRemoteType);
+ default:
+ // For any other nested URIs, we use the innerURI to determine the
+ // remote type. In theory we should use the innermost URI, but some URIs
+ // have fake inner URIs (e.g. about URIs with inner moz-safe-about) and
+ // if such URIs are wrapped in other nested schemes like view-source:,
+ // we don't want to "skip" past "about:" by going straight to the
+ // innermost URI. Any URIs like this will need to be handled in the
+ // cases above, so we don't still end up using the fake inner URI here.
+ if (aURI instanceof Ci.nsINestedURI) {
+ let innerURI = aURI.QueryInterface(Ci.nsINestedURI).innerURI;
+ return this.getRemoteTypeForURIObject(innerURI, aMultiProcess,
+ aPreferredRemoteType);
+ }
+
+ return validatedWebRemoteType(aPreferredRemoteType);
}
-
- return validatedWebRemoteType(aPreferredRemoteType);
},
shouldLoadURIInThisProcess(aURI) {
let remoteType = Services.appinfo.remoteType;
- return remoteType == this.getRemoteTypeForURI(aURI.spec, true, remoteType);
+ return remoteType == this.getRemoteTypeForURIObject(aURI, true, remoteType);
},
shouldLoadURI(aDocShell, aURI, aReferrer) {
// Inner frames should always load in the current process
if (aDocShell.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeParent)
return true;
// If we are in a Large-Allocation process, and it wouldn't be content visible
new file mode 100644
--- /dev/null
+++ b/browser/modules/test/xpcshell/test_E10SUtils_nested_URIs.js
@@ -0,0 +1,94 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+
+const {utils: Cu, interfaces: Ci} = Components;
+
+Cu.import("resource:///modules/E10SUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+var TEST_PREFERRED_REMOTE_TYPES = [
+ E10SUtils.WEB_REMOTE_TYPE,
+ E10SUtils.NOT_REMOTE,
+ "fakeRemoteType",
+]
+
+// These test cases give a nestedURL and a plainURL that should always load in
+// the same remote type. By making these tests comparisons, they should work
+// with any pref combination.
+var TEST_CASES = [
+ {
+ nestedURL: "jar:file:///some.file!/",
+ plainURL: "file:///some.file",
+ },
+ {
+ nestedURL: "jar:jar:file:///some.file!/!/",
+ plainURL: "file:///some.file",
+ },
+ {
+ nestedURL: "jar:http://some.site/file!/",
+ plainURL: "http://some.site/file",
+ },
+ {
+ nestedURL: "feed:http://some.site",
+ plainURL: "http://some.site",
+ },
+ {
+ nestedURL: "pcast:http://some.site",
+ plainURL: "http://some.site",
+ },
+ {
+ nestedURL: "view-source:http://some.site",
+ plainURL: "http://some.site",
+ },
+ {
+ nestedURL: "view-source:file:///some.file",
+ plainURL: "file:///some.file",
+ },
+ {
+ nestedURL: "view-source:about:home",
+ plainURL: "about:home",
+ },
+ {
+ nestedURL: "view-source:about:robots",
+ plainURL: "about:robots",
+ },
+ {
+ nestedURL: "view-source:feed:http://some.site",
+ plainURL: "http://some.site",
+ },
+ {
+ nestedURL: "view-source:pcast:http://some.site",
+ plainURL: "http://some.site",
+ },
+]
+
+function run_test() {
+ for (let testCase of TEST_CASES) {
+ for (let preferredRemoteType of TEST_PREFERRED_REMOTE_TYPES) {
+ let plainUri = Services.io.newURI(testCase.plainURL);
+ let plainRemoteType =
+ E10SUtils.getRemoteTypeForURIObject(plainUri, true, preferredRemoteType);
+
+ let nestedUri = Services.io.newURI(testCase.nestedURL);
+ let nestedRemoteType =
+ E10SUtils.getRemoteTypeForURIObject(nestedUri, true, preferredRemoteType);
+
+ let nestedStr = nestedUri.scheme + ":";
+ do {
+ nestedUri = nestedUri.QueryInterface(Ci.nsINestedURI).innerURI;
+ if (nestedUri.scheme == "about") {
+ nestedStr += nestedUri.spec;
+ break;
+ }
+
+ nestedStr += nestedUri.scheme + ":";
+ } while (nestedUri instanceof Ci.nsINestedURI);
+
+ let plainStr = plainUri.scheme == "about" ? plainUri.spec
+ : plainUri.scheme + ":";
+ equal(nestedRemoteType, plainRemoteType,
+ `Check that ${nestedStr} loads in same remote type as ${plainStr}`
+ + ` with preferred remote type: ${preferredRemoteType}`);
+ }
+ }
+}
--- a/browser/modules/test/xpcshell/xpcshell.ini
+++ b/browser/modules/test/xpcshell/xpcshell.ini
@@ -1,10 +1,11 @@
[DEFAULT]
head =
firefox-appdir = browser
skip-if = toolkit == 'android'
[test_AttributionCode.js]
skip-if = os != 'win'
[test_DirectoryLinksProvider.js]
+[test_E10SUtils_nested_URIs.js]
[test_SitePermissions.js]
[test_LaterRun.js]
--- a/config/system-headers
+++ b/config/system-headers
@@ -484,16 +484,17 @@ ft2build.h
fts.h
gconf/gconf-client.h
Gdiplus.h
gdk/gdk.h
gdk/gdkkeysyms.h
gdk/gdkprivate.h
gdk/gdkx.h
gdk/gdkdirectfb.h
+gdk/gdkwayland.h
gdk-pixbuf/gdk-pixbuf.h
Gestalt.h
getopt.h
glibconfig.h
glib.h
glib-object.h
gmodule.h
gnome.h
--- a/devtools/shared/transport/tests/unit/head_dbg.js
+++ b/devtools/shared/transport/tests/unit/head_dbg.js
@@ -1,262 +1,157 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
+/* exported Cr, CC, NetUtil, defer, errorCount, initTestDebuggerServer,
+ writeTestTempFile, socket_transport, local_transport, really_long
+*/
+
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
var CC = Components.Constructor;
const { require } =
Cu.import("resource://devtools/shared/Loader.jsm", {});
const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
const promise = require("promise");
const defer = require("devtools/shared/defer");
const { Task } = require("devtools/shared/task");
const Services = require("Services");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
// We do not want to log packets by default, because in some tests,
// we can be sending large amounts of data. The test harness has
// trouble dealing with logging all the data, and we end up with
// intermittent time outs (e.g. bug 775924).
// Services.prefs.setBoolPref("devtools.debugger.log", true);
// Services.prefs.setBoolPref("devtools.debugger.log.verbose", true);
// Enable remote debugging for the relevant tests.
Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true);
const { DebuggerServer } = require("devtools/server/main");
const { DebuggerClient } = require("devtools/shared/client/main");
-function testExceptionHook(ex) {
- try {
- do_report_unexpected_exception(ex);
- } catch (ex) {
- return {throw: ex};
+// Convert an nsIScriptError 'flags' value into an appropriate string.
+function scriptErrorFlagsToKind(flags) {
+ let kind;
+ if (flags & Ci.nsIScriptError.warningFlag) {
+ kind = "warning";
}
- return undefined;
-}
+ if (flags & Ci.nsIScriptError.exceptionFlag) {
+ kind = "exception";
+ } else {
+ kind = "error";
+ }
-// Convert an nsIScriptError 'aFlags' value into an appropriate string.
-function scriptErrorFlagsToKind(aFlags) {
- var kind;
- if (aFlags & Ci.nsIScriptError.warningFlag)
- kind = "warning";
- if (aFlags & Ci.nsIScriptError.exceptionFlag)
- kind = "exception";
- else
- kind = "error";
-
- if (aFlags & Ci.nsIScriptError.strictFlag)
+ if (flags & Ci.nsIScriptError.strictFlag) {
kind = "strict " + kind;
+ }
return kind;
}
// Register a console listener, so console messages don't just disappear
// into the ether.
var errorCount = 0;
var listener = {
- observe: function (aMessage) {
+ observe: function (message) {
errorCount++;
+ let string = "";
try {
// If we've been given an nsIScriptError, then we can print out
// something nicely formatted, for tools like Emacs to pick up.
- var scriptError = aMessage.QueryInterface(Ci.nsIScriptError);
- dump(aMessage.sourceName + ":" + aMessage.lineNumber + ": " +
- scriptErrorFlagsToKind(aMessage.flags) + ": " +
- aMessage.errorMessage + "\n");
- var string = aMessage.errorMessage;
+ message.QueryInterface(Ci.nsIScriptError);
+ dump(message.sourceName + ":" + message.lineNumber + ": " +
+ scriptErrorFlagsToKind(message.flags) + ": " +
+ message.errorMessage + "\n");
+ string = message.errorMessage;
} catch (x) {
// Be a little paranoid with message, as the whole goal here is to lose
// no information.
try {
- var string = "" + aMessage.message;
- } catch (x) {
- var string = "<error converting error message to string>";
+ string = message.message;
+ } catch (e) {
+ string = "<error converting error message to string>";
}
}
// Make sure we exit all nested event loops so that the test can finish.
while (DebuggerServer.xpcInspector.eventLoopNestLevel > 0) {
DebuggerServer.xpcInspector.exitNestedEventLoop();
}
// Throw in most cases, but ignore the "strict" messages
- if (!(aMessage.flags & Ci.nsIScriptError.strictFlag)) {
+ if (!(message.flags & Ci.nsIScriptError.strictFlag)) {
do_throw("head_dbg.js got console message: " + string + "\n");
}
}
};
var consoleService = Cc["@mozilla.org/consoleservice;1"]
.getService(Ci.nsIConsoleService);
consoleService.registerListener(listener);
-function check_except(func) {
- try {
- func();
- } catch (e) {
- do_check_true(true);
- return;
- }
- dump("Should have thrown an exception: " + func.toString());
- do_check_true(false);
-}
-
-function testGlobal(aName) {
- let systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
- .createInstance(Ci.nsIPrincipal);
-
- let sandbox = Cu.Sandbox(systemPrincipal);
- sandbox.__name = aName;
- return sandbox;
-}
-
-function addTestGlobal(aName)
-{
- let global = testGlobal(aName);
- DebuggerServer.addTestGlobal(global);
- return global;
-}
-
-// List the DebuggerClient |aClient|'s tabs, look for one whose title is
-// |aTitle|, and apply |aCallback| to the packet's entry for that tab.
-function getTestTab(aClient, aTitle, aCallback) {
- aClient.listTabs(function (aResponse) {
- for (let tab of aResponse.tabs) {
- if (tab.title === aTitle) {
- aCallback(tab);
- return;
- }
- }
- aCallback(null);
- });
-}
-
-// Attach to |aClient|'s tab whose title is |aTitle|; pass |aCallback| the
-// response packet and a TabClient instance referring to that tab.
-function attachTestTab(aClient, aTitle, aCallback) {
- getTestTab(aClient, aTitle, function (aTab) {
- aClient.attachTab(aTab.actor, aCallback);
- });
-}
-
-// Attach to |aClient|'s tab whose title is |aTitle|, and then attach to
-// that tab's thread. Pass |aCallback| the thread attach response packet, a
-// TabClient referring to the tab, and a ThreadClient referring to the
-// thread.
-function attachTestThread(aClient, aTitle, aCallback) {
- attachTestTab(aClient, aTitle, function (aResponse, aTabClient) {
- function onAttach(aResponse, aThreadClient) {
- aCallback(aResponse, aTabClient, aThreadClient);
- }
- aTabClient.attachThread({ useSourceMaps: true }, onAttach);
- });
-}
-
-// Attach to |aClient|'s tab whose title is |aTitle|, attach to the tab's
-// thread, and then resume it. Pass |aCallback| the thread's response to
-// the 'resume' packet, a TabClient for the tab, and a ThreadClient for the
-// thread.
-function attachTestTabAndResume(aClient, aTitle, aCallback) {
- attachTestThread(aClient, aTitle, function (aResponse, aTabClient, aThreadClient) {
- aThreadClient.resume(function (aResponse) {
- aCallback(aResponse, aTabClient, aThreadClient);
- });
- });
-}
-
/**
* Initialize the testing debugger server.
*/
function initTestDebuggerServer() {
DebuggerServer.registerModule("devtools/server/actors/script", {
prefix: "script",
constructor: "ScriptActor",
type: { global: true, tab: true }
});
DebuggerServer.registerModule("xpcshell-test/testactors");
// Allow incoming connections.
DebuggerServer.init();
}
-function finishClient(aClient) {
- aClient.close().then(function () {
- do_test_finished();
- });
-}
-
-/**
- * Takes a relative file path and returns the absolute file url for it.
- */
-function getFileUrl(aName, aAllowMissing = false) {
- let file = do_get_file(aName, aAllowMissing);
- return Services.io.newFileURI(file).spec;
-}
-
-/**
- * Returns the full path of the file with the specified name in a
- * platform-independent and URL-like form.
- */
-function getFilePath(aName, aAllowMissing = false) {
- let file = do_get_file(aName, aAllowMissing);
- let path = Services.io.newFileURI(file).spec;
- let filePrePath = "file://";
- if ("nsILocalFileWin" in Ci &&
- file instanceof Ci.nsILocalFileWin) {
- filePrePath += "/";
- }
- return path.slice(filePrePath.length);
-}
-
/**
* Wrapper around do_get_file to prefix files with the name of current test to
* avoid collisions when running in parallel.
*/
function getTestTempFile(fileName, allowMissing) {
let thisTest = _TEST_FILE.toString().replace(/\\/g, "/");
thisTest = thisTest.substring(thisTest.lastIndexOf("/") + 1);
thisTest = thisTest.replace(/\..*$/, "");
return do_get_file(fileName + "-" + thisTest, allowMissing);
}
-function writeTestTempFile(aFileName, aContent) {
- let file = getTestTempFile(aFileName, true);
+function writeTestTempFile(fileName, content) {
+ let file = getTestTempFile(fileName, true);
let stream = Cc["@mozilla.org/network/file-output-stream;1"]
.createInstance(Ci.nsIFileOutputStream);
stream.init(file, -1, -1, 0);
try {
do {
- let numWritten = stream.write(aContent, aContent.length);
- aContent = aContent.slice(numWritten);
- } while (aContent.length > 0);
+ let numWritten = stream.write(content, content.length);
+ content = content.slice(numWritten);
+ } while (content.length > 0);
} finally {
stream.close();
}
}
/** * Transport Factories ***/
var socket_transport = Task.async(function* () {
if (!DebuggerServer.listeningSockets) {
let AuthenticatorType = DebuggerServer.Authenticators.get("PROMPT");
let authenticator = new AuthenticatorType.Server();
authenticator.allowConnection = () => {
return DebuggerServer.AuthenticationResult.ALLOW;
};
- let listener = DebuggerServer.createListener();
- listener.portOrPath = -1;
- listener.authenticator = authenticator;
- yield listener.open();
+ let debuggerListener = DebuggerServer.createListener();
+ debuggerListener.portOrPath = -1;
+ debuggerListener.authenticator = authenticator;
+ yield debuggerListener.open();
}
let port = DebuggerServer._listeners[0].port;
do_print("Debugger server port is " + port);
return DebuggerClient.socketConnect({ host: "127.0.0.1", port });
});
function local_transport() {
return promise.resolve(DebuggerServer.connectPipe());
--- a/devtools/shared/transport/tests/unit/test_bulk_error.js
+++ b/devtools/shared/transport/tests/unit/test_bulk_error.js
@@ -68,18 +68,18 @@ function json_reply(client, response) {
actor: response.testBulk,
type: "jsonReply",
length: reallyLong.length
});
// Send bulk data to server
let copyDeferred = defer();
request.on("bulk-send-ready", ({writer, done}) => {
- let input = Cc["@mozilla.org/io/string-input-stream;1"].
- createInstance(Ci.nsIStringInputStream);
+ let input = Cc["@mozilla.org/io/string-input-stream;1"]
+ .createInstance(Ci.nsIStringInputStream);
input.setData(reallyLong, reallyLong.length);
try {
writer.copyFrom(input, () => {
input.close();
done();
});
do_throw(new Error("Copying should fail, the stream is not async."));
} catch (e) {
--- a/devtools/shared/transport/tests/unit/test_client_server_bulk.js
+++ b/devtools/shared/transport/tests/unit/test_client_server_bulk.js
@@ -145,35 +145,37 @@ var test_bulk_request_cs = Task.async(fu
let transport = yield transportFactory();
let client = new DebuggerClient(transport);
client.connect().then(([app, traits]) => {
do_check_eq(traits.bulk, true);
client.listTabs(clientDeferred.resolve);
});
+ function bulkSendReadyCallback({copyFrom}) {
+ NetUtil.asyncFetch({
+ uri: NetUtil.newURI(getTestTempFile("bulk-input")),
+ loadUsingSystemPrincipal: true
+ }, input => {
+ copyFrom(input).then(() => {
+ input.close();
+ bulkCopyDeferred.resolve();
+ });
+ });
+ }
+
clientDeferred.promise.then(response => {
let request = client.startBulkRequest({
actor: response.testBulk,
type: actorType,
length: really_long().length
});
// Send bulk data to server
- request.on("bulk-send-ready", ({copyFrom}) => {
- NetUtil.asyncFetch({
- uri: NetUtil.newURI(getTestTempFile("bulk-input")),
- loadUsingSystemPrincipal: true
- }, input => {
- copyFrom(input).then(() => {
- input.close();
- bulkCopyDeferred.resolve();
- });
- });
- });
+ request.on("bulk-send-ready", bulkSendReadyCallback);
// Set up reply handling for this type
replyHandlers[replyType](request).then(() => {
client.close();
transport.close();
});
}).then(null, do_throw);
--- a/devtools/shared/transport/tests/unit/test_dbgsocket.js
+++ b/devtools/shared/transport/tests/unit/test_dbgsocket.js
@@ -1,28 +1,27 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
var gPort;
var gExtraListener;
-function run_test()
-{
+function run_test() {
do_print("Starting test at " + new Date().toTimeString());
initTestDebuggerServer();
add_task(test_socket_conn);
add_task(test_socket_shutdown);
add_test(test_pipe_conn);
run_next_test();
}
-function* test_socket_conn()
-{
+function* test_socket_conn() {
do_check_eq(DebuggerServer.listeningSockets, 0);
let AuthenticatorType = DebuggerServer.Authenticators.get("PROMPT");
let authenticator = new AuthenticatorType.Server();
authenticator.allowConnection = () => {
return DebuggerServer.AuthenticationResult.ALLOW;
};
let listener = DebuggerServer.createListener();
do_check_true(listener);
@@ -48,77 +47,74 @@ function* test_socket_conn()
// Assert that connection settings are available on transport object
let settings = transport.connectionSettings;
do_check_eq(settings.host, "127.0.0.1");
do_check_eq(settings.port, gPort);
let closedDeferred = defer();
transport.hooks = {
- onPacket: function (aPacket) {
- this.onPacket = function (aPacket) {
- do_check_eq(aPacket.unicode, unicodeString);
+ onPacket: function (packet) {
+ this.onPacket = function ({unicode}) {
+ do_check_eq(unicode, unicodeString);
transport.close();
};
// Verify that things work correctly when bigger than the output
// transport buffers and when transporting unicode...
transport.send({to: "root",
type: "echo",
reallylong: really_long(),
unicode: unicodeString});
- do_check_eq(aPacket.from, "root");
+ do_check_eq(packet.from, "root");
},
- onClosed: function (aStatus) {
+ onClosed: function (status) {
closedDeferred.resolve();
},
};
transport.ready();
return closedDeferred.promise;
}
-function* test_socket_shutdown()
-{
+function* test_socket_shutdown() {
do_check_eq(DebuggerServer.listeningSockets, 2);
gExtraListener.close();
do_check_eq(DebuggerServer.listeningSockets, 1);
do_check_true(DebuggerServer.closeAllListeners());
do_check_eq(DebuggerServer.listeningSockets, 0);
// Make sure closing the listener twice does nothing.
do_check_false(DebuggerServer.closeAllListeners());
do_check_eq(DebuggerServer.listeningSockets, 0);
do_print("Connecting to a server socket at " + new Date().toTimeString());
try {
- let transport = yield DebuggerClient.socketConnect({
+ yield DebuggerClient.socketConnect({
host: "127.0.0.1",
port: gPort
});
} catch (e) {
if (e.result == Cr.NS_ERROR_CONNECTION_REFUSED ||
e.result == Cr.NS_ERROR_NET_TIMEOUT) {
// The connection should be refused here, but on slow or overloaded
// machines it may just time out.
do_check_true(true);
return;
- } else {
- throw e;
}
+ throw e;
}
// Shouldn't reach this, should never connect.
do_check_true(false);
}
-function test_pipe_conn()
-{
+function test_pipe_conn() {
let transport = DebuggerServer.connectPipe();
transport.hooks = {
- onPacket: function (aPacket) {
- do_check_eq(aPacket.from, "root");
+ onPacket: function (packet) {
+ do_check_eq(packet.from, "root");
transport.close();
},
- onClosed: function (aStatus) {
+ onClosed: function (status) {
run_next_test();
}
};
transport.ready();
}
--- a/devtools/shared/transport/tests/unit/test_dbgsocket_connection_drop.js
+++ b/devtools/shared/transport/tests/unit/test_dbgsocket_connection_drop.js
@@ -2,16 +2,17 @@
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/**
* Bug 755412 - checks if the server drops the connection on an improperly
* framed packet, i.e. when the length header is invalid.
*/
+"use strict";
const { RawPacket } = require("devtools/shared/transport/packets");
function run_test() {
do_print("Starting test at " + new Date().toTimeString());
initTestDebuggerServer();
add_task(test_socket_conn_drops_after_invalid_header);
@@ -56,26 +57,26 @@ var test_helper = Task.async(function* (
listener.open();
let transport = yield DebuggerClient.socketConnect({
host: "127.0.0.1",
port: listener.port
});
let closedDeferred = defer();
transport.hooks = {
- onPacket: function (aPacket) {
- this.onPacket = function (aPacket) {
+ onPacket: function (packet) {
+ this.onPacket = function () {
do_throw(new Error("This connection should be dropped."));
transport.close();
};
// Inject the payload directly into the stream.
transport._outgoing.push(new RawPacket(transport, payload));
transport._flushOutgoing();
},
- onClosed: function (aStatus) {
+ onClosed: function (status) {
do_check_true(true);
closedDeferred.resolve();
},
};
transport.ready();
return closedDeferred.promise;
});
--- a/devtools/shared/transport/tests/unit/test_delimited_read.js
+++ b/devtools/shared/transport/tests/unit/test_delimited_read.js
@@ -1,10 +1,11 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
const StreamUtils = require("devtools/shared/transport/stream-utils");
const StringInputStream = CC("@mozilla.org/io/string-input-stream;1",
"nsIStringInputStream", "setData");
function run_test() {
add_task(function* () {
--- a/devtools/shared/transport/tests/unit/test_no_bulk.js
+++ b/devtools/shared/transport/tests/unit/test_no_bulk.js
@@ -15,24 +15,22 @@ function run_test() {
});
run_next_test();
}
/** * Tests ***/
var test_bulk_send_error = Task.async(function* (transportFactory) {
- let deferred = defer();
let transport = yield transportFactory();
let client = new DebuggerClient(transport);
return client.connect().then(([app, traits]) => {
do_check_false(traits.bulk);
try {
client.startBulkRequest();
do_throw(new Error("Can't use bulk since server doesn't support it"));
} catch (e) {
do_check_true(true);
}
-
});
});
--- a/devtools/shared/transport/tests/unit/test_packet.js
+++ b/devtools/shared/transport/tests/unit/test_packet.js
@@ -1,10 +1,11 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
const { JSONPacket, BulkPacket } =
require("devtools/shared/transport/packets");
function run_test() {
add_test(test_packet_done);
run_next_test();
}
--- a/devtools/shared/transport/tests/unit/test_transport_bulk.js
+++ b/devtools/shared/transport/tests/unit/test_transport_bulk.js
@@ -69,19 +69,19 @@ var test_bulk_transfer_transport = Task.
clientDeferred.resolve();
};
transport.close();
});
}
// Client
transport.hooks = {
- onPacket: function (aPacket) {
+ onPacket: function (packet) {
// We've received the initial start up packet
- do_check_eq(aPacket.from, "root");
+ do_check_eq(packet.from, "root");
// Server
do_check_eq(Object.keys(DebuggerServer._connections).length, 1);
do_print(Object.keys(DebuggerServer._connections));
for (let connId in DebuggerServer._connections) {
DebuggerServer._connections[connId].onBulkPacket = on_bulk_packet;
}
--- a/devtools/shared/transport/tests/unit/testactors-no-bulk.js
+++ b/devtools/shared/transport/tests/unit/testactors-no-bulk.js
@@ -1,19 +1,20 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
const { RootActor } = require("devtools/server/actors/root");
const { DebuggerServer } = require("devtools/server/main");
/**
* Root actor that doesn't have the bulk trait.
*/
-function createRootActor(aConnection) {
- let root = new RootActor(aConnection, {
+function createRootActor(connection) {
+ let root = new RootActor(connection, {
globalActorFactories: DebuggerServer.globalActorFactories
});
root.applicationType = "xpcshell-tests";
root.traits = {
bulk: false
};
return root;
}
--- a/devtools/shared/transport/tests/unit/testactors.js
+++ b/devtools/shared/transport/tests/unit/testactors.js
@@ -1,72 +1,73 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
const { ActorPool, appendExtraActors, createExtraActors } =
require("devtools/server/actors/common");
const { RootActor } = require("devtools/server/actors/root");
const { ThreadActor } = require("devtools/server/actors/script");
const { DebuggerServer } = require("devtools/server/main");
const promise = require("promise");
var gTestGlobals = [];
-DebuggerServer.addTestGlobal = function (aGlobal) {
- gTestGlobals.push(aGlobal);
+DebuggerServer.addTestGlobal = function (global) {
+ gTestGlobals.push(global);
};
// A mock tab list, for use by tests. This simply presents each global in
// gTestGlobals as a tab, and the list is fixed: it never calls its
// onListChanged handler.
//
// As implemented now, we consult gTestGlobals when we're constructed, not
// when we're iterated over, so tests have to add their globals before the
// root actor is created.
-function TestTabList(aConnection) {
- this.conn = aConnection;
+function TestTabList(connection) {
+ this.conn = connection;
// An array of actors for each global added with
// DebuggerServer.addTestGlobal.
this._tabActors = [];
// A pool mapping those actors' names to the actors.
- this._tabActorPool = new ActorPool(aConnection);
+ this._tabActorPool = new ActorPool(connection);
for (let global of gTestGlobals) {
- let actor = new TestTabActor(aConnection, global);
+ let actor = new TestTabActor(connection, global);
actor.selected = false;
this._tabActors.push(actor);
this._tabActorPool.addActor(actor);
}
if (this._tabActors.length > 0) {
this._tabActors[0].selected = true;
}
- aConnection.addActorPool(this._tabActorPool);
+ connection.addActorPool(this._tabActorPool);
}
TestTabList.prototype = {
constructor: TestTabList,
getList: function () {
return promise.resolve([...this._tabActors]);
}
};
-function createRootActor(aConnection) {
- let root = new RootActor(aConnection, {
- tabList: new TestTabList(aConnection),
+function createRootActor(connection) {
+ let root = new RootActor(connection, {
+ tabList: new TestTabList(connection),
globalActorFactories: DebuggerServer.globalActorFactories
});
root.applicationType = "xpcshell-tests";
return root;
}
-function TestTabActor(aConnection, aGlobal) {
- this.conn = aConnection;
- this._global = aGlobal;
+function TestTabActor(connection, global) {
+ this.conn = connection;
+ this._global = global;
this._threadActor = new ThreadActor(this, this._global);
this.conn.addActor(this._threadActor);
this._attached = false;
this._extraActors = {};
}
TestTabActor.prototype = {
constructor: TestTabActor,
@@ -91,28 +92,28 @@ TestTabActor.prototype = {
this.conn.addActorPool(this._tabActorPool);
}
this._appendExtraActors(response);
return response;
},
- onAttach: function (aRequest) {
+ onAttach: function (request) {
this._attached = true;
let response = { type: "tabAttached", threadActor: this._threadActor.actorID };
this._appendExtraActors(response);
return response;
},
- onDetach: function (aRequest) {
+ onDetach: function (request) {
if (!this._attached) {
- return { "error":"wrongState" };
+ return { "error": "wrongState" };
}
return { type: "detached" };
},
/* Support for DebuggerServer.addTabActor. */
_createExtraActors: createExtraActors,
_appendExtraActors: appendExtraActors
};
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -7567,17 +7567,23 @@ nsDocShell::EndPageLoad(nsIWebProgress*
nsIChannel* aChannel, nsresult aStatus)
{
if (!aChannel) {
return NS_ERROR_NULL_POINTER;
}
nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
if (reporter) {
- reporter->FlushConsoleReports(GetDocument());
+ nsCOMPtr<nsILoadGroup> loadGroup;
+ aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
+ if (loadGroup) {
+ reporter->FlushConsoleReports(loadGroup);
+ } else {
+ reporter->FlushConsoleReports(GetDocument());
+ }
}
nsCOMPtr<nsIURI> url;
nsresult rv = aChannel->GetURI(getter_AddRefs(url));
if (NS_FAILED(rv)) {
return rv;
}
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -928,28 +928,38 @@ TimeoutManager::Timeouts::ResetTimersFor
// Since we reset When() we need to move |timeout| to the right
// place in the list so that it remains sorted by When().
// Get the pointer to the next timeout now, before we move the
// current timeout in the list.
Timeout* nextTimeout = timeout->getNext();
- // It is safe to remove and re-insert because When() is now
- // strictly smaller than it used to be, so we know we'll insert
- // |timeout| before nextTimeout.
- NS_ASSERTION(!nextTimeout ||
- timeout->When() < nextTimeout->When(), "How did that happen?");
- timeout->remove();
- // Insert() will addref |timeout| and reset mFiringDepth. Make sure to
- // undo that after calling it.
- uint32_t firingDepth = timeout->mFiringDepth;
- Insert(timeout, aSortBy);
- timeout->mFiringDepth = firingDepth;
- timeout->Release();
+ // Since we are only reducing intervals in this method we can
+ // make an optimization here. If the reduction does not cause us
+ // to fall before our previous timeout then we do not have to
+ // remove and re-insert the current timeout. This is important
+ // because re-insertion makes this algorithm O(n^2). Since we
+ // will typically be shifting a lot of timers at once this
+ // optimization saves us a lot of work.
+ Timeout* prevTimeout = timeout->getPrevious();
+ if (prevTimeout && prevTimeout->When() > timeout->When()) {
+ // It is safe to remove and re-insert because When() is now
+ // strictly smaller than it used to be, so we know we'll insert
+ // |timeout| before nextTimeout.
+ NS_ASSERTION(!nextTimeout ||
+ timeout->When() < nextTimeout->When(), "How did that happen?");
+ timeout->remove();
+ // Insert() will addref |timeout| and reset mFiringDepth. Make sure to
+ // undo that after calling it.
+ uint32_t firingDepth = timeout->mFiringDepth;
+ Insert(timeout, aSortBy);
+ timeout->mFiringDepth = firingDepth;
+ timeout->Release();
+ }
nsresult rv = timeout->InitTimer(aQueue, delay.ToMilliseconds());
if (NS_FAILED(rv)) {
NS_WARNING("Error resetting non background timer for DOM timeout!");
return rv;
}
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -1792,18 +1792,17 @@ nsContentUtils::ParseLegacyFontSize(cons
return clamped(value, 1, 7);
}
/* static */
bool
nsContentUtils::IsControlledByServiceWorker(nsIDocument* aDocument)
{
- if (aDocument &&
- aDocument->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId) {
+ if (nsContentUtils::IsInPrivateBrowsing(aDocument)) {
return false;
}
RefPtr<workers::ServiceWorkerManager> swm =
workers::ServiceWorkerManager::GetInstance();
MOZ_ASSERT(swm);
ErrorResult rv;
@@ -3197,16 +3196,50 @@ nsContentUtils::GetOriginAttributes(nsIL
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
if (loadContext && loadContext->GetOriginAttributes(attrs)) {
attrs.StripAttributes(OriginAttributes::STRIP_ADDON_ID);
}
}
return attrs;
}
+// static
+bool
+nsContentUtils::IsInPrivateBrowsing(nsIDocument* aDoc)
+{
+ if (!aDoc) {
+ return false;
+ }
+
+ nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
+ if (loadGroup) {
+ return IsInPrivateBrowsing(loadGroup);
+ }
+
+ nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
+ return channel && NS_UsePrivateBrowsing(channel);
+}
+
+// static
+bool
+nsContentUtils::IsInPrivateBrowsing(nsILoadGroup* aLoadGroup)
+{
+ if (!aLoadGroup) {
+ return false;
+ }
+ bool isPrivate = false;
+ nsCOMPtr<nsIInterfaceRequestor> callbacks;
+ aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
+ if (callbacks) {
+ nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
+ isPrivate = loadContext && loadContext->UsePrivateBrowsing();
+ }
+ return isPrivate;
+}
+
bool
nsContentUtils::DocumentInactiveForImageLoads(nsIDocument* aDocument)
{
if (aDocument && !IsChromeDoc(aDocument) && !aDocument->IsResourceDoc()) {
nsCOMPtr<nsPIDOMWindowInner> win =
do_QueryInterface(aDocument->GetScopeObject());
return !win || !win->GetDocShell();
}
@@ -3216,36 +3249,19 @@ nsContentUtils::DocumentInactiveForImage
imgLoader*
nsContentUtils::GetImgLoaderForDocument(nsIDocument* aDoc)
{
NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aDoc), nullptr);
if (!aDoc) {
return imgLoader::NormalLoader();
}
-
- nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
- if (loadGroup) {
- nsCOMPtr<nsIInterfaceRequestor> callbacks;
- loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
- if (callbacks) {
- nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
- if (loadContext && loadContext->UsePrivateBrowsing()) {
- return imgLoader::PrivateBrowsingLoader();
- }
- }
- return imgLoader::NormalLoader();
- }
-
- nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
- if (channel && NS_UsePrivateBrowsing(channel)) {
- return imgLoader::PrivateBrowsingLoader();
- }
-
- return imgLoader::NormalLoader();
+ bool isPrivate = IsInPrivateBrowsing(aDoc);
+ return isPrivate ? imgLoader::PrivateBrowsingLoader()
+ : imgLoader::NormalLoader();
}
// static
imgLoader*
nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel,
nsIDocument* aContext)
{
NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aContext), nullptr);
@@ -3667,16 +3683,32 @@ nsContentUtils::ReportToConsoleNonLocali
uint64_t innerWindowID = 0;
if (aDocument) {
if (!aURI) {
aURI = aDocument->GetDocumentURI();
}
innerWindowID = aDocument->InnerWindowID();
}
+ return ReportToConsoleByWindowID(aErrorText, aErrorFlags, aCategory,
+ innerWindowID, aURI, aSourceLine,
+ aLineNumber, aColumnNumber, aLocationMode);
+}
+
+/* static */ nsresult
+nsContentUtils::ReportToConsoleByWindowID(const nsAString& aErrorText,
+ uint32_t aErrorFlags,
+ const nsACString& aCategory,
+ uint64_t aInnerWindowID,
+ nsIURI* aURI,
+ const nsAFlatString& aSourceLine,
+ uint32_t aLineNumber,
+ uint32_t aColumnNumber,
+ MissingErrorLocationMode aLocationMode)
+{
nsresult rv;
if (!sConsoleService) { // only need to bother null-checking here
rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
NS_ENSURE_SUCCESS(rv, rv);
}
nsAutoCString spec;
if (!aLineNumber && aLocationMode == eUSE_CALLING_LOCATION) {
@@ -3693,17 +3725,17 @@ nsContentUtils::ReportToConsoleNonLocali
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = errorObject->InitWithWindowID(aErrorText,
NS_ConvertUTF8toUTF16(spec), // file name
aSourceLine,
aLineNumber, aColumnNumber,
aErrorFlags, aCategory,
- innerWindowID);
+ aInnerWindowID);
NS_ENSURE_SUCCESS(rv, rv);
return sConsoleService->LogMessage(errorObject);
}
void
nsContentUtils::LogMessageToConsole(const char* aMsg)
{
@@ -7332,18 +7364,28 @@ nsContentUtils::GetInnerWindowID(nsIRequ
nsCOMPtr<nsILoadGroup> loadGroup;
nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
if (NS_FAILED(rv) || !loadGroup) {
return 0;
}
+ return GetInnerWindowID(loadGroup);
+}
+
+uint64_t
+nsContentUtils::GetInnerWindowID(nsILoadGroup* aLoadGroup)
+{
+ if (!aLoadGroup) {
+ return 0;
+ }
+
nsCOMPtr<nsIInterfaceRequestor> callbacks;
- rv = loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
+ nsresult rv = aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
if (NS_FAILED(rv) || !callbacks) {
return 0;
}
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
if (!loadContext) {
return 0;
}
@@ -8616,17 +8658,17 @@ nsContentUtils::InternalStorageAllowedFo
if (aWindow) {
// If the document is sandboxed, then it is not permitted to use storage
nsIDocument* document = aWindow->GetExtantDoc();
if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
return StorageAccess::eDeny;
}
// Check if we are in private browsing, and record that fact
- if (document->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId) {
+ if (IsInPrivateBrowsing(document)) {
access = StorageAccess::ePrivateBrowsing;
}
}
nsCOMPtr<nsIPermissionManager> permissionManager =
services::GetPermissionManager();
if (!permissionManager) {
return StorageAccess::eDeny;
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -813,16 +813,26 @@ public:
/**
* Returns origin attributes of the load group.
**/
static mozilla::OriginAttributes
GetOriginAttributes(nsILoadGroup* aLoadGroup);
/**
+ * Returns true if this document is in a Private Browsing window.
+ */
+ static bool IsInPrivateBrowsing(nsIDocument* aDoc);
+
+ /**
+ * Returns true if this loadGroup uses Private Browsing.
+ */
+ static bool IsInPrivateBrowsing(nsILoadGroup* aLoadGroup);
+
+ /**
* If aNode is not an element, return true exactly when aContent's binding
* parent is null.
*
* If aNode is an element, return true exactly when aContent's binding parent
* is the same as aNode's.
*
* This method is particularly useful for callers who are trying to ensure
* that they are working with a non-anonymous descendant of a given node. If
@@ -880,16 +890,47 @@ public:
const nsAFlatString& aSourceLine
= EmptyString(),
uint32_t aLineNumber = 0,
uint32_t aColumnNumber = 0,
MissingErrorLocationMode aLocationMode
= eUSE_CALLING_LOCATION);
/**
+ * Report a non-localized error message to the error console base on the
+ * innerWindowID.
+ * @param aErrorText the error message
+ * @param aErrorFlags See nsIScriptError.
+ * @param aCategory Name of module reporting error.
+ * @param [aInnerWindowID] Inner window ID for document which triggered the
+ * message.
+ * @param [aURI=nullptr] (Optional) URI of resource containing error.
+ * @param [aSourceLine=EmptyString()] (Optional) The text of the line that
+ contains the error (may be empty).
+ * @param [aLineNumber=0] (Optional) Line number within resource
+ containing error.
+ * @param [aColumnNumber=0] (Optional) Column number within resource
+ containing error.
+ If aURI is null, then aDocument->GetDocumentURI() is used.
+ * @param [aLocationMode] (Optional) Specifies the behavior if
+ error location information is omitted.
+ */
+ static nsresult ReportToConsoleByWindowID(const nsAString& aErrorText,
+ uint32_t aErrorFlags,
+ const nsACString& aCategory,
+ uint64_t aInnerWindowID,
+ nsIURI* aURI = nullptr,
+ const nsAFlatString& aSourceLine
+ = EmptyString(),
+ uint32_t aLineNumber = 0,
+ uint32_t aColumnNumber = 0,
+ MissingErrorLocationMode aLocationMode
+ = eUSE_CALLING_LOCATION);
+
+ /**
* Report a localized error message to the error console.
* @param aErrorFlags See nsIScriptError.
* @param aCategory Name of module reporting error.
* @param aDocument Reference to the document which triggered the message.
* @param aFile Properties file containing localized message.
* @param aMessageName Name of localized message.
* @param [aParams=nullptr] (Optional) Parameters to be substituted into
localized message.
@@ -2442,21 +2483,26 @@ public:
/**
* Returns whether a given header is forbidden for an XHR or fetch
* response.
*/
static bool IsForbiddenResponseHeader(const nsACString& aHeader);
/**
- * Returns the inner window ID for the window associated with a request,
+ * Returns the inner window ID for the window associated with a request.
*/
static uint64_t GetInnerWindowID(nsIRequest* aRequest);
/**
+ * Returns the inner window ID for the window associated with a load group.
+ */
+ static uint64_t GetInnerWindowID(nsILoadGroup* aLoadGroup);
+
+ /**
* If the hostname for aURI is an IPv6 it encloses it in brackets,
* otherwise it just outputs the hostname in aHost.
*/
static nsresult GetHostOrIPv6WithBrackets(nsIURI* aURI, nsAString& aHost);
static nsresult GetHostOrIPv6WithBrackets(nsIURI* aURI, nsCString& aHost);
/*
* Call the given callback on all remote children of the given top-level
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -2483,17 +2483,22 @@ nsScriptLoader::OnStreamComplete(nsIIncr
MOZ_ASSERT(mReporter);
nsAutoCString sourceUri;
if (mDocument && mDocument->GetDocumentURI()) {
mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
}
rv = aSRIDataVerifier->Verify(aRequest->mIntegrity, channel, sourceUri,
mReporter);
- mReporter->FlushConsoleReports(mDocument);
+ if (channelRequest) {
+ mReporter->FlushReportsToConsole(
+ nsContentUtils::GetInnerWindowID(channelRequest));
+ } else {
+ mReporter->FlushConsoleReports(mDocument);
+ }
if (NS_FAILED(rv)) {
rv = NS_ERROR_SRI_CORRUPT;
}
} else {
nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
if (loadInfo->GetEnforceSRI()) {
MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
--- a/dom/console/ConsoleReportCollector.cpp
+++ b/dom/console/ConsoleReportCollector.cpp
@@ -34,64 +34,85 @@ ConsoleReportCollector::AddConsoleReport
mPendingReports.AppendElement(PendingReport(aErrorFlags, aCategory,
aPropertiesFile, aSourceFileURI,
aLineNumber, aColumnNumber,
aMessageName, aStringParams));
}
void
-ConsoleReportCollector::FlushConsoleReports(nsIDocument* aDocument,
- ReportAction aAction)
+ConsoleReportCollector::FlushReportsToConsole(uint64_t aInnerWindowID,
+ ReportAction aAction)
{
- MOZ_ASSERT(NS_IsMainThread());
-
nsTArray<PendingReport> reports;
{
MutexAutoLock lock(mMutex);
if (aAction == ReportAction::Forget) {
mPendingReports.SwapElements(reports);
} else {
reports = mPendingReports;
}
}
for (uint32_t i = 0; i < reports.Length(); ++i) {
PendingReport& report = reports[i];
+ nsXPIDLString errorText;
+ nsresult rv;
+ if (!report.mStringParams.IsEmpty()) {
+ rv = nsContentUtils::FormatLocalizedString(report.mPropertiesFile,
+ report.mMessageName.get(),
+ report.mStringParams,
+ errorText);
+ } else {
+ rv = nsContentUtils::GetLocalizedString(report.mPropertiesFile,
+ report.mMessageName.get(),
+ errorText);
+ }
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ continue;
+ }
+
// It would be nice if we did not have to do this since ReportToConsole()
// just turns around and converts it back to a spec.
nsCOMPtr<nsIURI> uri;
if (!report.mSourceFileURI.IsEmpty()) {
nsresult rv = NS_NewURI(getter_AddRefs(uri), report.mSourceFileURI);
MOZ_ALWAYS_SUCCEEDS(rv);
if (NS_FAILED(rv)) {
continue;
}
}
- // Convert back from nsTArray<nsString> to the char16_t** format required
- // by our l10n libraries and ReportToConsole. (bug 1219762)
- UniquePtr<const char16_t*[]> params;
- uint32_t paramsLength = report.mStringParams.Length();
- if (paramsLength > 0) {
- params = MakeUnique<const char16_t*[]>(paramsLength);
- for (uint32_t j = 0; j < paramsLength; ++j) {
- params[j] = report.mStringParams[j].get();
- }
- }
+ nsContentUtils::ReportToConsoleByWindowID(errorText,
+ report.mErrorFlags,
+ report.mCategory,
+ aInnerWindowID,
+ uri,
+ EmptyString(),
+ report.mLineNumber,
+ report.mColumnNumber);
+ }
+}
- nsContentUtils::ReportToConsole(report.mErrorFlags, report.mCategory,
- aDocument, report.mPropertiesFile,
- report.mMessageName.get(),
- params.get(),
- paramsLength, uri, EmptyString(),
- report.mLineNumber, report.mColumnNumber);
- }
+void
+ConsoleReportCollector::FlushConsoleReports(nsIDocument* aDocument,
+ ReportAction aAction)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ FlushReportsToConsole(aDocument ? aDocument->InnerWindowID() : 0, aAction);
+}
+
+void
+ConsoleReportCollector::FlushConsoleReports(nsILoadGroup* aLoadGroup,
+ ReportAction aAction)
+{
+ FlushReportsToConsole(nsContentUtils::GetInnerWindowID(aLoadGroup), aAction);
}
void
ConsoleReportCollector::FlushConsoleReports(nsIConsoleReportCollector* aCollector)
{
MOZ_ASSERT(aCollector);
nsTArray<PendingReport> reports;
@@ -106,81 +127,16 @@ ConsoleReportCollector::FlushConsoleRepo
aCollector->AddConsoleReport(report.mErrorFlags, report.mCategory,
report.mPropertiesFile, report.mSourceFileURI,
report.mLineNumber, report.mColumnNumber,
report.mMessageName, report.mStringParams);
}
}
void
-ConsoleReportCollector::FlushReportsByWindowId(uint64_t aWindowId,
- ReportAction aAction)
-{
- MOZ_ASSERT(NS_IsMainThread());
-
- nsTArray<PendingReport> reports;
-
- {
- MutexAutoLock lock(mMutex);
- if (aAction == ReportAction::Forget) {
- mPendingReports.SwapElements(reports);
- } else {
- reports = mPendingReports;
- }
- }
-
- nsCOMPtr<nsIConsoleService> consoleService =
- do_GetService(NS_CONSOLESERVICE_CONTRACTID);
- if (!consoleService) {
- NS_WARNING("GetConsoleService failed");
- return;
- }
-
- nsresult rv;
- for (uint32_t i = 0; i < reports.Length(); ++i) {
- PendingReport& report = reports[i];
-
- nsXPIDLString errorText;
- if (!report.mStringParams.IsEmpty()) {
- rv = nsContentUtils::FormatLocalizedString(report.mPropertiesFile,
- report.mMessageName.get(),
- report.mStringParams,
- errorText);
- } else {
- rv = nsContentUtils::GetLocalizedString(report.mPropertiesFile,
- report.mMessageName.get(),
- errorText);
- }
- if (NS_WARN_IF(NS_FAILED(rv))) {
- continue;
- }
-
- nsCOMPtr<nsIScriptError> errorObject =
- do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- continue;
- }
-
- rv = errorObject->InitWithWindowID(errorText,
- NS_ConvertUTF8toUTF16(report.mSourceFileURI),
- EmptyString(),
- report.mLineNumber,
- report.mColumnNumber,
- report.mErrorFlags,
- report.mCategory,
- aWindowId);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- continue;
- }
-
- consoleService->LogMessage(errorObject);
- }
-}
-
-void
ConsoleReportCollector::ClearConsoleReports()
{
MutexAutoLock lock(mMutex);
mPendingReports.Clear();
}
ConsoleReportCollector::~ConsoleReportCollector()
--- a/dom/console/ConsoleReportCollector.h
+++ b/dom/console/ConsoleReportCollector.h
@@ -22,27 +22,31 @@ public:
AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory,
nsContentUtils::PropertiesFile aPropertiesFile,
const nsACString& aSourceFileURI,
uint32_t aLineNumber, uint32_t aColumnNumber,
const nsACString& aMessageName,
const nsTArray<nsString>& aStringParams) override;
void
+ FlushReportsToConsole(uint64_t aInnerWindowID,
+ ReportAction aAction = ReportAction::Forget) override;
+
+ void
FlushConsoleReports(nsIDocument* aDocument,
ReportAction aAction = ReportAction::Forget) override;
void
+ FlushConsoleReports(nsILoadGroup* aLoadGroup,
+ ReportAction aAction = ReportAction::Forget) override;
+
+ void
FlushConsoleReports(nsIConsoleReportCollector* aCollector) override;
void
- FlushReportsByWindowId(uint64_t aWindowId,
- ReportAction aAction = ReportAction::Forget) override;
-
- void
ClearConsoleReports() override;
private:
~ConsoleReportCollector();
struct PendingReport
{
PendingReport(uint32_t aErrorFlags, const nsACString& aCategory,
--- a/dom/console/nsIConsoleReportCollector.h
+++ b/dom/console/nsIConsoleReportCollector.h
@@ -69,47 +69,58 @@ public:
// An enum calss to indicate whether should free the pending reports or not.
// Forget Free the pending reports.
// Save Keep the pending reports.
enum class ReportAction {
Forget,
Save
};
+ // Flush all pending reports to the console. May be called from any thread.
+ //
+ // aInnerWindowID A inner window ID representing where to flush the reports.
+ // aAction An action to determine whether to reserve the pending
+ // reports. Defalut action is to forget the report.
+ virtual void
+ FlushReportsToConsole(uint64_t aInnerWindowID,
+ ReportAction aAction = ReportAction::Forget) = 0;
+
// Flush all pending reports to the console. Main thread only.
//
// aDocument An optional document representing where to flush the
// reports. If provided, then the corresponding window's
// web console will get the reports. Otherwise the reports
// go to the browser console.
// aAction An action to determine whether to reserve the pending
// reports. Defalut action is to forget the report.
virtual void
FlushConsoleReports(nsIDocument* aDocument,
ReportAction aAction = ReportAction::Forget) = 0;
+ // Flush all pending reports to the console. May be called from any thread.
+ //
+ // aLoadGroup An optional loadGroup representing where to flush the
+ // reports. If provided, then the corresponding window's
+ // web console will get the reports. Otherwise the reports
+ // go to the browser console.
+ // aAction An action to determine whether to reserve the pending
+ // reports. Defalut action is to forget the report.
+ virtual void
+ FlushConsoleReports(nsILoadGroup* aLoadGroup,
+ ReportAction aAction = ReportAction::Forget) = 0;
+
+
// Flush all pending reports to another collector. May be called from any
// thread.
//
// aCollector A required collector object that will effectively take
// ownership of our currently console reports.
virtual void
FlushConsoleReports(nsIConsoleReportCollector* aCollector) = 0;
- // Flush all pending reports to the console accroding to window ID. Main
- // thread only.
- //
- // aWindowId A window ID representing where to flush the reports and it's
- // typically the inner window ID.
- //
- // aAction An action to decide whether free the pending reports or not.
- virtual void
- FlushReportsByWindowId(uint64_t aWindowId,
- ReportAction aAction = ReportAction::Forget) = 0;
-
// Clear all pending reports.
virtual void
ClearConsoleReports() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIConsoleReportCollector, NS_NSICONSOLEREPORTCOLLECTOR_IID)
#endif // nsIConsoleReportCollector_h
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -95,41 +95,41 @@ private:
FlushConsoleReport() override;
};
class MainThreadFetchResolver final : public FetchDriverObserver
{
RefPtr<Promise> mPromise;
RefPtr<Response> mResponse;
- nsCOMPtr<nsIDocument> mDocument;
+ nsCOMPtr<nsILoadGroup> mLoadGroup;
NS_DECL_OWNINGTHREAD
public:
explicit MainThreadFetchResolver(Promise* aPromise);
void
OnResponseAvailableInternal(InternalResponse* aResponse) override;
- void SetDocument(nsIDocument* aDocument)
+ void SetLoadGroup(nsILoadGroup* aLoadGroup)
{
- mDocument = aDocument;
+ mLoadGroup = aLoadGroup;
}
virtual void OnResponseEnd() override
{
FlushConsoleReport();
}
private:
~MainThreadFetchResolver();
void FlushConsoleReport() override
{
- mReporter->FlushConsoleReports(mDocument);
+ mReporter->FlushConsoleReports(mLoadGroup);
}
};
class MainThreadFetchRunnable : public Runnable
{
RefPtr<WorkerFetchResolver> mResolver;
RefPtr<InternalRequest> mRequest;
@@ -233,17 +233,17 @@ FetchRequest(nsIGlobalObject* aGlobal, c
}
}
Telemetry::Accumulate(Telemetry::FETCH_IS_MAINTHREAD, 1);
RefPtr<MainThreadFetchResolver> resolver = new MainThreadFetchResolver(p);
RefPtr<FetchDriver> fetch = new FetchDriver(r, principal, loadGroup);
fetch->SetDocument(doc);
- resolver->SetDocument(doc);
+ resolver->SetLoadGroup(loadGroup);
aRv = fetch->Fetch(resolver);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
} else {
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
@@ -448,40 +448,40 @@ WorkerFetchResolver::FlushConsoleReport(
MOZ_ASSERT(mPromiseProxy);
if(!mReporter) {
return;
}
workers::WorkerPrivate* worker = mPromiseProxy->GetWorkerPrivate();
if (!worker) {
- mReporter->FlushConsoleReports((nsIDocument*)nullptr);
+ mReporter->FlushReportsToConsole(0);
return;
}
if (worker->IsServiceWorker()) {
// Flush to service worker
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (!swm) {
- mReporter->FlushConsoleReports((nsIDocument*)nullptr);
+ mReporter->FlushReportsToConsole(0);
return;
}
swm->FlushReportsToAllClients(worker->WorkerName(), mReporter);
return;
}
if (worker->IsSharedWorker()) {
// Flush to shared worker
worker->FlushReportsToSharedWorkers(mReporter);
return;
}
// Flush to dedicated worker
- mReporter->FlushConsoleReports(worker->GetDocument());
+ mReporter->FlushConsoleReports(worker->GetLoadGroup());
}
nsresult
ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
nsIInputStream** aStream,
nsCString& aContentTypeWithCharset,
uint64_t& aContentLength)
{
--- a/dom/file/File.cpp
+++ b/dom/file/File.cpp
@@ -31,16 +31,17 @@
#include "nsPrintfCString.h"
#include "mozilla/SHA1.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/Preferences.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/BlobBinding.h"
#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/FileBinding.h"
+#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRunnable.h"
#include "nsThreadUtils.h"
#include "nsStreamUtils.h"
#include "SlicedInputStream.h"
namespace mozilla {
namespace dom {
@@ -465,25 +466,28 @@ File::WrapObject(JSContext* aCx, JS::Han
void
File::GetName(nsAString& aFileName) const
{
mImpl->GetName(aFileName);
}
void
-File::GetPath(nsAString& aPath) const
+File::GetRelativePath(nsAString& aPath) const
{
- mImpl->GetPath(aPath);
-}
+ aPath.Truncate();
-void
-File::SetPath(const nsAString& aPath)
-{
- mImpl->SetPath(aPath);
+ nsAutoString path;
+ mImpl->GetDOMPath(path);
+
+ // WebkitRelativePath doesn't start with '/'
+ if (!path.IsEmpty()) {
+ MOZ_ASSERT(path[0] == FILESYSTEM_DOM_PATH_SEPARATOR_CHAR);
+ aPath.Assign(Substring(path, 1));
+ }
}
Date
File::GetLastModifiedDate(ErrorResult& aRv)
{
int64_t value = GetLastModified(aRv);
if (aRv.Failed()) {
return Date();
@@ -665,24 +669,24 @@ NS_IMPL_ISUPPORTS_INHERITED0(BlobImplFil
void
BlobImplBase::GetName(nsAString& aName) const
{
MOZ_ASSERT(mIsFile, "Should only be called on files");
aName = mName;
}
void
-BlobImplBase::GetPath(nsAString& aPath) const
+BlobImplBase::GetDOMPath(nsAString& aPath) const
{
MOZ_ASSERT(mIsFile, "Should only be called on files");
aPath = mPath;
}
void
-BlobImplBase::SetPath(const nsAString& aPath)
+BlobImplBase::SetDOMPath(const nsAString& aPath)
{
MOZ_ASSERT(mIsFile, "Should only be called on files");
mPath = aPath;
}
void
BlobImplBase::GetMozFullPath(nsAString& aFileName,
SystemCallerGuarantee /* unused */,
--- a/dom/file/File.h
+++ b/dom/file/File.h
@@ -220,23 +220,17 @@ public:
ErrorResult& aRv);
void GetName(nsAString& aName) const;
int64_t GetLastModified(ErrorResult& aRv);
Date GetLastModifiedDate(ErrorResult& aRv);
- // GetPath and SetPath are currently used only for the webkitRelativePath
- // attribute and they are only used when this File object is created from a
- // Directory, generated by a Directory Picker.
-
- void GetPath(nsAString& aName) const;
-
- void SetPath(const nsAString& aName);
+ void GetRelativePath(nsAString& aPath) const;
void GetMozFullPath(nsAString& aFilename, SystemCallerGuarantee aGuarantee,
ErrorResult& aRv) const;
void GetMozFullPathInternal(nsAString& aName, ErrorResult& aRv) const;
protected:
virtual bool HasFileInterface() const override { return true; }
@@ -255,19 +249,19 @@ class BlobImpl : public nsISupports
public:
NS_DECLARE_STATIC_IID_ACCESSOR(BLOBIMPL_IID)
NS_DECL_THREADSAFE_ISUPPORTS
BlobImpl() {}
virtual void GetName(nsAString& aName) const = 0;
- virtual void GetPath(nsAString& aName) const = 0;
+ virtual void GetDOMPath(nsAString& aName) const = 0;
- virtual void SetPath(const nsAString& aName) = 0;
+ virtual void SetDOMPath(const nsAString& aName) = 0;
virtual int64_t GetLastModified(ErrorResult& aRv) = 0;
virtual void SetLastModified(int64_t aLastModified) = 0;
virtual void GetMozFullPath(nsAString& aName,
SystemCallerGuarantee /* unused */,
ErrorResult& aRv) const = 0;
@@ -401,19 +395,19 @@ public:
{
MOZ_ASSERT(aLength != UINT64_MAX, "Must know length when creating slice");
// Ensure non-null mContentType by default
mContentType.SetIsVoid(false);
}
virtual void GetName(nsAString& aName) const override;
- virtual void GetPath(nsAString& aName) const override;
+ virtual void GetDOMPath(nsAString& aName) const override;
- virtual void SetPath(const nsAString& aName) override;
+ virtual void SetDOMPath(const nsAString& aName) override;
virtual int64_t GetLastModified(ErrorResult& aRv) override;
virtual void SetLastModified(int64_t aLastModified) override;
virtual void GetMozFullPath(nsAString& aName,
SystemCallerGuarantee /* unused */,
ErrorResult& aRv) const override;
--- a/dom/file/ipc/Blob.cpp
+++ b/dom/file/ipc/Blob.cpp
@@ -2066,20 +2066,20 @@ public:
NoteDyingActor();
NS_DECL_ISUPPORTS_INHERITED
void
GetName(nsAString& aName) const override;
void
- GetPath(nsAString& aPath) const override;
+ GetDOMPath(nsAString& aPath) const override;
void
- SetPath(const nsAString& aPath) override;
+ SetDOMPath(const nsAString& aPath) override;
int64_t
GetLastModified(ErrorResult& aRv) override;
void
SetLastModified(int64_t aLastModified) override;
void
@@ -2170,27 +2170,27 @@ private:
* BlobChild::RemoteBlobImpl
******************************************************************************/
BlobChild::
RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
BlobImpl* aRemoteBlobImpl,
const nsAString& aName,
const nsAString& aContentType,
- const nsAString& aPath,
+ const nsAString& aDOMPath,
uint64_t aLength,
int64_t aModDate,
BlobImplIsDirectory aIsDirectory,
bool aIsSameProcessBlob)
: BlobImplBase(aName, aContentType, aLength, aModDate)
, mWorkerPrivate(nullptr)
, mMutex("BlobChild::RemoteBlobImpl::mMutex")
, mIsSlice(false), mIsDirectory(aIsDirectory == eDirectory)
{
- SetPath(aPath);
+ SetDOMPath(aDOMPath);
if (aIsSameProcessBlob) {
MOZ_ASSERT(aRemoteBlobImpl);
mSameProcessBlobImpl = aRemoteBlobImpl;
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
} else {
mDifferentProcessBlobImpl = aRemoteBlobImpl;
}
@@ -2838,26 +2838,26 @@ void
BlobParent::
RemoteBlobImpl::GetName(nsAString& aName) const
{
mBlobImpl->GetName(aName);
}
void
BlobParent::
-RemoteBlobImpl::GetPath(nsAString& aPath) const
+RemoteBlobImpl::GetDOMPath(nsAString& aPath) const
{
- mBlobImpl->GetPath(aPath);
+ mBlobImpl->GetDOMPath(aPath);
}
void
BlobParent::
-RemoteBlobImpl::SetPath(const nsAString& aPath)
+RemoteBlobImpl::SetDOMPath(const nsAString& aPath)
{
- mBlobImpl->SetPath(aPath);
+ mBlobImpl->SetDOMPath(aPath);
}
int64_t
BlobParent::
RemoteBlobImpl::GetLastModified(ErrorResult& aRv)
{
return mBlobImpl->GetLastModified(aRv);
}
@@ -3191,28 +3191,28 @@ BlobChild::CommonInit(BlobChild* aOther,
uint64_t length = otherImpl->GetSize(rv);
MOZ_ASSERT(!rv.Failed());
RemoteBlobImpl* remoteBlob = nullptr;
if (otherImpl->IsFile()) {
nsAutoString name;
otherImpl->GetName(name);
- nsAutoString path;
- otherImpl->GetPath(path);
+ nsAutoString domPath;
+ otherImpl->GetDOMPath(domPath);
int64_t modDate = otherImpl->GetLastModified(rv);
MOZ_ASSERT(!rv.Failed());
RemoteBlobImpl::BlobImplIsDirectory directory = otherImpl->IsDirectory() ?
RemoteBlobImpl::BlobImplIsDirectory::eDirectory :
RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory;
remoteBlob =
- new RemoteBlobImpl(this, otherImpl, name, contentType, path,
+ new RemoteBlobImpl(this, otherImpl, name, contentType, domPath,
length, modDate, directory,
false /* SameProcessBlobImpl */);
} else {
remoteBlob = new RemoteBlobImpl(this, otherImpl, contentType, length,
false /* SameProcessBlobImpl */);
}
// This RemoteBlob must be kept alive untill RecvCreatedFromKnownBlob is
@@ -3286,33 +3286,33 @@ BlobChild::CommonInit(const ChildBlobCon
nsString contentType;
blobImpl->GetType(contentType);
if (blobImpl->IsFile()) {
nsAutoString name;
blobImpl->GetName(name);
- nsAutoString path;
- blobImpl->GetPath(path);
+ nsAutoString domPath;
+ blobImpl->GetDOMPath(domPath);
int64_t lastModifiedDate = blobImpl->GetLastModified(rv);
MOZ_ASSERT(!rv.Failed());
RemoteBlobImpl::BlobImplIsDirectory directory =
blobImpl->IsDirectory() ?
RemoteBlobImpl::BlobImplIsDirectory::eDirectory :
RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory;
remoteBlob =
new RemoteBlobImpl(this,
blobImpl,
name,
contentType,
- path,
+ domPath,
size,
lastModifiedDate,
directory,
true /* SameProcessBlobImpl */);
} else {
remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size,
true /* SameProcessBlobImpl */);
}
@@ -3485,24 +3485,24 @@ BlobChild::GetOrCreateFromImpl(ChildMana
ErrorResult rv;
uint64_t length = aBlobImpl->GetSize(rv);
MOZ_ASSERT(!rv.Failed());
if (aBlobImpl->IsFile()) {
nsAutoString name;
aBlobImpl->GetName(name);
- nsAutoString path;
- aBlobImpl->GetPath(path);
+ nsAutoString domPath;
+ aBlobImpl->GetDOMPath(domPath);
int64_t modDate = aBlobImpl->GetLastModified(rv);
MOZ_ASSERT(!rv.Failed());
blobParams =
- FileBlobConstructorParams(name, contentType, path, length, modDate,
+ FileBlobConstructorParams(name, contentType, domPath, length, modDate,
aBlobImpl->IsDirectory(), blobData);
} else {
blobParams = NormalBlobConstructorParams(contentType, length, blobData);
}
}
auto* actor = new BlobChild(aManager, aBlobImpl);
@@ -4038,24 +4038,24 @@ BlobParent::GetOrCreateFromImpl(ParentMa
ErrorResult rv;
uint64_t length = aBlobImpl->GetSize(rv);
MOZ_ASSERT(!rv.Failed());
if (aBlobImpl->IsFile()) {
nsAutoString name;
aBlobImpl->GetName(name);
- nsAutoString path;
- aBlobImpl->GetPath(path);
+ nsAutoString domPath;
+ aBlobImpl->GetDOMPath(domPath);
int64_t modDate = aBlobImpl->GetLastModified(rv);
MOZ_ASSERT(!rv.Failed());
blobParams =
- FileBlobConstructorParams(name, contentType, path, length, modDate,
+ FileBlobConstructorParams(name, contentType, domPath, length, modDate,
aBlobImpl->IsDirectory(), void_t());
} else {
blobParams = NormalBlobConstructorParams(contentType, length, void_t());
}
}
}
nsID id;
--- a/dom/file/moz.build
+++ b/dom/file/moz.build
@@ -18,16 +18,17 @@ EXPORTS += [
'nsHostObjectURI.h',
]
EXPORTS.mozilla.dom += [
'BlobSet.h',
'File.h',
'FileList.h',
'FileReader.h',
+ 'MultipartBlobImpl.h',
'MutableBlobStorage.h',
'MutableBlobStreamListener.h',
]
UNIFIED_SOURCES += [
'BlobSet.cpp',
'File.cpp',
'FileList.cpp',
--- a/dom/filesystem/GetDirectoryListingTask.cpp
+++ b/dom/filesystem/GetDirectoryListingTask.cpp
@@ -247,17 +247,17 @@ GetDirectoryListingTaskParent::GetSucces
// This is specific for unix root filesystem.
if (!mDOMPath.EqualsLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL)) {
filePath.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
}
nsAutoString name;
blobImpl->GetName(name);
filePath.Append(name);
- blobImpl->SetPath(filePath);
+ blobImpl->SetDOMPath(filePath);
fileData.blobParent() =
BlobParent::GetOrCreate(mRequestParent->Manager(), blobImpl);
inputs.AppendElement(fileData);
} else {
MOZ_ASSERT(mTargetData[i].mType == FileOrDirectoryPath::eDirectoryPath);
FileSystemDirectoryListingResponseDirectory directoryData;
directoryData.directoryRealPath() = mTargetData[i].mPath;
--- a/dom/filesystem/GetFilesHelper.cpp
+++ b/dom/filesystem/GetFilesHelper.cpp
@@ -272,27 +272,27 @@ GetFilesHelper::RunIO()
MOZ_ASSERT(!mListingCompleted);
nsCOMPtr<nsIFile> file;
mErrorResult = NS_NewLocalFile(mDirectoryPath, true, getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(mErrorResult))) {
return;
}
- nsAutoString path;
- mErrorResult = file->GetLeafName(path);
+ nsAutoString leafName;
+ mErrorResult = file->GetLeafName(leafName);
if (NS_WARN_IF(NS_FAILED(mErrorResult))) {
return;
}
- if (path.IsEmpty()) {
- path.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
- }
+ nsAutoString domPath;
+ domPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
+ domPath.Append(leafName);
- mErrorResult = ExploreDirectory(path, file);
+ mErrorResult = ExploreDirectory(domPath, file);
}
void
GetFilesHelper::RunMainThread()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mDirectoryPath.IsEmpty());
MOZ_ASSERT(!mListingCompleted);
@@ -379,17 +379,17 @@ GetFilesHelperBase::ExploreDirectory(con
nsAutoString leafName;
if (NS_WARN_IF(NS_FAILED(currFile->GetLeafName(leafName)))) {
continue;
}
domPath.Append(leafName);
if (isFile) {
RefPtr<BlobImpl> blobImpl = new BlobImplFile(currFile);
- blobImpl->SetPath(domPath);
+ blobImpl->SetDOMPath(domPath);
if (!mTargetBlobImplArray.AppendElement(blobImpl, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
continue;
}
--- a/dom/filesystem/compat/FileSystemFileEntry.cpp
+++ b/dom/filesystem/compat/FileSystemFileEntry.cpp
@@ -2,43 +2,74 @@
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileSystemFileEntry.h"
#include "CallbackRunnables.h"
#include "mozilla/dom/File.h"
+#include "mozilla/dom/MultipartBlobImpl.h"
#include "mozilla/dom/FileSystemFileEntryBinding.h"
namespace mozilla {
namespace dom {
namespace {
class FileCallbackRunnable final : public Runnable
{
public:
- FileCallbackRunnable(FileCallback* aCallback, File* aFile)
+ FileCallbackRunnable(FileCallback* aCallback, ErrorCallback* aErrorCallback,
+ File* aFile)
: mCallback(aCallback)
+ , mErrorCallback(aErrorCallback)
, mFile(aFile)
{
MOZ_ASSERT(aCallback);
MOZ_ASSERT(aFile);
}
NS_IMETHOD
Run() override
{
- mCallback->HandleEvent(*mFile);
+ // Here we clone the File object.
+
+ nsAutoString name;
+ mFile->GetName(name);
+
+ nsAutoString type;
+ mFile->GetType(type);
+
+ nsTArray<RefPtr<BlobImpl>> blobImpls;
+ blobImpls.AppendElement(mFile->Impl());
+
+ ErrorResult rv;
+ RefPtr<BlobImpl> blobImpl =
+ MultipartBlobImpl::Create(Move(blobImpls), name, type, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ if (mErrorCallback) {
+ RefPtr<DOMException> exception =
+ DOMException::Create(rv.StealNSResult());
+ mErrorCallback->HandleEvent(*exception);
+ }
+
+ return NS_OK;
+ }
+
+ RefPtr<File> file = File::Create(mFile->GetParentObject(), blobImpl);
+ MOZ_ASSERT(file);
+
+ mCallback->HandleEvent(*file);
return NS_OK;
}
private:
RefPtr<FileCallback> mCallback;
+ RefPtr<ErrorCallback> mErrorCallback;
RefPtr<File> mFile;
};
} // anonymous namespace
NS_IMPL_CYCLE_COLLECTION_INHERITED(FileSystemFileEntry, FileSystemEntry, mFile)
NS_IMPL_ADDREF_INHERITED(FileSystemFileEntry, FileSystemEntry)
@@ -72,17 +103,17 @@ void
FileSystemFileEntry::GetName(nsAString& aName, ErrorResult& aRv) const
{
mFile->GetName(aName);
}
void
FileSystemFileEntry::GetFullPath(nsAString& aPath, ErrorResult& aRv) const
{
- mFile->GetPath(aPath);
+ mFile->Impl()->GetDOMPath(aPath);
if (aPath.IsEmpty()) {
// We're under the root directory. webkitRelativePath
// (implemented as GetPath) is for cases when file is selected because its
// ancestor directory is selected. But that is not the case here, so need to
// manually prepend '/'.
nsAutoString name;
mFile->GetName(name);
aPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
@@ -90,15 +121,18 @@ FileSystemFileEntry::GetFullPath(nsAStri
}
}
void
FileSystemFileEntry::GetFile(FileCallback& aSuccessCallback,
const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback) const
{
RefPtr<FileCallbackRunnable> runnable =
- new FileCallbackRunnable(&aSuccessCallback, mFile);
+ new FileCallbackRunnable(&aSuccessCallback,
+ aErrorCallback.WasPassed()
+ ? &aErrorCallback.Value() : nullptr,
+ mFile);
DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
}
} // dom namespace
} // mozilla namespace
--- a/dom/filesystem/compat/tests/test_basic.html
+++ b/dom/filesystem/compat/tests/test_basic.html
@@ -403,16 +403,35 @@ function test_getParent(entry, parentEnt
} else {
test_getParent(e, parentEntry, false);
}
}, function(e) {
ok(false, "This should not happen.");
});
}
+function test_webkitRelativePath() {
+ fileEntry.file(function(file1) {
+ ok(file1, "We have a file here!");
+ ok(!file1.webkitRelativePath, "webkitRelativePath is an empty string");
+
+ fileEntry.file(function(file2) {
+ ok(file2, "We have a file here!");
+ ok(!file2.webkitRelativePath, "webkitRelativePath is an empty string");
+ isnot(file1, file2, "The 2 files are not the same");
+
+ next();
+ }, function() {
+ ok(false, "Something when wrong!");
+ });
+ }, function() {
+ ok(false, "Something when wrong!");
+ });
+}
+
var tests = [
setup_tests,
populate_entries,
test_entries,
test_fileEntry,
test_fileEntry_file,
@@ -447,16 +466,18 @@ var tests = [
test_root_getDirectory_securityError,
test_root_getDirectory_typeMismatchError,
test_root_getDirectory_nonValidPath,
test_root_getDirectory_nonExistingPath,
test_root_getDirectory_simple,
test_root_getDirectory_deep,
+ test_webkitRelativePath,
+
cleanUpTestingFiles,
];
function next() {
if (!tests.length) {
SimpleTest.finish();
return;
}
--- a/dom/filesystem/tests/filesystem_commons.js
+++ b/dom/filesystem/tests/filesystem_commons.js
@@ -1,12 +1,18 @@
function createPath(parentDir, dirOrFile) {
return parentDir.path + (parentDir.path == '/' ? '' : '/') + dirOrFile.name;
}
+function createRelativePath(parentDir, dirOrFile) {
+ let path = createPath(parentDir, dirOrFile);
+ is(path[0], "/", "The full path should start with '/'");
+ return path.substring(1);
+}
+
function setup_tests(aNext) {
SimpleTest.requestLongerTimeout(2);
SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
["dom.webkitBlink.dirPicker.enabled", true]]}, aNext);
}
function test_basic(aDirectory, aNext) {
ok(aDirectory, "Directory exists.");
@@ -24,17 +30,17 @@ function test_getFilesAndDirectories(aDi
if (data[i] instanceof Directory) {
isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
isnot(data[i].path, '/', "Subdirectory path should be called with the leafname");
isnot(data[i].path, dir.path, "Subdirectory path should contain the parent path.");
is(data[i].path, createPath(dir, data[i]), "Subdirectory path should be called parentdir.path + '/' + leafname: " + data[i].path);
}
if (data[i] instanceof File) {
- is(data[i].webkitRelativePath, createPath(dir, data[i]), "File.webkitRelativePath should be called: parentdir.path + '/' + file.name: " + data[i].webkitRelativePath);
+ is(data[i].webkitRelativePath, createRelativePath(dir, data[i]), "File.webkitRelativePath should be called: parentdir.path + '/' + file.name: " + data[i].webkitRelativePath);
}
}
}
);
}
aDirectory.getFilesAndDirectories().then(
function(data) {
@@ -46,34 +52,35 @@ function test_getFilesAndDirectories(aDi
isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
is(data[i].path, createPath(aDirectory, data[i]), "Subdirectory path should be called parentdir.path + '/' + leafname: " + data[i].path);
if (aRecursive) {
promises.push(checkSubDir(data[i]));
}
}
if (data[i] instanceof File) {
- is(data[i].webkitRelativePath, createPath(aDirectory, data[i]), "File.webkitRelativePath should be called '/' + file.name: " + data[i].webkitRelativePath);
+ is(data[i].webkitRelativePath, createRelativePath(aDirectory, data[i]), "File.webkitRelativePath should be called file.name: " + data[i].webkitRelativePath);
}
}
return Promise.all(promises);
},
function() {
ok(false, "Something when wrong");
}
).then(aNext);
}
function test_getFiles(aDirectory, aRecursive, aNext) {
aDirectory.getFiles(aRecursive).then(
function(data) {
for (var i = 0; i < data.length; ++i) {
ok(data[i] instanceof File, "File: " + data[i].name);
- ok(data[i].webkitRelativePath.indexOf(aDirectory.path) == 0 &&
+ is(aDirectory.path[0], '/', "Directory path must start with '/'");
+ ok(data[i].webkitRelativePath.indexOf(aDirectory.path.substring(1)) == 0 &&
data[i].webkitRelativePath.indexOf('/' + data[i].name) + ('/' + data[i].name).length == data[i].webkitRelativePath.length,
"File.webkitRelativePath should be called dir.path + '/' + file.name: " + data[i].webkitRelativePath);
}
},
function() {
ok(false, "Something when wrong");
}
).then(aNext);
--- a/dom/html/HTMLFormSubmission.cpp
+++ b/dom/html/HTMLFormSubmission.cpp
@@ -506,21 +506,21 @@ FSMultipartFormData::AddNameBlobOrNullPa
nsAutoCString contentType;
nsCOMPtr<nsIInputStream> fileStream;
if (aBlob) {
nsAutoString filename16;
RefPtr<File> file = aBlob->ToFile();
if (file) {
- nsAutoString path;
- file->GetPath(path);
+ nsAutoString relativePath;
+ file->GetRelativePath(relativePath);
if (Directory::WebkitBlinkDirectoryPickerEnabled(nullptr, nullptr) &&
- !path.IsEmpty()) {
- filename16 = path;
+ !relativePath.IsEmpty()) {
+ filename16 = relativePath;
}
if (filename16.IsEmpty()) {
RetrieveFileName(aBlob, filename16);
}
}
rv = EncodeVal(filename16, filename, true);
--- a/dom/indexedDB/FileSnapshot.h
+++ b/dom/indexedDB/FileSnapshot.h
@@ -55,25 +55,25 @@ private:
// BlobImpl
virtual void
GetName(nsAString& aName) const override
{
mBlobImpl->GetName(aName);
}
virtual void
- GetPath(nsAString& aPath) const override
+ GetDOMPath(nsAString& aPath) const override
{
- mBlobImpl->GetPath(aPath);
+ mBlobImpl->GetDOMPath(aPath);
}
virtual void
- SetPath(const nsAString& aPath) override
+ SetDOMPath(const nsAString& aPath) override
{
- mBlobImpl->SetPath(aPath);
+ mBlobImpl->SetDOMPath(aPath);
}
virtual int64_t
GetLastModified(ErrorResult& aRv) override
{
return mBlobImpl->GetLastModified(aRv);
}
--- a/dom/security/test/contentverifier/browser.ini
+++ b/dom/security/test/contentverifier/browser.ini
@@ -11,11 +11,9 @@ support-files =
file_about_newtab_sri.html
file_about_newtab_sri_signature
goodChain.pem
head.js
script.js
style.css
[browser_verify_content_about_newtab.js]
-skip-if = true # This started permafailing randomly (Bug 1336654)
[browser_verify_content_about_newtab2.js]
-skip-if = true # This started permafailing randomly (Bug 1336654)
--- a/dom/security/test/contentverifier/file_about_newtab_good_signature
+++ b/dom/security/test/contentverifier/file_about_newtab_good_signature
@@ -1,1 +1,1 @@
--mqpvTYdZX4HYQDW1nScojL7ICw5yj8UF2gzxyLbSCx9UIfHH-gWZ40F_PFtqjHxoC1J3dHDb3VedVhOYczdaLrNKbRvPrlnkdGx7Rl8qEBrtZpF1py1Z9uAGoCrgUHa
\ No newline at end of file
+HUndgHvxHNMiAe1SXoeyOOraUJCdxHqWkAYTu0Cq1KpAHcWZEVelNTvyXGbTLWj8btsmqNLAm08UlyK43q_2oO9DQfez3Fo8DhsKvm7TqgSXCkhUoxsYNanxWXhqw-Jw
\ No newline at end of file
--- a/dom/security/test/contentverifier/goodChain.pem
+++ b/dom/security/test/contentverifier/goodChain.pem
@@ -1,51 +1,51 @@
-----BEGIN CERTIFICATE-----
-MIICUzCCAT2gAwIBAgIUNy0IWlDRDL53zwvj1lq0GCpIe2EwCwYJKoZIhvcNAQEL
-MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE0MTEyNzAwMDAwMFoYDzIwMTcwMjA0
+MIICUzCCAT2gAwIBAgIUJ1BtYqWRwUsVaZCGPp9eTHIC04QwCwYJKoZIhvcNAQEL
+MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE1MTEyODAwMDAwMFoYDzIwMTgwMjA1
MDAwMDAwWjAUMRIwEAYDVQQDDAllZS1pbnQtQ0EwdjAQBgcqhkjOPQIBBgUrgQQA
IgNiAAShaHJDNitcexiJ83kVRhWhxz+0je6GPgIpFdtgjiUt5LcTLajOmOgxU05q
nAwLCcjWOa3oMgbluoE0c6EfozDgXajJbkOD/ieHPalxA74oiM/wAvBa9xof3cyD
dKpuqc6jTjBMMBMGA1UdJQQMMAoGCCsGAQUFBwMDMDUGA1UdEQQuMCyCKnJlbW90
ZW5ld3RhYi5jb250ZW50LXNpZ25hdHVyZS5tb3ppbGxhLm9yZzALBgkqhkiG9w0B
-AQsDggEBAIeB4WKghknsrow+lj3qzDiHrPBc9AMlb4aZvS6yzazmXr80rXxnsKkb
-ZV1PW/cU6xXH5srWHpfJwypvvYS74btNtuacjKVH2AJdua4482WQIi9gCkXIufRx
-2nSS6pYgYZ4vD+yG8v+3SCChOCXnLjRaN9WxMi8tldbOW9pH44O3vrSSL70pQ2Ph
-8ncUbUbCNNtYhtOe2Z4XT9Cswmfkf4OIQ3gy9eYK2ySEUWP+lHs9KnnNXrLcA/ae
-cSUdI00i3C3OS9yldeyNHzVb8mSsZ5d1WkJrkf/hnXWGrMHRTtlJlG7t7cN8S0Oi
-tQoinJyxrZ+zabFIyl/euDc+Y/dijOU=
+AQsDggEBALiLck6k50ok9ahVq45P3feY1PeUXcIYZkJd8aPDYM+0kfg5+JyJBykA
+mtHWPE1QQjs7VRMfaLfu04E4UJMI2V1AON1qtgR9BQLctW85KFACg2omfiCKwJh0
+5Q8cxBFx9BpNMayqLJwHttB6oluxZFTB8CL/hfpbYpHz1bMEDCVSRP588YBrc8mV
+OLqzQK+k3ewwGvfD6SvXmTny37MxqwxdTPFJNnpqzKAsQIvz8Skic9BkA1NFk0Oq
+lsKKoiibbOCmwS9XY/laAkBaC3winuhciYAC0ImAopZ4PBCU0AOHGrNbhZXWYQxt
+uHBj34FqvIRCqgM06JCEwN0ULgix4kI=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIC0TCCAbugAwIBAgIULYyr3v/0zZ+XiR22NH7hOcnj2FcwCwYJKoZIhvcNAQEL
-MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
+MIIC0TCCAbugAwIBAgIUPcKbBQpKwTzrrlqzM+d3z5DWiNUwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTUxMTI4MDAwMDAwWhgPMjAxODAyMDUwMDAw
MDBaMBExDzANBgNVBAMMBmludC1DQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72x
nAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lM
wmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF
4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20
yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xx
j5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMlMCMwDAYDVR0TBAUwAwEB/zAT
-BgNVHSUEDDAKBggrBgEFBQcDAzALBgkqhkiG9w0BAQsDggEBADfRBKSM08JF6vqz
-0EA+KNc0XIEAWApuHuwX6XXWeLgo6QN4E/9qfrsaO+C366WT+JDsjDOi40wW46SA
-XbguxtZQeZasNDUWp/leZix4RSJoHB7OllG1rgZJfN76zKVaXRGUmyQObkMMOJZe
-wIA0OBURT8ik9Z89pD0IWrqscds71Edfjt0hHgg63wVvIaklReZXvFOD3VmSCPNn
-2wB6ZzECcbhJpnzxZdsoMSGH0C6apYnNNTjqZjO90JVm/Ph/7nbi/KncYXA6ccl6
-Jz2mfiAquWIua2+CzBGbqjZVSATTpWCp+cXQJE1xka+hWUaL5HPTq1bTULRFlauZ
-HGl5lJk=
+BgNVHSUEDDAKBggrBgEFBQcDAzALBgkqhkiG9w0BAQsDggEBADDPjITgz8joxLRW
+wpLxELKSgO/KQ6iAXztjMHq9ovT7Fy0fqBnQ1mMVFr+sBXLgtUCM45aip6PjhUXc
+zs5Dq5STg+kz7qtmAjEQvOPcyictbgdu/K7+uMhXQhlzhOgyW88Uk5vrAezNTc/e
+TvSmWp1FcgVAfaeMN/90nzD1KIHoUt7zqZIz9ub8jXPVzQNZq4vh33smZhmbdTdV
+DaHUyef5cR1VTEGB+L1qzUIQqpHmD4UkMNP1nYedWfauiQhRt6Ql3rJSCRuEvsOA
+iBTJlwai/EFwfyfHkOV2GNgv+A5wHHEjBtF5c4PCxQEL5Vw+mfZHLsDVqF3279ZY
+lQ6jQ9g=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIICzTCCAbegAwIBAgIUIVkGGA8HiO3RIKGjdOjVi+d6EVkwCwYJKoZIhvcNAQEL
-MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
+MIICzTCCAbegAwIBAgIUKRLJoCmk0A6PHrNc8CxFn//4BYcwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTUxMTI4MDAwMDAwWhgPMjAxODAyMDUwMDAw
MDBaMA0xCzAJBgNVBAMMAmNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu
Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO
7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf
qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt
HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx
uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyUwIzAMBgNVHRMEBTADAQH/MBMGA1Ud
-JQQMMAoGCCsGAQUFBwMDMAsGCSqGSIb3DQEBCwOCAQEAlpbRzRIPnf43AwGfMvKP
-zOtntRy2nE9GlmY9I00uioHUnUrPLs8aw3UDtyiDWMGqcYysXGx9EX2Vk0POS4gf
-G6PA95F6GxTtbzIEZmTPVuzA/cfc9HU3HXDPqh+dySJ8/Ta4c4vX1lgeGGAvstNe
-q+9DaCGXs8MqMF8KtXNmOm3eS9q622hKEvTVEoxqj1t365kwKHaNpbObddQ6Xcny
-akvfh2L+8QbJSflcm8fL/JTup/2/cRG1ytOsaiXEr9JBEITOtQO0Ot/4Qzq+MJjv
-weaJ3hZ0c+cTy3tEvt+I7+lnW4Q5dB7aLR2/BZfLubhxz1SUVMuHfLH64fc0Uf1Q
-Nw==
+JQQMMAoGCCsGAQUFBwMDMAsGCSqGSIb3DQEBCwOCAQEAABgMK6EyVIXTjD5qaxPO
+DWz6yREACmAQBcowKWvfhwgi27DPSXyFGDbzTPEo+7RrIcXJkVAhLouGT51fCwTZ
+zb6Sgf6ztX7VSppY9AT4utvlZKP1xQ5WhIYsMtdHCHLHIkRjeWyoBEfUx50UXNLK
+Snl+A02GKYWiX+TLLg2DPN2s7v/mm8NLMQNgUlL7KakB2FHFyPa8otPpL4llg7UJ
+iBTVQ0c3JoiVbwZaY1Z8QinfMXUrTK9egUC4BAcId1dE8glzA5RRlw1fTLWpGApt
+hUmbDnl9N2a9NhGX323ypNzIATexafipzWe7bc4u/+bFdrUqnKUoEka73pZBdHdA
+FQ==
-----END CERTIFICATE-----
--- a/dom/security/test/contentverifier/head.js
+++ b/dom/security/test/contentverifier/head.js
@@ -78,17 +78,17 @@ function* doTest(aExpectedStrings, reloa
aboutNewTabService.newTabURL = aNewTabPref;
}
// set prefs
yield pushPrefs(
["browser.newtabpage.remote.content-signing-test", true],
["browser.newtabpage.remote", true],
["security.content.signature.root_hash",
- "65:AE:D8:1E:B5:12:AE:B0:6B:38:58:BC:7C:47:35:3D:D4:EA:25:F1:63:DA:08:BB:86:3A:2E:97:39:66:8F:55"]);
+ "CC:BE:04:87:74:B2:98:24:4A:C6:7A:71:BC:6F:DB:D6:C0:48:17:29:57:51:96:47:38:CC:24:C8:E4:F9:DD:CB"]);
if (aNewTabPref === URI_BAD_CSP) {
// Use stricter CSP to test CSP violation.
yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self'; style-src 'self'"]);
} else {
// Use weaker CSP to test normal content.
yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self' 'unsafe-inline'; style-src 'self'"]);
}
--- a/dom/webidl/File.webidl
+++ b/dom/webidl/File.webidl
@@ -28,17 +28,17 @@ dictionary ChromeFilePropertyBag : FileP
DOMString name = "";
};
// Mozilla extensions
partial interface File {
[GetterThrows, Deprecated="FileLastModifiedDate"]
readonly attribute Date lastModifiedDate;
- [BinaryName="path", Func="mozilla::dom::Directory::WebkitBlinkDirectoryPickerEnabled"]
+ [BinaryName="relativePath", Func="mozilla::dom::Directory::WebkitBlinkDirectoryPickerEnabled"]
readonly attribute USVString webkitRelativePath;
[GetterThrows, ChromeOnly, NeedsCallerType]
readonly attribute DOMString mozFullPath;
[ChromeOnly, Throws, NeedsCallerType]
static File createFromNsIFile(nsIFile file,
optional ChromeFilePropertyBag options);
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -44,17 +44,17 @@ ServiceWorkerContainer::IsEnabled(JSCont
JS::Rooted<JSObject*> global(aCx, aGlobal);
nsCOMPtr<nsPIDOMWindowInner> window = Navigator::GetWindowFromGlobal(global);
if (!window) {
return false;
}
nsIDocument* doc = window->GetExtantDoc();
- if (!doc || doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId) {
+ if (!doc || nsContentUtils::IsInPrivateBrowsing(doc)) {
return false;
}
return Preferences::GetBool("dom.serviceWorkers.enabled", false);
}
ServiceWorkerContainer::ServiceWorkerContainer(nsPIDOMWindowInner* aWindow)
: DOMEventTargetHelper(aWindow)
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1649,20 +1649,21 @@ ServiceWorkerManager::FlushReportsToAllC
continue;
}
nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
if (!doc || !doc->IsCurrentActiveDocument() || !doc->GetWindow()) {
continue;
}
- windows.AppendElement(doc->InnerWindowID());
-
- aReporter->FlushConsoleReports(doc,
- nsIConsoleReportCollector::ReportAction::Save);
+ uint64_t innerWindowId = doc->InnerWindowID();
+ windows.AppendElement(innerWindowId);
+
+ aReporter->FlushReportsToConsole(
+ innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
}
// Report to any documents that have called .register() for this scope. They
// may not be controlled, but will still want to see error reports.
WeakDocumentList* regList = mRegisteringDocuments.Get(aScope);
if (regList) {
for (int32_t i = regList->Length() - 1; i >= 0; --i) {
nsCOMPtr<nsIDocument> doc = do_QueryReferent(regList->ElementAt(i));
@@ -1677,18 +1678,18 @@ ServiceWorkerManager::FlushReportsToAllC
uint64_t innerWindowId = doc->InnerWindowID();
if (windows.Contains(innerWindowId)) {
continue;
}
windows.AppendElement(innerWindowId);
- aReporter->FlushConsoleReports(doc,
- nsIConsoleReportCollector::ReportAction::Save);
+ aReporter->FlushReportsToConsole(
+ innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
}
if (regList->IsEmpty()) {
regList = nullptr;
nsAutoPtr<WeakDocumentList> doomed;
mRegisteringDocuments.RemoveAndForget(aScope, doomed);
}
}
@@ -1707,25 +1708,25 @@ ServiceWorkerManager::FlushReportsToAllC
uint64_t innerWindowId = nsContentUtils::GetInnerWindowID(inner);
if (innerWindowId == 0 || windows.Contains(innerWindowId)) {
continue;
}
windows.AppendElement(innerWindowId);
- aReporter->FlushReportsByWindowId(innerWindowId,
- nsIConsoleReportCollector::ReportAction::Save);
+ aReporter->FlushReportsToConsole(
+ innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
}
}
// If there are no documents to report to, at least report something to the
// browser console.
if (windows.IsEmpty()) {
- aReporter->FlushConsoleReports((nsIDocument*)nullptr);
+ aReporter->FlushReportsToConsole(0);
return;
}
aReporter->ClearConsoleReports();
}
void
ServiceWorkerManager::HandleError(JSContext* aCx,
@@ -2555,17 +2556,17 @@ ServiceWorkerManager::IsAvailable(nsIPri
return registration && registration->GetActive();
}
bool
ServiceWorkerManager::IsControlled(nsIDocument* aDoc, ErrorResult& aRv)
{
MOZ_ASSERT(aDoc);
- if (aDoc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId) {
+ if (nsContentUtils::IsInPrivateBrowsing(aDoc)) {
// Handle the case where a service worker was previously registered in
// a non-private window (bug 1255621).
return false;
}
RefPtr<ServiceWorkerRegistrationInfo> registration;
nsresult rv = GetDocumentRegistration(aDoc, getter_AddRefs(registration));
if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE)) {
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -3730,25 +3730,26 @@ WorkerPrivateParent<Derived>::FlushRepor
}
bool reportErrorToBrowserConsole = true;
// Flush the reports.
for (uint32_t index = 0; index < windowActions.Length(); index++) {
WindowAction& windowAction = windowActions[index];
- aReporter->FlushConsoleReports(windowAction.mWindow->GetExtantDoc(),
- nsIConsoleReportCollector::ReportAction::Save);
+ aReporter->FlushReportsToConsole(
+ windowAction.mWindow->WindowID(),
+ nsIConsoleReportCollector::ReportAction::Save);
reportErrorToBrowserConsole = false;
}
// Finally report to broswer console if there is no any window or shared
// worker.
if (reportErrorToBrowserConsole) {
- aReporter->FlushConsoleReports((nsIDocument*)nullptr);
+ aReporter->FlushReportsToConsole(0);
return;
}
aReporter->ClearConsoleReports();
}
template <class Derived>
NS_IMPL_ADDREF_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper)
new file mode 100644
--- /dev/null
+++ b/embedding/moz.build
@@ -0,0 +1,3 @@
+with Files("**"):
+ BUG_COMPONENT = ("Core", "General")
+
--- a/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp
+++ b/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp
@@ -677,20 +677,23 @@ static void bilevel_to_bw(const uint8_t*
template<bool APPLY_PREBLEND>
static void rgb_to_a8(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, const uint8_t* table8) {
const size_t dstRB = glyph.rowBytes();
const U16CPU width = glyph.fWidth;
uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage);
for (U16CPU y = 0; y < glyph.fHeight; y++) {
for (U16CPU i = 0; i < width; i++) {
- U8CPU r = *(src++);
- U8CPU g = *(src++);
- U8CPU b = *(src++);
- dst[i] = sk_apply_lut_if<APPLY_PREBLEND>((r + g + b) / 3, table8);
+ U8CPU g = src[1];
+ src += 3;
+
+ // Ignore the R, B channels. It looks the closest to what
+ // D2D with grayscale AA has. But there's no way
+ // to just get a grayscale AA alpha texture from a glyph run.
+ dst[i] = sk_apply_lut_if<APPLY_PREBLEND>(g, table8);
}
dst = (uint8_t*)((char*)dst + dstRB);
}
}
template<bool APPLY_PREBLEND, bool RGB>
static void rgb_to_lcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -2068,25 +2068,25 @@ gfxFont::Draw(const gfxTextRun *aTextRun
if (aTextRun->UseCenterBaseline()) {
gfxPoint baseAdj(0, (metrics.emAscent - metrics.emDescent) / 2);
mat.Translate(baseAdj);
}
aRunParams.context->SetMatrix(mat);
}
- UniquePtr<SVGContextPaint> contextPaint;
+ RefPtr<SVGContextPaint> contextPaint;
if (fontParams.haveSVGGlyphs && !fontParams.contextPaint) {
// If no pattern is specified for fill, use the current pattern
NS_ASSERTION((int(aRunParams.drawMode) & int(DrawMode::GLYPH_STROKE)) == 0,
"no pattern supplied for stroking text");
RefPtr<gfxPattern> fillPattern = aRunParams.context->GetPattern();
- contextPaint.reset(
+ contextPaint =
new SimpleTextContextPaint(fillPattern, nullptr,
- aRunParams.context->CurrentMatrix()));
+ aRunParams.context->CurrentMatrix());
fontParams.contextPaint = contextPaint.get();
}
// Synthetic-bold strikes are each offset one device pixel in run direction.
// (these values are only needed if IsSyntheticBold() is true)
if (IsSyntheticBold()) {
double xscale = CalcXScale(aRunParams.context->GetDrawTarget());
fontParams.synBoldOnePixelOffset = aRunParams.direction * xscale;
--- a/image/ClippedImage.cpp
+++ b/image/ClippedImage.cpp
@@ -467,18 +467,19 @@ ClippedImage::DrawSingleTile(gfxContext*
// The size in pixels at which the output will ultimately be drawn is
// irrelevant here since the purpose of the SVG viewport size is to
// determine what *region* of the SVG document will be drawn.
CSSIntSize vSize(aOldContext.GetViewportSize());
vSize.width = ceil(vSize.width * double(innerSize.width) / mClip.width);
vSize.height =
ceil(vSize.height * double(innerSize.height) / mClip.height);
- return SVGImageContext(vSize,
- aOldContext.GetPreserveAspectRatio());
+ SVGImageContext context(aOldContext);
+ context.SetViewportSize(vSize);
+ return context;
};
return InnerImage()->Draw(aContext, size, region,
aWhichFrame, aSamplingFilter,
aSVGContext.map(unclipViewport),
aFlags, aOpacity);
}
--- a/image/OrientedImage.cpp
+++ b/image/OrientedImage.cpp
@@ -292,18 +292,19 @@ OrientedImage::Draw(gfxContext* aContext
ImageRegion region(aRegion);
region.TransformBoundsBy(inverseMatrix);
auto orientViewport = [&](const SVGImageContext& aOldContext) {
CSSIntSize viewportSize(aOldContext.GetViewportSize());
if (mOrientation.SwapsWidthAndHeight()) {
swap(viewportSize.width, viewportSize.height);
}
- return SVGImageContext(viewportSize,
- aOldContext.GetPreserveAspectRatio());
+ SVGImageContext context(aOldContext);
+ context.SetViewportSize(viewportSize);
+ return context;
};
return InnerImage()->Draw(aContext, size, region, aWhichFrame,
aSamplingFilter,
aSVGContext.map(orientViewport), aFlags,
aOpacity);
}
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -850,19 +850,18 @@ VectorImage::Draw(gfxContext* aContext,
// overwrite SVG preserveAspectRatio attibute of this image with none, and
// always stretch this image to viewport non-uniformly.
// And we can do this only if the caller pass in the the SVG viewport, via
// aSVGContext.
if ((aFlags & FLAG_FORCE_PRESERVEASPECTRATIO_NONE) && aSVGContext.isSome()) {
Maybe<SVGPreserveAspectRatio> aspectRatio =
Some(SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE,
SVG_MEETORSLICE_UNKNOWN));
- svgContext =
- Some(SVGImageContext(aSVGContext->GetViewportSize(),
- aspectRatio));
+ svgContext = Some(SVGImageContext(*aSVGContext)); // copy
+ svgContext->SetPreserveAspectRatio(aspectRatio);
} else {
svgContext = aSVGContext;
}
float animTime =
(aWhichFrame == FRAME_FIRST) ? 0.0f
: mSVGDocumentWrapper->GetCurrentTime();
AutoSVGRenderingState autoSVGState(svgContext, animTime,
@@ -875,16 +874,23 @@ VectorImage::Draw(gfxContext* aContext,
// If we have an prerasterized version of this image that matches the
// drawing parameters, use that.
RefPtr<gfxDrawable> svgDrawable = LookupCachedSurface(params);
if (svgDrawable) {
Show(svgDrawable, params);
return DrawResult::SUCCESS;
}
+ Maybe<AutoSetRestoreSVGContextPaint> autoContextPaint;
+ if (aSVGContext &&
+ aSVGContext->GetContextPaint()) {
+ autoContextPaint.emplace(aSVGContext->GetContextPaint(),
+ mSVGDocumentWrapper->GetDocument());
+ }
+
// We didn't get a hit in the surface cache, so we'll need to rerasterize.
CreateSurfaceAndShow(params, aContext->GetDrawTarget()->GetBackendType());
return DrawResult::SUCCESS;
}
already_AddRefed<gfxDrawable>
VectorImage::LookupCachedSurface(const SVGDrawingParameters& aParams)
{
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -416,20 +416,21 @@ function ArrayReduce(callbackfn/*, initi
return accumulator;
}
function ArrayStaticReduce(list, callbackfn) {
if (arguments.length < 2)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "Array.reduce");
if (!IsCallable(callbackfn))
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
+
if (arguments.length > 2)
return callFunction(ArrayReduce, list, callbackfn, arguments[2]);
- else
- return callFunction(ArrayReduce, list, callbackfn);
+
+ return callFunction(ArrayReduce, list, callbackfn);
}
/* ES5 15.4.4.22. */
function ArrayReduceRight(callbackfn/*, initialValue*/) {
/* Step 1. */
var O = ToObject(this);
/* Steps 2-3. */
@@ -483,20 +484,21 @@ function ArrayReduceRight(callbackfn/*,
return accumulator;
}
function ArrayStaticReduceRight(list, callbackfn) {
if (arguments.length < 2)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "Array.reduceRight");
if (!IsCallable(callbackfn))
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
+
if (arguments.length > 2)
return callFunction(ArrayReduceRight, list, callbackfn, arguments[2]);
- else
- return callFunction(ArrayReduceRight, list, callbackfn);
+
+ return callFunction(ArrayReduceRight, list, callbackfn);
}
/* ES6 draft 2013-05-14 15.4.3.23. */
function ArrayFind(predicate/*, thisArg*/) {
/* Steps 1-2. */
var O = ToObject(this);
/* Steps 3-5. */
@@ -821,17 +823,17 @@ function ArrayFrom(items, mapfn = undefi
// We could hit this when |A| is a proxy and it ignores
// |_DefineDataProperty|, but it happens only after too long loop.
/*
if (k >= 0x1fffffffffffff)
ThrowTypeError(JSMSG_TOO_LONG_ARRAY);
*/
// Steps 5.e.vi-vii.
- var mappedValue = mapping ? callContentFunction(mapfn, thisArg, nextValue, k) : nextValue;
+ var mappedValue = mapping ? callContentFunction(mapfn, T, nextValue, k) : nextValue;
// Steps 5.e.ii (reordered), 5.e.viii.
_DefineDataProperty(A, k++, mappedValue);
}
// Step 5.e.iv.
A.length = k;
return A;
@@ -850,17 +852,17 @@ function ArrayFrom(items, mapfn = undefi
var A = IsConstructor(C) ? new C(len) : std_Array(len);
// Steps 15-16.
for (var k = 0; k < len; k++) {
// Steps 16.a-c.
var kValue = items[k];
// Steps 16.d-e.
- var mappedValue = mapping ? callContentFunction(mapfn, thisArg, kValue, k) : kValue;
+ var mappedValue = mapping ? callContentFunction(mapfn, T, kValue, k) : kValue;
// Steps 16.f-g.
_DefineDataProperty(A, k, mappedValue);
}
// Steps 17-18.
A.length = len;
--- a/js/src/builtin/Function.js
+++ b/js/src/builtin/Function.js
@@ -28,17 +28,17 @@ function FunctionBind(thisArg, ...boundA
F = bind_bindFunctionN(target, thisArg, boundArgs);
}
// Steps 5-11.
_FinishBoundFunctionInit(F, target, argCount);
// Ensure that the apply intrinsic has been cloned so it can be baked into
// JIT code.
- var funApply = std_Function_apply;
+ void std_Function_apply;
// Step 12.
return F;
}
/**
* bind_bindFunction{0,1,2} are special cases of the generic bind_bindFunctionN
* below. They avoid the need to merge the lists of bound arguments and call
* arguments to the bound function into a new list which is then used in a
@@ -189,18 +189,18 @@ function bind_bindFunctionN(fun, thisArg
if (_IsConstructing()) {
newTarget = new.target;
if (newTarget === bound)
newTarget = fun;
}
if (arguments.length === 0) {
if (newTarget !== undefined)
return bind_constructFunctionN(fun, newTarget, boundArgs);
- else
- return bind_applyFunctionN(fun, thisArg, boundArgs);
+
+ return bind_applyFunctionN(fun, thisArg, boundArgs);
}
var callArgs = FUN_APPLY(bind_mapArguments, null, arguments);
return bind_invokeFunctionN(fun, thisArg, newTarget, boundArgs, callArgs);
};
}
function bind_mapArguments() {
var len = arguments.length;
--- a/js/src/builtin/Generator.js
+++ b/js/src/builtin/Generator.js
@@ -123,17 +123,17 @@ function LegacyGeneratorCloseInternal()
CloseClosingLegacyGeneratorObject(this);
}
function LegacyGeneratorClose() {
if (!IsObject(this) || !IsLegacyGeneratorObject(this))
return callFunction(CallLegacyGeneratorMethodIfWrapped, this, "LegacyGeneratorClose");
if (LegacyGeneratorObjectIsClosed(this))
- return;
+ return undefined;
callFunction(LegacyGeneratorCloseInternal, this);
}
function InterpretGeneratorResume(gen, val, kind) {
// If we want to resume a generator in the interpreter, the script containing
// the resumeGenerator/JSOP_RESUME also has to run in the interpreter. The
// forceInterpreter() call below compiles to a bytecode op that prevents us
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -1738,18 +1738,18 @@ function resolveNumberFormatInternals(la
var NumberFormat = numberFormatInternalProperties;
// Step 9.
var localeData = NumberFormat.localeData;
// Step 10.
var r = ResolveLocale(callFunction(NumberFormat.availableLocales, NumberFormat),
- lazyNumberFormatData.requestedLocales,
- lazyNumberFormatData.opt,
+ requestedLocales,
+ opt,
NumberFormat.relevantExtensionKeys,
localeData);
// Steps 11-12. (Step 13 is not relevant to our implementation.)
internalProps.locale = r.locale;
internalProps.numberingSystem = r.nu;
// Compute formatting options.
@@ -2399,16 +2399,17 @@ function InitializeDateTimeFormat(dateTi
//
// For some reason (ICU not exposing enough interface?) we drop the
// requested format matcher on the floor after this. In any case, even if
// doing so is justified, we have to do this work here in case it triggers
// getters or similar. (bug 852837)
var formatMatcher =
GetOption(options, "formatMatcher", "string", ["basic", "best fit"],
"best fit");
+ void formatMatcher;
// Steps 23-25 provided by ICU, more or less - see comment after this function.
// Step 26.
var hr12 = GetOption(options, "hour12", "boolean", undefined, undefined);
// Pass hr12 on to ICU.
if (hr12 !== undefined)
@@ -2926,18 +2927,16 @@ var pluralRulesInternalProperties = {
/**
* Compute an internal properties object from |lazyPluralRulesData|.
*/
function resolvePluralRulesInternals(lazyPluralRulesData) {
assert(IsObject(lazyPluralRulesData), "lazy data not an object?");
var internalProps = std_Object_create(null);
- var requestedLocales = lazyPluralRulesData.requestedLocales;
-
var PluralRules = pluralRulesInternalProperties;
// Step 13.
const r = ResolveLocale(callFunction(PluralRules.availableLocales, PluralRules),
lazyPluralRulesData.requestedLocales,
lazyPluralRulesData.opt,
noRelevantExtensionKeys, undefined);
--- a/js/src/builtin/Module.js
+++ b/js/src/builtin/Module.js
@@ -226,17 +226,17 @@ function ModuleDeclarationInstantiation(
if (!IsObject(this) || !IsModule(this))
return callFunction(CallModuleMethodIfWrapped, this, "ModuleDeclarationInstantiation");
// Step 1
let module = this;
// Step 5
if (GetModuleEnvironment(module) !== undefined)
- return;
+ return undefined;
// Step 7
CreateModuleEnvironment(module);
let env = GetModuleEnvironment(module);
SetModuleState(this, MODULE_STATE_INSTANTIATED);
try {
--- a/js/src/builtin/Sorting.js
+++ b/js/src/builtin/Sorting.js
@@ -263,17 +263,17 @@ function MergeSort(array, len, comparefn
MoveHoles(array, len, denseList, denseLen);
return array;
}
// We do all of our allocating up front
var lBuffer = new List();
var rBuffer = new List();
- var mid, end, endOne, endTwo;
+ var mid, end;
for (var windowSize = 1; windowSize < denseLen; windowSize = 2 * windowSize) {
for (var start = 0; start < denseLen - 1; start += 2 * windowSize) {
assert(windowSize < denseLen, "The window size is larger than the array denseLength!");
// The midpoint between the two subarrays.
mid = start + windowSize - 1;
// To keep from going over the edge.
end = start + 2 * windowSize - 1;
end = end < denseLen - 1 ? end : denseLen - 1;
@@ -332,17 +332,17 @@ function QuickSort(array, len, comparefn
// Managing the stack ourselves seems to provide a small performance boost.
var stack = new List();
var top = 0;
var start = 0;
var end = len - 1;
- var pivotIndex, i, j, leftLen, rightLen;
+ var pivotIndex, leftLen, rightLen;
for (;;) {
// Insertion sort for the first N elements where N is some value
// determined by performance testing.
if (end - start <= 23) {
InsertionSort(array, start, end, comparefn);
if (top < 1)
break;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/setelem-id-guard.js
@@ -0,0 +1,91 @@
+function setSlot() {
+ var o = {x: 1, y: 2};
+ for (var i=0; i<100; i++) {
+ var p = (i & 1) ? "x" : "y";
+ o[p] = i;
+ assertEq((i & 1) ? o.x : o.y, i);
+ }
+}
+setSlot();
+
+function setUnboxed() {
+ var arr = [];
+ for (var i=0; i<100; i++)
+ arr.push({x: 1, y: 2});
+
+ for (var i=0; i<100; i++) {
+ var o = arr[i];
+ var p = (i & 1) ? "x" : "y";
+ o[p] = i;
+ assertEq((i & 1) ? o.x : o.y, i);
+ }
+}
+setUnboxed();
+
+function setUnboxedExpando() {
+ var arr = [];
+ for (var i=0; i<100; i++)
+ arr.push({x: 1, y: 2});
+
+ for (var i=0; i<100; i++) {
+ var o = arr[i];
+ o.a = 1;
+ o.b = 2;
+ var p = (i & 1) ? "a" : "b";
+ o[p] = i;
+ o[p] = o[p] + 4;
+ assertEq((i & 1) ? o.a : o.b, i + 4);
+ }
+}
+setUnboxedExpando();
+
+function setArrayLength() {
+ var arr = [];
+ for (var i=0; i<100; i++) {
+ var p = (i & 1) ? "length" : "x";
+ arr[p] = i;
+ assertEq((i & 1) ? arr.length : arr.x, i);
+ }
+}
+setArrayLength();
+
+function setter() {
+ var c = 0;
+ var o = {set x(i) { c += i; }, set y(i) { c += i + 2; }};
+ for (var i=0; i<100; i++) {
+ var p = (i & 1) ? "x" : "y";
+ o[p] = i;
+ }
+ assertEq(c, 5050);
+}
+setter();
+
+function addSlot() {
+ for (var i=0; i<100; i++) {
+ var o = {};
+ var p1 = (i & 1) ? "x" : "y";
+ var p2 = (i % 3) ? "a" : "b";
+ o[p1] = i;
+ o[p2] = i;
+ assertEq((i & 1) ? o.x : o.y, i);
+ assertEq((i % 3) ? o.a : o.b, i);
+ }
+}
+addSlot();
+
+function addExpandoSlot() {
+ var arr = [];
+ for (var i=0; i<100; i++)
+ arr.push({c: 1, d: 2});
+
+ for (var i=0; i<100; i++) {
+ var o = arr[i];
+ var p1 = (i & 1) ? "x" : "y";
+ var p2 = (i % 3) ? "a" : "b";
+ o[p1] = i;
+ o[p2] = i;
+ assertEq((i & 1) ? o.x : o.y, i);
+ assertEq((i % 3) ? o.a : o.b, i);
+ }
+}
+addExpandoSlot();
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -17,16 +17,23 @@
using namespace js;
using namespace js::jit;
using mozilla::Maybe;
class AutoStubFrame;
+Address
+CacheRegisterAllocator::addressOf(MacroAssembler& masm, BaselineFrameSlot slot) const
+{
+ uint32_t offset = stackPushed_ + ICStackValueOffset + slot.slot() * sizeof(JS::Value);
+ return Address(masm.getStackPointer(), offset);
+}
+
// BaselineCacheIRCompiler compiles CacheIR to BaselineIC native code.
class MOZ_RAII BaselineCacheIRCompiler : public CacheIRCompiler
{
// Some Baseline IC stubs can be used in IonMonkey through SharedStubs.
// Those stubs have different machine code, so we need to track whether
// we're compiling for Baseline or Ion.
ICStubEngine engine_;
@@ -1254,29 +1261,39 @@ BaselineCacheIRCompiler::init(CacheKind
{
if (!allocator.init())
return false;
// Baseline ICs monitor values when needed, so returning doubles is fine.
allowDoubleResult_.emplace(true);
size_t numInputs = writer_.numInputOperands();
- AllocatableGeneralRegisterSet available(ICStubCompiler::availableGeneralRegs(numInputs));
+
+ // Baseline passes the first 2 inputs in R0/R1, other Values are stored on
+ // the stack.
+ size_t numInputsInRegs = std::min(numInputs, size_t(2));
+ AllocatableGeneralRegisterSet available(ICStubCompiler::availableGeneralRegs(numInputsInRegs));
switch (kind) {
case CacheKind::GetProp:
MOZ_ASSERT(numInputs == 1);
allocator.initInputLocation(0, R0);
break;
case CacheKind::GetElem:
case CacheKind::SetProp:
MOZ_ASSERT(numInputs == 2);
allocator.initInputLocation(0, R0);
allocator.initInputLocation(1, R1);
break;
+ case CacheKind::SetElem:
+ MOZ_ASSERT(numInputs == 3);
+ allocator.initInputLocation(0, R0);
+ allocator.initInputLocation(1, R1);
+ allocator.initInputLocation(2, BaselineFrameSlot(0));
+ break;
case CacheKind::GetName:
MOZ_ASSERT(numInputs == 1);
allocator.initInputLocation(0, R0.scratchReg(), JSVAL_TYPE_OBJECT);
#if defined(JS_NUNBOX32)
// availableGeneralRegs can't know that GetName is only using
// the payloadReg and not typeReg on x86.
available.add(R0.typeReg());
#endif
@@ -1313,16 +1330,17 @@ jit::AttachBaselineCacheIRStub(JSContext
switch (kind) {
case CacheKind::GetProp:
case CacheKind::GetElem:
case CacheKind::GetName:
stubDataOffset = sizeof(ICCacheIR_Monitored);
stubKind = CacheIRStubKind::Monitored;
break;
case CacheKind::SetProp:
+ case CacheKind::SetElem:
stubDataOffset = sizeof(ICCacheIR_Updated);
stubKind = CacheIRStubKind::Updated;
break;
}
JitCompartment* jitCompartment = cx->compartment()->jitCompartment();
// Check if we already have JitCode for this stub.
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1125,17 +1125,54 @@ DoSetElemFallback(JSContext* cx, Baselin
op == JSOP_INITHIDDENELEM ||
op == JSOP_INITELEM_ARRAY ||
op == JSOP_INITELEM_INC);
RootedObject obj(cx, ToObjectFromStack(cx, objv));
if (!obj)
return false;
+ bool isTemporarilyUnoptimizable = false;
+
+ bool attached = false;
+ if (stub->numOptimizedStubs() < ICSetElem_Fallback::MAX_OPTIMIZED_STUBS &&
+ !JitOptions.disableCacheIR)
+ {
+ SetPropIRGenerator gen(cx, pc, CacheKind::SetElem, &isTemporarilyUnoptimizable,
+ objv, index, rhs);
+ if (gen.tryAttachStub()) {
+ ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+ ICStubEngine::Baseline, frame->script(), stub);
+ if (newStub) {
+ JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
+ attached = true;
+
+ if (gen.needUpdateStub()) {
+ newStub->toCacheIR_Updated()->updateStubGroup() = gen.updateStubGroup();
+ newStub->toCacheIR_Updated()->updateStubId() = gen.updateStubId();
+ }
+
+ if (gen.shouldNotePreliminaryObjectStub())
+ newStub->toCacheIR_Updated()->notePreliminaryObject();
+ else if (gen.shouldUnlinkPreliminaryObjectStubs())
+ StripPreliminaryObjectStubs(cx, stub);
+ }
+ }
+ }
+
RootedShape oldShape(cx, obj->maybeShape());
+ RootedObjectGroup oldGroup(cx, JSObject::getGroup(cx, obj));
+ if (!oldGroup)
+ return false;
+
+ if (obj->is<UnboxedPlainObject>()) {
+ MOZ_ASSERT(!oldShape);
+ if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
+ oldShape = expando->lastProperty();
+ }
// Check the old capacity
uint32_t oldCapacity = 0;
uint32_t oldInitLength = 0;
if (index.isInt32() && index.toInt32() >= 0) {
oldCapacity = GetAnyBoxedOrUnboxedCapacity(obj);
oldInitLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
}
@@ -1167,22 +1204,41 @@ DoSetElemFallback(JSContext* cx, Baselin
// Overwrite the object on the stack (pushed for the decompiler) with the rhs.
MOZ_ASSERT(stack[2] == objv);
stack[2] = rhs;
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
+ if (attached)
+ return true;
+
if (stub->numOptimizedStubs() >= ICSetElem_Fallback::MAX_OPTIMIZED_STUBS) {
// TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
// But for now we just bail.
return true;
}
+ if (!JitOptions.disableCacheIR) {
+ SetPropIRGenerator gen(cx, pc, CacheKind::SetElem, &isTemporarilyUnoptimizable,
+ objv, index, rhs);
+ if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
+ ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+ ICStubEngine::Baseline, frame->script(), stub);
+ if (newStub) {
+ JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
+ attached = true;
+ newStub->toCacheIR_Updated()->updateStubGroup() = gen.updateStubGroup();
+ newStub->toCacheIR_Updated()->updateStubId() = gen.updateStubId();
+ return true;
+ }
+ }
+ }
+
// Try to generate new stubs.
if (IsNativeOrUnboxedDenseElementAccess(obj, index) && !rhs.isMagic(JS_ELEMENTS_HOLE)) {
bool addingCase;
size_t protoDepth;
if (CanOptimizeDenseOrUnboxedArraySetElem(obj, index.toInt32(),
oldShape, oldCapacity, oldInitLength,
&addingCase, &protoDepth))
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -1298,35 +1298,52 @@ GetPropIRGenerator::tryAttachProxyElemen
// so we don't attach a new stub for every id.
MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
writer.callProxyGetByValueResult(objId, getElemKeyValueId());
writer.typeMonitorResult();
return true;
}
void
+IRGenerator::emitIdGuard(ValOperandId valId, jsid id)
+{
+ if (JSID_IS_SYMBOL(id)) {
+ SymbolOperandId symId = writer.guardIsSymbol(valId);
+ writer.guardSpecificSymbol(symId, JSID_TO_SYMBOL(id));
+ } else {
+ MOZ_ASSERT(JSID_IS_ATOM(id));
+ StringOperandId strId = writer.guardIsString(valId);
+ writer.guardSpecificAtom(strId, JSID_TO_ATOM(id));
+ }
+}
+
+void
GetPropIRGenerator::maybeEmitIdGuard(jsid id)
{
if (cacheKind_ == CacheKind::GetProp) {
// Constant PropertyName, no guards necessary.
MOZ_ASSERT(&idVal_.toString()->asAtom() == JSID_TO_ATOM(id));
return;
}
MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
+ emitIdGuard(getElemKeyValueId(), id);
+}
- ValOperandId valId = getElemKeyValueId();
- if (JSID_IS_SYMBOL(id)) {
- SymbolOperandId symId = writer.guardIsSymbol(valId);
- writer.guardSpecificSymbol(symId, JSID_TO_SYMBOL(id));
- } else {
- MOZ_ASSERT(JSID_IS_ATOM(id));
- StringOperandId strId = writer.guardIsString(valId);
- writer.guardSpecificAtom(strId, JSID_TO_ATOM(id));
+void
+SetPropIRGenerator::maybeEmitIdGuard(jsid id)
+{
+ if (cacheKind_ == CacheKind::SetProp) {
+ // Constant PropertyName, no guards necessary.
+ MOZ_ASSERT(&idVal_.toString()->asAtom() == JSID_TO_ATOM(id));
+ return;
}
+
+ MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
+ emitIdGuard(setElemKeyValueId(), id);
}
GetNameIRGenerator::GetNameIRGenerator(JSContext* cx, jsbytecode* pc, HandleScript script,
HandleObject env, HandlePropertyName name)
: IRGenerator(cx, pc, CacheKind::GetName),
script_(script),
env_(env),
name_(name)
@@ -1598,32 +1615,40 @@ SetPropIRGenerator::SetPropIRGenerator(J
needUpdateStub_(false)
{}
bool
SetPropIRGenerator::tryAttachStub()
{
AutoAssertNoPendingException aanpe(cx_);
- ValOperandId lhsValId(writer.setInputOperandId(0));
- ValOperandId rhsValId(writer.setInputOperandId(1));
+ ValOperandId objValId(writer.setInputOperandId(0));
+ ValOperandId rhsValId;
+ if (cacheKind_ == CacheKind::SetProp) {
+ rhsValId = ValOperandId(writer.setInputOperandId(1));
+ } else {
+ MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
+ MOZ_ASSERT(setElemKeyValueId().id() == 1);
+ writer.setInputOperandId(1);
+ rhsValId = ValOperandId(writer.setInputOperandId(2));
+ }
RootedId id(cx_);
bool nameOrSymbol;
if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
cx_->clearPendingException();
return false;
}
if (lhsVal_.isObject()) {
RootedObject obj(cx_, &lhsVal_.toObject());
if (obj->watched())
return false;
- ObjOperandId objId = writer.guardIsObject(lhsValId);
+ ObjOperandId objId = writer.guardIsObject(objValId);
if (nameOrSymbol) {
if (tryAttachNativeSetSlot(obj, objId, id, rhsValId))
return true;
if (tryAttachUnboxedExpandoSetSlot(obj, objId, id, rhsValId))
return true;
if (tryAttachUnboxedProperty(obj, objId, id, rhsValId))
return true;
if (tryAttachSetter(obj, objId, id, rhsValId))
@@ -1684,16 +1709,18 @@ SetPropIRGenerator::tryAttachNativeSetSl
// overwritten. Don't attach a stub in this case, so that we don't
// execute another write to the property without TI seeing that write.
EnsureTrackPropertyTypes(cx_, obj, id);
if (!PropertyHasBeenMarkedNonConstant(obj, id)) {
*isTemporarilyUnoptimizable_ = true;
return false;
}
+ maybeEmitIdGuard(id);
+
// For Baseline, we have to guard on both the shape and group, because the
// type update IC applies to a single group. When we port the Ion IC, we can
// do a bit better and avoid the group guard if we don't have to guard on
// the property types.
NativeObject* nobj = &obj->as<NativeObject>();
writer.guardGroup(objId, nobj->group());
writer.guardShape(objId, nobj->lastProperty());
@@ -1717,16 +1744,17 @@ SetPropIRGenerator::tryAttachUnboxedExpa
UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
if (!expando)
return false;
Shape* propShape = LookupShapeForSetSlot(expando, id);
if (!propShape)
return false;
+ maybeEmitIdGuard(id);
writer.guardGroup(objId, obj->group());
ObjOperandId expandoId = writer.guardAndLoadUnboxedExpando(objId);
writer.guardShape(expandoId, expando->lastProperty());
// Property types must be added to the unboxed object's group, not the
// expando's group (it has unknown properties).
setUpdateStubInfo(obj->group(), id);
EmitStoreSlotAndReturn(writer, expandoId, expando, propShape, rhsId);
@@ -1750,16 +1778,17 @@ SetPropIRGenerator::tryAttachUnboxedProp
{
if (!obj->is<UnboxedPlainObject>() || !cx_->runtime()->jitSupportsFloatingPoint)
return false;
const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
if (!property)
return false;
+ maybeEmitIdGuard(id);
writer.guardGroup(objId, obj->group());
EmitGuardUnboxedPropertyType(writer, property->type, rhsId);
writer.storeUnboxedProperty(objId, property->type,
UnboxedPlainObject::offsetOfData() + property->offset,
rhsId);
writer.returnFromIC();
setUpdateStubInfo(obj->group(), id);
@@ -1787,16 +1816,17 @@ SetPropIRGenerator::tryAttachTypedObject
TypeDescr* fieldDescr = &structDescr->fieldDescr(fieldIndex);
if (!fieldDescr->is<SimpleTypeDescr>())
return false;
uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
+ maybeEmitIdGuard(id);
writer.guardNoDetachedTypedObjects();
writer.guardShape(objId, obj->as<TypedObject>().shape());
writer.guardGroup(objId, obj->group());
setUpdateStubInfo(obj->group(), id);
// Scalar types can always be stored without a type update stub.
if (fieldDescr->is<ScalarTypeDescr>()) {
@@ -1844,31 +1874,38 @@ EmitCallSetterResultNoGuards(CacheIRWrit
writer.callScriptedSetter(objId, target, rhsId);
writer.returnFromIC();
}
bool
SetPropIRGenerator::tryAttachSetter(HandleObject obj, ObjOperandId objId, HandleId id,
ValOperandId rhsId)
{
+ // Don't attach a setter stub for ops like JSOP_INITELEM.
+ if (IsPropertyInitOp(JSOp(*pc_)))
+ return false;
+ MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
+
PropertyResult prop;
JSObject* holder;
if (!LookupPropertyPure(cx_, obj, id, &holder, &prop))
return false;
if (prop.isNonNativeProperty())
return false;
Shape* shape = prop.maybeShape();
if (!IsCacheableSetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable_) &&
!IsCacheableSetPropCallNative(obj, holder, shape))
{
return false;
}
+ maybeEmitIdGuard(id);
+
Maybe<ObjOperandId> expandoId;
TestMatchingReceiver(writer, obj, shape, objId, &expandoId);
if (obj != holder) {
GeneratePrototypeGuards(writer, obj, holder, objId);
// Guard on the holder's shape.
ObjOperandId holderId = writer.loadObject(holder);
@@ -1881,29 +1918,38 @@ SetPropIRGenerator::tryAttachSetter(Hand
bool
SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj, ObjOperandId objId, HandleId id,
ValOperandId rhsId)
{
if (!obj->is<ArrayObject>() || !JSID_IS_ATOM(id, cx_->names().length))
return false;
+ maybeEmitIdGuard(id);
writer.guardClass(objId, GuardClassKind::Array);
writer.callSetArrayLength(objId, IsStrictSetPC(pc_), rhsId);
writer.returnFromIC();
return true;
}
bool
SetPropIRGenerator::tryAttachAddSlotStub(HandleObjectGroup oldGroup, HandleShape oldShape)
{
AutoAssertNoPendingException aanpe(cx_);
- ValOperandId lhsValId(writer.setInputOperandId(0));
- ValOperandId rhsValId(writer.setInputOperandId(1));
+ ValOperandId objValId(writer.setInputOperandId(0));
+ ValOperandId rhsValId;
+ if (cacheKind_ == CacheKind::SetProp) {
+ rhsValId = ValOperandId(writer.setInputOperandId(1));
+ } else {
+ MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
+ MOZ_ASSERT(setElemKeyValueId().id() == 1);
+ writer.setInputOperandId(1);
+ rhsValId = ValOperandId(writer.setInputOperandId(2));
+ }
RootedId id(cx_);
bool nameOrSymbol;
if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
cx_->clearPendingException();
return false;
}
@@ -1987,17 +2033,19 @@ SetPropIRGenerator::tryAttachAddSlotStub
// Don't attach if we are adding a property to an object which the new
// script properties analysis hasn't been performed for yet, as there
// may be a shape change required here afterwards.
if (oldGroup->newScript() && !oldGroup->newScript()->analyzed()) {
*isTemporarilyUnoptimizable_ = true;
return false;
}
- ObjOperandId objId = writer.guardIsObject(lhsValId);
+ ObjOperandId objId = writer.guardIsObject(objValId);
+ maybeEmitIdGuard(id);
+
writer.guardGroup(objId, oldGroup);
// Shape guard the holder.
ObjOperandId holderId = objId;
if (!obj->isNative()) {
MOZ_ASSERT(obj->as<UnboxedPlainObject>().maybeExpando());
holderId = writer.guardAndLoadUnboxedExpando(objId);
}
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -130,16 +130,17 @@ class TypedOperandId : public OperandId
};
enum class CacheKind : uint8_t
{
GetProp,
GetElem,
GetName,
SetProp,
+ SetElem,
};
#define CACHE_IR_OPS(_) \
_(GuardIsObject) \
_(GuardIsObjectOrNull) \
_(GuardIsString) \
_(GuardIsSymbol) \
_(GuardIsInt32Index) \
@@ -836,16 +837,18 @@ class MOZ_RAII IRGenerator
CacheKind cacheKind_;
IRGenerator(const IRGenerator&) = delete;
IRGenerator& operator=(const IRGenerator&) = delete;
bool maybeGuardInt32Index(const Value& index, ValOperandId indexId,
uint32_t* int32Index, Int32OperandId* int32IndexId);
+ void emitIdGuard(ValOperandId valId, jsid id);
+
public:
explicit IRGenerator(JSContext* cx, jsbytecode* pc, CacheKind cacheKind);
const CacheIRWriter& writerRef() const { return writer; }
CacheKind cacheKind() const { return cacheKind_; }
};
enum class CanAttachGetter { Yes, No };
@@ -962,16 +965,31 @@ class MOZ_RAII SetPropIRGenerator : publ
void setUpdateStubInfo(ObjectGroup* group, jsid id) {
MOZ_ASSERT(!needUpdateStub_);
needUpdateStub_ = true;
updateStubGroup_ = group;
updateStubId_ = id;
}
+ ValOperandId setElemKeyValueId() const {
+ MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
+ return ValOperandId(1);
+ }
+ ValOperandId rhsValueId() const {
+ if (cacheKind_ == CacheKind::SetProp)
+ return ValOperandId(1);
+ MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
+ return ValOperandId(2);
+ }
+
+ // If this is a SetElem cache, emit instructions to guard the incoming Value
+ // matches |id|.
+ void maybeEmitIdGuard(jsid id);
+
bool tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
ValOperandId rhsId);
bool tryAttachUnboxedExpandoSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
ValOperandId rhsId);
bool tryAttachUnboxedProperty(HandleObject obj, ObjOperandId objId, HandleId id,
ValOperandId rhsId);
bool tryAttachTypedObjectProperty(HandleObject obj, ObjOperandId objId, HandleId id,
ValOperandId rhsId);
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -26,16 +26,24 @@ CacheRegisterAllocator::useValueRegister
return loc.valueReg();
case OperandLocation::ValueStack: {
ValueOperand reg = allocateValueRegister(masm);
popValue(masm, &loc, reg);
return reg;
}
+ case OperandLocation::BaselineFrame: {
+ ValueOperand reg = allocateValueRegister(masm);
+ Address addr = addressOf(masm, loc.baselineFrameSlot());
+ masm.loadValue(addr, reg);
+ loc.setValueReg(reg);
+ return reg;
+ }
+
case OperandLocation::Constant: {
ValueOperand reg = allocateValueRegister(masm);
masm.moveValue(loc.constant(), reg);
loc.setValueReg(reg);
return reg;
}
case OperandLocation::PayloadReg: {
@@ -72,16 +80,21 @@ CacheRegisterAllocator::useFixedValueReg
case OperandLocation::ValueReg:
masm.moveValue(loc.valueReg(), reg);
MOZ_ASSERT(!currentOpRegs_.aliases(loc.valueReg()), "Register shouldn't be in use");
availableRegs_.add(loc.valueReg());
break;
case OperandLocation::ValueStack:
popValue(masm, &loc, reg);
break;
+ case OperandLocation::BaselineFrame: {
+ Address addr = addressOf(masm, loc.baselineFrameSlot());
+ masm.loadValue(addr, reg);
+ break;
+ }
case OperandLocation::Constant:
masm.moveValue(loc.constant(), reg);
break;
case OperandLocation::PayloadReg:
masm.tagValue(loc.payloadType(), loc.payloadReg(), reg);
MOZ_ASSERT(!currentOpRegs_.has(loc.payloadReg()), "Register shouldn't be in use");
availableRegs_.add(loc.payloadReg());
break;
@@ -138,16 +151,24 @@ CacheRegisterAllocator::useRegister(Macr
MOZ_ASSERT(loc.valueStack() < stackPushed_);
masm.unboxObject(Address(masm.getStackPointer(), stackPushed_ - loc.valueStack()),
reg);
}
loc.setPayloadReg(reg, typedId.type());
return reg;
}
+ case OperandLocation::BaselineFrame: {
+ Register reg = allocateRegister(masm);
+ Address addr = addressOf(masm, loc.baselineFrameSlot());
+ masm.unboxNonDouble(addr, reg);
+ loc.setPayloadReg(reg, typedId.type());
+ return reg;
+ };
+
case OperandLocation::Constant: {
Value v = loc.constant();
Register reg = allocateRegister(masm);
if (v.isString())
masm.movePtr(ImmGCPtr(v.toString()), reg);
else if (v.isSymbol())
masm.movePtr(ImmGCPtr(v.toSymbol()), reg);
else
@@ -201,16 +222,17 @@ CacheRegisterAllocator::freeDeadOperandR
availableRegs_.add(loc.payloadReg());
break;
case OperandLocation::ValueReg:
availableRegs_.add(loc.valueReg());
break;
case OperandLocation::Uninitialized:
case OperandLocation::PayloadStack:
case OperandLocation::ValueStack:
+ case OperandLocation::BaselineFrame:
case OperandLocation::Constant:
break;
}
loc.setUninitialized();
}
}
void
@@ -379,16 +401,17 @@ CacheRegisterAllocator::inputRegisterSet
case OperandLocation::PayloadReg:
result.add(loc.payloadReg());
continue;
case OperandLocation::ValueReg:
result.add(loc.valueReg());
continue;
case OperandLocation::PayloadStack:
case OperandLocation::ValueStack:
+ case OperandLocation::BaselineFrame:
case OperandLocation::Constant:
continue;
case OperandLocation::Uninitialized:
break;
}
MOZ_CRASH("Invalid kind");
}
@@ -398,16 +421,17 @@ CacheRegisterAllocator::inputRegisterSet
JSValueType
CacheRegisterAllocator::knownType(ValOperandId val) const
{
const OperandLocation& loc = operandLocations_[val.id()];
switch (loc.kind()) {
case OperandLocation::ValueReg:
case OperandLocation::ValueStack:
+ case OperandLocation::BaselineFrame:
return JSVAL_TYPE_UNKNOWN;
case OperandLocation::PayloadStack:
case OperandLocation::PayloadReg:
return loc.payloadType();
case OperandLocation::Constant:
return loc.constant().isDouble()
@@ -533,16 +557,17 @@ OperandLocation::aliasesReg(const Operan
switch (other.kind_) {
case PayloadReg:
return aliasesReg(other.payloadReg());
case ValueReg:
return aliasesReg(other.valueReg());
case PayloadStack:
case ValueStack:
+ case BaselineFrame:
case Constant:
return false;
case Uninitialized:
break;
}
MOZ_CRASH("Invalid kind");
}
@@ -584,16 +609,17 @@ CacheRegisterAllocator::restoreInputStat
popPayload(masm, &cur, scratch);
masm.tagValue(cur.payloadType(), scratch, dest.valueReg());
continue;
}
case OperandLocation::ValueStack:
popValue(masm, &cur, dest.valueReg());
continue;
case OperandLocation::Constant:
+ case OperandLocation::BaselineFrame:
case OperandLocation::Uninitialized:
break;
}
} else if (dest.kind() == OperandLocation::PayloadReg) {
// We have to restore a payload register.
switch (cur.kind()) {
case OperandLocation::ValueReg:
MOZ_ASSERT(dest.payloadType() != JSVAL_TYPE_DOUBLE);
@@ -611,20 +637,23 @@ CacheRegisterAllocator::restoreInputStat
case OperandLocation::ValueStack:
MOZ_ASSERT(stackPushed_ >= sizeof(js::Value));
MOZ_ASSERT(cur.valueStack() <= stackPushed_);
MOZ_ASSERT(dest.payloadType() != JSVAL_TYPE_DOUBLE);
masm.unboxNonDouble(Address(masm.getStackPointer(), stackPushed_ - cur.valueStack()),
dest.payloadReg());
continue;
case OperandLocation::Constant:
+ case OperandLocation::BaselineFrame:
case OperandLocation::Uninitialized:
break;
}
- } else if (dest.kind() == OperandLocation::Constant) {
+ } else if (dest.kind() == OperandLocation::Constant ||
+ dest.kind() == OperandLocation::BaselineFrame)
+ {
// Nothing to do.
continue;
}
MOZ_CRASH("Invalid kind");
}
for (const SpilledRegister& spill : spilledRegs_) {
@@ -944,16 +973,18 @@ OperandLocation::operator==(const Operan
case PayloadReg:
return payloadReg() == other.payloadReg() && payloadType() == other.payloadType();
case ValueReg:
return valueReg() == other.valueReg();
case PayloadStack:
return payloadStack() == other.payloadStack() && payloadType() == other.payloadType();
case ValueStack:
return valueStack() == other.valueStack();
+ case BaselineFrame:
+ return baselineFrameSlot() == other.baselineFrameSlot();
case Constant:
return constant() == other.constant();
}
MOZ_CRASH("Invalid OperandLocation kind");
}
AutoOutputRegister::AutoOutputRegister(CacheIRCompiler& compiler)
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -42,27 +42,43 @@ namespace jit {
_(LoadStringLengthResult) \
_(LoadStringCharResult) \
_(LoadArgumentsObjectArgResult) \
_(LoadDenseElementResult) \
_(LoadDenseElementHoleResult) \
_(LoadUnboxedArrayElementResult) \
_(LoadTypedElementResult)
+// Represents a Value on the Baseline frame's expression stack. Slot 0 is the
+// value on top of the stack (the most recently pushed value), slot 1 is the
+// value pushed before that, etc.
+class BaselineFrameSlot
+{
+ uint32_t slot_;
+
+ public:
+ explicit BaselineFrameSlot(uint32_t slot) : slot_(slot) {}
+ uint32_t slot() const { return slot_; }
+
+ bool operator==(const BaselineFrameSlot& other) const { return slot_ == other.slot_; }
+ bool operator!=(const BaselineFrameSlot& other) const { return slot_ != other.slot_; }
+};
+
// OperandLocation represents the location of an OperandId. The operand is
// either in a register or on the stack, and is either boxed or unboxed.
class OperandLocation
{
public:
enum Kind {
Uninitialized = 0,
PayloadReg,
ValueReg,
PayloadStack,
ValueStack,
+ BaselineFrame,
Constant,
};
private:
Kind kind_;
union Data {
struct {
@@ -70,16 +86,17 @@ class OperandLocation
JSValueType type;
} payloadReg;
ValueOperand valueReg;
struct {
uint32_t stackPushed;
JSValueType type;
} payloadStack;
uint32_t valueStackPushed;
+ BaselineFrameSlot baselineFrameSlot;
Value constant;
Data() : valueStackPushed(0) {}
};
Data data_;
public:
OperandLocation() : kind_(Uninitialized) {}
@@ -111,16 +128,20 @@ class OperandLocation
return data_.payloadReg.type;
MOZ_ASSERT(kind_ == PayloadStack);
return data_.payloadStack.type;
}
Value constant() const {
MOZ_ASSERT(kind_ == Constant);
return data_.constant;
}
+ BaselineFrameSlot baselineFrameSlot() const {
+ MOZ_ASSERT(kind_ == BaselineFrame);
+ return data_.baselineFrameSlot;
+ }
void setPayloadReg(Register reg, JSValueType type) {
kind_ = PayloadReg;
data_.payloadReg.reg = reg;
data_.payloadReg.type = type;
}
void setValueReg(ValueOperand reg) {
kind_ = ValueReg;
@@ -134,16 +155,20 @@ class OperandLocation
void setValueStack(uint32_t stackPushed) {
kind_ = ValueStack;
data_.valueStackPushed = stackPushed;
}
void setConstant(const Value& v) {
kind_ = Constant;
data_.constant = v;
}
+ void setBaselineFrame(BaselineFrameSlot slot) {
+ kind_ = BaselineFrame;
+ data_.baselineFrameSlot = slot;
+ }
bool isInRegister() const { return kind_ == PayloadReg || kind_ == ValueReg; }
bool isOnStack() const { return kind_ == PayloadStack || kind_ == ValueStack; }
size_t stackPushed() const {
if (kind_ == PayloadStack)
return data_.payloadStack.stackPushed;
MOZ_ASSERT(kind_ == ValueStack);
@@ -281,16 +306,20 @@ class MOZ_RAII CacheRegisterAllocator
void initInputLocation(size_t i, Register reg, JSValueType type) {
origInputLocations_[i].setPayloadReg(reg, type);
operandLocations_[i].setPayloadReg(reg, type);
}
void initInputLocation(size_t i, const Value& v) {
origInputLocations_[i].setConstant(v);
operandLocations_[i].setConstant(v);
}
+ void initInputLocation(size_t i, BaselineFrameSlot slot) {
+ origInputLocations_[i].setBaselineFrame(slot);
+ operandLocations_[i].setBaselineFrame(slot);
+ }
void initInputLocation(size_t i, const TypedOrValueRegister& reg);
void initInputLocation(size_t i, const ConstantOrRegister& value);
const SpilledRegisterVector& spilledRegs() const { return spilledRegs_; }
MOZ_MUST_USE bool setSpilledRegs(const SpilledRegisterVector& regs) {
spilledRegs_.clear();
@@ -333,16 +362,18 @@ class MOZ_RAII CacheRegisterAllocator
releaseRegister(reg.valueReg());
#endif
}
// Removes spilled values from the native stack. This should only be
// called after all registers have been allocated.
void discardStack(MacroAssembler& masm);
+ Address addressOf(MacroAssembler& masm, BaselineFrameSlot slot) const;
+
// Returns the register for the given operand. If the operand is currently
// not in a register, it will load it into one.
ValueOperand useValueRegister(MacroAssembler& masm, ValOperandId val);
ValueOperand useFixedValueRegister(MacroAssembler& masm, ValOperandId valId, ValueOperand reg);
Register useRegister(MacroAssembler& masm, TypedOperandId typedId);
// Allocates an output register for the given operand.
Register defineRegister(MacroAssembler& masm, TypedOperandId typedId);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -253,16 +253,17 @@ CodeGenerator::visitOutOfLineICFallback(
StoreValueTo(getPropIC->output()).generate(this);
restoreLiveIgnore(lir, StoreValueTo(getPropIC->output()).clobbered());
masm.jump(ool->rejoin());
return;
}
case CacheKind::GetName:
case CacheKind::SetProp:
+ case CacheKind::SetElem:
MOZ_CRASH("Baseline-specific for now");
}
MOZ_CRASH();
}
StringObject*
MNewStringObject::templateObj() const
{
--- a/js/src/jit/ExecutableAllocator.h
+++ b/js/src/jit/ExecutableAllocator.h
@@ -31,16 +31,19 @@
#include "mozilla/Maybe.h"
#include "mozilla/XorShift128PlusRNG.h"
#include <limits>
#include <stddef.h> // for ptrdiff_t
#include "jsalloc.h"
+#ifdef JS_CODEGEN_ARM
+#include "jit/arm/Architecture-arm.h"
+#endif
#include "jit/arm/Simulator-arm.h"
#if defined(JS_CODEGEN_ARM64)
#include "jit/arm64/vixl/Cpu-vixl.h"
#endif
#include "jit/mips32/Simulator-mips32.h"
#include "jit/mips64/Simulator-mips64.h"
#include "jit/ProcessExecutableMemory.h"
#include "js/GCAPI.h"
@@ -259,28 +262,45 @@ class ExecutableAllocator
#elif defined(JS_CODEGEN_ARM) && defined(XP_IOS)
static void cacheFlush(void* code, size_t size)
{
sys_icache_invalidate(code, size);
}
#elif defined(JS_CODEGEN_ARM) && (defined(__linux__) || defined(ANDROID)) && defined(__GNUC__)
static void cacheFlush(void* code, size_t size)
{
+ void* end = (void*)(reinterpret_cast<char*>(code) + size);
asm volatile (
"push {r7}\n"
"mov r0, %0\n"
"mov r1, %1\n"
"mov r7, #0xf0000\n"
"add r7, r7, #0x2\n"
"mov r2, #0x0\n"
"svc 0x0\n"
"pop {r7}\n"
:
- : "r" (code), "r" (reinterpret_cast<char*>(code) + size)
+ : "r" (code), "r" (end)
: "r0", "r1", "r2");
+
+ if (ForceDoubleCacheFlush()) {
+ void* start = (void*)((uintptr_t)code + 1);
+ asm volatile (
+ "push {r7}\n"
+ "mov r0, %0\n"
+ "mov r1, %1\n"
+ "mov r7, #0xf0000\n"
+ "add r7, r7, #0x2\n"
+ "mov r2, #0x0\n"
+ "svc 0x0\n"
+ "pop {r7}\n"
+ :
+ : "r" (start), "r" (end)
+ : "r0", "r1", "r2");
+ }
}
#elif defined(JS_CODEGEN_ARM64) && (defined(__linux__) || defined(ANDROID)) && defined(__GNUC__)
static void cacheFlush(void* code, size_t size)
{
vixl::CPU::EnsureIAndDCacheCoherency(code, size);
}
#elif defined(__sparc__)
static void cacheFlush(void* code, size_t size)
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -9350,16 +9350,19 @@ IonBuilder::jsop_rest()
MDefinition *array = current->peek(-1);
MElements* elements = MElements::New(alloc(), array);
current->add(elements);
// Unroll the argument copy loop. We don't need to do any bounds or hole
// checking here.
MConstant* index = nullptr;
for (unsigned i = numFormals; i < numActuals; i++) {
+ if (!alloc().ensureBallast())
+ return abort(AbortReason::Alloc);
+
index = MConstant::New(alloc(), Int32Value(i - numFormals));
current->add(index);
MDefinition* arg = inlineCallInfo_->argv()[i];
MStoreElement* store = MStoreElement::New(alloc(), elements, index, arg,
/* needsHoleCheck = */ false);
current->add(store);
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -38,16 +38,17 @@ IonIC::scratchRegisterForEntryJump()
Register temp = asGetPropertyIC()->maybeTemp();
if (temp != InvalidReg)
return temp;
TypedOrValueRegister output = asGetPropertyIC()->output();
return output.hasValue() ? output.valueReg().scratchReg() : output.typedReg().gpr();
}
case CacheKind::GetName:
case CacheKind::SetProp:
+ case CacheKind::SetElem:
MOZ_CRASH("Baseline-specific for now");
}
MOZ_CRASH("Invalid kind");
}
void
IonIC::reset(Zone* zone)
--- a/js/src/jit/arm/Architecture-arm.cpp
+++ b/js/src/jit/arm/Architecture-arm.cpp
@@ -129,16 +129,23 @@ CanonicalizeARMHwCapFlags(uint32_t flags
// Older kernels do not implement the HWCAP_VFPD32 flag.
if ((flags & HWCAP_VFPv3) && !(flags & HWCAP_VFPv3D16))
flags |= HWCAP_VFPD32;
return flags;
}
+volatile bool forceDoubleCacheFlush = false;
+
+bool
+ForceDoubleCacheFlush() {
+ return forceDoubleCacheFlush;
+}
+
// The override flags parsed from the ARMHWCAP environment variable or from the
// --arm-hwcap js shell argument.
volatile uint32_t armHwCapFlags = HWCAP_UNINITIALIZED;
bool
ParseARMHwCapFlags(const char* armHwCap)
{
uint32_t flags = 0;
@@ -198,50 +205,58 @@ InitARMFlags()
#ifdef JS_SIMULATOR_ARM
// HWCAP_FIXUP_FAULT is on by default even if HWCAP_ALIGNMENT_FAULT is
// not on by default, because some memory access instructions always fault.
// Notably, this is true for floating point accesses.
flags = HWCAP_ARMv7 | HWCAP_VFP | HWCAP_VFPv3 | HWCAP_VFPv4 | HWCAP_NEON | HWCAP_IDIVA
| HWCAP_FIXUP_FAULT;
#else
-#if defined(__linux__)
+#if defined(__linux__) || defined(ANDROID)
// This includes Android and B2G.
bool readAuxv = false;
int fd = open("/proc/self/auxv", O_RDONLY);
if (fd > 0) {
struct { uint32_t a_type; uint32_t a_val; } aux;
while (read(fd, &aux, sizeof(aux))) {
if (aux.a_type == AT_HWCAP) {
flags = aux.a_val;
readAuxv = true;
break;
}
}
close(fd);
}
- if (!readAuxv) {
+ FILE* fp = fopen("/proc/cpuinfo", "r");
+ if (fp) {
+ char buf[1024];
+ memset(buf, 0, sizeof(buf));
+ size_t len = fread(buf, sizeof(char), sizeof(buf) - 1, fp);
+ fclose(fp);
+ buf[len] = '\0';
+
// Read the cpuinfo Features if the auxv is not available.
- FILE* fp = fopen("/proc/cpuinfo", "r");
- if (fp) {
- char buf[1024];
- memset(buf, 0, sizeof(buf));
- size_t len = fread(buf, sizeof(char), sizeof(buf) - 1, fp);
- fclose(fp);
- buf[len] = '\0';
+ if (!readAuxv) {
char* featureList = strstr(buf, "Features");
if (featureList) {
if (char* featuresEnd = strstr(featureList, "\n"))
*featuresEnd = '\0';
flags = ParseARMCpuFeatures(featureList + 8);
}
if (strstr(buf, "ARMv7"))
flags |= HWCAP_ARMv7;
}
+
+ // The exynos7420 cpu (EU galaxy S6 (Note)) has a bug where sometimes
+ // flushing doesn't invalidate the instruction cache. As a result we force
+ // it by calling the cacheFlush twice on different start addresses.
+ char* exynos7420 = strstr(buf, "Exynos7420");
+ if (exynos7420)
+ forceDoubleCacheFlush = true;
}
#endif
// If compiled to use specialized features then these features can be
// assumed to be present otherwise the compiler would fail to run.
#ifdef JS_CODEGEN_ARM_HARDFP
// Compiled to use the hardfp ABI.
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -772,16 +772,18 @@ static inline bool UseHardFpABI()
#if defined(JS_CODEGEN_ARM_HARDFP)
return true;
#else
return false;
#endif
}
#endif
+bool ForceDoubleCacheFlush();
+
// In order to handle SoftFp ABI calls, we need to be able to express that we
// have ABIArg which are represented by pair of general purpose registers.
#define JS_CODEGEN_REGISTER_PAIR 1
} // namespace jit
} // namespace js
#endif /* jit_arm_Architecture_arm_h */
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -57,18 +57,19 @@ enum {
JOF_ATOMOBJECT = 19, /* uint16_t constant index + object index */
JOF_SCOPE = 20, /* unsigned 32-bit scope index */
JOF_ENVCOORD = 21, /* embedded ScopeCoordinate immediate */
JOF_TYPEMASK = 0x001f, /* mask for above immediate types */
JOF_NAME = 1 << 5, /* name operation */
JOF_PROP = 2 << 5, /* obj.prop operation */
JOF_ELEM = 3 << 5, /* obj[index] operation */
- JOF_MODEMASK = 7 << 5, /* mask for above addressing modes */
- JOF_SET = 1 << 8, /* set (i.e., assignment) operation */
+ JOF_MODEMASK = 3 << 5, /* mask for above addressing modes */
+ JOF_PROPSET = 1 << 7, /* property/element/name set operation */
+ JOF_PROPINIT = 1 << 8, /* property/element/name init operation */
/* 1 << 9 is unused */
/* 1 << 10 is unused */
/* 1 << 11 is unused */
/* 1 << 12 is unused */
/* 1 << 13 is unused */
JOF_DETECTING = 1 << 14, /* object detection for warning-quelling */
/* 1 << 15 is unused */
JOF_LEFTASSOC = 1 << 16, /* left-associative operator */
@@ -689,16 +690,28 @@ IsAliasedVarOp(JSOp op)
inline bool
IsGlobalOp(JSOp op)
{
return CodeSpec[op].format & JOF_GNAME;
}
inline bool
+IsPropertySetOp(JSOp op)
+{
+ return CodeSpec[op].format & JOF_PROPSET;
+}
+
+inline bool
+IsPropertyInitOp(JSOp op)
+{
+ return CodeSpec[op].format & JOF_PROPINIT;
+}
+
+inline bool
IsEqualityOp(JSOp op)
{
return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
}
inline bool
IsCheckStrictOp(JSOp op)
{
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -461,27 +461,27 @@ 1234567890123456789012345678901234567890
* Pops the top two values on the stack as 'val' and 'obj', and performs
* 'obj.prop = val', pushing 'val' back onto the stack. Throws a TypeError
* if the set-operation failed (per strict mode semantics).
* Category: Literals
* Type: Object
* Operands: uint32_t nameIndex
* Stack: obj, val => val
*/ \
- macro(JSOP_STRICTSETPROP, 48, "strict-setprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING|JOF_CHECKSTRICT) \
+ macro(JSOP_STRICTSETPROP, 48, "strict-setprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSTRICT) \
/*
* Pops a scope and value from the stack, assigns value to the given name,
* and pushes the value back on the stack. If the set failed, then throw
* a TypeError, per usual strict mode semantics.
* Category: Variables and Scopes
* Type: Variables
* Operands: uint32_t nameIndex
* Stack: scope, val => val
*/ \
- macro(JSOP_STRICTSETNAME, 49, "strict-setname", NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_CHECKSTRICT) \
+ macro(JSOP_STRICTSETNAME, 49, "strict-setname", NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSTRICT) \
/*
* spreadcall variant of JSOP_EVAL
*
* Invokes 'eval' with 'args' and pushes the return value onto the stack.
*
* If 'eval' in global scope is not original one, invokes the function
* with 'this' and 'args', and pushes return value onto the stack.
* Category: Statements
@@ -519,17 +519,17 @@ 1234567890123456789012345678901234567890
/*
* Pops the top two values on the stack as 'val' and 'obj' and performs
* 'obj.prop = val', pushing 'val' back onto the stack.
* Category: Literals
* Type: Object
* Operands: uint32_t nameIndex
* Stack: obj, val => val
*/ \
- macro(JSOP_SETPROP, 54, "setprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING|JOF_CHECKSLOPPY) \
+ macro(JSOP_SETPROP, 54, "setprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSLOPPY) \
/*
* Pops the top two values on the stack as 'propval' and 'obj', pushes
* 'propval' property of 'obj' onto the stack.
* Category: Literals
* Type: Object
* Operands:
* Stack: obj, propval => obj[propval]
*/ \
@@ -538,28 +538,28 @@ 1234567890123456789012345678901234567890
* Pops the top three values on the stack as 'val', 'propval' and 'obj',
* sets 'propval' property of 'obj' as 'val', pushes 'obj' onto the
* stack.
* Category: Literals
* Type: Object
* Operands:
* Stack: obj, propval, val => val
*/ \
- macro(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING|JOF_CHECKSLOPPY) \
+ macro(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSLOPPY) \
/*
* Pops the top three values on the stack as 'val', 'propval' and 'obj',
* sets 'propval' property of 'obj' as 'val', pushes 'obj' onto the
* stack. Throws a TypeError if the set fails, per strict mode
* semantics.
* Category: Literals
* Type: Object
* Operands:
* Stack: obj, propval, val => val
*/ \
- macro(JSOP_STRICTSETELEM, 57, "strict-setelem", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING|JOF_CHECKSTRICT) \
+ macro(JSOP_STRICTSETELEM, 57, "strict-setelem", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSTRICT) \
/*
* Invokes 'callee' with 'this' and 'args', pushes return value onto the
* stack.
* Category: Statements
* Type: Function
* Operands: uint16_t argc
* Stack: callee, this, args[0], ..., args[argc-1] => rval
* nuses: (argc+2)
@@ -803,33 +803,33 @@ 1234567890123456789012345678901234567890
* Fast set op for function arguments and local variables.
*
* Sets 'arguments[argno]' as the top of stack value.
* Category: Variables and Scopes
* Type: Arguments
* Operands: uint16_t argno
* Stack: v => v
*/ \
- macro(JSOP_SETARG, 85, "setarg", NULL, 3, 1, 1, JOF_QARG |JOF_NAME|JOF_SET) \
+ macro(JSOP_SETARG, 85, "setarg", NULL, 3, 1, 1, JOF_QARG |JOF_NAME) \
/*
* Pushes the value of local variable onto the stack.
* Category: Variables and Scopes
* Type: Local Variables
* Operands: uint32_t localno
* Stack: => val
*/ \
macro(JSOP_GETLOCAL, 86,"getlocal", NULL, 4, 0, 1, JOF_LOCAL|JOF_NAME) \
/*
* Stores the top stack value to the given local.
* Category: Variables and Scopes
* Type: Local Variables
* Operands: uint32_t localno
* Stack: v => v
*/ \
- macro(JSOP_SETLOCAL, 87,"setlocal", NULL, 4, 1, 1, JOF_LOCAL|JOF_NAME|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_SETLOCAL, 87,"setlocal", NULL, 4, 1, 1, JOF_LOCAL|JOF_NAME|JOF_DETECTING) \
\
/*
* Pushes unsigned 16-bit int immediate integer operand onto the stack.
* Category: Literals
* Type: Constants
* Operands: uint16_t val
* Stack: => val
*/ \
@@ -889,102 +889,102 @@ 1234567890123456789012345678901234567890
*
* Pops the top two values on the stack as 'val' and 'obj', defines
* 'nameIndex' property of 'obj' as 'val', pushes 'obj' onto the stack.
* Category: Literals
* Type: Object
* Operands: uint32_t nameIndex
* Stack: obj, val => obj
*/ \
- macro(JSOP_INITPROP, 93, "initprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITPROP, 93, "initprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_DETECTING) \
\
/*
* Initialize a numeric property in an object literal, like '{1: x}'.
*
* Pops the top three values on the stack as 'val', 'id' and 'obj', defines
* 'id' property of 'obj' as 'val', pushes 'obj' onto the stack.
* Category: Literals
* Type: Object
* Operands:
* Stack: obj, id, val => obj
*/ \
- macro(JSOP_INITELEM, 94, "initelem", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITELEM, 94, "initelem", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_DETECTING) \
\
/*
* Pops the top three values on the stack as 'val', 'index' and 'obj', sets
* 'index' property of 'obj' as 'val', pushes 'obj' and 'index + 1' onto
* the stack.
*
* This opcode is used in Array literals with spread and spreadcall
* arguments.
* Category: Literals
* Type: Array
* Operands:
* Stack: obj, index, val => obj, (index + 1)
*/ \
- macro(JSOP_INITELEM_INC,95, "initelem_inc", NULL, 1, 3, 2, JOF_BYTE|JOF_ELEM|JOF_SET) \
+ macro(JSOP_INITELEM_INC,95, "initelem_inc", NULL, 1, 3, 2, JOF_BYTE|JOF_ELEM|JOF_PROPINIT) \
\
/*
* Initialize an array element.
*
* Pops the top two values on the stack as 'val' and 'obj', sets 'index'
* property of 'obj' as 'val', pushes 'obj' onto the stack.
* Category: Literals
* Type: Array
* Operands: uint32_t index
* Stack: obj, val => obj
*/ \
- macro(JSOP_INITELEM_ARRAY,96, "initelem_array", NULL, 5, 2, 1, JOF_UINT32|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITELEM_ARRAY,96, "initelem_array", NULL, 5, 2, 1, JOF_UINT32|JOF_ELEM|JOF_PROPINIT|JOF_DETECTING) \
\
/*
* Initialize a getter in an object literal.
*
* Pops the top two values on the stack as 'val' and 'obj', defines getter
* of 'obj' as 'val', pushes 'obj' onto the stack.
* Category: Literals
* Type: Object
* Operands: uint32_t nameIndex
* Stack: obj, val => obj
*/ \
- macro(JSOP_INITPROP_GETTER, 97, "initprop_getter", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITPROP_GETTER, 97, "initprop_getter", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_DETECTING) \
/*
* Initialize a setter in an object literal.
*
* Pops the top two values on the stack as 'val' and 'obj', defines setter
* of 'obj' as 'val', pushes 'obj' onto the stack.
* Category: Literals
* Type: Object
* Operands: uint32_t nameIndex
* Stack: obj, val => obj
*/ \
- macro(JSOP_INITPROP_SETTER, 98, "initprop_setter", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITPROP_SETTER, 98, "initprop_setter", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_DETECTING) \
/*
* Initialize a numeric getter in an object literal like
* '{get 2() {}}'.
*
* Pops the top three values on the stack as 'val', 'id' and 'obj', defines
* 'id' getter of 'obj' as 'val', pushes 'obj' onto the stack.
* Category: Literals
* Type: Object
* Operands:
* Stack: obj, id, val => obj
*/ \
- macro(JSOP_INITELEM_GETTER, 99, "initelem_getter", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITELEM_GETTER, 99, "initelem_getter", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_DETECTING) \
/*
* Initialize a numeric setter in an object literal like
* '{set 2(v) {}}'.
*
* Pops the top three values on the stack as 'val', 'id' and 'obj', defines
* 'id' setter of 'obj' as 'val', pushes 'obj' onto the stack.
* Category: Literals
* Type: Object
* Operands:
* Stack: obj, id, val => obj
*/ \
- macro(JSOP_INITELEM_SETTER, 100, "initelem_setter", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITELEM_SETTER, 100, "initelem_setter", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_DETECTING) \
/*
* Pushes the call site object specified by objectIndex onto the stack. Defines the raw
* property specified by objectIndex + 1 on the call site object and freezes both the call site
* object as well as its raw property.
* Category: Literals
* Type: Object
* Operands: uint32_t objectIndex
* Stack: => obj
@@ -1024,17 +1024,17 @@ 1234567890123456789012345678901234567890
* Pops the top three values on the stack as 'val' and 'obj', and 'receiver',
* and performs 'obj.prop = val', pushing 'val' back onto the stack.
* Throws a TypeError if the set-operation failed (per strict mode semantics).
* Category: Literals
* Type: Object
* Operands: uint32_t nameIndex
* Stack: receiver, obj, val => val
*/ \
- macro(JSOP_STRICTSETPROP_SUPER, 105, "strictsetprop-super", NULL, 5, 3, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING|JOF_CHECKSTRICT) \
+ macro(JSOP_STRICTSETPROP_SUPER, 105, "strictsetprop-super", NULL, 5, 3, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSTRICT) \
\
/*
* This opcode precedes every labeled statement. It's a no-op.
*
* 'offset' is the offset to the next instruction after this statement,
* the one 'break LABEL;' would jump to. IonMonkey uses this.
* Category: Statements
* Type: Jumps
@@ -1046,17 +1046,17 @@ 1234567890123456789012345678901234567890
/*
* Pops the top three values on the stack as 'val', 'obj' and 'receiver',
* and performs 'obj.prop = val', pushing 'val' back onto the stack.
* Category: Literals
* Type: Object
* Operands: uint32_t nameIndex
* Stack: receiver, obj, val => val
*/ \
- macro(JSOP_SETPROP_SUPER, 107, "setprop-super", NULL, 5, 3, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING|JOF_CHECKSLOPPY) \
+ macro(JSOP_SETPROP_SUPER, 107, "setprop-super", NULL, 5, 3, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSLOPPY) \
\
/*
* Invokes 'callee' with 'this' and 'args', pushes return value onto the
* stack.
*
* If 'callee' is determined to be the canonical 'Function.prototype.call'
* function, then this operation is optimized to directly call 'callee'
* with 'args[0]' as 'this', and the remaining arguments as formal args
@@ -1087,26 +1087,26 @@ 1234567890123456789012345678901234567890
* Looks up name on the scope chain and pushes the scope which contains
* the name onto the stack. If not found, pushes global scope onto the
* stack.
* Category: Variables and Scopes
* Type: Variables
* Operands: uint32_t nameIndex
* Stack: => scope
*/ \
- macro(JSOP_BINDNAME, 110,"bindname", NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_SET) \
+ macro(JSOP_BINDNAME, 110,"bindname", NULL, 5, 0, 1, JOF_ATOM|JOF_NAME) \
/*
* Pops a scope and value from the stack, assigns value to the given name,
* and pushes the value back on the stack
* Category: Variables and Scopes
* Type: Variables
* Operands: uint32_t nameIndex
* Stack: scope, val => val
*/ \
- macro(JSOP_SETNAME, 111,"setname", NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_CHECKSLOPPY) \
+ macro(JSOP_SETNAME, 111,"setname", NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSLOPPY) \
\
/* Exception handling ops. */ \
/*
* Pops the top of stack value as 'v', sets pending exception as 'v', then
* raises error.
* Category: Statements
* Type: Exception Handling
* Operands:
@@ -1415,17 +1415,17 @@ 1234567890123456789012345678901234567890
macro(JSOP_GETALIASEDVAR, 136,"getaliasedvar",NULL, 5, 0, 1, JOF_ENVCOORD|JOF_NAME|JOF_TYPESET) \
/*
* Sets aliased variable as the top of stack value.
* Category: Variables and Scopes
* Type: Aliased Variables
* Operands: uint8_t hops, uint24_t slot
* Stack: v => v
*/ \
- macro(JSOP_SETALIASEDVAR, 137,"setaliasedvar",NULL, 5, 1, 1, JOF_ENVCOORD|JOF_NAME|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_SETALIASEDVAR, 137,"setaliasedvar",NULL, 5, 1, 1, JOF_ENVCOORD|JOF_NAME|JOF_PROPSET|JOF_DETECTING) \
\
/*
* Checks if the value of the local variable is the
* JS_UNINITIALIZED_LEXICAL magic, throwing an error if so.
* Category: Variables and Scopes
* Type: Local Variables
* Operands: uint32_t localno
* Stack: =>
@@ -1434,17 +1434,17 @@ 1234567890123456789012345678901234567890
/*
* Initializes an uninitialized local lexical binding with the top of stack
* value.
* Category: Variables and Scopes
* Type: Local Variables
* Operands: uint32_t localno
* Stack: v => v
*/ \
- macro(JSOP_INITLEXICAL, 139, "initlexical", NULL, 4, 1, 1, JOF_LOCAL|JOF_NAME|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITLEXICAL, 139, "initlexical", NULL, 4, 1, 1, JOF_LOCAL|JOF_NAME|JOF_DETECTING) \
/*
* Checks if the value of the aliased variable is the
* JS_UNINITIALIZED_LEXICAL magic, throwing an error if so.
* Category: Variables and Scopes
* Type: Aliased Variables
* Operands: uint8_t hops, uint24_t slot
* Stack: =>
*/ \
@@ -1452,17 +1452,17 @@ 1234567890123456789012345678901234567890
/*
* Initializes an uninitialized aliased lexical binding with the top of
* stack value.
* Category: Variables and Scopes
* Type: Aliased Variables
* Operands: uint8_t hops, uint24_t slot
* Stack: v => v
*/ \
- macro(JSOP_INITALIASEDLEXICAL, 141, "initaliasedlexical", NULL, 5, 1, 1, JOF_ENVCOORD|JOF_NAME|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITALIASEDLEXICAL, 141, "initaliasedlexical", NULL, 5, 1, 1, JOF_ENVCOORD|JOF_NAME|JOF_PROPINIT|JOF_DETECTING) \
/*
* Pushes a JS_UNINITIALIZED_LEXICAL value onto the stack, representing an
* uninitialized lexical binding.
*
* This opcode is used with the JSOP_INITLET opcode.
* Category: Literals
* Type: Constants
* Operands:
@@ -1484,17 +1484,17 @@ 1234567890123456789012345678901234567890
macro(JSOP_GETINTRINSIC, 143, "getintrinsic", NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_TYPESET) \
/*
* Stores the top stack value in the specified intrinsic.
* Category: Variables and Scopes
* Type: Intrinsics
* Operands: uint32_t nameIndex
* Stack: val => val
*/ \
- macro(JSOP_SETINTRINSIC, 144, "setintrinsic", NULL, 5, 1, 1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_SETINTRINSIC, 144, "setintrinsic", NULL, 5, 1, 1, JOF_ATOM|JOF_NAME|JOF_DETECTING) \
/*
* Like JSOP_CALL, but used as part of for-of and destructuring bytecode
* to provide better error messages.
* Category: Statements
* Type: Function
* Operands: uint16_t argc (must be 0)
* Stack: callee, this => rval
* nuses: 2
@@ -1505,28 +1505,28 @@ 1234567890123456789012345678901234567890
*
* Pops the top two values on the stack as 'val' and 'obj', defines
* 'nameIndex' property of 'obj' as 'val', pushes 'obj' onto the stack.
* Category: Literals
* Type: Object
* Operands: uint32_t nameIndex
* Stack: obj, val => obj
*/ \
- macro(JSOP_INITLOCKEDPROP, 146, "initlockedprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITLOCKEDPROP, 146, "initlockedprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_DETECTING) \
/*
* Initialize a non-enumerable data-property on an object.
*
* Pops the top two values on the stack as 'val' and 'obj', defines
* 'nameIndex' property of 'obj' as 'val', pushes 'obj' onto the stack.
* Category: Literals
* Type: Object
* Operands: uint32_t nameIndex
* Stack: obj, val => obj
*/ \
- macro(JSOP_INITHIDDENPROP, 147,"inithiddenprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITHIDDENPROP, 147,"inithiddenprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_DETECTING) \
/*
* Push "new.target"
*
* Category: Variables and Scopes
* Type: Arguments
* Operands:
* Stack: => new.target
*/ \
@@ -1603,31 +1603,31 @@ 1234567890123456789012345678901234567890
*
* 'scope' should be the global scope unless the script has a non-syntactic
* global scope, in which case acts like JSOP_SETNAME.
* Category: Variables and Scopes
* Type: Free Variables
* Operands: uint32_t nameIndex
* Stack: scope, val => val
*/ \
- macro(JSOP_SETGNAME, 155,"setgname", NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME|JOF_CHECKSLOPPY) \
+ macro(JSOP_SETGNAME, 155,"setgname", NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_DETECTING|JOF_GNAME|JOF_CHECKSLOPPY) \
\
/*
* Pops the top two values on the stack as 'val' and 'scope', sets property
* of 'scope' as 'val' and pushes 'val' back on the stack. Throws a
* TypeError if the set fails, per strict mode semantics.
*
* 'scope' should be the global scope unless the script has a non-syntactic
* global scope, in which case acts like JSOP_STRICTSETNAME.
* Category: Variables and Scopes
* Type: Free Variables
* Operands: uint32_t nameIndex
* Stack: scope, val => val
*/ \
- macro(JSOP_STRICTSETGNAME, 156, "strict-setgname", NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME|JOF_CHECKSTRICT) \
+ macro(JSOP_STRICTSETGNAME, 156, "strict-setgname", NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_PROPSET|JOF_DETECTING|JOF_GNAME|JOF_CHECKSTRICT) \
/*
* Pushes the implicit 'this' value for calls to the associated name onto
* the stack; only used when we know this implicit this will be our first
* non-syntactic scope.
*
* Category: Variables and Scopes
* Type: This
* Operands: uint32_t nameIndex
@@ -1637,26 +1637,26 @@ 1234567890123456789012345678901234567890
/*
* LIKE JSOP_SETELEM, but takes receiver on the stack, and the propval is
* evaluated before the base.
* Category: Literals
* Type: Object
* Operands:
* Stack: propval, receiver, obj, val => val
*/ \
- macro(JSOP_SETELEM_SUPER, 158, "setelem-super", NULL, 1, 4, 1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING|JOF_CHECKSLOPPY) \
+ macro(JSOP_SETELEM_SUPER, 158, "setelem-super", NULL, 1, 4, 1, JOF_BYTE |JOF_ELEM|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSLOPPY) \
/*
* LIKE JSOP_STRICTSETELEM, but takes receiver on the stack, and the
* propval is evaluated before the base.
* Category: Literals
* Type: Object
* Operands:
* Stack: propval, receiver, obj, val => val
*/ \
- macro(JSOP_STRICTSETELEM_SUPER, 159, "strict-setelem-super", NULL, 1, 4, 1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING|JOF_CHECKSTRICT) \
+ macro(JSOP_STRICTSETELEM_SUPER, 159, "strict-setelem-super", NULL, 1, 4, 1, JOF_BYTE |JOF_ELEM|JOF_PROPSET|JOF_DETECTING|JOF_CHECKSTRICT) \
\
/*
* Pushes a regular expression literal onto the stack.
* It requires special "clone on exec" handling.
* Category: Literals
* Type: RegExp
* Operands: uint32_t regexpIndex
* Stack: => regexp
@@ -1666,17 +1666,17 @@ 1234567890123456789012345678901234567890
/*
* Initializes an uninitialized global lexical binding with the top of
* stack value.
* Category: Variables and Scopes
* Type: Free Variables
* Operands: uint32_t nameIndex
* Stack: val => val
*/ \
- macro(JSOP_INITGLEXICAL, 161,"initglexical", NULL, 5, 1, 1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME) \
+ macro(JSOP_INITGLEXICAL, 161,"initglexical", NULL, 5, 1, 1, JOF_ATOM|JOF_NAME|JOF_PROPINIT|JOF_GNAME) \
\
/* Defines the new mutable binding on global lexical scope.
*
* Throws if a binding with the same name already exists on the scope, or
* if a var binding with the same name exists on the global.
* Category: Variables and Scopes
* Type: Variables
* Operands: uint32_t nameIndex
@@ -1743,84 +1743,84 @@ 1234567890123456789012345678901234567890
* Throws a runtime TypeError for invalid assignment to 'const'. The
* localno is used for better error messages.
*
* Category: Variables and Scopes
* Type: Local Variables
* Operands: uint32_t localno
* Stack: v => v
*/ \
- macro(JSOP_THROWSETCONST, 169, "throwsetconst", NULL, 4, 1, 1, JOF_LOCAL|JOF_NAME|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_THROWSETCONST, 169, "throwsetconst", NULL, 4, 1, 1, JOF_LOCAL|JOF_NAME|JOF_DETECTING) \
/*
* Throws a runtime TypeError for invalid assignment to 'const'. The
* scope coordinate is used for better error messages.
*
* Category: Variables and Scopes
* Type: Aliased Variables
* Operands: uint8_t hops, uint24_t slot
* Stack: v => v
*/ \
- macro(JSOP_THROWSETALIASEDCONST, 170, "throwsetaliasedconst", NULL, 5, 1, 1, JOF_ENVCOORD|JOF_NAME|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_THROWSETALIASEDCONST, 170, "throwsetaliasedconst", NULL, 5, 1, 1, JOF_ENVCOORD|JOF_NAME|JOF_DETECTING) \
/*
* Initialize a non-enumerable getter in an object literal.
*
* Pops the top two values on the stack as 'val' and 'obj', defines
* getter of 'obj' as 'val', pushes 'obj' onto the stack.
* Category: Literals
* Type: Object
* Operands: uint32_t nameIndex
* Stack: obj, val => obj
*/ \
- macro(JSOP_INITHIDDENPROP_GETTER, 171, "inithiddenprop_getter", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITHIDDENPROP_GETTER, 171, "inithiddenprop_getter", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_DETECTING) \
/*
* Initialize a non-enumerable setter in an object literal.
*
* Pops the top two values on the stack as 'val' and 'obj', defines
* setter of 'obj' as 'val', pushes 'obj' onto the stack.
* Category: Literals
* Type: Object
* Operands: uint32_t nameIndex
* Stack: obj, val => obj
*/ \
- macro(JSOP_INITHIDDENPROP_SETTER, 172, "inithiddenprop_setter", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITHIDDENPROP_SETTER, 172, "inithiddenprop_setter", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_DETECTING) \
/*
* Initialize a non-enumerable numeric getter in an object literal like
* '{get 2() {}}'.
*
* Pops the top three values on the stack as 'val', 'id' and 'obj', defines
* 'id' getter of 'obj' as 'val', pushes 'obj' onto the stack.
* Category: Literals
* Type: Object
* Operands:
* Stack: obj, id, val => obj
*/ \
- macro(JSOP_INITHIDDENELEM_GETTER, 173, "inithiddenelem_getter", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITHIDDENELEM_GETTER, 173, "inithiddenelem_getter", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_DETECTING) \
/*
* Initialize a non-enumerable numeric setter in an object literal like
* '{set 2(v) {}}'.
*
* Pops the top three values on the stack as 'val', 'id' and 'obj', defines
* 'id' setter of 'obj' as 'val', pushes 'obj' onto the stack.
* Category: Literals
* Type: Object
* Operands:
* Stack: obj, id, val => obj
*/ \
- macro(JSOP_INITHIDDENELEM_SETTER, 174, "inithiddenelem_setter", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITHIDDENELEM_SETTER, 174, "inithiddenelem_setter", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_DETECTING) \
/*
* Initialize a non-enumerable numeric property in an object literal, like '{1: x}'.
*
* Pops the top three values on the stack as 'val', 'id' and 'obj', defines
* 'id' property of 'obj' as 'val', pushes 'obj' onto the stack.
* Category: Literals
* Type: Object
* Operands:
* Stack: obj, id, val => obj
*/ \
- macro(JSOP_INITHIDDENELEM, 175, "inithiddenelem", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+ macro(JSOP_INITHIDDENELEM, 175, "inithiddenelem", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_DETECTING) \
/*
* Gets the value of a module import by name and pushes it onto the stack.
* Category: Variables and Scopes
* Type: Variables
* Operands: uint32_t nameIndex
* Stack: => val
*/ \
macro(JSOP_GETIMPORT, 176,"getimport", NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_TYPESET) \
@@ -1850,17 +1850,17 @@ 1234567890123456789012345678901234567890
* closed over, does not have a frame slot to look up the name with for
* the error message.
*
* Category: Variables and Scopes
* Type: Local Variables
* Operands:
* Stack: v => v
*/ \
- macro(JSOP_THROWSETCALLEE, 179, "throwsetcallee", NULL, 1, 1, 1, JOF_SET|JOF_BYTE) \
+ macro(JSOP_THROWSETCALLEE, 179, "throwsetcallee", NULL, 1, 1, 1, JOF_BYTE) \
/*
* Pushes a var environment onto the env chain.
* Category: Variables and Scopes
* Type: Var Scope
* Operands: uint32_t scopeIndex
* Stack: =>
*/ \
macro(JSOP_PUSHVARENV, 180, "pushvarenv", NULL, 5, 0, 0, JOF_SCOPE) \
@@ -2157,17 +2157,17 @@ 1234567890123456789012345678901234567890
* non-syntactic global scope. Otherwise will act like JSOP_BINDNAME.
*
* 'nameIndex' is only used when acting like JSOP_BINDNAME.
* Category: Variables and Scopes
* Type: Free Variables
* Operands: uint32_t nameIndex
* Stack: => global
*/ \
- macro(JSOP_BINDGNAME, 214, "bindgname", NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME) \
+ macro(JSOP_BINDGNAME, 214, "bindgname", NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_GNAME) \
\
/*
* Pushes 8-bit int immediate integer operand onto the stack.
* Category: Literals
* Type: Constants
* Operands: int8_t val
* Stack: => val
*/ \
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -359,31 +359,43 @@ class BaseCompiler
MIRType type_; // Type of the value, or MIRType::None
uint32_t offs_; // Zero-based frame offset of value, or UINT32_MAX
MIRType type() const { MOZ_ASSERT(type_ != MIRType::None); return type_; }
uint32_t offs() const { MOZ_ASSERT(offs_ != UINT32_MAX); return offs_; }
};
+ // Bit set used for simple bounds check elimination. Capping this at 64
+ // locals makes sense; even 32 locals would probably be OK in practice.
+ //
+ // For more information about BCE, see the block comment above
+ // popMemoryAccess(), below.
+
+ typedef uint64_t BCESet;
+
// Control node, representing labels and stack heights at join points.
struct Control
{
Control()
: framePushed(UINT32_MAX),
stackSize(UINT32_MAX),
+ bceSafeOnEntry(0),
+ bceSafeOnExit(~BCESet(0)),
deadOnArrival(false),
deadThenBranch(false)
{}
NonAssertingLabel label; // The "exit" label
NonAssertingLabel otherLabel; // Used for the "else" branch of if-then-else
uint32_t framePushed; // From masm
uint32_t stackSize; // Value stack height
+ BCESet bceSafeOnEntry; // Bounds check info flowing into the item
+ BCESet bceSafeOnExit; // Bounds check info flowing out of the item
bool deadOnArrival; // deadCode_ was set on entry to the region
bool deadThenBranch; // deadCode_ was set on exit from "then"
};
struct BaseCompilePolicy : OpIterPolicy
{
static const bool Output = true;
@@ -464,16 +476,17 @@ class BaseCompiler
TempAllocator& alloc_;
const ValTypeVector& locals_; // Types of parameters and locals
int32_t localSize_; // Size of local area in bytes (stable after beginFunction)
int32_t varLow_; // Low byte offset of local area for true locals (not parameters)
int32_t varHigh_; // High byte offset + 1 of local area for true locals
int32_t maxFramePushed_; // Max value of masm.framePushed() observed
bool deadCode_; // Flag indicating we should decode & discard the opcode
bool debugEnabled_;
+ BCESet bceSafe_; // Locals that have been bounds checked and not updated since
ValTypeVector SigI64I64_;
ValTypeVector SigD_;
ValTypeVector SigF_;
ValTypeVector SigI_;
ValTypeVector Sig_;
Label returnLabel_;
Label stackOverflowLabel_;
TrapOffset prologueTrapOffset_;
@@ -1712,16 +1725,24 @@ class BaseCompiler
c = v.i64val();
if (c <= cutoff || !IsPowerOfTwo(static_cast<uint64_t>(c)))
return false;
power = FloorLog2(c);
stk_.popBack();
return true;
}
+ MOZ_MUST_USE bool peekLocalI32(uint32_t* local) {
+ Stk& v = stk_.back();
+ if (v.kind() != Stk::LocalI32)
+ return false;
+ *local = v.slot();
+ return true;
+ }
+
// TODO / OPTIMIZE (Bug 1316818): At the moment we use ReturnReg
// for JoinReg. It is possible other choices would lead to better
// register allocation, as ReturnReg is often first in the
// register set and will be heavily wanted by the register
// allocator that uses takeFirst().
//
// Obvious options:
// - pick a register at the back of the register set
@@ -2014,16 +2035,17 @@ class BaseCompiler
void initControl(Control& item)
{
// Make sure the constructor was run properly
MOZ_ASSERT(item.framePushed == UINT32_MAX && item.stackSize == UINT32_MAX);
item.framePushed = masm.framePushed();
item.stackSize = stk_.length();
item.deadOnArrival = deadCode_;
+ item.bceSafeOnEntry = bceSafe_;
}
Control& controlItem() {
return iter_.controlItem();
}
Control& controlItem(uint32_t relativeDepth) {
return iter_.controlItem(relativeDepth);
@@ -3182,16 +3204,34 @@ class BaseCompiler
loadFromFramePtr(tmp, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
masm.storeDouble(r, Address(tmp, globalToTlsOffset(globalDataOffset)));
}
//////////////////////////////////////////////////////////////////////
//
// Heap access.
+ void bceCheckLocal(MemoryAccessDesc* access, uint32_t local, bool* omitBoundsCheck) {
+ if (local >= sizeof(BCESet)*8)
+ return;
+
+ if ((bceSafe_ & (BCESet(1) << local)) && access->offset() < wasm::OffsetGuardLimit)
+ *omitBoundsCheck = true;
+
+ // The local becomes safe even if the offset is beyond the guard limit.
+ bceSafe_ |= (BCESet(1) << local);
+ }
+
+ void bceLocalIsUpdated(uint32_t local) {
+ if (local >= sizeof(BCESet)*8)
+ return;
+
+ bceSafe_ &= ~(BCESet(1) << local);
+ }
+
void checkOffset(MemoryAccessDesc* access, RegI32 ptr) {
if (access->offset() >= OffsetGuardLimit) {
masm.branchAdd32(Assembler::CarrySet, Imm32(access->offset()), ptr,
trap(Trap::OutOfBounds));
access->clearOffset();
}
}
@@ -5089,69 +5129,80 @@ BaseCompiler::emitBlock()
void
BaseCompiler::endBlock(ExprType type)
{
Control& block = controlItem();
// Save the value.
AnyReg r;
- if (!deadCode_)
+ if (!deadCode_) {
r = popJoinRegUnlessVoid(type);
+ block.bceSafeOnExit &= bceSafe_;
+ }
// Leave the block.
popStackOnBlockExit(block.framePushed);
popValueStackTo(block.stackSize);
// Bind after cleanup: branches out will have popped the stack.
if (block.label.used()) {
masm.bind(&block.label);
// No value was provided by the fallthrough but the branch out will
// have stored one in joinReg, so capture that.
if (deadCode_)
r = captureJoinRegUnlessVoid(type);
deadCode_ = false;
}
+ bceSafe_ = block.bceSafeOnExit;
+
// Retain the value stored in joinReg by all paths, if there are any.
if (!deadCode_)
pushJoinRegUnlessVoid(r);
}
bool
BaseCompiler::emitLoop()
{
if (!iter_.readLoop())
return false;
if (!deadCode_)
sync(); // Simplifies branching out from block
initControl(controlItem());
+ bceSafe_ = 0;
if (!deadCode_) {
masm.bind(&controlItem(0).label);
addInterruptCheck();
}
return true;
}
void
BaseCompiler::endLoop(ExprType type)
{
Control& block = controlItem();
AnyReg r;
- if (!deadCode_)
+ if (!deadCode_) {
r = popJoinRegUnlessVoid(type);
+ // block.bceSafeOnExit need not be updated because it won't be used for
+ // the fallthrough path.
+ }
popStackOnBlockExit(block.framePushed);
popValueStackTo(block.stackSize);
+ // bceSafe_ stays the same along the fallthrough path because branches to
+ // loops branch to the top.
+
// Retain the value stored in joinReg by all paths.
if (!deadCode_)
pushJoinRegUnlessVoid(r);
}
// The bodies of the "then" and "else" arms can be arbitrary sequences
// of expressions, they push control and increment the nesting and can
// even be targeted by jumps. A branch to the "if" block branches to
@@ -5198,17 +5249,22 @@ BaseCompiler::endIfThen()
popValueStackTo(ifThen.stackSize);
if (ifThen.otherLabel.used())
masm.bind(&ifThen.otherLabel);
if (ifThen.label.used())
masm.bind(&ifThen.label);
+ if (!deadCode_)
+ ifThen.bceSafeOnExit &= bceSafe_;
+
deadCode_ = ifThen.deadOnArrival;
+
+ bceSafe_ = ifThen.bceSafeOnExit & ifThen.bceSafeOnEntry;
}
bool
BaseCompiler::emitElse()
{
ExprType thenType;
Nothing unused_thenValue;
@@ -5233,20 +5289,23 @@ BaseCompiler::emitElse()
if (!deadCode_)
masm.jump(&ifThenElse.label);
if (ifThenElse.otherLabel.used())
masm.bind(&ifThenElse.otherLabel);
// Reset to the "else" branch.
- if (!deadCode_)
+ if (!deadCode_) {
freeJoinRegUnlessVoid(r);
+ ifThenElse.bceSafeOnExit &= bceSafe_;
+ }
deadCode_ = ifThenElse.deadOnArrival;
+ bceSafe_ = ifThenElse.bceSafeOnEntry;
return true;
}
void
BaseCompiler::endIfThenElse(ExprType type)
{
Control& ifThenElse = controlItem();
@@ -5254,18 +5313,20 @@ BaseCompiler::endIfThenElse(ExprType typ
// The expression type is not a reliable guide to what we'll find
// on the stack, we could have (if E (i32.const 1) (unreachable))
// in which case the "else" arm is AnyType but the type of the
// full expression is I32. So restore whatever's there, not what
// we want to find there. The "then" arm has the same constraint.
AnyReg r;
- if (!deadCode_)
+ if (!deadCode_) {
r = popJoinRegUnlessVoid(type);
+ ifThenElse.bceSafeOnExit &= bceSafe_;
+ }
popStackOnBlockExit(ifThenElse.framePushed);
popValueStackTo(ifThenElse.stackSize);
if (ifThenElse.label.used())
masm.bind(&ifThenElse.label);
bool joinLive = !ifThenElse.deadOnArrival &&
@@ -5274,16 +5335,18 @@ BaseCompiler::endIfThenElse(ExprType typ
if (joinLive) {
// No value was provided by the "then" path but capture the one
// provided by the "else" path.
if (deadCode_)
r = captureJoinRegUnlessVoid(type);
deadCode_ = false;
}
+ bceSafe_ = ifThenElse.bceSafeOnExit;
+
if (!deadCode_)
pushJoinRegUnlessVoid(r);
}
bool
BaseCompiler::emitEnd()
{
LabelKind kind;
@@ -5314,16 +5377,17 @@ BaseCompiler::emitBr()
Nothing unused_value;
if (!iter_.readBr(&relativeDepth, &type, &unused_value))
return false;
if (deadCode_)
return true;
Control& target = controlItem(relativeDepth);
+ target.bceSafeOnExit &= bceSafe_;
// Save any value in the designated join register, where the
// normal block exit code will also leave it.
AnyReg r = popJoinRegUnlessVoid(type);
popStackBeforeBranch(target.framePushed);
masm.jump(&target.label);
@@ -5348,16 +5412,17 @@ BaseCompiler::emitBrIf()
return false;
if (deadCode_) {
resetLatentOp();
return true;
}
Control& target = controlItem(relativeDepth);
+ target.bceSafeOnExit &= bceSafe_;
BranchState b(&target.label, target.framePushed, InvertBranch(false), type);
emitBranchSetup(&b);
emitBranchPerform(&b);
return true;
}
@@ -5403,31 +5468,33 @@ BaseCompiler::emitBrTable()
AnyReg r = popJoinRegUnlessVoid(type);
Label dispatchCode;
masm.branch32(Assembler::Below, rc, Imm32(tableLength), &dispatchCode);
// This is the out-of-range stub. rc is dead here but we don't need it.
popStackBeforeBranch(controlItem(defaultDepth).framePushed);
+ controlItem(defaultDepth).bceSafeOnExit &= bceSafe_;
masm.jump(&controlItem(defaultDepth).label);
// Emit stubs. rc is dead in all of these but we don't need it.
//
// The labels in the vector are in the TempAllocator and will
// be freed by and by.
//
// TODO / OPTIMIZE (Bug 1316804): Branch directly to the case code if we
// can, don't emit an intermediate stub.
for (uint32_t i = 0; i < tableLength; i++) {
stubs.infallibleEmplaceBack(NonAssertingLabel());
masm.bind(&stubs.back());
uint32_t k = depths[i];
popStackBeforeBranch(controlItem(k).framePushed);
+ controlItem(k).bceSafeOnExit &= bceSafe_;
masm.jump(&controlItem(k).label);
}
// Emit table.
Label theTable;
jumpTable(stubs, &theTable);
@@ -5886,16 +5953,17 @@ BaseCompiler::emitGetLocal()
template<bool isSetLocal>
bool
BaseCompiler::emitSetOrTeeLocal(uint32_t slot)
{
if (deadCode_)
return true;
+ bceLocalIsUpdated(slot);
switch (locals_[slot]) {
case ValType::I32: {
RegI32 rv = popI32();
syncLocal(slot);
storeToFrameI32(rv, frameOffsetFromSlot(slot, MIRType::Int32));
if (isSetLocal)
freeI32(rv);
else
@@ -6064,35 +6132,81 @@ BaseCompiler::emitSetGlobal()
}
default:
MOZ_CRASH("Global variable type");
break;
}
return true;
}
-// See EffectiveAddressAnalysis::analyzeAsmJSHeapAccess() for comparable Ion code.
+// Bounds check elimination.
+//
+// We perform BCE on two kinds of address expressions: on constant heap pointers
+// that are known to be in the heap or will be handled by the out-of-bounds trap
+// handler; and on local variables that have been checked in dominating code
+// without being updated since.
+//
+// For an access through a constant heap pointer + an offset we can eliminate
+// the bounds check if the sum of the address and offset is below the sum of the
+// minimum memory length and the offset guard length.
+//
+// For an access through a local variable + an offset we can eliminate the
+// bounds check if the local variable has already been checked and has not been
+// updated since, and the offset is less than the guard limit.
+//
+// To track locals for which we can eliminate checks we use a bit vector
+// bceSafe_ that has a bit set for those locals whose bounds have been checked
+// and which have not subsequently been set. Initially this vector is zero.
+//
+// In straight-line code a bit is set when we perform a bounds check on an
+// access via the local and is reset when the variable is updated.
//
+// In control flow, the bit vector is manipulated as follows. Each ControlItem
+// has a value bceSafeOnEntry, which is the value of bceSafe_ on entry to the
+// item, and a value bceSafeOnExit, which is initially ~0. On a branch (br,
+// brIf, brTable), we always AND the branch target's bceSafeOnExit with the
+// value of bceSafe_ at the branch point. On exiting an item by falling out of
+// it, provided we're not in dead code, we AND the current value of bceSafe_
+// into the item's bceSafeOnExit. Additional processing depends on the item
+// type:
+//
+// - After a block, set bceSafe_ to the block's bceSafeOnExit.
+//
+// - On loop entry, after pushing the ControlItem, set bceSafe_ to zero; the
+// back edges would otherwise require us to iterate to a fixedpoint.
+//
+// - After a loop, the bceSafe_ is left unchanged, because only fallthrough
+// control flow will reach that point and the bceSafe_ value represents the
+// correct state of the fallthrough path.
+//
+// - Set bceSafe_ to the ControlItem's bceSafeOnEntry at both the 'then' branch
+// and the 'else' branch.
+//
+// - After an if-then-else, set bceSafe_ to the if-then-else's bceSafeOnExit.
+//
+// - After an if-then, set bceSafe_ to the if-then's bceSafeOnExit AND'ed with
+// the if-then's bceSafeOnEntry.
+//
+// Finally, when the debugger allows locals to be mutated we must disable BCE
+// for references via a local, by returning immediately from bceCheckLocal if
+// debugEnabled_ is true.
+
// TODO / OPTIMIZE (bug 1329576): There are opportunities to generate better
// code by not moving a constant address with a zero offset into a register.
BaseCompiler::RegI32
BaseCompiler::popMemoryAccess(MemoryAccessDesc* access, bool* omitBoundsCheck)
{
// Caller must initialize.
MOZ_ASSERT(!*omitBoundsCheck);
int32_t addrTmp;
if (popConstI32(addrTmp)) {
uint32_t addr = addrTmp;
- // We can eliminate the bounds check if the sum of the constant address
- // and the known offset are below the sum of the minimum memory length
- // and the offset guard length.
-
uint64_t ea = uint64_t(addr) + uint64_t(access->offset());
uint64_t limit = uint64_t(env_.minMemoryLength) + uint64_t(wasm::OffsetGuardLimit);
*omitBoundsCheck = ea < limit;
// Fold the offset into the pointer if we can, as this is always
// beneficial.
@@ -6101,16 +6215,20 @@ BaseCompiler::popMemoryAccess(MemoryAcce
access->clearOffset();
}
RegI32 r = needI32();
loadConstI32(r, int32_t(addr));
return r;
}
+ uint32_t local;
+ if (peekLocalI32(&local))
+ bceCheckLocal(access, local, omitBoundsCheck);
+
return popI32();
}
bool
BaseCompiler::emitLoad(ValType type, Scalar::Type viewType)
{
LinearMemoryAddress<Nothing> addr;
if (!iter_.readLoad(type, Scalar::byteSize(viewType), &addr))
@@ -7166,16 +7284,17 @@ BaseCompiler::BaseCompiler(const ModuleE
alloc_(*alloc),
locals_(locals),
localSize_(0),
varLow_(0),
varHigh_(0),
maxFramePushed_(0),
deadCode_(false),
debugEnabled_(debugEnabled),
+ bceSafe_(0),
prologueTrapOffset_(trapOffset()),
stackAddOffset_(0),
latentOp_(LatentOp::None),
latentType_(ValType::I32),
latentIntCmp_(Assembler::Equal),
latentDoubleCmp_(Assembler::DoubleEqual),
masm(*masm),
availGPR_(GeneralRegisterSet::All()),
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -6463,17 +6463,17 @@ static DrawResult
DrawImageInternal(gfxContext& aContext,
nsPresContext* aPresContext,
imgIContainer* aImage,
const SamplingFilter aSamplingFilter,
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
- const SVGImageContext* aSVGContext,
+ const Maybe<const SVGImageContext>& aSVGContext,
uint32_t aImageFlags,
ExtendMode aExtendMode = ExtendMode::CLAMP,
float aOpacity = 1.0)
{
DrawResult result = DrawResult::SUCCESS;
aImageFlags |= imgIContainer::FLAG_ASYNC_NOTIFY;
@@ -6499,25 +6499,26 @@ DrawImageInternal(gfxContext&
{
gfxContextMatrixAutoSaveRestore contextMatrixRestorer(&aContext);
RefPtr<gfxContext> destCtx = &aContext;
destCtx->SetMatrix(params.imageSpaceToDeviceSpace);
- Maybe<SVGImageContext> svgContext = ToMaybe(aSVGContext);
- if (!svgContext) {
+ Maybe<const SVGImageContext> fallbackContext;
+ if (!aSVGContext) {
// Use the default viewport.
- svgContext = Some(SVGImageContext(params.svgViewportSize, Nothing()));
+ fallbackContext.emplace(params.svgViewportSize);
}
result = aImage->Draw(destCtx, params.size, params.region,
imgIContainer::FRAME_CURRENT, aSamplingFilter,
- svgContext, aImageFlags, aOpacity);
+ aSVGContext ? aSVGContext : fallbackContext,
+ aImageFlags, aOpacity);
}
return result;
}
/* static */ DrawResult
nsLayoutUtils::DrawSingleUnscaledImage(gfxContext& aContext,
@@ -6549,27 +6550,27 @@ nsLayoutUtils::DrawSingleUnscaledImage(g
nsRect fill(aDest, source.Size());
// Ensure that only a single image tile is drawn. If aSourceArea extends
// outside the image bounds, we want to honor the aSourceArea-to-aDest
// translation but we don't want to actually tile the image.
fill.IntersectRect(fill, dest);
return DrawImageInternal(aContext, aPresContext,
aImage, aSamplingFilter,
dest, fill, aDest, aDirty ? *aDirty : dest,
- nullptr, aImageFlags);
+ /* no SVGImageContext */ Nothing(), aImageFlags);
}
/* static */ DrawResult
nsLayoutUtils::DrawSingleImage(gfxContext& aContext,
nsPresContext* aPresContext,
imgIContainer* aImage,
const SamplingFilter aSamplingFilter,
const nsRect& aDest,
const nsRect& aDirty,
- const SVGImageContext* aSVGContext,
+ const Maybe<const SVGImageContext>& aSVGContext,
uint32_t aImageFlags,
const nsPoint* aAnchorPoint,
const nsRect* aSourceArea)
{
nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel();
CSSIntSize pixelImageSize(ComputeSizeForDrawingWithFallback(aImage, aDest.Size()));
if (pixelImageSize.width < 1 || pixelImageSize.height < 1) {
NS_ASSERTION(pixelImageSize.width >= 0 && pixelImageSize.height >= 0,
@@ -6686,34 +6687,34 @@ nsLayoutUtils::DrawBackgroundImage(gfxCo
const nsRect& aDirty,
uint32_t aImageFlags,
ExtendMode aExtendMode,
float aOpacity)
{
PROFILER_LABEL("layout", "nsLayoutUtils::DrawBackgroundImage",
js::ProfileEntry::Category::GRAPHICS);
- SVGImageContext svgContext(aImageSize, Nothing());
+ const Maybe<const SVGImageContext> svgContext(Some(SVGImageContext(aImageSize)));
/* Fast path when there is no need for image spacing */
if (aRepeatSize.width == aDest.width && aRepeatSize.height == aDest.height) {
return DrawImageInternal(aContext, aPresContext, aImage,
aSamplingFilter, aDest, aFill, aAnchor,
- aDirty, &svgContext, aImageFlags, aExtendMode,
+ aDirty, svgContext, aImageFlags, aExtendMode,
aOpacity);
}
nsPoint firstTilePos = aDest.TopLeft() +
nsPoint(NSToIntFloor(float(aFill.x - aDest.x) / aRepeatSize.width) * aRepeatSize.width,
NSToIntFloor(float(aFill.y - aDest.y) / aRepeatSize.height) * aRepeatSize.height);
for (int32_t i = firstTilePos.x; i < aFill.XMost(); i += aRepeatSize.width) {
for (int32_t j = firstTilePos.y; j < aFill.YMost(); j += aRepeatSize.height) {
nsRect dest(i, j, aDest.width, aDest.height);
DrawResult result = DrawImageInternal(aContext, aPresContext, aImage, aSamplingFilter,
- dest, dest, aAnchor, aDirty, &svgContext,
+ dest, dest, aAnchor, aDirty, svgContext,
aImageFlags, ExtendMode::CLAMP,
aOpacity);
if (result != DrawResult::SUCCESS) {
return result;
}
}
}
@@ -6729,17 +6730,19 @@ nsLayoutUtils::DrawImage(gfxContext&
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
uint32_t aImageFlags,
float aOpacity)
{
return DrawImageInternal(aContext, aPresContext, aImage,
aSamplingFilter, aDest, aFill, aAnchor,
- aDirty, nullptr, aImageFlags, ExtendMode::CLAMP,
+ aDirty,
+ /* no SVGImageContext */ Nothing(),
+ aImageFlags, ExtendMode::CLAMP,
aOpacity);
}
/* static */ nsRect
nsLayoutUtils::GetWholeImageDestination(const nsSize& aWholeImageSize,
const nsRect& aImageSourceArea,
const nsRect& aDestArea)
{
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -26,16 +26,17 @@
#include "imgIContainer.h"
#include "mozilla/gfx/2D.h"
#include "Units.h"
#include "mozilla/ToString.h"
#include "mozilla/ReflowOutput.h"
#include "ImageContainer.h"
#include "gfx2DGlue.h"
#include "nsStyleConsts.h"
+#include "SVGImageContext.h"
#include <limits>
#include <algorithm>
class nsPresContext;
class nsIContent;
class nsIAtom;
class nsIScrollableFrame;
class nsIDOMEvent;
@@ -60,17 +61,16 @@ class nsIDocument;
struct gfxPoint;
struct nsStyleFont;
struct nsStyleImageOrientation;
struct nsOverflowAreas;
namespace mozilla {
enum class CSSPseudoElementType : uint8_t;
class EventListenerManager;
-class SVGImageContext;
struct IntrinsicSize;
struct ContainerLayerParameters;
class WritingMode;
namespace dom {
class CanvasRenderingContext2D;
class DOMRectList;
class Element;
class HTMLImageElement;
@@ -155,16 +155,17 @@ public:
typedef FrameMetrics::ViewID ViewID;
typedef mozilla::CSSPoint CSSPoint;
typedef mozilla::CSSSize CSSSize;
typedef mozilla::CSSIntSize CSSIntSize;
typedef mozilla::CSSRect CSSRect;
typedef mozilla::ScreenMargin ScreenMargin;
typedef mozilla::LayoutDeviceIntSize LayoutDeviceIntSize;
typedef mozilla::StyleGeometryBox StyleGeometryBox;
+ typedef mozilla::SVGImageContext SVGImageContext;
/**
* Finds previously assigned ViewID for the given content element, if any.
* Returns whether a ViewID was previously assigned.
*/
static bool FindIDFor(const nsIContent* aContent, ViewID* aOutViewId);
/**
@@ -1859,36 +1860,40 @@ public:
* Draw a whole image without tiling.
*
* @param aRenderingContext Where to draw the image, set up with an
* appropriate scale and transform for drawing in
* app units.
* @param aImage The image.
* @param aDest The area that the image should fill.
* @param aDirty Pixels outside this area may be skipped.
- * @param aSVGContext If non-null, SVG-related rendering context
- * such as overridden attributes on the image
- * document's root <svg> node. Ignored for
- * raster images.
+ * @param aSVGContext Optionally provides an SVGImageContext.
+ * Callers should pass an SVGImageContext with at
+ * least the viewport size set if aImage may be of
+ * type imgIContainer::TYPE_VECTOR, or pass
+ * Nothing() if it is of type
+ * imgIContainer::TYPE_RASTER (to save cycles
+ * constructing an SVGImageContext, since this
+ * argument will be ignored for raster images).
* @param aImageFlags Image flags of the imgIContainer::FLAG_*
* variety.
* @param aAnchor If non-null, a point which we will ensure
* is pixel-aligned in the output.
* @param aSourceArea If non-null, this area is extracted from
* the image and drawn in aDest. It's
* in appunits. For best results it should
* be aligned with image pixels.
*/
static DrawResult DrawSingleImage(gfxContext& aContext,
nsPresContext* aPresContext,
imgIContainer* aImage,
const SamplingFilter aSamplingFilter,
const nsRect& aDest,
const nsRect& aDirty,
- const mozilla::SVGImageContext* aSVGContext,
+ const mozilla::Maybe<const SVGImageContext>& aSVGContext,
uint32_t aImageFlags,
const nsPoint* aAnchorPoint = nullptr,
const nsRect* aSourceArea = nullptr);
/**
* Given an imgIContainer, this method attempts to obtain an intrinsic
* px-valued height & width for it. If the imgIContainer has a non-pixel
* value for either height or width, this method tries to generate a pixel
--- a/layout/forms/nsRangeFrame.cpp
+++ b/layout/forms/nsRangeFrame.cpp
@@ -557,17 +557,24 @@ nsRangeFrame::GetValueAtEventPoint(Widge
nsPresContext *presContext = PresContext();
bool notUsedCanOverride;
LayoutDeviceIntSize size;
presContext->GetTheme()->
GetMinimumWidgetSize(presContext, this, NS_THEME_RANGE_THUMB, &size,
¬UsedCanOverride);
thumbSize.width = presContext->DevPixelsToAppUnits(size.width);
thumbSize.height = presContext->DevPixelsToAppUnits(size.height);
- MOZ_ASSERT(thumbSize.width > 0 && thumbSize.height > 0);
+ // For GTK, GetMinimumWidgetSize returns zero for the thumb dimension
+ // perpendicular to the orientation of the slider. That's okay since we
+ // only care about the dimension in the direction of the slider when using
+ // |thumbSize| below, but it means this assertion need to check
+ // IsHorizontal().
+ MOZ_ASSERT((IsHorizontal() && thumbSize.width > 0) ||
+ (!IsHorizontal() && thumbSize.height > 0),
+ "The thumb is expected to take up some slider space");
} else {
nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame();
if (thumbFrame) { // diplay:none?
thumbSize = thumbFrame->GetSize();
}
}
Decimal fraction;
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -27,16 +27,17 @@
#include "nsCounterManager.h"
#include "nsBidiUtils.h"
#include "CounterStyleManager.h"
#include "imgIContainer.h"
#include "ImageLayers.h"
#include "imgRequestProxy.h"
#include "nsIURI.h"
+#include "SVGImageContext.h"
#include <algorithm>
#ifdef ACCESSIBILITY
#include "nsAccessibilityService.h"
#endif
using namespace mozilla;
@@ -253,17 +254,19 @@ public:
Paint(nsRenderingContext& aRenderingContext, nsPoint aPt,
const nsRect& aDirtyRect, uint32_t aFlags,
bool aDisableSubpixelAA, nsIFrame* aFrame)
{
if (IsImageType()) {
SamplingFilter filter = nsLayoutUtils::GetSamplingFilterForFrame(aFrame);
return nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(),
aFrame->PresContext(), mImage, filter,
- mDest, aDirtyRect, nullptr, aFlags);
+ mDest, aDirtyRect,
+ /* no SVGImageContext */ Nothing(),
+ aFlags);
}
if (IsPathType()) {
DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
switch (mListStyleType) {
case NS_STYLE_LIST_STYLE_CIRCLE:
MOZ_ASSERT(mPath);
drawTarget->Stroke(mPath, ColorPattern(ToDeviceColor(mColor)));
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -73,16 +73,17 @@
#include "mozilla/StyleSetHandle.h"
#include "mozilla/StyleSetHandleInlines.h"
#include "nsBlockFrame.h"
#include "nsStyleStructInlines.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/Link.h"
+#include "SVGImageContext.h"
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::image;
using namespace mozilla::layers;
// sizes (pixels) for image icon, padding and border frame
@@ -1418,17 +1419,17 @@ nsImageFrame::DisplayAltFeedback(nsRende
if (imageStatus & imgIRequest::STATUS_LOAD_COMPLETE) {
nsCOMPtr<imgIContainer> imgCon;
request->GetImage(getter_AddRefs(imgCon));
MOZ_ASSERT(imgCon, "Load complete, but no image container?");
nsRect dest(flushRight ? inner.XMost() - size : inner.x,
inner.y, size, size);
result = nsLayoutUtils::DrawSingleImage(*gfx, PresContext(), imgCon,
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
- nullptr, aFlags);
+ /* no SVGImageContext */ Nothing(), aFlags);
}
// If we could not draw the icon, just draw some graffiti in the mean time.
if (result == DrawResult::NOT_READY) {
ColorPattern color(ToDeviceColor(Color(1.f, 0.f, 0.f, 1.f)));
nscoord iconXPos = flushRight ? inner.XMost() - size : inner.x;
@@ -1695,17 +1696,17 @@ nsImageFrame::PaintImage(nsRenderingCont
if (mForceSyncDecoding) {
flags |= imgIContainer::FLAG_SYNC_DECODE;
}
DrawResult result =
nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(),
PresContext(), aImage,
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
- nullptr, flags, &anchorPoint);
+ /* no SVGImageContext */ Nothing(), flags, &anchorPoint);
nsImageMap* map = GetImageMap();
if (map) {
gfxPoint devPixelOffset =
nsLayoutUtils::PointToGfxPoint(dest.TopLeft(),
PresContext()->AppUnitsPerDevPixel());
AutoRestoreTransform autoRestoreTransform(drawTarget);
drawTarget->SetTransform(
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -55,16 +55,17 @@
#include "mozilla/css/ImageLoader.h"
#include "ImageContainer.h"
#include "mozilla/Telemetry.h"
#include "gfxUtils.h"
#include "gfxGradientCache.h"
#include "nsInlineFrame.h"
#include "nsRubyTextContainerFrame.h"
#include <algorithm>
+#include "SVGImageContext.h"
using namespace mozilla;
using namespace mozilla::css;
using namespace mozilla::gfx;
using namespace mozilla::image;
using mozilla::CSSSizeOrRatio;
static int gFrameTreeLockCount = 0;
@@ -5987,17 +5988,17 @@ nsImageRenderer::DrawBorderImageComponen
SamplingFilter samplingFilter = nsLayoutUtils::GetSamplingFilterForFrame(mForFrame);
if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) {
return nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(),
aPresContext,
subImage,
samplingFilter,
aFill, aDirtyRect,
- nullptr,
+ /* no SVGImageContext */ Nothing(),
drawFlags);
}
nsSize repeatSize;
nsRect fillRect(aFill);
nsRect tile = ComputeTile(fillRect, aHFill, aVFill, aUnitSize, repeatSize);
CSSIntSize imageSize(srcRect.width, srcRect.height);
return nsLayoutUtils::DrawBackgroundImage(*aRenderingContext.ThebesContext(),
--- a/layout/reftests/forms/reftest.list
+++ b/layout/reftests/forms/reftest.list
@@ -1,10 +1,10 @@
fuzzy-if(skiaContent,1,10) HTTP(..) == text-control-baseline-1.html text-control-baseline-1-ref.html
-fuzzy-if(cocoaWidget,16,64) fuzzy-if(Android,52,64) fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu),104,224) fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),57,400) == display-block-baselines-1.html display-block-baselines-1-ref.html # anti-aliasing issues
+fuzzy-if(cocoaWidget,16,64) fuzzy-if(Android,52,64) fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu),104,224) fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),88,400) == display-block-baselines-1.html display-block-baselines-1-ref.html # anti-aliasing issues
== display-block-baselines-2.html display-block-baselines-2-ref.html
== display-block-baselines-3.html display-block-baselines-3-ref.html
== display-block-baselines-4.html display-block-baselines-4-ref.html
fuzzy-if(Android,4,8) == display-block-baselines-5.html display-block-baselines-5-ref.html
# button element
include button/reftest.list
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -961,17 +961,25 @@ SheetLoadData::OnStreamComplete(nsIUnich
}
} else {
nsAutoCString sourceUri;
if (mLoader->mDocument && mLoader->mDocument->GetDocumentURI()) {
mLoader->mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
}
nsresult rv = SRICheck::VerifyIntegrity(sriMetadata, aLoader, aBuffer,
sourceUri, mLoader->mReporter);
- mLoader->mReporter->FlushConsoleReports(mLoader->mDocument);
+
+ nsCOMPtr<nsILoadGroup> loadGroup;
+ channel->GetLoadGroup(getter_AddRefs(loadGroup));
+ if (loadGroup) {
+ mLoader->mReporter->FlushConsoleReports(loadGroup);
+ } else {
+ mLoader->mReporter->FlushConsoleReports(mLoader->mDocument);
+ }
+
if (NS_FAILED(rv)) {
LOG((" Load was blocked by SRI"));
MOZ_LOG(gSriPRLog, mozilla::LogLevel::Debug,
("css::Loader::OnStreamComplete, bad metadata"));
mLoader->SheetComplete(this, NS_ERROR_SRI_CORRUPT);
return NS_OK;
}
}
--- a/layout/svg/SVGContextPaint.cpp
+++ b/layout/svg/SVGContextPaint.cpp
@@ -1,15 +1,16 @@
/* 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 "SVGContextPaint.h"
#include "gfxContext.h"
+#include "gfxUtils.h"
#include "mozilla/gfx/2D.h"
#include "nsIDocument.h"
#include "nsSVGPaintServerFrame.h"
#include "nsSVGEffects.h"
#include "nsSVGPaintServerFrame.h"
using namespace mozilla::gfx;
@@ -137,16 +138,27 @@ SVGContextPaint::InitStrokeGeometry(gfxC
SVGContextPaint::GetContextPaint(nsIContent* aContent)
{
nsIDocument* ownerDoc = aContent->OwnerDoc();
if (!ownerDoc->IsBeingUsedAsImage()) {
return nullptr;
}
+ // XXX The SVGContextPaint that was passed to SetProperty was const. Ideally
+ // we could and should re-apply that constness to the SVGContextPaint that
+ // we get here (SVGImageContext is never changed after it is initialized).
+ // Unfortunately lazy initialization of SVGContextPaint (which is a member of
+ // SVGImageContext, and also conceptually never changes after construction)
+ // prevents some of SVGContextPaint's conceptually const methods from being
+ // const. Trying to fix SVGContextPaint (perhaps by using |mutable|) is a
+ // bit of a headache so for now we punt on that, don't reapply the constness
+ // to the SVGContextPaint here, and trust that no one will add code that
+ // actually modifies the object.
+
return static_cast<SVGContextPaint*>(
ownerDoc->GetProperty(nsGkAtoms::svgContextPaint));
}
already_AddRefed<gfxPattern>
SVGContextPaintImpl::GetFillPattern(const DrawTarget* aDrawTarget,
float aOpacity,
const gfxMatrix& aCTM)
@@ -224,17 +236,17 @@ SVGContextPaintImpl::Paint::GetPattern(c
return nullptr;
}
mPatternCache.Put(aOpacity, pattern);
return pattern.forget();
}
AutoSetRestoreSVGContextPaint::AutoSetRestoreSVGContextPaint(
- SVGContextPaint* aContextPaint,
+ const SVGContextPaint* aContextPaint,
nsIDocument* aSVGDocument)
: mSVGDocument(aSVGDocument)
, mOuterContextPaint(aSVGDocument->GetProperty(nsGkAtoms::svgContextPaint))
{
// The way that we supply context paint is to temporarily set the context
// paint on the owner document of the SVG that we're painting while it's
// being painted.
@@ -242,25 +254,58 @@ AutoSetRestoreSVGContextPaint::AutoSetRe
MOZ_ASSERT(aSVGDocument->IsBeingUsedAsImage(),
"SVGContextPaint::GetContextPaint assumes this");
if (mOuterContextPaint) {
mSVGDocument->UnsetProperty(nsGkAtoms::svgContextPaint);
}
DebugOnly<nsresult> res =
- mSVGDocument->SetProperty(nsGkAtoms::svgContextPaint, aContextPaint);
+ mSVGDocument->SetProperty(nsGkAtoms::svgContextPaint,
+ const_cast<SVGContextPaint*>(aContextPaint));
NS_WARNING_ASSERTION(NS_SUCCEEDED(res), "Failed to set context paint");
}
AutoSetRestoreSVGContextPaint::~AutoSetRestoreSVGContextPaint()
{
mSVGDocument->UnsetProperty(nsGkAtoms::svgContextPaint);
if (mOuterContextPaint) {
DebugOnly<nsresult> res =
mSVGDocument->SetProperty(nsGkAtoms::svgContextPaint, mOuterContextPaint);
NS_WARNING_ASSERTION(NS_SUCCEEDED(res), "Failed to restore context paint");
}
}
+
+// SVGEmbeddingContextPaint
+
+void
+SVGEmbeddingContextPaint::SetFill(nscolor aFill)
+{
+ mFill = new gfxPattern(ToDeviceColor(aFill));
+}
+
+void
+SVGEmbeddingContextPaint::SetStroke(nscolor aStroke)
+{
+ mStroke = new gfxPattern(ToDeviceColor(aStroke));
+}
+
+uint32_t
+SVGEmbeddingContextPaint::Hash() const
+{
+ uint32_t hash = 0;
+
+ Color color;
+
+ if (mFill && mFill->GetSolidColor(color)) {
+ hash = HashGeneric(hash, color.ToABGR());
+ }
+ if (mStroke && mStroke->GetSolidColor(color)) {
+ hash = HashGeneric(hash, color.ToABGR());
+ }
+
+ return hash;
+}
+
} // namespace mozilla
--- a/layout/svg/SVGContextPaint.h
+++ b/layout/svg/SVGContextPaint.h
@@ -6,17 +6,19 @@
#ifndef MOZILLA_SVGCONTEXTPAINT_H_
#define MOZILLA_SVGCONTEXTPAINT_H_
#include "DrawMode.h"
#include "gfxMatrix.h"
#include "gfxPattern.h"
#include "gfxTypes.h"
#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Assertions.h"
#include "mozilla/gfx/2D.h"
+#include "nsColor.h"
#include "nsStyleStruct.h"
#include "nsTArray.h"
class gfxContext;
class nsIDocument;
class nsSVGPaintServerFrame;
namespace mozilla {
@@ -27,25 +29,36 @@ namespace mozilla {
* keywords. See:
*
* https://www.w3.org/TR/SVG2/painting.html#context-paint
*
* This feature allows the color in an SVG-in-OpenType glyph to come from the
* computed style for the text that is being drawn, for example, or for color
* in an SVG embedded by an <img> element to come from the embedding <img>
* element.
+ *
+ * This class is reference counted so that it can be shared among many similar
+ * SVGImageContext objects. (SVGImageContext objects are frequently
+ * copy-constructed with small modifications, and we'd like for those copies to
+ * be able to share their context-paint data cheaply.) However, in most cases,
+ * SVGContextPaint instances are stored in a local RefPtr and only last for the
+ * duration of a function call.
+ * XXX Note: SVGImageContext doesn't actually have a SVGContextPaint member yet,
+ * but it will in a later patch in the patch series that added this comment.
*/
-class SVGContextPaint
+class SVGContextPaint : public RefCounted<SVGContextPaint>
{
protected:
typedef mozilla::gfx::DrawTarget DrawTarget;
SVGContextPaint() {}
public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(SVGContextPaint)
+
virtual ~SVGContextPaint() {}
virtual already_AddRefed<gfxPattern> GetFillPattern(const DrawTarget* aDrawTarget,
float aOpacity,
const gfxMatrix& aCTM) = 0;
virtual already_AddRefed<gfxPattern> GetStrokePattern(const DrawTarget* aDrawTarget,
float aOpacity,
const gfxMatrix& aCTM) = 0;
@@ -76,16 +89,23 @@ public:
gfxFloat GetStrokeDashOffset() {
return mDashOffset;
}
gfxFloat GetStrokeWidth() {
return mStrokeWidth;
}
+ virtual uint32_t Hash() const {
+ MOZ_ASSERT_UNREACHABLE("Only VectorImage needs to hash, and that should "
+ "only be operating on our SVGEmbeddingContextPaint "
+ "subclass");
+ return 0;
+ }
+
private:
// Member-vars are initialized in InitStrokeGeometry.
FallibleTArray<gfxFloat> mDashes;
MOZ_INIT_OUTSIDE_CTOR gfxFloat mDashOffset;
MOZ_INIT_OUTSIDE_CTOR gfxFloat mStrokeWidth;
};
/**
@@ -93,17 +113,17 @@ private:
* piece of SVG is being painted. The context paint is set on the SVG's owner
* document, as expected by SVGContextPaint::GetContextPaint. Any pre-existing
* context paint is restored after this class removes the context paint that it
* set.
*/
class MOZ_RAII AutoSetRestoreSVGContextPaint
{
public:
- AutoSetRestoreSVGContextPaint(SVGContextPaint* aContextPaint,
+ AutoSetRestoreSVGContextPaint(const SVGContextPaint* aContextPaint,
nsIDocument* aSVGDocument);
~AutoSetRestoreSVGContextPaint();
private:
nsIDocument* mSVGDocument;
// The context paint that needs to be restored by our dtor after it removes
// aContextPaint:
void* mOuterContextPaint;
};
@@ -186,12 +206,56 @@ public:
Paint mFillPaint;
Paint mStrokePaint;
float mFillOpacity;
float mStrokeOpacity;
};
+/**
+ * This class is used to pass context paint to an SVG image when an element
+ * references that image (e.g. via HTML <img> or SVG <image>, or by referencing
+ * it from a CSS property such as 'background-image'). In this case we only
+ * support context colors and not paint servers.
+ */
+class SVGEmbeddingContextPaint : public SVGContextPaint
+{
+public:
+ SVGEmbeddingContextPaint() {}
+
+ void SetFill(nscolor aFill);
+ void SetStroke(nscolor aStroke);
+
+ already_AddRefed<gfxPattern> GetFillPattern(const DrawTarget* aDrawTarget,
+ float aOpacity,
+ const gfxMatrix& aCTM) override {
+ return do_AddRef(mFill);
+ }
+
+ already_AddRefed<gfxPattern> GetStrokePattern(const DrawTarget* aDrawTarget,
+ float aOpacity,
+ const gfxMatrix& aCTM) override {
+ return do_AddRef(mStroke);
+ }
+
+ float GetFillOpacity() const override {
+ // Always 1.0f since we don't currently allow 'context-fill-opacity'
+ return 1.0f;
+ };
+
+ float GetStrokeOpacity() const override {
+ // Always 1.0f since we don't currently allow 'context-stroke-opacity'
+ return 1.0f;
+ };
+
+ uint32_t Hash() const override;
+
+private:
+ // Note: if these are set at all, they'll have type PatternType::COLOR.
+ RefPtr<gfxPattern> mFill;
+ RefPtr<gfxPattern> mStroke;
+};
+
} // namespace mozilla
#endif // MOZILLA_SVGCONTEXTPAINT_H_
--- a/layout/svg/SVGGeometryFrame.cpp
+++ b/layout/svg/SVGGeometryFrame.cpp
@@ -279,26 +279,19 @@ SVGGeometryFrame::BuildDisplayList(nsDis
DrawResult
SVGGeometryFrame::PaintSVG(gfxContext& aContext,
const gfxMatrix& aTransform,
const nsIntRect* aDirtyRect)
{
if (!StyleVisibility()->IsVisible())
return DrawResult::SUCCESS;
- gfxMatrix childToUserSpace = aTransform;
- if (GetContent()->IsSVGElement()) {
- childToUserSpace = static_cast<const nsSVGElement*>(GetContent())->
- PrependLocalTransformsTo(childToUserSpace,
- eChildToUserSpace);
- }
-
// Matrix to the geometry's user space:
gfxMatrix newMatrix =
- aContext.CurrentMatrix().PreMultiply(childToUserSpace).NudgeToIntegers();
+ aContext.CurrentMatrix().PreMultiply(aTransform).NudgeToIntegers();
if (newMatrix.IsSingular()) {
return DrawResult::BAD_ARGS;
}
uint32_t paintOrder = StyleSVG()->mPaintOrder;
if (paintOrder == NS_STYLE_PAINT_ORDER_NORMAL) {
Render(&aContext, eRenderFill | eRenderStroke, newMatrix);
PaintMarkers(aContext, aTransform);
new file mode 100644
--- /dev/null
+++ b/layout/svg/SVGImageContext.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+// Main header first:
+#include "SVGImageContext.h"
+
+// Keep others in (case-insensitive) order:
+#include "gfxUtils.h"
+#include "mozilla/Preferences.h"
+#include "nsPresContext.h"
+
+namespace mozilla {
+
+bool
+SVGImageContext::MaybeStoreContextPaint(nsIFrame* aFromFrame)
+{
+ static bool sEnabledForContent = false;
+ static bool sEnabledForContentCached = false;
+
+ if (!sEnabledForContentCached) {
+ Preferences::AddBoolVarCache(&sEnabledForContent,
+ "svg.context-properties.content.enabled", false);
+ sEnabledForContentCached = true;
+ }
+
+ if (!sEnabledForContent &&
+ !aFromFrame->PresContext()->IsChrome()) {
+ // Context paint is pref'ed off for content and this is a content doc.
+ return false;
+ }
+
+ // XXX return early if the 'context-properties' property is not set.
+
+ bool haveContextPaint = false;
+
+ RefPtr<SVGEmbeddingContextPaint> contextPaint = new SVGEmbeddingContextPaint();
+
+ const nsStyleSVG* style = aFromFrame->StyleSVG();
+
+ // XXX don't set values for properties not listed in 'context-properties'.
+
+ if (style->mFill.Type() == eStyleSVGPaintType_Color) {
+ haveContextPaint = true;
+ contextPaint->SetFill(style->mFill.GetColor());
+ }
+ if (style->mStroke.Type() == eStyleSVGPaintType_Color) {
+ haveContextPaint = true;
+ contextPaint->SetStroke(style->mStroke.GetColor());
+ }
+
+ if (haveContextPaint) {
+ mContextPaint = contextPaint.forget();
+ }
+
+ return mContextPaint != nullptr;
+}
+
+} // namespace mozilla
--- a/layout/svg/SVGImageContext.h
+++ b/layout/svg/SVGImageContext.h
@@ -2,86 +2,109 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_SVGCONTEXT_H_
#define MOZILLA_SVGCONTEXT_H_
#include "mozilla/Maybe.h"
+#include "mozilla/SVGContextPaint.h"
#include "SVGPreserveAspectRatio.h"
#include "Units.h"
+class nsIFrame;
+
namespace mozilla {
// SVG image-specific rendering context. For imgIContainer::Draw.
// Used to pass information such as
// - viewport information from CSS, and
// - overridden attributes from an SVG <image> element
// to the image's internal SVG document when it's drawn.
class SVGImageContext
{
public:
SVGImageContext()
: mGlobalOpacity(1.0)
{ }
// Note: 'aIsPaintingSVGImageElement' should be used to indicate whether
// the SVG image in question is being painted for an SVG <image> element.
- SVGImageContext(CSSIntSize aViewportSize,
- Maybe<SVGPreserveAspectRatio> aPreserveAspectRatio,
- gfxFloat aOpacity = 1.0,
- bool aIsPaintingSVGImageElement = false)
+ explicit SVGImageContext(const CSSIntSize& aViewportSize,
+ Maybe<SVGPreserveAspectRatio> aPreserveAspectRatio = Nothing(),
+ gfxFloat aOpacity = 1.0,
+ bool aIsPaintingSVGImageElement = false)
: mViewportSize(aViewportSize)
, mPreserveAspectRatio(aPreserveAspectRatio)
, mGlobalOpacity(aOpacity)
, mIsPaintingSVGImageElement(aIsPaintingSVGImageElement)
{ }
+ bool MaybeStoreContextPaint(nsIFrame* aFromFrame);
+
const CSSIntSize& GetViewportSize() const {
return mViewportSize;
}
+ void SetViewportSize(const CSSIntSize& aSize) {
+ mViewportSize = aSize;
+ }
+
const Maybe<SVGPreserveAspectRatio>& GetPreserveAspectRatio() const {
return mPreserveAspectRatio;
}
+ void SetPreserveAspectRatio(const Maybe<SVGPreserveAspectRatio>& aPAR) {
+ mPreserveAspectRatio = aPAR;
+ }
+
gfxFloat GetGlobalOpacity() const {
return mGlobalOpacity;
}
+ const SVGContextPaint* GetContextPaint() const {
+ return mContextPaint.get();
+ }
+
bool IsPaintingForSVGImageElement() const {
return mIsPaintingSVGImageElement;
}
bool operator==(const SVGImageContext& aOther) const {
return mViewportSize == aOther.mViewportSize &&
mPreserveAspectRatio == aOther.mPreserveAspectRatio &&
mGlobalOpacity == aOther.mGlobalOpacity &&
mIsPaintingSVGImageElement == aOther.mIsPaintingSVGImageElement;
}
bool operator!=(const SVGImageContext& aOther) const {
return !(*this == aOther);
}
uint32_t Hash() const {
- return HashGeneric(mViewportSize.width,
+ uint32_t hash = 0;
+ if (mContextPaint) {
+ hash = HashGeneric(hash, mContextPaint->Hash());
+ }
+ return HashGeneric(hash,
+ mViewportSize.width,
mViewportSize.height,
mPreserveAspectRatio.map(HashPAR).valueOr(0),
- HashBytes(&mGlobalOpacity, sizeof(gfxFloat)),
+ HashBytes(&mGlobalOpacity, sizeof(mGlobalOpacity)),
mIsPaintingSVGImageElement);
}
private:
static uint32_t HashPAR(const SVGPreserveAspectRatio& aPAR) {
return aPAR.Hash();
}
// NOTE: When adding new member-vars, remember to update Hash() & operator==.
+ RefPtr<SVGContextPaint> mContextPaint;
CSSIntSize mViewportSize;
Maybe<SVGPreserveAspectRatio> mPreserveAspectRatio;
gfxFloat mGlobalOpacity;
bool mIsPaintingSVGImageElement;
};
} // namespace mozilla
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -3652,20 +3652,20 @@ SVGTextFrame::PaintSVG(gfxContext& aCont
// Determine how much of the left and right edges of the text frame we
// need to ignore.
SVGCharClipDisplayItem item(run);
// Set up the fill and stroke so that SVG glyphs can get painted correctly
// when they use context-fill etc.
aContext.SetMatrix(initialMatrix);
- SVGContextPaintImpl contextPaint;
- DrawMode drawMode = contextPaint.Init(&aDrawTarget,
- aContext.CurrentMatrix(),
- frame, outerContextPaint);
+ RefPtr<SVGContextPaintImpl> contextPaint = new SVGContextPaintImpl();
+ DrawMode drawMode = contextPaint->Init(&aDrawTarget,
+ aContext.CurrentMatrix(),
+ frame, outerContextPaint);
if (drawMode & DrawMode::GLYPH_STROKE) {
// This may change the gfxContext's transform (for non-scaling stroke),
// in which case this needs to happen before we call SetMatrix() below.
nsSVGUtils::SetupCairoStrokeGeometry(frame, &aContext, outerContextPaint);
}
// Set up the transform for painting the text frame for the substring
@@ -3676,17 +3676,17 @@ SVGTextFrame::PaintSVG(gfxContext& aCont
aContext.SetMatrix(runTransform);
if (drawMode != DrawMode(0)) {
bool paintSVGGlyphs;
nsTextFrame::PaintTextParams params(rendCtx.ThebesContext());
params.framePt = gfxPoint();
params.dirtyRect = LayoutDevicePixel::
FromAppUnits(frame->GetVisualOverflowRect(), auPerDevPx);
- params.contextPaint = &contextPaint;
+ params.contextPaint = contextPaint;
if (ShouldRenderAsPath(frame, paintSVGGlyphs)) {
SVGTextDrawPathCallbacks callbacks(&rendCtx, frame,
matrixForPaintServers,
paintSVGGlyphs);
params.callbacks = &callbacks;
frame->PaintText(params, item);
} else {
frame->PaintText(params, item);
--- a/layout/svg/moz.build
+++ b/layout/svg/moz.build
@@ -55,16 +55,17 @@ UNIFIED_SOURCES += [
'nsSVGUseFrame.cpp',
'nsSVGUtils.cpp',
'SVGContextPaint.cpp',
'SVGFEContainerFrame.cpp',
'SVGFEImageFrame.cpp',
'SVGFELeafFrame.cpp',
'SVGFEUnstyledLeafFrame.cpp',
'SVGGeometryFrame.cpp',
+ 'SVGImageContext.cpp',
'SVGTextFrame.cpp',
'SVGViewFrame.cpp',
]
if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']:
SOURCES += ['nsSVGMaskFrameNEON.cpp']
SOURCES['nsSVGMaskFrameNEON.cpp'].flags += CONFIG['NEON_FLAGS']
--- a/layout/svg/nsSVGImageFrame.cpp
+++ b/layout/svg/nsSVGImageFrame.cpp
@@ -397,19 +397,20 @@ nsSVGImageFrame::PaintSVG(gfxContext& aC
if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
// Package up the attributes of this image element which can override the
// attributes of mImageContainer's internal SVG document. The 'width' &
// 'height' values we're passing in here are in CSS units (though they
// come from width/height *attributes* in SVG). They influence the region
// of the SVG image's internal document that is visible, in combination
// with preserveAspectRatio and viewBox.
- SVGImageContext context(CSSIntSize::Truncate(width, height),
- Some(imgElem->mPreserveAspectRatio.GetAnimValue()),
- 1.0, true);
+ const Maybe<const SVGImageContext> context(
+ Some(SVGImageContext(CSSIntSize::Truncate(width, height),
+ Some(imgElem->mPreserveAspectRatio.GetAnimValue()),
+ 1.0, /* aIsPaintingSVGImageElement */ true)));
// For the actual draw operation to draw crisply (and at the right size),
// our destination rect needs to be |width|x|height|, *in dev pixels*.
LayoutDeviceSize devPxSize(width, height);
nsRect destRect(nsPoint(),
LayoutDevicePixel::ToAppUnits(devPxSize,
appUnitsPerDevPx));
@@ -418,17 +419,17 @@ nsSVGImageFrame::PaintSVG(gfxContext& aC
// and that's not always true for TYPE_VECTOR images.
result = nsLayoutUtils::DrawSingleImage(
aContext,
PresContext(),
mImageContainer,
nsLayoutUtils::GetSamplingFilterForFrame(this),
destRect,
aDirtyRect ? dirtyRect : destRect,
- &context,
+ context,
drawFlags);
} else { // mImageContainer->GetType() == TYPE_RASTER
result = nsLayoutUtils::DrawSingleUnscaledImage(
aContext,
PresContext(),
mImageContainer,
nsLayoutUtils::GetSamplingFilterForFrame(this),
nsPoint(0, 0),
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -53,16 +53,17 @@
#include "nsTextFrame.h"
#include "SVGContentUtils.h"
#include "SVGTextFrame.h"
#include "mozilla/Unused.h"
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::gfx;
+using namespace mozilla::image;
static bool sSVGPathCachingEnabled;
static bool sSVGDisplayListHitTestingEnabled;
static bool sSVGDisplayListPaintingEnabled;
static bool sSVGNewGetBBoxEnabled;
bool
NS_SVGPathCachingEnabled()
--- a/layout/svg/nsSVGUtils.h
+++ b/layout/svg/nsSVGUtils.h
@@ -5,16 +5,17 @@
#ifndef NS_SVGUTILS_H
#define NS_SVGUTILS_H
// include math.h to pick up definition of M_ maths defines e.g. M_PI
#include <math.h>
#include "DrawMode.h"
+#include "DrawResult.h"
#include "gfx2DGlue.h"
#include "gfxMatrix.h"
#include "gfxPoint.h"
#include "gfxRect.h"
#include "mozilla/gfx/Rect.h"
#include "nsAlgorithm.h"
#include "nsChangeHint.h"
#include "nsColor.h"
--- a/layout/xul/nsImageBoxFrame.cpp
+++ b/layout/xul/nsImageBoxFrame.cpp
@@ -47,16 +47,17 @@
#include "ImageContainer.h"
#include "nsIContent.h"
#include "nsContentUtils.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/Maybe.h"
+#include "SVGImageContext.h"
#define ONLOAD_CALLED_TOO_EARLY 1
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::image;
using namespace mozilla::layers;
@@ -404,17 +405,18 @@ nsImageBoxFrame::PaintImage(nsRenderingC
anchorPoint.ptr());
}
return nsLayoutUtils::DrawSingleImage(
*aRenderingContext.ThebesContext(),
PresContext(), imgCon,
nsLayoutUtils::GetSamplingFilterForFrame(this),
- dest, dirty, nullptr, aFlags,
+ dest, dirty,
+ /* no SVGImageContext */ Nothing(), aFlags,
anchorPoint.ptrOr(nullptr),
hasSubRect ? &mSubRect : nullptr);
}
void nsDisplayXULImage::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx)
{
// Even though we call StartDecoding when we get a new image we pass
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -2747,36 +2747,43 @@ HttpBaseChannel::AddConsoleReport(uint32
{
mReportCollector->AddConsoleReport(aErrorFlags, aCategory, aPropertiesFile,
aSourceFileURI, aLineNumber,
aColumnNumber, aMessageName,
aStringParams);
}
void
+HttpBaseChannel::FlushReportsToConsole(uint64_t aInnerWindowID,
+ ReportAction aAction)
+{
+ mReportCollector->FlushReportsToConsole(aInnerWindowID, aAction);
+}
+
+void
HttpBaseChannel::FlushConsoleReports(nsIDocument* aDocument,
ReportAction aAction)
{
mReportCollector->FlushConsoleReports(aDocument, aAction);
}
void
+HttpBaseChannel::FlushConsoleReports(nsILoadGroup* aLoadGroup,
+ ReportAction aAction)
+{
+ mReportCollector->FlushConsoleReports(aLoadGroup, aAction);
+}
+
+void
HttpBaseChannel::FlushConsoleReports(nsIConsoleReportCollector* aCollector)
{
mReportCollector->FlushConsoleReports(aCollector);
}
void
-HttpBaseChannel::FlushReportsByWindowId(uint64_t aWindowId,
- ReportAction aAction)
-{
- mReportCollector->FlushReportsByWindowId(aWindowId, aAction);
-}
-
-void
HttpBaseChannel::ClearConsoleReports()
{
mReportCollector->ClearConsoleReports();
}
nsIPrincipal *
HttpBaseChannel::GetURIPrincipal()
{
@@ -2933,21 +2940,25 @@ HttpBaseChannel::DoNotifyListener()
DoNotifyListenerCleanup();
// If this is a navigation, then we must let the docshell flush the reports
// to the console later. The LoadDocument() is pointing at the detached
// document that started the navigation. We want to show the reports on the
// new document. Otherwise the console is wiped and the user never sees
// the information.
- if (!IsNavigation() && mLoadInfo) {
- nsCOMPtr<nsIDOMDocument> dommyDoc;
- mLoadInfo->GetLoadingDocument(getter_AddRefs(dommyDoc));
- nsCOMPtr<nsIDocument> doc = do_QueryInterface(dommyDoc);
- FlushConsoleReports(doc);
+ if (!IsNavigation()) {
+ if (mLoadGroup) {
+ FlushConsoleReports(mLoadGroup);
+ } else if (mLoadInfo) {
+ nsCOMPtr<nsIDOMDocument> dommyDoc;
+ mLoadInfo->GetLoadingDocument(getter_AddRefs(dommyDoc));
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(dommyDoc);
+ FlushConsoleReports(doc);
+ }
}
}
void
HttpBaseChannel::AddCookiesToRequest()
{
if (mLoadFlags & LOAD_ANONYMOUS) {
return;
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -273,27 +273,31 @@ public:
AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory,
nsContentUtils::PropertiesFile aPropertiesFile,
const nsACString& aSourceFileURI,
uint32_t aLineNumber, uint32_t aColumnNumber,
const nsACString& aMessageName,
const nsTArray<nsString>& aStringParams) override;
void
+ FlushReportsToConsole(uint64_t aInnerWindowID,
+ ReportAction aAction = ReportAction::Forget) override;
+
+ void
FlushConsoleReports(nsIDocument* aDocument,
ReportAction aAction = ReportAction::Forget) override;
void
+ FlushConsoleReports(nsILoadGroup* aLoadGroup,
+ ReportAction aAction = ReportAction::Forget) override;
+
+ void
FlushConsoleReports(nsIConsoleReportCollector* aCollector) override;
void
- FlushReportsByWindowId(uint64_t aWindowId,
- ReportAction aAction = ReportAction::Forget) override;
-
- void
ClearConsoleReports() override;
class nsContentEncodings : public nsIUTF8StringEnumerator
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIUTF8STRINGENUMERATOR
--- a/security/nss/Makefile
+++ b/security/nss/Makefile
@@ -91,25 +91,16 @@ endif
ifdef CCC
NSPR_CONFIGURE_ENV += CXX=$(CCC)
endif
# Remove -arch definitions. NSPR can't handle that.
NSPR_CONFIGURE_ENV := $(filter-out -arch x86_64,$(NSPR_CONFIGURE_ENV))
NSPR_CONFIGURE_ENV := $(filter-out -arch i386,$(NSPR_CONFIGURE_ENV))
NSPR_CONFIGURE_ENV := $(filter-out -arch ppc,$(NSPR_CONFIGURE_ENV))
-ifdef SANITIZER_CFLAGS
-ifdef BUILD_OPT
-NSPR_CONFIGURE_OPTS += --enable-debug-symbols
-endif
-NSPR_CONFIGURE_ENV += CFLAGS='$(SANITIZER_CFLAGS)' \
- CXXFLAGS='$(SANITIZER_CFLAGS)' \
- LDFLAGS='$(SANITIZER_LDFLAGS)'
-endif
-
#
# Some pwd commands on Windows (for example, the pwd
# command in Cygwin) return a pathname that begins
# with a (forward) slash. When such a pathname is
# passed to Windows build tools (for example, cl), it
# is mistaken as a command-line option. If that is the case,
# we use a relative pathname as NSPR's prefix on Windows.
#
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-0a7ba014dbb3
+0750d7a0402b
--- a/security/nss/automation/taskcluster/graph/src/extend.js
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -13,18 +13,17 @@ const WINDOWS_CHECKOUT_CMD =
"(sleep 2; hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss) || " +
"(sleep 5; hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss)\"";
/*****************************************************************************/
queue.filter(task => {
if (task.group == "Builds") {
// Remove extra builds on {A,UB}San and ARM.
- if (task.collection == "asan" || task.collection == "arm-debug" ||
- task.collection == "gyp-asan") {
+ if (task.collection == "asan" || task.collection == "arm-debug") {
return false;
}
// Remove extra builds w/o libpkix for non-linux64-debug.
if (task.symbol == "noLibpkix" &&
(task.platform != "linux64" || task.collection != "debug")) {
return false;
}
@@ -43,26 +42,26 @@ queue.filter(task => {
}
// Temporarily disable SSL tests on ARM.
if (task.tests == "ssl" && task.collection == "arm-debug") {
return false;
}
// GYP builds with -Ddisable_libpkix=1 by default.
- if ((task.collection == "gyp" || task.collection == "gyp-asan") &&
+ if ((task.collection == "gyp" || task.collection == "asan") &&
task.tests == "chains") {
return false;
}
return true;
});
queue.map(task => {
- if (task.collection == "asan" || task.collection == "gyp-asan") {
+ if (task.collection == "asan") {
// CRMF and FIPS tests still leak, unfortunately.
if (task.tests == "crmf" || task.tests == "fips") {
task.env.ASAN_OPTIONS = "detect_leaks=0";
}
}
if (task.collection == "arm-debug") {
// These tests take quite some time on our poor ARM devices.
@@ -113,44 +112,28 @@ export default async function main() {
"-c",
"bin/checkout.sh && nss/automation/taskcluster/scripts/build_gyp.sh"
],
platform: "linux64",
collection: "gyp",
image: LINUX_IMAGE
});
- await scheduleLinux("Linux 64 (debug, gyp, asan, ubsan)", {
+ await scheduleLinux("Linux 64 (GYP, ASan, debug)", {
command: [
"/bin/bash",
"-c",
"bin/checkout.sh && nss/automation/taskcluster/scripts/build_gyp.sh -g -v --ubsan --asan"
],
env: {
UBSAN_OPTIONS: "print_stacktrace=1",
NSS_DISABLE_ARENA_FREE_LIST: "1",
NSS_DISABLE_UNLOAD: "1",
CC: "clang",
- CCC: "clang++"
- },
- platform: "linux64",
- collection: "gyp-asan",
- image: LINUX_IMAGE
- });
-
- await scheduleLinux("Linux 64 (ASan, debug)", {
- env: {
- UBSAN_OPTIONS: "print_stacktrace=1",
- NSS_DISABLE_ARENA_FREE_LIST: "1",
- NSS_DISABLE_UNLOAD: "1",
- CC: "clang",
CCC: "clang++",
- USE_UBSAN: "1",
- USE_ASAN: "1",
- USE_64: "1"
},
platform: "linux64",
collection: "asan",
image: LINUX_IMAGE
});
await scheduleWindows("Windows 2012 64 (opt)", {
env: {BUILD_OPT: "1"}
--- a/security/nss/automation/taskcluster/graph/src/try_syntax.js
+++ b/security/nss/automation/taskcluster/graph/src/try_syntax.js
@@ -18,17 +18,17 @@ function parseOptions(opts) {
// If the given value is nonsense default to debug and opt builds.
if (builds.length == 0) {
builds = ["d", "o"];
}
// Parse platforms.
let allPlatforms = ["linux", "linux64", "linux64-asan", "win64", "arm",
- "linux64-gyp", "linux64-gyp-asan", "linux64-fuzz"];
+ "linux64-gyp", "linux64-fuzz"];
let platforms = intersect(opts.platform.split(/\s*,\s*/), allPlatforms);
// If the given value is nonsense or "none" default to all platforms.
if (platforms.length == 0 && opts.platform != "none") {
platforms = allPlatforms;
}
// Parse unit tests.
@@ -103,33 +103,30 @@ function filter(opts) {
// Filter by platform.
let found = opts.platforms.some(platform => {
let aliases = {
"linux": "linux32",
"linux64-asan": "linux64",
"linux64-fuzz": "linux64",
"linux64-gyp": "linux64",
- "linux64-gyp-asan": "linux64",
"win64": "windows2012-64",
"arm": "linux32"
};
// Check the platform name.
let keep = (task.platform == (aliases[platform] || platform));
// Additional checks.
if (platform == "linux64-asan") {
keep &= coll("asan");
} else if (platform == "arm") {
keep &= coll("arm-opt") || coll("arm-debug");
} else if (platform == "linux64-gyp") {
keep &= coll("gyp");
- } else if (platform == "linux64-gyp-asan") {
- keep &= coll("gyp-asan");
} else if (platform == "linux64-fuzz") {
keep &= coll("fuzz");
} else {
keep &= coll("opt") || coll("debug");
}
return keep;
});
--- a/security/nss/cmd/selfserv/selfserv.c
+++ b/security/nss/cmd/selfserv/selfserv.c
@@ -154,17 +154,17 @@ static PRLogModuleInfo *lm;
fflush(stderr); \
}
#define VLOG(arg) PR_LOG(lm, PR_LOG_DEBUG, arg)
static void
PrintUsageHeader(const char *progName)
{
fprintf(stderr,
- "Usage: %s -n rsa_nickname -p port [-BDENRbjlmrsuvx] [-w password]\n"
+ "Usage: %s -n rsa_nickname -p port [-BDENRZbjlmrsuvx] [-w password]\n"
" [-t threads] [-i pid_file] [-c ciphers] [-Y] [-d dbdir] [-g numblocks]\n"
" [-f password_file] [-L [seconds]] [-M maxProcs] [-P dbprefix]\n"
" [-V [min-version]:[max-version]] [-a sni_name]\n"
" [ T <good|revoked|unknown|badsig|corrupted|none|ocsp>] [-A ca]\n"
" [-C SSLCacheEntries] [-S dsa_nickname] -Q [-I groups]"
#ifndef NSS_DISABLE_ECC
" [-e ec_nickname]"
#endif /* NSS_DISABLE_ECC */
@@ -214,25 +214,26 @@ PrintParameterUsage()
" badsig: use a good status but with an invalid signature\n"
" corrupted: stapled cert status is an invalid block of data\n"
" random: each connection uses a random status from this list:\n"
" good, revoked, unknown, failure, badsig, corrupted\n"
" ocsp: fetch from external OCSP server using AIA, or none\n"
"-A <ca> Nickname of a CA used to sign a stapled cert status\n"
"-U override default ECDHE ephemeral key reuse, 0: refresh, 1: reuse\n"
"-H override default DHE server support, 0: disable, 1: enable, "
- " 2: require DH named groups\n"
+ " 2: require DH named groups\n"
"-W override default DHE server weak parameters support, 0: disable, 1: enable\n"
"-c Restrict ciphers\n"
"-Y prints cipher values allowed for parameter -c and exits\n"
"-G enables the extended master secret extension [RFC7627]\n"
"-Q enables ALPN for HTTP/1.1 [RFC7301]\n"
"-I comma separated list of enabled groups for TLS key exchange.\n"
" The following values are valid:\n"
- " P256, P384, P521, x25519, FF2048, FF3072, FF4096, FF6144, FF8192\n",
+ " P256, P384, P521, x25519, FF2048, FF3072, FF4096, FF6144, FF8192\n"
+ "-Z enable 0-RTT (for TLS 1.3; also use -u)\n",
stderr);
}
static void
Usage(const char *progName)
{
PrintUsageHeader(progName);
PrintParameterUsage();
--- a/security/nss/coreconf/Darwin.mk
+++ b/security/nss/coreconf/Darwin.mk
@@ -140,11 +140,8 @@ SYS_SQLITE3_VERSION_MINOR := $(shell ech
ifeq (3,$(SYS_SQLITE3_VERSION_MAJOR))
ifeq (,$(filter-out 0 1 2 3 4,$(SYS_SQLITE3_VERSION_MINOR)))
# sqlite <= 3.4.x is too old, it doesn't provide sqlite3_file_control
else
NSS_USE_SYSTEM_SQLITE = 1
endif
endif
-
-include $(CORE_DEPTH)/coreconf/sanitizers.mk
-DARWIN_SDK_SHLIBFLAGS += $(SANITIZER_LDFLAGS)
--- a/security/nss/coreconf/Linux.mk
+++ b/security/nss/coreconf/Linux.mk
@@ -142,19 +142,17 @@ endif
DSO_CFLAGS = -fPIC
DSO_LDOPTS = -shared $(ARCHFLAG) -Wl,--gc-sections
# The linker on Red Hat Linux 7.2 and RHEL 2.1 (GNU ld version 2.11.90.0.8)
# incorrectly reports undefined references in the libraries we link with, so
# we don't use -z defs there.
# Also, -z defs conflicts with Address Sanitizer, which emits relocations
# against the libsanitizer runtime built into the main executable.
ZDEFS_FLAG = -Wl,-z,defs
-ifneq ($(USE_ASAN),1)
DSO_LDOPTS += $(if $(findstring 2.11.90.0.8,$(shell ld -v)),,$(ZDEFS_FLAG))
-endif
LDFLAGS += $(ARCHFLAG)
# On Maemo, we need to use the -rpath-link flag for even the standard system
# library directories.
ifdef _SBOX_DIR
LDFLAGS += -Wl,-rpath-link,/usr/lib:/lib
endif
@@ -204,10 +202,8 @@ ifeq ($(OS_RELEASE),2.4)
DEFINES += -DNO_FORK_CHECK
endif
ifdef USE_GCOV
OS_CFLAGS += --coverage
LDFLAGS += --coverage
DSO_LDOPTS += --coverage
endif
-
-include $(CORE_DEPTH)/coreconf/sanitizers.mk
--- a/security/nss/coreconf/arch.mk
+++ b/security/nss/coreconf/arch.mk
@@ -7,17 +7,17 @@
# Master "Core Components" macros for getting the OS architecture #
# defines these symbols:
# OS_ARCH (from uname -r)
# OS_TEST (from uname -m)
# OS_RELEASE (from uname -v and/or -r)
# OS_TARGET User defined, or set to OS_ARCH
# CPU_ARCH (from unmame -m or -p, ONLY on WINNT)
# OS_CONFIG OS_TARGET + OS_RELEASE
-# OBJDIR_TAG (uses ASAN_TAG, GCOV_TAG, 64BIT_TAG)
+# OBJDIR_TAG (uses GCOV_TAG, 64BIT_TAG)
# OBJDIR_NAME
#######################################################################
#
# Macros for getting the OS architecture
#
OS_ARCH := $(subst /,_,$(shell uname -s))
@@ -263,32 +263,27 @@ endif
OS_CONFIG = $(OS_TARGET)$(OS_RELEASE)
#
# OBJDIR_TAG depends on the predefined variable BUILD_OPT,
# to distinguish between debug and release builds.
#
-ifeq ($(USE_ASAN), 1)
- ASAN_TAG = _ASAN
-else
- ASAN_TAG =
-endif
ifeq ($(USE_GCOV), 1)
GCOV_TAG = _GCOV
else
GCOV_TAG =
endif
ifeq ($(USE_64), 1)
64BIT_TAG = _64
else
64BIT_TAG =
endif
-OBJDIR_TAG_BASE=$(ASAN_TAG)$(GCOV_TAG)$(64BIT_TAG)
+OBJDIR_TAG_BASE=$(GCOV_TAG)$(64BIT_TAG)
ifdef BUILD_OPT
OBJDIR_TAG = $(OBJDIR_TAG_BASE)_OPT
else
ifdef BUILD_IDG
OBJDIR_TAG = $(OBJDIR_TAG_BASE)_IDG
else
OBJDIR_TAG = $(OBJDIR_TAG_BASE)_DBG
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
/*
* A dummy header file that is a dependency for all the object files.
* Used to force a full recompilation of NSS in Mozilla's Tinderbox
* depend builds. See comments in rules.mk.
*/
#error "Do not include this header file."
-
deleted file mode 100644
--- a/security/nss/coreconf/sanitizers.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# Address Sanitizer support; include this in OS-specific .mk files
-# *after* defining the variables that are appended to here.
-
-ifeq ($(USE_ASAN), 1)
-SANITIZER_FLAGS_COMMON = -fsanitize=address
-
-ifeq ($(USE_UBSAN), 1)
-SANITIZER_FLAGS_COMMON += -fsanitize=undefined -fno-sanitize-recover=undefined
-endif
-
-ifeq ($(FUZZ), 1)
-SANITIZER_FLAGS_COMMON += -fsanitize-coverage=edge
-endif
-
-SANITIZER_FLAGS_COMMON += $(EXTRA_SANITIZER_FLAGS)
-SANITIZER_CFLAGS = $(SANITIZER_FLAGS_COMMON)
-SANITIZER_LDFLAGS = $(SANITIZER_FLAGS_COMMON)
-OS_CFLAGS += $(SANITIZER_CFLAGS)
-LDFLAGS += $(SANITIZER_LDFLAGS)
-
-# ASan needs frame pointers to save stack traces for allocation/free sites.
-# (Warning: some platforms, like ARM Linux in Thumb mode, don't have useful
-# frame pointers even with this option.)
-SANITIZER_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
-
-ifdef BUILD_OPT
-# You probably want to be able to get debug info for failures, even with an
-# optimized build.
-OPTIMIZER += -g
-else
-# Try maintaining reasonable performance, ASan and UBSan slow things down.
-OPTIMIZER += -O1
-endif
-
-endif
--- a/security/nss/fuzz/clone_libfuzzer.sh
+++ b/security/nss/fuzz/clone_libfuzzer.sh
@@ -1,22 +1,22 @@
#!/bin/sh
d=$(dirname $0)
-$d/git-copy.sh https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer 33c20f597a2e312611d52677ff0fdd9335b485b7 $d/libFuzzer
+$d/git-copy.sh https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer 0b27dad707a1d67ec854423e25b1a521c9d5ab7a $d/libFuzzer
# [https://llvm.org/bugs/show_bug.cgi?id=31318]
# This prevents a known buffer overrun that won't be fixed as the affected code
# will go away in the near future. Until that is we have to patch it as we seem
# to constantly run into it.
cat <<EOF | patch -p0 -d $d
diff --git libFuzzer/FuzzerLoop.cpp libFuzzer/FuzzerLoop.cpp
--- libFuzzer/FuzzerLoop.cpp
+++ libFuzzer/FuzzerLoop.cpp
-@@ -472,6 +472,9 @@
+@@ -476,6 +476,9 @@
uint8_t dummy;
ExecuteCallback(&dummy, 0);
+ // Number of counters might have changed.
+ PrepareCounters(&MaxCoverage);
+
for (const auto &U : *InitialCorpus) {
if (size_t NumFeatures = RunOne(U)) {
@@ -25,19 +25,19 @@ EOF
# Latest Libfuzzer uses __sanitizer_dump_coverage(), a symbol to be introduced
# with LLVM 4.0. To keep our code working with LLVM 3.x to simplify development
# of fuzzers we'll just provide it ourselves.
cat <<EOF | patch -p0 -d $d
diff --git libFuzzer/FuzzerTracePC.cpp libFuzzer/FuzzerTracePC.cpp
--- libFuzzer/FuzzerTracePC.cpp
+++ libFuzzer/FuzzerTracePC.cpp
-@@ -24,6 +24,12 @@
- #include <set>
- #include <sstream>
+@@ -31,6 +31,12 @@
+ __sancov_trace_pc_guard_8bit_counters[fuzzer::TracePC::kNumPCs];
+ uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs];
+#if defined(__clang_major__) && (__clang_major__ == 3)
+void __sanitizer_dump_coverage(const uintptr_t *pcs, uintptr_t len) {
+ // SanCov in LLVM 4.x will provide this symbol. Make 3.x work.
+}
+#endif
+
namespace fuzzer {
--- a/security/nss/fuzz/fuzz.gyp
+++ b/security/nss/fuzz/fuzz.gyp
@@ -37,36 +37,17 @@
'<(DEPTH)/lib/pkcs7/pkcs7.gyp:pkcs7',
# This is a static build of pk11wrap, softoken, and freebl.
'<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static',
],
'conditions': [
['fuzz_oss==0', {
'type': 'static_library',
'sources': [
- 'libFuzzer/FuzzerCrossOver.cpp',
- 'libFuzzer/FuzzerDriver.cpp',
- 'libFuzzer/FuzzerExtFunctionsDlsym.cpp',
- 'libFuzzer/FuzzerExtFunctionsWeak.cpp',
- 'libFuzzer/FuzzerExtFunctionsWeakAlias.cpp',
- 'libFuzzer/FuzzerIO.cpp',
- 'libFuzzer/FuzzerIOPosix.cpp',
- 'libFuzzer/FuzzerIOWindows.cpp',
- 'libFuzzer/FuzzerLoop.cpp',
- 'libFuzzer/FuzzerMain.cpp',
- 'libFuzzer/FuzzerMerge.cpp',
- 'libFuzzer/FuzzerMutate.cpp',
- 'libFuzzer/FuzzerSHA1.cpp',
- 'libFuzzer/FuzzerTracePC.cpp',
- 'libFuzzer/FuzzerTraceState.cpp',
- 'libFuzzer/FuzzerUtil.cpp',
- 'libFuzzer/FuzzerUtilDarwin.cpp',
- 'libFuzzer/FuzzerUtilLinux.cpp',
- 'libFuzzer/FuzzerUtilPosix.cpp',
- 'libFuzzer/FuzzerUtilWindows.cpp',
+ '<!@(ls <(DEPTH)/fuzz/libFuzzer/*.cpp)',
],
'cflags/': [
['exclude', '-fsanitize-coverage'],
],
'xcode_settings': {
'OTHER_CFLAGS/': [
['exclude', '-fsanitize-coverage'],
],
--- a/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
@@ -118,16 +118,28 @@ TEST_F(TlsConnectTest, TestFallbackFromT
SSL_LIBRARY_VERSION_TLS_1_2);
server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
SSL_LIBRARY_VERSION_TLS_1_3);
ConnectExpectFail();
ASSERT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code());
}
#endif
+TEST_P(TlsConnectGeneric, TestFallbackSCSVVersionMatch) {
+ client_->SetFallbackSCSVEnabled(true);
+ Connect();
+}
+
+TEST_P(TlsConnectGenericPre13, TestFallbackSCSVVersionMismatch) {
+ client_->SetFallbackSCSVEnabled(true);
+ server_->SetVersionRange(version_, version_ + 1);
+ ConnectExpectFail();
+ client_->CheckErrorCode(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT);
+}
+
// The TLS v1.3 spec section C.4 states that 'Implementations MUST NOT send or
// accept any records with a version less than { 3, 0 }'. Thus we will not
// allow version ranges including both SSL v3 and TLS v1.3.
TEST_F(TlsConnectTest, DisallowSSLv3HelloWithTLSv13Enabled) {
SECStatus rv;
SSLVersionRange vrange = {SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_3};
--- a/security/nss/gtests/ssl_gtest/tls_agent.cc
+++ b/security/nss/gtests/ssl_gtest/tls_agent.cc
@@ -366,16 +366,24 @@ void TlsAgent::SetSessionCacheEnabled(bo
void TlsAgent::Set0RttEnabled(bool en) {
EXPECT_TRUE(EnsureTlsSetup());
SECStatus rv =
SSL_OptionSet(ssl_fd_, SSL_ENABLE_0RTT_DATA, en ? PR_TRUE : PR_FALSE);
EXPECT_EQ(SECSuccess, rv);
}
+void TlsAgent::SetFallbackSCSVEnabled(bool en) {
+ EXPECT_TRUE(role_ == CLIENT && EnsureTlsSetup());
+
+ SECStatus rv =
+ SSL_OptionSet(ssl_fd_, SSL_ENABLE_FALLBACK_SCSV, en ? PR_TRUE : PR_FALSE);
+ EXPECT_EQ(SECSuccess, rv);
+}
+
void TlsAgent::SetShortHeadersEnabled() {
EXPECT_TRUE(EnsureTlsSetup());
SECStatus rv = SSLInt_EnableShortHeaders(ssl_fd_);
EXPECT_EQ(SECSuccess, rv);
}
void TlsAgent::SetVersionRange(uint16_t minver, uint16_t maxver) {
--- a/security/nss/gtests/ssl_gtest/tls_agent.h
+++ b/security/nss/gtests/ssl_gtest/tls_agent.h
@@ -126,16 +126,17 @@ class TlsAgent : public PollTarget {
void SetupClientAuth();
void RequestClientAuth(bool requireAuth);
void ConfigureSessionCache(SessionResumptionMode mode);
void SetSessionTicketsEnabled(bool en);
void SetSessionCacheEnabled(bool en);
void Set0RttEnabled(bool en);
+ void SetFallbackSCSVEnabled(bool en);
void SetShortHeadersEnabled();
void SetVersionRange(uint16_t minver, uint16_t maxver);
void GetVersionRange(uint16_t* minver, uint16_t* maxver);
void CheckPreliminaryInfo();
void ResetPreliminaryInfo();
void SetExpectedVersion(uint16_t version);
void SetServerKeyBits(uint16_t bits);
void ExpectReadWriteError();
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -8549,17 +8549,17 @@ ssl3_HandleClientHello(sslSocket *ss, SS
/* Now parse the rest of the extensions. */
rv = ssl3_HandleParsedExtensions(ss, client_hello);
if (rv != SECSuccess) {
goto loser; /* malformed */
}
/* If the ClientHello version is less than our maximum version, check for a
* TLS_FALLBACK_SCSV and reject the connection if found. */
- if (ss->vrange.max > ss->clientHelloVersion) {
+ if (ss->vrange.max > ss->version) {
for (i = 0; i + 1 < suites.len; i += 2) {
PRUint16 suite_i = (suites.data[i] << 8) | suites.data[i + 1];
if (suite_i != TLS_FALLBACK_SCSV)
continue;
desc = inappropriate_fallback;
errCode = SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT;
goto alert_loser;
}
--- a/security/nss/nss-tool/common/argparse.h
+++ b/security/nss/nss-tool/common/argparse.h
@@ -8,22 +8,22 @@
#include <string>
#include <unordered_map>
#include <vector>
class ArgParser {
public:
ArgParser(const std::vector<std::string>& arguments);
- bool Has(std::string arg) { return programArgs_.count(arg) > 0; }
+ bool Has(std::string arg) const { return programArgs_.count(arg) > 0; }
- std::string Get(std::string arg) { return programArgs_[arg]; }
+ std::string Get(std::string arg) const { return programArgs_.at(arg); }
- size_t GetPositionalArgumentCount() { return positionalArgs_.size(); }
- std::string GetPositionalArgument(size_t pos) {
+ size_t GetPositionalArgumentCount() const { return positionalArgs_.size(); }
+ std::string GetPositionalArgument(size_t pos) const {
return positionalArgs_.at(pos);
}
private:
std::unordered_map<std::string, std::string> programArgs_;
std::vector<std::string> positionalArgs_;
};
--- a/security/nss/nss-tool/db/dbtool.cc
+++ b/security/nss/nss-tool/db/dbtool.cc
@@ -2,16 +2,17 @@
* 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 "dbtool.h"
#include "argparse.h"
#include "scoped_ptrs.h"
#include <dirent.h>
+#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <regex>
#include <sstream>
#include <cert.h>
#include <certdb.h>
@@ -46,38 +47,60 @@ static std::string PrintFlags(unsigned i
ss << "I";
}
if (flags & CERTDB_GOVT_APPROVED_CA) {
ss << "G";
}
return ss.str();
}
+static std::vector<char> ReadFromIstream(std::istream &is) {
+ std::vector<char> certData;
+ while (is) {
+ char buf[1024];
+ is.read(buf, sizeof(buf));
+ certData.insert(certData.end(), buf, buf + is.gcount());
+ }
+
+ return certData;
+}
+
void DBTool::Usage() {
- std::cerr << "Usage: nss db [--path <directory>] [--create] --list-certs"
+ std::cerr << "Usage: nss db [--path <directory>]" << std::endl;
+ std::cerr << " --create" << std::endl;
+ std::cerr << " --list-certs" << std::endl;
+ std::cerr << " --import-cert [<path>] --name <name> [--trusts <trusts>]"
<< std::endl;
}
bool DBTool::Run(const std::vector<std::string> &arguments) {
ArgParser parser(arguments);
+ if (!parser.Has("--create") && !parser.Has("--list-certs") &&
+ !parser.Has("--import-cert")) {
+ return false;
+ }
+
+ PRAccessHow how = PR_ACCESS_READ_OK;
+ bool readOnly = true;
+ if (parser.Has("--create") || parser.Has("--import-cert")) {
+ how = PR_ACCESS_WRITE_OK;
+ readOnly = false;
+ }
+
std::string initDir(".");
if (parser.Has("--path")) {
initDir = parser.Get("--path");
- if (PR_Access(initDir.c_str(), PR_ACCESS_READ_OK) != PR_SUCCESS) {
- std::cerr << "Directory '" << initDir
- << "' does not exist or you don't have permissions!"
- << std::endl;
- return false;
- }
+ }
+ if (PR_Access(initDir.c_str(), how) != PR_SUCCESS) {
+ std::cerr << "Directory '" << initDir
+ << "' does not exist or you don't have permissions!" << std::endl;
+ return false;
}
- if (!parser.Has("--list-certs") && !parser.Has("--create")) {
- return false;
- }
std::cout << "Using database directory: " << initDir << std::endl
<< std::endl;
bool dbFilesExist = PathHasDBFiles(initDir);
if (parser.Has("--create") && dbFilesExist) {
std::cerr << "Trying to create database files in a directory where they "
"already exists. Delete the db files before creating new ones."
<< std::endl;
@@ -88,38 +111,39 @@ bool DBTool::Run(const std::vector<std::
std::cerr << "Create them using 'nss db --create [--path /foo/bar]' before "
"continuing."
<< std::endl;
return false;
}
// init NSS
const char *certPrefix = ""; // certutil -P option --- can leave this empty
- SECStatus rv =
- NSS_Initialize(initDir.c_str(), certPrefix, certPrefix, "secmod.db", 0);
+ SECStatus rv = NSS_Initialize(initDir.c_str(), certPrefix, certPrefix,
+ "secmod.db", readOnly ? NSS_INIT_READONLY : 0);
if (rv != SECSuccess) {
std::cerr << "NSS init failed!" << std::endl;
return false;
}
+ bool ret = true;
if (parser.Has("--list-certs")) {
ListCertificates();
- }
-
- if (parser.Has("--create")) {
+ } else if (parser.Has("--import-cert")) {
+ ret = ImportCertificate(parser);
+ } else if (parser.Has("--create")) {
std::cout << "DB files created successfully." << std::endl;
}
// shutdown nss
if (NSS_Shutdown() != SECSuccess) {
std::cerr << "NSS Shutdown failed!" << std::endl;
return false;
}
- return true;
+ return ret;
}
bool DBTool::PathHasDBFiles(std::string path) {
std::regex certDBPattern("cert.*\\.db");
std::regex keyDBPattern("key.*\\.db");
DIR *dir;
if (!(dir = opendir(path.c_str()))) {
@@ -180,8 +204,78 @@ void DBTool::ListCertificates() {
trusts = ss.str();
} else {
trusts = ",,";
}
std::cout << std::setw(60) << std::left << name << " " << trusts
<< std::endl;
}
}
+
+bool DBTool::ImportCertificate(const ArgParser &parser) {
+ if (!parser.Has("--name")) {
+ std::cerr << "A name (--name) is required to import a certificate."
+ << std::endl;
+ return false;
+ }
+
+ std::string derFilePath = parser.Get("--import-cert");
+ std::string certName = parser.Get("--name");
+ std::string trustString("TCu,Cu,Tu");
+ if (parser.Has("--trusts")) {
+ trustString = parser.Get("--trusts");
+ }
+
+ CERTCertTrust trust;
+ SECStatus rv = CERT_DecodeTrustString(&trust, trustString.c_str());
+ if (rv != SECSuccess) {
+ std::cerr << "Cannot decode trust string!" << std::endl;
+ return false;
+ }
+
+ ScopedPK11SlotInfo slot = ScopedPK11SlotInfo(PK11_GetInternalKeySlot());
+ if (slot.get() == nullptr) {
+ std::cerr << "Error: Init PK11SlotInfo failed!\n";
+ return false;
+ }
+
+ std::vector<char> certData;
+ if (derFilePath.empty()) {
+ std::cout << "No Certificate file path given, using stdin." << std::endl;
+ certData = ReadFromIstream(std::cin);
+ } else {
+ std::ifstream is(derFilePath, std::ifstream::binary);
+ if (!is.good()) {
+ std::cerr << "IO Error when opening " << derFilePath << std::endl;
+ std::cerr
+ << "Certificate file does not exist or you don't have permissions."
+ << std::endl;
+ return false;
+ }
+ certData = ReadFromIstream(is);
+ }
+
+ ScopedCERTCertificate cert(
+ CERT_DecodeCertFromPackage(certData.data(), certData.size()));
+ if (cert.get() == nullptr) {
+ std::cerr << "Error: Could not decode certificate!" << std::endl;
+ return false;
+ }
+
+ rv = PK11_ImportCert(slot.get(), cert.get(), CK_INVALID_HANDLE,
+ certName.c_str(), PR_FALSE);
+ if (rv != SECSuccess) {
+ // TODO handle authentication -> PK11_Authenticate (see certutil.c line
+ // 134)
+ std::cerr << "Error: Could not add certificate to database!" << std::endl;
+ return false;
+ }
+
+ rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert.get(), &trust);
+ if (rv != SECSuccess) {
+ std::cerr << "Cannot change cert's trust" << std::endl;
+ return false;
+ }
+
+ std::cout << "Certificate import was successful!" << std::endl;
+ // TODO show information about imported certificate
+ return true;
+}
--- a/security/nss/nss-tool/db/dbtool.h
+++ b/security/nss/nss-tool/db/dbtool.h
@@ -2,21 +2,23 @@
* 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 dbtool_h__
#define dbtool_h__
#include <string>
#include <vector>
+#include "argparse.h"
class DBTool {
public:
bool Run(const std::vector<std::string>& arguments);
void Usage();
private:
bool PathHasDBFiles(std::string path);
void ListCertificates();
+ bool ImportCertificate(const ArgParser& parser);
};
#endif // dbtool_h__
--- a/security/nss/nss-tool/nss_tool.cc
+++ b/security/nss/nss-tool/nss_tool.cc
@@ -8,17 +8,17 @@
#include <prinit.h>
#include "argparse.h"
#include "db/dbtool.h"
static void Usage() {
std::cerr << "Usage: nss <command> <subcommand> [options]" << std::endl;
- std::cerr << " nss db [--path <directory>] --list-certs" << std::endl;
+ std::cerr << " nss db [--path <directory>] <commands>" << std::endl;
}
int main(int argc, char **argv) {
if (argc < 2) {
Usage();
return 1;
}
--- a/security/nss/tests/all.sh
+++ b/security/nss/tests/all.sh
@@ -57,17 +57,16 @@
# -----------------------------------------------------------
# HOST - test machine host name
# DOMSUF - test machine domain name
#
# Optional environment variables to specify build to use:
# -------------------------------------------------------
# BUILT_OPT - use optimized/debug build
# USE_64 - use 64bit/32bit build
-# USE_ASAN - use Address Sanitizer build
#
# Optional environment variables to enable specific NSS features:
# ---------------------------------------------------------------
# NSS_DISABLE_ECC - disable ECC
#
# Optional environment variables to select which cycles/suites to test:
# ---------------------------------------------------------------------
# NSS_CYCLES - list of cycles to run (separated by space
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -133,16 +133,31 @@ def target_tasks_graphics(full_task_grap
def filter(task):
if task.attributes['kind'] == 'artifact-build':
return False
return True
return [l for l in filtered_for_project if filter(full_task_graph[l])]
+@_target_task('mochitest_valgrind')
+def target_tasks_valgrind(full_task_graph, parameters):
+ """Target tasks that only run on the cedar branch."""
+ def filter(task):
+ platform = task.attributes.get('build_platform')
+ # only select platforms
+ if platform not in ['linux64']:
+ return False
+ if task.attributes.get('unittest_suite'):
+ if not (task.attributes['unittest_suite'].startswith('mochitest-valgrind')):
+ return False
+ return True
+ return [l for l, t in full_task_graph.tasks.iteritems() if filter(t)]
+
+
@_target_task('nightly_fennec')
def target_tasks_nightly(full_task_graph, parameters):
"""Select the set of tasks required for a nightly build of fennec. The
nightly build process involves a pipeline of builds, signing,
and, eventually, uploading the tasks to balrog."""
def filter(task):
platform = task.attributes.get('build_platform')
if platform in ('android-api-15-nightly', 'android-x86-nightly'):
--- a/tools/profiler/lul/LulMain.cpp
+++ b/tools/profiler/lul/LulMain.cpp
@@ -10,16 +10,17 @@
#include <stdlib.h>
#include <stdio.h>
#include <algorithm> // std::sort
#include <string>
#include "mozilla/Assertions.h"
#include "mozilla/ArrayUtils.h"
+#include "mozilla/CheckedInt.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/MemoryChecking.h"
#include "mozilla/Sprintf.h"
#include "LulCommonExt.h"
#include "LulElfExt.h"
#include "LulMainInt.h"
@@ -29,16 +30,17 @@
// Set this to 1 for verbose logging
#define DEBUG_MAIN 0
namespace lul {
using std::string;
using std::vector;
using std::pair;
+using mozilla::CheckedInt;
using mozilla::DebugOnly;
// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
//
// Some functions in this file are marked RUNS IN NO-MALLOC CONTEXT.
// Any such function -- and, hence, the transitive closure of those
// reachable from it -- must not do any dynamic memory allocation.
@@ -1009,23 +1011,40 @@ LUL::CountMappings()
// RUNS IN NO-MALLOC CONTEXT
static
TaggedUWord DerefTUW(TaggedUWord aAddr, const StackImage* aStackImg)
{
if (!aAddr.Valid()) {
return TaggedUWord();
}
+
+ // Lower limit check. |aAddr.Value()| is the lowest requested address
+ // and |aStackImg->mStartAvma| is the lowest address we actually have,
+ // so the comparison is straightforward.
if (aAddr.Value() < aStackImg->mStartAvma) {
return TaggedUWord();
}
- if (aAddr.Value() + sizeof(uintptr_t) > aStackImg->mStartAvma
- + aStackImg->mLen) {
+
+ // Upper limit check. We must compute the highest requested address
+ // and the highest address we actually have, but being careful to
+ // avoid overflow. In particular if |aAddr| is 0xFFF...FFF or the
+ // 3/7 values below that, then we will get overflow. See bug #1245477.
+ typedef CheckedInt<uintptr_t> CheckedUWord;
+ CheckedUWord highest_requested_plus_one
+ = CheckedUWord(aAddr.Value()) + CheckedUWord(sizeof(uintptr_t));
+ CheckedUWord highest_available_plus_one
+ = CheckedUWord(aStackImg->mStartAvma) + CheckedUWord(aStackImg->mLen);
+ if (!highest_requested_plus_one.isValid() // overflow?
+ || !highest_available_plus_one.isValid() // overflow?
+ || (highest_requested_plus_one.value()
+ > highest_available_plus_one.value())) { // in range?
return TaggedUWord();
}
+
return TaggedUWord(*(uintptr_t*)(aStackImg->mContents + aAddr.Value()
- aStackImg->mStartAvma));
}
// RUNS IN NO-MALLOC CONTEXT
static
TaggedUWord EvaluateReg(int16_t aReg, const UnwindRegs* aOldRegs,
TaggedUWord aCFA)