--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -848,44 +848,60 @@
return this;
throw Components.results.NS_NOINTERFACE;
}
});
]]>
</body>
</method>
+ <field name="serializationHelper">
+ Cc["@mozilla.org/network/serialization-helper;1"]
+ .getService(Ci.nsISerializationHelper);
+ </field>
+
+ <field name="mIconLoadingPrincipal">
+ null
+ </field>
+
<method name="setIcon">
<parameter name="aTab"/>
<parameter name="aURI"/>
<parameter name="aLoadingPrincipal"/>
<body>
<![CDATA[
let browser = this.getBrowserForTab(aTab);
browser.mIconURL = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
+ let loadingPrincipal = aLoadingPrincipal
+ ? aLoadingPrincipal
+ : Services.scriptSecurityManager.getSystemPrincipal();
if (aURI) {
if (!(aURI instanceof Ci.nsIURI)) {
aURI = makeURI(aURI);
}
- // We do not serialize the principal from within SessionStore.jsm,
- // hence if aLoadingPrincipal is null we default to the
- // systemPrincipal which will allow the favicon to load.
- let loadingPrincipal = aLoadingPrincipal
- ? aLoadingPrincipal
- : Services.scriptSecurityManager.getSystemPrincipal();
PlacesUIUtils.loadFavicon(browser, loadingPrincipal, aURI);
}
let sizedIconUrl = browser.mIconURL || "";
if (sizedIconUrl != aTab.getAttribute("image")) {
- if (sizedIconUrl)
+ if (sizedIconUrl) {
aTab.setAttribute("image", sizedIconUrl);
- else
+ if (!browser.mIconLoadingPrincipal ||
+ !browser.mIconLoadingPrincipal.equals(loadingPrincipal)) {
+ aTab.setAttribute("iconLoadingPrincipal",
+ this.serializationHelper.serializeToString(loadingPrincipal));
+ browser.mIconLoadingPrincipal = loadingPrincipal;
+ }
+ }
+ else {
aTab.removeAttribute("image");
+ aTab.removeAttribute("iconLoadingPrincipal");
+ delete browser.mIconLoadingPrincipal;
+ }
this._tabAttrModified(aTab, ["image"]);
}
this._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]);
]]>
</body>
</method>
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media.js
@@ -538,16 +538,17 @@ function test() {
for (let test of gTests) {
info(test.desc);
yield test.run();
// Cleanup before the next test
yield expectNoObserverCalled();
}
}).then(finish, ex => {
+ Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, true);
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
content.location = rootDir + "get_user_media.html";
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_anim.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_anim.js
@@ -88,16 +88,17 @@ function test() {
Task.spawn(function* () {
yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let test of gTests) {
info(test.desc);
yield test.run();
}
}).then(finish, ex => {
+ Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, true);
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
content.location = rootDir + "get_user_media.html";
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_in_frame.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_in_frame.js
@@ -257,16 +257,17 @@ function test() {
for (let test of gTests) {
info(test.desc);
yield test.run();
// Cleanup before the next test
yield expectNoObserverCalled();
}
}).then(finish, ex => {
+ Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, true);
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
let url = rootDir + "get_user_media.html";
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_tear_off_tab.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_tear_off_tab.js
@@ -90,16 +90,17 @@ function test() {
for (let test of gTests) {
info(test.desc);
yield test.run();
// Cleanup before the next test
yield expectNoObserverCalled();
}
}).then(finish, ex => {
+ Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, true);
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content/",
"https://example.com/");
content.location = rootDir + "get_user_media.html";
--- a/browser/components/preferences/in-content/advanced.js
+++ b/browser/components/preferences/in-content/advanced.js
@@ -732,19 +732,17 @@ var gAdvancedPane = {
* requests one
*/
/**
* Displays the user's certificates and associated options.
*/
showCertificates: function ()
{
- openDialog("chrome://pippki/content/certManager.xul",
- "mozilla:certmanager",
- "modal=yes", null);
+ gSubDialog.open("chrome://pippki/content/certManager.xul");
},
/**
* Displays a dialog from which the user can manage his security devices.
*/
showSecurityDevices: function ()
{
gSubDialog.open("chrome://pippki/content/device_manager.xul");
--- a/browser/components/sessionstore/SessionHistory.jsm
+++ b/browser/components/sessionstore/SessionHistory.jsm
@@ -167,17 +167,17 @@ var SessionHistoryInternal = {
let x = {}, y = {};
shEntry.getScrollPosition(x, y);
if (x.value != 0 || y.value != 0)
entry.scroll = x.value + "," + y.value;
}
// Collect triggeringPrincipal data for the current history entry.
try {
- let triggeringPrincipal = this.serializeTriggeringPrincipal(shEntry);
+ let triggeringPrincipal = Utils.serializePrincipal(shEntry.triggeringPrincipal);
if (triggeringPrincipal) {
entry.triggeringPrincipal_b64 = triggeringPrincipal;
}
} catch (ex) {
// Not catching anything specific here, just possible errors
// from writeCompoundObject() and the like.
debug("Failed serializing triggeringPrincipal data: " + ex);
}
@@ -214,49 +214,16 @@ var SessionHistoryInternal = {
entry.children = children;
}
}
return entry;
},
/**
- * Serialize triggeringPrincipal data contained in the given session history entry.
- *
- * @param shEntry
- * The session history entry.
- * @return The base64 encoded triggeringPrincipal data.
- */
- serializeTriggeringPrincipal: function (shEntry) {
- if (!shEntry.triggeringPrincipal) {
- return null;
- }
-
- let binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].
- createInstance(Ci.nsIObjectOutputStream);
- let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
- pipe.init(false, false, 0, 0xffffffff, null);
- binaryStream.setOutputStream(pipe.outputStream);
- binaryStream.writeCompoundObject(shEntry.triggeringPrincipal, Ci.nsIPrincipal, true);
- binaryStream.close();
-
- // Now we want to read the data from the pipe's input end and encode it.
- let scriptableStream = Cc["@mozilla.org/binaryinputstream;1"].
- createInstance(Ci.nsIBinaryInputStream);
- scriptableStream.setInputStream(pipe.inputStream);
- let triggeringPrincipalBytes =
- scriptableStream.readByteArray(scriptableStream.available());
-
- // We can stop doing base64 encoding once our serialization into JSON
- // is guaranteed to handle all chars in strings, including embedded
- // nulls.
- return btoa(String.fromCharCode.apply(null, triggeringPrincipalBytes));
- },
-
- /**
* Restores session history data for a given docShell.
*
* @param docShell
* The docShell that owns the session history.
* @param tabData
* The tabdata including all history entries.
*/
restore: function (docShell, tabData) {
@@ -383,26 +350,17 @@ var SessionHistoryInternal = {
// Bug 1286472. To remain backward compatible we still have to support that
// field for a few cycles before we can remove it within Bug 1289785.
if (entry.owner_b64) {
entry.triggeringPricipal_b64 = entry.owner_b64;
delete entry.owner_b64;
}
if (entry.triggeringPrincipal_b64) {
- var triggeringPrincipalInput = Cc["@mozilla.org/io/string-input-stream;1"]
- .createInstance(Ci.nsIStringInputStream);
- var binaryData = atob(entry.triggeringPrincipal_b64);
- triggeringPrincipalInput.setData(binaryData, binaryData.length);
- var binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
- createInstance(Ci.nsIObjectInputStream);
- binaryStream.setInputStream(triggeringPrincipalInput);
- try { // Catch possible deserialization exceptions
- shEntry.triggeringPrincipal = binaryStream.readObject(true);
- } catch (ex) { debug(ex); }
+ shEntry.triggeringPrincipal = Utils.deserializePrincipal(entry.triggeringPrincipal_b64);
}
if (entry.children && shEntry instanceof Ci.nsISHContainer) {
for (var i = 0; i < entry.children.length; i++) {
//XXXzpao Wallpaper patch for bug 514751
if (!entry.children[i].url)
continue;
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -806,22 +806,20 @@ var SessionStoreInternal = {
tab.crop = "center";
}
} else if (tab.hasAttribute("customizemode")) {
win.gCustomizeMode.setTab(tab);
}
// Restore the tab icon.
if ("image" in tabData) {
- // Using null as the loadingPrincipal because serializing
- // the principal would be overkill. Within SetIcon we
- // default to the systemPrincipal if aLoadingPrincipal is
- // null which will allow the favicon to load.
- win.gBrowser.setIcon(tab, tabData.image, null);
- TabStateCache.update(browser, {image: null});
+ // Use the serialized contentPrincipal with the new icon load.
+ let loadingPrincipal = Utils.deserializePrincipal(tabData.iconLoadingPrincipal);
+ win.gBrowser.setIcon(tab, tabData.image, loadingPrincipal);
+ TabStateCache.update(browser, { image: null, iconLoadingPrincipal: null });
}
let event = win.document.createEvent("Events");
event.initEvent("SSTabRestoring", true, false);
tab.dispatchEvent(event);
break;
case "SessionStore:restoreTabContentStarted":
if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
@@ -1770,16 +1768,17 @@ var SessionStoreInternal = {
let tabTitle = this._replaceLoadingTitle(aTab.label, tabbrowser, aTab);
let {permanentKey} = aTab.linkedBrowser;
let tabData = {
permanentKey,
state: tabState,
title: tabTitle,
image: tabbrowser.getIcon(aTab),
+ iconLoadingPrincipal: Utils.serializePrincipal(aTab.linkedBrowser.contentPrincipal),
pos: aTab._tPos,
closedAt: Date.now()
};
let closedTabs = this._windows[aWindow.__SSi]._closedTabs;
// Determine whether the tab contains any information worth saving. Note
// that there might be pending state changes queued in the child that
@@ -3316,16 +3315,17 @@ var SessionStoreInternal = {
formdata: tabData.formdata || null,
disallow: tabData.disallow || null,
pageStyle: tabData.pageStyle || null,
// This information is only needed until the tab has finished restoring.
// When that's done it will be removed from the cache and we always
// collect it in TabState._collectBaseTabData().
image: tabData.image || "",
+ iconLoadingPrincipal: tabData.iconLoadingPrincipal || null,
userTypedValue: tabData.userTypedValue || "",
userTypedClear: tabData.userTypedClear || 0
});
browser.messageManager.sendAsyncMessage("SessionStore:restoreHistory",
{tabData: tabData, epoch: epoch, loadArguments});
// Restore tab attributes.
--- a/browser/components/sessionstore/TabAttributes.jsm
+++ b/browser/components/sessionstore/TabAttributes.jsm
@@ -7,17 +7,19 @@
this.EXPORTED_SYMBOLS = ["TabAttributes"];
// We never want to directly read or write these attributes.
// 'image' should not be accessed directly but handled by using the
// gBrowser.getIcon()/setIcon() methods.
// 'muted' should not be accessed directly but handled by using the
// tab.linkedBrowser.audioMuted/toggleMuteAudio methods.
// 'pending' is used internal by sessionstore and managed accordingly.
-const ATTRIBUTES_TO_SKIP = new Set(["image", "muted", "pending"]);
+// 'iconLoadingPrincipal' is same as 'image' that it should be handled by
+// using the gBrowser.getIcon()/setIcon() methods.
+const ATTRIBUTES_TO_SKIP = new Set(["image", "muted", "pending", "iconLoadingPrincipal"]);
// A set of tab attributes to persist. We will read a given list of tab
// attributes when collecting tab data and will re-set those attributes when
// the given tab data is restored to a new tab.
this.TabAttributes = Object.freeze({
persist: function (name) {
return TabAttributesInternal.persist(name);
},
--- a/browser/components/sessionstore/TabState.jsm
+++ b/browser/components/sessionstore/TabState.jsm
@@ -11,16 +11,18 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyFilter",
"resource:///modules/sessionstore/PrivacyFilter.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "TabStateCache",
"resource:///modules/sessionstore/TabStateCache.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "TabAttributes",
"resource:///modules/sessionstore/TabAttributes.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Utils",
+ "resource:///modules/sessionstore/Utils.jsm");
/**
* Module that contains tab state collection methods.
*/
this.TabState = Object.freeze({
update: function (browser, data) {
TabStateInternal.update(browser, data);
},
@@ -117,16 +119,21 @@ var TabStateInternal = {
// be read from the tab/browser every time we collect data.
// Store the tab icon.
if (!("image" in tabData)) {
let tabbrowser = tab.ownerGlobal.gBrowser;
tabData.image = tabbrowser.getIcon(tab);
}
+ // Store the serialized contentPrincipal of this tab to use for the icon.
+ if (!("iconLoadingPrincipal" in tabData)) {
+ tabData.iconLoadingPrincipal = Utils.serializePrincipal(browser.contentPrincipal);
+ }
+
// If there is a userTypedValue set, then either the user has typed something
// in the URL bar, or a new tab was opened with a URI to load.
// If so, we also track whether we were still in the process of loading something.
if (!("userTypedValue" in tabData) && browser.userTypedValue) {
tabData.userTypedValue = browser.userTypedValue;
// We always used to keep track of the loading state as an integer, where
// '0' indicated the user had typed since the last load (or no load was
// ongoing), and any positive value indicated we had started a load since
--- a/browser/components/sessionstore/Utils.jsm
+++ b/browser/components/sessionstore/Utils.jsm
@@ -6,16 +6,21 @@
this.EXPORTED_SYMBOLS = ["Utils"];
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm", this);
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+
+XPCOMUtils.defineLazyServiceGetter(this, "serializationHelper",
+ "@mozilla.org/network/serialization-helper;1",
+ "nsISerializationHelper");
this.Utils = Object.freeze({
makeURI: function (url) {
return Services.io.newURI(url, null, null);
},
makeInputStream: function (aString) {
let stream = Cc["@mozilla.org/io/string-input-stream;1"].
@@ -55,10 +60,40 @@ this.Utils = Object.freeze({
shallowCopy: function (obj) {
let retval = {};
for (let key of Object.keys(obj)) {
retval[key] = obj[key];
}
return retval;
+ },
+
+ /**
+ * Serialize principal data.
+ *
+ * @param {nsIPrincipal} principal The principal to serialize.
+ * @return {String} The base64 encoded principal data.
+ */
+ serializePrincipal(principal) {
+ if (!principal)
+ return null;
+
+ return serializationHelper.serializeToString(principal);
+ },
+
+ /**
+ * Deserialize a base64 encoded principal (serialized with
+ * Utils::serializePrincipal).
+ *
+ * @param {String} principal_b64 A base64 encoded serialized principal.
+ * @return {nsIPrincipal} A deserialized principal.
+ */
+ deserializePrincipal(principal_b64) {
+ if (!principal_b64)
+ return null;
+
+ let principal = serializationHelper.deserializeObject(principal_b64);
+ principal.QueryInterface(Ci.nsIPrincipal);
+
+ return principal;
}
});
--- a/browser/components/sessionstore/test/browser_attributes.js
+++ b/browser/components/sessionstore/test/browser_attributes.js
@@ -12,28 +12,31 @@ const PREF = "browser.sessionstore.resto
add_task(function* test() {
Services.prefs.setBoolPref(PREF, true)
registerCleanupFunction(() => Services.prefs.clearUserPref(PREF));
// Add a new tab with a nice icon.
let tab = gBrowser.addTab("about:robots");
yield promiseBrowserLoaded(tab.linkedBrowser);
- // Check that the tab has an 'image' attribute.
+ // Check that the tab has 'image' and 'iconLoadingPrincipal' attributes.
ok(tab.hasAttribute("image"), "tab.image exists");
+ ok(tab.hasAttribute("iconLoadingPrincipal"), "tab.iconLoadingPrincipal exists");
tab.toggleMuteAudio();
// Check that the tab has a 'muted' attribute.
ok(tab.hasAttribute("muted"), "tab.muted exists");
// Make sure we do not persist 'image' or 'muted' attributes.
ss.persistTabAttribute("image");
ss.persistTabAttribute("muted");
+ ss.persistTabAttribute("iconLoadingPrincipal");
let {attributes} = JSON.parse(ss.getTabState(tab));
ok(!("image" in attributes), "'image' attribute not saved");
+ ok(!("iconLoadingPrincipal" in attributes), "'iconLoadingPrincipal' attribute not saved");
ok(!("muted" in attributes), "'muted' attribute not saved");
ok(!("custom" in attributes), "'custom' attribute not saved");
// Test persisting a custom attribute.
tab.setAttribute("custom", "foobar");
ss.persistTabAttribute("custom");
({attributes} = JSON.parse(ss.getTabState(tab)));
--- a/browser/components/sessionstore/test/browser_label_and_icon.js
+++ b/browser/components/sessionstore/test/browser_label_and_icon.js
@@ -1,13 +1,15 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
+const {classes: Cc, interfaces: Ci} = Components;
+
/**
* Make sure that tabs are restored on demand as otherwise the tab will start
* loading immediately and we can't check its icon and label.
*/
add_task(function setup() {
Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
registerCleanupFunction(() => {
@@ -34,11 +36,18 @@ add_task(function test_label_and_icon()
tab = gBrowser.addTab("about:blank");
ss.setTabState(tab, state);
yield promiseTabRestoring(tab);
// Check that label and icon are set for the restoring tab.
ok(gBrowser.getIcon(tab).startsWith("data:image/png;"), "icon is set");
is(tab.label, "Gort! Klaatu barada nikto!", "label is set");
+ let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
+ .getService(Ci.nsISerializationHelper);
+ let serializedPrincipal = tab.getAttribute("iconLoadingPrincipal");
+ let iconLoadingPrincipal = serhelper.deserializeObject(serializedPrincipal)
+ .QueryInterface(Ci.nsIPrincipal);
+ is(iconLoadingPrincipal.origin, "about:robots", "correct loadingPrincipal used");
+
// Cleanup.
yield promiseRemoveTab(tab);
});
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Preferences.jsm
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Preferences.jsm
@@ -5,81 +5,102 @@
"use strict";
this.EXPORTED_SYMBOLS = ["Preferences"];
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://testing-common/TestUtils.jsm");
Cu.import("resource://testing-common/ContentTask.jsm");
this.Preferences = {
init(libDir) {
- Services.prefs.setBoolPref("browser.preferences.inContent", true);
-
let panes = [
["paneGeneral", null],
["paneSearch", null],
["paneContent", null],
["paneApplications", null],
["panePrivacy", null],
+ ["panePrivacy", null, DNTDialog],
["paneSecurity", null],
["paneSync", null],
["paneAdvanced", "generalTab"],
["paneAdvanced", "dataChoicesTab"],
["paneAdvanced", "networkTab"],
["paneAdvanced", "updateTab"],
["paneAdvanced", "encryptionTab"],
+ ["paneAdvanced", "encryptionTab", certManager],
];
- for (let [primary, advanced] of panes) {
+ for (let [primary, advanced, customFn] of panes) {
let configName = primary.replace(/^pane/, "prefs") + (advanced ? "-" + advanced : "");
+ if (customFn) {
+ configName += "-" + customFn.name;
+ }
this.configurations[configName] = {};
- this.configurations[configName].applyConfig = prefHelper.bind(null, primary, advanced);
+ this.configurations[configName].applyConfig = prefHelper.bind(null, primary, advanced, customFn);
}
},
- configurations: {
- "panePrivacy-DNTDialog": {
- applyConfig: Task.async(function*() {
- let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
- yield prefHelper("panePrivacy", null);
-
- yield ContentTask.spawn(browserWindow.gBrowser.selectedBrowser, null, function* () {
- content.document.getElementById("doNotTrackSettings").click();
- });
- }),
- },
- },
+ configurations: {},
};
-let prefHelper = Task.async(function*(primary, advanced) {
+let prefHelper = Task.async(function*(primary, advanced = null, customFn = null) {
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
- let selectedBrowser = browserWindow.gBrowser;
+ let selectedBrowser = browserWindow.gBrowser.selectedBrowser;
+
+ // close any dialog that might still be open
+ yield ContentTask.spawn(selectedBrowser, null, function*() {
+ if (!content.window.gSubDialog) {
+ return;
+ }
+ content.window.gSubDialog.close();
+ });
+
let readyPromise = null;
if (selectedBrowser.currentURI.specIgnoringRef == "about:preferences") {
- readyPromise = new Promise((resolve) => {
- browserWindow.addEventListener("MozAfterPaint", function paneSwitch() {
- browserWindow.removeEventListener("MozAfterPaint", paneSwitch);
- resolve();
- });
- });
-
+ if (selectedBrowser.currentURI.spec == "about:preferences#" + primary.replace(/^pane/, "")) {
+ // We're already on the correct pane.
+ readyPromise = Promise.resolve();
+ } else {
+ readyPromise = paintPromise(browserWindow);
+ }
} else {
readyPromise = TestUtils.topicObserved("advanced-pane-loaded");
}
if (primary == "paneAdvanced") {
browserWindow.openAdvancedPreferences(advanced);
} else {
browserWindow.openPreferences(primary);
}
yield readyPromise;
- // close any dialog that might still be open
- yield ContentTask.spawn(selectedBrowser.selectedBrowser, null, function*() {
- content.window.gSubDialog.close();
+ if (customFn) {
+ let customPaintPromise = paintPromise(browserWindow);
+ yield* customFn(selectedBrowser);
+ yield customPaintPromise;
+ }
+});
+
+function paintPromise(browserWindow) {
+ return new Promise((resolve) => {
+ browserWindow.addEventListener("MozAfterPaint", function onPaint() {
+ browserWindow.removeEventListener("MozAfterPaint", onPaint);
+ resolve();
+ });
});
-});
+}
+
+function* DNTDialog(aBrowser) {
+ yield ContentTask.spawn(aBrowser, null, function* () {
+ content.document.getElementById("doNotTrackSettings").click();
+ });
+}
+
+function* certManager(aBrowser) {
+ yield ContentTask.spawn(aBrowser, null, function* () {
+ content.document.getElementById("viewCertificatesButton").click();
+ });
+}
--- a/config/recurse.mk
+++ b/config/recurse.mk
@@ -166,16 +166,12 @@ toolkit/library/target: widget/gtk/mozgt
endif
ifdef MOZ_LDAP_XPCOM
ldap/target: config/external/nss/target mozglue/build/target
toolkit/library/target: ldap/target
endif
ifeq ($(MOZ_REPLACE_MALLOC_LINKAGE),dummy library)
mozglue/build/target memory/replace/logalloc/replay/target: memory/replace/dummy/target
endif
-# js/src/target can end up invoking js/src/host rules (through object files
-# depending on jsautokw.h, which depends on host_jskwgen, and that can't
-# happen at the same time (bug #1146738)
-js/src/target: js/src/host
endif
# Most things are built during compile (target/host), but some things happen during export
# Those need to depend on config/export for system wrappers.
$(addprefix build/unix/stdc++compat/,target host) build/clang-plugin/target: config/export
--- a/devtools/client/debugger/test/mochitest/browser2.ini
+++ b/devtools/client/debugger/test/mochitest/browser2.ini
@@ -202,17 +202,17 @@ skip-if = e10s && debug
skip-if = e10s && debug
[browser_dbg_pretty-print-08.js]
skip-if = e10s && debug
[browser_dbg_pretty-print-09.js]
skip-if = e10s && debug
[browser_dbg_pretty-print-10.js]
skip-if = e10s && debug
[browser_dbg_pretty-print-11.js]
-skip-if = e10s && debug
+skip-if = e10s && debug || true
[browser_dbg_pretty-print-12.js]
skip-if = e10s && debug
[browser_dbg_pretty-print-13.js]
skip-if = e10s && debug
[browser_dbg_pretty-print-on-paused.js]
skip-if = e10s && debug
[browser_dbg_progress-listener-bug.js]
skip-if = e10s && debug
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -546,18 +546,17 @@ Toolbox.prototype = {
shortcuts.on(L10N.getStr("toolbox.help.key"), selectOptions);
},
_splitConsoleOnKeypress: function (e) {
if (e.keyCode === KeyCodes.DOM_VK_ESCAPE) {
this.toggleSplitConsole();
// If the debugger is paused, don't let the ESC key stop any pending
// navigation.
- let jsdebugger = this.getPanel("jsdebugger");
- if (jsdebugger && jsdebugger.panelWin.gThreadClient.state == "paused") {
+ if (this._threadClient.state == "paused") {
e.preventDefault();
}
}
},
/**
* Add a shortcut key that should work when a split console
* has focus to the toolbox.
--- a/devtools/client/responsive.html/actions/touch-simulation.js
+++ b/devtools/client/responsive.html/actions/touch-simulation.js
@@ -7,16 +7,16 @@
"use strict";
const {
UPDATE_TOUCH_SIMULATION_ENABLED
} = require("./index");
module.exports = {
- updateTouchSimulationEnabled(enabled) {
+ updateTouchSimulationEnabled(enabled = false) {
return {
type: UPDATE_TOUCH_SIMULATION_ENABLED,
enabled,
};
},
};
--- a/devtools/client/responsive.html/app.js
+++ b/devtools/client/responsive.html/app.js
@@ -43,16 +43,17 @@ let App = createClass({
},
onChangeViewportDevice(id, device) {
window.postMessage({
type: "change-viewport-device",
device,
}, "*");
this.props.dispatch(changeDevice(id, device.name));
+ this.props.dispatch(updateTouchSimulationEnabled(device.touch));
},
onContentResize({ width, height }) {
window.postMessage({
type: "content-resize",
width,
height,
}, "*");
--- a/devtools/client/responsive.html/manager.js
+++ b/devtools/client/responsive.html/manager.js
@@ -419,19 +419,20 @@ ResponsiveUI.prototype = {
let { browserWindow, tab } = this;
if (event.origin !== "chrome://devtools") {
return;
}
switch (event.data.type) {
case "change-viewport-device":
- let { userAgent, pixelRatio } = event.data.device;
+ let { userAgent, pixelRatio, touch } = event.data.device;
this.updateUserAgent(userAgent);
this.updateDPPX(pixelRatio);
+ this.updateTouchSimulation(touch);
break;
case "content-resize":
let { width, height } = event.data;
this.emit("content-resize", {
width,
height,
});
break;
--- a/devtools/client/responsive.html/test/browser/browser_device_change.js
+++ b/devtools/client/responsive.html/test/browser/browser_device_change.js
@@ -35,83 +35,81 @@ addRDMTask(TEST_URL, function* ({ ui, ma
// Wait until the viewport has been added and the device list has been loaded
yield waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
// Test defaults
testViewportDimensions(ui, 320, 480);
yield testUserAgent(ui, DEFAULT_UA);
- testDevicePixelRatio(yield getViewportDevicePixelRatio(ui), DEFAULT_DPPX);
+ yield testDevicePixelRatio(ui, DEFAULT_DPPX);
+ yield testTouchEventsOverride(ui, false);
testViewportSelectLabel(ui, "no device selected");
- let waitingPixelRatio = onceDevicePixelRatioChange(ui);
-
- // Test device with custom UA
+ // Test device with custom properties
yield switchDevice(ui, "Fake Phone RDM Test");
yield waitForViewportResizeTo(ui, testDevice.width, testDevice.height);
yield testUserAgent(ui, testDevice.userAgent);
-
- // Test device with custom pixelRatio
- testDevicePixelRatio(yield waitingPixelRatio, testDevice.pixelRatio);
- waitingPixelRatio = onceDevicePixelRatioChange(ui);
+ yield testDevicePixelRatio(ui, testDevice.pixelRatio);
+ yield testTouchEventsOverride(ui, true);
// Test resetting device when resizing viewport
yield testViewportResize(ui, ".viewport-vertical-resize-handle",
[-10, -10], [testDevice.width, testDevice.height - 10], [0, -10], ui);
yield testUserAgent(ui, DEFAULT_UA);
+ yield testDevicePixelRatio(ui, DEFAULT_DPPX);
+ yield testTouchEventsOverride(ui, false);
testViewportSelectLabel(ui, "no device selected");
- testDevicePixelRatio(yield waitingPixelRatio, DEFAULT_DPPX);
- // Test device where UA field is blank
+ // Test device with generic properties
yield switchDevice(ui, "Laptop (1366 x 768)");
yield waitForViewportResizeTo(ui, 1366, 768);
yield testUserAgent(ui, DEFAULT_UA);
+ yield testDevicePixelRatio(ui, 1);
+ yield testTouchEventsOverride(ui, false);
ok(removeDevice(testDevice),
"Test Device properly removed.");
});
function testViewportDimensions(ui, w, h) {
let viewport = ui.toolWindow.document.querySelector(".viewport-content");
is(ui.toolWindow.getComputedStyle(viewport).getPropertyValue("width"),
`${w}px`, `Viewport should have width of ${w}px`);
is(ui.toolWindow.getComputedStyle(viewport).getPropertyValue("height"),
`${h}px`, `Viewport should have height of ${h}px`);
}
-function testViewportSelectLabel(ui, label) {
+function testViewportSelectLabel(ui, expected) {
let select = ui.toolWindow.document.querySelector(".viewport-device-selector");
- is(select.selectedOptions[0].textContent, label,
- `Select label should be changed to ${label}`);
+ is(select.selectedOptions[0].textContent, expected,
+ `Select label should be changed to ${expected}`);
}
-function* testUserAgent(ui, value) {
+function* testUserAgent(ui, expected) {
let ua = yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
return content.navigator.userAgent;
});
- is(ua, value, `UA should be set to ${value}`);
+ is(ua, expected, `UA should be set to ${expected}`);
+}
+
+function* testDevicePixelRatio(ui, expected) {
+ let dppx = yield getViewportDevicePixelRatio(ui);
+ is(dppx, expected, `devicePixelRatio should be set to ${expected}`);
}
-function testDevicePixelRatio(dppx, expected) {
- is(dppx, expected, `devicePixelRatio should be set to ${expected}`);
+function* testTouchEventsOverride(ui, expected) {
+ let { document } = ui.toolWindow;
+ let touchButton = document.querySelector("#global-touch-simulation-button");
+
+ let flag = yield ui.emulationFront.getTouchEventsOverride();
+ is(flag === Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED, expected,
+ `Touch events override should be ${expected ? "enabled" : "disabled"}`);
+ is(touchButton.classList.contains("active"), expected,
+ `Touch simulation button should be ${expected ? "" : "not"} active.`);
}
function* getViewportDevicePixelRatio(ui) {
return yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
return content.devicePixelRatio;
});
}
-
-function onceDevicePixelRatioChange(ui) {
- return ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
- let pixelRatio = content.devicePixelRatio;
- let mql = content.matchMedia(`(resolution: ${pixelRatio}dppx)`);
-
- return new Promise(resolve => {
- mql.addListener(function listener() {
- mql.removeListener(listener);
- resolve(content.devicePixelRatio);
- });
- });
- });
-}
--- a/devtools/client/webconsole/test/browser_webconsole_output_06.js
+++ b/devtools/client/webconsole/test/browser_webconsole_output_06.js
@@ -193,17 +193,17 @@ var inputTests = [
printOutput: "[object Object]",
inspectable: true,
variablesViewLabel: "Object[1]",
},
// 21
{
input: '({0: "a", 1: "b", length: 1})',
- output: 'Object { 1: "b", length: 1, 1 more\u2026 }',
+ output: 'Object { 0: "a", 1: "b", length: 1 }',
printOutput: "[object Object]",
inspectable: true,
variablesViewLabel: "Object",
},
// 22
{
input: '({0: "a", 1: "b", length: 2})',
@@ -220,17 +220,17 @@ var inputTests = [
printOutput: "[object Object]",
inspectable: true,
variablesViewLabel: "Object[3]",
},
// 24
{
input: '({0: "a", 2: "b", length: 2})',
- output: 'Object { 2: "b", length: 2, 1 more\u2026 }',
+ output: 'Object { 0: "a", 2: "b", length: 2 }',
printOutput: "[object Object]",
inspectable: true,
variablesViewLabel: "Object",
},
// 25
{
input: '({0: "a", 2: "b", length: 3})',
@@ -238,26 +238,26 @@ var inputTests = [
printOutput: "[object Object]",
inspectable: true,
variablesViewLabel: "Object[3]",
},
// 26
{
input: '({0: "a", b: "b", length: 1})',
- output: 'Object { b: "b", length: 1, 1 more\u2026 }',
+ output: 'Object { 0: "a", b: "b", length: 1 }',
printOutput: "[object Object]",
inspectable: true,
variablesViewLabel: "Object",
},
// 27
{
input: '({0: "a", b: "b", length: 2})',
- output: 'Object { b: "b", length: 2, 1 more\u2026 }',
+ output: 'Object { 0: "a", b: "b", length: 2 }',
printOutput: "[object Object]",
inspectable: true,
variablesViewLabel: "Object",
},
// 28
{
input: '({42: "a"})',
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object.js
@@ -1085,38 +1085,38 @@ function enumWeakSetEntries(objectActor)
};
}
/**
* Functions for adding information to ObjectActor grips for the purpose of
* having customized output. This object holds arrays mapped by
* Debugger.Object.prototype.class.
*
- * In each array you can add functions that take two
+ * In each array you can add functions that take three
* arguments:
* - the ObjectActor instance and its hooks to make a preview for,
* - the grip object being prepared for the client,
* - the raw JS object after calling Debugger.Object.unsafeDereference(). This
* argument is only provided if the object is safe for reading properties and
* executing methods. See DevToolsUtils.isSafeJSObject().
*
* Functions must return false if they cannot provide preview
* information for the debugger object, or true otherwise.
*/
DebuggerServer.ObjectActorPreviewers = {
- String: [function (objectActor, grip) {
- return wrappedPrimitivePreviewer("String", String, objectActor, grip);
+ String: [function (objectActor, grip, rawObj) {
+ return wrappedPrimitivePreviewer("String", String, objectActor, grip, rawObj);
}],
- Boolean: [function (objectActor, grip) {
- return wrappedPrimitivePreviewer("Boolean", Boolean, objectActor, grip);
+ Boolean: [function (objectActor, grip, rawObj) {
+ return wrappedPrimitivePreviewer("Boolean", Boolean, objectActor, grip, rawObj);
}],
- Number: [function (objectActor, grip) {
- return wrappedPrimitivePreviewer("Number", Number, objectActor, grip);
+ Number: [function (objectActor, grip, rawObj) {
+ return wrappedPrimitivePreviewer("Number", Number, objectActor, grip, rawObj);
}],
Function: [function ({obj, hooks}, grip) {
if (obj.name) {
grip.name = obj.name;
}
if (obj.displayName) {
@@ -1374,47 +1374,46 @@ DebuggerServer.ObjectActorPreviewers = {
* The class to expect, eg. String. The valueOf() method of the class is
* invoked on the given object.
* @param ObjectActor objectActor
* The object actor
* @param Object grip
* The result grip to fill in
* @return Booolean true if the object was handled, false otherwise
*/
-function wrappedPrimitivePreviewer(className, classObj, objectActor, grip) {
+function wrappedPrimitivePreviewer(className, classObj, objectActor, grip, rawObj) {
let {obj, hooks} = objectActor;
if (!obj.proto || obj.proto.class != className) {
return false;
}
- let raw = obj.unsafeDereference();
let v = null;
try {
- v = classObj.prototype.valueOf.call(raw);
+ v = classObj.prototype.valueOf.call(rawObj);
} catch (ex) {
// valueOf() can throw if the raw JS object is "misbehaved".
return false;
}
if (v === null) {
return false;
}
- let canHandle = GenericObject(objectActor, grip, className === "String");
+ let canHandle = GenericObject(objectActor, grip, rawObj, className === "String");
if (!canHandle) {
return false;
}
grip.preview.wrappedValue =
hooks.createValueGrip(makeDebuggeeValueIfNeeded(obj, v));
return true;
}
-function GenericObject(objectActor, grip, specialStringBehavior = false) {
+function GenericObject(objectActor, grip, rawObj, specialStringBehavior = false) {
let {obj, hooks} = objectActor;
if (grip.preview || grip.displayString || hooks.getGripDepth() > 1) {
return false;
}
let i = 0, names = [];
let preview = grip.preview = {
kind: "Object",
@@ -1854,17 +1853,19 @@ DebuggerServer.ObjectActorPreviewers.Obj
} else {
items.push(null);
}
}
return true;
},
- GenericObject,
+ function Object(objectActor, grip, rawObj) {
+ return GenericObject(objectActor, grip, rawObj, /* specialStringBehavior = */ false);
+ },
];
/**
* Get thisDebugger.Object referent's `promiseState`.
*
* @returns Object
* An object of one of the following forms:
* - { state: "pending" }
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -12041,21 +12041,16 @@ nsDocument::UpdateVisibilityState()
{
dom::VisibilityState oldState = mVisibilityState;
mVisibilityState = GetVisibilityState();
if (oldState != mVisibilityState) {
nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
NS_LITERAL_STRING("visibilitychange"),
/* bubbles = */ true,
/* cancelable = */ false);
- nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
- NS_LITERAL_STRING("mozvisibilitychange"),
- /* bubbles = */ true,
- /* cancelable = */ false);
-
EnumerateActivityObservers(NotifyActivityChanged, nullptr);
}
if (mVisibilityState == dom::VisibilityState::Visible) {
MaybeActiveMediaComponents();
}
}
@@ -12105,37 +12100,23 @@ nsDocument::MaybeActiveMediaComponents()
mEverInForeground = true;
if (GetWindow()->GetMediaSuspend() == nsISuspendedTypes::SUSPENDED_BLOCK) {
GetWindow()->SetMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED);
}
}
NS_IMETHODIMP
-nsDocument::GetMozHidden(bool* aHidden)
-{
- *aHidden = MozHidden();
- return NS_OK;
-}
-
-NS_IMETHODIMP
nsDocument::GetHidden(bool* aHidden)
{
*aHidden = Hidden();
return NS_OK;
}
NS_IMETHODIMP
-nsDocument::GetMozVisibilityState(nsAString& aState)
-{
- WarnOnceAbout(ePrefixedVisibilityAPI);
- return GetVisibilityState(aState);
-}
-
-NS_IMETHODIMP
nsDocument::GetVisibilityState(nsAString& aState)
{
const EnumEntry& entry =
VisibilityStateValues::strings[static_cast<int>(mVisibilityState)];
aState.AssignASCII(entry.value, entry.length);
return NS_OK;
}
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2601,30 +2601,20 @@ public:
{
UnlockPointer(this);
}
#ifdef MOZILLA_INTERNAL_API
bool Hidden() const
{
return mVisibilityState != mozilla::dom::VisibilityState::Visible;
}
- bool MozHidden() const
- {
- WarnOnceAbout(ePrefixedVisibilityAPI);
- return Hidden();
- }
mozilla::dom::VisibilityState VisibilityState() const
{
return mVisibilityState;
}
- mozilla::dom::VisibilityState MozVisibilityState() const
- {
- WarnOnceAbout(ePrefixedVisibilityAPI);
- return VisibilityState();
- }
#endif
virtual mozilla::dom::StyleSheetList* StyleSheets() = 0;
void GetSelectedStyleSheetSet(nsAString& aSheetSet);
virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet) = 0;
virtual void GetLastStyleSheetSet(nsString& aSheetSet) = 0;
void GetPreferredStyleSheetSet(nsAString& aSheetSet);
virtual mozilla::dom::DOMStringList* StyleSheetSets() = 0;
virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) = 0;
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1684,17 +1684,18 @@ CanvasRenderingContext2D::SetErrorState(
}
void
CanvasRenderingContext2D::RegisterAllocation()
{
// XXX - It would make more sense to track the allocation in
// PeristentBufferProvider, rather than here.
static bool registered = false;
- if (!registered) {
+ // FIXME: Disable the reporter for now, see bug 1241865
+ if (!registered && false) {
registered = true;
RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
}
gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
JSContext* context = nsContentUtils::GetCurrentJSContext();
if (context) {
JS_updateMallocCounter(context, mWidth * mHeight * 4);
--- a/dom/canvas/OffscreenCanvas.cpp
+++ b/dom/canvas/OffscreenCanvas.cpp
@@ -142,20 +142,19 @@ OffscreenCanvas::GetContext(JSContext* a
contextType == CanvasContextType::WebGL2) {
WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
gl::GLContext* gl = webGL->GL();
mCanvasRenderer->mContext = mCurrentContext;
mCanvasRenderer->SetActiveThread();
mCanvasRenderer->mGLContext = gl;
mCanvasRenderer->SetIsAlphaPremultiplied(webGL->IsPremultAlpha() || !gl->Caps().alpha);
- if (ImageBridgeChild::IsCreated()) {
+ if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
- mCanvasClient = ImageBridgeChild::GetSingleton()->
- CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags);
+ mCanvasClient = imageBridge->CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags);
mCanvasRenderer->SetCanvasClient(mCanvasClient);
gl::GLScreenBuffer* screen = gl->Screen();
gl::SurfaceCaps caps = screen->mCaps;
auto forwarder = mCanvasClient->GetForwarder();
UniquePtr<gl::SurfaceFactory> factory =
gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags);
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -681,18 +681,21 @@ WebGLContext::CreateAndInitGLWith(FnCrea
while (!fallbackCaps.empty()) {
const gl::SurfaceCaps& caps = fallbackCaps.front();
potentialGL = fnCreateGL(caps, flags, this, out_failReasons);
if (potentialGL)
break;
fallbackCaps.pop();
}
- if (!potentialGL)
+ if (!potentialGL) {
+ out_failReasons->push_back(FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_CAPS",
+ "Exhausted GL driver caps."));
return false;
+ }
FailureReason reason;
mGL_OnlyClearInDestroyResourcesAndContext = potentialGL;
MOZ_RELEASE_ASSERT(gl);
if (!InitAndValidateGL(&reason)) {
DestroyResourcesAndContext();
MOZ_RELEASE_ASSERT(!gl);
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -647,42 +647,36 @@ EventListenerManager::RemoveEventListene
{
if (!aListenerHolder || !aEventMessage || mClearingListeners) {
return;
}
Listener* listener;
uint32_t count = mListeners.Length();
- uint32_t typeCount = 0;
bool deviceType = IsDeviceType(aEventMessage);
RefPtr<EventListenerManager> kungFuDeathGrip(this);
for (uint32_t i = 0; i < count; ++i) {
listener = &mListeners.ElementAt(i);
if (EVENT_TYPE_EQUALS(listener, aEventMessage, aUserType, aTypeString,
aAllEvents)) {
- ++typeCount;
if (listener->mListener == aListenerHolder &&
listener->mFlags.EqualsForRemoval(aFlags)) {
mListeners.RemoveElementAt(i);
- --count;
NotifyEventListenerRemoved(aUserType);
- if (!deviceType) {
- return;
+ if (!aAllEvents && deviceType) {
+ DisableDevice(aEventMessage);
}
- --typeCount;
+ return;
}
}
}
- if (!aAllEvents && deviceType && typeCount == 0) {
- DisableDevice(aEventMessage);
- }
}
bool
EventListenerManager::ListenerCanHandle(const Listener* aListener,
const WidgetEvent* aEvent,
EventMessage aEventMessage) const
{
@@ -922,16 +916,19 @@ EventListenerManager::RemoveEventHandler
}
EventMessage eventMessage = nsContentUtils::GetEventMessage(aName);
Listener* listener = FindEventHandler(eventMessage, aName, aTypeString);
if (listener) {
mListeners.RemoveElementAt(uint32_t(listener - &mListeners.ElementAt(0)));
NotifyEventListenerRemoved(aName);
+ if (IsDeviceType(eventMessage)) {
+ DisableDevice(eventMessage);
+ }
}
}
nsresult
EventListenerManager::CompileEventHandlerInternal(Listener* aListener,
const nsAString* aBody,
Element* aElement)
{
--- a/dom/events/test/test_bug742376.html
+++ b/dom/events/test/test_bug742376.html
@@ -26,35 +26,49 @@ function hasListeners() {
dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_ROTATION_VECTOR, window) ||
dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_GAME_ROTATION_VECTOR, window);
}
is(hasListeners(), false, "Must not have listeners before tests start");
function dumbListener(event) {}
function dumbListener2(event) {}
+function dumbListener3(event) {}
window.addEventListener("deviceorientation", dumbListener, false);
+window.addEventListener("random_event_name", function() {}, false);
window.addEventListener("deviceorientation", dumbListener2, false);
is(hasListeners(), true, "Listeners should have been added");
window.setTimeout(function() {
window.removeEventListener("deviceorientation", dumbListener, false);
is(hasListeners(), true, "Only some listeners should have been removed");
window.setTimeout(function() {
window.removeEventListener("deviceorientation", dumbListener2, false);
window.setTimeout(function() {
is(hasListeners(), false, "Listeners should have been removed");
- SimpleTest.finish();
+ testEventHandler();
}, 0);
}, 0);
}, 0);
+function testEventHandler() {
+ window.ondeviceorientation = function() {}
+ window.setTimeout(function() {
+ is(hasListeners(), true, "Handler should have been added");
+ window.ondeviceorientation = null;
+ window.setTimeout(function() {
+ is(hasListeners(), false, "Handler should have been removed");
+ SimpleTest.finish();
+ }, 0);
+ }, 0)
+}
+
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -260,17 +260,17 @@ FetchRequest(nsIGlobalObject* aGlobal, c
RefPtr<WorkerFetchResolver> resolver = WorkerFetchResolver::Create(worker, p);
if (!resolver) {
NS_WARNING("Could not add WorkerFetchResolver workerHolder to worker");
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
return nullptr;
}
RefPtr<MainThreadFetchRunnable> run = new MainThreadFetchRunnable(resolver, r);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(run));
+ worker->DispatchToMainThread(run.forget());
}
return p.forget();
}
MainThreadFetchResolver::MainThreadFetchResolver(Promise* aPromise)
: mPromise(aPromise)
{
@@ -1027,17 +1027,22 @@ FetchBody<Derived>::BeginConsumeBody()
// The FetchBody is not thread-safe refcounted. We addref it here and release
// it once the stream read is finished.
if (!AddRefObject()) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIRunnable> r = new BeginConsumeBodyRunnable<Derived>(this);
- nsresult rv = NS_DispatchToMainThread(r);
+ nsresult rv = NS_OK;
+ if (mWorkerPrivate) {
+ rv = mWorkerPrivate->DispatchToMainThread(r.forget());
+ } else {
+ rv = NS_DispatchToMainThread(r.forget());
+ }
if (NS_WARN_IF(NS_FAILED(rv))) {
ReleaseObject();
return rv;
}
return NS_OK;
}
/*
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -4123,16 +4123,27 @@ HTMLInputElement::MaybeInitPickers(Event
}
if (mType == NS_FORM_INPUT_DATE) {
return InitDatePicker();
}
return NS_OK;
}
+/**
+ * Return true if the input event should be ignore because of it's modifiers
+ */
+static bool
+IgnoreInputEventWithModifier(WidgetInputEvent* aEvent)
+{
+ return aEvent->IsShift() || aEvent->IsControl() || aEvent->IsAlt() ||
+ aEvent->IsMeta() || aEvent->IsAltGraph() || aEvent->IsFn() ||
+ aEvent->IsOS();
+}
+
nsresult
HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
{
if (!aVisitor.mPresContext) {
// Hack alert! In order to open file picker even in case the element isn't
// in document, try to init picker even without PresContext.
return MaybeInitPickers(aVisitor);
}
@@ -4269,20 +4280,17 @@ HTMLInputElement::PostHandleEvent(EventC
}
if (NS_SUCCEEDED(rv)) {
WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
if (mType == NS_FORM_INPUT_NUMBER &&
keyEvent && keyEvent->mMessage == eKeyPress &&
aVisitor.mEvent->IsTrusted() &&
(keyEvent->mKeyCode == NS_VK_UP || keyEvent->mKeyCode == NS_VK_DOWN) &&
- !(keyEvent->IsShift() || keyEvent->IsControl() ||
- keyEvent->IsAlt() || keyEvent->IsMeta() ||
- keyEvent->IsAltGraph() || keyEvent->IsFn() ||
- keyEvent->IsOS())) {
+ !IgnoreInputEventWithModifier(keyEvent)) {
// We handle the up/down arrow keys specially for <input type=number>.
// On some platforms the editor for the nested text control will
// process these keys to send the cursor to the start/end of the text
// control and as a result aVisitor.mEventStatus will already have been
// set to nsEventStatus_eConsumeNoDefault. However, we know that
// whenever the up/down arrow keys cause the value of the number
// control to change the string in the text control will change, and
// the cursor will be moved to the end of the text control, overwriting
@@ -4491,20 +4499,17 @@ HTMLInputElement::PostHandleEvent(EventC
aVisitor.mDOMEvent->StopPropagation();
} else {
rv = NS_ERROR_FAILURE;
}
}
}
if (mType == NS_FORM_INPUT_NUMBER && aVisitor.mEvent->IsTrusted()) {
if (mouseEvent->button == WidgetMouseEvent::eLeftButton &&
- !(mouseEvent->IsShift() || mouseEvent->IsControl() ||
- mouseEvent->IsAlt() || mouseEvent->IsMeta() ||
- mouseEvent->IsAltGraph() || mouseEvent->IsFn() ||
- mouseEvent->IsOS())) {
+ !IgnoreInputEventWithModifier(mouseEvent)) {
nsNumberControlFrame* numberControlFrame =
do_QueryFrame(GetPrimaryFrame());
if (numberControlFrame) {
if (aVisitor.mEvent->mMessage == eMouseDown &&
IsMutable()) {
switch (numberControlFrame->GetSpinButtonForPointerEvent(
aVisitor.mEvent->AsMouseEvent())) {
case nsNumberControlFrame::eSpinButtonUp:
@@ -4657,20 +4662,17 @@ HTMLInputElement::PostHandleEventForRang
case eTouchStart: {
if (mIsDraggingRange) {
break;
}
if (nsIPresShell::GetCapturingContent()) {
break; // don't start drag if someone else is already capturing
}
WidgetInputEvent* inputEvent = aVisitor.mEvent->AsInputEvent();
- if (inputEvent->IsShift() || inputEvent->IsControl() ||
- inputEvent->IsAlt() || inputEvent->IsMeta() ||
- inputEvent->IsAltGraph() || inputEvent->IsFn() ||
- inputEvent->IsOS()) {
+ if (IgnoreInputEventWithModifier(inputEvent)) {
break; // ignore
}
if (aVisitor.mEvent->mMessage == eMouseDown) {
if (aVisitor.mEvent->AsMouseEvent()->buttons ==
WidgetMouseEvent::eLeftButtonFlag) {
StartRangeThumbDrag(inputEvent);
} else if (mIsDraggingRange) {
CancelRangeThumbDrag();
--- a/dom/interfaces/core/nsIDOMDocument.idl
+++ b/dom/interfaces/core/nsIDOMDocument.idl
@@ -392,19 +392,17 @@ interface nsIDOMDocument : nsIDOMNode
* @see <http://dvcs.w3.org/hg/pointerlock/raw-file/default/index.html>
*/
void mozExitPointerLock();
/**
* Visibility API implementation.
*/
readonly attribute boolean hidden;
- readonly attribute boolean mozHidden;
readonly attribute DOMString visibilityState;
- readonly attribute DOMString mozVisibilityState;
/**
* Returns "BackCompat" if we're in quirks mode or "CSS1Compat" if we're in
* strict mode. (XML documents are always in strict mode.)
*/
readonly attribute DOMString compatMode;
/**
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -148,18 +148,16 @@ NoExposedPropsWarning=Exposing chrome JS
MutationEventWarning=Use of Mutation Events is deprecated. Use MutationObserver instead.
# LOCALIZATION NOTE: Do not translate "Components"
ComponentsWarning=The Components object is deprecated. It will soon be removed.
PluginHangUITitle=Warning: Unresponsive plugin
PluginHangUIMessage=%S may be busy, or it may have stopped responding. You can stop the plugin now, or you can continue to see if the plugin will complete.
PluginHangUIWaitButton=Continue
PluginHangUIStopButton=Stop plugin
PrefixedFullscreenAPIWarning=Prefixed Fullscreen API is deprecated. Please use unprefixed API for fullscreen. For more help https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API
-# LOCALIZATION NOTE: Do not translate "mozHidden", "mozVisibilityState", "hidden", or "visibilityState"
-PrefixedVisibilityAPIWarning=‘mozHidden’ and ‘mozVisibilityState’ are deprecated. Please use the unprefixed ‘hidden’ and ‘visibilityState’ instead.
# LOCALIZATION NOTE: Do not translate "NodeIterator" or "detach()".
NodeIteratorDetachWarning=Calling detach() on a NodeIterator no longer has an effect.
# LOCALIZATION NOTE: Do not translate "LenientThis" and "this"
LenientThisWarning=Ignoring get or set of property that has [LenientThis] because the “this” object is incorrect.
# LOCALIZATION NOTE: Do not translate "nsIDOMWindowUtils", "getWindowWithOuterId", or "nsIWindowMediator"
GetWindowWithOuterIdWarning=Use of nsIDOMWindowUtils.getOuterWindowWithId() is deprecated. Instead, use the nsIWindowMediator method of the same name.
# LOCALIZATION NOTE: Do not translate "getPreventDefault" or "defaultPrevented".
GetPreventDefaultWarning=Use of getPreventDefault() is deprecated. Use defaultPrevented instead.
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -8,16 +8,17 @@
#include "MediaDecoderStateMachine.h"
#include "MP4Demuxer.h"
#include "mozilla/Preferences.h"
#include "nsCharSeparatedTokenizer.h"
#ifdef MOZ_EME
#include "mozilla/CDMProxy.h"
#endif
#include "mozilla/Logging.h"
+#include "mozilla/SharedThreadPool.h"
#include "nsMimeTypes.h"
#include "nsContentTypeParser.h"
#include "VideoUtils.h"
#ifdef XP_WIN
#include "mozilla/WindowsVersion.h"
#endif
#ifdef MOZ_WIDGET_ANDROID
--- a/dom/media/mediasource/AutoTaskQueue.h
+++ b/dom/media/mediasource/AutoTaskQueue.h
@@ -3,16 +3,17 @@
/* 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_AUTOTASKQUEUE_H_
#define MOZILLA_AUTOTASKQUEUE_H_
#include "mozilla/RefPtr.h"
+#include "mozilla/SharedThreadPool.h"
#include "mozilla/TaskQueue.h"
namespace mozilla {
// A convenience TaskQueue not requiring explicit shutdown.
class AutoTaskQueue : public AbstractThread
{
public:
--- a/dom/media/platforms/wmf/DXVA2Manager.cpp
+++ b/dom/media/platforms/wmf/DXVA2Manager.cpp
@@ -423,17 +423,17 @@ D3D9DXVA2Manager::Init(nsACString& aFail
mDecoderService = decoderService;
mResetToken = resetToken;
mD3D9 = d3d9Ex;
mDevice = device;
mDeviceManager = deviceManager;
mSyncSurface = syncSurf;
- mTextureClientAllocator = new D3D9RecycleAllocator(layers::ImageBridgeChild::GetSingleton(),
+ mTextureClientAllocator = new D3D9RecycleAllocator(layers::ImageBridgeChild::GetSingleton().get(),
mDevice);
mTextureClientAllocator->SetMaxPoolSize(5);
Telemetry::Accumulate(Telemetry::MEDIA_DECODER_BACKEND_USED,
uint32_t(media::MediaDecoderBackend::WMFDXVA2D3D9));
reporter.SetSuccessful();
@@ -747,17 +747,17 @@ D3D11DXVA2Manager::Init(nsACString& aFai
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
hr = mDevice->CreateTexture2D(&desc, NULL, getter_AddRefs(mSyncSurface));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
- mTextureClientAllocator = new D3D11RecycleAllocator(layers::ImageBridgeChild::GetSingleton(),
+ mTextureClientAllocator = new D3D11RecycleAllocator(layers::ImageBridgeChild::GetSingleton().get(),
mDevice);
mTextureClientAllocator->SetMaxPoolSize(5);
Telemetry::Accumulate(Telemetry::MEDIA_DECODER_BACKEND_USED,
uint32_t(media::MediaDecoderBackend::WMFDXVA2D3D11));
reporter.SetSuccessful();
--- a/dom/media/systemservices/MediaSystemResourceManager.cpp
+++ b/dom/media/systemservices/MediaSystemResourceManager.cpp
@@ -37,17 +37,18 @@ MediaSystemResourceManager::Shutdown()
sSingleton->CloseIPC();
sSingleton = nullptr;
}
}
/* static */ void
MediaSystemResourceManager::Init()
{
- if (!ImageBridgeChild::IsCreated()) {
+ RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
+ if (!imageBridge) {
NS_WARNING("ImageBridge does not exist");
return;
}
if (InImageBridgeChildThread()) {
if (!sSingleton) {
#ifdef DEBUG
static int timesCreated = 0;
@@ -68,17 +69,17 @@ MediaSystemResourceManager::Init()
if (!sSingleton) {
sSingleton = new MediaSystemResourceManager();
}
ReentrantMonitorAutoEnter childThreadAutoMon(barrier);
done = true;
barrier.NotifyAll();
});
- ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(runnable.forget());
+ imageBridge->GetMessageLoop()->PostTask(runnable.forget());
// should stop the thread until done.
while (!done) {
barrier.Wait();
}
}
MediaSystemResourceManager::MediaSystemResourceManager()
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -672,17 +672,17 @@ PluginInstanceParent::RecvInitDXGISurfac
return true;
}
ImageContainer *container = GetImageContainer();
if (!container) {
return true;
}
- ImageBridgeChild* forwarder = ImageBridgeChild::GetSingleton();
+ RefPtr<ImageBridgeChild> forwarder = ImageBridgeChild::GetSingleton();
if (!forwarder) {
return true;
}
RefPtr<ID3D11Device> d3d11 = DeviceManagerDx::Get()->GetContentDevice();
if (!d3d11) {
return true;
}
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -3125,17 +3125,17 @@ PluginModuleParent::RecvReturnSitesWithD
mSitesWithDataCallbacks.erase(aCallbackId);
return true;
}
layers::TextureClientRecycleAllocator*
PluginModuleParent::EnsureTextureAllocator()
{
if (!mTextureAllocator) {
- mTextureAllocator = new TextureClientRecycleAllocator(ImageBridgeChild::GetSingleton());
+ mTextureAllocator = new TextureClientRecycleAllocator(ImageBridgeChild::GetSingleton().get());
}
return mTextureAllocator;
}
#ifdef MOZ_CRASHREPORTER_INJECTOR
// We only add the crash reporter to subprocess which have the filename
// FlashPlayerPlugin*
--- a/dom/security/test/sri/iframe_require-sri-for_main.html
+++ b/dom/security/test/sri/iframe_require-sri-for_main.html
@@ -1,31 +1,40 @@
<script>
window.hasCORSLoaded = false; // set through script_crossdomain1.js
</script>
-<!-- cors-enabled. should be loaded -->
+<!-- script tag cors-enabled. should be loaded -->
<script src="http://example.com/tests/dom/security/test/sri/script_crossdomain1.js"
crossorigin=""
integrity="sha512-9Tv2DL1fHvmPQa1RviwKleE/jq72jgxj8XGLyWn3H6Xp/qbtfK/jZINoPFAv2mf0Nn1TxhZYMFULAbzJNGkl4Q=="
onload="parent.postMessage('good_sriLoaded', '*');"></script>
-<!-- cors but not using SRI. should trigger onerror -->
+<!-- script tag cors but not using SRI. should trigger onerror -->
<script src="http://example.com/tests/dom/security/test/sri/script_crossdomain5.js"
onload="parent.postMessage('bad_nonsriLoaded', '*');"
onerror="parent.postMessage('good_nonsriBlocked', '*');"></script>
-<!-- cors and integrity. it should just load fine. -->
+<!-- svg:script tag with cors but not using SRI. should trigger onerror -->
+<svg xmlns="http://www.w3.org/2000/svg">
+ <script xlink:href="http://example.com/tests/dom/security/test/sri/script_crossdomain3.js"
+ onload="parent.postMessage('bad_svg_nonsriLoaded', '*');"
+ onerror="parent.postMessage('good_svg_nonsriBlocked', '*');"></script>
+ ></script>
+</svg>
+
+<!-- stylesheet with cors and integrity. it should just load fine. -->
<link rel="stylesheet" href="style1.css"
integrity="sha256-qs8lnkunWoVldk5d5E+652yth4VTSHohlBKQvvgGwa8="
onload="parent.postMessage('good_sriLoaded', '*');">
-<!-- not using SRI, should trigger onerror -->
+<!-- stylesheet not using SRI, should trigger onerror -->
<link rel="stylesheet" href="style3.css"
onload="parent.postMessage('bad_nonsriLoaded', '*');"
onerror="parent.postMessage('good_nonsriBlocked', '*');">
+
<p id="black-text">black text</p>
<script>
window.onload = function() {
parent.postMessage("finish", '*');
}
</script>
--- a/dom/security/test/sri/test_require-sri-for_csp_directive.html
+++ b/dom/security/test/sri/test_require-sri-for_csp_directive.html
@@ -22,16 +22,22 @@
ok(true, "Eligible SRI resources was correctly loaded.");
break;
case 'bad_nonsriLoaded':
ok(false, "Eligible non-SRI resource should be blocked by the CSP!");
break;
case 'good_nonsriBlocked':
ok(true, "Eligible non-SRI resources was correctly blocked by the CSP.");
break;
+ case 'bad_svg_nonsriLoaded':
+ ok(false, 'Eligible non-SRI resource should be blocked by the CSP.');
+ break;
+ case 'good_svg_nonsriBlocked':
+ ok(true, 'Eligible non-SRI svg script was correctly blocked by the CSP.');
+ break;
case 'finish':
var blackText = frame.contentDocument.getElementById('black-text');
var blackTextColor = frame.contentWindow.getComputedStyle(blackText, null).getPropertyValue('color');
ok(blackTextColor == 'rgb(0, 0, 0)', "The second part should not be black.");
removeEventListener('message', handler);
SimpleTest.finish();
break;
default:
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/bugs/bug918719.sjs
@@ -0,0 +1,47 @@
+// Keep track of one in-flight XHR by allowing secondary ones to
+// tell us when the client has received a Progress event and wants
+// more data or to stop the in-flight XHR.
+
+const STATE_NAME = "bug918719_loading_event_test";
+const CHUNK_DATA = "chunk";
+
+function setReq(req) {
+ setObjectState(STATE_NAME, req);
+}
+
+function getReq() {
+ let req;
+ getObjectState(STATE_NAME, function(v) {
+ req = v;
+ });
+ return req;
+}
+
+function handleRequest(request, response)
+{
+ var pairs = request.queryString.split("&");
+ var command = pairs.shift();
+
+ response.setHeader("Content-Type", "text/plain");
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ switch(command) {
+ case "more":
+ getReq().write(CHUNK_DATA);
+ break;
+
+ case "done":
+ getReq().finish();
+ setReq(null);
+ break;
+
+ default:
+ response.processAsync();
+ response.write(CHUNK_DATA);
+ setReq(response);
+ return;
+ }
+
+ response.setHeader("Content-Length", "2", false);
+ response.write("ok");
+}
--- a/dom/tests/mochitest/bugs/mochitest.ini
+++ b/dom/tests/mochitest/bugs/mochitest.ini
@@ -2,16 +2,17 @@
support-files =
bug289714.sjs
bug346659-echoer.html
bug346659-opener-echoer.html
bug346659-opener.html
bug346659-parent-echoer.html
bug346659-parent.html
bug458091_child.html
+ bug918719.sjs
child_bug260264.html
devicemotion_inner.html
devicemotion_outer.html
file_bug291653.html
file_bug406375.html
file_bug504862.html
file_bug593174_1.html
file_bug593174_2.html
@@ -150,16 +151,17 @@ skip-if = (buildapp == 'b2g' && toolkit
[test_bug809290.html]
[test_bug817476.html]
[test_bug823173.html]
[test_bug848088.html]
[test_bug850517.html]
[test_bug857555.html]
[test_bug862540.html]
[test_bug876098.html]
+[test_bug918719.html]
[test_bug927901.html]
[test_devicemotion_multiple_listeners.html]
skip-if = toolkit == 'android' #bug 775227
[test_domparser_after_blank.html]
[test_errorReporting.html]
[test_onerror_message.html]
[test_protochains.html]
[test_resize_move_windows.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/bugs/test_bug918719.html
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=918719
+-->
+<head>
+ <title>Test for Bug 918719</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=918719">Mozilla Bug 918719</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+const SERVER_URL = "bug918719.sjs";
+
+function sendCommand(cmd) {
+ let xhr = new XMLHttpRequest();
+ xhr.open("get", SERVER_URL + "?" + cmd);
+ xhr.send();
+}
+
+function runTest() {
+ // Manipulate one in-flight XHR using secondary command XHRs, to guarantee
+ // that multiple OnDataAvailable events are triggered (which are where
+ // LOADING readystatechanges are triggered). We return a promise that will
+ // resolve with a count of the number of LOADING events that were detected.
+
+ return new Promise((resolve, reject) => {
+ let xhr = new XMLHttpRequest();
+ let numProgressEvents = 0;
+ let numLoadingEvents = 0;
+
+ xhr.onreadystatechange = e => {
+ if (xhr.readyState === xhr.LOADING) {
+ ++numLoadingEvents;
+ }
+ };
+
+ xhr.onprogress = e => {
+ if (++numProgressEvents < 2) {
+ sendCommand("more");
+ } else {
+ sendCommand("done");
+ }
+ };
+
+ xhr.onerror = e => {
+ reject(e);
+ };
+
+ xhr.onloadend = e => {
+ resolve(numLoadingEvents);
+ };
+
+ xhr.open("GET", SERVER_URL);
+ xhr.send();
+ });
+}
+
+function prefChangePromise(args) {
+ return new Promise(function(resolve) {
+ SpecialPowers.pushPrefEnv(args, resolve);
+ });
+}
+
+runTest().then(function(count) {
+ ok(count === 1, "Only one loading readystatechange event should have been fired with the pref off.");
+}).then(function() {
+ return prefChangePromise({"set": [["dom.fire_extra_xhr_loading_readystatechanges", true]]});
+}).then(function() {
+ return runTest();
+}).then(function(count) {
+ ok(count > 1, "Multiple loading readystatechange events should have been fired with the pref on.");
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -273,19 +273,17 @@ partial interface Document {
// this is deprecated from CustomElements v0
[Throws, Func="CustomElementsRegistry::IsCustomElementsEnabled"]
object registerElement(DOMString name, optional ElementRegistrationOptions options);
};
// http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PageVisibility/Overview.html#sec-document-interface
partial interface Document {
readonly attribute boolean hidden;
- readonly attribute boolean mozHidden;
readonly attribute VisibilityState visibilityState;
- readonly attribute VisibilityState mozVisibilityState;
};
// http://dev.w3.org/csswg/cssom/#extensions-to-the-document-interface
partial interface Document {
[Constant]
readonly attribute StyleSheetList styleSheets;
attribute DOMString? selectedStyleSheetSet;
readonly attribute DOMString? lastStyleSheetSet;
--- a/dom/workers/ServiceWorkerClient.cpp
+++ b/dom/workers/ServiceWorkerClient.cpp
@@ -226,14 +226,14 @@ ServiceWorkerClient::PostMessage(JSConte
RefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
new ServiceWorkerClientPostMessageRunnable(mWindowId);
runnable->Write(aCx, aMessage, transferable, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
- aRv = NS_DispatchToMainThread(runnable);
+ aRv = workerPrivate->DispatchToMainThread(runnable.forget());
if (NS_WARN_IF(aRv.Failed())) {
return;
}
}
--- a/dom/workers/ServiceWorkerClients.cpp
+++ b/dom/workers/ServiceWorkerClients.cpp
@@ -670,17 +670,17 @@ ServiceWorkerClients::Get(const nsAStrin
PromiseWorkerProxy::Create(workerPrivate, promise);
if (!promiseProxy) {
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
return promise.forget();
}
RefPtr<GetRunnable> r =
new GetRunnable(promiseProxy, aClientId);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+ MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
return promise.forget();
}
already_AddRefed<Promise>
ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions,
ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
@@ -706,17 +706,17 @@ ServiceWorkerClients::MatchAll(const Cli
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
return promise.forget();
}
RefPtr<MatchAllRunnable> r =
new MatchAllRunnable(promiseProxy,
NS_ConvertUTF16toUTF8(scope),
aOptions.mIncludeUncontrolled);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+ MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
return promise.forget();
}
already_AddRefed<Promise>
ServiceWorkerClients::OpenWindow(const nsAString& aUrl,
ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
@@ -747,17 +747,17 @@ ServiceWorkerClients::OpenWindow(const n
return nullptr;
}
nsString scope;
mWorkerScope->GetScope(scope);
RefPtr<OpenWindowRunnable> r = new OpenWindowRunnable(promiseProxy,
aUrl, scope);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+ MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
return promise.forget();
}
already_AddRefed<Promise>
ServiceWorkerClients::Claim(ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
@@ -776,11 +776,11 @@ ServiceWorkerClients::Claim(ErrorResult&
}
nsString scope;
mWorkerScope->GetScope(scope);
RefPtr<ClaimRunnable> runnable =
new ClaimRunnable(promiseProxy, NS_ConvertUTF16toUTF8(scope));
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
+ MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(runnable.forget()));
return promise.forget();
}
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -389,17 +389,23 @@ void RespondWithCopyComplete(void* aClos
NS_ERROR_INTERCEPTION_FAILED);
} else {
event = new FinishResponse(data->mInterceptedChannel,
data->mInternalResponse,
data->mWorkerChannelInfo,
data->mScriptSpec,
data->mResponseURLSpec);
}
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(event));
+ // In theory this can happen after the worker thread is terminated.
+ WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+ if (worker) {
+ MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(event.forget()));
+ } else {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(event.forget()));
+ }
}
namespace {
void
ExtractErrorValues(JSContext* aCx, JS::Handle<JS::Value> aValue,
nsACString& aSourceSpecOut, uint32_t *aLineOut,
uint32_t *aColumnOut, nsString& aMessageOut)
@@ -719,17 +725,23 @@ RespondWithHandler::RejectedCallback(JSC
CancelRequest(NS_ERROR_INTERCEPTION_FAILED);
}
void
RespondWithHandler::CancelRequest(nsresult aStatus)
{
nsCOMPtr<nsIRunnable> runnable =
new CancelChannelRunnable(mInterceptedChannel, mRegistration, aStatus);
- NS_DispatchToMainThread(runnable);
+ // Note, this may run off the worker thread during worker termination.
+ WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+ if (worker) {
+ MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(runnable.forget()));
+ } else {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
+ }
mRequestWasHandled = true;
}
} // namespace
void
FetchEvent::RespondWith(JSContext* aCx, Promise& aArg, ErrorResult& aRv)
{
@@ -852,18 +864,18 @@ public:
// only use the extracted location if we found one
if (!spec.IsEmpty()) {
mSourceSpec = spec;
mLine = line;
mColumn = column;
}
- MOZ_ALWAYS_SUCCEEDS(
- NS_DispatchToMainThread(NewRunnableMethod(this, &WaitUntilHandler::ReportOnMainThread)));
+ MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(
+ NewRunnableMethod(this, &WaitUntilHandler::ReportOnMainThread)));
}
void
ReportOnMainThread()
{
AssertIsOnMainThread();
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -207,17 +207,17 @@ public:
private:
void
Done(bool aResult)
{
#ifdef DEBUG
mDone = true;
#endif
mCallback->SetResult(aResult);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(mCallback));
+ MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(mCallback));
}
};
} // anonymous namespace
nsresult
ServiceWorkerPrivate::CheckScriptEvaluation(LifeCycleEventCallback* aCallback)
{
@@ -490,17 +490,17 @@ public:
MOZ_ASSERT(aRegistration);
}
void
PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
{
nsCOMPtr<nsIRunnable> runnable =
new RegistrationUpdateRunnable(mRegistration, true /* time check */);
- NS_DispatchToMainThread(runnable.forget());
+ aWorkerPrivate->DispatchToMainThread(runnable.forget());
ExtendableEventWorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
}
};
/*
* Fires 'install' event on the ServiceWorkerGlobalScope. Modifies busy count
* since it fires the event. This is ok since there can't be nested
@@ -530,17 +530,17 @@ public:
MOZ_ASSERT(aWorkerPrivate);
return DispatchLifecycleEvent(aCx, aWorkerPrivate);
}
nsresult
Cancel() override
{
mCallback->SetResult(false);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(mCallback));
+ MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(mCallback));
return WorkerRunnable::Cancel();
}
private:
bool
DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
@@ -626,17 +626,17 @@ public:
mWorkerPrivate->AssertIsOnWorkerThread();
if (mDone) {
return;
}
mDone = true;
mCallback->SetResult(aResult);
- nsresult rv = NS_DispatchToMainThread(mCallback);
+ nsresult rv = mWorkerPrivate->DispatchToMainThread(mCallback);
if (NS_WARN_IF(NS_FAILED(rv))) {
NS_RUNTIMEABORT("Failed to dispatch life cycle event handler.");
}
ReleaseWorker();
}
void
@@ -757,28 +757,29 @@ public:
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
{
Report(nsIPushErrorReporter::DELIVERY_UNHANDLED_REJECTION);
}
void Report(uint16_t aReason = nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR)
{
+ WorkerPrivate* workerPrivate = mWorkerPrivate;
mWorkerPrivate->AssertIsOnWorkerThread();
mWorkerPrivate = nullptr;
if (NS_WARN_IF(aReason > nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) ||
mMessageId.IsEmpty()) {
return;
}
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod<uint16_t>(this,
&PushErrorReporter::ReportOnMainThread, aReason);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
- NS_DispatchToMainThread(runnable.forget())));
+ workerPrivate->DispatchToMainThread(runnable.forget())));
}
void ReportOnMainThread(uint16_t aReason)
{
AssertIsOnMainThread();
nsCOMPtr<nsIPushErrorReporter> reporter =
do_GetService("@mozilla.org/push/Service;1");
if (reporter) {
@@ -1415,17 +1416,17 @@ public:
MOZ_ASSERT(aWorkerPrivate);
return DispatchFetchEvent(aCx, aWorkerPrivate);
}
nsresult
Cancel() override
{
nsCOMPtr<nsIRunnable> runnable = new ResumeRequest(mInterceptedChannel);
- if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+ if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable))) {
NS_WARNING("Failed to resume channel on FetchEventRunnable::Cancel()!\n");
}
WorkerRunnable::Cancel();
return NS_OK;
}
private:
~FetchEventRunnable() {}
@@ -1529,17 +1530,17 @@ private:
}
if (!runnable) {
runnable = new CancelChannelRunnable(mInterceptedChannel,
mRegistration,
NS_ERROR_INTERCEPTION_FAILED);
}
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
+ MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
}
RefPtr<Promise> waitUntilPromise = event->GetPromise();
if (waitUntilPromise) {
KeepAliveHandler::CreateAndAttachToPromise(mKeepAliveToken,
waitUntilPromise);
}
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -1105,17 +1105,17 @@ ServiceWorkerRegistrationWorkerThread::U
RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
if (!proxy) {
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
return nullptr;
}
RefPtr<UpdateRunnable> r = new UpdateRunnable(proxy, mScope);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+ MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
return promise.forget();
}
already_AddRefed<Promise>
ServiceWorkerRegistrationWorkerThread::Unregister(ErrorResult& aRv)
{
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
@@ -1137,17 +1137,17 @@ ServiceWorkerRegistrationWorkerThread::U
RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
if (!proxy) {
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
return nullptr;
}
RefPtr<StartUnregisterRunnable> r = new StartUnregisterRunnable(proxy, mScope);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+ MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
return promise.forget();
}
class StartListeningRunnable final : public Runnable
{
RefPtr<WorkerListener> mListener;
public:
@@ -1175,17 +1175,17 @@ ServiceWorkerRegistrationWorkerThread::I
if (!HoldWorker(worker, Closing)) {
mListener = nullptr;
NS_WARNING("Could not add feature");
return;
}
RefPtr<StartListeningRunnable> r =
new StartListeningRunnable(mListener);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+ MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
}
class AsyncStopListeningRunnable final : public Runnable
{
RefPtr<WorkerListener> mListener;
public:
explicit AsyncStopListeningRunnable(WorkerListener* aListener)
: mListener(aListener)
@@ -1233,17 +1233,17 @@ ServiceWorkerRegistrationWorkerThread::R
mWorkerPrivate->AssertIsOnWorkerThread();
ReleaseWorker();
mListener->ClearRegistration();
if (aReason == RegistrationIsGoingAway) {
RefPtr<AsyncStopListeningRunnable> r =
new AsyncStopListeningRunnable(mListener);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+ MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(r.forget()));
} else if (aReason == WorkerIsGoingAway) {
RefPtr<SyncStopListeningRunnable> r =
new SyncStopListeningRunnable(mWorkerPrivate, mListener);
ErrorResult rv;
r->Dispatch(rv);
if (rv.Failed()) {
NS_ERROR("Failed to dispatch stop listening runnable!");
// And now what?
--- a/dom/workers/ServiceWorkerWindowClient.cpp
+++ b/dom/workers/ServiceWorkerWindowClient.cpp
@@ -178,17 +178,17 @@ ServiceWorkerWindowClient::Focus(ErrorRe
}
if (workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
RefPtr<PromiseWorkerProxy> promiseProxy =
PromiseWorkerProxy::Create(workerPrivate, promise);
if (promiseProxy) {
RefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
promiseProxy);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+ MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
} else {
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
}
} else {
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
}
@@ -511,15 +511,15 @@ ServiceWorkerWindowClient::Navigate(cons
return promise.forget();
}
RefPtr<PromiseWorkerProxy> promiseProxy =
PromiseWorkerProxy::Create(workerPrivate, promise);
if (promiseProxy) {
RefPtr<ClientNavigateRunnable> r =
new ClientNavigateRunnable(mWindowId, aUrl, promiseProxy);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+ MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
} else {
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
}
return promise.forget();
}
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -65,16 +65,17 @@
#include "mozilla/dom/SimpleGlobalObject.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/StructuredCloneHolder.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
#include "mozilla/dom/WorkerGlobalScopeBinding.h"
#include "mozilla/Preferences.h"
+#include "mozilla/TaskQueue.h"
#include "mozilla/TimelineConsumers.h"
#include "mozilla/WorkerTimelineMarker.h"
#include "nsAlgorithm.h"
#include "nsContentUtils.h"
#include "nsCycleCollector.h"
#include "nsError.h"
#include "nsDOMJSUtils.h"
#include "nsHostObjectProtocolHandler.h"
@@ -396,17 +397,17 @@ private:
nsCOMPtr<nsILoadGroup> loadGroupToCancel;
mFinishedWorker->ForgetOverridenLoadGroup(loadGroupToCancel);
nsTArray<nsCOMPtr<nsISupports>> doomed;
mFinishedWorker->ForgetMainThreadObjects(doomed);
RefPtr<MainThreadReleaseRunnable> runnable =
new MainThreadReleaseRunnable(doomed, loadGroupToCancel);
- if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+ if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
NS_WARNING("Failed to dispatch, going to leak!");
}
RuntimeService* runtime = RuntimeService::GetService();
NS_ASSERTION(runtime, "This should never be null!");
mFinishedWorker->DisableDebugger();
@@ -3852,17 +3853,17 @@ WorkerDebugger::Close()
void
WorkerDebugger::PostMessageToDebugger(const nsAString& aMessage)
{
mWorkerPrivate->AssertIsOnWorkerThread();
RefPtr<PostDebuggerMessageRunnable> runnable =
new PostDebuggerMessageRunnable(this, aMessage);
- if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
+ if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
NS_WARNING("Failed to post message to debugger on main thread!");
}
}
void
WorkerDebugger::PostMessageToDebuggerOnMainThread(const nsAString& aMessage)
{
AssertIsOnMainThread();
@@ -3877,17 +3878,17 @@ void
WorkerDebugger::ReportErrorToDebugger(const nsAString& aFilename,
uint32_t aLineno,
const nsAString& aMessage)
{
mWorkerPrivate->AssertIsOnWorkerThread();
RefPtr<ReportDebuggerErrorRunnable> runnable =
new ReportDebuggerErrorRunnable(this, aFilename, aLineno, aMessage);
- if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
+ if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
NS_WARNING("Failed to report error to debugger on main thread!");
}
}
void
WorkerDebugger::ReportErrorToDebuggerOnMainThread(const nsAString& aFilename,
uint32_t aLineno,
const nsAString& aMessage)
@@ -3910,16 +3911,17 @@ WorkerPrivate::WorkerPrivate(WorkerPriva
: WorkerPrivateParent<WorkerPrivate>(aParent, aScriptURL,
aIsChromeWorker, aWorkerType,
aWorkerName, aLoadInfo)
, mDebuggerRegistered(false)
, mDebugger(nullptr)
, mJSContext(nullptr)
, mPRThread(nullptr)
, mDebuggerEventLoopLevel(0)
+ , mMainThreadEventTarget(do_GetMainThread())
, mErrorHandlerRecursionCount(0)
, mNextTimeoutId(1)
, mStatus(Pending)
, mFrozen(false)
, mTimerRunning(false)
, mRunningExpiredTimeouts(false)
, mPendingEventQueueClearing(false)
, mMemoryReporterRunning(false)
@@ -3938,16 +3940,39 @@ WorkerPrivate::WorkerPrivate(WorkerPriva
aParent->GetAllPreferences(mPreferences);
mOnLine = aParent->OnLine();
}
else {
AssertIsOnMainThread();
RuntimeService::GetDefaultPreferences(mPreferences);
mOnLine = !NS_IsOffline() && !NS_IsAppOffline(aLoadInfo.mPrincipal);
}
+
+ nsCOMPtr<nsIEventTarget> target;
+
+ if (aParent) {
+ target = aParent->MainThreadEventTarget();
+ }
+
+ // TODO: If we have a window, try to use its MainThreadTaskQueue as the
+ // target for our sub-queue.
+
+ if (!target) {
+ nsCOMPtr<nsIThread> mainThread;
+ NS_GetMainThread(getter_AddRefs(mainThread));
+ MOZ_DIAGNOSTIC_ASSERT(mainThread);
+ target = mainThread;
+ }
+
+ // Throttle events to the main thread using a TaskQueue specific to this
+ // worker thread.
+ mMainThreadTaskQueue = new TaskQueue(target.forget());
+
+ // Expose our task queue as the worker's main thread nsIEventTarget.
+ mMainThreadEventTarget = mMainThreadTaskQueue->WrapAsEventTarget();
}
WorkerPrivate::~WorkerPrivate()
{
}
// static
already_AddRefed<WorkerPrivate>
@@ -4477,16 +4502,24 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
{
MutexAutoLock lock(mMutex);
mStatus = Dead;
mJSContext = nullptr;
}
+ // Shutdown the main thread TaskQueue and wait for it to drain. Make
+ // sure to clear our references first, however, so that new runnables
+ // are not dispatched into the closing TaskQueue.
+ mMainThreadEventTarget = do_GetMainThread();
+ RefPtr<TaskQueue> taskQueue = mMainThreadTaskQueue.forget();
+ taskQueue->BeginShutdown();
+ taskQueue->AwaitShutdownAndIdle();
+
// After mStatus is set to Dead there can be no more
// WorkerControlRunnables so no need to lock here.
if (!mControlQueue.IsEmpty()) {
WorkerControlRunnable* runnable;
while (mControlQueue.Pop(runnable)) {
runnable->Cancel();
runnable->Release();
}
@@ -4542,16 +4575,24 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
}
}
if (!debuggerRunnablesPending && !normalRunnablesPending) {
// Both the debugger event queue and the normal event queue has been
// exhausted, cancel the periodic GC timer and schedule the idle GC timer.
SetGCTimerMode(IdleTimer);
}
+
+ // If the worker thread is spamming the main thread faster than it can
+ // process the work, then pause the worker thread until the MT catches
+ // up.
+ if (mMainThreadTaskQueue &&
+ mMainThreadTaskQueue->ImpreciseLengthForHeuristics() > 5000) {
+ mMainThreadTaskQueue->AwaitIdle();
+ }
}
MOZ_CRASH("Shouldn't get here!");
}
void
WorkerPrivate::OnProcessNextEvent()
{
@@ -4584,17 +4625,37 @@ WorkerPrivate::MaybeDispatchLoadFailedRu
{
AssertIsOnWorkerThread();
nsCOMPtr<nsIRunnable> runnable = StealLoadFailedAsyncRunnable();
if (!runnable) {
return;
}
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
+ MOZ_ALWAYS_SUCCEEDS(DispatchToMainThread(runnable.forget()));
+}
+
+nsIEventTarget*
+WorkerPrivate::MainThreadEventTarget()
+{
+ return mMainThreadEventTarget;
+}
+
+nsresult
+WorkerPrivate::DispatchToMainThread(nsIRunnable* aRunnable, uint32_t aFlags)
+{
+ nsCOMPtr<nsIRunnable> r = aRunnable;
+ return DispatchToMainThread(r.forget(), aFlags);
+}
+
+nsresult
+WorkerPrivate::DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable,
+ uint32_t aFlags)
+{
+ return mMainThreadEventTarget->Dispatch(Move(aRunnable), aFlags);
}
void
WorkerPrivate::InitializeGCTimers()
{
AssertIsOnWorkerThread();
// We need a timer for GC. The basic plan is to run a non-shrinking GC
@@ -4785,17 +4846,17 @@ WorkerPrivate::ScheduleDeletion(WorkerRa
new WorkerFinishedRunnable(parent, this);
if (!runnable->Dispatch()) {
NS_WARNING("Failed to dispatch runnable!");
}
}
else {
RefPtr<TopLevelWorkerFinishedRunnable> runnable =
new TopLevelWorkerFinishedRunnable(this);
- if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+ if (NS_FAILED(DispatchToMainThread(runnable.forget()))) {
NS_WARNING("Failed to dispatch runnable!");
}
}
}
bool
WorkerPrivate::BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats,
bool aAnonymize)
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -54,16 +54,17 @@ class nsITimer;
class nsIURI;
template<class T> class nsMainThreadPtrHandle;
namespace JS {
struct RuntimeStats;
} // namespace JS
namespace mozilla {
+class TaskQueue;
namespace dom {
class Function;
class MessagePort;
class MessagePortIdentifier;
class PromiseNativeHandler;
class StructuredCloneHolder;
class WorkerDebuggerGlobalScope;
class WorkerGlobalScope;
@@ -912,16 +913,18 @@ class WorkerPrivate : public WorkerPriva
// Things touched on worker thread only.
RefPtr<WorkerGlobalScope> mScope;
RefPtr<WorkerDebuggerGlobalScope> mDebuggerScope;
nsTArray<ParentType*> mChildWorkers;
nsTObserverArray<WorkerHolder*> mHolders;
nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
uint32_t mDebuggerEventLoopLevel;
+ RefPtr<TaskQueue> mMainThreadTaskQueue;
+ nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
struct SyncLoopInfo
{
explicit SyncLoopInfo(EventTarget* aEventTarget);
RefPtr<EventTarget> mEventTarget;
bool mCompleted;
bool mResult;
@@ -1336,16 +1339,30 @@ public:
{
AssertIsOnWorkerThread();
return mWorkerScriptExecutedSuccessfully;
}
void
MaybeDispatchLoadFailedRunnable();
+ // Get the event target to use when dispatching to the main thread
+ // from this Worker thread. This may be the main thread itself or
+ // a TaskQueue throttling runnables to the main thread.
+ nsIEventTarget*
+ MainThreadEventTarget();
+
+ nsresult
+ DispatchToMainThread(nsIRunnable* aRunnable,
+ uint32_t aFlags = NS_DISPATCH_NORMAL);
+
+ nsresult
+ DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable,
+ uint32_t aFlags = NS_DISPATCH_NORMAL);
+
private:
WorkerPrivate(WorkerPrivate* aParent,
const nsAString& aScriptURL, bool aIsChromeWorker,
WorkerType aWorkerType, const nsACString& aSharedWorkerName,
WorkerLoadInfo& aLoadInfo);
bool
MayContinueRunning()
--- a/dom/workers/WorkerRunnable.cpp
+++ b/dom/workers/WorkerRunnable.cpp
@@ -10,16 +10,17 @@
#include "nsIEventTarget.h"
#include "nsIGlobalObject.h"
#include "nsIRunnable.h"
#include "nsThreadUtils.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/TaskQueue.h"
#include "mozilla/Telemetry.h"
#include "js/RootingAPI.h"
#include "js/Value.h"
#include "WorkerPrivate.h"
#include "WorkerScope.h"
@@ -113,20 +114,17 @@ WorkerRunnable::DispatchInternal()
}
MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
return NS_SUCCEEDED(parent->Dispatch(runnable.forget()));
}
- nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
- MOZ_ASSERT(mainThread);
-
- return NS_SUCCEEDED(mainThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
+ return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
}
void
WorkerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
bool aDispatchResult)
{
MOZ_ASSERT(aWorkerPrivate);
@@ -552,20 +550,17 @@ WorkerControlRunnable::DispatchInternal(
if (mBehavior == WorkerThreadUnchangedBusyCount) {
return NS_SUCCEEDED(mWorkerPrivate->DispatchControlRunnable(runnable.forget()));
}
if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
return NS_SUCCEEDED(parent->DispatchControlRunnable(runnable.forget()));
}
- nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
- MOZ_ASSERT(mainThread);
-
- return NS_SUCCEEDED(mainThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
+ return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
}
NS_IMPL_ISUPPORTS_INHERITED0(WorkerControlRunnable, WorkerRunnable)
WorkerMainThreadRunnable::WorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
const nsACString& aTelemetryKey)
: mWorkerPrivate(aWorkerPrivate)
, mTelemetryKey(aTelemetryKey)
@@ -578,20 +573,18 @@ WorkerMainThreadRunnable::Dispatch(Error
{
mWorkerPrivate->AssertIsOnWorkerThread();
TimeStamp startTime = TimeStamp::NowLoRes();
AutoSyncLoopHolder syncLoop(mWorkerPrivate);
mSyncLoopTarget = syncLoop.EventTarget();
- RefPtr<WorkerMainThreadRunnable> runnable(this);
- DebugOnly<nsresult> rv =
- NS_DispatchToMainThread(runnable.forget(), NS_DISPATCH_NORMAL);
+ DebugOnly<nsresult> rv = mWorkerPrivate->DispatchToMainThread(this);
MOZ_ASSERT(NS_SUCCEEDED(rv),
"Should only fail after xpcom-shutdown-threads and we're gone by then");
if (!syncLoop.Run()) {
aRv.ThrowUncatchableException();
}
// Telemetry is apparently not threadsafe
@@ -677,17 +670,17 @@ WorkerProxyToMainThreadRunnable::Dispatc
{
mWorkerPrivate->AssertIsOnWorkerThread();
if (NS_WARN_IF(!HoldWorker())) {
RunBackOnWorkerThread();
return false;
}
- if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) {
+ if (NS_WARN_IF(NS_FAILED(mWorkerPrivate->DispatchToMainThread(this)))) {
ReleaseWorker();
RunBackOnWorkerThread();
return false;
}
return true;
}
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -692,17 +692,17 @@ ServiceWorkerGlobalScope::SkipWaiting(Er
promise->MaybeResolveWithUndefined();
return promise.forget();
}
RefPtr<WorkerScopeSkipWaitingRunnable> runnable =
new WorkerScopeSkipWaitingRunnable(promiseProxy,
NS_ConvertUTF16toUTF8(mScope));
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
+ MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
return promise.forget();
}
bool
ServiceWorkerGlobalScope::OpenWindowEnabled(JSContext* aCx, JSObject* aObj)
{
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
--- a/dom/workers/test/test_consoleAndBlobs.html
+++ b/dom/workers/test/test_consoleAndBlobs.html
@@ -17,22 +17,24 @@
SpecialPowers.addObserver(this, "console-api-log-event", false);
}
var order = 0;
consoleListener.prototype = {
observe: function(aSubject, aTopic, aData) {
ok(true, "Something has been received");
is(aTopic, "console-api-log-event");
- SpecialPowers.removeObserver(this, "console-api-log-event");
var obj = aSubject.wrappedJSObject;
- is(obj.arguments[0].size, 3, "The size is correct");
- is(obj.arguments[0].type, 'foo/bar', "The type is correct");
- SimpleTest.finish();
+ if (obj.arguments[0] && obj.arguments[0].msg === 'consoleAndBlobs') {
+ SpecialPowers.removeObserver(this, "console-api-log-event");
+ is(obj.arguments[0].blob.size, 3, "The size is correct");
+ is(obj.arguments[0].blob.type, 'foo/bar', "The type is correct");
+ SimpleTest.finish();
+ }
}
}
var cl = new consoleListener();
new Worker('worker_consoleAndBlobs.js');
SimpleTest.waitForExplicitFinish();
--- a/dom/workers/test/worker_consoleAndBlobs.js
+++ b/dom/workers/test/worker_consoleAndBlobs.js
@@ -1,8 +1,8 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";
var b = new Blob(['123'], { type: 'foo/bar'});
-console.log(b);
+console.log({ msg: 'consoleAndBlobs', blob: b });
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -164,16 +164,17 @@ XMLHttpRequestMainThread::XMLHttpRequest
: mResponseBodyDecodedPos(0),
mResponseType(XMLHttpRequestResponseType::_empty),
mRequestObserver(nullptr),
mState(State::unsent),
mFlagSynchronous(false), mFlagAborted(false), mFlagParseBody(false),
mFlagSyncLooping(false), mFlagBackgroundRequest(false),
mFlagHadUploadListenersOnSend(false), mFlagACwithCredentials(false),
mFlagTimedOut(false), mFlagDeleted(false), mFlagSend(false),
+ mSendExtraLoadingEvents(Preferences::GetBool("dom.fire_extra_xhr_loading_readystatechanges", true)),
mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
mProgressSinceLastProgressEvent(false),
mRequestSentTime(0), mTimeoutMilliseconds(0),
mErrorLoad(false), mErrorParsingXML(false),
mWaitingForOnStopRequest(false),
mProgressTimerIsActive(false),
mIsHtml(false),
mWarnAboutSyncHtml(false),
@@ -1726,17 +1727,19 @@ XMLHttpRequestMainThread::OnDataAvailabl
}
ChangeState(State::loading);
return request->Cancel(NS_OK);
}
mDataAvailable += totalRead;
- ChangeState(State::loading);
+ if (mState == State::headers_received || mSendExtraLoadingEvents) {
+ ChangeState(State::loading);
+ }
if (!mFlagSynchronous && !mProgressTimerIsActive) {
StartProgressEventTimer();
}
return NS_OK;
}
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -698,16 +698,24 @@ protected:
bool mFlagDeleted;
// The XHR2 spec's send() flag. Set when the XHR begins uploading, until it
// finishes downloading (or an error/abort has occurred during either phase).
// Used to guard against the user trying to alter headers/etc when it's too
// late, and ensure the XHR only handles one in-flight request at once.
bool mFlagSend;
+ // Before ProgressEvents were a thing, multiple readystatechange events were
+ // fired during the loading state to give sites a way to monitor XHR progress.
+ // The XHR spec now has proper progress events and dictates that only one
+ // "loading" readystatechange should be fired per send. However, it's possible
+ // that some content still relies on this old behavior, so we're keeping it
+ // (behind a preference) for now. See bug 918719.
+ bool mSendExtraLoadingEvents;
+
RefPtr<XMLHttpRequestUpload> mUpload;
int64_t mUploadTransferred;
int64_t mUploadTotal;
bool mUploadComplete;
bool mProgressSinceLastProgressEvent;
// Timeout support
PRTime mRequestSentTime;
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -1607,17 +1607,17 @@ XMLHttpRequestWorker::ReleaseProxy(Relea
if (mProxy) {
if (aType == XHRIsGoingAway) {
// We're in a GC finalizer, so we can't do a sync call here (and we don't
// need to).
RefPtr<AsyncTeardownRunnable> runnable =
new AsyncTeardownRunnable(mProxy);
mProxy = nullptr;
- if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+ if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
NS_ERROR("Failed to dispatch teardown runnable!");
}
} else {
// This isn't necessary if the worker is going away or the XHR is going
// away.
if (aType == Default) {
// Don't let any more events run.
mProxy->mOuterEventStreamId++;
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -341,18 +341,20 @@ GLLibraryEGL::EnsureInitialized(bool for
MOZ_ASSERT(false, "d3dcompiler DLL loading failed.");
} while (false);
LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libGLESv2.dll"));
mEGLLibrary = LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libEGL.dll"));
- if (!mEGLLibrary)
+ if (!mEGLLibrary) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_LOAD");
return false;
+ }
}
#else // !Windows
// On non-Windows (Android) we use system copies of libEGL. We look for
// the APITrace lib, libEGL.so, and libEGL.so.1 in that order.
#if defined(ANDROID)
@@ -367,16 +369,17 @@ GLLibraryEGL::EnsureInitialized(bool for
#if defined(XP_UNIX)
if (!mEGLLibrary) {
mEGLLibrary = PR_LoadLibrary("libEGL.so.1");
}
#endif
if (!mEGLLibrary) {
NS_WARNING("Couldn't load EGL LIB.");
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_LOAD_2");
return false;
}
#endif // !Windows
#define SYMBOL(name) \
{ (PRFuncPtr*) &mSymbols.f##name, { "egl" #name, nullptr } }
@@ -407,16 +410,17 @@ GLLibraryEGL::EnsureInitialized(bool for
SYMBOL(BindTexImage),
SYMBOL(ReleaseTexImage),
SYMBOL(QuerySurface),
{ nullptr, { nullptr } }
};
if (!GLLibraryLoader::LoadSymbols(mEGLLibrary, &earlySymbols[0])) {
NS_WARNING("Couldn't find required entry points in EGL library (early init)");
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_SYM");
return false;
}
GLLibraryLoader::SymLoadStruct optionalSymbols[] = {
// On Android 4.3 and up, certain features like ANDROID_native_fence_sync
// can only be queried by using a special eglQueryString.
{ (PRFuncPtr*) &mSymbols.fQueryStringImplementationANDROID,
{ "_Z35eglQueryStringImplementationANDROIDPvi", nullptr } },
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -146,17 +146,17 @@ GPUProcessManager::EnsureGPUReady()
if (mGPUChild) {
mGPUChild->EnsureGPUReady();
}
}
void
GPUProcessManager::EnsureImageBridgeChild()
{
- if (ImageBridgeChild::IsCreated()) {
+ if (ImageBridgeChild::GetSingleton()) {
return;
}
EnsureGPUReady();
if (!mGPUChild) {
ImageBridgeChild::InitSameProcess();
return;
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -145,17 +145,19 @@ ImageContainer::ImageContainer(uint64_t
{
MOZ_ASSERT(mAsyncContainerID != sInvalidAsyncContainerId);
}
ImageContainer::~ImageContainer()
{
if (mIPDLChild) {
mIPDLChild->ForgetImageContainer();
- ImageBridgeChild::DispatchReleaseImageContainer(mIPDLChild);
+ if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
+ imageBridge->ReleaseImageContainer(mIPDLChild);
+ }
}
}
RefPtr<PlanarYCbCrImage>
ImageContainer::CreatePlanarYCbCrImage()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (mImageClient && mImageClient->AsImageClientSingle()) {
@@ -264,28 +266,32 @@ ImageContainer::ClearImagesFromImageBrid
}
void
ImageContainer::SetCurrentImages(const nsTArray<NonOwningImage>& aImages)
{
MOZ_ASSERT(!aImages.IsEmpty());
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (mImageClient) {
- ImageBridgeChild::DispatchImageClientUpdate(mImageClient, this);
+ if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
+ imageBridge->UpdateImageClient(mImageClient, this);
+ }
}
SetCurrentImageInternal(aImages);
}
void
ImageContainer::ClearAllImages()
{
if (mImageClient) {
// Let ImageClient release all TextureClients. This doesn't return
// until ImageBridge has called ClearCurrentImageFromImageBridge.
- ImageBridgeChild::FlushAllImages(mImageClient, this);
+ if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
+ imageBridge->FlushAllImages(mImageClient, this);
+ }
return;
}
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
SetCurrentImageInternal(nsTArray<NonOwningImage>());
}
void
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -5,16 +5,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_LAYERS_COMPOSITABLEFORWARDER
#define MOZILLA_LAYERS_COMPOSITABLEFORWARDER
#include <stdint.h> // for int32_t, uint64_t
#include "gfxTypes.h"
#include "mozilla/Attributes.h" // for override
+#include "mozilla/UniquePtr.h"
#include "mozilla/layers/CompositableClient.h" // for CompositableClient
#include "mozilla/layers/CompositorTypes.h"
#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
#include "mozilla/layers/LayersTypes.h" // for LayersBackend
#include "mozilla/layers/TextureClient.h" // for TextureClient
#include "mozilla/layers/TextureForwarder.h" // for TextureForwarder
#include "nsRegion.h" // for nsIntRegion
#include "mozilla/gfx/Rect.h"
@@ -74,19 +75,20 @@ public:
* additionally forward modifications of the Layer tree).
* ImageBridgeChild is another CompositableForwarder.
*/
class CompositableForwarder : public TextureForwarder
{
public:
CompositableForwarder()
- : mActiveResourceTracker(1000, "CompositableForwarder")
- , mSerial(++sSerialCounter)
- {}
+ : mSerial(++sSerialCounter)
+ {
+ mActiveResourceTracker = MakeUnique<ActiveResourceTracker>(1000, "CompositableForwarder");
+ }
/**
* Setup the IPDL actor for aCompositable to be part of layers
* transactions.
*/
virtual void Connect(CompositableClient* aCompositable,
ImageContainer* aImageContainer = nullptr) = 0;
@@ -202,26 +204,26 @@ public:
return mTextureFactoryIdentifier.mSupportsPartialUploads;
}
const TextureFactoryIdentifier& GetTextureFactoryIdentifier() const
{
return mTextureFactoryIdentifier;
}
- ActiveResourceTracker& GetActiveResourceTracker() { return mActiveResourceTracker; }
+ ActiveResourceTracker& GetActiveResourceTracker() { return *mActiveResourceTracker.get(); }
protected:
TextureFactoryIdentifier mTextureFactoryIdentifier;
nsTArray<RefPtr<TextureClient> > mTexturesToRemove;
nsTArray<RefPtr<CompositableClient>> mCompositableClientsToRemove;
RefPtr<SyncObject> mSyncObject;
- ActiveResourceTracker mActiveResourceTracker;
+ UniquePtr<ActiveResourceTracker> mActiveResourceTracker;
const int32_t mSerial;
static mozilla::Atomic<int32_t> sSerialCounter;
};
} // namespace layers
} // namespace mozilla
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -249,17 +249,16 @@ CompositorBridgeChild::ChildProcessHasCo
}
PLayerTransactionChild*
CompositorBridgeChild::AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints,
const uint64_t& aId,
TextureFactoryIdentifier*,
bool*)
{
- MOZ_ASSERT(mCanSend);
LayerTransactionChild* c = new LayerTransactionChild(aId);
c->AddIPDLReference();
return c;
}
bool
CompositorBridgeChild::DeallocPLayerTransactionChild(PLayerTransactionChild* actor)
{
@@ -689,142 +688,127 @@ CompositorBridgeChild::CancelNotifyAfter
if (tabChild == aTabChild) {
mWeakTabChild = nullptr;
}
}
bool
CompositorBridgeChild::SendWillClose()
{
- MOZ_ASSERT(mCanSend);
- if (!mCanSend) {
- return true;
- }
+ MOZ_RELEASE_ASSERT(mCanSend);
return PCompositorBridgeChild::SendWillClose();
}
bool
CompositorBridgeChild::SendPause()
{
- MOZ_ASSERT(mCanSend);
if (!mCanSend) {
- return true;
+ return false;
}
return PCompositorBridgeChild::SendPause();
}
bool
CompositorBridgeChild::SendResume()
{
- MOZ_ASSERT(mCanSend);
if (!mCanSend) {
- return true;
+ return false;
}
return PCompositorBridgeChild::SendResume();
}
bool
CompositorBridgeChild::SendNotifyChildCreated(const uint64_t& id)
{
- MOZ_ASSERT(mCanSend);
if (!mCanSend) {
- return true;
+ return false;
}
return PCompositorBridgeChild::SendNotifyChildCreated(id);
}
bool
CompositorBridgeChild::SendAdoptChild(const uint64_t& id)
{
- MOZ_ASSERT(mCanSend);
if (!mCanSend) {
- return true;
+ return false;
}
return PCompositorBridgeChild::SendAdoptChild(id);
}
bool
CompositorBridgeChild::SendMakeSnapshot(const SurfaceDescriptor& inSnapshot, const gfx::IntRect& dirtyRect)
{
- MOZ_ASSERT(mCanSend);
if (!mCanSend) {
- return true;
+ return false;
}
return PCompositorBridgeChild::SendMakeSnapshot(inSnapshot, dirtyRect);
}
bool
CompositorBridgeChild::SendFlushRendering()
{
- MOZ_ASSERT(mCanSend);
if (!mCanSend) {
- return true;
+ return false;
}
return PCompositorBridgeChild::SendFlushRendering();
}
bool
CompositorBridgeChild::SendStartFrameTimeRecording(const int32_t& bufferSize, uint32_t* startIndex)
{
- MOZ_ASSERT(mCanSend);
if (!mCanSend) {
- return true;
+ return false;
}
return PCompositorBridgeChild::SendStartFrameTimeRecording(bufferSize, startIndex);
}
bool
CompositorBridgeChild::SendStopFrameTimeRecording(const uint32_t& startIndex, nsTArray<float>* intervals)
{
- MOZ_ASSERT(mCanSend);
if (!mCanSend) {
- return true;
+ return false;
}
return PCompositorBridgeChild::SendStopFrameTimeRecording(startIndex, intervals);
}
bool
CompositorBridgeChild::SendNotifyRegionInvalidated(const nsIntRegion& region)
{
- MOZ_ASSERT(mCanSend);
if (!mCanSend) {
- return true;
+ return false;
}
return PCompositorBridgeChild::SendNotifyRegionInvalidated(region);
}
bool
CompositorBridgeChild::SendRequestNotifyAfterRemotePaint()
{
- MOZ_ASSERT(mCanSend);
if (!mCanSend) {
- return true;
+ return false;
}
return PCompositorBridgeChild::SendRequestNotifyAfterRemotePaint();
}
bool
CompositorBridgeChild::SendClearApproximatelyVisibleRegions(uint64_t aLayersId,
uint32_t aPresShellId)
{
- MOZ_ASSERT(mCanSend);
if (!mCanSend) {
- return true;
+ return false;
}
return PCompositorBridgeChild::SendClearApproximatelyVisibleRegions(aLayersId,
aPresShellId);
}
bool
CompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
const CSSIntRegion& aRegion)
{
- MOZ_ASSERT(mCanSend);
if (!mCanSend) {
- return true;
+ return false;
}
return PCompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(aGuid, aRegion);
}
PTextureChild*
CompositorBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
const LayersBackend&,
const TextureFlags&,
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -28,21 +28,24 @@
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
#include "mozilla/layers/ImageClient.h" // for ImageClient
#include "mozilla/layers/ImageContainerChild.h"
#include "mozilla/layers/LayersMessages.h" // for CompositableOperation
#include "mozilla/layers/PCompositableChild.h" // for PCompositableChild
#include "mozilla/layers/TextureClient.h" // for TextureClient
#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mtransport/runnable_utils.h"
+#include "nsContentUtils.h"
#include "nsISupportsImpl.h" // for ImageContainer::AddRef, etc
#include "nsTArray.h" // for AutoTArray, nsTArray, etc
#include "nsTArrayForwardDeclare.h" // for AutoTArray
#include "nsThreadUtils.h" // for NS_IsMainThread
#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
+#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h" // for StaticRefPtr
#include "mozilla/layers/TextureClient.h"
namespace mozilla {
namespace ipc {
class Shmem;
} // namespace ipc
@@ -161,19 +164,16 @@ struct CompositableTransaction
};
struct AutoEndTransaction {
explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
~AutoEndTransaction() { mTxn->End(); }
CompositableTransaction* mTxn;
};
-/* static */
-Atomic<bool> ImageBridgeChild::sIsShutDown(false);
-
void
ImageBridgeChild::UseTextures(CompositableClient* aCompositable,
const nsTArray<TimedTextureClient>& aTextures)
{
MOZ_ASSERT(aCompositable);
MOZ_ASSERT(aCompositable->GetIPDLActor());
MOZ_ASSERT(aCompositable->IsConnected());
@@ -386,16 +386,17 @@ ImageBridgeChild::CancelWaitForRecycle(u
RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId);
if (!client) {
return;
}
mTexturesWaitingRecycled.Remove(aTextureId);
}
// Singleton
+static StaticMutex sImageBridgeSingletonLock;
static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton;
static Thread *sImageBridgeChildThread = nullptr;
void
ImageBridgeChild::FallbackDestroyActors() {
if (mTxn && !mTxn->mDestroyedActors.IsEmpty()) {
mTxn->FallbackDestroyActors();
}
@@ -445,127 +446,141 @@ public:
}
private:
SynchronousTask* mTask;
ReentrantMonitorAutoEnter mAutoEnter;
};
// dispatched function
-static void ImageBridgeShutdownStep1(SynchronousTask* aTask)
+void
+ImageBridgeChild::ShutdownStep1(SynchronousTask* aTask)
{
AutoCompleteTask complete(aTask);
MOZ_ASSERT(InImageBridgeChildThread(),
"Should be in ImageBridgeChild thread.");
MediaSystemResourceManager::Shutdown();
- if (sImageBridgeChildSingleton) {
- // Force all managed protocols to shut themselves down cleanly
- InfallibleTArray<PCompositableChild*> compositables;
- sImageBridgeChildSingleton->ManagedPCompositableChild(compositables);
- for (int i = compositables.Length() - 1; i >= 0; --i) {
- auto compositable = CompositableClient::FromIPDLActor(compositables[i]);
- if (compositable) {
- compositable->Destroy();
- }
+ // Force all managed protocols to shut themselves down cleanly
+ InfallibleTArray<PCompositableChild*> compositables;
+ ManagedPCompositableChild(compositables);
+ for (int i = compositables.Length() - 1; i >= 0; --i) {
+ auto compositable = CompositableClient::FromIPDLActor(compositables[i]);
+ if (compositable) {
+ compositable->Destroy();
}
- InfallibleTArray<PTextureChild*> textures;
- sImageBridgeChildSingleton->ManagedPTextureChild(textures);
- for (int i = textures.Length() - 1; i >= 0; --i) {
- RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
- if (client) {
- client->Destroy();
- }
+ }
+ InfallibleTArray<PTextureChild*> textures;
+ ManagedPTextureChild(textures);
+ for (int i = textures.Length() - 1; i >= 0; --i) {
+ RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
+ if (client) {
+ client->Destroy();
}
- sImageBridgeChildSingleton->FallbackDestroyActors();
+ }
+ FallbackDestroyActors();
- sImageBridgeChildSingleton->SendWillClose();
- sImageBridgeChildSingleton->MarkShutDown();
- // From now on, no message can be sent through the image bridge from the
- // client side except the final Stop message.
- }
+ SendWillClose();
+ MarkShutDown();
+
+ // From now on, no message can be sent through the image bridge from the
+ // client side except the final Stop message.
}
// dispatched function
-static void
-ImageBridgeShutdownStep2(SynchronousTask* aTask)
+void
+ImageBridgeChild::ShutdownStep2(SynchronousTask* aTask)
{
AutoCompleteTask complete(aTask);
MOZ_ASSERT(InImageBridgeChildThread(),
"Should be in ImageBridgeChild thread.");
- sImageBridgeChildSingleton->Close();
+ if (!mCalledClose) {
+ Close();
+ mCalledClose = true;
+ }
+}
+
+void
+ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ mCanSend = false;
+ mCalledClose = true;
}
-/* static */ void
-CreateImageClientSync(SynchronousTask* aTask,
- RefPtr<ImageBridgeChild> aChild,
- RefPtr<ImageClient>* result,
- CompositableType aType,
- ImageContainer* aImageContainer,
- ImageContainerChild* aContainerChild)
+void
+ImageBridgeChild::DeallocPImageBridgeChild()
+{
+ this->Release();
+}
+
+void
+ImageBridgeChild::CreateImageClientSync(SynchronousTask* aTask,
+ RefPtr<ImageClient>* result,
+ CompositableType aType,
+ ImageContainer* aImageContainer,
+ ImageContainerChild* aContainerChild)
{
AutoCompleteTask complete(aTask);
- *result = aChild->CreateImageClientNow(aType, aImageContainer, aContainerChild);
+ *result = CreateImageClientNow(aType, aImageContainer, aContainerChild);
}
// dispatched function
-static void CreateCanvasClientSync(SynchronousTask* aTask,
- CanvasClient::CanvasClientType aType,
- TextureFlags aFlags,
- RefPtr<CanvasClient>* const outResult)
+void
+ImageBridgeChild::CreateCanvasClientSync(SynchronousTask* aTask,
+ CanvasClient::CanvasClientType aType,
+ TextureFlags aFlags,
+ RefPtr<CanvasClient>* const outResult)
{
AutoCompleteTask complete(aTask);
- *outResult = sImageBridgeChildSingleton->CreateCanvasClientNow(aType, aFlags);
-}
-
-static void ConnectImageBridge(ImageBridgeChild * child, ImageBridgeParent * parent)
-{
- MessageLoop *parentMsgLoop = parent->GetMessageLoop();
- ipc::MessageChannel *parentChannel = parent->GetIPCChannel();
- child->Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide);
+ *outResult = CreateCanvasClientNow(aType, aFlags);
}
ImageBridgeChild::ImageBridgeChild()
- : mShuttingDown(false)
+ : mCanSend(false)
+ , mCalledClose(false)
, mFwdTransactionId(0)
#ifdef MOZ_WIDGET_GONK
, mWaitingFenceHandleMutex("ImageBridgeChild::mWaitingFenceHandleMutex")
#endif
{
MOZ_ASSERT(NS_IsMainThread());
mTxn = new CompositableTransaction();
+ mShutdownObserver = new ShutdownObserver(this);
}
ImageBridgeChild::~ImageBridgeChild()
{
+ // We should have already removed this in WillShutdown, since it must be
+ // removed on the main thread.
+ MOZ_ASSERT(!mShutdownObserver);
+
delete mTxn;
}
void
ImageBridgeChild::MarkShutDown()
{
- MOZ_ASSERT(!mShuttingDown);
mTexturesWaitingRecycled.Clear();
mTrackersHolder.DestroyAsyncTransactionTrackersHolder();
- mShuttingDown = true;
+ mCanSend = false;
}
void
ImageBridgeChild::Connect(CompositableClient* aCompositable,
ImageContainer* aImageContainer)
{
MOZ_ASSERT(aCompositable);
- MOZ_ASSERT(!mShuttingDown);
MOZ_ASSERT(InImageBridgeChildThread());
+ MOZ_ASSERT(CanSend());
uint64_t id = 0;
PImageContainerChild* imageContainerChild = nullptr;
if (aImageContainer)
imageContainerChild = aImageContainer->GetPImageContainerChild();
PCompositableChild* child =
@@ -574,265 +589,255 @@ ImageBridgeChild::Connect(CompositableCl
MOZ_ASSERT(child);
aCompositable->InitIPDLActor(child, id);
}
PCompositableChild*
ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo,
PImageContainerChild* aChild, uint64_t* aID)
{
- MOZ_ASSERT(!mShuttingDown);
+ MOZ_ASSERT(CanSend());
return AsyncCompositableChild::CreateActor();
}
bool
ImageBridgeChild::DeallocPCompositableChild(PCompositableChild* aActor)
{
AsyncCompositableChild::DestroyActor(aActor);
return true;
}
Thread* ImageBridgeChild::GetThread() const
{
return sImageBridgeChildThread;
}
-ImageBridgeChild* ImageBridgeChild::GetSingleton()
+/* static */ RefPtr<ImageBridgeChild>
+ImageBridgeChild::GetSingleton()
{
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
return sImageBridgeChildSingleton;
}
-bool ImageBridgeChild::IsCreated()
-{
- return GetSingleton() != nullptr;
-}
-
-static void
-ReleaseImageContainerNow(RefPtr<ImageBridgeChild> aBridge, RefPtr<ImageContainerChild> aChild)
-{
- MOZ_ASSERT(InImageBridgeChildThread());
-
- aChild->SendAsyncDelete();
-}
-
-// static
-void ImageBridgeChild::DispatchReleaseImageContainer(ImageContainerChild* aChild)
+void
+ImageBridgeChild::ReleaseImageContainer(RefPtr<ImageContainerChild> aChild)
{
if (!aChild) {
return;
}
- RefPtr<ImageBridgeChild> bridge = GetSingleton();
- if (!bridge) {
+ if (!InImageBridgeChildThread()) {
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ReleaseImageContainer,
+ aChild);
+ GetMessageLoop()->PostTask(runnable.forget());
return;
}
- RefPtr<ImageContainerChild> child(aChild);
- bridge->GetMessageLoop()->PostTask(
- NewRunnableFunction(&ReleaseImageContainerNow, bridge, child));
+ aChild->SendAsyncDelete();
}
-static void ReleaseTextureClientNow(TextureClient* aClient)
+void
+ImageBridgeChild::ReleaseTextureClientNow(TextureClient* aClient)
{
MOZ_ASSERT(InImageBridgeChildThread());
RELEASE_MANUALLY(aClient);
}
-// static
-void ImageBridgeChild::DispatchReleaseTextureClient(TextureClient* aClient)
+/* static */ void
+ImageBridgeChild::DispatchReleaseTextureClient(TextureClient* aClient)
{
if (!aClient) {
return;
}
- if (!IsCreated()) {
+ RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
+ if (!imageBridge) {
// TextureClient::Release should normally happen in the ImageBridgeChild
// thread because it usually generate some IPDL messages.
// However, if we take this branch it means that the ImageBridgeChild
// has already shut down, along with the TextureChild, which means no
// message will be sent and it is safe to run this code from any thread.
MOZ_ASSERT(aClient->GetIPDLActor() == nullptr);
RELEASE_MANUALLY(aClient);
return;
}
- sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
- NewRunnableFunction(&ReleaseTextureClientNow, aClient));
+ RefPtr<Runnable> runnable = WrapRunnable(
+ imageBridge,
+ &ImageBridgeChild::ReleaseTextureClientNow,
+ aClient);
+ imageBridge->GetMessageLoop()->PostTask(runnable.forget());
}
-static void
-UpdateImageClientNow(RefPtr<ImageClient> aClient, RefPtr<ImageContainer>&& aContainer)
+void
+ImageBridgeChild::UpdateImageClient(RefPtr<ImageClient> aClient, RefPtr<ImageContainer> aContainer)
{
- if (!ImageBridgeChild::IsCreated() || ImageBridgeChild::IsShutDown()) {
- NS_WARNING("Something is holding on to graphics resources after the shutdown"
- "of the graphics subsystem!");
+ if (!aClient || !aContainer) {
return;
}
- MOZ_ASSERT(aClient);
- MOZ_ASSERT(aContainer);
+
+ if (!InImageBridgeChildThread()) {
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::UpdateImageClient,
+ aClient,
+ aContainer);
+ GetMessageLoop()->PostTask(runnable.forget());
+ return;
+ }
+
+ if (!CanSend()) {
+ return;
+ }
// If the client has become disconnected before this event was dispatched,
// early return now.
if (!aClient->IsConnected()) {
return;
}
- sImageBridgeChildSingleton->BeginTransaction();
+ BeginTransaction();
aClient->UpdateImage(aContainer, Layer::CONTENT_OPAQUE);
- sImageBridgeChildSingleton->EndTransaction();
+ EndTransaction();
}
-// static
-void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient,
- ImageContainer* aContainer)
-{
- if (!ImageBridgeChild::IsCreated() || ImageBridgeChild::IsShutDown()) {
- NS_WARNING("Something is holding on to graphics resources after the shutdown"
- "of the graphics subsystem!");
- return;
- }
- if (!aClient || !aContainer || !IsCreated()) {
- return;
- }
-
- if (InImageBridgeChildThread()) {
- UpdateImageClientNow(aClient, aContainer);
- return;
- }
-
- sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
- NewRunnableFunction(&UpdateImageClientNow,
- RefPtr<ImageClient>(aClient), RefPtr<ImageContainer>(aContainer)));
-}
-
-static void
-UpdateAsyncCanvasRendererSync(SynchronousTask* aTask, AsyncCanvasRenderer* aWrapper)
+void
+ImageBridgeChild::UpdateAsyncCanvasRendererSync(SynchronousTask* aTask, AsyncCanvasRenderer* aWrapper)
{
AutoCompleteTask complete(aTask);
- ImageBridgeChild::UpdateAsyncCanvasRendererNow(aWrapper);
+ UpdateAsyncCanvasRendererNow(aWrapper);
}
-// static
-void ImageBridgeChild::UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aWrapper)
+void
+ImageBridgeChild::UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aWrapper)
{
aWrapper->GetCanvasClient()->UpdateAsync(aWrapper);
if (InImageBridgeChildThread()) {
UpdateAsyncCanvasRendererNow(aWrapper);
return;
}
SynchronousTask task("UpdateAsyncCanvasRenderer Lock");
- sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
- NewRunnableFunction(&UpdateAsyncCanvasRendererSync, &task, aWrapper));
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::UpdateAsyncCanvasRendererSync,
+ &task,
+ aWrapper);
+ GetMessageLoop()->PostTask(runnable.forget());
task.Wait();
}
-// static
-void ImageBridgeChild::UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aWrapper)
+void
+ImageBridgeChild::UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aWrapper)
{
MOZ_ASSERT(aWrapper);
- sImageBridgeChildSingleton->BeginTransaction();
+
+ if (!CanSend()) {
+ return;
+ }
+
+ BeginTransaction();
aWrapper->GetCanvasClient()->Updated();
- sImageBridgeChildSingleton->EndTransaction();
+ EndTransaction();
}
-static void
-FlushAllImagesSync(SynchronousTask* aTask,
- ImageClient* aClient,
- ImageContainer* aContainer,
- RefPtr<AsyncTransactionWaiter>&& aWaiter)
+void
+ImageBridgeChild::FlushAllImagesSync(SynchronousTask* aTask,
+ ImageClient* aClient,
+ ImageContainer* aContainer,
+ RefPtr<AsyncTransactionWaiter> aWaiter)
{
#ifdef MOZ_WIDGET_GONK
MOZ_ASSERT(aWaiter);
#else
MOZ_ASSERT(!aWaiter);
#endif
AutoCompleteTask complete(aTask);
- if (!ImageBridgeChild::IsCreated() || ImageBridgeChild::IsShutDown()) {
- // How sad. If we get into this branch it means that the ImageBridge
- // got destroyed between the time we ImageBridgeChild::FlushAllImage
- // was called on some thread, and the time this function was proxied
- // to the ImageBridge thread. ImageBridge gets destroyed way to late
- // in the shutdown of gecko for this to be happening for a good reason.
- NS_WARNING("Something is holding on to graphics resources after the shutdown"
- "of the graphics subsystem!");
+ if (!CanSend()) {
#ifdef MOZ_WIDGET_GONK
aWaiter->DecrementWaitCount();
#endif
return;
}
+
MOZ_ASSERT(aClient);
- sImageBridgeChildSingleton->BeginTransaction();
+ BeginTransaction();
if (aContainer) {
aContainer->ClearImagesFromImageBridge();
}
aClient->FlushAllImages(aWaiter);
- sImageBridgeChildSingleton->EndTransaction();
+ EndTransaction();
// This decrement is balanced by the increment in FlushAllImages.
// If any AsyncTransactionTrackers were created by FlushAllImages and attached
// to aWaiter, aWaiter will not complete until those trackers all complete.
// Otherwise, aWaiter will be ready to complete now.
#ifdef MOZ_WIDGET_GONK
aWaiter->DecrementWaitCount();
#endif
}
-// static
-void ImageBridgeChild::FlushAllImages(ImageClient* aClient,
- ImageContainer* aContainer)
+void
+ImageBridgeChild::FlushAllImages(ImageClient* aClient, ImageContainer* aContainer)
{
- if (!IsCreated() || IsShutDown()) {
- return;
- }
MOZ_ASSERT(aClient);
- MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
MOZ_ASSERT(!InImageBridgeChildThread());
+
if (InImageBridgeChildThread()) {
NS_ERROR("ImageBridgeChild::FlushAllImages() is called on ImageBridge thread.");
return;
}
SynchronousTask task("FlushAllImages Lock");
RefPtr<AsyncTransactionWaiter> waiter;
#ifdef MOZ_WIDGET_GONK
waiter = new AsyncTransactionWaiter();
// This increment is balanced by the decrement in FlushAllImagesSync
waiter->IncrementWaitCount();
#endif
- sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
- NewRunnableFunction(&FlushAllImagesSync, &task, aClient, aContainer, waiter));
+
+ // RefPtrs on arguments are not needed since this dispatches synchronously.
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::FlushAllImagesSync,
+ &task,
+ aClient,
+ aContainer,
+ waiter);
+ GetMessageLoop()->PostTask(runnable.forget());
task.Wait();
#ifdef MOZ_WIDGET_GONK
waiter->WaitComplete();
#endif
}
void
ImageBridgeChild::BeginTransaction()
{
- MOZ_ASSERT(!mShuttingDown);
+ MOZ_ASSERT(CanSend());
MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
UpdateFwdTransactionId();
mTxn->Begin();
}
void
ImageBridgeChild::EndTransaction()
{
- MOZ_ASSERT(!mShuttingDown);
+ MOZ_ASSERT(CanSend());
MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
AutoEndTransaction _(mTxn);
if (mTxn->IsEmpty()) {
return;
}
@@ -867,183 +872,231 @@ ImageBridgeChild::EndTransaction()
NS_RUNTIMEABORT("not reached");
}
}
void
ImageBridgeChild::SendImageBridgeThreadId()
{
#ifdef MOZ_WIDGET_GONK
- SendImageBridgeThreadId(gettid());
+ PImageBridgeChild::SendImageBridgeThreadId(gettid());
#endif
}
-static void CallSendImageBridgeThreadId(ImageBridgeChild* aImageBridgeChild)
-{
- MOZ_ASSERT(InImageBridgeChildThread());
- aImageBridgeChild->SendImageBridgeThreadId();
-}
-
bool
ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint)
{
MOZ_ASSERT(NS_IsMainThread());
gfxPlatform::GetPlatform();
sImageBridgeChildThread = new ImageBridgeThread();
if (!sImageBridgeChildThread->Start()) {
return false;
}
- sImageBridgeChildSingleton = new ImageBridgeChild();
+ RefPtr<ImageBridgeChild> child = new ImageBridgeChild();
- MessageLoop* loop = sImageBridgeChildSingleton->GetMessageLoop();
+ RefPtr<Runnable> runnable = NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
+ child,
+ &ImageBridgeChild::Bind,
+ Move(aEndpoint));
+ child->GetMessageLoop()->PostTask(runnable.forget());
- loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
- sImageBridgeChildSingleton, &ImageBridgeChild::Bind, Move(aEndpoint)));
- loop->PostTask(NewRunnableFunction(
- CallSendImageBridgeThreadId, sImageBridgeChildSingleton.get()));
+ // Assign this after so other threads can't post messages before we connect to IPDL.
+ {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = child;
+ }
- return sImageBridgeChildSingleton;
+ return true;
}
void
ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint)
{
- aEndpoint.Bind(this);
+ if (!aEndpoint.Bind(this)) {
+ return;
+ }
+
+ // This reference is dropped in DeallocPImageBridgeChild.
+ this->AddRef();
+
+ mCanSend = true;
+ SendImageBridgeThreadId();
+}
+
+void
+ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent)
+{
+ MessageLoop *parentMsgLoop = aParent->GetMessageLoop();
+ ipc::MessageChannel *parentChannel = aParent->GetIPCChannel();
+ Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide);
+
+ // This reference is dropped in DeallocPImageBridgeChild.
+ this->AddRef();
+
+ mCanSend = true;
+ SendImageBridgeThreadId();
}
void ImageBridgeChild::ShutDown()
{
MOZ_ASSERT(NS_IsMainThread());
- sIsShutDown = true;
+ if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
+ child->WillShutdown();
- if (ImageBridgeChild::IsCreated()) {
- MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = nullptr;
+ }
- {
- SynchronousTask task("ImageBridge ShutdownStep1 lock");
+ delete sImageBridgeChildThread;
+ sImageBridgeChildThread = nullptr;
+}
- sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
- NewRunnableFunction(&ImageBridgeShutdownStep1, &task));
-
- task.Wait();
- }
+void
+ImageBridgeChild::WillShutdown()
+{
+ {
+ SynchronousTask task("ImageBridge ShutdownStep1 lock");
- {
- SynchronousTask task("ImageBridge ShutdownStep2 lock");
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ShutdownStep1,
+ &task);
+ GetMessageLoop()->PostTask(runnable.forget());
- sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
- NewRunnableFunction(&ImageBridgeShutdownStep2, &task));
+ task.Wait();
+ }
+
+ {
+ SynchronousTask task("ImageBridge ShutdownStep2 lock");
- task.Wait();
- }
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ShutdownStep2,
+ &task);
+ GetMessageLoop()->PostTask(runnable.forget());
- sImageBridgeChildSingleton = nullptr;
+ task.Wait();
+ }
- delete sImageBridgeChildThread;
- sImageBridgeChildThread = nullptr;
+ if (mShutdownObserver) {
+ mShutdownObserver->Unregister();
+ mShutdownObserver = nullptr;
}
}
void
ImageBridgeChild::InitSameProcess()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
MOZ_ASSERT(!sImageBridgeChildSingleton);
MOZ_ASSERT(!sImageBridgeChildThread);
sImageBridgeChildThread = new ImageBridgeThread();
if (!sImageBridgeChildThread->IsRunning()) {
sImageBridgeChildThread->Start();
}
- sImageBridgeChildSingleton = new ImageBridgeChild();
+ RefPtr<ImageBridgeChild> child = new ImageBridgeChild();
RefPtr<ImageBridgeParent> parent = ImageBridgeParent::CreateSameProcess();
- sImageBridgeChildSingleton->ConnectAsync(parent);
- sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
- NewRunnableFunction(CallSendImageBridgeThreadId,
- sImageBridgeChildSingleton.get()));
+ RefPtr<Runnable> runnable = WrapRunnable(
+ child,
+ &ImageBridgeChild::BindSameProcess,
+ parent);
+ child->GetMessageLoop()->PostTask(runnable.forget());
+
+ // Assign this after so other threads can't post messages before we connect to IPDL.
+ {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = child;
+ }
}
/* static */ void
ImageBridgeChild::InitWithGPUProcess(Endpoint<PImageBridgeChild>&& aEndpoint)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!sImageBridgeChildSingleton);
MOZ_ASSERT(!sImageBridgeChildThread);
sImageBridgeChildThread = new ImageBridgeThread();
if (!sImageBridgeChildThread->IsRunning()) {
sImageBridgeChildThread->Start();
}
- sImageBridgeChildSingleton = new ImageBridgeChild();
+ RefPtr<ImageBridgeChild> child = new ImageBridgeChild();
+
+ MessageLoop* loop = child->GetMessageLoop();
+ loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
+ child, &ImageBridgeChild::Bind, Move(aEndpoint)));
- MessageLoop* loop = sImageBridgeChildSingleton->GetMessageLoop();
- loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
- sImageBridgeChildSingleton, &ImageBridgeChild::Bind, Move(aEndpoint)));
+ // Assign this after so other threads can't post messages before we connect to IPDL.
+ {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = child;
+ }
}
bool InImageBridgeChildThread()
{
- return ImageBridgeChild::IsCreated() &&
+ return sImageBridgeChildThread &&
sImageBridgeChildThread->thread_id() == PlatformThread::CurrentId();
}
MessageLoop * ImageBridgeChild::GetMessageLoop() const
{
return sImageBridgeChildThread ? sImageBridgeChildThread->message_loop() : nullptr;
}
-void ImageBridgeChild::ConnectAsync(ImageBridgeParent* aParent)
-{
- GetMessageLoop()->PostTask(NewRunnableFunction(&ConnectImageBridge,
- this, aParent));
-}
-
-void
+/* static */ void
ImageBridgeChild::IdentifyCompositorTextureHost(const TextureFactoryIdentifier& aIdentifier)
{
- if (sImageBridgeChildSingleton) {
- sImageBridgeChildSingleton->IdentifyTextureHost(aIdentifier);
+ if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
+ child->IdentifyTextureHost(aIdentifier);
}
}
RefPtr<ImageClient>
ImageBridgeChild::CreateImageClient(CompositableType aType,
ImageContainer* aImageContainer,
ImageContainerChild* aContainerChild)
{
if (InImageBridgeChildThread()) {
return CreateImageClientNow(aType, aImageContainer, aContainerChild);
}
SynchronousTask task("CreateImageClient Lock");
RefPtr<ImageClient> result = nullptr;
- GetMessageLoop()->PostTask(
- NewRunnableFunction(&CreateImageClientSync, &task, this, &result, aType,
- aImageContainer, aContainerChild));
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::CreateImageClientSync,
+ &task,
+ &result,
+ aType,
+ aImageContainer,
+ aContainerChild);
+ GetMessageLoop()->PostTask(runnable.forget());
task.Wait();
return result;
}
RefPtr<ImageClient>
ImageBridgeChild::CreateImageClientNow(CompositableType aType,
ImageContainer* aImageContainer,
ImageContainerChild* aContainerChild)
{
- MOZ_ASSERT(!mShuttingDown);
MOZ_ASSERT(InImageBridgeChildThread());
if (aImageContainer) {
SendPImageContainerConstructor(aContainerChild);
aContainerChild->RegisterWithIPDL();
}
RefPtr<ImageClient> client = ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS);
@@ -1059,19 +1112,26 @@ ImageBridgeChild::CreateCanvasClient(Can
TextureFlags aFlag)
{
if (InImageBridgeChildThread()) {
return CreateCanvasClientNow(aType, aFlag);
}
SynchronousTask task("CreateCanvasClient Lock");
+ // RefPtrs on arguments are not needed since this dispatches synchronously.
RefPtr<CanvasClient> result = nullptr;
- GetMessageLoop()->PostTask(NewRunnableFunction(&CreateCanvasClientSync,
- &task, aType, aFlag, &result));
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::CreateCanvasClientSync,
+ &task,
+ aType,
+ aFlag,
+ &result);
+ GetMessageLoop()->PostTask(runnable.forget());
task.Wait();
return result.forget();
}
already_AddRefed<CanvasClient>
ImageBridgeChild::CreateCanvasClientNow(CanvasClient::CanvasClientType aType,
@@ -1086,51 +1146,50 @@ ImageBridgeChild::CreateCanvasClientNow(
return client.forget();
}
bool
ImageBridgeChild::AllocUnsafeShmem(size_t aSize,
ipc::SharedMemory::SharedMemoryType aType,
ipc::Shmem* aShmem)
{
- MOZ_ASSERT(!mShuttingDown);
- if (InImageBridgeChildThread()) {
- return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
- } else {
+ if (!InImageBridgeChildThread()) {
return DispatchAllocShmemInternal(aSize, aType, aShmem, true); // true: unsafe
}
+
+ MOZ_ASSERT(CanSend());
+ return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
}
bool
ImageBridgeChild::AllocShmem(size_t aSize,
ipc::SharedMemory::SharedMemoryType aType,
ipc::Shmem* aShmem)
{
- MOZ_ASSERT(!mShuttingDown);
- if (InImageBridgeChildThread()) {
- return PImageBridgeChild::AllocShmem(aSize, aType,
- aShmem);
- } else {
+ if (!InImageBridgeChildThread()) {
return DispatchAllocShmemInternal(aSize, aType, aShmem, false); // false: unsafe
}
+
+ MOZ_ASSERT(CanSend());
+ return PImageBridgeChild::AllocShmem(aSize, aType, aShmem);
}
// NewRunnableFunction accepts a limited number of parameters so we need a
// struct here
struct AllocShmemParams {
RefPtr<ISurfaceAllocator> mAllocator;
size_t mSize;
ipc::SharedMemory::SharedMemoryType mType;
ipc::Shmem* mShmem;
bool mUnsafe;
bool mSuccess;
};
-static void
-ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams)
+void
+ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams)
{
AutoCompleteTask complete(aTask);
MOZ_ASSERT(aParams);
auto shmAllocator = aParams->mAllocator->AsShmemAllocator();
if (aParams->mUnsafe) {
aParams->mSuccess = shmAllocator->AllocUnsafeShmem(aParams->mSize,
@@ -1150,72 +1209,81 @@ ImageBridgeChild::DispatchAllocShmemInte
bool aUnsafe)
{
SynchronousTask task("AllocatorProxy alloc");
AllocShmemParams params = {
this, aSize, aType, aShmem, aUnsafe, true
};
- GetMessageLoop()->PostTask(NewRunnableFunction(&ProxyAllocShmemNow,
- &task, ¶ms));
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ProxyAllocShmemNow,
+ &task,
+ ¶ms);
+ GetMessageLoop()->PostTask(runnable.forget());
task.Wait();
return params.mSuccess;
}
-static void ProxyDeallocShmemNow(SynchronousTask* aTask,
- ISurfaceAllocator* aAllocator,
- ipc::Shmem* aShmem)
+void
+ImageBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask,
+ ISurfaceAllocator* aAllocator,
+ ipc::Shmem* aShmem)
{
AutoCompleteTask complete(aTask);
MOZ_ASSERT(aShmem);
aAllocator->AsShmemAllocator()->DeallocShmem(*aShmem);
}
void
ImageBridgeChild::DeallocShmem(ipc::Shmem& aShmem)
{
if (InImageBridgeChildThread()) {
PImageBridgeChild::DeallocShmem(aShmem);
- } else {
- SynchronousTask task("AllocatorProxy Dealloc");
+ return;
+ }
+
+ SynchronousTask task("AllocatorProxy Dealloc");
- GetMessageLoop()->PostTask(NewRunnableFunction(&ProxyDeallocShmemNow,
- &task,
- this,
- &aShmem));
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ProxyDeallocShmemNow,
+ &task,
+ this,
+ &aShmem);
+ GetMessageLoop()->PostTask(runnable.forget());
- task.Wait();
- }
+ task.Wait();
}
PTextureChild*
ImageBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
const LayersBackend&,
const TextureFlags&,
const uint64_t& aSerial)
{
- MOZ_ASSERT(!mShuttingDown);
+ MOZ_ASSERT(CanSend());
return TextureClient::CreateIPDLActor();
}
bool
ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor)
{
return TextureClient::DestroyIPDLActor(actor);
}
PMediaSystemResourceManagerChild*
ImageBridgeChild::AllocPMediaSystemResourceManagerChild()
{
- MOZ_ASSERT(!mShuttingDown);
+ MOZ_ASSERT(CanSend());
return new mozilla::media::MediaSystemResourceManagerChild();
}
bool
ImageBridgeChild::DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild* aActor)
{
MOZ_ASSERT(aActor);
delete static_cast<mozilla::media::MediaSystemResourceManagerChild*>(aActor);
@@ -1305,17 +1373,17 @@ ImageBridgeChild::RecvDidComposite(Infal
}
PTextureChild*
ImageBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
LayersBackend aLayersBackend,
TextureFlags aFlags,
uint64_t aSerial)
{
- MOZ_ASSERT(!mShuttingDown);
+ MOZ_ASSERT(CanSend());
return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial);
}
static bool
IBCAddOpDestroy(CompositableTransaction* aTxn, const OpDestroy& op, bool synchronously)
{
if (aTxn->Finished()) {
return false;
@@ -1342,17 +1410,17 @@ ImageBridgeChild::DestroyInTransaction(P
return IBCAddOpDestroy(mTxn, OpDestroy(aCompositable), synchronously);
}
void
ImageBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable,
TextureClient* aTexture)
{
- MOZ_ASSERT(!mShuttingDown);
+ MOZ_ASSERT(CanSend());
MOZ_ASSERT(aTexture);
MOZ_ASSERT(aTexture->IsSharedWithCompositor());
MOZ_ASSERT(aCompositable->IsConnected());
if (!aTexture || !aTexture->IsSharedWithCompositor() || !aCompositable->IsConnected()) {
return;
}
CompositableOperation op(
@@ -1366,17 +1434,17 @@ ImageBridgeChild::RemoveTextureFromCompo
}
}
void
ImageBridgeChild::RemoveTextureFromCompositableAsync(AsyncTransactionTracker* aAsyncTransactionTracker,
CompositableClient* aCompositable,
TextureClient* aTexture)
{
- MOZ_ASSERT(!mShuttingDown);
+ MOZ_ASSERT(CanSend());
MOZ_ASSERT(aTexture);
MOZ_ASSERT(aTexture->IsSharedWithCompositor());
MOZ_ASSERT(aCompositable->IsConnected());
if (!aTexture || !aTexture->IsSharedWithCompositor() || !aCompositable->IsConnected()) {
return;
}
CompositableOperation op(
@@ -1392,30 +1460,66 @@ ImageBridgeChild::RemoveTextureFromCompo
mTrackersHolder.HoldUntilComplete(aAsyncTransactionTracker);
}
bool ImageBridgeChild::IsSameProcess() const
{
return OtherPid() == base::GetCurrentProcId();
}
-static void
-DestroyCompositableNow(RefPtr<ImageBridgeChild> aImageBridge,
- RefPtr<CompositableChild> aCompositable)
-{
- aImageBridge->Destroy(aCompositable);
-}
-
void
ImageBridgeChild::Destroy(CompositableChild* aCompositable)
{
if (!InImageBridgeChildThread()) {
- RefPtr<ImageBridgeChild> self = this;
- RefPtr<CompositableChild> compositable = aCompositable;
- GetMessageLoop()->PostTask(
- NewRunnableFunction(&DestroyCompositableNow, self, compositable));
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::Destroy,
+ RefPtr<CompositableChild>(aCompositable));
+ GetMessageLoop()->PostTask(runnable.forget());
return;
}
CompositableForwarder::Destroy(aCompositable);
}
+bool
+ImageBridgeChild::CanSend() const
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+ return mCanSend;
+}
+
+void
+ImageBridgeChild::OnXPCOMShutdown()
+{
+ // This uses nsIObserverService, so it must be cleaned up. Other threads may
+ // hold references to ImageBridgeChild and we may actually be destroyed well
+ // after XPCOM shutdown.
+ mActiveResourceTracker = nullptr;
+}
+
+NS_IMPL_ISUPPORTS(ImageBridgeChild::ShutdownObserver, nsIObserver);
+
+ImageBridgeChild::ShutdownObserver::ShutdownObserver(ImageBridgeChild* aImageBridge)
+ : mImageBridge(aImageBridge)
+{
+ nsContentUtils::RegisterShutdownObserver(this);
+}
+
+void
+ImageBridgeChild::ShutdownObserver::Unregister()
+{
+ nsContentUtils::UnregisterShutdownObserver(this);
+ mImageBridge = nullptr;
+}
+
+NS_IMETHODIMP
+ImageBridgeChild::ShutdownObserver::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+ mImageBridge->OnXPCOMShutdown();
+ }
+ return NS_OK;
+}
+
} // namespace layers
} // namespace mozilla
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -14,16 +14,17 @@
#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
#include "mozilla/layers/AsyncTransactionTracker.h" // for AsyncTransactionTrackerHolder
#include "mozilla/layers/CanvasClient.h"
#include "mozilla/layers/CompositableForwarder.h"
#include "mozilla/layers/CompositorTypes.h"
#include "mozilla/layers/PImageBridgeChild.h"
#include "mozilla/Mutex.h"
#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsIObserver.h"
#include "nsRegion.h" // for nsIntRegion
#include "mozilla/gfx/Rect.h"
class MessageLoop;
namespace base {
class Thread;
} // namespace base
@@ -40,16 +41,18 @@ class AsyncTransactionTracker;
class ImageClient;
class ImageContainer;
class ImageContainerChild;
class ImageBridgeParent;
class CompositableClient;
struct CompositableTransaction;
class Image;
class TextureClient;
+class SynchronousTask;
+struct AllocShmemParams;
/**
* Returns true if the current thread is the ImageBrdigeChild's thread.
*
* Can be called from any thread.
*/
bool InImageBridgeChildThread();
@@ -104,16 +107,17 @@ bool InImageBridgeChildThread();
* not used at all (except for the very first transaction that provides the
* CompositableHost with an AsyncID).
*/
class ImageBridgeChild final : public PImageBridgeChild
, public CompositableForwarder
, public ShmemAllocator
{
friend class ImageContainer;
+
typedef InfallibleTArray<AsyncParentMessageData> AsyncParentMessageArray;
public:
virtual ShmemAllocator* AsShmemAllocator() override { return this; }
/**
* Creates the image bridge with a dedicated thread for ImageBridgeChild.
*
@@ -130,46 +134,22 @@ public:
* ImageBridge's thread.
*
* If you don't want to destroy the thread, call DestroyBridge directly
* instead.
*/
static void ShutDown();
/**
- * Returns true if the singleton has been created.
- *
- * Can be called from any thread.
- */
- static bool IsCreated();
- /**
- * Returns true if the singleton's ShutDown() was called.
- *
- * Can be called from any thread.
- */
- static bool IsShutDown()
- {
- return sIsShutDown;
- }
-
- /**
* returns the singleton instance.
*
* can be called from any thread.
*/
- static ImageBridgeChild* GetSingleton();
-
+ static RefPtr<ImageBridgeChild> GetSingleton();
- /**
- * Dispatches a task to the ImageBridgeChild thread to do the connection
- */
- void ConnectAsync(ImageBridgeParent* aParent);
-
- using PImageBridgeChild::SendImageBridgeThreadId;
- void SendImageBridgeThreadId();
static void IdentifyCompositorTextureHost(const TextureFactoryIdentifier& aIdentifier);
void BeginTransaction();
void EndTransaction();
/**
* Returns the ImageBridgeChild's thread.
@@ -228,31 +208,64 @@ public:
// Create an ImageClient from the ImageBridge thread.
RefPtr<ImageClient> CreateImageClientNow(
CompositableType aType,
ImageContainer* aImageContainer,
ImageContainerChild* aContainerChild);
already_AddRefed<CanvasClient> CreateCanvasClient(CanvasClient::CanvasClientType aType,
TextureFlags aFlag);
- already_AddRefed<CanvasClient> CreateCanvasClientNow(CanvasClient::CanvasClientType aType,
- TextureFlags aFlag);
-
- static void DispatchReleaseImageContainer(ImageContainerChild* aChild);
+ void ReleaseImageContainer(RefPtr<ImageContainerChild> aChild);
+ void UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aClient);
+ void UpdateImageClient(RefPtr<ImageClient> aClient, RefPtr<ImageContainer> aContainer);
static void DispatchReleaseTextureClient(TextureClient* aClient);
- static void DispatchImageClientUpdate(ImageClient* aClient, ImageContainer* aContainer);
-
- static void UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aClient);
- static void UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aClient);
/**
* Flush all Images sent to CompositableHost.
*/
- static void FlushAllImages(ImageClient* aClient, ImageContainer* aContainer);
+ void FlushAllImages(ImageClient* aClient, ImageContainer* aContainer);
+
+private:
+ // Helpers for dispatching.
+ already_AddRefed<CanvasClient> CreateCanvasClientNow(
+ CanvasClient::CanvasClientType aType,
+ TextureFlags aFlags);
+ void CreateCanvasClientSync(
+ SynchronousTask* aTask,
+ CanvasClient::CanvasClientType aType,
+ TextureFlags aFlags,
+ RefPtr<CanvasClient>* const outResult);
+
+ void CreateImageClientSync(
+ SynchronousTask* aTask,
+ RefPtr<ImageClient>* result,
+ CompositableType aType,
+ ImageContainer* aImageContainer,
+ ImageContainerChild* aContainerChild);
+ void ReleaseTextureClientNow(TextureClient* aClient);
+
+ void UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aClient);
+ void UpdateAsyncCanvasRendererSync(
+ SynchronousTask* aTask,
+ AsyncCanvasRenderer* aWrapper);
+
+ void FlushAllImagesSync(
+ SynchronousTask* aTask,
+ ImageClient* aClient,
+ ImageContainer* aContainer,
+ RefPtr<AsyncTransactionWaiter> aWaiter);
+
+ void ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams);
+ void ProxyDeallocShmemNow(
+ SynchronousTask* aTask,
+ ISurfaceAllocator* aAllocator,
+ Shmem* aShmem);
+
+public:
// CompositableForwarder
virtual void Connect(CompositableClient* aCompositable,
ImageContainer* aImageContainer) override;
virtual bool UsesImageBridge() const override { return true; }
/**
@@ -349,34 +362,63 @@ public:
virtual void UpdateFwdTransactionId() override { ++mFwdTransactionId; }
virtual uint64_t GetFwdTransactionId() override { return mFwdTransactionId; }
bool InForwarderThread() override {
return InImageBridgeChildThread();
}
-
- void MarkShutDown();
-
- void FallbackDestroyActors();
-
protected:
ImageBridgeChild();
bool DispatchAllocShmemInternal(size_t aSize,
SharedMemory::SharedMemoryType aType,
Shmem* aShmem,
bool aUnsafe);
void Bind(Endpoint<PImageBridgeChild>&& aEndpoint);
+ void BindSameProcess(RefPtr<ImageBridgeParent> aParent);
+
+ void SendImageBridgeThreadId();
+
+ void WillShutdown();
+ void ShutdownStep1(SynchronousTask* aTask);
+ void ShutdownStep2(SynchronousTask* aTask);
+ void MarkShutDown();
+ void FallbackDestroyActors();
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void DeallocPImageBridgeChild() override;
+
+ bool CanSend() const;
+
+private:
+ class ShutdownObserver final : public nsIObserver
+ {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ explicit ShutdownObserver(ImageBridgeChild* aImageBridge);
+ void Unregister();
+
+ private:
+ ~ShutdownObserver() {};
+
+ ImageBridgeChild* mImageBridge;
+ };
+ friend class ShutdownObserver;
+
+ void OnXPCOMShutdown();
private:
CompositableTransaction* mTxn;
- Atomic<bool> mShuttingDown;
- static Atomic<bool> sIsShutDown;
+
+ bool mCanSend;
+ bool mCalledClose;
/**
* Transaction id of CompositableForwarder.
* It is incrementaed by UpdateFwdTransactionId() in each BeginTransaction() call.
*/
uint64_t mFwdTransactionId;
/**
@@ -386,14 +428,16 @@ private:
nsDataHashtable<nsUint64HashKey, RefPtr<TextureClient> > mTexturesWaitingRecycled;
AsyncTransactionTrackersHolder mTrackersHolder;
#ifdef MOZ_WIDGET_GONK
Mutex mWaitingFenceHandleMutex;
nsDataHashtable<nsUint64HashKey, RefPtr<TextureClient> > mTexturesWaitingFenceHandle;
#endif
+
+ RefPtr<ShutdownObserver> mShutdownObserver;
};
} // namespace layers
} // namespace mozilla
#endif
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -1669,16 +1669,17 @@ dateTimeFormat_toSource(JSContext* cx, u
static const JSFunctionSpec dateTimeFormat_static_methods[] = {
JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_DateTimeFormat_supportedLocalesOf", 1, 0),
JS_FS_END
};
static const JSFunctionSpec dateTimeFormat_methods[] = {
JS_SELF_HOSTED_FN("resolvedOptions", "Intl_DateTimeFormat_resolvedOptions", 0, 0),
+ JS_SELF_HOSTED_FN("formatToParts", "Intl_DateTimeFormat_formatToParts", 0, 0),
#if JS_HAS_TOSOURCE
JS_FN(js_toSource_str, dateTimeFormat_toSource, 0, 0),
#endif
JS_FS_END
};
/**
* DateTimeFormat constructor.
@@ -1806,33 +1807,16 @@ CreateDateTimeFormatPrototype(JSContext*
}
if (!DefineProperty(cx, proto, cx->names().format, UndefinedHandleValue,
JS_DATA_TO_FUNC_PTR(JSGetterOp, &getter.toObject()),
nullptr, JSPROP_GETTER | JSPROP_SHARED))
{
return nullptr;
}
- // If the still-experimental DateTimeFormat.prototype.formatToParts method
- // is enabled, also add it.
- if (cx->compartment()->creationOptions().experimentalDateTimeFormatFormatToPartsEnabled()) {
- RootedValue ftp(cx);
- HandlePropertyName name = cx->names().formatToParts;
- if (!GlobalObject::getSelfHostedFunction(cx, cx->global(),
- cx->names().DateTimeFormatFormatToParts,
- name,
- 0, &ftp))
- {
- return nullptr;
- }
-
- if (!DefineProperty(cx, proto, cx->names().formatToParts, ftp, nullptr, nullptr, 0))
- return nullptr;
- }
-
RootedValue options(cx);
if (!CreateDefaultOptions(cx, &options))
return nullptr;
// 12.2.1 and 12.3
if (!IntlInitialize(cx, proto, cx->names().InitializeDateTimeFormat, UndefinedHandleValue,
options))
{
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -12031,18 +12031,20 @@ IonBuilder::loadUnboxedValue(MDefinition
break;
case JSVAL_TYPE_STRING:
load = MLoadUnboxedString::New(alloc(), elements, index, elementsOffset);
break;
case JSVAL_TYPE_OBJECT: {
MLoadUnboxedObjectOrNull::NullBehavior nullBehavior;
- if (types->hasType(TypeSet::NullType()) || barrier != BarrierKind::NoBarrier)
+ if (types->hasType(TypeSet::NullType()))
nullBehavior = MLoadUnboxedObjectOrNull::HandleNull;
+ else if (barrier != BarrierKind::NoBarrier)
+ nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull;
else
nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible;
load = MLoadUnboxedObjectOrNull::New(alloc(), elements, index, nullBehavior,
elementsOffset);
break;
}
default:
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2140,17 +2140,16 @@ class JS_PUBLIC_API(CompartmentCreationO
public:
CompartmentCreationOptions()
: addonId_(nullptr),
traceGlobal_(nullptr),
invisibleToDebugger_(false),
mergeable_(false),
preserveJitCode_(false),
cloneSingletons_(false),
- experimentalDateTimeFormatFormatToPartsEnabled_(false),
sharedMemoryAndAtomics_(false),
secureContext_(false)
{
zone_.spec = JS::FreshZone;
}
// A null add-on ID means that the compartment is not associated with an
// add-on.
@@ -2205,33 +2204,16 @@ class JS_PUBLIC_API(CompartmentCreationO
}
bool cloneSingletons() const { return cloneSingletons_; }
CompartmentCreationOptions& setCloneSingletons(bool flag) {
cloneSingletons_ = flag;
return *this;
}
- // ECMA-402 is considering adding a "formatToParts" DateTimeFormat method,
- // that exposes not just a formatted string but its ordered subcomponents.
- // The method, its semantics, and its name are all well short of being
- // finalized, so for now it's exposed *only* if requested.
- //
- // Until "formatToParts" is included in a final specification edition, it's
- // subject to change or removal at any time. Do *not* rely on it in
- // mission-critical code that can't be changed if ECMA-402 decides not to
- // accept the method in its current form.
- bool experimentalDateTimeFormatFormatToPartsEnabled() const {
- return experimentalDateTimeFormatFormatToPartsEnabled_;
- }
- CompartmentCreationOptions& setExperimentalDateTimeFormatFormatToPartsEnabled(bool flag) {
- experimentalDateTimeFormatFormatToPartsEnabled_ = flag;
- return *this;
- }
-
bool getSharedMemoryAndAtomicsEnabled() const;
CompartmentCreationOptions& setSharedMemoryAndAtomicsEnabled(bool flag);
// This flag doesn't affect JS engine behavior. It is used by Gecko to
// mark whether content windows and workers are "Secure Context"s. See
// https://w3c.github.io/webappsec-secure-contexts/
// https://bugzilla.mozilla.org/show_bug.cgi?id=1162772#c34
bool secureContext() const { return secureContext_; }
@@ -2246,17 +2228,16 @@ class JS_PUBLIC_API(CompartmentCreationO
union {
ZoneSpecifier spec;
void* pointer; // js::Zone* is not exposed in the API.
} zone_;
bool invisibleToDebugger_;
bool mergeable_;
bool preserveJitCode_;
bool cloneSingletons_;
- bool experimentalDateTimeFormatFormatToPartsEnabled_;
bool sharedMemoryAndAtomics_;
bool secureContext_;
};
/**
* CompartmentBehaviors specifies behaviors of a compartment that can be
* changed after the compartment's been created.
*/
--- a/js/src/jsautokw.py
+++ b/js/src/jsautokw.py
@@ -1,22 +1,213 @@
# 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/.
-from __future__ import print_function
+import re
+import sys
+
+def read_keyword_list(filename):
+ macro_pat = re.compile(r"^\s*macro\(([^,]+), *[^,]+, *[^\)]+\)\s*\\?$")
+
+ keyword_list = []
+ index = 0
+ with open(filename, 'r') as f:
+ for line in f:
+ m = macro_pat.search(line)
+ if m:
+ keyword_list.append((index, m.group(1)))
+ index += 1
+
+ assert(len(keyword_list) != 0)
+
+ return keyword_list
+
+def line(opt, s):
+ opt['output'].write('{}{}\n'.format(' ' * opt['indent_level'], s))
+
+def indent(opt):
+ opt['indent_level'] += 1
+
+def dedent(opt):
+ opt['indent_level'] -= 1
+
+def span_and_count_at(keyword_list, column):
+ assert(len(keyword_list) != 0);
+
+ chars_dict = {}
+ for index, keyword in keyword_list:
+ chars_dict[ord(keyword[column])] = True
+
+ chars = sorted(chars_dict.keys())
+ return chars[-1] - chars[0] + 1, len(chars)
+
+def optimal_switch_column(opt, keyword_list, columns, unprocessed_columns):
+ assert(len(keyword_list) != 0);
+ assert(unprocessed_columns != 0);
+
+ min_count = 0
+ min_span = 0
+ min_count_index = 0
+ min_span_index = 0
+
+ for index in range(0, unprocessed_columns):
+ span, count = span_and_count_at(keyword_list, columns[index])
+ assert(span != 0)
-import os
-import sys
-import subprocess
+ if span == 1:
+ assert(count == 1)
+ return 1, True
+
+ assert(count != 1)
+ if index == 0 or min_span > span:
+ min_span = span
+ min_span_index = index
+
+ if index == 0 or min_count > count:
+ min_count = count
+ min_count_index = index
+
+ if min_count <= opt['use_if_threshold']:
+ return min_count_index, True
+
+ return min_span_index, False
+
+def split_list_per_column(keyword_list, column):
+ assert(len(keyword_list) != 0);
+
+ column_dict = {}
+ for item in keyword_list:
+ index, keyword = item
+ per_column = column_dict.setdefault(keyword[column], [])
+ per_column.append(item)
+
+ return sorted(column_dict.items(), key=lambda (char, keyword): ord(char))
+
+def generate_letter_switch(opt, unprocessed_columns, keyword_list,
+ columns=None):
+ assert(len(keyword_list) != 0);
+
+ if not columns:
+ columns = range(0, unprocessed_columns)
+
+ if len(keyword_list) == 1:
+ index, keyword = keyword_list[0]
+
+ if unprocessed_columns == 0:
+ line(opt, 'JSKW_GOT_MATCH({}) /* {} */'.format(index, keyword))
+ return
+
+ if unprocessed_columns > opt['char_tail_test_threshold']:
+ line(opt, 'JSKW_TEST_GUESS({}) /* {} */'.format(index, keyword))
+ return
+
+ conds = []
+ for column in columns[0:unprocessed_columns]:
+ quoted = repr(keyword[column])
+ conds.append('JSKW_AT({})=={}'.format(column, quoted))
-def main(output, exe):
- # moz.build passes in the exe name without any path, so to run it we need to
- # prepend the './'
- run_exe = exe if os.path.isabs(exe) else './%s' % exe
+ line(opt, 'if ({}) {{'.format(' && '.join(conds)))
+
+ indent(opt)
+ line(opt, 'JSKW_GOT_MATCH({}) /* {} */'.format(index, keyword))
+ dedent(opt)
+
+ line(opt, '}')
+ line(opt, 'JSKW_NO_MATCH()')
+ return
+
+ assert(unprocessed_columns != 0);
+
+ optimal_column_index, use_if = optimal_switch_column(opt, keyword_list,
+ columns,
+ unprocessed_columns)
+ optimal_column = columns[optimal_column_index]
+
+ # Make a copy to avoid breaking passed list.
+ columns = columns[:]
+ columns[optimal_column_index] = columns[unprocessed_columns - 1]
+
+ list_per_column = split_list_per_column(keyword_list, optimal_column)
+
+ if not use_if:
+ line(opt, 'switch (JSKW_AT({})) {{'.format(optimal_column))
+
+ for char, keyword_list_per_column in list_per_column:
+ quoted = repr(char)
+ if use_if:
+ line(opt, 'if (JSKW_AT({}) == {}) {{'.format(optimal_column,
+ quoted))
+ else:
+ line(opt, ' case {}:'.format(quoted))
+
+ indent(opt)
+ generate_letter_switch(opt, unprocessed_columns - 1,
+ keyword_list_per_column, columns)
+ dedent(opt)
+
+ if use_if:
+ line(opt, '}')
+
+ if not use_if:
+ line(opt, '}')
+
+ line(opt, 'JSKW_NO_MATCH()')
+
+def split_list_per_length(keyword_list):
+ assert(len(keyword_list) != 0);
- # Use universal_newlines so everything is '\n', which gets converted to
- # '\r\n' when writing out the file in Windows.
- data = subprocess.check_output([run_exe], universal_newlines=True)
- output.write(data)
+ length_dict = {}
+ for item in keyword_list:
+ index, keyword = item
+ per_length = length_dict.setdefault(len(keyword), [])
+ per_length.append(item)
+
+ return sorted(length_dict.items(), key=lambda (length, keyword): length)
+
+def generate_switch(opt, keyword_list):
+ assert(len(keyword_list) != 0);
+
+ line(opt, '/*')
+ line(opt, ' * Generating switch for the list of {} entries:'.format(len(keyword_list)))
+ for index, keyword in keyword_list:
+ line(opt, ' * {}'.format(keyword))
+ line(opt, ' */')
+
+ list_per_length = split_list_per_length(keyword_list)
+
+ use_if = False
+ if len(list_per_length) < opt['use_if_threshold']:
+ use_if = True
+
+ if not use_if:
+ line(opt, 'switch (JSKW_LENGTH()) {')
+
+ for length, keyword_list_per_length in list_per_length:
+ if use_if:
+ line(opt, 'if (JSKW_LENGTH() == {}) {{'.format(length))
+ else:
+ line(opt, ' case {}:'.format(length))
+
+ indent(opt)
+ generate_letter_switch(opt, length, keyword_list_per_length)
+ dedent(opt)
+
+ if use_if:
+ line(opt, '}')
+
+ if not use_if:
+ line(opt, '}')
+ line(opt, 'JSKW_NO_MATCH()')
+
+def main(output, keywords_h):
+ keyword_list = read_keyword_list(keywords_h)
+
+ opt = {
+ 'indent_level': 1,
+ 'use_if_threshold': 3,
+ 'char_tail_test_threshold': 4,
+ 'output': output
+ }
+ generate_switch(opt, keyword_list)
if __name__ == '__main__':
main(sys.stdout, *sys.argv[1:])
deleted file mode 100644
--- a/js/src/jskwgen.cpp
+++ /dev/null
@@ -1,425 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include <assert.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "vm/Keywords.h"
-
-static const char * const keyword_list[] = {
-#define KEYWORD_STRING(keyword, name, type) #keyword,
- FOR_EACH_JAVASCRIPT_KEYWORD(KEYWORD_STRING)
-#undef KEYWORD_STRING
-};
-
-struct gen_opt {
- FILE* output; /* output file for generated source */
- unsigned use_if_threshold; /* max number of choices to generate
- "if" selector instead of "switch" */
- unsigned char_tail_test_threshold; /* max number of unprocessed columns
- to use inlined char compare
- for remaining chars and not generic
- string compare code */
- unsigned indent_level; /* current source identation level */
-};
-
-static unsigned column_to_compare;
-
-static int
-length_comparator(const void* a, const void* b)
-{
- const char* str1 = keyword_list[*(unsigned*)a];
- const char* str2 = keyword_list[*(unsigned*)b];
- return (int)strlen(str1) - (int)strlen(str2);
-}
-
-static int
-column_comparator(const void* a, const void* b)
-{
- const char* str1 = keyword_list[*(unsigned*)a];
- const char* str2 = keyword_list[*(unsigned*)b];
- return (int)str1[column_to_compare] - (int)str2[column_to_compare];
-}
-
-static unsigned
-count_different_lengths(unsigned indexes[], unsigned nelem)
-{
- unsigned nlength, current_length, i, l;
-
- current_length = 0;
- nlength = 0;
- for (i = 0; i != nelem; ++i) {
- l = (unsigned)strlen(keyword_list[indexes[i]]);
- assert(l != 0);
- if (current_length != l) {
- ++nlength;
- current_length = l;
- }
- }
- return nlength;
-}
-
-static void
-find_char_span_and_count(unsigned indexes[], unsigned nelem, unsigned column,
- unsigned* span_result, unsigned* count_result)
-{
- unsigned i, count;
- unsigned char c, prev, minc, maxc;
-
- assert(nelem != 0);
- minc = maxc = prev = (unsigned char)keyword_list[indexes[0]][column];
- count = 1;
- for (i = 1; i != nelem; ++i) {
- c = (unsigned char)keyword_list[indexes[i]][column];
- if (prev != c) {
- prev = c;
- ++count;
- if (minc > c) {
- minc = c;
- } else if (maxc < c) {
- maxc = c;
- }
- }
- }
-
- *span_result = maxc - minc + 1;
- *count_result = count;
-}
-
-static unsigned
-find_optimal_switch_column(struct gen_opt* opt,
- unsigned indexes[], unsigned nelem,
- unsigned columns[], unsigned unprocessed_columns,
- int* use_if_result)
-{
- unsigned i;
- unsigned span, min_span, min_span_index;
- unsigned nchar, min_nchar, min_nchar_index;
-
- assert(unprocessed_columns != 0);
- i = 0;
- min_nchar = min_span = (unsigned)-1;
- min_nchar_index = min_span_index = 0;
- do {
- column_to_compare = columns[i];
- qsort(indexes, nelem, sizeof(indexes[0]), column_comparator);
- find_char_span_and_count(indexes, nelem, column_to_compare,
- &span, &nchar);
- assert(span != 0);
- if (span == 1) {
- assert(nchar == 1);
- *use_if_result = 1;
- return 1;
- }
- assert(nchar != 1);
- if (min_span > span) {
- min_span = span;
- min_span_index = i;
- }
- if (min_nchar > nchar) {
- min_nchar = nchar;
- min_nchar_index = i;
- }
- } while (++i != unprocessed_columns);
-
- if (min_nchar <= opt->use_if_threshold) {
- *use_if_result = 1;
- i = min_nchar_index;
- } else {
- *use_if_result = 0;
- i = min_span_index;
- }
-
- /*
- * Restore order corresponding to i if it was destroyed by
- * subsequent sort.
- */
- if (i != unprocessed_columns - 1) {
- column_to_compare = columns[i];
- qsort(indexes, nelem, sizeof(indexes[0]), column_comparator);
- }
-
- return i;
-}
-
-
-static void
-p(struct gen_opt* opt, const char* format, ...)
-{
- va_list ap;
-
- va_start(ap, format);
- vfprintf(opt->output, format, ap);
- va_end(ap);
-}
-
-/* Size for '\xxx' where xxx is octal escape */
-#define MIN_QUOTED_CHAR_BUFFER 7
-
-static char*
-qchar(char c, char* quoted_buffer)
-{
- char* s;
-
- s = quoted_buffer;
- *s++ = '\'';
- switch (c) {
- case '\n': c = 'n'; goto one_char_escape;
- case '\r': c = 'r'; goto one_char_escape;
- case '\t': c = 't'; goto one_char_escape;
- case '\f': c = 't'; goto one_char_escape;
- case '\0': c = '0'; goto one_char_escape;
- case '\'': goto one_char_escape;
- one_char_escape:
- *s++ = '\\';
- break;
- default:
- if (!isprint(c)) {
- *s++ = '\\';
- *s++ = (char)('0' + (0x3 & (((unsigned char)c) >> 6)));
- *s++ = (char)('0' + (0x7 & (((unsigned char)c) >> 3)));
- c = (char)('0' + (0x7 & ((unsigned char)c)));
- }
- }
- *s++ = c;
- *s++ = '\'';
- *s = '\0';
- assert(s + 1 <= quoted_buffer + MIN_QUOTED_CHAR_BUFFER);
- return quoted_buffer;
-}
-
-static void
-nl(struct gen_opt* opt)
-{
- putc('\n', opt->output);
-}
-
-static void
-indent(struct gen_opt* opt)
-{
- unsigned n = opt->indent_level;
- while (n != 0) {
- --n;
- fputs(" ", opt->output);
- }
-}
-
-static void
-line(struct gen_opt* opt, const char* format, ...)
-{
- va_list ap;
-
- indent(opt);
- va_start(ap, format);
- vfprintf(opt->output, format, ap);
- va_end(ap);
- nl(opt);
-}
-
-static void
-generate_letter_switch_r(struct gen_opt* opt,
- unsigned indexes[], unsigned nelem,
- unsigned columns[], unsigned unprocessed_columns)
-{
- char qbuf[MIN_QUOTED_CHAR_BUFFER];
-
- assert(nelem != 0);
- if (nelem == 1) {
- unsigned kw_index = indexes[0];
- const char* keyword = keyword_list[kw_index];
-
- if (unprocessed_columns == 0) {
- line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword);
- } else if (unprocessed_columns > opt->char_tail_test_threshold) {
- line(opt, "JSKW_TEST_GUESS(%u) /* %s */", kw_index, keyword);
- } else {
- unsigned i, column;
-
- indent(opt); p(opt, "if (");
- for (i = 0; i != unprocessed_columns; ++i) {
- column = columns[i];
- qchar(keyword[column], qbuf);
- p(opt, "%sJSKW_AT(%u)==%s", (i == 0) ? "" : " && ",
- column, qbuf);
- }
- p(opt, ") {"); nl(opt);
- ++opt->indent_level;
- line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword);
- --opt->indent_level;
- line(opt, "}");
- line(opt, "JSKW_NO_MATCH()");
- }
- } else {
- unsigned optimal_column_index, optimal_column;
- unsigned i;
- int use_if;
- char current;
-
- assert(unprocessed_columns != 0);
- optimal_column_index = find_optimal_switch_column(opt, indexes, nelem,
- columns,
- unprocessed_columns,
- &use_if);
- optimal_column = columns[optimal_column_index];
- columns[optimal_column_index] = columns[unprocessed_columns - 1];
-
- if (!use_if)
- line(opt, "switch (JSKW_AT(%u)) {", optimal_column);
-
- current = keyword_list[indexes[0]][optimal_column];
- for (i = 0; i != nelem;) {
- unsigned same_char_begin = i;
- char next = current;
-
- for (++i; i != nelem; ++i) {
- next = keyword_list[indexes[i]][optimal_column];
- if (next != current)
- break;
- }
- qchar(current, qbuf);
- if (use_if) {
- line(opt, "if (JSKW_AT(%u) == %s) {", optimal_column, qbuf);
- } else {
- line(opt, " case %s:", qbuf);
- }
- ++opt->indent_level;
- generate_letter_switch_r(opt, indexes + same_char_begin,
- i - same_char_begin,
- columns, unprocessed_columns - 1);
- --opt->indent_level;
- if (use_if) {
- line(opt, "}");
- }
- current = next;
- }
-
- if (!use_if) {
- line(opt, "}");
- }
-
- columns[optimal_column_index] = optimal_column;
-
- line(opt, "JSKW_NO_MATCH()");
- }
-}
-
-static void
-generate_letter_switch(struct gen_opt* opt,
- unsigned indexes[], unsigned nelem,
- unsigned current_length)
-{
- unsigned* columns;
- unsigned i;
-
- columns = (unsigned*) malloc(sizeof(columns[0]) * current_length);
- if (!columns) {
- perror("malloc");
- exit(EXIT_FAILURE);
- }
- for (i = 0; i != current_length; ++i) {
- columns[i] = i;
- }
- generate_letter_switch_r(opt, indexes, nelem, columns, current_length);
- free(columns);
-}
-
-
-static void
-generate_switch(struct gen_opt* opt)
-{
- unsigned* indexes;
- unsigned nlength;
- unsigned i, current;
- int use_if;
- unsigned nelem;
-
- nelem = sizeof(keyword_list)/sizeof(keyword_list[0]);
-
- line(opt, "/*");
- line(opt, " * Generating switch for the list of %u entries:", nelem);
- for (i = 0; i != nelem; ++i) {
- line(opt, " * %s", keyword_list[i]);
- }
- line(opt, " */");
-
- indexes = (unsigned*) malloc(sizeof(indexes[0]) * nelem);
- if (!indexes) {
- perror("malloc");
- exit(EXIT_FAILURE);
- }
- for (i = 0; i != nelem; ++i)
- indexes[i] = i;
- qsort(indexes, nelem, sizeof(indexes[i]), length_comparator);
- nlength = count_different_lengths(indexes, nelem);
-
- use_if = (nlength <= opt->use_if_threshold);
-
- if (!use_if)
- line(opt, "switch (JSKW_LENGTH()) {");
-
- current = (unsigned)strlen(keyword_list[indexes[0]]);
- for (i = 0; i != nelem;) {
- unsigned same_length_begin = i;
- unsigned next = current;
-
- for (++i; i != nelem; ++i) {
- next = (unsigned)strlen(keyword_list[indexes[i]]);
- if (next != current)
- break;
- }
- if (use_if) {
- line(opt, "if (JSKW_LENGTH() == %u) {", current);
- } else {
- line(opt, " case %u:", current);
- }
- ++opt->indent_level;
- generate_letter_switch(opt, indexes + same_length_begin,
- i - same_length_begin,
- current);
- --opt->indent_level;
- if (use_if) {
- line(opt, "}");
- }
- current = next;
- }
- if (!use_if)
- line(opt, "}");
- line(opt, "JSKW_NO_MATCH()");
- free(indexes);
-}
-
-int main(int argc, char** argv)
-{
- struct gen_opt opt;
-
- if (argc < 2) {
- opt.output = stdout;
- } else {
- opt.output = fopen(argv[1], "w");
- if (!opt.output) {
- perror("fopen");
- exit(EXIT_FAILURE);
- }
- }
- opt.indent_level = 1;
- opt.use_if_threshold = 3;
- opt.char_tail_test_threshold = 4;
-
- generate_switch(&opt);
-
- if (opt.output != stdout) {
- if (fclose(opt.output)) {
- perror("fclose");
- exit(EXIT_FAILURE);
- }
- }
-
- return EXIT_SUCCESS;
-}
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -604,25 +604,21 @@ if CONFIG['HAVE_LINUX_PERF_EVENT_H']:
]
if CONFIG['LINUX_HEADERS_INCLUDES']:
SOURCES['perf/pm_linux.cpp'].flags += [CONFIG['LINUX_HEADERS_INCLUDES']]
else:
SOURCES += [
'perf/pm_stub.cpp'
]
-HostSimplePrograms([
- 'host_jskwgen',
-])
-
GENERATED_FILES += ['jsautokw.h']
jsautokw = GENERATED_FILES['jsautokw.h']
jsautokw.script = 'jsautokw.py'
jsautokw.inputs += [
- '!host_jskwgen%s' % CONFIG['HOST_BIN_SUFFIX'],
+ 'vm/Keywords.h'
]
# JavaScript must be built shared, even for static builds, as it is used by
# other modules which are always built shared. Failure to do so results in
# the js code getting copied into xpinstall and jsd as well as mozilla-bin,
# and then the static data cells used for locking no longer work.
#
# In fact, we now build both a static and a shared library, as the
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4455,21 +4455,16 @@ NewGlobal(JSContext* cx, unsigned argc,
if (v.isBoolean())
creationOptions.setInvisibleToDebugger(v.toBoolean());
if (!JS_GetProperty(cx, opts, "cloneSingletons", &v))
return false;
if (v.isBoolean())
creationOptions.setCloneSingletons(v.toBoolean());
- if (!JS_GetProperty(cx, opts, "experimentalDateTimeFormatFormatToPartsEnabled", &v))
- return false;
- if (v.isBoolean())
- creationOptions.setExperimentalDateTimeFormatFormatToPartsEnabled(v.toBoolean());
-
if (!JS_GetProperty(cx, opts, "sameZoneAs", &v))
return false;
if (v.isObject())
creationOptions.setSameZoneAs(UncheckedUnwrap(&v.toObject()));
if (!JS_GetProperty(cx, opts, "disableLazyParsing", &v))
return false;
if (v.isBoolean())
--- a/js/src/tests/Intl/DateTimeFormat/formatToParts.js
+++ b/js/src/tests/Intl/DateTimeFormat/formatToParts.js
@@ -1,9 +1,9 @@
-// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.newGlobal||!newGlobal({experimentalDateTimeFormatFormatToPartsEnabled:true}).Intl.DateTimeFormat().formatToParts)
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// Tests the format function with a diverse set of locales and options.
// Always use UTC to avoid dependencies on test environment.
/*
* Return true if A is equal to B, where equality on arrays and objects
@@ -42,65 +42,59 @@ function deepEqual(a, b) {
function composeDate(parts) {
return parts.map(({value}) => value)
.reduce((string, part) => string + part);
}
var format;
var date = Date.UTC(2012, 11, 17, 3, 0, 42);
-// The experimental formatToParts method is only exposed if specifically
-// requested. Perform all tests using DateTimeFormat instances from a global
-// object with this method enabled.
-var DateTimeFormat =
- newGlobal({experimentalDateTimeFormatFormatToPartsEnabled:true}).Intl.DateTimeFormat;
-
// Locale en-US; default options.
-format = new DateTimeFormat("en-us", {timeZone: "UTC"});
+format = new Intl.DateTimeFormat("en-us", {timeZone: "UTC"});
assertEq(deepEqual(format.formatToParts(date), [
{ type: 'month', value: '12' },
{ type: 'literal', value: '/' },
{ type: 'day', value: '17' },
{ type: 'literal', value: '/' },
{ type: 'year', value: '2012' }
]), true);
// Just date
-format = new DateTimeFormat("en-us", {
+format = new Intl.DateTimeFormat("en-us", {
year: 'numeric',
month: 'numeric',
day: 'numeric',
timeZone: "UTC"});
assertEq(deepEqual(format.formatToParts(date), [
{ type: 'month', value: '12' },
{ type: 'literal', value: '/' },
{ type: 'day', value: '17' },
{ type: 'literal', value: '/' },
{ type: 'year', value: '2012' }
]), true);
assertEq(composeDate(format.formatToParts(date)), format.format(date));
// Just time in hour24
-format = new DateTimeFormat("en-us", {
+format = new Intl.DateTimeFormat("en-us", {
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
hour12: false,
timeZone: "UTC"});
assertEq(deepEqual(format.formatToParts(date), [
{ type: 'hour', value: '03' },
{ type: 'literal', value: ':' },
{ type: 'minute', value: '00' },
{ type: 'literal', value: ':' },
{ type: 'second', value: '42' }
]), true);
assertEq(composeDate(format.formatToParts(date)), format.format(date));
// Just time in hour12
-format = new DateTimeFormat("en-us", {
+format = new Intl.DateTimeFormat("en-us", {
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
hour12: true,
timeZone: "UTC"});
assertEq(deepEqual(format.formatToParts(date), [
{ type: 'hour', value: '3' },
{ type: 'literal', value: ':' },
@@ -108,47 +102,47 @@ assertEq(deepEqual(format.formatToParts(
{ type: 'literal', value: ':' },
{ type: 'second', value: '42' },
{ type: 'literal', value: ' ' },
{ type: 'dayPeriod', value: 'AM' }
]), true);
assertEq(composeDate(format.formatToParts(date)), format.format(date));
// Just month.
-format = new DateTimeFormat("en-us", {
+format = new Intl.DateTimeFormat("en-us", {
month: "narrow",
timeZone: "UTC"});
assertEq(deepEqual(format.formatToParts(date), [
{ type: 'month', value: 'D' }
]), true);
assertEq(composeDate(format.formatToParts(date)), format.format(date));
// Just weekday.
-format = new DateTimeFormat("en-us", {
+format = new Intl.DateTimeFormat("en-us", {
weekday: "narrow",
timeZone: "UTC"});
assertEq(deepEqual(format.formatToParts(date), [
{ type: 'weekday', value: 'M' }
]), true);
assertEq(composeDate(format.formatToParts(date)), format.format(date));
// Year and era.
-format = new DateTimeFormat("en-us", {
+format = new Intl.DateTimeFormat("en-us", {
year: "numeric",
era: "short",
timeZone: "UTC"});
assertEq(deepEqual(format.formatToParts(date), [
{ type: 'year', value: '2012' },
{ type: 'literal', value: ' ' },
{ type: 'era', value: 'AD' }
]), true);
assertEq(composeDate(format.formatToParts(date)), format.format(date));
// Time and date
-format = new DateTimeFormat("en-us", {
+format = new Intl.DateTimeFormat("en-us", {
weekday: 'long',
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
hour12: true,
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -110,17 +110,16 @@
macro(flags, flags, "flags") \
macro(float32, float32, "float32") \
macro(Float32x4, Float32x4, "Float32x4") \
macro(float64, float64, "float64") \
macro(Float64x2, Float64x2, "Float64x2") \
macro(forceInterpreter, forceInterpreter, "forceInterpreter") \
macro(forEach, forEach, "forEach") \
macro(format, format, "format") \
- macro(formatToParts, formatToParts, "formatToParts") \
macro(frame, frame, "frame") \
macro(from, from, "from") \
macro(fulfilled, fulfilled, "fulfilled") \
macro(fulfillHandler, fulfillHandler, "fulfillHandler") \
macro(futexNotEqual, futexNotEqual, "not-equal") \
macro(futexOK, futexOK, "ok") \
macro(futexTimedOut, futexTimedOut, "timed-out") \
macro(gcCycleNumber, gcCycleNumber, "gcCycleNumber") \
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -434,22 +434,16 @@ InitGlobalObjectOptions(JS::CompartmentO
bool shouldDiscardSystemSource = ShouldDiscardSystemSource();
bool extraWarningsForSystemJS = ExtraWarningsForSystemJS();
bool isSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
if (isSystem) {
// Make sure [SecureContext] APIs are visible:
aOptions.creationOptions().setSecureContext(true);
-
-#if 0 // TODO: Reenable in Bug 1288653
- // Enable the ECMA-402 experimental formatToParts in any chrome page
- aOptions.creationOptions()
- .setExperimentalDateTimeFormatFormatToPartsEnabled(true);
-#endif
}
if (shouldDiscardSystemSource) {
bool discardSource = isSystem;
aOptions.behaviors().setDiscardSource(discardSource);
}
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -115,21 +115,25 @@ RenderFrameParent::Init(nsFrameLoader* a
mAsyncPanZoomEnabled = lm && lm->AsyncPanZoomEnabled();
TabParent* browser = TabParent::GetFrom(mFrameLoader);
if (XRE_IsParentProcess()) {
// Our remote frame will push layers updates to the compositor,
// and we'll keep an indirect reference to that tree.
browser->Manager()->AsContentParent()->AllocateLayerTreeId(browser, &mLayersId);
if (lm && lm->AsClientLayerManager()) {
- lm->AsClientLayerManager()->GetRemoteRenderer()->SendNotifyChildCreated(mLayersId);
+ if (!lm->AsClientLayerManager()->GetRemoteRenderer()->SendNotifyChildCreated(mLayersId)) {
+ return false;
+ }
}
} else if (XRE_IsContentProcess()) {
ContentChild::GetSingleton()->SendAllocateLayerTreeId(browser->Manager()->ChildID(), browser->GetTabId(), &mLayersId);
- CompositorBridgeChild::Get()->SendNotifyChildCreated(mLayersId);
+ if (!CompositorBridgeChild::Get()->SendNotifyChildCreated(mLayersId)) {
+ return false;
+ }
}
mInitted = true;
return true;
}
bool
RenderFrameParent::IsInitted()
--- a/layout/reftests/w3c-css/submitted/masking/reftest.list
+++ b/layout/reftests/w3c-css/submitted/masking/reftest.list
@@ -81,26 +81,26 @@ fails == mask-origin-2.html mask-origin-
default-preferences pref(layout.css.clip-path-shapes.enabled,true)
fuzzy-if(winWidget,1,21) == clip-path-contentBox-1a.html clip-path-geometryBox-1-ref.html
fuzzy-if(winWidget,1,21) == clip-path-contentBox-1b.html clip-path-geometryBox-1-ref.html
fuzzy-if(winWidget,1,21) == clip-path-contentBox-1c.html clip-path-geometryBox-1-ref.html
fuzzy-if(winWidget,1,21) == clip-path-paddingBox-1a.html clip-path-geometryBox-1-ref.html
fuzzy-if(winWidget,1,21) == clip-path-paddingBox-1b.html clip-path-geometryBox-1-ref.html
fuzzy-if(winWidget,1,21) == clip-path-paddingBox-1c.html clip-path-geometryBox-1-ref.html
-fuzzy(64,311) == clip-path-borderBox-1a.html clip-path-geometryBox-1-ref.html
+fuzzy(64,370) == clip-path-borderBox-1a.html clip-path-geometryBox-1-ref.html
fuzzy-if(winWidget,1,21) == clip-path-borderBox-1b.html clip-path-geometryBox-1-ref.html
-fuzzy(64,311) == clip-path-borderBox-1c.html clip-path-geometryBox-1-ref.html
+fuzzy(64,370) == clip-path-borderBox-1c.html clip-path-geometryBox-1-ref.html
fuzzy-if(winWidget,1,21) == clip-path-marginBox-1a.html clip-path-geometryBox-1-ref.html
-fuzzy(64,311) == clip-path-fillBox-1a.html clip-path-geometryBox-1-ref.html
-fuzzy(64,311) == clip-path-strokeBox-1a.html clip-path-geometryBox-1-ref.html
-fuzzy(64,311) == clip-path-strokeBox-1b.html clip-path-geometryBox-1-ref.html
+fuzzy(64,370) == clip-path-fillBox-1a.html clip-path-geometryBox-1-ref.html
+fuzzy(64,370) == clip-path-strokeBox-1a.html clip-path-geometryBox-1-ref.html
+fuzzy(64,370) == clip-path-strokeBox-1b.html clip-path-geometryBox-1-ref.html
fuzzy-if(winWidget,1,21) == clip-path-viewBox-1a.html clip-path-geometryBox-1-ref.html
fuzzy-if(winWidget,1,21) == clip-path-viewBox-1b.html clip-path-geometryBox-1-ref.html
-fuzzy(64,311) == clip-path-viewBox-1c.html clip-path-geometryBox-1-ref.html
-fuzzy-if(winWidget,1,98) == clip-path-geometryBox-2.html clip-path-geometryBox-2-ref.html
+fuzzy(64,370) == clip-path-viewBox-1c.html clip-path-geometryBox-1-ref.html
+fuzzy-if(winWidget,9,98) == clip-path-geometryBox-2.html clip-path-geometryBox-2-ref.html
default-preferences
# mask with opacity test cases
fuzzy(1,5000) == mask-opacity-1a.html mask-opacity-1-ref.html
fuzzy(1,5000) == mask-opacity-1b.html mask-opacity-1-ref.html
fuzzy(1,5000) == mask-opacity-1c.html mask-opacity-1-ref.html
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -762,23 +762,17 @@ pref("gfx.font_rendering.directwrite.use
#endif
pref("gfx.font_rendering.opentype_svg.enabled", true);
#ifdef XP_WIN
// comma separated list of backends to use in order of preference
// e.g., pref("gfx.canvas.azure.backends", "direct2d,skia,cairo");
pref("gfx.canvas.azure.backends", "direct2d1.1,skia,cairo");
-
-#ifdef NIGHTLY_BUILD
-pref("gfx.content.azure.backends", "direct2d1.1,skia,cairo");
-#else
pref("gfx.content.azure.backends", "direct2d1.1,cairo");
-#endif
-
#else
#ifdef XP_MACOSX
pref("gfx.content.azure.backends", "skia");
pref("gfx.canvas.azure.backends", "skia");
// Accelerated cg canvas where available (10.7+)
pref("gfx.canvas.azure.accelerated", true);
#else
pref("gfx.canvas.azure.backends", "skia");
@@ -5083,16 +5077,20 @@ pref("dom.voicemail.enabled", false);
#endif
// Numeric default service id for Voice Mail API calls with |serviceId|
// parameter omitted.
pref("dom.voicemail.defaultServiceId", 0);
// Enable mapped array buffer by default.
pref("dom.mapped_arraybuffer.enabled", true);
+// Whether to send more than one "loading" readystatechange during XHRs to
+// simulate progress events for sites still not using modern progress events.
+pref("dom.fire_extra_xhr_loading_readystatechanges", false);
+
// The tables used for Safebrowsing phishing and malware checks.
pref("urlclassifier.malwareTable", "goog-malware-shavar,goog-unwanted-shavar,test-malware-simple,test-unwanted-simple");
#ifdef MOZILLA_OFFICIAL
// In the official build, we are allowed to use google's private
// phishing list "goog-phish-shavar". See Bug 1288840.
pref("urlclassifier.phishTable", "goog-phish-shavar,test-phish-simple");
#else
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -49,16 +49,18 @@ Atomic<PRThread*, Relaxed> gSocketThread
#define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
#define SOCKET_LIMIT_TARGET 1000U
#define SOCKET_LIMIT_MIN 50U
#define BLIP_INTERVAL_PREF "network.activity.blipIntervalMilliseconds"
#define MAX_TIME_BETWEEN_TWO_POLLS "network.sts.max_time_for_events_between_two_polls"
#define TELEMETRY_PREF "toolkit.telemetry.enabled"
#define MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN "network.sts.max_time_for_pr_close_during_shutdown"
+#define REPAIR_POLLABLE_EVENT_TIME 10
+
uint32_t nsSocketTransportService::gMaxCount;
PRCallOnceType nsSocketTransportService::gMaxCountInitOnce;
//-----------------------------------------------------------------------------
// ctor/dtor (called on the main/UI thread by the service manager)
nsSocketTransportService::nsSocketTransportService()
: mThread(nullptr)
@@ -80,16 +82,19 @@ nsSocketTransportService::nsSocketTransp
, mKeepaliveProbeCount(kDefaultTCPKeepCount)
, mKeepaliveEnabledPref(false)
, mServingPendingQueue(false)
, mMaxTimePerPollIter(100)
, mTelemetryEnabledPref(false)
, mMaxTimeForPrClosePref(PR_SecondsToInterval(5))
, mSleepPhase(false)
, mProbedMaxCount(false)
+#if defined(XP_WIN)
+ , mPolling(false)
+#endif
{
NS_ASSERTION(NS_IsMainThread(), "wrong thread");
PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
mActiveList = (SocketContext *)
moz_xmalloc(sizeof(SocketContext) * mActiveListSize);
mIdleList = (SocketContext *)
moz_xmalloc(sizeof(SocketContext) * mIdleListSize);
@@ -756,16 +761,23 @@ nsSocketTransportService::OnDispatchedEv
if (PR_GetCurrentThread() == gSocketThread) {
// this check is redundant to one done inside ::Signal(), but
// we can do it here and skip obtaining the lock - given that
// this is a relatively common occurance its worth the
// redundant code
SOCKET_LOG(("OnDispatchedEvent Same Thread Skip Signal\n"));
return NS_OK;
}
+#else
+ if (gIOService->IsNetTearingDown()) {
+ // Poll can hang sometimes. If we are in shutdown, we are going to
+ // start a watchdog. If we do not exit poll within
+ // REPAIR_POLLABLE_EVENT_TIME signal a pollable event again.
+ StartPollWatchdog();
+ }
#endif
MutexAutoLock lock(mLock);
if (mPollableEvent) {
mPollableEvent->Signal();
}
return NS_OK;
}
@@ -1063,17 +1075,23 @@ nsSocketTransportService::DoPollIteratio
#endif
// Measures seconds spent while blocked on PR_Poll
uint32_t pollInterval = 0;
int32_t n = 0;
*pollDuration = 0;
if (!gIOService->IsNetTearingDown()) {
// Let's not do polling during shutdown.
+#if defined(XP_WIN)
+ StartPolling();
+#endif
n = Poll(&pollInterval, pollDuration);
+#if defined(XP_WIN)
+ EndPolling();
+#endif
}
if (n < 0) {
SOCKET_LOG((" PR_Poll error [%d] os error [%d]\n", PR_GetError(),
PR_GetOSError()));
}
else {
//
@@ -1304,16 +1322,23 @@ nsSocketTransportService::Observe(nsISup
}
if (!strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
if (timer == mAfterWakeUpTimer) {
mAfterWakeUpTimer = nullptr;
mSleepPhase = false;
}
+
+#if defined(XP_WIN)
+ if (timer == mPollRepairTimer) {
+ DoPollRepair();
+ }
+#endif
+
} else if (!strcmp(topic, NS_WIDGET_SLEEP_OBSERVER_TOPIC)) {
mSleepPhase = true;
if (mAfterWakeUpTimer) {
mAfterWakeUpTimer->Cancel();
mAfterWakeUpTimer = nullptr;
}
} else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
if (mSleepPhase && !mAfterWakeUpTimer) {
@@ -1526,10 +1551,56 @@ nsSocketTransportService::GetSocketConne
{
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
for (uint32_t i = 0; i < mActiveCount; i++)
AnalyzeConnection(data, &mActiveList[i], true);
for (uint32_t i = 0; i < mIdleCount; i++)
AnalyzeConnection(data, &mIdleList[i], false);
}
+#if defined(XP_WIN)
+void
+nsSocketTransportService::StartPollWatchdog()
+{
+ MutexAutoLock lock(mLock);
+
+ // Poll can hang sometimes. If we are in shutdown, we are going to start a
+ // watchdog. If we do not exit poll within REPAIR_POLLABLE_EVENT_TIME
+ // signal a pollable event again.
+ MOZ_ASSERT(gIOService->IsNetTearingDown());
+ if (mPolling && !mPollRepairTimer) {
+ mPollRepairTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ mPollRepairTimer->Init(this, REPAIR_POLLABLE_EVENT_TIME,
+ nsITimer::TYPE_REPEATING_SLACK);
+ }
+}
+
+void
+nsSocketTransportService::DoPollRepair()
+{
+ MutexAutoLock lock(mLock);
+ if (mPolling && mPollableEvent) {
+ mPollableEvent->Signal();
+ } else if (mPollRepairTimer) {
+ mPollRepairTimer->Cancel();
+ }
+}
+
+void
+nsSocketTransportService::StartPolling()
+{
+ MutexAutoLock lock(mLock);
+ mPolling = true;
+}
+
+void
+nsSocketTransportService::EndPolling()
+{
+ MutexAutoLock lock(mLock);
+ mPolling = false;
+ if (mPollRepairTimer) {
+ mPollRepairTimer->Cancel();
+ }
+}
+#endif
+
} // namespace net
} // namespace mozilla
--- a/netwerk/base/nsSocketTransportService2.h
+++ b/netwerk/base/nsSocketTransportService2.h
@@ -255,16 +255,25 @@ private:
SocketContext *context, bool aActive);
void ClosePrivateConnections();
void DetachSocketWithGuard(bool aGuardLocals,
SocketContext *socketList,
int32_t index);
void MarkTheLastElementOfPendingQueue();
+
+#if defined(XP_WIN)
+ Atomic<bool> mPolling;
+ nsCOMPtr<nsITimer> mPollRepairTimer;
+ void StartPollWatchdog();
+ void DoPollRepair();
+ void StartPolling();
+ void EndPolling();
+#endif
};
extern nsSocketTransportService *gSocketTransportService;
extern Atomic<PRThread*, Relaxed> gSocketThread;
} // namespace net
} // namespace mozilla
--- a/netwerk/base/nsURLParsers.cpp
+++ b/netwerk/base/nsURLParsers.cpp
@@ -492,16 +492,22 @@ nsAuthURLParser::ParseAuthority(const ch
usernamePos, usernameLen,
passwordPos, passwordLen);
if (NS_FAILED(rv)) return rv;
rv = ParseServerInfo(p + 1, authLen - (p - auth + 1),
hostnamePos, hostnameLen,
port);
if (NS_FAILED(rv)) return rv;
OFFSET_RESULT(hostname, p + 1 - auth);
+
+ // malformed if has a username or password
+ // but no host info, such as: http://u:p@/
+ if ((usernamePos || passwordPos) && (!hostnamePos || !*hostnameLen)) {
+ return NS_ERROR_MALFORMED_URI;
+ }
}
else {
// auth = <server-info>
SET_RESULT(username, 0, -1);
SET_RESULT(password, 0, -1);
rv = ParseServerInfo(auth, authLen,
hostnamePos, hostnameLen,
port);
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -8,16 +8,20 @@
#include "HttpLog.h"
// Log on level :5, instead of default :4.
#undef LOG
#define LOG(args) LOG5(args)
#undef LOG_ENABLED
#define LOG_ENABLED() LOG5_ENABLED()
+#define TLS_EARLY_DATA_NOT_AVAILABLE 0
+#define TLS_EARLY_DATA_AVAILABLE_BUT_NOT_USED 1
+#define TLS_EARLY_DATA_AVAILABLE_AND_USED 2
+
#include "ASpdySession.h"
#include "mozilla/ChaosMode.h"
#include "mozilla/Telemetry.h"
#include "nsHttpConnection.h"
#include "nsHttpHandler.h"
#include "nsHttpPipeline.h"
#include "nsHttpRequestHead.h"
#include "nsHttpResponseHead.h"
@@ -77,16 +81,17 @@ nsHttpConnection::nsHttpConnection()
, mLastHttpResponseVersion(NS_HTTP_VERSION_1_1)
, mTransactionCaps(0)
, mResponseTimeoutEnabled(false)
, mTCPKeepaliveConfig(kTCPKeepaliveDisabled)
, mForceSendPending(false)
, m0RTTChecked(false)
, mWaitingFor0RTTResponse(false)
, mContentBytesWritten0RTT(0)
+ , mEarlyDataNegotiated(false)
{
LOG(("Creating nsHttpConnection @%p\n", this));
// the default timeout is for when this connection has not yet processed a
// transaction
static const PRIntervalTime k5Sec = PR_SecondsToInterval(5);
mIdleTimeout =
(k5Sec < gHttpHandler->IdleTimeout()) ? k5Sec : gHttpHandler->IdleTimeout();
@@ -336,29 +341,31 @@ nsHttpConnection::EnsureNPNComplete(nsre
if (rv == NS_ERROR_NOT_CONNECTED) {
rvEarlyAlpn = ssl->GetAlpnEarlySelection(earlyNegotiatedNPN);
}
}
if (NS_FAILED(rvEarlyAlpn)) {
LOG(("nsHttpConnection::EnsureNPNComplete %p - "
"early selected alpn not available", this));
+ mEarlyDataNegotiated = false;
} else {
LOG(("nsHttpConnection::EnsureNPNComplete %p -"
"early selected alpn: %s", this, earlyNegotiatedNPN.get()));
uint32_t infoIndex;
const SpdyInformation *info = gHttpHandler->SpdyInfo();
// We are doing 0RTT only with Http/1 right now!
if (NS_FAILED(info->GetNPNIndex(earlyNegotiatedNPN, &infoIndex))) {
// Check if early-data is allowed for this transaction.
if (mTransaction->Do0RTT()) {
LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - We "
"can do 0RTT!", this));
mWaitingFor0RTTResponse = true;
}
+ mEarlyDataNegotiated = true;
}
}
}
if (rv == NS_ERROR_NOT_CONNECTED) {
if (mWaitingFor0RTTResponse) {
aOut0RTTWriteHandshakeValue = mTransaction->ReadSegments(this,
nsIOService::gDefaultSegmentSize, &aOut0RTTBytesWritten);
@@ -381,28 +388,48 @@ nsHttpConnection::EnsureNPNComplete(nsre
if (NS_SUCCEEDED(rv)) {
LOG(("nsHttpConnection::EnsureNPNComplete %p [%s] negotiated to '%s'%s\n",
this, mConnInfo->HashKey().get(), negotiatedNPN.get(),
mTLSFilter ? " [Double Tunnel]" : ""));
bool ealyDataAccepted = false;
if (mWaitingFor0RTTResponse) {
- mWaitingFor0RTTResponse = false;
// Check if early data has been accepted.
rv = ssl->GetEarlyDataAccepted(&ealyDataAccepted);
LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - early data "
"that was sent during 0RTT %s been accepted.",
this, ealyDataAccepted ? "has" : "has not"));
+
if (NS_FAILED(rv) ||
NS_FAILED(mTransaction->Finish0RTT(!ealyDataAccepted))) {
mTransaction->Close(NS_ERROR_NET_RESET);
goto npnComplete;
}
}
+
+ int16_t tlsVersion;
+ ssl->GetSSLVersionUsed(&tlsVersion);
+ // Send the 0RTT telemetry only for tls1.3
+ if (tlsVersion > nsISSLSocketControl::TLS_VERSION_1_2) {
+ Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_NEGOTIATED,
+ (!mEarlyDataNegotiated) ? TLS_EARLY_DATA_NOT_AVAILABLE
+ : ((mWaitingFor0RTTResponse) ? TLS_EARLY_DATA_AVAILABLE_AND_USED
+ : TLS_EARLY_DATA_AVAILABLE_BUT_NOT_USED));
+ if (mWaitingFor0RTTResponse) {
+ Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_ACCEPTED,
+ ealyDataAccepted);
+ }
+ if (ealyDataAccepted) {
+ Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_BYTES_WRITTEN,
+ mContentBytesWritten0RTT);
+ }
+ }
+ mWaitingFor0RTTResponse = false;
+
if (!ealyDataAccepted) {
uint32_t infoIndex;
const SpdyInformation *info = gHttpHandler->SpdyInfo();
if (NS_SUCCEEDED(info->GetNPNIndex(negotiatedNPN, &infoIndex))) {
StartSpdy(info->Version[infoIndex]);
}
} else {
LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - %d bytes "
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -364,14 +364,15 @@ private:
// checked.
bool mWaitingFor0RTTResponse; // We have are
// sending 0RTT
// data and we
// are waiting
// for the end of
// the handsake.
int64_t mContentBytesWritten0RTT;
+ bool mEarlyDataNegotiated; //Only used for telemetry
};
} // namespace net
} // namespace mozilla
#endif // nsHttpConnection_h__
--- a/netwerk/test/unit/test_URIs.js
+++ b/netwerk/test/unit/test_URIs.js
@@ -112,24 +112,16 @@ var gTests = [
prePath: "ftp://foo:@ftp.mozilla.org:100",
port: 100,
username: "foo",
password: "",
path: "/pub/mozilla.org/README",
ref: "",
nsIURL: true, nsINestedURI: false },
//Bug 706249
- { spec: "http:x:@",
- scheme: "http",
- prePath: "http://x:@",
- username: "x",
- password: "",
- path: "",
- ref: "",
- nsIURL: true, nsINestedURI: false },
{ spec: "gopher://mozilla.org/",
scheme: "gopher",
prePath: "gopher:",
path: "//mozilla.org/",
ref: "",
nsIURL: false, nsINestedURI: false },
{ spec: "http://www.example.com/",
scheme: "http",
--- a/netwerk/test/unit/test_standardurl.js
+++ b/netwerk/test/unit/test_standardurl.js
@@ -332,16 +332,18 @@ add_test(function test_backslashReplacem
run_next_test();
});
add_test(function test_authority_host()
{
Assert.throws(() => { stringToURL("http:"); }, "TYPE_AUTHORITY should have host");
Assert.throws(() => { stringToURL("http:///"); }, "TYPE_AUTHORITY should have host");
+ Assert.throws(() => { stringToURL("http://u:p@/"); }, "User or password without host is not allowed");
+ Assert.throws(() => { stringToURL("http:@/"); }, "Must have a host");
run_next_test();
});
add_test(function test_trim_C0_and_space()
{
var url = stringToURL("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f http://example.com/ \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f ");
do_check_eq(url.spec, "http://example.com/");
--- a/security/manager/pki/resources/content/certManager.xul
+++ b/security/manager/pki/resources/content/certManager.xul
@@ -8,24 +8,24 @@
<?xul-overlay href="chrome://pippki/content/MineOverlay.xul"?>
<?xul-overlay href="chrome://pippki/content/OthersOverlay.xul"?>
<?xul-overlay href="chrome://pippki/content/WebSitesOverlay.xul"?>
<?xul-overlay href="chrome://pippki/content/CAOverlay.xul"?>
<?xul-overlay href="chrome://pippki/content/OrphanOverlay.xul"?>
<!DOCTYPE dialog SYSTEM "chrome://pippki/locale/certManager.dtd">
-<dialog id="certmanager"
+<dialog id="certmanager"
windowtype="mozilla:certmanager"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&certmgr.title;"
onload="LoadCerts();"
onunload="DeregisterSmartCardObservers();"
buttons="accept"
- style="width: 48em; height: 32em;"
+ style="width: 63em; height: 32em;"
persist="screenX screenY width height">
<stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
<script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
<script type="application/javascript" src="chrome://pippki/content/certManager.js"/>
<vbox flex="1">
--- a/taskcluster/taskgraph/transforms/tests/make_task_description.py
+++ b/taskcluster/taskgraph/transforms/tests/make_task_description.py
@@ -183,18 +183,24 @@ def docker_worker_setup(config, test, ta
'mount-point': '/home/worker/tooltool-cache',
})
taskdesc['scopes'].extend([
'docker-worker:relengapi-proxy:tooltool.download.internal',
'docker-worker:relengapi-proxy:tooltool.download.public',
])
# assemble the command line
+ command = [
+ '/home/worker/bin/run-task',
+ # The workspace cache/volume is default owned by root:root.
+ '--chown', '/home/worker/workspace',
+ '--',
+ '/home/worker/bin/test-linux.sh',
+ ]
- command = ["bash", "/home/worker/bin/test.sh"]
if mozharness.get('no-read-buildbot-config'):
command.append("--no-read-buildbot-config")
command.extend([
{"task-reference": "--installer-url=" + installer_url},
{"task-reference": "--test-packages-url=" + test_packages_url},
])
command.extend(mozharness.get('extra-options', []))
--- a/testing/docker/desktop-test/Dockerfile
+++ b/testing/docker/desktop-test/Dockerfile
@@ -37,17 +37,16 @@ ENV USER worker
ENV LOGNAME worker
ENV HOSTNAME taskcluster-worker
ENV LANG en_US.UTF-8
ENV LC_ALL en_US.UTF-8
# Add utilities and configuration
COPY dot-files/config /home/worker/.config
COPY dot-files/pulse /home/worker/.pulse
-COPY bin /home/worker/bin
RUN chmod +x bin/*
# TODO: remove this when buildbot is gone
COPY buildprops.json /home/worker/buildprops.json
COPY tc-vcs-config.yml /etc/taskcluster-vcs.yml
# TODO: remove
ADD https://raw.githubusercontent.com/taskcluster/buildbot-step/master/buildbot_step /home/worker/bin/buildbot_step
RUN chmod u+x /home/worker/bin/buildbot_step
deleted file mode 100644
--- a/testing/docker/desktop-test/bin/test.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#! /bin/bash -vex
-
-set -x -e
-
-: GECKO_HEAD_REPOSITORY ${GECKO_HEAD_REPOSITORY:=https://hg.mozilla.org/mozilla-central}
-: GECKO_HEAD_REV ${GECKO_HEAD_REV:=default}
-: WORKSPACE ${WORKSPACE:=/home/worker/workspace}
-
-
-# TODO: when bug 1093833 is solved and tasks can run as non-root, reduce this
-# to a simple fail-if-root check
-if [ $(id -u) = 0 ]; then
- chown -R worker:worker /home/worker
- # drop privileges by re-running this script
- exec sudo -E -u worker bash /home/worker/bin/test.sh "${@}"
-fi
-
-[ -d $WORKSPACE ] || mkdir -p $WORKSPACE
-cd $WORKSPACE
-exec /home/worker/bin/test-linux.sh "${@}"
deleted file mode 100644
--- a/testing/docker/desktop1604-test/bin/test.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#! /bin/bash -vex
-
-set -x -e
-
-: GECKO_HEAD_REPOSITORY ${GECKO_HEAD_REPOSITORY:=https://hg.mozilla.org/mozilla-central}
-: GECKO_HEAD_REV ${GECKO_HEAD_REV:=default}
-: WORKSPACE ${WORKSPACE:=/home/worker/workspace}
-
-
-# TODO: when bug 1093833 is solved and tasks can run as non-root, reduce this
-# to a simple fail-if-root check
-if [ $(id -u) = 0 ]; then
- chown -R worker:worker /home/worker
- # drop privileges by re-running this script
- exec sudo -E -u worker bash /home/worker/bin/test.sh "${@}"
-fi
-
-[ -d $WORKSPACE ] || mkdir -p $WORKSPACE
-cd $WORKSPACE
-exec /home/worker/bin/test-linux.sh "${@}"
--- a/testing/docker/recipes/run-task
+++ b/testing/docker/recipes/run-task
@@ -151,16 +151,23 @@ def main(args):
task_args = args[i + 1:]
except ValueError:
our_args = args
task_args = []
parser = argparse.ArgumentParser()
parser.add_argument('--user', default='worker', help='user to run as')
parser.add_argument('--group', default='worker', help='group to run as')
+ # We allow paths to be chowned by the --user:--group before permissions are
+ # dropped. This is often necessary for caches/volumes, since they default
+ # to root:root ownership.
+ parser.add_argument('--chown', action='append',
+ help='Directory to chown to --user:--group')
+ parser.add_argument('--chown-recursive', action='append',
+ help='Directory to recursively chown to --user:--group')
parser.add_argument('--vcs-checkout',
help='Directory where Gecko checkout should be created')
args = parser.parse_args(our_args)
try:
user = pwd.getpwnam(args.user)
except KeyError:
@@ -175,16 +182,33 @@ def main(args):
return 1
uid = user.pw_uid
gid = group.gr_gid
# Find all groups to which this user is a member.
gids = [g.gr_gid for g in grp.getgrall() if args.group in g.gr_mem]
+ # Change ownership of requested paths.
+ # FUTURE: parse argument values for user/group if we don't want to
+ # use --user/--group.
+ for path in args.chown or []:
+ print_line(b'chown', b'changing ownership of %s to %s:%s\n' % (
+ path, user.pw_name, group.gr_name))
+ os.chown(path, uid, gid)
+
+ for path in args.chown_recursive or []:
+ print_line(b'chown', b'recursively changing ownership of %s to %s:%s\n' %
+ (path, user.pw_name, group.gr_name))
+ for root, dirs, files in os.walk(path):
+ for d in dirs:
+ os.chown(os.path.join(root, d), uid, gid)
+ for f in files:
+ os.chown(os.path.join(root, f), uid, gid)
+
checkout = args.vcs_checkout
if checkout:
# Ensure the directory for the source checkout exists.
try:
os.makedirs(os.path.dirname(checkout))
except OSError as e:
if e.errno != errno.EEXIST:
raise
deleted file mode 100644
--- a/testing/web-platform/meta/XMLHttpRequest/event-readystatechange-loaded.htm.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[event-readystatechange-loaded.htm]
- type: testharness
- [XMLHttpRequest: the LOADING state change should only happen once]
- expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/XMLHttpRequest/open-url-bogus.htm.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[open-url-bogus.htm]
- type: testharness
- [XMLHttpRequest: open() - bogus URLs (http://u:p@/)]
- expected: FAIL
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002a.html.ini
@@ -0,0 +1,7 @@
+[dir-isolation-002a.html]
+ type: reftest
+ expected:
+ if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002b.html.ini
@@ -0,0 +1,8 @@
+[dir-isolation-002b.html]
+ type: reftest
+ expected:
+ if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002c.html.ini
@@ -0,0 +1,8 @@
+[dir-isolation-002c.html]
+ type: reftest
+ expected:
+ if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006a.html.ini
@@ -0,0 +1,7 @@
+[dir-isolation-006a.html]
+ type: reftest
+ expected:
+ if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006b.html.ini
@@ -0,0 +1,8 @@
+[dir-isolation-006b.html]
+ type: reftest
+ expected:
+ if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006c.html.ini
@@ -0,0 +1,8 @@
+[dir-isolation-006c.html]
+ type: reftest
+ expected:
+ if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009a.html.ini
@@ -0,0 +1,8 @@
+[dir-isolation-009a.html]
+ type: reftest
+ expected:
+ if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009b.html.ini
@@ -0,0 +1,8 @@
+[dir-isolation-009b.html]
+ type: reftest
+ expected:
+ if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009c.html.ini
@@ -0,0 +1,7 @@
+[dir-isolation-009c.html]
+ type: reftest
+ expected:
+ if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+ if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
--- a/testing/web-platform/meta/url/url-constructor.html.ini
+++ b/testing/web-platform/meta/url/url-constructor.html.ini
@@ -76,19 +76,16 @@
expected: FAIL
[Parsing: <http://example.com/foo/%2e> against <about:blank>]
expected: FAIL
[Parsing: <data:test# »> against <about:blank>]
expected: FAIL
- [Parsing: <http://user:pass@/> against <about:blank>]
- expected: FAIL
-
[Parsing: <httpa://foo:80/> against <about:blank>]
expected: FAIL
[Parsing: <gopher://foo:70/> against <about:blank>]
expected: FAIL
[Parsing: <gopher://foo:443/> against <about:blank>]
expected: FAIL
@@ -109,40 +106,16 @@
expected: FAIL
[Parsing: <http:/:b@www.example.com> against <about:blank>]
expected: FAIL
[Parsing: <http://:b@www.example.com> against <about:blank>]
expected: FAIL
- [Parsing: <http://user@/www.example.com> against <about:blank>]
- expected: FAIL
-
- [Parsing: <http:@/www.example.com> against <about:blank>]
- expected: FAIL
-
- [Parsing: <http:/@/www.example.com> against <about:blank>]
- expected: FAIL
-
- [Parsing: <http://@/www.example.com> against <about:blank>]
- expected: FAIL
-
- [Parsing: <https:@/www.example.com> against <about:blank>]
- expected: FAIL
-
- [Parsing: <http:a:b@/www.example.com> against <about:blank>]
- expected: FAIL
-
- [Parsing: <http:/a:b@/www.example.com> against <about:blank>]
- expected: FAIL
-
- [Parsing: <http://a:b@/www.example.com> against <about:blank>]
- expected: FAIL
-
[Parsing: <http://www.@pple.com> against <about:blank>]
expected: FAIL
[Parsing: <http://:@www.example.com> against <about:blank>]
expected: FAIL
[Parsing: <http://zyx.com> against <http://other.com/>]
expected: FAIL
--- a/toolkit/components/extensions/test/xpcshell/head.js
+++ b/toolkit/components/extensions/test/xpcshell/head.js
@@ -1,16 +1,17 @@
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-/* exported createHttpServer, promiseConsoleOutput */
+/* exported createHttpServer, promiseConsoleOutput, cleanupDir */
Components.utils.import("resource://gre/modules/Task.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Timer.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Extension",
"resource://gre/modules/Extension.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionData",
"resource://gre/modules/Extension.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
@@ -77,8 +78,33 @@ var promiseConsoleOutput = Task.async(fu
Services.console.logStringMessage(DONE);
yield awaitListener;
return {messages, result};
} finally {
Services.console.unregisterListener(listener);
}
});
+
+// Attempt to remove a directory. If the Windows OS is still using the
+// file sometimes remove() will fail. So try repeatedly until we can
+// remove it or we give up.
+function cleanupDir(dir) {
+ let count = 0;
+ return new Promise((resolve, reject) => {
+ function tryToRemoveDir() {
+ count += 1;
+ try {
+ dir.remove(true);
+ } catch (e) {
+ // ignore
+ }
+ if (!dir.exists()) {
+ return resolve();
+ }
+ if (count >= 25) {
+ return reject(`Failed to cleanup directory: ${dir}`);
+ }
+ setTimeout(tryToRemoveDir, 100);
+ }
+ tryToRemoveDir();
+ });
+}
--- a/toolkit/components/extensions/test/xpcshell/test_ext_downloads_search.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_downloads_search.js
@@ -90,18 +90,17 @@ add_task(function* test_search() {
}
Services.prefs.setIntPref("browser.download.folderList", 2);
Services.prefs.setComplexValue("browser.download.dir", nsIFile, downloadDir);
do_register_cleanup(() => {
Services.prefs.clearUserPref("browser.download.folderList");
Services.prefs.clearUserPref("browser.download.dir");
- downloadDir.remove(true);
- return clearDownloads();
+ return cleanupDir(downloadDir).then(clearDownloads);
});
yield clearDownloads().then(downloads => {
do_print(`removed ${downloads.length} pre-existing downloads from history`);
});
let extension = ExtensionTestUtils.loadExtension({
background: backgroundScript,
--- a/toolkit/components/passwordmgr/test/browser/browser.ini
+++ b/toolkit/components/passwordmgr/test/browser/browser.ini
@@ -10,17 +10,16 @@ support-files =
form_cross_origin_secure_action.html
head.js
insecure_test.html
insecure_test_subframe.html
multiple_forms.html
streamConverter_content.sjs
[browser_capture_doorhanger.js]
-skip-if = e10s # Bug 1277105
support-files =
subtst_notifications_1.html
subtst_notifications_2.html
subtst_notifications_2pw_0un.html
subtst_notifications_2pw_1un_1text.html
subtst_notifications_3.html
subtst_notifications_4.html
subtst_notifications_5.html
--- a/toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger.js
@@ -45,16 +45,18 @@ add_task(function* test_remember_opens()
add_task(function* test_clickNever() {
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
is(fieldValues.username, "notifyu1", "Checking submitted username");
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = getCaptureDoorhanger("password-save");
ok(notif, "got notification popup");
is(true, Services.logins.getLoginSavingEnabled("http://example.com"),
"Checking for login saving enabled");
+
+ yield* checkDoorhangerUsernamePassword("notifyu1", "notifyp1");
clickDoorhangerButton(notif, NEVER_BUTTON);
});
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
info("Make sure Never took effect");
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
is(fieldValues.username, "notifyu1", "Checking submitted username");
@@ -72,16 +74,18 @@ add_task(function* test_clickNever() {
add_task(function* test_clickRemember() {
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
is(fieldValues.username, "notifyu1", "Checking submitted username");
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = getCaptureDoorhanger("password-save");
ok(notif, "got notification popup");
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
+
+ yield* checkDoorhangerUsernamePassword("notifyu1", "notifyp1");
clickDoorhangerButton(notif, REMEMBER_BUTTON);
});
let logins = Services.logins.getAllLogins();
is(logins.length, 1, "Should only have 1 login");
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
is(login.username, "notifyu1", "Check the username used on the new entry");
is(login.password, "notifyp1", "Check the password used on the new entry");
@@ -266,16 +270,18 @@ add_task(function* test_changeUPLoginOnU
info("Check for change-password popup, u+p login on u+p form. (not changed)");
Services.logins.addLogin(login1);
yield testSubmittingLoginForm("subtst_notifications_8.html", function*(fieldValues) {
is(fieldValues.username, "notifyu1", "Checking submitted username");
is(fieldValues.password, "pass2", "Checking submitted password");
let notif = getCaptureDoorhanger("password-change");
ok(notif, "got notification popup");
+
+ yield* checkDoorhangerUsernamePassword("notifyu1", "pass2");
clickDoorhangerButton(notif, DONT_CHANGE_BUTTON);
});
let logins = Services.logins.getAllLogins();
is(logins.length, 1, "Should only have 1 login");
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
is(login.username, "notifyu1", "Check the username unchanged");
is(login.password, "notifyp1", "Check the password unchanged");
@@ -288,17 +294,20 @@ add_task(function* test_changeUPLoginOnU
info("Check for change-password popup, u+p login on u+p form.");
Services.logins.addLogin(login1);
yield testSubmittingLoginForm("subtst_notifications_8.html", function*(fieldValues) {
is(fieldValues.username, "notifyu1", "Checking submitted username");
is(fieldValues.password, "pass2", "Checking submitted password");
let notif = getCaptureDoorhanger("password-change");
ok(notif, "got notification popup");
+
+ yield* checkDoorhangerUsernamePassword("notifyu1", "pass2");
clickDoorhangerButton(notif, CHANGE_BUTTON);
+
ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
});
let logins = Services.logins.getAllLogins();
is(logins.length, 1, "Should only have 1 login");
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
is(login.username, "notifyu1", "Check the username unchanged");
is(login.password, "pass2", "Check the password changed");
@@ -316,17 +325,20 @@ add_task(function* test_changePLoginOnUP
info("Check for change-password popup, p-only login on u+p form.");
Services.logins.addLogin(login2);
yield testSubmittingLoginForm("subtst_notifications_9.html", function*(fieldValues) {
is(fieldValues.username, "", "Checking submitted username");
is(fieldValues.password, "pass2", "Checking submitted password");
let notif = getCaptureDoorhanger("password-change");
ok(notif, "got notification popup");
+
+ yield* checkDoorhangerUsernamePassword("", "pass2");
clickDoorhangerButton(notif, CHANGE_BUTTON);
+
ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
});
let logins = Services.logins.getAllLogins();
is(logins.length, 1, "Should only have 1 login");
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
is(login.username, "", "Check the username unchanged");
is(login.password, "pass2", "Check the password changed");
@@ -338,17 +350,20 @@ add_task(function* test_changePLoginOnUP
add_task(function* test_changePLoginOnPForm() {
info("Check for change-password popup, p-only login on p-only form.");
yield testSubmittingLoginForm("subtst_notifications_10.html", function*(fieldValues) {
is(fieldValues.username, "null", "Checking submitted username");
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = getCaptureDoorhanger("password-change");
ok(notif, "got notification popup");
+
+ yield* checkDoorhangerUsernamePassword("", "notifyp1");
clickDoorhangerButton(notif, CHANGE_BUTTON);
+
ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
});
let logins = Services.logins.getAllLogins();
is(logins.length, 1, "Should only have 1 login");
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
is(login.username, "", "Check the username unchanged");
is(login.password, "notifyp1", "Check the password changed");
@@ -485,17 +500,20 @@ add_task(function* test_changeUPLoginOnP
info("Check for change-password popup, u+p login on password update form.");
Services.logins.addLogin(login1);
yield testSubmittingLoginForm("subtst_notifications_change_p.html", function*(fieldValues) {
is(fieldValues.username, "null", "Checking submitted username");
is(fieldValues.password, "pass2", "Checking submitted password");
let notif = getCaptureDoorhanger("password-change");
ok(notif, "got notification popup");
+
+ yield* checkDoorhangerUsernamePassword("notifyu1", "pass2");
clickDoorhangerButton(notif, CHANGE_BUTTON);
+
ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
});
let logins = Services.logins.getAllLogins();
is(logins.length, 1, "Should only have 1 login");
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
is(login.username, "notifyu1", "Check the username unchanged");
is(login.password, "pass2", "Check the password changed");
@@ -517,17 +535,19 @@ add_task(function* test_recipeCaptureFie
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = getCaptureDoorhanger("password-save");
ok(notif, "got notification popup");
// Sanity check, no logins should exist yet.
let logins = Services.logins.getAllLogins();
is(logins.length, 0, "Should not have any logins yet");
+ yield* checkDoorhangerUsernamePassword("notifyu1", "notifyp1");
clickDoorhangerButton(notif, REMEMBER_BUTTON);
+
}, "http://example.org"); // The recipe is for example.org
let logins = Services.logins.getAllLogins();
is(logins.length, 1, "Should only have 1 login");
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
is(login.username, "notifyu1", "Check the username unchanged");
is(login.password, "notifyp1", "Check the password unchanged");
is(login.timesUsed, 1, "Check times used");
@@ -610,17 +630,20 @@ add_task(function* test_httpsUpgradeCapt
let logins = Services.logins.getAllLogins();
is(logins.length, 1, "Should have the HTTP login");
yield testSubmittingLoginForm("subtst_notifications_8.html", function*(fieldValues) {
is(fieldValues.username, "notifyu1", "Checking submitted username");
is(fieldValues.password, "pass2", "Checking submitted password");
let notif = getCaptureDoorhanger("password-change");
ok(notif, "checking for a change popup");
+
+ yield* checkDoorhangerUsernamePassword("notifyu1", "pass2");
clickDoorhangerButton(notif, CHANGE_BUTTON);
+
ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
}, "https://example.com"); // This is HTTPS whereas the saved login is HTTP
checkOnlyLoginWasUsedTwice({ justChanged: true });
logins = Services.logins.getAllLogins();
is(logins.length, 1, "Should only have 1 login still");
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
is(login.hostname, "https://example.com", "Check the hostname is upgraded");
@@ -638,16 +661,18 @@ add_task(function* test_httpsUpgradeCapt
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
is(fieldValues.username, "notifyu1", "Checking submitted username");
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = getCaptureDoorhanger("password-save");
ok(notif, "got notification popup");
is(Services.logins.getAllLogins().length, 1, "Should only have the HTTPS login");
+
+ yield* checkDoorhangerUsernamePassword("notifyu1", "notifyp1");
clickDoorhangerButton(notif, REMEMBER_BUTTON);
});
let logins = Services.logins.getAllLogins();
is(logins.length, 2, "Should have both HTTP and HTTPS logins");
for (let login of logins) {
login = login.QueryInterface(Ci.nsILoginMetaInfo);
is(login.username, "notifyu1", "Check the username used on the new entry");
--- a/toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger_window_open.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger_window_open.js
@@ -28,27 +28,16 @@ function withTestTabUntilStorageChange(a
ok(true, "loaded " + aPageFile);
info("running test case task");
yield* aTaskFn();
info("waiting for storage change");
yield storageChangedPromised;
});
}
-function* checkDoorhangerUsernamePassword(username, password) {
- yield BrowserTestUtils.waitForCondition(() => {
- return document.getElementById("password-notification-username").value == username;
- }, "Wait for nsLoginManagerPrompter writeDataToUI()");
- is(document.getElementById("password-notification-username").value, username,
- "Check doorhanger username");
- is(document.getElementById("password-notification-password").value, password,
- "Check doorhanger password");
-}
-
-
add_task(function* setup() {
yield SimpleTest.promiseFocus(window);
});
add_task(function* test_saveChromeHiddenAutoClose() {
let notifShownPromise = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
// query arguments are: username, password, features, auto-close (delimited by '|')
let url = "subtst_notifications_11.html?notifyu1|notifyp1|" +
--- a/toolkit/components/passwordmgr/test/browser/head.js
+++ b/toolkit/components/passwordmgr/test/browser/head.js
@@ -113,9 +113,25 @@ function clickDoorhangerButton(aPopup, a
ok(true, "Triggering main action");
notification.button.doCommand();
} else if (aButtonIndex <= aPopup.secondaryActions.length) {
ok(true, "Triggering secondary action " + aButtonIndex);
notification.childNodes[aButtonIndex].doCommand();
}
}
+/**
+ * Checks the doorhanger's username and password.
+ *
+ * @param {String} username The username.
+ * @param {String} password The password.
+ */
+function* checkDoorhangerUsernamePassword(username, password) {
+ yield BrowserTestUtils.waitForCondition(() => {
+ return document.getElementById("password-notification-username").value == username;
+ }, "Wait for nsLoginManagerPrompter writeDataToUI()");
+ is(document.getElementById("password-notification-username").value, username,
+ "Check doorhanger username");
+ is(document.getElementById("password-notification-password").value, password,
+ "Check doorhanger password");
+}
+
// End popup notification (doorhanger) functions //
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1716,16 +1716,40 @@
"alert_emails": ["rbarnes@mozilla.com"],
"bug_numbers": [1266571],
"expires_in_version": "52",
"kind": "enumerated",
"n_values": 8,
"releaseChannelCollection": "opt-out",
"description": "Recorded once for each HTTP 401 response. The value records the type of authentication and the TLS-enabled status. (0=basic/clear, 1=basic/tls, 2=digest/clear, 3=digest/tls, 4=ntlm/clear, 5=ntlm/tls, 6=negotiate/clear, 7=negotiate/tls)"
},
+ "TLS_EARLY_DATA_NEGOTIATED": {
+ "expires_in_version": "never",
+ "kind": "enumerated",
+ "n_values": 3,
+ "description": "TLS early data was available: 0 - not available, 1 - avaialable but not used, 2 - available and used.",
+ "alert_emails": ["necko@mozilla.com"],
+ "bug_numbers": [1296288]
+ },
+ "TLS_EARLY_DATA_ACCEPTED": {
+ "expires_in_version": "never",
+ "kind": "boolean",
+ "description": "TLS early data was used and it was accepted or rejected by the remote host.",
+ "alert_emails": ["necko@mozilla.com"],
+ "bug_numbers": [1296288]
+ },
+ "TLS_EARLY_DATA_BYTES_WRITTEN": {
+ "expires_in_version": "never",
+ "kind": "exponential",
+ "high": 60000,
+ "n_buckets": 100,
+ "description": "Amount of bytes sent using TLS early data.",
+ "alert_emails": ["necko@mozilla.com"],
+ "bug_numbers": [1296288]
+ },
"SSL_HANDSHAKE_VERSION": {
"alert_emails": ["seceng-telemetry@mozilla.com"],
"bug_numbers": [1250568],
"expires_in_version": "never",
"kind": "enumerated",
"n_values": 16,
"description": "SSL Version (1=tls1, 2=tls1.1, 3=tls1.2, 4=tls1.3)"
},
--- a/toolkit/components/thumbnails/test/browser_thumbnails_bg_no_cookies_stored.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_bg_no_cookies_stored.js
@@ -10,19 +10,20 @@ function* runTests() {
xhr: bgTestPageURL({ setRedCookie: true})
});
ok(!thumbnailExists(url), "Thumbnail file should not exist before capture.");
yield bgCapture(url);
ok(thumbnailExists(url), "Thumbnail file should exist after capture.");
removeThumbnail(url);
// now load it up in a browser - it should *not* be red, otherwise the
// cookie above was saved.
- let tab = gBrowser.loadOneTab(url, { inBackground: false });
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
let browser = tab.linkedBrowser;
- yield whenLoaded(browser);
// The root element of the page shouldn't be red.
- let redStr = "rgb(255, 0, 0)";
- isnot(browser.contentDocument.documentElement.style.backgroundColor,
- redStr,
- "The page shouldn't be red.");
+ yield ContentTask.spawn(browser, null, function() {
+ Assert.notEqual(content.document.documentElement.style.backgroundColor,
+ "rgb(255, 0, 0)",
+ "The page shouldn't be red.");
+ });
+
gBrowser.removeTab(tab);
}
--- a/widget/windows/IMMHandler.cpp
+++ b/widget/windows/IMMHandler.cpp
@@ -1465,23 +1465,24 @@ IMMHandler::HandleComposition(nsWindow*
MOZ_LOG(gIMMLog, LogLevel::Info,
("HandleComposition, GCS_COMPCLAUSE, clauseArrayLength=%ld but "
"clauseArrayLength2=%ld",
clauseArrayLength, clauseArrayLength2));
if (clauseArrayLength > clauseArrayLength2)
clauseArrayLength = clauseArrayLength2;
}
- if (useA_API) {
+ if (useA_API && clauseArrayLength > 0) {
// Convert each values of sIMECompClauseArray. The values mean offset of
// the clauses in ANSI string. But we need the values in Unicode string.
nsAutoCString compANSIStr;
if (ConvertToANSIString(mCompositionString, GetKeyboardCodePage(),
compANSIStr)) {
uint32_t maxlen = compANSIStr.Length();
+ mClauseArray.SetLength(clauseArrayLength);
mClauseArray[0] = 0; // first value must be 0
for (int32_t i = 1; i < clauseArrayLength; i++) {
uint32_t len = std::min(mClauseArray[i], maxlen);
mClauseArray[i] = ::MultiByteToWideChar(GetKeyboardCodePage(),
MB_PRECOMPOSED,
(LPCSTR)compANSIStr.get(),
len, nullptr, 0);
}
@@ -1989,16 +1990,22 @@ IMMHandler::DispatchCompositionChangeEve
MOZ_LOG(gIMMLog, LogLevel::Info,
("DispatchCompositionChangeEvent, mClauseArray[%ld]=%lu. "
"This is larger than mCompositionString.Length()=%lu",
i + 1, current, mCompositionString.Length()));
current = int32_t(mCompositionString.Length());
}
uint32_t length = current - lastOffset;
+ if (NS_WARN_IF(lastOffset >= mAttributeArray.Length())) {
+ MOZ_LOG(gIMMLog, LogLevel::Error,
+ ("DispatchCompositionChangeEvent, FAILED due to invalid data of "
+ "mClauseArray or mAttributeArray"));
+ return;
+ }
TextRangeType textRangeType =
PlatformToNSAttr(mAttributeArray[lastOffset]);
rv = dispatcher->AppendClauseToPendingComposition(length, textRangeType);
if (NS_WARN_IF(NS_FAILED(rv))) {
MOZ_LOG(gIMMLog, LogLevel::Error,
("DispatchCompositionChangeEvent, FAILED due to"
"TextEventDispatcher::AppendClauseToPendingComposition() failure"));
return;
--- a/xpcom/ds/nsExpirationTracker.h
+++ b/xpcom/ds/nsExpirationTracker.h
@@ -88,17 +88,17 @@ public:
, mInAgeOneGeneration(false)
, mName(aName)
{
static_assert(K >= 2 && K <= nsExpirationState::NOT_TRACKED,
"Unsupported number of generations (must be 2 <= K <= 15)");
mObserver = new ExpirationTrackerObserver();
mObserver->Init(this);
}
- ~nsExpirationTracker()
+ virtual ~nsExpirationTracker()
{
if (mTimer) {
mTimer->Cancel();
}
mObserver->Destroy();
}
/**
--- a/xpcom/tests/gtest/TestStateWatching.cpp
+++ b/xpcom/tests/gtest/TestStateWatching.cpp
@@ -1,15 +1,16 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "gtest/gtest.h"
+#include "mozilla/SharedThreadPool.h"
#include "mozilla/StateWatching.h"
#include "mozilla/TaskQueue.h"
#include "nsISupportsImpl.h"
#include "VideoUtils.h"
namespace TestStateWatching {
using namespace mozilla;
--- a/xpcom/threads/AbstractThread.cpp
+++ b/xpcom/threads/AbstractThread.cpp
@@ -65,17 +65,16 @@ public:
virtual bool IsCurrentThreadIn() override
{
// Compare NSPR threads so that this works after shutdown when
// NS_GetCurrentThread starts returning null.
PRThread* thread = nullptr;
mTarget->GetPRThread(&thread);
bool in = PR_GetCurrentThread() == thread;
- MOZ_ASSERT(in == (GetCurrent() == this));
return in;
}
void FireTailDispatcher()
{
MOZ_DIAGNOSTIC_ASSERT(mTailDispatcher.isSome());
mTailDispatcher.ref().DrainDirectTasks();
mTailDispatcher.reset();
--- a/xpcom/threads/SharedThreadPool.h
+++ b/xpcom/threads/SharedThreadPool.h
@@ -53,17 +53,20 @@ public:
NS_IMETHOD_(MozExternalRefCountType) Release(void) override;
// Forward behaviour to wrapped thread pool implementation.
NS_FORWARD_SAFE_NSITHREADPOOL(mPool);
// Call this when dispatching from an event on the same
// threadpool that is about to complete. We should not create a new thread
// in that case since a thread is about to become idle.
- nsresult TailDispatch(nsIRunnable *event) { return Dispatch(event, NS_DISPATCH_TAIL); }
+ nsresult DispatchFromEndOfTaskInThisPool(nsIRunnable *event)
+ {
+ return Dispatch(event, NS_DISPATCH_AT_END);
+ }
NS_IMETHOD DispatchFromScript(nsIRunnable *event, uint32_t flags) override {
return Dispatch(event, flags);
}
NS_IMETHOD Dispatch(already_AddRefed<nsIRunnable> event, uint32_t flags) override
{ return !mEventTarget ? NS_ERROR_NULL_POINTER : mEventTarget->Dispatch(Move(event), flags); }
--- a/xpcom/threads/TaskQueue.cpp
+++ b/xpcom/threads/TaskQueue.cpp
@@ -1,25 +1,76 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "mozilla/TaskQueue.h"
+#include "nsIEventTarget.h"
#include "nsThreadUtils.h"
-#include "mozilla/SharedThreadPool.h"
namespace mozilla {
-TaskQueue::TaskQueue(already_AddRefed<SharedThreadPool> aPool,
- bool aRequireTailDispatch)
+class TaskQueue::EventTargetWrapper final : public nsIEventTarget
+{
+ RefPtr<TaskQueue> mTaskQueue;
+
+ ~EventTargetWrapper()
+ {
+ }
+
+public:
+ explicit EventTargetWrapper(TaskQueue* aTaskQueue)
+ : mTaskQueue(aTaskQueue)
+ {
+ MOZ_ASSERT(mTaskQueue);
+ }
+
+ NS_IMETHOD
+ DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) override
+ {
+ nsCOMPtr<nsIRunnable> ref = aEvent;
+ return Dispatch(ref.forget(), aFlags);
+ }
+
+ NS_IMETHOD
+ Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags) override
+ {
+ nsCOMPtr<nsIRunnable> runnable = aEvent;
+ MonitorAutoLock mon(mTaskQueue->mQueueMonitor);
+ return mTaskQueue->DispatchLocked(/* passed by ref */runnable,
+ AbortIfFlushing,
+ DontAssertDispatchSuccess,
+ NormalDispatch);
+ }
+
+ NS_IMETHOD
+ DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t aFlags) override
+ {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ NS_IMETHOD
+ IsOnCurrentThread(bool* aResult) override
+ {
+ *aResult = mTaskQueue->IsCurrentThreadIn();
+ return NS_OK;
+ }
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+};
+
+NS_IMPL_ISUPPORTS(TaskQueue::EventTargetWrapper, nsIEventTarget)
+
+TaskQueue::TaskQueue(already_AddRefed<nsIEventTarget> aTarget,
+ bool aRequireTailDispatch)
: AbstractThread(aRequireTailDispatch)
- , mPool(aPool)
+ , mTarget(aTarget)
, mQueueMonitor("TaskQueue::Queue")
, mTailDispatcher(nullptr)
, mIsRunning(false)
, mIsShutdown(false)
, mIsFlushing(false)
{
MOZ_COUNT_CTOR(TaskQueue);
}
@@ -59,17 +110,17 @@ TaskQueue::DispatchLocked(nsCOMPtr<nsIRu
if (mIsShutdown) {
return NS_ERROR_FAILURE;
}
mTasks.push(aRunnable.forget());
if (mIsRunning) {
return NS_OK;
}
RefPtr<nsIRunnable> runner(new Runner(this));
- nsresult rv = mPool->Dispatch(runner.forget(), NS_DISPATCH_NORMAL);
+ nsresult rv = mTarget->Dispatch(runner.forget(), NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch runnable to run TaskQueue");
return rv;
}
mIsRunning = true;
return NS_OK;
}
@@ -131,24 +182,37 @@ TaskQueue::BeginShutdown()
bool
TaskQueue::IsEmpty()
{
MonitorAutoLock mon(mQueueMonitor);
return mTasks.empty();
}
+uint32_t
+TaskQueue::ImpreciseLengthForHeuristics()
+{
+ MonitorAutoLock mon(mQueueMonitor);
+ return mTasks.size();
+}
+
bool
TaskQueue::IsCurrentThreadIn()
{
bool in = NS_GetCurrentThread() == mRunningThread;
- MOZ_ASSERT(in == (GetCurrent() == this));
return in;
}
+already_AddRefed<nsIEventTarget>
+TaskQueue::WrapAsEventTarget()
+{
+ nsCOMPtr<nsIEventTarget> ref = new EventTargetWrapper(this);
+ return ref.forget();
+}
+
nsresult
TaskQueue::Runner::Run()
{
RefPtr<nsIRunnable> event;
{
MonitorAutoLock mon(mQueue->mQueueMonitor);
MOZ_ASSERT(mQueue->mIsRunning);
if (mQueue->mTasks.size() == 0) {
@@ -186,21 +250,21 @@ TaskQueue::Runner::Run()
mQueue->mIsRunning = false;
mQueue->MaybeResolveShutdown();
mon.NotifyAll();
return NS_OK;
}
}
// There's at least one more event that we can run. Dispatch this Runner
- // to the thread pool again to ensure it runs again. Note that we don't just
- // run in a loop here so that we don't hog the thread pool. This means we may
+ // to the target again to ensure it runs again. Note that we don't just
+ // run in a loop here so that we don't hog the target. This means we may
// run on another thread next time, but we rely on the memory fences from
// mQueueMonitor for thread safety of non-threadsafe tasks.
- nsresult rv = mQueue->mPool->TailDispatch(this);
+ nsresult rv = mQueue->mTarget->Dispatch(this, NS_DISPATCH_AT_END);
if (NS_FAILED(rv)) {
// Failed to dispatch, shutdown!
MonitorAutoLock mon(mQueue->mQueueMonitor);
mQueue->mIsRunning = false;
mQueue->mIsShutdown = true;
mQueue->MaybeResolveShutdown();
mon.NotifyAll();
}
--- a/xpcom/threads/TaskQueue.h
+++ b/xpcom/threads/TaskQueue.h
@@ -10,35 +10,53 @@
#include "mozilla/Monitor.h"
#include "mozilla/MozPromise.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TaskDispatcher.h"
#include "mozilla/Unused.h"
#include <queue>
-#include "mozilla/SharedThreadPool.h"
#include "nsThreadUtils.h"
+class nsIEventTarget;
class nsIRunnable;
namespace mozilla {
-class SharedThreadPool;
-
typedef MozPromise<bool, bool, false> ShutdownPromise;
-// Abstracts executing runnables in order in a thread pool. The runnables
-// dispatched to the TaskQueue will be executed in the order in which
+// Abstracts executing runnables in order on an arbitrary event target. The
+// runnables dispatched to the TaskQueue will be executed in the order in which
// they're received, and are guaranteed to not be executed concurrently.
// They may be executed on different threads, and a memory barrier is used
// to make this threadsafe for objects that aren't already threadsafe.
-class TaskQueue : public AbstractThread {
+//
+// Note, since a TaskQueue can also be converted to an nsIEventTarget using
+// WrapAsEventTarget() its possible to construct a hierarchy of TaskQueues.
+// Consider these three TaskQueues:
+//
+// TQ1 dispatches to the main thread
+// TQ2 dispatches to TQ1
+// TQ3 dispatches to TQ1
+//
+// This ensures there is only ever a single runnable from the entire chain on
+// the main thread. It also ensures that TQ2 and TQ3 only have a single runnable
+// in TQ1 at any time.
+//
+// This arrangement lets you prioritize work by dispatching runnables directly
+// to TQ1. You can issue many runnables for important work. Meanwhile the TQ2
+// and TQ3 work will always execute at most one runnable and then yield.
+class TaskQueue : public AbstractThread
+{
+ class EventTargetWrapper;
+
public:
- explicit TaskQueue(already_AddRefed<SharedThreadPool> aPool, bool aSupportsTailDispatch = false);
+ explicit TaskQueue(already_AddRefed<nsIEventTarget> aTarget,
+ bool aSupportsTailDispatch = false);
TaskDispatcher& TailDispatcher() override;
TaskQueue* AsTaskQueue() override { return this; }
void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
DispatchFailureHandling aFailureHandling = AssertDispatchSuccess,
DispatchReason aReason = NormalDispatch) override
@@ -54,34 +72,39 @@ public:
// dispatch failure, it will be deleted here outside the lock. We do so
// since the destructor of the runnable might access TaskQueue and result
// in deadlocks.
}
// Puts the queue in a shutdown state and returns immediately. The queue will
// remain alive at least until all the events are drained, because the Runners
// hold a strong reference to the task queue, and one of them is always held
- // by the threadpool event queue when the task queue is non-empty.
+ // by the target event queue when the task queue is non-empty.
//
// The returned promise is resolved when the queue goes empty.
RefPtr<ShutdownPromise> BeginShutdown();
// Blocks until all task finish executing.
void AwaitIdle();
// Blocks until the queue is flagged for shutdown and all tasks have finished
// executing.
void AwaitShutdownAndIdle();
bool IsEmpty();
+ uint32_t ImpreciseLengthForHeuristics();
// Returns true if the current thread is currently running a Runnable in
// the task queue.
bool IsCurrentThreadIn() override;
+ // Create a new nsIEventTarget wrapper object that dispatches to this
+ // TaskQueue.
+ already_AddRefed<nsIEventTarget> WrapAsEventTarget();
+
protected:
virtual ~TaskQueue();
// Blocks until all task finish executing. Called internally by methods
// that need to wait until the task queue is idle.
// mQueueMonitor must be held.
void AwaitIdleLocked();
@@ -93,21 +116,21 @@ protected:
DispatchFailureHandling aFailureHandling,
DispatchReason aReason = NormalDispatch);
void MaybeResolveShutdown()
{
mQueueMonitor.AssertCurrentThreadOwns();
if (mIsShutdown && !mIsRunning) {
mShutdownPromise.ResolveIfExists(true, __func__);
- mPool = nullptr;
+ mTarget = nullptr;
}
}
- RefPtr<SharedThreadPool> mPool;
+ nsCOMPtr<nsIEventTarget> mTarget;
// Monitor that protects the queue and mIsRunning;
Monitor mQueueMonitor;
// Queue of tasks to run.
std::queue<nsCOMPtr<nsIRunnable>> mTasks;
// The thread currently running the task queue. We store a reference
@@ -121,47 +144,49 @@ protected:
Atomic<nsIThread*> mRunningThread;
// RAII class that gets instantiated for each dispatched task.
class AutoTaskGuard : public AutoTaskDispatcher
{
public:
explicit AutoTaskGuard(TaskQueue* aQueue)
: AutoTaskDispatcher(/* aIsTailDispatcher = */ true), mQueue(aQueue)
+ , mLastCurrentThread(nullptr)
{
// NB: We don't hold the lock to aQueue here. Don't do anything that
// might require it.
MOZ_ASSERT(!mQueue->mTailDispatcher);
mQueue->mTailDispatcher = this;
- MOZ_ASSERT(sCurrentThreadTLS.get() == nullptr);
+ mLastCurrentThread = sCurrentThreadTLS.get();
sCurrentThreadTLS.set(aQueue);
MOZ_ASSERT(mQueue->mRunningThread == nullptr);
mQueue->mRunningThread = NS_GetCurrentThread();
}
~AutoTaskGuard()
{
DrainDirectTasks();
MOZ_ASSERT(mQueue->mRunningThread == NS_GetCurrentThread());
mQueue->mRunningThread = nullptr;
- sCurrentThreadTLS.set(nullptr);
+ sCurrentThreadTLS.set(mLastCurrentThread);
mQueue->mTailDispatcher = nullptr;
}
private:
TaskQueue* mQueue;
+ AbstractThread* mLastCurrentThread;
};
TaskDispatcher* mTailDispatcher;
- // True if we've dispatched an event to the pool to execute events from
+ // True if we've dispatched an event to the target to execute events from
// the queue.
bool mIsRunning;
// True if we've started our shutdown process.
bool mIsShutdown;
MozPromiseHolder<ShutdownPromise> mShutdownPromise;
// True if we're flushing; we reject new tasks if we're flushing.
--- a/xpcom/threads/nsIEventTarget.idl
+++ b/xpcom/threads/nsIEventTarget.idl
@@ -45,17 +45,17 @@ interface nsIEventTarget : nsISupports
* that was dispatched to the same event target, and that event is about to
* finish.
*
* A thread pool can use this as an optimization hint to not spin up
* another thread, since the current thread is about to become idle.
*
* These events are always async.
*/
- const unsigned long DISPATCH_TAIL = 2;
+ const unsigned long DISPATCH_AT_END = 2;
/**
* Check to see if this event target is associated with the current thread.
*
* @returns
* A boolean value that if "true" indicates that events dispatched to this
* event target will run on the current thread (i.e., the thread calling
* this method).
@@ -118,10 +118,10 @@ interface nsIEventTarget : nsISupports
*/
[noscript] void delayedDispatch(in alreadyAddRefed_nsIRunnable event, in unsigned long delay);
};
%{C++
// convenient aliases:
#define NS_DISPATCH_NORMAL nsIEventTarget::DISPATCH_NORMAL
#define NS_DISPATCH_SYNC nsIEventTarget::DISPATCH_SYNC
-#define NS_DISPATCH_TAIL nsIEventTarget::DISPATCH_TAIL
+#define NS_DISPATCH_AT_END nsIEventTarget::DISPATCH_AT_END
%}
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -743,17 +743,18 @@ nsThread::DispatchInternal(already_AddRe
// Allows waiting; ensure no locks are held that would deadlock us!
while (wrapper->IsPending()) {
NS_ProcessNextEvent(thread, true);
}
return NS_OK;
}
- NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
+ NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL ||
+ aFlags == NS_DISPATCH_AT_END, "unexpected dispatch flags");
return PutEvent(event.take(), aTarget);
}
//-----------------------------------------------------------------------------
// nsIEventTarget
NS_IMETHODIMP
nsThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
--- a/xpcom/threads/nsThreadPool.cpp
+++ b/xpcom/threads/nsThreadPool.cpp
@@ -81,17 +81,17 @@ nsThreadPool::PutEvent(already_AddRefed<
return NS_ERROR_NOT_AVAILABLE;
}
LOG(("THRD-P(%p) put [%d %d %d]\n", this, mIdleCount, mThreads.Count(),
mThreadLimit));
MOZ_ASSERT(mIdleCount <= (uint32_t)mThreads.Count(), "oops");
// Make sure we have a thread to service this event.
if (mThreads.Count() < (int32_t)mThreadLimit &&
- !(aFlags & NS_DISPATCH_TAIL) &&
+ !(aFlags & NS_DISPATCH_AT_END) &&
// Spawn a new thread if we don't have enough idle threads to serve
// pending events immediately.
mEvents.Count(lock) >= mIdleCount) {
spawnThread = true;
}
mEvents.PutEvent(Move(aEvent), lock);
stackSize = mStackSize;
@@ -265,17 +265,17 @@ nsThreadPool::Dispatch(already_AddRefed<
new nsThreadSyncDispatch(thread, Move(aEvent));
PutEvent(wrapper);
while (wrapper->IsPending()) {
NS_ProcessNextEvent(thread);
}
} else {
NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL ||
- aFlags == NS_DISPATCH_TAIL, "unexpected dispatch flags");
+ aFlags == NS_DISPATCH_AT_END, "unexpected dispatch flags");
PutEvent(Move(aEvent), aFlags);
}
return NS_OK;
}
NS_IMETHODIMP
nsThreadPool::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
{