diff --git a/accessible/base/nsAccCache.h b/accessible/base/nsAccCache.h
index 4e97dbf2c4f4..cb39accdee11 100644
--- a/accessible/base/nsAccCache.h
+++ b/accessible/base/nsAccCache.h
@@ -25,21 +25,4 @@ UnbindCacheEntriesFromDocument(
}
}
-/**
- * Clear the cache and shutdown the accessibles.
- */
-template
-static void
-ClearCache(nsRefPtrHashtable, T>& aCache)
-{
- for (auto iter = aCache.Iter(); !iter.Done(); iter.Next()) {
- T* accessible = iter.Data();
- MOZ_ASSERT(accessible);
- if (accessible && !accessible->IsDefunct()) {
- accessible->Shutdown();
- }
- iter.Remove();
- }
-}
-
#endif
diff --git a/accessible/generic/DocAccessible.cpp b/accessible/generic/DocAccessible.cpp
index e80cd9bb2452..2862bac83cc1 100644
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -483,7 +483,17 @@ DocAccessible::Shutdown()
mDependentIDsHash.Clear();
mNodeToAccessibleMap.Clear();
- ClearCache(mAccessibleCache);
+
+ for (auto iter = mAccessibleCache.Iter(); !iter.Done(); iter.Next()) {
+ Accessible* accessible = iter.Data();
+ MOZ_ASSERT(accessible);
+ if (accessible && !accessible->IsDefunct()) {
+ // Unlink parent to avoid its cleaning overhead in shutdown.
+ accessible->mParent = nullptr;
+ accessible->Shutdown();
+ }
+ iter.Remove();
+ }
HyperTextAccessibleWrap::Shutdown();
diff --git a/browser/app/moz.build b/browser/app/moz.build
index d43092b4f18b..cbf111027c5b 100644
--- a/browser/app/moz.build
+++ b/browser/app/moz.build
@@ -29,10 +29,6 @@ LOCAL_INCLUDES += [
'/xpcom/build',
]
-USE_LIBS += [
- 'mozglue',
-]
-
if CONFIG['LIBFUZZER']:
USE_LIBS += [ 'fuzzer' ]
LOCAL_INCLUDES += [
diff --git a/browser/base/content/browser-feeds.js b/browser/base/content/browser-feeds.js
index 10c0c14bb9e2..3e95db0f130d 100644
--- a/browser/base/content/browser-feeds.js
+++ b/browser/base/content/browser-feeds.js
@@ -3,11 +3,136 @@
* 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/. */
+XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
+ "resource://gre/modules/DeferredTask.jsm");
+
+const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
+const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
+const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
+
+const PREF_SHOW_FIRST_RUN_UI = "browser.feeds.showFirstRunUI";
+
+const PREF_SELECTED_APP = "browser.feeds.handlers.application";
+const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
+const PREF_SELECTED_ACTION = "browser.feeds.handler";
+const PREF_SELECTED_READER = "browser.feeds.handler.default";
+
+const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
+const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
+const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
+const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";
+
+const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
+const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
+const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
+const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";
+
+const PREF_UPDATE_DELAY = 2000;
+
+const SETTABLE_PREFS = new Set([
+ PREF_VIDEO_SELECTED_ACTION,
+ PREF_AUDIO_SELECTED_ACTION,
+ PREF_SELECTED_ACTION,
+ PREF_VIDEO_SELECTED_READER,
+ PREF_AUDIO_SELECTED_READER,
+ PREF_SELECTED_READER,
+ PREF_VIDEO_SELECTED_WEB,
+ PREF_AUDIO_SELECTED_WEB,
+ PREF_SELECTED_WEB
+]);
+
+const EXECUTABLE_PREFS = new Set([
+ PREF_SELECTED_APP,
+ PREF_VIDEO_SELECTED_APP,
+ PREF_AUDIO_SELECTED_APP
+]);
+
+const VALID_ACTIONS = new Set(["ask", "reader", "bookmarks"]);
+const VALID_READERS = new Set(["web", "client", "default", "bookmarks"]);
+
+XPCOMUtils.defineLazyPreferenceGetter(this, "SHOULD_LOG",
+ "feeds.log", false);
+
+function LOG(str) {
+ if (SHOULD_LOG)
+ dump("*** Feeds: " + str + "\n");
+}
+
+function getPrefActionForType(t) {
+ switch (t) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ return PREF_VIDEO_SELECTED_ACTION;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ return PREF_AUDIO_SELECTED_ACTION;
+
+ default:
+ return PREF_SELECTED_ACTION;
+ }
+}
+
+function getPrefReaderForType(t) {
+ switch (t) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ return PREF_VIDEO_SELECTED_READER;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ return PREF_AUDIO_SELECTED_READER;
+
+ default:
+ return PREF_SELECTED_READER;
+ }
+}
+
+function getPrefWebForType(t) {
+ switch (t) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ return PREF_VIDEO_SELECTED_WEB;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ return PREF_AUDIO_SELECTED_WEB;
+
+ default:
+ return PREF_SELECTED_WEB;
+ }
+}
+
+function getPrefAppForType(t) {
+ switch (t) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ return PREF_VIDEO_SELECTED_APP;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ return PREF_AUDIO_SELECTED_APP;
+
+ default:
+ return PREF_SELECTED_APP;
+ }
+}
+
+/**
+ * Maps a feed type to a maybe-feed mimetype.
+ */
+function getMimeTypeForFeedType(aFeedType) {
+ switch (aFeedType) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ return TYPE_MAYBE_VIDEO_FEED;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ return TYPE_MAYBE_AUDIO_FEED;
+
+ default:
+ return TYPE_MAYBE_FEED;
+ }
+}
+
/**
* The Feed Handler object manages discovery of RSS/ATOM feeds in web pages
* and shows UI when they are discovered.
*/
var FeedHandler = {
+ _prefChangeCallback: null,
+
/** Called when the user clicks on the Subscribe to This Page... menu item,
* or when the user clicks the feed button when the page contains multiple
* feeds.
@@ -195,7 +320,8 @@ var FeedHandler = {
return file.leafName;
},
- chooseClientApp(aTitle, aPrefName, aBrowser) {
+ _chooseClientApp(aTitle, aTypeName, aBrowser) {
+ const prefName = getPrefAppForType(aTypeName);
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
fp.init(window, aTitle, Ci.nsIFilePicker.modeOpen);
@@ -222,7 +348,7 @@ var FeedHandler = {
}
if (fp.file.leafName != appName) {
- Services.prefs.setComplexValue(aPrefName, Ci.nsILocalFile, selectedApp);
+ Services.prefs.setComplexValue(prefName, Ci.nsILocalFile, selectedApp);
aBrowser.messageManager.sendAsyncMessage("FeedWriter:SetApplicationLauncherMenuItem",
{ name: this._getFileDisplayName(selectedApp),
type: "SelectedAppMenuItem" });
@@ -277,70 +403,240 @@ var FeedHandler = {
}
},
+ // nsISupports
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+ Ci.nsISupportsWeakReference]),
+
+
init() {
window.messageManager.addMessageListener("FeedWriter:ChooseClientApp", this);
- window.messageManager.addMessageListener("FeedWriter:RequestClientAppName", this);
- window.messageManager.addMessageListener("FeedWriter:SetFeedCharPref", this);
- window.messageManager.addMessageListener("FeedWriter:SetFeedComplexString", this);
+ window.messageManager.addMessageListener("FeedWriter:GetSubscriptionUI", this);
+ window.messageManager.addMessageListener("FeedWriter:SetFeedPrefsAndSubscribe", this);
window.messageManager.addMessageListener("FeedWriter:ShownFirstRun", this);
Services.ppmm.addMessageListener("FeedConverter:ExecuteClientApp", this);
+
+ const prefs = Services.prefs;
+ prefs.addObserver(PREF_SELECTED_ACTION, this, true);
+ prefs.addObserver(PREF_SELECTED_READER, this, true);
+ prefs.addObserver(PREF_SELECTED_WEB, this, true);
+ prefs.addObserver(PREF_VIDEO_SELECTED_ACTION, this, true);
+ prefs.addObserver(PREF_VIDEO_SELECTED_READER, this, true);
+ prefs.addObserver(PREF_VIDEO_SELECTED_WEB, this, true);
+ prefs.addObserver(PREF_AUDIO_SELECTED_ACTION, this, true);
+ prefs.addObserver(PREF_AUDIO_SELECTED_READER, this, true);
+ prefs.addObserver(PREF_AUDIO_SELECTED_WEB, this, true);
},
uninit() {
Services.ppmm.removeMessageListener("FeedConverter:ExecuteClientApp", this);
+
+ this._prefChangeCallback = null;
+ },
+
+ // nsIObserver
+ observe(subject, topic, data) {
+ if (topic == "nsPref:changed") {
+ LOG(`Pref changed ${data}`)
+ if (this._prefChangeCallback) {
+ this._prefChangeCallback.disarm();
+ }
+ // Multiple prefs are set at the same time, debounce to reduce noise
+ // This can happen in one feed and we want to message all feed pages
+ this._prefChangeCallback = new DeferredTask(() => {
+ this._prefChanged(data);
+ }, PREF_UPDATE_DELAY);
+ this._prefChangeCallback.arm();
+ }
+ },
+
+ _prefChanged(prefName) {
+ // Don't observe for PREF_*SELECTED_APP as user likely just picked one
+ // That is also handled by SetApplicationLauncherMenuItem call
+ // Rather than the others which happen on subscription
+ switch (prefName) {
+ case PREF_SELECTED_READER:
+ case PREF_SELECTED_WEB:
+ case PREF_VIDEO_SELECTED_READER:
+ case PREF_VIDEO_SELECTED_WEB:
+ case PREF_AUDIO_SELECTED_READER:
+ case PREF_AUDIO_SELECTED_WEB:
+ case PREF_SELECTED_ACTION:
+ case PREF_VIDEO_SELECTED_ACTION:
+ case PREF_AUDIO_SELECTED_ACTION:
+ const response = {
+ default: this._getReaderForType(Ci.nsIFeed.TYPE_FEED),
+ [Ci.nsIFeed.TYPE_AUDIO]: this._getReaderForType(Ci.nsIFeed.TYPE_AUDIO),
+ [Ci.nsIFeed.TYPE_VIDEO]: this._getReaderForType(Ci.nsIFeed.TYPE_VIDEO)
+ };
+ Services.mm.broadcastAsyncMessage("FeedWriter:PreferenceUpdated",
+ response);
+ break;
+ }
+ },
+
+ _initSubscriptionUIResponse(feedType) {
+ const wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
+ getService(Ci.nsIWebContentConverterService);
+ const handlersRaw = wccr.getContentHandlers(getMimeTypeForFeedType(feedType));
+ const handlers = [];
+ for (let handler of handlersRaw) {
+ LOG(`Handler found: ${handler}`);
+ handlers.push({
+ name: handler.name,
+ uri: handler.uri
+ });
+ }
+ let showFirstRunUI = true;
+ // eslint-disable-next-line mozilla/use-default-preference-values
+ try {
+ showFirstRunUI = Services.prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI);
+ } catch (ex) { }
+ const response = { handlers, showFirstRunUI };
+ let selectedClientApp;
+ const feedTypePref = getPrefAppForType(feedType);
+ try {
+ selectedClientApp = Services.prefs.getComplexValue(feedTypePref, Ci.nsILocalFile);
+ } catch (ex) {
+ // Just do nothing, then we won't bother populating
+ }
+
+ let defaultClientApp = null;
+ try {
+ // This can sometimes not exist
+ defaultClientApp = Cc["@mozilla.org/browser/shell-service;1"]
+ .getService(Ci.nsIShellService)
+ .defaultFeedReader;
+ } catch (ex) {
+ // Just do nothing, then we don't bother populating
+ }
+
+ if (selectedClientApp && selectedClientApp.exists()) {
+ if (defaultClientApp && selectedClientApp.path != defaultClientApp.path) {
+ // Only set the default menu item if it differs from the selected one
+ response.defaultMenuItem = this._getFileDisplayName(defaultClientApp);
+ }
+ response.selectedMenuItem = this._getFileDisplayName(selectedClientApp);
+ }
+ response.reader = this._getReaderForType(feedType);
+ return response;
+ },
+
+ _setPref(aPrefName, aPrefValue, aIsComplex = false) {
+ LOG(`FeedWriter._setPref ${aPrefName}`);
+ // Ensure we have a pref that is settable
+ if (aPrefName && SETTABLE_PREFS.has(aPrefName)) {
+ if (aIsComplex) {
+ Services.prefs.setStringPref(aPrefName, aPrefValue);
+ } else {
+ Services.prefs.setCharPref(aPrefName, aPrefValue);
+ }
+ } else {
+ LOG(`FeedWriter._setPref ${aPrefName} not allowed`);
+ }
+ },
+
+ _getReaderForType(feedType) {
+ let prefs = Services.prefs;
+ let handler = "bookmarks";
+ let url;
+ // eslint-disable-next-line mozilla/use-default-preference-values
+ try {
+ handler = prefs.getCharPref(getPrefReaderForType(feedType));
+ } catch (ex) { }
+
+ if (handler === "web") {
+ try {
+ url = prefs.getStringPref(getPrefWebForType(feedType));
+ } catch (ex) {
+ LOG("FeedWriter._setSelectedHandler: invalid or no handler in prefs");
+ url = null;
+ }
+ }
+ const alwaysUse = this._getAlwaysUseState(feedType);
+ const action = prefs.getCharPref(getPrefActionForType(feedType));
+ return { handler, url, alwaysUse, action };
+ },
+
+ _getAlwaysUseState(feedType) {
+ try {
+ return Services.prefs.getCharPref(getPrefActionForType(feedType)) != "ask";
+ } catch (ex) { }
+ return false;
},
receiveMessage(msg) {
+ let handler;
switch (msg.name) {
- case "FeedWriter:ChooseClientApp":
- this.chooseClientApp(msg.data.title, msg.data.prefName, msg.target);
+ case "FeedWriter:GetSubscriptionUI":
+ const response = this._initSubscriptionUIResponse(msg.data.feedType);
+ msg.target.messageManager
+ .sendAsyncMessage("FeedWriter:GetSubscriptionUIResponse",
+ response);
break;
- case "FeedWriter:RequestClientAppName":
- let selectedClientApp;
- try {
- selectedClientApp = Services.prefs.getComplexValue(msg.data.feedTypePref, Ci.nsILocalFile);
- } catch (ex) {
- // Just do nothing, then we won't bother populating
- }
-
- let defaultClientApp = null;
- try {
- // This can sometimes not exist
- defaultClientApp = Cc["@mozilla.org/browser/shell-service;1"]
- .getService(Ci.nsIShellService)
- .defaultFeedReader;
- } catch (ex) {
- // Just do nothing, then we don't bother populating
- }
-
- if (selectedClientApp && selectedClientApp.exists()) {
- if (defaultClientApp && selectedClientApp.path != defaultClientApp.path) {
- // Only set the default menu item if it differs from the selected one
- msg.target.messageManager
- .sendAsyncMessage("FeedWriter:SetApplicationLauncherMenuItem",
- { name: this._getFileDisplayName(defaultClientApp),
- type: "DefaultAppMenuItem" });
- }
- msg.target.messageManager
- .sendAsyncMessage("FeedWriter:SetApplicationLauncherMenuItem",
- { name: this._getFileDisplayName(selectedClientApp),
- type: "SelectedAppMenuItem" });
- }
+ case "FeedWriter:ChooseClientApp":
+ this._chooseClientApp(msg.data.title, msg.data.feedType, msg.target);
break;
case "FeedWriter:ShownFirstRun":
- Services.prefs.setBoolPref("browser.feeds.showFirstRunUI", false);
+ Services.prefs.setBoolPref(PREF_SHOW_FIRST_RUN_UI, false);
break;
- case "FeedWriter:SetFeedCharPref":
- Services.prefs.setCharPref(msg.data.pref, msg.data.value);
- break;
- case "FeedWriter:SetFeedComplexString": {
- Services.prefs.setStringPref(msg.data.pref, msg.data.value);
- break;
- }
+ case "FeedWriter:SetFeedPrefsAndSubscribe":
+ const settings = msg.data;
+ if (!settings.action || !VALID_ACTIONS.has(settings.action)) {
+ LOG(`Invalid action ${settings.action}`);
+ return;
+ }
+ if (!settings.reader || !VALID_READERS.has(settings.reader)) {
+ LOG(`Invalid reader ${settings.reader}`);
+ return;
+ }
+ const actionPref = getPrefActionForType(settings.feedType);
+ this._setPref(actionPref, settings.action);
+ const readerPref = getPrefReaderForType(settings.feedType);
+ this._setPref(readerPref, settings.reader);
+ handler = null;
+
+ switch (settings.reader) {
+ case "web":
+ // This is a web set URI by content using window.registerContentHandler()
+ // Lets make sure we know about it before setting it
+ const webPref = getPrefWebForType(settings.feedType);
+ let wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
+ getService(Ci.nsIWebContentConverterService);
+ // If the user provided an invalid web URL this function won't give us a reference
+ handler = wccr.getWebContentHandlerByURI(getMimeTypeForFeedType(settings.feedType), settings.uri);
+ if (handler) {
+ this._setPref(webPref, settings.uri, true);
+ if (settings.useAsDefault) {
+ wccr.setAutoHandler(getMimeTypeForFeedType(settings.feedType), handler);
+ }
+ msg.target.messageManager
+ .sendAsyncMessage("FeedWriter:SetFeedPrefsAndSubscribeResponse",
+ { redirect: handler.getHandlerURI(settings.feedLocation) });
+ } else {
+ LOG(`No handler found for web ${settings.feedType} ${settings.uri}`);
+ }
+ break;
+ default:
+ const feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
+ getService(Ci.nsIFeedResultService);
+
+ feedService.addToClientReader(settings.feedLocation,
+ settings.feedTitle,
+ settings.feedSubtitle,
+ settings.feedType,
+ settings.reader);
+ }
+ break;
case "FeedConverter:ExecuteClientApp":
- this.executeClientApp(msg.data.spec, msg.data.title,
- msg.data.subtitle, msg.data.feedHandler);
+ // Always check feedHandler is from a set array of executable prefs
+ if (EXECUTABLE_PREFS.has(msg.data.feedHandler)) {
+ this.executeClientApp(msg.data.spec, msg.data.title,
+ msg.data.subtitle, msg.data.feedHandler);
+ } else {
+ LOG(`FeedConverter:ExecuteClientApp - Will not exec ${msg.data.feedHandler}`);
+ }
break;
}
},
diff --git a/browser/base/content/browser-syncui.js b/browser/base/content/browser-syncui.js
index 6e9ccb283a2f..8d81b7d25e9b 100644
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -473,7 +473,7 @@ XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
// XXXzpao these strings should probably be moved from /services to /browser... (bug 583381)
// but for now just make it work
return Services.strings.createBundle(
- "chrome://weave/locale/services/sync.properties");
+ "chrome://weave/locale/sync.properties");
});
XPCOMUtils.defineLazyGetter(gSyncUI, "log", function() {
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 5ceca60efb10..14c6f43444fd 100755
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -15,7 +15,7 @@ Cu.import("resource://gre/modules/NotificationDB.jsm");
// lazy module getters
-/* global AboutHome:false, AddonWatcher:false, AppConstants: false,
+/* global AboutHome:false, AddonWatcher:false,
BrowserUITelemetry:false, BrowserUsageTelemetry:false, BrowserUtils:false,
CastingApps:false, CharsetMenu:false, Color:false, ContentSearch:false,
Deprecated:false, E10SUtils:false, FormValidationHandler:false,
@@ -38,7 +38,6 @@ Cu.import("resource://gre/modules/NotificationDB.jsm");
[
["AboutHome", "resource:///modules/AboutHome.jsm"],
["AddonWatcher", "resource://gre/modules/AddonWatcher.jsm"],
- ["AppConstants", "resource://gre/modules/AppConstants.jsm"],
["BrowserUITelemetry", "resource:///modules/BrowserUITelemetry.jsm"],
["BrowserUsageTelemetry", "resource:///modules/BrowserUsageTelemetry.jsm"],
["BrowserUtils", "resource://gre/modules/BrowserUtils.jsm"],
@@ -2572,8 +2571,13 @@ function URLBarSetURI(aURI) {
valid = !isBlankPageURL(uri.spec);
}
+ let isDifferentValidValue = valid && value != gURLBar.value;
gURLBar.value = value;
gURLBar.valueIsTyped = !valid;
+ if (isDifferentValidValue) {
+ gURLBar.selectionStart = gURLBar.selectionEnd = 0;
+ }
+
SetPageProxyState(valid ? "valid" : "invalid");
}
@@ -6680,6 +6684,35 @@ function BrowserOpenSyncTabs() {
gSyncUI.openSyncedTabsPanel();
}
+function ReportFalseDeceptiveSite() {
+ let docURI = gBrowser.selectedBrowser.documentURI;
+ let isPhishingPage =
+ docURI && docURI.spec.startsWith("about:blocked?e=deceptiveBlocked");
+
+ if (isPhishingPage) {
+ let mm = gBrowser.selectedBrowser.messageManager;
+ let onMessage = (message) => {
+ mm.removeMessageListener("DeceptiveBlockedDetails:Result", onMessage);
+ let reportUrl = gSafeBrowsing.getReportURL("PhishMistake", message.data.blockedInfo);
+ if (reportUrl) {
+ openUILinkIn(reportUrl, "tab");
+ } else {
+ let promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
+ getService(Ci.nsIPromptService);
+ let bundle =
+ Services.strings.createBundle("chrome://browser/locale/safebrowsing/safebrowsing.properties");
+ promptService.alert(window,
+ bundle.GetStringFromName("errorReportFalseDeceptiveTitle"),
+ bundle.formatStringFromName("errorReportFalseDeceptiveMessage",
+ [message.data.blockedInfo.provider], 1));
+ }
+ }
+ mm.addMessageListener("DeceptiveBlockedDetails:Result", onMessage);
+
+ mm.sendAsyncMessage("DeceptiveBlockedDetails");
+ }
+}
+
/**
* Format a URL
* eg:
diff --git a/browser/base/content/content.js b/browser/base/content/content.js
index 1be217b95de6..f34d11f9a323 100644
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -262,6 +262,35 @@ function getSerializedSecurityInfo(docShell) {
return serhelper.serializeToString(securityInfo);
}
+function getSiteBlockedErrorDetails(docShell) {
+ let blockedInfo = {};
+ if (docShell.failedChannel) {
+ let classifiedChannel = docShell.failedChannel.
+ QueryInterface(Ci.nsIClassifiedChannel);
+ if (classifiedChannel) {
+ let httpChannel = docShell.failedChannel.QueryInterface(Ci.nsIHttpChannel);
+
+ let reportUri = httpChannel.URI.clone();
+
+ // Remove the query to avoid leaking sensitive data
+ if (reportUri instanceof Ci.nsIURL) {
+ reportUri.query = "";
+ }
+
+ blockedInfo = { list: classifiedChannel.matchedList,
+ provider: classifiedChannel.matchedProvider,
+ uri: reportUri.asciiSpec };
+ }
+ }
+ return blockedInfo;
+}
+
+addMessageListener("DeceptiveBlockedDetails", (message) => {
+ sendAsyncMessage("DeceptiveBlockedDetails:Result", {
+ blockedInfo: getSiteBlockedErrorDetails(docShell),
+ });
+});
+
var AboutNetAndCertErrorListener = {
init(chromeGlobal) {
addMessageListener("CertErrorDetails", this);
@@ -583,32 +612,13 @@ var ClickEventHandler = {
let docShell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
- let blockedInfo = {};
- if (docShell.failedChannel) {
- let classifiedChannel = docShell.failedChannel.
- QueryInterface(Ci.nsIClassifiedChannel);
- if (classifiedChannel) {
- let httpChannel = docShell.failedChannel.QueryInterface(Ci.nsIHttpChannel);
-
- let reportUri = httpChannel.URI.clone();
-
- // Remove the query to avoid leaking sensitive data
- if (reportUri instanceof Ci.nsIURL) {
- reportUri.query = "";
- }
-
- blockedInfo = { list: classifiedChannel.matchedList,
- provider: classifiedChannel.matchedProvider,
- uri: reportUri.asciiSpec };
- }
- }
sendAsyncMessage("Browser:SiteBlockedError", {
location: ownerDoc.location.href,
reason,
elementId: targetElement.getAttribute("id"),
isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView),
- blockedInfo
+ blockedInfo: getSiteBlockedErrorDetails(docShell),
});
},
diff --git a/browser/base/content/report-phishing-overlay.xul b/browser/base/content/report-phishing-overlay.xul
index 712079f82479..112bf75a0d88 100644
--- a/browser/base/content/report-phishing-overlay.xul
+++ b/browser/base/content/report-phishing-overlay.xul
@@ -29,7 +29,7 @@
accesskey="&safeb.palm.notdeceptive.accesskey;"
insertbefore="aboutSeparator"
observes="reportPhishingErrorBroadcaster"
- oncommand="openUILinkIn(gSafeBrowsing.getReportURL('PhishMistake'), 'tab');"
+ oncommand="ReportFalseDeceptiveSite();"
onclick="checkForMiddleClick(this, event);"/>
diff --git a/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher.js b/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher.js
index e9c0fad6dad0..c37f3cb8d254 100644
--- a/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher.js
+++ b/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher.js
@@ -1,5 +1,9 @@
"use strict";
+// Bug 1318389 - This test does a lot of window and tab manipulation,
+// causing it to take a long time on debug.
+requestLongerTimeout(2);
+
add_task(setupPrefsAndRecentWindowBehavior);
// Each of the test cases below is run twice: once for login-success and once
diff --git a/browser/base/content/test/general/browser_syncui.js b/browser/base/content/test/general/browser_syncui.js
index 29954278ed99..71074ef5ed97 100644
--- a/browser/base/content/test/general/browser_syncui.js
+++ b/browser/base/content/test/general/browser_syncui.js
@@ -6,7 +6,7 @@ var {Weave} = Cu.import("resource://services-sync/main.js", {});
var stringBundle = Cc["@mozilla.org/intl/stringbundle;1"]
.getService(Ci.nsIStringBundleService)
- .createBundle("chrome://weave/locale/services/sync.properties");
+ .createBundle("chrome://weave/locale/sync.properties");
// ensure test output sees log messages.
Log.repository.getLogger("browserwindow.syncui").addAppender(new Log.DumpAppender());
diff --git a/browser/base/content/test/general/browser_windowactivation.js b/browser/base/content/test/general/browser_windowactivation.js
index 73e972a45592..0102c7e958be 100644
--- a/browser/base/content/test/general/browser_windowactivation.js
+++ b/browser/base/content/test/general/browser_windowactivation.js
@@ -4,39 +4,31 @@
/* eslint-env mozilla/frame-script */
-var testPage = "data:text/html,";
+var testPage = "data:text/html;charset=utf-8,";
var colorChangeNotifications = 0;
var otherWindow;
var browser1, browser2;
-function test() {
- waitForExplicitFinish();
- waitForFocus(reallyRunTests);
-}
+add_task(function* reallyRunTests() {
-function reallyRunTests() {
+ let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, testPage);
+ browser1 = tab1.linkedBrowser;
- let tab1 = gBrowser.addTab();
- let tab2 = gBrowser.addTab();
- browser1 = gBrowser.getBrowserForTab(tab1);
- browser2 = gBrowser.getBrowserForTab(tab2);
+ // This can't use openNewForegroundTab because if we focus tab2 now, we
+ // won't send a focus event during test 6, further down in this file.
+ let tab2 = gBrowser.addTab(testPage);
+ browser2 = tab2.linkedBrowser;
+ yield BrowserTestUtils.browserLoaded(browser2);
+
+ browser1.messageManager.loadFrameScript("data:,(" + childFunction.toString() + ")();", true);
+ browser2.messageManager.loadFrameScript("data:,(" + childFunction.toString() + ")();", true);
gURLBar.focus();
- var loadCount = 0;
- function check() {
- // wait for both tabs to load
- if (++loadCount != 2) {
- return;
- }
-
- browser1.removeEventListener("load", check, true);
- browser2.removeEventListener("load", check, true);
-
- sendGetBackgroundRequest(true);
- }
+ let testFinished = {};
+ testFinished.promise = new Promise(resolve => testFinished.resolve = resolve);
// The test performs four checks, using -moz-window-inactive on two child tabs.
// First, the initial state should be transparent. The second check is done
@@ -74,7 +66,7 @@ function reallyRunTests() {
break;
case 8:
is(message.data.color, "rgba(0, 0, 0, 0)", "second window after tab switch");
- finishTest();
+ testFinished.resolve();
break;
case 9:
ok(false, "too many color change notifications");
@@ -97,16 +89,17 @@ function reallyRunTests() {
ok(message.data.ok, "Test:DeactivateEvent");
});
- browser1.addEventListener("load", check, true);
- browser2.addEventListener("load", check, true);
- browser1.contentWindow.location = testPage;
- browser2.contentWindow.location = testPage;
-
- browser1.messageManager.loadFrameScript("data:,(" + childFunction.toString() + ")();", true);
- browser2.messageManager.loadFrameScript("data:,(" + childFunction.toString() + ")();", true);
-
gBrowser.selectedTab = tab1;
-}
+
+ // Start the test.
+ sendGetBackgroundRequest(true);
+
+ yield testFinished.promise;
+
+ yield BrowserTestUtils.removeTab(tab1);
+ yield BrowserTestUtils.removeTab(tab2);
+ otherWindow = null;
+});
function sendGetBackgroundRequest(ifChanged) {
browser1.messageManager.sendAsyncMessage("Test:GetBackgroundColor", { ifChanged });
@@ -114,19 +107,12 @@ function sendGetBackgroundRequest(ifChanged) {
}
function runOtherWindowTests() {
- otherWindow = window.open("data:text/html,Hi", "", "chrome");
+ otherWindow = window.open("data:text/html;charset=utf-8,Hi", "", "chrome");
waitForFocus(function() {
sendGetBackgroundRequest(true);
}, otherWindow);
}
-function finishTest() {
- gBrowser.removeCurrentTab();
- gBrowser.removeCurrentTab();
- otherWindow = null;
- finish();
-}
-
function childFunction() {
let oldColor = null;
diff --git a/browser/base/content/test/popupNotifications/browser.ini b/browser/base/content/test/popupNotifications/browser.ini
index 18810ea5c2e0..c84ed3f3ea43 100644
--- a/browser/base/content/test/popupNotifications/browser.ini
+++ b/browser/base/content/test/popupNotifications/browser.ini
@@ -13,7 +13,7 @@ skip-if = (os == "linux" && (debug || asan))
[browser_popupNotification_4.js]
skip-if = (os == "linux" && (debug || asan))
[browser_popupNotification_5.js]
-skip-if = (os == "linux" && (debug || asan))
+skip-if = true # bug 1332646
[browser_popupNotification_checkbox.js]
skip-if = (os == "linux" && (debug || asan))
[browser_popupNotification_keyboard.js]
diff --git a/browser/base/content/test/static/browser.ini b/browser/base/content/test/static/browser.ini
index 25760848bf62..75fb4b904b64 100644
--- a/browser/base/content/test/static/browser.ini
+++ b/browser/base/content/test/static/browser.ini
@@ -2,6 +2,8 @@
support-files =
head.js
+[browser_all_files_referenced.js]
+skip-if = debug # no point in running on both opt and debug, and will likely intermittently timeout on debug
[browser_misused_characters_in_strings.js]
support-files =
bug1262648_string_with_newlines.dtd
diff --git a/browser/base/content/test/static/browser_all_files_referenced.js b/browser/base/content/test/static/browser_all_files_referenced.js
new file mode 100644
index 000000000000..e72d5d2d0603
--- /dev/null
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -0,0 +1,498 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var isDevtools = SimpleTest.harnessParameters.subsuite == "devtools";
+
+var gExceptionPaths = ["chrome://browser/content/defaultthemes/",
+ "chrome://browser/locale/searchplugins/"];
+
+var whitelist = new Set([
+ // browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+ {file: "chrome://pdf.js/locale/chrome.properties"},
+ {file: "chrome://pdf.js/locale/viewer.properties"},
+
+ // security/manager/pki/resources/content/device_manager.js
+ {file: "chrome://pippki/content/load_device.xul"},
+
+ // browser/modules/ReaderParent.jsm
+ {file: "chrome://browser/skin/reader-tour.png"},
+ {file: "chrome://browser/skin/reader-tour@2x.png"},
+
+ // Used by setting this url as a pref in about:config
+ {file: "chrome://browser/content/newtab/alternativeDefaultSites.json"},
+
+ // Add-on compat
+ {file: "chrome://browser/skin/devtools/common.css"},
+ {file: "chrome://global/content/XPCNativeWrapper.js"},
+ {file: "chrome://global/locale/brand.dtd"},
+
+ // The l10n build system can't package string files only for some platforms.
+ // See bug 1339424 for why this is hard to fix.
+ {file: "chrome://global/locale/fallbackMenubar.properties",
+ platforms: ["linux", "win"]},
+ {file: "chrome://global/locale/printPageSetup.dtd", platforms: ["macosx"]},
+ {file: "chrome://global/locale/printPreviewProgress.dtd",
+ platforms: ["macosx"]},
+ {file: "chrome://global/locale/printProgress.dtd", platforms: ["macosx"]},
+ {file: "chrome://global/locale/printdialog.dtd",
+ platforms: ["macosx", "win"]},
+ {file: "chrome://global/locale/printjoboptions.dtd",
+ platforms: ["macosx", "win"]},
+
+ // services/cloudsync/CloudSyncLocal.jsm
+ {file: "chrome://weave/locale/errors.properties"},
+
+ // devtools/client/inspector/bin/dev-server.js
+ {file: "chrome://devtools/content/inspector/markup/markup.xhtml",
+ isFromDevTools: true},
+
+ // Starting from here, files in the whitelist are bugs that need fixing.
+ // Bug 1339420
+ {file: "chrome://branding/content/icon128.png"},
+ // Bug 1339424 (wontfix?)
+ {file: "chrome://browser/locale/taskbar.properties",
+ platforms: ["linux", "macosx"]},
+ // Bug 1320156
+ {file: "chrome://browser/skin/Privacy-16.png", platforms: ["linux"]},
+ // Bug 1343584
+ {file: "chrome://browser/skin/click-to-play-warning-stripes.png"},
+ // Bug 1343824
+ {file: "chrome://browser/skin/customizableui/customize-illustration-rtl@2x.png",
+ platforms: ["linux", "win"]},
+ {file: "chrome://browser/skin/customizableui/customize-illustration@2x.png",
+ platforms: ["linux", "win"]},
+ {file: "chrome://browser/skin/customizableui/info-icon-customizeTip@2x.png",
+ platforms: ["linux", "win"]},
+ {file: "chrome://browser/skin/customizableui/panelarrow-customizeTip@2x.png",
+ platforms: ["linux", "win"]},
+ // Bug 1320058
+ {file: "chrome://browser/skin/preferences/saveFile.png", platforms: ["win"]},
+ // Bug 1348369
+ {file: "chrome://formautofill/content/editProfile.xhtml"},
+ // Bug 1316187
+ {file: "chrome://global/content/customizeToolbar.xul"},
+ // Bug 1343837
+ {file: "chrome://global/content/findUtils.js"},
+ // Bug 1343843
+ {file: "chrome://global/content/url-classifier/unittests.xul"},
+ // Bug 1343839
+ {file: "chrome://global/locale/headsUpDisplay.properties"},
+ // Bug 1348358
+ {file: "chrome://global/skin/arrow.css"},
+ {file: "chrome://global/skin/arrow/arrow-dn-sharp.gif",
+ platforms: ["linux", "win"]},
+ {file: "chrome://global/skin/arrow/arrow-down.png",
+ platforms: ["linux", "win"]},
+ {file: "chrome://global/skin/arrow/arrow-lft-sharp-end.gif"},
+ {file: "chrome://global/skin/arrow/arrow-lft-sharp.gif",
+ platforms: ["linux", "win"]},
+ {file: "chrome://global/skin/arrow/arrow-rit-sharp-end.gif"},
+ {file: "chrome://global/skin/arrow/arrow-rit-sharp.gif",
+ platforms: ["linux", "win"]},
+ {file: "chrome://global/skin/arrow/arrow-up-sharp.gif",
+ platforms: ["linux", "win"]},
+ {file: "chrome://global/skin/arrow/panelarrow-horizontal.svg",
+ platforms: ["linux"]},
+ {file: "chrome://global/skin/arrow/panelarrow-vertical.svg",
+ platforms: ["linux"]},
+ // Bug 1348529
+ {file: "chrome://global/skin/checkbox/cbox-check-dis.gif",
+ platforms: ["linux"]},
+ {file: "chrome://global/skin/checkbox/cbox-check.gif", platforms: ["linux"]},
+ // Bug 1348359
+ {file: "chrome://global/skin/dirListing/folder.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/dirListing/local.png", platforms: ["linux", "win"]},
+ {file: "chrome://global/skin/dirListing/remote.png"},
+ {file: "chrome://global/skin/dirListing/up.png", platforms: ["linux"]},
+ // Bug 1348362
+ {file: "chrome://global/skin/icons/Close.gif", platforms: ["win"]},
+ {file: "chrome://global/skin/icons/Error.png", platforms: ["linux", "macosx"]},
+ {file: "chrome://global/skin/icons/Landscape.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/Minimize.gif", platforms: ["win"]},
+ {file: "chrome://global/skin/icons/Portrait.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/Print-preview.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/Question.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/Restore.gif", platforms: ["win"]},
+ {file: "chrome://global/skin/icons/Search-close.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/Search-glass.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/Warning.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/checkbox.png", platforms: ["macosx"]},
+ {file: "chrome://global/skin/icons/checkbox@2x.png", platforms: ["macosx"]},
+ {file: "chrome://global/skin/icons/close-inverted.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/close-inverted@2x.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/close.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/close@2x.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/collapse.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/error-64.png", platforms: ["linux", "win"]},
+ {file: "chrome://global/skin/icons/error-large.png", platforms: ["macosx"]},
+ {file: "chrome://global/skin/icons/expand.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/folder-item.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/question-large.png", platforms: ["macosx"]},
+ {file: "chrome://global/skin/icons/warning-32.png", platforms: ["macosx"]},
+ {file: "chrome://global/skin/icons/warning-64.png", platforms: ["linux", "win"]},
+ {file: "chrome://global/skin/icons/warning-large.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/icons/windowControls.png", platforms: ["linux"]},
+ // Bug 1348521
+ {file: "chrome://global/skin/linkTree.css"},
+ // Bug 1348522
+ {file: "chrome://global/skin/media/clicktoplay-bgtexture.png"},
+ {file: "chrome://global/skin/media/videoClickToPlayButton.svg"},
+ // Bug 1348524
+ {file: "chrome://global/skin/notification/close.png", platforms: ["macosx"]},
+ // Bug 1348525
+ {file: "chrome://global/skin/splitter/grip-bottom.gif", platforms: ["linux"]},
+ {file: "chrome://global/skin/splitter/grip-left.gif", platforms: ["linux"]},
+ {file: "chrome://global/skin/splitter/grip-right.gif", platforms: ["linux"]},
+ {file: "chrome://global/skin/splitter/grip-top.gif", platforms: ["linux"]},
+ // Bug 1348526
+ {file: "chrome://global/skin/tree/sort-asc-classic.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/tree/sort-asc.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/tree/sort-dsc-classic.png", platforms: ["linux"]},
+ {file: "chrome://global/skin/tree/sort-dsc.png", platforms: ["linux"]},
+ // Bug 1344267
+ {file: "chrome://marionette/content/test_anonymous_content.xul"},
+ {file: "chrome://marionette/content/test_dialog.properties"},
+ {file: "chrome://marionette/content/test_dialog.xul"},
+ // Bug 1348532
+ {file: "chrome://mozapps/content/extensions/list.xul"},
+ // Bug 1348533
+ {file: "chrome://mozapps/skin/downloads/buttons.png", platforms: ["macosx"]},
+ {file: "chrome://mozapps/skin/downloads/downloadButtons.png", platforms: ["linux", "win"]},
+ // Bug 1348555
+ {file: "chrome://mozapps/skin/extensions/dictionaryGeneric-16.png"},
+ {file: "chrome://mozapps/skin/extensions/search.png", platforms: ["macosx"]},
+ {file: "chrome://mozapps/skin/extensions/themeGeneric-16.png"},
+ // Bug 1348556
+ {file: "chrome://mozapps/skin/plugins/pluginBlocked.png"},
+ // Bug 1348558
+ {file: "chrome://mozapps/skin/update/downloadButtons.png",
+ platforms: ["linux"]},
+ // Bug 1348559
+ {file: "chrome://pippki/content/resetpassword.xul"},
+ // Bug 1348562
+ {file: "chrome://devtools/skin/images/debugging-devices.svg",
+ isFromDevTools: true},
+ {file: "chrome://devtools/skin/images/fast-forward.svg",
+ isFromDevTools: true},
+ {file: "chrome://devtools/skin/images/firebug/spinner.png",
+ isFromDevTools: true},
+ {file: "chrome://devtools/skin/images/noise.png",
+ isFromDevTools: true},
+
+].filter(item =>
+ ("isFromDevTools" in item) == isDevtools &&
+ (!item.platforms || item.platforms.includes(AppConstants.platform))
+).map(item => item.file));
+
+const ignorableWhitelist = new Set([
+ // chrome://xslt-qa/ isn't referenced, but isn't included in packaged builds,
+ // so it's fine to just ignore it and ignore if the exceptions are unused.
+ "chrome://xslt-qa/content/buster/result-view.xul",
+ "chrome://xslt-qa/content/xslt-qa-overlay.xul",
+ // The communicator.css file is kept for add-on backward compat, but it is
+ // referenced by something in xslt-qa, so the exception won't be used when
+ // running the test on a local non-packaged build.
+ "chrome://communicator/skin/communicator.css",
+
+ // These 2 files are unreferenced only when building without the crash
+ // reporter (eg. Linux x64 asan builds on treeherder)
+ "chrome://global/locale/crashes.dtd",
+ "chrome://global/locale/crashes.properties",
+]);
+for (let entry of ignorableWhitelist)
+ whitelist.add(entry);
+
+const gInterestingCategories = new Set([
+ "agent-style-sheets", "webextension-scripts",
+ "webextension-schemas", "webextension-scripts-addon",
+ "webextension-scripts-content", "webextension-scripts-devtools"
+]);
+
+var gChromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
+ .getService(Ci.nsIChromeRegistry);
+var gChromeMap = new Map();
+var gOverrideMap = new Map();
+var gReferencesFromCode = new Set();
+
+function getBaseUriForChromeUri(chromeUri) {
+ let chromeFile = chromeUri + "gobbledygooknonexistentfile.reallynothere";
+ let uri = Services.io.newURI(chromeFile);
+ let fileUri = gChromeReg.convertChromeURL(uri);
+ return fileUri.resolve(".");
+}
+
+function parseManifest(manifestUri) {
+ return fetchFile(manifestUri.spec).then(data => {
+ for (let line of data.split("\n")) {
+ let [type, ...argv] = line.split(/\s+/);
+ if (type == "content" || type == "skin" || type == "locale") {
+ let chromeUri = `chrome://${argv[0]}/${type}/`;
+ gChromeMap.set(getBaseUriForChromeUri(chromeUri), chromeUri);
+ } else if (type == "override" || type == "overlay") {
+ // Overlays aren't really overrides, but behave the same in
+ // that the overlay is only referenced if the original xul
+ // file is referenced somewhere.
+ let os = "os=" + Services.appinfo.OS;
+ if (!argv.some(s => s.startsWith("os=") && s != os)) {
+ gOverrideMap.set(Services.io.newURI(argv[1]).specIgnoringRef,
+ Services.io.newURI(argv[0]).specIgnoringRef);
+ }
+ } else if (type == "category" && gInterestingCategories.has(argv[0])) {
+ gReferencesFromCode.add(argv[2]);
+ }
+ }
+ });
+}
+
+function parseCSSFile(fileUri) {
+ return fetchFile(fileUri.spec).then(data => {
+ for (let line of data.split("\n")) {
+ let urls = line.match(/url\([^()]+\)/g);
+ if (!urls) {
+ // @import rules can take a string instead of a url.
+ let importMatch = line.match(/@import ['"]?([^'"]*)['"]?/);
+ if (importMatch && importMatch[1]) {
+ let url = Services.io.newURI(importMatch[1], null, fileUri).spec;
+ gReferencesFromCode.add(convertToChromeUri(url));
+ }
+ continue;
+ }
+
+ for (let url of urls) {
+ // Remove the url(" prefix and the ") suffix.
+ url = url.replace(/url\(([^)]*)\)/, "$1")
+ .replace(/^"(.*)"$/, "$1")
+ .replace(/^'(.*)'$/, "$1");
+ if (url.startsWith("data:"))
+ continue;
+
+ try {
+ url = Services.io.newURI(url, null, fileUri).specIgnoringRef;
+ gReferencesFromCode.add(convertToChromeUri(url));
+ } catch (e) {
+ ok(false, "unexpected error while resolving this URI: " + url);
+ }
+ }
+ }
+ });
+}
+
+function parseCodeFile(fileUri) {
+ return fetchFile(fileUri.spec).then(data => {
+ for (let line of data.split("\n")) {
+ let urls =
+ line.match(/["']chrome:\/\/[a-zA-Z0-9 -]+\/(content|skin|locale)\/[^"' ]*["']/g);
+ if (!urls) {
+ // If there's no absolute chrome URL, look for relative ones in
+ // src and href attributes.
+ let match = line.match("(?:src|href)=[\"']([^$&\"']+)");
+ if (match && match[1]) {
+ let url = Services.io.newURI(match[1], null, fileUri).spec;
+ gReferencesFromCode.add(convertToChromeUri(url));
+ }
+
+ if (isDevtools) {
+ // Handle usage of devtools' LocalizationHelper object
+ match = line.match('"devtools/client/locales/([^/.]+).properties"');
+ if (match && match[1]) {
+ gReferencesFromCode.add("chrome://devtools/locale/" +
+ match[1] + ".properties");
+ }
+
+ match = line.match('"devtools/shared/locales/([^/.]+).properties"');
+ if (match && match[1]) {
+ gReferencesFromCode.add("chrome://devtools-shared/locale/" +
+ match[1] + ".properties");
+ }
+ }
+ continue;
+ }
+
+ for (let url of urls) {
+ // Remove quotes.
+ url = url.slice(1, -1);
+ // Remove ? or \ trailing characters.
+ if (url.endsWith("?") || url.endsWith("\\"))
+ url = url.slice(0, -1);
+
+ // Make urls like chrome://browser/skin/ point to an actual file,
+ // and remove the ref if any.
+ url = Services.io.newURI(url).specIgnoringRef;
+
+ gReferencesFromCode.add(url);
+ }
+ }
+ });
+}
+
+function convertToChromeUri(fileUri) {
+ let baseUri = fileUri;
+ let path = "";
+ while (true) {
+ let slashPos = baseUri.lastIndexOf("/", baseUri.length - 2);
+ if (slashPos <= 0) {
+ // File not accessible from chrome protocol,
+ // TODO: bug 1349005 handle resource:// urls.
+ return fileUri;
+ }
+ path = baseUri.slice(slashPos + 1) + path;
+ baseUri = baseUri.slice(0, slashPos + 1);
+ if (gChromeMap.has(baseUri)) {
+ let chromeBaseUri = gChromeMap.get(baseUri);
+ return `${chromeBaseUri}${path}`;
+ }
+ }
+}
+
+function chromeFileExists(aURI) {
+ let available = 0;
+ try {
+ let channel = NetUtil.newChannel({uri: aURI, loadUsingSystemPrincipal: true});
+ let stream = channel.open();
+ let sstream = Cc["@mozilla.org/scriptableinputstream;1"]
+ .createInstance(Ci.nsIScriptableInputStream);
+ sstream.init(stream);
+ available = sstream.available();
+ sstream.close();
+ } catch (e) {
+ if (e.result != Components.results.NS_ERROR_FILE_NOT_FOUND) {
+ dump("Checking " + aURI + ": " + e + "\n");
+ Cu.reportError(e);
+ }
+ }
+ return available > 0;
+}
+
+function findChromeUrlsFromArray(array) {
+ const prefix = "chrome://";
+ // Find the 'c' character...
+ for (let index = 0;
+ (index = array.indexOf(prefix.charCodeAt(0), index)) != -1;
+ ++index) {
+ // Then ensure we actually have the whole chrome:// prefix.
+ let found = true;
+ for (let i = 1; i < prefix.length; ++i) {
+ if (array[index + i] != prefix.charCodeAt(i)) {
+ found = false;
+ break;
+ }
+ }
+ if (!found)
+ continue;
+
+ // C strings are null terminated, but " also terminates urls
+ // (nsIndexedToHTML.cpp contains an HTML fragment with several chrome urls)
+ // Let's also terminate the string on the # character to skip references.
+ let end = Math.min(array.indexOf(0, index),
+ array.indexOf('"'.charCodeAt(0), index),
+ array.indexOf("#".charCodeAt(0), index));
+ let string = "";
+ for ( ; index < end; ++index)
+ string += String.fromCharCode(array[index]);
+
+ // Only keep strings that look like real chrome urls.
+ if (/chrome:\/\/[a-zA-Z09 -]+\/(content|skin|locale)\//.test(string))
+ gReferencesFromCode.add(string);
+ }
+}
+
+add_task(function* checkAllTheFiles() {
+ let libxulPath = OS.Constants.Path.libxul;
+ if (AppConstants.platform != "macosx")
+ libxulPath = OS.Constants.Path.libDir + "/" + libxulPath;
+ let libxul = yield OS.File.read(libxulPath);
+ findChromeUrlsFromArray(libxul);
+ // Handle NS_LITERAL_STRING.
+ findChromeUrlsFromArray(new Uint16Array(libxul.buffer));
+
+ const kCodeExtensions = [".xul", ".xml", ".xsl", ".js", ".jsm", ".html", ".xhtml"];
+
+ let appDir = Services.dirsvc.get("GreD", Ci.nsIFile);
+ // This asynchronously produces a list of URLs (sadly, mostly sync on our
+ // test infrastructure because it runs against jarfiles there, and
+ // our zipreader APIs are all sync)
+ let uris = yield generateURIsFromDirTree(appDir, [".css", ".manifest", ".json", ".jpg", ".png", ".gif", ".svg", ".dtd", ".properties"].concat(kCodeExtensions));
+
+ // Parse and remove all manifests from the list.
+ // NOTE that this must be done before filtering out devtools paths
+ // so that all chrome paths can be recorded.
+ let manifestPromises = [];
+ uris = uris.filter(uri => {
+ let path = uri.path;
+ if (path.endsWith(".manifest")) {
+ manifestPromises.push(parseManifest(uri));
+ return false;
+ }
+
+ return true;
+ });
+
+ // Wait for all manifest to be parsed
+ yield Promise.all(manifestPromises);
+
+ // We build a list of promises that get resolved when their respective
+ // files have loaded and produced no errors.
+ let allPromises = [];
+
+ for (let uri of uris) {
+ let path = uri.path;
+ if (path.endsWith(".css"))
+ allPromises.push(parseCSSFile(uri));
+ else if (kCodeExtensions.some(ext => path.endsWith(ext)))
+ allPromises.push(parseCodeFile(uri));
+ }
+
+ // Wait for all the files to have actually loaded:
+ yield Promise.all(allPromises);
+
+ // Keep only chrome:// files, and filter out either the devtools paths or
+ // the non-devtools paths:
+ let devtoolsPrefixes = ["chrome://webide/", "chrome://devtools"];
+ let chromeFiles =
+ uris.map(uri => convertToChromeUri(uri.spec))
+ .filter(u => u.startsWith("chrome://"))
+ .filter(u => isDevtools == devtoolsPrefixes.some(prefix => u.startsWith(prefix)));
+
+ let isUnreferenced =
+ file => !gReferencesFromCode.has(file) &&
+ !gExceptionPaths.some(e => file.startsWith(e)) &&
+ (!gOverrideMap.has(file) || isUnreferenced(gOverrideMap.get(file)));
+
+ let notWhitelisted = file => {
+ if (!whitelist.has(file))
+ return true;
+ whitelist.delete(file);
+ return false;
+ };
+
+ let unreferencedFiles =
+ chromeFiles.filter(isUnreferenced).filter(notWhitelisted).sort();
+
+ is(unreferencedFiles.length, 0, "there should be no unreferenced files");
+ for (let file of unreferencedFiles)
+ ok(false, "unreferenced chrome file: " + file);
+
+ for (let file of whitelist) {
+ if (ignorableWhitelist.has(file))
+ info("ignored unused whitelist entry: " + file);
+ else
+ ok(false, "unused whitelist entry: " + file);
+ }
+
+ for (let file of gReferencesFromCode) {
+ if (isDevtools != devtoolsPrefixes.some(prefix => file.startsWith(prefix)))
+ continue;
+
+ if (file.startsWith("chrome://") && !chromeFileExists(file)) {
+ // Ignore chrome prefixes that have been automatically expanded.
+ let pathParts =
+ file.match("chrome://([^/]+)/content/([^/.]+)\.xul") ||
+ file.match("chrome://([^/]+)/skin/([^/.]+)\.css");
+ if (!pathParts || pathParts[1] != pathParts[2]) {
+ // TODO: bug 1349010 - add a whitelist and make this reliable enough
+ // that we could make the test fail when this catches something new.
+ info("missing file with code reference: " + file);
+ }
+ }
+ }
+});
diff --git a/browser/base/content/test/static/browser_misused_characters_in_strings.js b/browser/base/content/test/static/browser_misused_characters_in_strings.js
index b18fcf6ff015..b6bbd4f53bdd 100644
--- a/browser/base/content/test/static/browser_misused_characters_in_strings.js
+++ b/browser/base/content/test/static/browser_misused_characters_in_strings.js
@@ -129,30 +129,6 @@ function ignoredError(filepath, key, type) {
return false;
}
-function fetchFile(uri) {
- return new Promise((resolve, reject) => {
- let xhr = new XMLHttpRequest();
- xhr.responseType = "text";
- xhr.open("GET", uri, true);
- xhr.onreadystatechange = function() {
- if (this.readyState != this.DONE) {
- return;
- }
- try {
- resolve(this.responseText);
- } catch (ex) {
- ok(false, `Script error reading ${uri}: ${ex}`);
- resolve("");
- }
- };
- xhr.onerror = error => {
- ok(false, `XHR error reading ${uri}: ${error}`);
- resolve("");
- };
- xhr.send(null);
- });
-}
-
function testForError(filepath, key, str, pattern, type, helpText) {
if (str.match(pattern) &&
!ignoredError(filepath, key, type)) {
diff --git a/browser/base/content/test/static/browser_parsable_css.js b/browser/base/content/test/static/browser_parsable_css.js
index c49aa7513a63..4a99bacd3ee7 100644
--- a/browser/base/content/test/static/browser_parsable_css.js
+++ b/browser/base/content/test/static/browser_parsable_css.js
@@ -107,30 +107,6 @@ function once(target, name) {
});
}
-function fetchFile(uri) {
- return new Promise((resolve, reject) => {
- let xhr = new XMLHttpRequest();
- xhr.responseType = "text";
- xhr.open("GET", uri, true);
- xhr.onreadystatechange = function() {
- if (this.readyState != this.DONE) {
- return;
- }
- try {
- resolve(this.responseText);
- } catch (ex) {
- ok(false, `Script error reading ${uri}: ${ex}`);
- resolve("");
- }
- };
- xhr.onerror = error => {
- ok(false, `XHR error reading ${uri}: ${error}`);
- resolve("");
- };
- xhr.send(null);
- });
-}
-
var gChromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
.getService(Ci.nsIChromeRegistry);
var gChromeMap = new Map();
diff --git a/browser/base/content/test/static/head.js b/browser/base/content/test/static/head.js
index 85695a617c80..5b10e0f1ced2 100644
--- a/browser/base/content/test/static/head.js
+++ b/browser/base/content/test/static/head.js
@@ -125,3 +125,27 @@ function* generateEntriesFromJarFile(jarFile, extension) {
}
zr.close();
}
+
+function fetchFile(uri) {
+ return new Promise((resolve, reject) => {
+ let xhr = new XMLHttpRequest();
+ xhr.responseType = "text";
+ xhr.open("GET", uri, true);
+ xhr.onreadystatechange = function() {
+ if (this.readyState != this.DONE) {
+ return;
+ }
+ try {
+ resolve(this.responseText);
+ } catch (ex) {
+ ok(false, `Script error reading ${uri}: ${ex}`);
+ resolve("");
+ }
+ };
+ xhr.onerror = error => {
+ ok(false, `XHR error reading ${uri}: ${error}`);
+ resolve("");
+ };
+ xhr.send(null);
+ });
+}
diff --git a/browser/base/content/test/urlbar/browser_bug304198.js b/browser/base/content/test/urlbar/browser_bug304198.js
index 0876e8889a11..86e3911e60af 100644
--- a/browser/base/content/test/urlbar/browser_bug304198.js
+++ b/browser/base/content/test/urlbar/browser_bug304198.js
@@ -48,6 +48,9 @@ add_task(function* () {
resolve();
}, {once: true});
gURLBar.focus();
+ if (gURLBar.selectionStart == gURLBar.selectionEnd) {
+ gURLBar.selectionStart = gURLBar.selectionEnd = gURLBar.textValue.length;
+ }
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
});
}
diff --git a/browser/base/content/test/urlbar/browser_canonizeURL.js b/browser/base/content/test/urlbar/browser_canonizeURL.js
index a422dcc9eb82..59ab54ca0b9a 100644
--- a/browser/base/content/test/urlbar/browser_canonizeURL.js
+++ b/browser/base/content/test/urlbar/browser_canonizeURL.js
@@ -29,6 +29,8 @@ add_task(function*() {
let promiseLoad = waitForDocLoadAndStopIt(expectedURL);
gURLBar.focus();
if (Object.keys(options).length > 0) {
+ gURLBar.selectionStart = gURLBar.selectionEnd =
+ gURLBar.inputField.value.length;
gURLBar.inputField.value = inputValue.slice(0, -1);
EventUtils.synthesizeKey(inputValue.slice(-1), {});
} else {
diff --git a/browser/components/extensions/test/browser/browser-common.ini b/browser/components/extensions/test/browser/browser-common.ini
index a7c17e4bd0ce..72d545fc7b23 100644
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -129,6 +129,7 @@ support-files =
[browser_ext_webNavigation_frameId0.js]
[browser_ext_webNavigation_getFrames.js]
[browser_ext_webNavigation_onCreatedNavigationTarget.js]
+[browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js]
[browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js]
[browser_ext_webNavigation_urlbar_transitions.js]
[browser_ext_windows.js]
diff --git a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js
index b82881469380..4959ebf6cbac 100644
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js
@@ -52,14 +52,6 @@ async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
}
-async function clickContextMenuItem({pageElementSelector, contextMenuItemLabel}) {
- const contentAreaContextMenu = await openContextMenu(pageElementSelector);
- const item = contentAreaContextMenu.getElementsByAttribute("label", contextMenuItemLabel);
- is(item.length, 1, `found contextMenu item for "${contextMenuItemLabel}"`);
- item[0].click();
- await closeContextMenu();
-}
-
add_task(function* test_on_created_navigation_target_from_mouse_click() {
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
@@ -127,59 +119,6 @@ add_task(function* test_on_created_navigation_target_from_mouse_click() {
yield extension.unload();
});
-add_task(function* test_on_created_navigation_target_from_context_menu() {
- const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
-
- const extension = ExtensionTestUtils.loadExtension({
- background,
- manifest: {
- permissions: ["webNavigation"],
- },
- });
-
- yield extension.startup();
-
- const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
-
- info("Open link in a new tab from the context menu");
-
- yield runTestCase({
- extension,
- async openNavTarget() {
- await clickContextMenuItem({
- pageElementSelector: "#test-create-new-tab-from-context-menu",
- contextMenuItemLabel: "Open Link in New Tab",
- });
- },
- expectedWebNavProps: {
- sourceTabId: expectedSourceTab.sourceTabId,
- sourceFrameId: 0,
- url: `${OPENED_PAGE}#new-tab-from-context-menu`,
- },
- });
-
- info("Open link in a new window from the context menu");
-
- yield runTestCase({
- extension,
- async openNavTarget() {
- await clickContextMenuItem({
- pageElementSelector: "#test-create-new-window-from-context-menu",
- contextMenuItemLabel: "Open Link in New Window",
- });
- },
- expectedWebNavProps: {
- sourceTabId: expectedSourceTab.sourceTabId,
- sourceFrameId: 0,
- url: `${OPENED_PAGE}#new-window-from-context-menu`,
- },
- });
-
- yield BrowserTestUtils.removeTab(tab);
-
- yield extension.unload();
-});
-
add_task(function* test_on_created_navigation_target_from_mouse_click_subframe() {
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
@@ -256,65 +195,3 @@ add_task(function* test_on_created_navigation_target_from_mouse_click_subframe()
yield extension.unload();
});
-add_task(function* test_on_created_navigation_target_from_context_menu_subframe() {
- const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
-
- const extension = ExtensionTestUtils.loadExtension({
- background,
- manifest: {
- permissions: ["webNavigation"],
- },
- });
-
- yield extension.startup();
-
- const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
-
- info("Open a subframe link in a new tab from the context menu");
-
- yield runTestCase({
- extension,
- async openNavTarget() {
- await clickContextMenuItem({
- pageElementSelector: function() {
- // This code runs as a framescript in the child process and it returns the
- // target link in the subframe.
- return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
- .document.querySelector("#test-create-new-tab-from-context-menu-subframe");
- },
- contextMenuItemLabel: "Open Link in New Tab",
- });
- },
- expectedWebNavProps: {
- sourceTabId: expectedSourceTab.sourceTabId,
- sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
- url: `${OPENED_PAGE}#new-tab-from-context-menu-subframe`,
- },
- });
-
- info("Open a subframe link in a new window from the context menu");
-
- yield runTestCase({
- extension,
- async openNavTarget() {
- await clickContextMenuItem({
- pageElementSelector: function() {
- // This code runs as a framescript in the child process and it returns the
- // target link in the subframe.
- return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
- .document.querySelector("#test-create-new-window-from-context-menu-subframe");
- },
- contextMenuItemLabel: "Open Link in New Window",
- });
- },
- expectedWebNavProps: {
- sourceTabId: expectedSourceTab.sourceTabId,
- sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
- url: `${OPENED_PAGE}#new-window-from-context-menu-subframe`,
- },
- });
-
- yield BrowserTestUtils.removeTab(tab);
-
- yield extension.unload();
-});
diff --git a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js
new file mode 100644
index 000000000000..6dc7896a2950
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js
@@ -0,0 +1,177 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
+const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
+const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
+
+async function background() {
+ const tabs = await browser.tabs.query({active: true, currentWindow: true});
+ const sourceTabId = tabs[0].id;
+
+ const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
+
+ browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
+ browser.test.sendMessage("webNavOnCreated", msg);
+ });
+
+ browser.webNavigation.onCompleted.addListener(async (msg) => {
+ // NOTE: checking the url is currently necessary because of Bug 1252129
+ // ( Filter out webNavigation events related to new window initialization phase).
+ if (msg.tabId !== sourceTabId && msg.url !== "about:blank") {
+ await browser.tabs.remove(msg.tabId);
+ browser.test.sendMessage("webNavOnCompleted", msg);
+ }
+ });
+
+ browser.tabs.onCreated.addListener((tab) => {
+ browser.test.sendMessage("tabsOnCreated", tab.id);
+ });
+
+ browser.test.sendMessage("expectedSourceTab", {
+ sourceTabId, sourceTabFrames,
+ });
+}
+
+async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
+ await openNavTarget();
+
+ const webNavMsg = await extension.awaitMessage("webNavOnCreated");
+ const createdTabId = await extension.awaitMessage("tabsOnCreated");
+ const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
+
+ let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
+
+ is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
+ is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
+ is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
+ is(webNavMsg.url, url, "Got the expected url property");
+
+ is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
+ is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
+}
+
+async function clickContextMenuItem({pageElementSelector, contextMenuItemLabel}) {
+ const contentAreaContextMenu = await openContextMenu(pageElementSelector);
+ const item = contentAreaContextMenu.getElementsByAttribute("label", contextMenuItemLabel);
+ is(item.length, 1, `found contextMenu item for "${contextMenuItemLabel}"`);
+ item[0].click();
+ await closeContextMenu();
+}
+
+add_task(function* test_on_created_navigation_target_from_context_menu() {
+ const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
+
+ const extension = ExtensionTestUtils.loadExtension({
+ background,
+ manifest: {
+ permissions: ["webNavigation"],
+ },
+ });
+
+ yield extension.startup();
+
+ const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
+
+ info("Open link in a new tab from the context menu");
+
+ yield runTestCase({
+ extension,
+ async openNavTarget() {
+ await clickContextMenuItem({
+ pageElementSelector: "#test-create-new-tab-from-context-menu",
+ contextMenuItemLabel: "Open Link in New Tab",
+ });
+ },
+ expectedWebNavProps: {
+ sourceTabId: expectedSourceTab.sourceTabId,
+ sourceFrameId: 0,
+ url: `${OPENED_PAGE}#new-tab-from-context-menu`,
+ },
+ });
+
+ info("Open link in a new window from the context menu");
+
+ yield runTestCase({
+ extension,
+ async openNavTarget() {
+ await clickContextMenuItem({
+ pageElementSelector: "#test-create-new-window-from-context-menu",
+ contextMenuItemLabel: "Open Link in New Window",
+ });
+ },
+ expectedWebNavProps: {
+ sourceTabId: expectedSourceTab.sourceTabId,
+ sourceFrameId: 0,
+ url: `${OPENED_PAGE}#new-window-from-context-menu`,
+ },
+ });
+
+ yield BrowserTestUtils.removeTab(tab);
+
+ yield extension.unload();
+});
+
+add_task(function* test_on_created_navigation_target_from_context_menu_subframe() {
+ const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
+
+ const extension = ExtensionTestUtils.loadExtension({
+ background,
+ manifest: {
+ permissions: ["webNavigation"],
+ },
+ });
+
+ yield extension.startup();
+
+ const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
+
+ info("Open a subframe link in a new tab from the context menu");
+
+ yield runTestCase({
+ extension,
+ async openNavTarget() {
+ await clickContextMenuItem({
+ pageElementSelector: function() {
+ // This code runs as a framescript in the child process and it returns the
+ // target link in the subframe.
+ return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
+ .document.querySelector("#test-create-new-tab-from-context-menu-subframe");
+ },
+ contextMenuItemLabel: "Open Link in New Tab",
+ });
+ },
+ expectedWebNavProps: {
+ sourceTabId: expectedSourceTab.sourceTabId,
+ sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
+ url: `${OPENED_PAGE}#new-tab-from-context-menu-subframe`,
+ },
+ });
+
+ info("Open a subframe link in a new window from the context menu");
+
+ yield runTestCase({
+ extension,
+ async openNavTarget() {
+ await clickContextMenuItem({
+ pageElementSelector: function() {
+ // This code runs as a framescript in the child process and it returns the
+ // target link in the subframe.
+ return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
+ .document.querySelector("#test-create-new-window-from-context-menu-subframe");
+ },
+ contextMenuItemLabel: "Open Link in New Window",
+ });
+ },
+ expectedWebNavProps: {
+ sourceTabId: expectedSourceTab.sourceTabId,
+ sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
+ url: `${OPENED_PAGE}#new-window-from-context-menu-subframe`,
+ },
+ });
+
+ yield BrowserTestUtils.removeTab(tab);
+
+ yield extension.unload();
+});
diff --git a/browser/components/feeds/FeedConverter.js b/browser/components/feeds/FeedConverter.js
index fa8719c71e6e..86b7f6c91b67 100644
--- a/browser/components/feeds/FeedConverter.js
+++ b/browser/components/feeds/FeedConverter.js
@@ -223,8 +223,7 @@ FeedConverter.prototype = {
try {
let title = feed.title ? feed.title.plainText() : "";
let desc = feed.subtitle ? feed.subtitle.plainText() : "";
- let feedReader = safeGetCharPref(getPrefActionForType(feed.type), "bookmarks");
- feedService.addToClientReader(result.uri.spec, title, desc, feed.type, feedReader);
+ feedService.addToClientReader(result.uri.spec, title, desc, feed.type, handler);
return;
} catch (ex) { /* fallback to preview mode */ }
}
diff --git a/browser/components/feeds/FeedWriter.js b/browser/components/feeds/FeedWriter.js
index 0bcebb03b814..c58a9b40df93 100644
--- a/browser/components/feeds/FeedWriter.js
+++ b/browser/components/feeds/FeedWriter.js
@@ -44,83 +44,11 @@ function makeURI(aURLSpec, aCharset) {
const XML_NS = "http://www.w3.org/XML/1998/namespace";
const HTML_NS = "http://www.w3.org/1999/xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
-const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
-const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
const URI_BUNDLE = "chrome://browser/locale/feeds/subscribe.properties";
-const PREF_SELECTED_APP = "browser.feeds.handlers.application";
-const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
-const PREF_SELECTED_ACTION = "browser.feeds.handler";
-const PREF_SELECTED_READER = "browser.feeds.handler.default";
-
-const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
-const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
-const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
-const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";
-
-const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
-const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
-const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
-const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";
-
-const PREF_SHOW_FIRST_RUN_UI = "browser.feeds.showFirstRunUI";
-
const TITLE_ID = "feedTitleText";
const SUBTITLE_ID = "feedSubtitleText";
-function getPrefAppForType(t) {
- switch (t) {
- case Ci.nsIFeed.TYPE_VIDEO:
- return PREF_VIDEO_SELECTED_APP;
-
- case Ci.nsIFeed.TYPE_AUDIO:
- return PREF_AUDIO_SELECTED_APP;
-
- default:
- return PREF_SELECTED_APP;
- }
-}
-
-function getPrefWebForType(t) {
- switch (t) {
- case Ci.nsIFeed.TYPE_VIDEO:
- return PREF_VIDEO_SELECTED_WEB;
-
- case Ci.nsIFeed.TYPE_AUDIO:
- return PREF_AUDIO_SELECTED_WEB;
-
- default:
- return PREF_SELECTED_WEB;
- }
-}
-
-function getPrefActionForType(t) {
- switch (t) {
- case Ci.nsIFeed.TYPE_VIDEO:
- return PREF_VIDEO_SELECTED_ACTION;
-
- case Ci.nsIFeed.TYPE_AUDIO:
- return PREF_AUDIO_SELECTED_ACTION;
-
- default:
- return PREF_SELECTED_ACTION;
- }
-}
-
-function getPrefReaderForType(t) {
- switch (t) {
- case Ci.nsIFeed.TYPE_VIDEO:
- return PREF_VIDEO_SELECTED_READER;
-
- case Ci.nsIFeed.TYPE_AUDIO:
- return PREF_AUDIO_SELECTED_READER;
-
- default:
- return PREF_SELECTED_READER;
- }
-}
-
/**
* Converts a number of bytes to the appropriate unit that results in a
* number that needs fewer than 4 digits
@@ -153,9 +81,6 @@ function FeedWriter() {
}
FeedWriter.prototype = {
- _mimeSvc : Cc["@mozilla.org/mime;1"].
- getService(Ci.nsIMIMEService),
-
_getPropertyAsBag(container, property) {
return container.fields.getProperty(property).
QueryInterface(Ci.nsIPropertyBag2);
@@ -226,18 +151,21 @@ FeedWriter.prototype = {
return this._bundle.GetStringFromName(key);
},
- _setCheckboxCheckedState(aCheckbox, aValue) {
- // see checkbox.xml, xbl bindings are not applied within the sandbox! TODO
- let change = (aValue != (aCheckbox.getAttribute("checked") == "true"));
- if (aValue)
- aCheckbox.setAttribute("checked", "true");
- else
- aCheckbox.removeAttribute("checked");
+ _setCheckboxCheckedState(aValue) {
+ let checkbox = this._document.getElementById("alwaysUse");
+ if (checkbox) {
+ // see checkbox.xml, xbl bindings are not applied within the sandbox! TODO
+ let change = (aValue != (checkbox.getAttribute("checked") == "true"));
+ if (aValue)
+ checkbox.setAttribute("checked", "true");
+ else
+ checkbox.removeAttribute("checked");
- if (change) {
- let event = this._document.createEvent("Events");
- event.initEvent("CheckboxStateChange", true, true);
- aCheckbox.dispatchEvent(event);
+ if (change) {
+ let event = this._document.createEvent("Events");
+ event.initEvent("CheckboxStateChange", true, true);
+ checkbox.dispatchEvent(event);
+ }
}
},
@@ -290,22 +218,6 @@ FeedWriter.prototype = {
return Ci.nsIFeed.TYPE_FEED;
},
- /**
- * Maps a feed type to a maybe-feed mimetype.
- */
- _getMimeTypeForFeedType() {
- switch (this._getFeedType()) {
- case Ci.nsIFeed.TYPE_VIDEO:
- return TYPE_MAYBE_VIDEO_FEED;
-
- case Ci.nsIFeed.TYPE_AUDIO:
- return TYPE_MAYBE_AUDIO_FEED;
-
- default:
- return TYPE_MAYBE_FEED;
- }
- },
-
/**
* Writes the feed title into the preview document.
* @param container
@@ -491,17 +403,11 @@ FeedWriter.prototype = {
if (enc.hasKey("type")) {
type_text = enc.get("type");
- try {
- let handlerInfoWrapper = this._mimeSvc.getFromTypeAndExtension(enc.get("type"), null);
-
- if (handlerInfoWrapper)
- type_text = handlerInfoWrapper.description;
-
- if (type_text && type_text.length > 0)
- mozicon = "moz-icon://goat?size=16&contentType=" + enc.get("type");
-
- } catch (ex) { }
+ if (enc.hasKey("typeDesc"))
+ type_text = enc.get("typeDesc");
+ if (type_text && type_text.length > 0)
+ mozicon = "moz-icon://goat?size=16&contentType=" + enc.get("type");
}
if (enc.hasKey("length") && /^[0-9]+$/.test(enc.get("length"))) {
@@ -596,19 +502,7 @@ FeedWriter.prototype = {
this._subscribeCallback = aCallback;
this._mm.sendAsyncMessage("FeedWriter:ChooseClientApp",
{ title: this._getString("chooseApplicationDialogTitle"),
- prefName: getPrefAppForType(this._getFeedType()) });
- },
-
- _setAlwaysUseCheckedState(feedType) {
- let checkbox = this._document.getElementById("alwaysUse");
- if (checkbox) {
- let alwaysUse = false;
- try {
- if (Services.prefs.getCharPref(getPrefActionForType(feedType)) != "ask")
- alwaysUse = true;
- } catch (ex) { }
- this._setCheckboxCheckedState(checkbox, alwaysUse);
- }
+ feedType: this._getFeedType() });
},
_setSubscribeUsingLabel() {
@@ -671,13 +565,13 @@ FeedWriter.prototype = {
}
break;
case "change":
+ LOG("Change fired");
if (event.target.selectedOptions[0].id == "chooseApplicationMenuItem") {
- this._chooseClientApp((aResult) => {
- if (!aResult) {
- // Select the (per-prefs) selected handler if no application
- // was selected
- this._setSelectedHandler(this._getFeedType());
- }
+ this._chooseClientApp(() => {
+ // Select the (per-prefs) selected handler if no application
+ // was selected
+ LOG("Selected handler after callback");
+ this._setAlwaysUseLabel();
});
} else {
this._setAlwaysUseLabel();
@@ -687,28 +581,18 @@ FeedWriter.prototype = {
},
_getWebHandlerElementsForURL(aURL) {
- let menu = this._document.getElementById("handlersMenuList");
- return menu.querySelectorAll('[webhandlerurl="' + aURL + '"]');
+ return this._handlersList.querySelectorAll('[webhandlerurl="' + aURL + '"]');
},
- _setSelectedHandler(feedType) {
- let prefs = Services.prefs;
- let handler = prefs.getCharPref(getPrefReaderForType(feedType), "bookmarks");
-
+ _setSelectedHandlerResponse(handler, url) {
+ LOG(`Selecting handler response ${handler} ${url}`);
switch (handler) {
case "web": {
if (this._handlersList) {
- let url;
- try {
- url = prefs.getStringPref(getPrefWebForType(feedType));
- } catch (ex) {
- LOG("FeedWriter._setSelectedHandler: invalid or no handler in prefs");
- return;
- }
let handlers =
this._getWebHandlerElementsForURL(url);
if (handlers.length == 0) {
- LOG("FeedWriter._setSelectedHandler: selected web handler isn't in the menulist")
+ LOG(`Selected web handler isn't in the menulist ${url}`);
return;
}
@@ -729,10 +613,10 @@ FeedWriter.prototype = {
}
},
- _initSubscriptionUI() {
- let handlersList = this._document.getElementById("handlersMenuList");
- if (!handlersList)
+ _initSubscriptionUI(setupMessage) {
+ if (!this._handlersList)
return;
+ LOG("UI init");
let feedType = this._getFeedType();
@@ -763,7 +647,7 @@ FeedWriter.prototype = {
menuItem.style.display = "none";
this._selectedAppMenuItem = menuItem;
- handlersList.appendChild(this._selectedAppMenuItem);
+ this._handlersList.appendChild(this._selectedAppMenuItem);
// Create the menuitem for the default reader, but don't show/populate it until
// we get confirmation of what it is from the parent
@@ -774,10 +658,7 @@ FeedWriter.prototype = {
menuItem.style.display = "none";
this._defaultHandlerMenuItem = menuItem;
- handlersList.appendChild(this._defaultHandlerMenuItem);
-
- this._mm.sendAsyncMessage("FeedWriter:RequestClientAppName",
- { feedTypePref: getPrefAppForType(feedType) });
+ this._handlersList.appendChild(this._defaultHandlerMenuItem);
// "Choose Application..." menuitem
menuItem = liveBookmarksMenuItem.cloneNode(false);
@@ -785,18 +666,14 @@ FeedWriter.prototype = {
menuItem.setAttribute("id", "chooseApplicationMenuItem");
menuItem.textContent = this._getString("chooseApplicationMenuItem");
- handlersList.appendChild(menuItem);
+ this._handlersList.appendChild(menuItem);
// separator
let chooseAppSep = liveBookmarksMenuItem.nextElementSibling.cloneNode(false);
chooseAppSep.textContent = liveBookmarksMenuItem.nextElementSibling.textContent;
- handlersList.appendChild(chooseAppSep);
+ this._handlersList.appendChild(chooseAppSep);
- // List of web handlers
- let wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
- getService(Ci.nsIWebContentConverterService);
- let handlers = wccr.getContentHandlers(this._getMimeTypeForFeedType(feedType));
- for (let handler of handlers) {
+ for (let handler of setupMessage.handlers) {
if (!handler.uri) {
LOG("Handler with name " + handler.name + " has no URI!? Skipping...");
continue;
@@ -807,29 +684,37 @@ FeedWriter.prototype = {
menuItem.textContent = handler.name;
menuItem.setAttribute("handlerType", "web");
menuItem.setAttribute("webhandlerurl", handler.uri);
- handlersList.appendChild(menuItem);
+ this._handlersList.appendChild(menuItem);
}
- this._setSelectedHandler(feedType);
+ this._setSelectedHandlerResponse(setupMessage.reader.handler, setupMessage.reader.url);
+
+ if (setupMessage.defaultMenuItem) {
+ LOG(`Setting default menu item ${setupMessage.defaultMenuItem}`);
+ this._setApplicationLauncherMenuItem(this._defaultHandlerMenuItem, setupMessage.defaultMenuItem);
+ }
+ if (setupMessage.selectedMenuItem) {
+ LOG(`Setting selected menu item ${setupMessage.selectedMenuItem}`);
+ this._setApplicationLauncherMenuItem(this._selectedAppMenuItem, setupMessage.selectedMenuItem);
+ }
// "Subscribe using..."
this._setSubscribeUsingLabel();
// "Always use..." checkbox initial state
- this._setAlwaysUseCheckedState(feedType);
+ this._setCheckboxCheckedState(setupMessage.reader.alwaysUse);
this._setAlwaysUseLabel();
// We update the "Always use.." checkbox label whenever the selected item
// in the list is changed
- handlersList.addEventListener("change", this);
+ this._handlersList.addEventListener("change", this);
// Set up the "Subscribe Now" button
this._document.getElementById("subscribeButton")
.addEventListener("click", this);
// first-run ui
- let showFirstRunUI = Services.prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI, true);
- if (showFirstRunUI) {
+ if (setupMessage.showFirstRunUI) {
let textfeedinfo1, textfeedinfo2;
switch (feedType) {
case Ci.nsIFeed.TYPE_VIDEO:
@@ -915,53 +800,74 @@ FeedWriter.prototype = {
LOG("Subscribe Preview: feed uri = " + this._window.location.href);
- // Set up the subscription UI
- this._initSubscriptionUI();
- let prefs = Services.prefs;
- prefs.addObserver(PREF_SELECTED_ACTION, this, false);
- prefs.addObserver(PREF_SELECTED_READER, this, false);
- prefs.addObserver(PREF_SELECTED_WEB, this, false);
- prefs.addObserver(PREF_SELECTED_APP, this, false);
- prefs.addObserver(PREF_VIDEO_SELECTED_ACTION, this, false);
- prefs.addObserver(PREF_VIDEO_SELECTED_READER, this, false);
- prefs.addObserver(PREF_VIDEO_SELECTED_WEB, this, false);
- prefs.addObserver(PREF_VIDEO_SELECTED_APP, this, false);
-
- prefs.addObserver(PREF_AUDIO_SELECTED_ACTION, this, false);
- prefs.addObserver(PREF_AUDIO_SELECTED_READER, this, false);
- prefs.addObserver(PREF_AUDIO_SELECTED_WEB, this, false);
- prefs.addObserver(PREF_AUDIO_SELECTED_APP, this, false);
+ this._mm.addMessageListener("FeedWriter:PreferenceUpdated", this);
this._mm.addMessageListener("FeedWriter:SetApplicationLauncherMenuItem", this);
+ this._mm.addMessageListener("FeedWriter:GetSubscriptionUIResponse", this);
+ this._mm.addMessageListener("FeedWriter:SetFeedPrefsAndSubscribeResponse", this);
+
+ const feedType = this._getFeedType();
+ this._mm.sendAsyncMessage("FeedWriter:GetSubscriptionUI",
+ { feedType });
},
receiveMessage(msg) {
+ if (!this._window) {
+ // this._window is null unless this.init was called with a trusted
+ // window object.
+ return;
+ }
+ LOG(`received message from parent ${msg.name}`);
switch (msg.name) {
- case "FeedWriter:SetApplicationLauncherMenuItem":
- let menuItem = null;
-
- if (msg.data.type == "DefaultAppMenuItem") {
- menuItem = this._defaultHandlerMenuItem;
- } else {
- // Most likely SelectedAppMenuItem
- menuItem = this._selectedAppMenuItem;
+ case "FeedWriter:PreferenceUpdated":
+ // This is called when browser-feeds.js spots a pref change
+ // This will happen when
+ // - about:preferences#applications changes
+ // - another feed reader page changes the preference
+ // - when this page itself changes the select and there isn't a redirect
+ // bookmarks and launching an external app means the page stays open after subscribe
+ const feedType = this._getFeedType();
+ LOG(`Got prefChange! ${JSON.stringify(msg.data)} current type: ${feedType}`);
+ let feedTypePref = msg.data.default;
+ if (feedType in msg.data) {
+ feedTypePref = msg.data[feedType];
}
-
- menuItem.textContent = msg.data.name;
- menuItem.style.display = "";
- menuItem.selected = true;
-
+ LOG(`Got pref ${JSON.stringify(feedTypePref)}`);
+ this._setCheckboxCheckedState(feedTypePref.alwaysUse);
+ this._setSelectedHandlerResponse(feedTypePref.handler, feedTypePref.url);
+ this._setAlwaysUseLabel();
+ break;
+ case "FeedWriter:SetFeedPrefsAndSubscribeResponse":
+ LOG(`FeedWriter:SetFeedPrefsAndSubscribeResponse - Redirecting ${msg.data.redirect}`);
+ this._window.location.href = msg.data.redirect;
+ break;
+ case "FeedWriter:GetSubscriptionUIResponse":
+ // Set up the subscription UI
+ this._initSubscriptionUI(msg.data);
+ break;
+ case "FeedWriter:SetApplicationLauncherMenuItem":
+ LOG(`FeedWriter:SetApplicationLauncherMenuItem - picked ${msg.data.name}`);
+ this._setApplicationLauncherMenuItem(this._selectedAppMenuItem, msg.data.name);
// Potentially a bit racy, but I don't think we can get into a state where this callback is set and
// we're not coming back from ChooseClientApp in browser-feeds.js
if (this._subscribeCallback) {
this._subscribeCallback();
this._subscribeCallback = null;
}
-
break;
}
},
+ _setApplicationLauncherMenuItem(menuItem, aName) {
+ /* unselect all handlers */
+ [...this._handlersList.children].forEach((option) => {
+ option.removeAttribute("selected");
+ });
+ menuItem.textContent = aName;
+ menuItem.style.display = "";
+ menuItem.selected = true;
+ },
+
writeContent() {
if (!this._window)
return;
@@ -983,24 +889,11 @@ FeedWriter.prototype = {
close() {
this._document.getElementById("subscribeButton")
.removeEventListener("click", this);
- this._document.getElementById("handlersMenuList")
+ this._handlersList
.removeEventListener("change", this);
this._document = null;
this._window = null;
- let prefs = Services.prefs;
- prefs.removeObserver(PREF_SELECTED_ACTION, this);
- prefs.removeObserver(PREF_SELECTED_READER, this);
- prefs.removeObserver(PREF_SELECTED_WEB, this);
- prefs.removeObserver(PREF_SELECTED_APP, this);
- prefs.removeObserver(PREF_VIDEO_SELECTED_ACTION, this);
- prefs.removeObserver(PREF_VIDEO_SELECTED_READER, this);
- prefs.removeObserver(PREF_VIDEO_SELECTED_WEB, this);
- prefs.removeObserver(PREF_VIDEO_SELECTED_APP, this);
-
- prefs.removeObserver(PREF_AUDIO_SELECTED_ACTION, this);
- prefs.removeObserver(PREF_AUDIO_SELECTED_READER, this);
- prefs.removeObserver(PREF_AUDIO_SELECTED_WEB, this);
- prefs.removeObserver(PREF_AUDIO_SELECTED_APP, this);
+ this._handlersList = null;
this._removeFeedFromCache();
this.__bundle = null;
@@ -1020,20 +913,6 @@ FeedWriter.prototype = {
}
},
- setFeedCharPref(aPrefName, aPrefValue) {
- this._mm.sendAsyncMessage("FeedWriter:SetFeedCharPref",
- { pref: aPrefName,
- value: aPrefValue });
- },
-
- setFeedComplexString(aPrefName, aPrefValue) {
- // This sends the string data across to the parent, which will use it in an nsISupportsString
- // for a complex value pref.
- this._mm.sendAsyncMessage("FeedWriter:SetFeedComplexString",
- { pref: aPrefName,
- value: aPrefValue });
- },
-
subscribe() {
let feedType = this._getFeedType();
@@ -1041,26 +920,21 @@ FeedWriter.prototype = {
let defaultHandler = "reader";
let useAsDefault = this._document.getElementById("alwaysUse").getAttribute("checked");
- let menuList = this._document.getElementById("handlersMenuList");
- let selectedItem = menuList.selectedOptions[0];
+ let selectedItem = this._handlersList.selectedOptions[0];
let subscribeCallback = () => {
+ let feedReader = null;
+ let settings = {
+ feedType,
+ useAsDefault,
+ // Pull the title and subtitle out of the document
+ feedTitle: this._document.getElementById(TITLE_ID).textContent,
+ feedSubtitle: this._document.getElementById(SUBTITLE_ID).textContent,
+ feedLocation: this._window.location.href
+ };
if (selectedItem.hasAttribute("webhandlerurl")) {
- let webURI = selectedItem.getAttribute("webhandlerurl");
- this.setFeedCharPref(getPrefReaderForType(feedType), "web");
- this.setFeedComplexString(getPrefWebForType(feedType), webURI);
-
- let wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
- getService(Ci.nsIWebContentConverterService);
- let handler = wccr.getWebContentHandlerByURI(this._getMimeTypeForFeedType(feedType), webURI);
- if (handler) {
- if (useAsDefault) {
- wccr.setAutoHandler(this._getMimeTypeForFeedType(feedType), handler);
- }
-
- this._window.location.href = handler.getHandlerURI(this._window.location.href);
- }
+ feedReader = "web";
+ settings.uri = selectedItem.getAttribute("webhandlerurl");
} else {
- let feedReader = null;
switch (selectedItem.id) {
case "selectedAppMenuItem":
feedReader = "client";
@@ -1073,27 +947,20 @@ FeedWriter.prototype = {
feedReader = "bookmarks";
break;
}
-
- this.setFeedCharPref(getPrefReaderForType(feedType), feedReader);
-
- let feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
- getService(Ci.nsIFeedResultService);
-
- // Pull the title and subtitle out of the document
- let feedTitle = this._document.getElementById(TITLE_ID).textContent;
- let feedSubtitle = this._document.getElementById(SUBTITLE_ID).textContent;
- feedService.addToClientReader(this._window.location.href, feedTitle, feedSubtitle, feedType, feedReader);
}
+ settings.reader = feedReader;
// If "Always use..." is checked, we should set PREF_*SELECTED_ACTION
// to either "reader" (If a web reader or if an application is selected),
// or to "bookmarks" (if the live bookmarks option is selected).
// Otherwise, we should set it to "ask"
- if (useAsDefault) {
- this.setFeedCharPref(getPrefActionForType(feedType), defaultHandler);
- } else {
- this.setFeedCharPref(getPrefActionForType(feedType), "ask");
+ if (!useAsDefault) {
+ defaultHandler = "ask";
}
+ settings.action = defaultHandler;
+ LOG(`FeedWriter:SetFeedPrefsAndSubscribe - ${JSON.stringify(settings)}`);
+ this._mm.sendAsyncMessage("FeedWriter:SetFeedPrefsAndSubscribe",
+ settings);
}
// Show the file picker before subscribing if the
@@ -1111,37 +978,6 @@ FeedWriter.prototype = {
}
},
- // nsIObserver
- observe(subject, topic, data) {
- if (!this._window) {
- // this._window is null unless this.init was called with a trusted
- // window object.
- return;
- }
-
- let feedType = this._getFeedType();
-
- if (topic == "nsPref:changed") {
- switch (data) {
- case PREF_SELECTED_READER:
- case PREF_SELECTED_WEB:
- case PREF_SELECTED_APP:
- case PREF_VIDEO_SELECTED_READER:
- case PREF_VIDEO_SELECTED_WEB:
- case PREF_VIDEO_SELECTED_APP:
- case PREF_AUDIO_SELECTED_READER:
- case PREF_AUDIO_SELECTED_WEB:
- case PREF_AUDIO_SELECTED_APP:
- this._setSelectedHandler(feedType);
- break;
- case PREF_SELECTED_ACTION:
- case PREF_VIDEO_SELECTED_ACTION:
- case PREF_AUDIO_SELECTED_ACTION:
- this._setAlwaysUseCheckedState(feedType);
- }
- }
- },
-
get _mm() {
let mm = this._window.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDocShell).
diff --git a/browser/components/migration/content/aboutWelcomeBack.xhtml b/browser/components/migration/content/aboutWelcomeBack.xhtml
index 058a329755f4..d4e4b120d7ef 100644
--- a/browser/components/migration/content/aboutWelcomeBack.xhtml
+++ b/browser/components/migration/content/aboutWelcomeBack.xhtml
@@ -20,7 +20,7 @@
&welcomeback2.tabtitle;
-
+
diff --git a/browser/extensions/e10srollout/bootstrap.js b/browser/extensions/e10srollout/bootstrap.js
index 2d6e1a32927f..13f2bd5b7ffe 100644
--- a/browser/extensions/e10srollout/bootstrap.js
+++ b/browser/extensions/e10srollout/bootstrap.js
@@ -19,7 +19,7 @@ const TEST_THRESHOLD = {
const ADDON_ROLLOUT_POLICY = {
"beta" : "51alladdons", // Any WebExtension or addon except with mpc = false
- "release" : "51set1",
+ "release" : "50allmpc",
"esr" : "esrA", // WebExtensions and Addons with mpc=true
};
@@ -73,7 +73,10 @@ function defineCohort() {
Preferences.set(PREF_E10S_ADDON_BLOCKLIST,
// bug 1185672 - Tab Mix Plus
- "{dc572301-7619-498c-a57d-39143191b318};");
+ "{dc572301-7619-498c-a57d-39143191b318};"
+ // bug 1344345 - Mega
+ + "firefox@mega.co.nz"
+ );
} else {
Preferences.reset(PREF_E10S_ADDON_POLICY);
}
diff --git a/browser/extensions/e10srollout/install.rdf.in b/browser/extensions/e10srollout/install.rdf.in
index d76caf9c00e6..ac4f80dc7ac6 100644
--- a/browser/extensions/e10srollout/install.rdf.in
+++ b/browser/extensions/e10srollout/install.rdf.in
@@ -10,7 +10,7 @@
e10srollout@mozilla.org
- 1.11
+ 1.12
2
true
true
diff --git a/browser/locales/en-US/chrome/browser/safebrowsing/safebrowsing.properties b/browser/locales/en-US/chrome/browser/safebrowsing/safebrowsing.properties
new file mode 100644
index 000000000000..aa72ddbffd14
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/safebrowsing/safebrowsing.properties
@@ -0,0 +1,6 @@
+# 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/.
+
+errorReportFalseDeceptiveTitle=This isn’t a deceptive site
+errorReportFalseDeceptiveMessage=It’s not possible to report this error at this time.
diff --git a/browser/locales/jar.mn b/browser/locales/jar.mn
index 35fc613f4536..b1edf111714f 100644
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -54,6 +54,7 @@
locale/browser/places/moveBookmarks.dtd (%chrome/browser/places/moveBookmarks.dtd)
locale/browser/safebrowsing/phishing-afterload-warning-message.dtd (%chrome/browser/safebrowsing/phishing-afterload-warning-message.dtd)
locale/browser/safebrowsing/report-phishing.dtd (%chrome/browser/safebrowsing/report-phishing.dtd)
+ locale/browser/safebrowsing/safebrowsing.properties (%chrome/browser/safebrowsing/safebrowsing.properties)
locale/browser/feeds/subscribe.dtd (%chrome/browser/feeds/subscribe.dtd)
locale/browser/feeds/subscribe.properties (%chrome/browser/feeds/subscribe.properties)
locale/browser/migration/migration.dtd (%chrome/browser/migration/migration.dtd)
diff --git a/browser/themes/shared/aboutProviderDirectory.css b/browser/themes/shared/aboutProviderDirectory.css
index 73e570aada4b..5b8b08dbca10 100644
--- a/browser/themes/shared/aboutProviderDirectory.css
+++ b/browser/themes/shared/aboutProviderDirectory.css
@@ -7,7 +7,7 @@ body {
#message-box {
margin-top: 2em;
- background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
+ background: url(chrome://browser/skin/info.svg) no-repeat left 8px;
padding-inline-start: 30px;
}
diff --git a/build/pgo/certs/README b/build/pgo/certs/README
index ba2b346f9ad9..a005ec95ebd2 100644
--- a/build/pgo/certs/README
+++ b/build/pgo/certs/README
@@ -1,9 +1,9 @@
The certificate authority and server certificates here are generated by $topsrcdir/build/pgo/genpgocert.py.
You can generate a new CA cert by running:
-$objdir/_virtualenv/bin/python $topsrcdir/build/pgo/genpgocert.py --gen-ca
+./mach python build/pgo/genpgocert.py --gen-ca
You can generate new server certificates by running:
-$objdir/_virtualenv/bin/python $topsrcdir/build/pgo/genpgocert.py --gen-server
+./mach python build/pgo/genpgocert.py --gen-server
-These will place the new files in this directory where you can commit them.
+These commands will modify cert8.db and key3.db. The changes to these should be committed.
diff --git a/caps/BasePrincipal.cpp b/caps/BasePrincipal.cpp
index b00ce460c26b..466e4994cd0b 100644
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -442,6 +442,21 @@ BasePrincipal::GetCsp(nsIContentSecurityPolicy** aCsp)
return NS_OK;
}
+NS_IMETHODIMP
+BasePrincipal::SetCsp(nsIContentSecurityPolicy* aCsp)
+{
+ // Never destroy an existing CSP on the principal.
+ // This method should only be called in rare cases.
+
+ MOZ_ASSERT(!mCSP, "do not destroy an existing CSP");
+ if (mCSP) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+
+ mCSP = aCsp;
+ return NS_OK;
+}
+
NS_IMETHODIMP
BasePrincipal::EnsureCSP(nsIDOMDocument* aDocument,
nsIContentSecurityPolicy** aCSP)
diff --git a/caps/BasePrincipal.h b/caps/BasePrincipal.h
index 7391e8b468bf..537b1a8e51e9 100644
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -222,6 +222,7 @@ public:
NS_IMETHOD SubsumesConsideringDomainIgnoringFPD(nsIPrincipal* other, bool* _retval) final;
NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal) final;
NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
+ NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
NS_IMETHOD EnsureCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
NS_IMETHOD GetPreloadCsp(nsIContentSecurityPolicy** aPreloadCSP) override;
NS_IMETHOD EnsurePreloadCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
diff --git a/caps/nsIPrincipal.idl b/caps/nsIPrincipal.idl
index 16aceafeff7d..870792527813 100644
--- a/caps/nsIPrincipal.idl
+++ b/caps/nsIPrincipal.idl
@@ -151,10 +151,11 @@ interface nsIPrincipal : nsISerializable
/**
* A Content Security Policy associated with this principal.
- *
* Use this function to query the associated CSP with this principal.
+ * Please *only* use this function to *set* a CSP when you know exactly what you are doing.
+ * Most likely you want to call ensureCSP instead of setCSP.
*/
- [noscript] readonly attribute nsIContentSecurityPolicy csp;
+ [noscript] attribute nsIContentSecurityPolicy csp;
/*
* Use this function to query a CSP associated with this principal.
diff --git a/caps/nsJSPrincipals.cpp b/caps/nsJSPrincipals.cpp
index ca998bc99597..6a69d5b12b60 100644
--- a/caps/nsJSPrincipals.cpp
+++ b/caps/nsJSPrincipals.cpp
@@ -135,7 +135,10 @@ ReadSuffixAndSpec(JSStructuredCloneReader* aReader,
}
nsAutoCString suffix;
- suffix.SetLength(suffixLength);
+ if (!suffix.SetLength(suffixLength, fallible)) {
+ return false;
+ }
+
if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
return false;
}
@@ -144,7 +147,10 @@ ReadSuffixAndSpec(JSStructuredCloneReader* aReader,
return false;
}
- aSpec.SetLength(specLength);
+ if (!aSpec.SetLength(specLength, fallible)) {
+ return false;
+ }
+
if (!JS_ReadBytes(aReader, aSpec.BeginWriting(), specLength)) {
return false;
}
@@ -195,7 +201,7 @@ ReadPrincipalInfo(JSStructuredCloneReader* aReader,
return false;
}
- aInfo = ContentPrincipalInfo(attrs, spec);
+ aInfo = ContentPrincipalInfo(attrs, void_t(), spec);
} else {
MOZ_CRASH("unexpected principal structured clone tag");
}
diff --git a/caps/nsNullPrincipal.cpp b/caps/nsNullPrincipal.cpp
index 8ab0bd27c91a..fe846ac25b0c 100644
--- a/caps/nsNullPrincipal.cpp
+++ b/caps/nsNullPrincipal.cpp
@@ -108,6 +108,20 @@ nsNullPrincipal::GetHashValue(uint32_t *aResult)
return NS_OK;
}
+NS_IMETHODIMP
+nsNullPrincipal::SetCsp(nsIContentSecurityPolicy* aCsp) {
+ // Never destroy an existing CSP on the principal.
+ // This method should only be called in rare cases.
+
+ MOZ_ASSERT(!mCSP, "do not destroy an existing CSP");
+ if (mCSP) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+
+ mCSP = aCsp;
+ return NS_OK;
+}
+
NS_IMETHODIMP
nsNullPrincipal::GetURI(nsIURI** aURI)
{
diff --git a/caps/nsNullPrincipal.h b/caps/nsNullPrincipal.h
index ad3345d74741..653029258f4b 100644
--- a/caps/nsNullPrincipal.h
+++ b/caps/nsNullPrincipal.h
@@ -45,6 +45,7 @@ public:
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
+ NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
NS_IMETHOD GetURI(nsIURI** aURI) override;
NS_IMETHOD GetDomain(nsIURI** aDomain) override;
NS_IMETHOD SetDomain(nsIURI* aDomain) override;
diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp
index 2135af2da087..a3874ab22ce0 100644
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -269,10 +269,68 @@ nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel,
nsIPrincipal** aPrincipal,
bool aIgnoreSandboxing)
{
- NS_PRECONDITION(aChannel, "Must have channel!");
- // Check whether we have an nsILoadInfo that says what we should do.
- nsCOMPtr loadInfo = aChannel->GetLoadInfo();
- if (loadInfo && loadInfo->GetForceInheritPrincipalOverruleOwner()) {
+ NS_PRECONDITION(aChannel, "Must have channel!");
+ // Check whether we have an nsILoadInfo that says what we should do.
+ nsCOMPtr loadInfo = aChannel->GetLoadInfo();
+ if (loadInfo && loadInfo->GetForceInheritPrincipalOverruleOwner()) {
+ nsCOMPtr principalToInherit = loadInfo->PrincipalToInherit();
+ if (!principalToInherit) {
+ principalToInherit = loadInfo->TriggeringPrincipal();
+ }
+ principalToInherit.forget(aPrincipal);
+ return NS_OK;
+ }
+
+ nsCOMPtr owner;
+ aChannel->GetOwner(getter_AddRefs(owner));
+ if (owner) {
+ CallQueryInterface(owner, aPrincipal);
+ if (*aPrincipal) {
+ return NS_OK;
+ }
+ }
+
+ if (loadInfo) {
+ if (!aIgnoreSandboxing && loadInfo->GetLoadingSandboxed()) {
+ MOZ_ALWAYS_TRUE(NS_SUCCEEDED(loadInfo->GetSandboxedLoadingPrincipal(aPrincipal)));
+ MOZ_ASSERT(*aPrincipal);
+ // if the new NullPrincipal (above) loads an iframe[srcdoc], we
+ // need to inherit an existing CSP to avoid bypasses (bug 1073952).
+ // We continue inheriting for nested frames with e.g., data: URLs.
+ if (loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_SUBDOCUMENT) {
+ nsCOMPtr uri;
+ aChannel->GetURI(getter_AddRefs(uri));
+ nsAutoCString URISpec;
+ uri->GetSpec(URISpec);
+ bool isData = (NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData);
+ if (URISpec.EqualsLiteral("about:srcdoc") || isData) {
+ nsCOMPtr principalToInherit = loadInfo->PrincipalToInherit();
+ if (!principalToInherit) {
+ principalToInherit = loadInfo->TriggeringPrincipal();
+ }
+ nsCOMPtr originalCsp;
+ principalToInherit->GetCsp(getter_AddRefs(originalCsp));
+ // if the principalToInherit had a CSP,
+ // add it to the newly created NullPrincipal.
+ if (originalCsp) {
+ nsresult rv = (*aPrincipal)->SetCsp(originalCsp);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ }
+ return NS_OK;
+ }
+
+ bool forceInherit = loadInfo->GetForceInheritPrincipal();
+ if (aIgnoreSandboxing && !forceInherit) {
+ // Check if SEC_FORCE_INHERIT_PRINCIPAL was dropped because of
+ // sandboxing:
+ if (loadInfo->GetLoadingSandboxed() &&
+ loadInfo->GetForceInheritPrincipalDropped()) {
+ forceInherit = true;
+ }
+ }
+ if (forceInherit) {
nsCOMPtr principalToInherit = loadInfo->PrincipalToInherit();
if (!principalToInherit) {
principalToInherit = loadInfo->TriggeringPrincipal();
@@ -281,67 +339,33 @@ nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel,
return NS_OK;
}
- nsCOMPtr owner;
- aChannel->GetOwner(getter_AddRefs(owner));
- if (owner) {
- CallQueryInterface(owner, aPrincipal);
- if (*aPrincipal) {
- return NS_OK;
- }
+ nsSecurityFlags securityFlags = loadInfo->GetSecurityMode();
+ // The data: inheritance flags should only apply to the initial load,
+ // not to loads that it might have redirected to.
+ if (loadInfo->RedirectChain().IsEmpty() &&
+ (securityFlags == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS ||
+ securityFlags == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS ||
+ securityFlags == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS)) {
+
+ nsCOMPtr uri;
+ nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr principalToInherit = loadInfo->PrincipalToInherit();
+ if (!principalToInherit) {
+ principalToInherit = loadInfo->TriggeringPrincipal();
+ }
+ bool inheritForAboutBlank = loadInfo->GetAboutBlankInherits();
+
+ if (nsContentUtils::ChannelShouldInheritPrincipal(principalToInherit,
+ uri,
+ inheritForAboutBlank,
+ false)) {
+ principalToInherit.forget(aPrincipal);
+ return NS_OK;
+ }
}
-
- if (loadInfo) {
- if (!aIgnoreSandboxing && loadInfo->GetLoadingSandboxed()) {
- MOZ_ALWAYS_TRUE(NS_SUCCEEDED(loadInfo->GetSandboxedLoadingPrincipal(aPrincipal)));
- MOZ_ASSERT(*aPrincipal);
- return NS_OK;
- }
-
- bool forceInherit = loadInfo->GetForceInheritPrincipal();
- if (aIgnoreSandboxing && !forceInherit) {
- // Check if SEC_FORCE_INHERIT_PRINCIPAL was dropped because of
- // sandboxing:
- if (loadInfo->GetLoadingSandboxed() &&
- loadInfo->GetForceInheritPrincipalDropped()) {
- forceInherit = true;
- }
- }
- if (forceInherit) {
- nsCOMPtr principalToInherit = loadInfo->PrincipalToInherit();
- if (!principalToInherit) {
- principalToInherit = loadInfo->TriggeringPrincipal();
- }
- principalToInherit.forget(aPrincipal);
- return NS_OK;
- }
-
- nsSecurityFlags securityFlags = loadInfo->GetSecurityMode();
- // The data: inheritance flags should only apply to the initial load,
- // not to loads that it might have redirected to.
- if (loadInfo->RedirectChain().IsEmpty() &&
- (securityFlags == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS ||
- securityFlags == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS ||
- securityFlags == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS)) {
-
- nsCOMPtr uri;
- nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr principalToInherit = loadInfo->PrincipalToInherit();
- if (!principalToInherit) {
- principalToInherit = loadInfo->TriggeringPrincipal();
- }
- bool inheritForAboutBlank = loadInfo->GetAboutBlankInherits();
-
- if (nsContentUtils::ChannelShouldInheritPrincipal(principalToInherit,
- uri,
- inheritForAboutBlank,
- false)) {
- principalToInherit.forget(aPrincipal);
- return NS_OK;
- }
- }
- }
- return GetChannelURIPrincipal(aChannel, aPrincipal);
+ }
+ return GetChannelURIPrincipal(aChannel, aPrincipal);
}
/* The principal of the URI that this channel is loading. This is never
diff --git a/caps/nsSystemPrincipal.cpp b/caps/nsSystemPrincipal.cpp
index 94cbeecc29a5..b1c33553ac8d 100644
--- a/caps/nsSystemPrincipal.cpp
+++ b/caps/nsSystemPrincipal.cpp
@@ -57,7 +57,7 @@ nsSystemPrincipal::GetHashValue(uint32_t *result)
return NS_OK;
}
-NS_IMETHODIMP
+NS_IMETHODIMP
nsSystemPrincipal::GetURI(nsIURI** aURI)
{
*aURI = nullptr;
@@ -78,6 +78,15 @@ nsSystemPrincipal::GetCsp(nsIContentSecurityPolicy** aCsp)
return NS_OK;
}
+NS_IMETHODIMP
+nsSystemPrincipal::SetCsp(nsIContentSecurityPolicy* aCsp)
+{
+ // Never destroy an existing CSP on the principal.
+ // This method should only be called in rare cases.
+
+ return NS_ERROR_FAILURE;
+}
+
NS_IMETHODIMP
nsSystemPrincipal::EnsureCSP(nsIDOMDocument* aDocument,
nsIContentSecurityPolicy** aCSP)
diff --git a/caps/nsSystemPrincipal.h b/caps/nsSystemPrincipal.h
index 2dd6a0f58ef1..0e67ce45e490 100644
--- a/caps/nsSystemPrincipal.h
+++ b/caps/nsSystemPrincipal.h
@@ -37,6 +37,7 @@ public:
NS_IMETHOD GetDomain(nsIURI** aDomain) override;
NS_IMETHOD SetDomain(nsIURI* aDomain) override;
NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
+ NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
NS_IMETHOD EnsureCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
NS_IMETHOD GetPreloadCsp(nsIContentSecurityPolicy** aPreloadCSP) override;
NS_IMETHOD EnsurePreloadCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
diff --git a/config/stl-headers b/config/stl-headers
index d4786f1c995a..3d46e8c2d8de 100644
--- a/config/stl-headers
+++ b/config/stl-headers
@@ -34,6 +34,7 @@ ostream
set
stack
string
+thread
type_traits
utility
vector
diff --git a/devtools/client/aboutdebugging/test/browser_tabs.js b/devtools/client/aboutdebugging/test/browser_tabs.js
index e82090607fa5..99e76be5d969 100644
--- a/devtools/client/aboutdebugging/test/browser_tabs.js
+++ b/devtools/client/aboutdebugging/test/browser_tabs.js
@@ -5,12 +5,6 @@
const TAB_URL = "data:text/html,foo";
-add_task(function* setup() {
- yield SpecialPowers.pushPrefEnv({
- set: [["dom.ipc.processCount", 1]]
- });
-});
-
add_task(function* () {
let { tab, document } = yield openAboutDebugging("tabs");
diff --git a/devtools/client/animationinspector/test/browser_animation_spacebar_toggles_animations.js b/devtools/client/animationinspector/test/browser_animation_spacebar_toggles_animations.js
index 799ecc28db78..593dbe685c21 100644
--- a/devtools/client/animationinspector/test/browser_animation_spacebar_toggles_animations.js
+++ b/devtools/client/animationinspector/test/browser_animation_spacebar_toggles_animations.js
@@ -4,12 +4,6 @@
"use strict";
-add_task(function* setup() {
- yield SpecialPowers.pushPrefEnv({
- set: [["dom.ipc.processCount", 1]]
- });
-});
-
// Test that the spacebar key press toggles the toggleAll button state
// when a node with no animation is selected.
// This test doesn't need to test if animations actually pause/resume
diff --git a/devtools/client/framework/test/browser.ini b/devtools/client/framework/test/browser.ini
index b594aec41065..4ed0ba871e7c 100644
--- a/devtools/client/framework/test/browser.ini
+++ b/devtools/client/framework/test/browser.ini
@@ -94,5 +94,8 @@ skip-if = os == "mac" && os_version == "10.8" || os == "win" && os_version == "5
[browser_toolbox_window_title_frame_select.js]
[browser_toolbox_zoom.js]
[browser_two_tabs.js]
-# We want this test to run for mochitest-dt as well, so we include it here:
+# We want these tests to run for mochitest-dt as well, so we include them here:
[../../../../browser/base/content/test/static/browser_parsable_css.js]
+skip-if = debug # no point in running on both opt and debug, and will likely intermittently timeout on debug
+[../../../../browser/base/content/test/static/browser_all_files_referenced.js]
+skip-if = debug # no point in running on both opt and debug, and will likely intermittently timeout on debug
diff --git a/devtools/client/responsive.html/test/browser/browser_toolbox_swap_browsers.js b/devtools/client/responsive.html/test/browser/browser_toolbox_swap_browsers.js
index 8f7afaf010c2..26bc4d1ee714 100644
--- a/devtools/client/responsive.html/test/browser/browser_toolbox_swap_browsers.js
+++ b/devtools/client/responsive.html/test/browser/browser_toolbox_swap_browsers.js
@@ -30,12 +30,6 @@ let checkToolbox = Task.async(function* (tab, location) {
ok(!!gDevTools.getToolbox(target), `Toolbox exists ${location}`);
});
-add_task(function* setup() {
- yield SpecialPowers.pushPrefEnv({
- set: [["dom.ipc.processCount", 1]]
- });
-});
-
add_task(function* () {
let tab = yield addTab(TEST_URL);
diff --git a/devtools/client/shared/components/notification-box.css b/devtools/client/shared/components/notification-box.css
index 1d2444431be0..12870ac4f778 100644
--- a/devtools/client/shared/components/notification-box.css
+++ b/devtools/client/shared/components/notification-box.css
@@ -51,6 +51,7 @@
.notificationbox .messageImage {
display: inline-block;
+ background-size: 16px;
width: 16px;
height: 16px;
margin: 6px;
@@ -59,7 +60,7 @@
/* Default icons for notifications */
.notificationbox .messageImage[data-type="info"] {
- background-image: url("chrome://global/skin/icons/information-16.png");
+ background-image: url("chrome://global/skin/icons/info.svg");
}
.notificationbox .messageImage[data-type="warning"] {
diff --git a/devtools/client/webconsole/test/browser.ini b/devtools/client/webconsole/test/browser.ini
index 5de0c42eedd3..61b135cbdb29 100644
--- a/devtools/client/webconsole/test/browser.ini
+++ b/devtools/client/webconsole/test/browser.ini
@@ -181,7 +181,6 @@ skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32
[browser_console_native_getters.js]
[browser_console_navigation_marker.js]
[browser_console_netlogging.js]
-skip-if = true # Bug 1298364
[browser_console_nsiconsolemessage.js]
[browser_console_optimized_out_vars.js]
[browser_console_private_browsing.js]
@@ -338,6 +337,7 @@ skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
[browser_webconsole_message_node_id.js]
[browser_webconsole_multiline_input.js]
[browser_webconsole_netlogging.js]
+skip-if = true # Bug 1298364
[browser_webconsole_netlogging_basic.js]
[browser_webconsole_netlogging_panel.js]
[browser_webconsole_netlogging_reset_filter.js]
diff --git a/devtools/client/webconsole/test/browser_webconsole_show_subresource_security_errors.js b/devtools/client/webconsole/test/browser_webconsole_show_subresource_security_errors.js
index 43cb96bdc487..2f2238d1fbbb 100644
--- a/devtools/client/webconsole/test/browser_webconsole_show_subresource_security_errors.js
+++ b/devtools/client/webconsole/test/browser_webconsole_show_subresource_security_errors.js
@@ -13,12 +13,6 @@ const TEST_DOC = "https://example.com/browser/devtools/client/webconsole/" +
"test/test_bug1092055_shouldwarn.html";
const SAMPLE_MSG = "specified a header that could not be parsed successfully.";
-add_task(function* setup() {
- yield SpecialPowers.pushPrefEnv({
- set: [["dom.ipc.processCount", 1]]
- });
-});
-
add_task(function* () {
let { browser } = yield loadTab(TEST_URI);
diff --git a/devtools/client/webconsole/test/browser_webconsole_split.js b/devtools/client/webconsole/test/browser_webconsole_split.js
index a855a246922c..bb858d1c192a 100644
--- a/devtools/client/webconsole/test/browser_webconsole_split.js
+++ b/devtools/client/webconsole/test/browser_webconsole_split.js
@@ -9,10 +9,6 @@ const TEST_URI = "data:text/html;charset=utf-8,Web Console test for splitting";
function test() {
waitForExplicitFinish();
- SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 1]]}, runTest);
-}
-
-function runTest() {
// Test is slow on Linux EC2 instances - Bug 962931
requestLongerTimeout(2);
diff --git a/devtools/server/actors/layout.js b/devtools/server/actors/layout.js
index 2b32f5d0a174..2edbd70b59a2 100644
--- a/devtools/server/actors/layout.js
+++ b/devtools/server/actors/layout.js
@@ -125,11 +125,16 @@ var LayoutActor = ActorClassWithSpec(layoutSpec, {
* @return {Array} An array of GridActor objects.
*/
getAllGrids: function (rootNode, traverseFrames) {
+ let grids = [];
+
+ if (!rootNode) {
+ return grids;
+ }
+
if (!traverseFrames) {
return this.getGrids(rootNode.rawNode);
}
- let grids = [];
for (let {document} of this.tabActor.windows) {
grids = [...grids, ...this.getGrids(document.documentElement)];
}
diff --git a/devtools/server/tests/browser/browser_animation_getMultipleStates.js b/devtools/server/tests/browser/browser_animation_getMultipleStates.js
index 4436695b0124..2e78aceb61f5 100644
--- a/devtools/server/tests/browser/browser_animation_getMultipleStates.js
+++ b/devtools/server/tests/browser/browser_animation_getMultipleStates.js
@@ -4,12 +4,6 @@
"use strict";
-add_task(function* setup() {
- yield SpecialPowers.pushPrefEnv({
- set: [["dom.ipc.processCount", 1]]
- });
-});
-
// Check that the duration, iterationCount and delay are retrieved correctly for
// multiple animations.
diff --git a/devtools/shared/css/generated/properties-db.js b/devtools/shared/css/generated/properties-db.js
index 32e94ea0e41e..311211cca70d 100644
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -2865,6 +2865,7 @@ exports.CSS_PROPERTIES = {
"column-rule-color",
"column-rule-style",
"column-rule-width",
+ "column-span",
"column-width",
"contain",
"content",
@@ -9346,6 +9347,10 @@ exports.PREFERENCES = [
"color-adjust",
"layout.css.color-adjust.enabled"
],
+ [
+ "column-span",
+ "layout.css.column-span.enabled"
+ ],
[
"contain",
"layout.css.contain.enabled"
diff --git a/dom/audiochannel/AudioChannelService.cpp b/dom/audiochannel/AudioChannelService.cpp
index 05535c7aaf78..92a4873555bb 100644
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -36,6 +36,8 @@ using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::hal;
+static mozilla::LazyLogModule gAudioChannelLog("AudioChannel");
+
namespace {
// If true, any new AudioChannelAgent will be muted when created.
@@ -226,13 +228,9 @@ AudioChannelService::Get()
return service.forget();
}
-/* static */ PRLogModuleInfo*
+/* static */ LogModule*
AudioChannelService::GetAudioChannelLog()
{
- static PRLogModuleInfo *gAudioChannelLog;
- if (!gAudioChannelLog) {
- gAudioChannelLog = PR_NewLogModule("AudioChannel");
- }
return gAudioChannelLog;
}
diff --git a/dom/audiochannel/AudioChannelService.h b/dom/audiochannel/AudioChannelService.h
index ac87846e7bec..42238db3686e 100644
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -16,6 +16,7 @@
#include "AudioChannelAgent.h"
#include "nsAttrValue.h"
#include "mozilla/dom/AudioChannelBinding.h"
+#include "mozilla/Logging.h"
#include
@@ -102,7 +103,7 @@ public:
static bool IsAudioChannelMutedByDefault();
- static PRLogModuleInfo* GetAudioChannelLog();
+ static LogModule* GetAudioChannelLog();
static bool IsEnableAudioCompeting();
diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
index 9df11a64b3b3..ed286b771bad 100644
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -4406,7 +4406,8 @@ nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType,
return NS_ERROR_INVALID_ARG;
// Loading the sheet sync.
- RefPtr loader = new css::Loader(GetStyleBackendType());
+ RefPtr loader =
+ new css::Loader(GetStyleBackendType(), GetDocGroup());
css::SheetParsingMode parsingMode;
switch (aType) {
@@ -10564,14 +10565,12 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler)
explicit UnblockParsingPromiseHandler(nsIDocument* aDocument, Promise* aPromise)
- : mDocument(aDocument)
- , mPromise(aPromise)
+ : mPromise(aPromise)
{
- nsCOMPtr parser = mDocument->CreatorParserOrNull();
+ nsCOMPtr parser = aDocument->CreatorParserOrNull();
if (parser) {
parser->BlockParser();
- } else {
- mDocument = nullptr;
+ mParser = do_GetWeakReference(parser);
}
}
@@ -10599,21 +10598,19 @@ protected:
private:
void MaybeUnblockParser() {
- if (mDocument) {
- nsCOMPtr parser = mDocument->CreatorParserOrNull();
- if (parser) {
- parser->UnblockParser();
- parser->ContinueInterruptedParsingAsync();
- }
- mDocument = nullptr;
+ nsCOMPtr parser = do_QueryReferent(mParser);
+ if (parser) {
+ parser->UnblockParser();
+ parser->ContinueInterruptedParsingAsync();
+ mParser = nullptr;
}
}
- RefPtr mDocument;
+ nsWeakPtr mParser;
RefPtr mPromise;
};
-NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler, mDocument, mPromise)
+NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler, mPromise)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UnblockParsingPromiseHandler)
NS_INTERFACE_MAP_ENTRY(nsISupports)
diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp
index 8b24dd13eb19..8a5d5df0b35a 100644
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -1975,15 +1975,15 @@ ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
aSavedResponseOut->mValue.principalInfo() = void_t();
if (!serializedInfo.IsEmpty()) {
- nsAutoCString originNoSuffix;
+ nsAutoCString specNoSuffix;
OriginAttributes attrs;
- if (!attrs.PopulateFromOrigin(serializedInfo, originNoSuffix)) {
+ if (!attrs.PopulateFromOrigin(serializedInfo, specNoSuffix)) {
NS_WARNING("Something went wrong parsing a serialized principal!");
return NS_ERROR_FAILURE;
}
aSavedResponseOut->mValue.principalInfo() =
- mozilla::ipc::ContentPrincipalInfo(attrs, originNoSuffix);
+ mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), specNoSuffix);
}
rv = state->GetBlobAsUTF8String(6, aSavedResponseOut->mValue.channelInfo().securityInfo());
diff --git a/dom/events/EventStates.h b/dom/events/EventStates.h
index b1b9654ef198..ddab2cdf8beb 100644
--- a/dom/events/EventStates.h
+++ b/dom/events/EventStates.h
@@ -12,7 +12,7 @@
namespace mozilla {
-#define NS_EVENT_STATE_HIGHEST_SERVO_BIT 12
+#define NS_EVENT_STATE_HIGHEST_SERVO_BIT 19
/**
* EventStates is the class used to represent the event states of nsIContent
@@ -223,6 +223,21 @@ private:
#define NS_EVENT_STATE_INVALID NS_DEFINE_EVENT_STATE_MACRO(11)
// UI friendly version of :valid pseudo-class.
#define NS_EVENT_STATE_MOZ_UI_VALID NS_DEFINE_EVENT_STATE_MACRO(12)
+// Content could not be rendered (image/object/etc).
+#define NS_EVENT_STATE_BROKEN NS_DEFINE_EVENT_STATE_MACRO(13)
+// Content disabled by the user (images turned off, say).
+#define NS_EVENT_STATE_USERDISABLED NS_DEFINE_EVENT_STATE_MACRO(14)
+// Content suppressed by the user (ad blocking, etc).
+#define NS_EVENT_STATE_SUPPRESSED NS_DEFINE_EVENT_STATE_MACRO(15)
+// Content is still loading such that there is nothing to show the
+// user (eg an image which hasn't started coming in yet).
+#define NS_EVENT_STATE_LOADING NS_DEFINE_EVENT_STATE_MACRO(16)
+// Handler for the content has been blocked.
+#define NS_EVENT_STATE_HANDLER_BLOCKED NS_DEFINE_EVENT_STATE_MACRO(17)
+// Handler for the content has been disabled.
+#define NS_EVENT_STATE_HANDLER_DISABLED NS_DEFINE_EVENT_STATE_MACRO(18)
+// Handler for the content has crashed
+#define NS_EVENT_STATE_HANDLER_CRASHED NS_DEFINE_EVENT_STATE_MACRO(19)
/*
* Bits below here do not have Servo-related ordering constraints.
@@ -232,42 +247,27 @@ private:
*/
// Drag is hovering over content.
-#define NS_EVENT_STATE_DRAGOVER NS_DEFINE_EVENT_STATE_MACRO(13)
+#define NS_EVENT_STATE_DRAGOVER NS_DEFINE_EVENT_STATE_MACRO(20)
// Content is required.
-#define NS_EVENT_STATE_REQUIRED NS_DEFINE_EVENT_STATE_MACRO(14)
+#define NS_EVENT_STATE_REQUIRED NS_DEFINE_EVENT_STATE_MACRO(21)
// Content is optional (and can be required).
-#define NS_EVENT_STATE_OPTIONAL NS_DEFINE_EVENT_STATE_MACRO(15)
+#define NS_EVENT_STATE_OPTIONAL NS_DEFINE_EVENT_STATE_MACRO(22)
// Link has been visited.
-#define NS_EVENT_STATE_VISITED NS_DEFINE_EVENT_STATE_MACRO(16)
+#define NS_EVENT_STATE_VISITED NS_DEFINE_EVENT_STATE_MACRO(23)
// Link hasn't been visited.
-#define NS_EVENT_STATE_UNVISITED NS_DEFINE_EVENT_STATE_MACRO(17)
+#define NS_EVENT_STATE_UNVISITED NS_DEFINE_EVENT_STATE_MACRO(24)
// Content value is in-range (and can be out-of-range).
-#define NS_EVENT_STATE_INRANGE NS_DEFINE_EVENT_STATE_MACRO(18)
+#define NS_EVENT_STATE_INRANGE NS_DEFINE_EVENT_STATE_MACRO(25)
// Content value is out-of-range.
-#define NS_EVENT_STATE_OUTOFRANGE NS_DEFINE_EVENT_STATE_MACRO(19)
+#define NS_EVENT_STATE_OUTOFRANGE NS_DEFINE_EVENT_STATE_MACRO(26)
// These two are temporary (see bug 302188)
// Content is read-only.
-#define NS_EVENT_STATE_MOZ_READONLY NS_DEFINE_EVENT_STATE_MACRO(20)
+#define NS_EVENT_STATE_MOZ_READONLY NS_DEFINE_EVENT_STATE_MACRO(27)
// Content is editable.
-#define NS_EVENT_STATE_MOZ_READWRITE NS_DEFINE_EVENT_STATE_MACRO(21)
+#define NS_EVENT_STATE_MOZ_READWRITE NS_DEFINE_EVENT_STATE_MACRO(28)
// Content is the default one (meaning depends of the context).
-#define NS_EVENT_STATE_DEFAULT NS_DEFINE_EVENT_STATE_MACRO(22)
-// Content could not be rendered (image/object/etc).
-#define NS_EVENT_STATE_BROKEN NS_DEFINE_EVENT_STATE_MACRO(23)
-// Content disabled by the user (images turned off, say).
-#define NS_EVENT_STATE_USERDISABLED NS_DEFINE_EVENT_STATE_MACRO(24)
-// Content suppressed by the user (ad blocking, etc).
-#define NS_EVENT_STATE_SUPPRESSED NS_DEFINE_EVENT_STATE_MACRO(25)
-// Content is still loading such that there is nothing to show the
-// user (eg an image which hasn't started coming in yet).
-#define NS_EVENT_STATE_LOADING NS_DEFINE_EVENT_STATE_MACRO(26)
-#define NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL NS_DEFINE_EVENT_STATE_MACRO(27)
-// Handler for the content has been blocked.
-#define NS_EVENT_STATE_HANDLER_BLOCKED NS_DEFINE_EVENT_STATE_MACRO(28)
-// Handler for the content has been disabled.
-#define NS_EVENT_STATE_HANDLER_DISABLED NS_DEFINE_EVENT_STATE_MACRO(29)
-// Handler for the content has crashed
-#define NS_EVENT_STATE_HANDLER_CRASHED NS_DEFINE_EVENT_STATE_MACRO(30)
+#define NS_EVENT_STATE_DEFAULT NS_DEFINE_EVENT_STATE_MACRO(29)
+#define NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL NS_DEFINE_EVENT_STATE_MACRO(30)
// Content has focus and should show a ring.
#define NS_EVENT_STATE_FOCUSRING NS_DEFINE_EVENT_STATE_MACRO(31)
// Content is a submit control and the form isn't valid.
diff --git a/dom/events/test/pointerevents/mochitest.ini b/dom/events/test/pointerevents/mochitest.ini
index d6b7416d1569..0084d7a6087d 100644
--- a/dom/events/test/pointerevents/mochitest.ini
+++ b/dom/events/test/pointerevents/mochitest.ini
@@ -6,6 +6,16 @@ support-files =
pointerevent_styles.css
pointerevent_support.js
+[test_bug1285128.html]
+[test_bug1293174_implicit_pointer_capture_for_touch_1.html]
+ support-files = bug1293174_implicit_pointer_capture_for_touch_1.html
+[test_bug1293174_implicit_pointer_capture_for_touch_2.html]
+ support-files = bug1293174_implicit_pointer_capture_for_touch_2.html
+[test_bug1303704.html]
+[test_bug1315862.html]
+[test_bug1323158.html]
+[test_empty_file.html]
+ disabled = disabled # Bug 1150091 - Issue with support-files
[test_pointerevent_attributes_hoverable_pointers-manual.html]
support-files =
pointerevent_attributes_hoverable_pointers-manual.html
@@ -118,14 +128,5 @@ support-files =
pointerevent_touch-action-pan-left-css_touch-manual.html
pointerevent_touch-action-pan-right-css_touch-manual.html
pointerevent_touch-action-pan-up-css_touch-manual.html
-[test_bug1285128.html]
-[test_bug1293174_implicit_pointer_capture_for_touch_1.html]
- support-files = bug1293174_implicit_pointer_capture_for_touch_1.html
-[test_bug1293174_implicit_pointer_capture_for_touch_2.html]
- support-files = bug1293174_implicit_pointer_capture_for_touch_2.html
-[test_bug1303704.html]
-[test_bug1323158.html]
+[test_trigger_fullscreen_by_pointer_events.html]
[test_trigger_popup_by_pointer_events.html]
-[test_empty_file.html]
- disabled = disabled # Bug 1150091 - Issue with support-files
-[test_bug1315862.html]
diff --git a/dom/events/test/pointerevents/test_trigger_fullscreen_by_pointer_events.html b/dom/events/test/pointerevents/test_trigger_fullscreen_by_pointer_events.html
new file mode 100644
index 000000000000..13913cd35dca
--- /dev/null
+++ b/dom/events/test/pointerevents/test_trigger_fullscreen_by_pointer_events.html
@@ -0,0 +1,53 @@
+
+
+
+
+ Test for triggering Fullscreen by pointer events
+
+
+
+
+
+
+
+
+
diff --git a/dom/file/ipc/Blob.cpp b/dom/file/ipc/Blob.cpp
index aa8b9dafd857..b79072a632c3 100644
--- a/dom/file/ipc/Blob.cpp
+++ b/dom/file/ipc/Blob.cpp
@@ -993,7 +993,8 @@ RemoteInputStream::BlockAndWaitForStream()
nsTArray fds;
OptionalFileDescriptorSetToFDs(optionalFDs, fds);
- nsCOMPtr stream = DeserializeInputStream(params, fds);
+ nsCOMPtr stream =
+ InputStreamHelper::DeserializeInputStream(params, fds);
MOZ_ASSERT(stream);
SetStream(stream);
@@ -4450,7 +4451,7 @@ BlobParent::RecvPBlobStreamConstructor(PBlobStreamParent* aActor,
if (mBlobImpl->IsMemoryFile()) {
InputStreamParams params;
nsTArray fds;
- SerializeInputStream(stream, params, fds);
+ InputStreamHelper::SerializeInputStream(stream, params, fds);
MOZ_ASSERT(params.type() != InputStreamParams::T__None);
MOZ_ASSERT(fds.IsEmpty());
@@ -4788,7 +4789,8 @@ InputStreamChild::Recv__delete__(const InputStreamParams& aParams,
const_cast(aOptionalSet),
fds);
- nsCOMPtr stream = DeserializeInputStream(aParams, fds);
+ nsCOMPtr stream =
+ InputStreamHelper::DeserializeInputStream(aParams, fds);
MOZ_ASSERT(stream);
mRemoteStream->SetStream(stream);
diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp
index 3aee7bcc5039..d30f58e8d456 100644
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -18796,10 +18796,12 @@ Maintenance::DirectoryWork()
MOZ_ASSERT(origin.IsEmpty());
int64_t dummyTimeStamp;
+ bool dummyPersisted;
nsCString dummySuffix;
if (NS_WARN_IF(NS_FAILED(
quotaManager->GetDirectoryMetadata2(originDir,
&dummyTimeStamp,
+ &dummyPersisted,
dummySuffix,
group,
origin)))) {
diff --git a/dom/media/MediaStreamGraph.cpp b/dom/media/MediaStreamGraph.cpp
index 8d2d725d2f84..3c46bd873a47 100644
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -4009,7 +4009,8 @@ MediaStreamGraph::ApplyAudioContextOperation(MediaStream* aDestinationStream,
}
void RunDuringShutdown() override
{
- MOZ_ASSERT(false, "We should be reviving the graph?");
+ MOZ_ASSERT(mAudioContextOperation == AudioContextOperation::Close,
+ "We should be reviving the graph?");
}
private:
diff --git a/dom/media/systemservices/MediaParent.cpp b/dom/media/systemservices/MediaParent.cpp
index 322ab954b779..c7798dd73dc6 100644
--- a/dom/media/systemservices/MediaParent.cpp
+++ b/dom/media/systemservices/MediaParent.cpp
@@ -107,7 +107,7 @@ class OriginKeyStore : public nsISupports
private:
void
PrincipalInfoToString(const ipc::PrincipalInfo& aPrincipalInfo,
- nsAutoCString aString)
+ nsACString& aString)
{
switch (aPrincipalInfo.type()) {
case ipc::PrincipalInfo::TSystemPrincipalInfo:
@@ -124,11 +124,11 @@ class OriginKeyStore : public nsISupports
case ipc::PrincipalInfo::TContentPrincipalInfo: {
const ipc::ContentPrincipalInfo& info =
aPrincipalInfo.get_ContentPrincipalInfo();
- aString.Assign(info.spec());
+ aString.Assign(info.originNoSuffix());
nsAutoCString suffix;
info.attrs().CreateSuffix(suffix);
- suffix.Append(suffix);
+ aString.Append(suffix);
return;
}
diff --git a/dom/media/test/mochitest.ini b/dom/media/test/mochitest.ini
index 9562d7252660..91fe6fd9a543 100644
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -1096,7 +1096,7 @@ tags = webvtt
[test_unseekable.html]
skip-if = toolkit == 'android' # android(bug 1232305)
[test_video_to_canvas.html]
-skip-if = android_version == '15' || android_version == '17' || (android_version == '19' && debug) || android_version == '22' # bug 1320418, android(bug 1232305)
+skip-if = toolkit == 'android' # android(bug 1232305), bugs 1320418,1347953,1347954,1348140,1348386
[test_video_in_audio_element.html]
skip-if = android_version == '15' || android_version == '17' # bug 1320417, 1326326, android(bug 1232323, bug 1232305)
[test_videoDocumentTitle.html]
diff --git a/dom/media/tests/mochitest/test_enumerateDevices.html b/dom/media/tests/mochitest/test_enumerateDevices.html
index 9e6d25cfd6dc..dd4db59d763f 100644
--- a/dom/media/tests/mochitest/test_enumerateDevices.html
+++ b/dom/media/tests/mochitest/test_enumerateDevices.html
@@ -117,8 +117,7 @@ runTest(async () => {
"Same origin deviceId for " + device.label + " must match");
}
for (let device of differentOriginDevices) {
- // TODO: s/todo/ok/ once bug 1340163 is fixed.
- todo(!devices.find(d => d.deviceId == device.deviceId),
+ ok(!devices.find(d => d.deviceId == device.deviceId),
"Different origin deviceId for " + device.label + " must be different");
}
diff --git a/dom/media/webvtt/vtt.jsm b/dom/media/webvtt/vtt.jsm
index 545fdbe31d73..3b3596bcbaf3 100644
--- a/dom/media/webvtt/vtt.jsm
+++ b/dom/media/webvtt/vtt.jsm
@@ -404,7 +404,7 @@ Cu.import('resource://gre/modules/Services.jsm');
// Otherwise just ignore the end tag.
continue;
}
- var ts = collectTimeStamp(t.substr(1, t.length - 2));
+ var ts = collectTimeStamp(t.substr(1, t.length - 1));
var node;
if (ts) {
// Timestamps are lead nodes as well.
diff --git a/dom/quota/ActorsChild.cpp b/dom/quota/ActorsChild.cpp
index b22878967dde..ab900dc39e6e 100644
--- a/dom/quota/ActorsChild.cpp
+++ b/dom/quota/ActorsChild.cpp
@@ -272,6 +272,7 @@ QuotaRequestChild::Recv__delete__(const RequestResponse& aResponse)
case RequestResponse::TClearDataResponse:
case RequestResponse::TClearAllResponse:
case RequestResponse::TResetAllResponse:
+ case RequestResponse::TPersistResponse:
HandleResponse();
break;
@@ -279,6 +280,10 @@ QuotaRequestChild::Recv__delete__(const RequestResponse& aResponse)
HandleResponse(aResponse.get_InitOriginResponse().created());
break;
+ case RequestResponse::TPersistedResponse:
+ HandleResponse(aResponse.get_PersistedResponse().persisted());
+ break;
+
default:
MOZ_CRASH("Unknown response type!");
}
diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp
index 03cb5435b625..22a3559d39bb 100644
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -1255,6 +1255,63 @@ private:
GetResponse(RequestResponse& aResponse) override;
};
+class PersistRequestBase
+ : public QuotaRequestBase
+{
+ const PrincipalInfo mPrincipalInfo;
+
+protected:
+ nsCString mSuffix;
+ nsCString mGroup;
+
+public:
+ bool
+ Init(Quota* aQuota) override;
+
+protected:
+ explicit PersistRequestBase(const PrincipalInfo& aPrincipalInfo);
+
+private:
+ nsresult
+ DoInitOnMainThread() override;
+};
+
+class PersistedOp final
+ : public PersistRequestBase
+{
+ bool mPersisted;
+
+public:
+ explicit PersistedOp(const RequestParams& aParams);
+
+private:
+ ~PersistedOp()
+ { }
+
+ nsresult
+ DoDirectoryWork(QuotaManager* aQuotaManager) override;
+
+ void
+ GetResponse(RequestResponse& aResponse) override;
+};
+
+class PersistOp final
+ : public PersistRequestBase
+{
+public:
+ explicit PersistOp(const RequestParams& aParams);
+
+private:
+ ~PersistOp()
+ { }
+
+ nsresult
+ DoDirectoryWork(QuotaManager* aQuotaManager) override;
+
+ void
+ GetResponse(RequestResponse& aResponse) override;
+};
+
/*******************************************************************************
* Helper Functions
******************************************************************************/
@@ -1852,6 +1909,52 @@ EnsureDirectory(nsIFile* aDirectory, bool* aCreated)
return NS_OK;
}
+nsresult
+EnsureOriginDirectory(nsIFile* aDirectory, bool* aCreated)
+{
+ AssertIsOnIOThread();
+
+ nsresult rv;
+
+#ifndef RELEASE_OR_BETA
+ bool exists;
+ rv = aDirectory->Exists(&exists);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!exists) {
+ nsString leafName;
+ nsresult rv = aDirectory->GetLeafName(leafName);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!leafName.EqualsLiteral(kChromeOrigin)) {
+ nsCString spec;
+ OriginAttributes attrs;
+ OriginParser::ResultType result =
+ OriginParser::ParseOrigin(NS_ConvertUTF16toUTF8(leafName),
+ spec,
+ &attrs);
+ if (NS_WARN_IF(result != OriginParser::ValidOrigin)) {
+ QM_WARNING("Preventing creation of a new origin directory which is not "
+ "supported by our origin parser or is obsolete!");
+
+ return NS_ERROR_FAILURE;
+ }
+ }
+ }
+#endif
+
+ rv = EnsureDirectory(aDirectory, aCreated);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
enum FileFlag {
kTruncateFileFlag,
kUpdateFileFlag,
@@ -2077,6 +2180,7 @@ CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp,
nsresult
CreateDirectoryMetadata2(nsIFile* aDirectory,
int64_t aTimestamp,
+ bool aPersisted,
const nsACString& aSuffix,
const nsACString& aGroup,
const nsACString& aOrigin)
@@ -2108,8 +2212,7 @@ CreateDirectoryMetadata2(nsIFile* aDirectory,
return rv;
}
- // Reserved for navigator.persist()
- rv = stream->WriteBoolean(false);
+ rv = stream->WriteBoolean(aPersisted);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@@ -2168,6 +2271,43 @@ CreateDirectoryMetadata2(nsIFile* aDirectory,
return NS_OK;
}
+nsresult
+CreateDirectoryMetadataFiles(nsIFile* aDirectory,
+ bool aPersisted,
+ const nsACString& aSuffix,
+ const nsACString& aGroup,
+ const nsACString& aOrigin,
+ int64_t* aTimestamp)
+{
+ AssertIsOnIOThread();
+
+ int64_t timestamp = PR_Now();
+
+ nsresult rv = CreateDirectoryMetadata(aDirectory,
+ timestamp,
+ aSuffix,
+ aGroup,
+ aOrigin);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = CreateDirectoryMetadata2(aDirectory,
+ timestamp,
+ aPersisted,
+ aSuffix,
+ aGroup,
+ aOrigin);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (aTimestamp) {
+ *aTimestamp = timestamp;
+ }
+ return NS_OK;
+}
+
nsresult
GetBinaryInputStream(nsIFile* aDirectory,
const nsAString& aFilename,
@@ -3512,6 +3652,8 @@ QuotaManager::UpdateOriginAccessTime(PersistenceType aPersistenceType,
void
QuotaManager::RemoveQuota()
{
+ AssertIsOnIOThread();
+
MutexAutoLock lock(mQuotaMutex);
for (auto iter = mGroupInfoPairs.Iter(); !iter.Done(); iter.Next()) {
@@ -3637,6 +3779,40 @@ QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
return GetQuotaObject(aPersistenceType, aGroup, aOrigin, file);
}
+Nullable
+QuotaManager::OriginPersisted(const nsACString& aGroup,
+ const nsACString& aOrigin)
+{
+ AssertIsOnIOThread();
+
+ MutexAutoLock lock(mQuotaMutex);
+
+ RefPtr originInfo = LockedGetOriginInfo(PERSISTENCE_TYPE_DEFAULT,
+ aGroup,
+ aOrigin);
+ if (originInfo) {
+ return Nullable(originInfo->LockedPersisted());
+ }
+
+ return Nullable();
+}
+
+void
+QuotaManager::PersistOrigin(const nsACString& aGroup,
+ const nsACString& aOrigin)
+{
+ AssertIsOnIOThread();
+
+ MutexAutoLock lock(mQuotaMutex);
+
+ RefPtr originInfo = LockedGetOriginInfo(PERSISTENCE_TYPE_DEFAULT,
+ aGroup,
+ aOrigin);
+ if (originInfo && !originInfo->LockedPersisted()) {
+ originInfo->LockedPersist();
+ }
+}
+
void
QuotaManager::AbortOperationsForProcess(ContentParentId aContentParentId)
{
@@ -3691,6 +3867,7 @@ QuotaManager::RestoreDirectoryMetadata2(nsIFile* aDirectory, bool aPersistent)
nsresult
QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory,
int64_t* aTimestamp,
+ bool* aPersisted,
nsACString& aSuffix,
nsACString& aGroup,
nsACString& aOrigin)
@@ -3698,6 +3875,7 @@ QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory,
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aDirectory);
MOZ_ASSERT(aTimestamp);
+ MOZ_ASSERT(aPersisted);
MOZ_ASSERT(mStorageInitialized);
nsCOMPtr binaryStream;
@@ -3750,6 +3928,7 @@ QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory,
}
*aTimestamp = timestamp;
+ *aPersisted = persisted;
aSuffix = suffix;
aGroup = group;
aOrigin = origin;
@@ -3760,12 +3939,14 @@ nsresult
QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
bool aPersistent,
int64_t* aTimestamp,
+ bool* aPersisted,
nsACString& aSuffix,
nsACString& aGroup,
nsACString& aOrigin)
{
nsresult rv = GetDirectoryMetadata2(aDirectory,
aTimestamp,
+ aPersisted,
aSuffix,
aGroup,
aOrigin);
@@ -3777,6 +3958,7 @@ QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
rv = GetDirectoryMetadata2(aDirectory,
aTimestamp,
+ aPersisted,
aSuffix,
aGroup,
aOrigin);
@@ -3789,11 +3971,13 @@ QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
}
nsresult
-QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory, int64_t* aTimestamp)
+QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory,
+ int64_t* aTimestamp,
+ bool* aPersisted)
{
AssertIsOnIOThread();
MOZ_ASSERT(aDirectory);
- MOZ_ASSERT(aTimestamp);
+ MOZ_ASSERT(aTimestamp || aPersisted);
MOZ_ASSERT(mStorageInitialized);
nsCOMPtr binaryStream;
@@ -3810,23 +3994,37 @@ QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory, int64_t* aTimestamp)
return rv;
}
- *aTimestamp = timestamp;
+ bool persisted;
+ if (aPersisted) {
+ rv = binaryStream->ReadBoolean(&persisted);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ if (aTimestamp) {
+ *aTimestamp = timestamp;
+ }
+ if (aPersisted) {
+ *aPersisted = persisted;
+ }
return NS_OK;
}
nsresult
QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
bool aPersistent,
- int64_t* aTimestamp)
+ int64_t* aTimestamp,
+ bool* aPersisted)
{
- nsresult rv = GetDirectoryMetadata2(aDirectory, aTimestamp);
+ nsresult rv = GetDirectoryMetadata2(aDirectory, aTimestamp, aPersisted);
if (NS_WARN_IF(NS_FAILED(rv))) {
rv = RestoreDirectoryMetadata2(aDirectory, aPersistent);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
- rv = GetDirectoryMetadata2(aDirectory, aTimestamp);
+ rv = GetDirectoryMetadata2(aDirectory, aTimestamp, aPersisted);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@@ -3899,12 +4097,14 @@ QuotaManager::InitializeRepository(PersistenceType aPersistenceType)
}
int64_t timestamp;
+ bool persisted;
nsCString suffix;
nsCString group;
nsCString origin;
rv = GetDirectoryMetadata2WithRestore(childDirectory,
/* aPersistent */ false,
×tamp,
+ &persisted,
suffix,
group,
origin);
@@ -3912,8 +4112,8 @@ QuotaManager::InitializeRepository(PersistenceType aPersistenceType)
return rv;
}
- rv = InitializeOrigin(aPersistenceType, group, origin, timestamp,
- /* aPersisted */ false, childDirectory);
+ rv = InitializeOrigin(aPersistenceType, group, origin, timestamp, persisted,
+ childDirectory);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@@ -4820,66 +5020,29 @@ QuotaManager::EnsureOriginIsInitializedInternal(
CheckTemporaryStorageLimits();
}
- int64_t timestamp;
-
-#ifndef RELEASE_OR_BETA
- bool exists;
- rv = directory->Exists(&exists);
+ bool created;
+ rv = EnsureOriginDirectory(directory, &created);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
- if (!exists) {
- nsString leafName;
- nsresult rv = directory->GetLeafName(leafName);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
-
- if (!leafName.EqualsLiteral(kChromeOrigin)) {
- nsCString spec;
- OriginAttributes attrs;
- OriginParser::ResultType result =
- OriginParser::ParseOrigin(NS_ConvertUTF16toUTF8(leafName),
- spec,
- &attrs);
- if (NS_WARN_IF(result != OriginParser::ValidOrigin)) {
- QM_WARNING("Preventing creation of a new origin directory which is not "
- "supported by our origin parser or is obsolete!");
-
- return NS_ERROR_FAILURE;
- }
- }
- }
-#endif
-
- bool created;
- rv = EnsureDirectory(directory, &created);
- NS_ENSURE_SUCCESS(rv, rv);
-
+ int64_t timestamp;
if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
if (created) {
- timestamp = PR_Now();
-
- rv = CreateDirectoryMetadata(directory,
- timestamp,
- aSuffix,
- aGroup,
- aOrigin);
+ rv = CreateDirectoryMetadataFiles(directory,
+ /* aPersisted */ true,
+ aSuffix,
+ aGroup,
+ aOrigin,
+ ×tamp);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
-
- rv = CreateDirectoryMetadata2(directory,
- timestamp,
- aSuffix,
- aGroup,
- aOrigin);
- NS_ENSURE_SUCCESS(rv, rv);
} else {
rv = GetDirectoryMetadata2WithRestore(directory,
/* aPersistent */ true,
- ×tamp);
+ ×tamp,
+ /* aPersisted */ nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@@ -4893,24 +5056,16 @@ QuotaManager::EnsureOriginIsInitializedInternal(
mInitializedOrigins.AppendElement(aOrigin);
} else if (created) {
- timestamp = PR_Now();
-
- rv = CreateDirectoryMetadata(directory,
- timestamp,
- aSuffix,
- aGroup,
- aOrigin);
+ rv = CreateDirectoryMetadataFiles(directory,
+ /* aPersisted */ false,
+ aSuffix,
+ aGroup,
+ aOrigin,
+ ×tamp);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
- rv = CreateDirectoryMetadata2(directory,
- timestamp,
- aSuffix,
- aGroup,
- aOrigin);
- NS_ENSURE_SUCCESS(rv, rv);
-
rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, timestamp,
/* aPersisted */ false, directory);
NS_ENSURE_SUCCESS(rv, rv);
@@ -5228,6 +5383,25 @@ QuotaManager::LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType,
}
}
+already_AddRefed
+QuotaManager::LockedGetOriginInfo(PersistenceType aPersistenceType,
+ const nsACString& aGroup,
+ const nsACString& aOrigin)
+{
+ mQuotaMutex.AssertCurrentThreadOwns();
+ MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
+
+ GroupInfoPair* pair;
+ if (mGroupInfoPairs.Get(aGroup, &pair)) {
+ RefPtr groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
+ if (groupInfo) {
+ return groupInfo->LockedGetOriginInfo(aOrigin);
+ }
+ }
+
+ return nullptr;
+}
+
void
QuotaManager::CheckTemporaryStorageLimits()
{
@@ -6135,6 +6309,14 @@ Quota::AllocPQuotaRequestParent(const RequestParams& aParams)
actor = new ResetOrClearOp(/* aClear */ false);
break;
+ case RequestParams::TPersistedParams:
+ actor = new PersistedOp(aParams);
+ break;
+
+ case RequestParams::TPersistParams:
+ actor = new PersistOp(aParams);
+ break;
+
default:
MOZ_CRASH("Should never get here!");
}
@@ -6817,9 +6999,11 @@ ClearRequestBase::DeleteFiles(QuotaManager* aQuotaManager,
nsCString suffix;
nsCString group;
nsCString origin;
+ bool persisted;
rv = aQuotaManager->GetDirectoryMetadata2WithRestore(file,
persistent,
×tamp,
+ &persisted,
suffix,
group,
origin);
@@ -6981,6 +7165,235 @@ ClearDataOp::GetResponse(RequestResponse& aResponse)
aResponse = ClearDataResponse();
}
+PersistRequestBase::PersistRequestBase(const PrincipalInfo& aPrincipalInfo)
+ : QuotaRequestBase(/* aExclusive */ false)
+ , mPrincipalInfo(aPrincipalInfo)
+{
+ AssertIsOnOwningThread();
+}
+
+bool
+PersistRequestBase::Init(Quota* aQuota)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aQuota);
+
+ if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota))) {
+ return false;
+ }
+
+ mPersistenceType.SetValue(PERSISTENCE_TYPE_DEFAULT);
+
+ mNeedsMainThreadInit = true;
+
+ return true;
+}
+
+nsresult
+PersistRequestBase::DoInitOnMainThread()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(GetState() == State_Initializing);
+ MOZ_ASSERT(mNeedsMainThreadInit);
+
+ nsresult rv;
+ nsCOMPtr principal =
+ PrincipalInfoToPrincipal(mPrincipalInfo, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Figure out which origin we're dealing with.
+ nsCString origin;
+ rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup,
+ &origin);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mOriginScope.SetFromOrigin(origin);
+
+ return NS_OK;
+}
+
+PersistedOp::PersistedOp(const RequestParams& aParams)
+ : PersistRequestBase(aParams.get_PersistedParams().principalInfo())
+ , mPersisted(false)
+{
+ MOZ_ASSERT(aParams.type() == RequestParams::TPersistedParams);
+}
+
+nsresult
+PersistedOp::DoDirectoryWork(QuotaManager* aQuotaManager)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(!mPersistenceType.IsNull());
+ MOZ_ASSERT(mPersistenceType.Value() == PERSISTENCE_TYPE_DEFAULT);
+ MOZ_ASSERT(mOriginScope.IsOrigin());
+
+ PROFILER_LABEL("Quota", "PersistedOp::DoDirectoryWork",
+ js::ProfileEntry::Category::OTHER);
+
+ Nullable persisted =
+ aQuotaManager->OriginPersisted(mGroup, mOriginScope.GetOrigin());
+
+ if (!persisted.IsNull()) {
+ mPersisted = persisted.Value();
+ return NS_OK;
+ }
+
+ // If we get here, it means the origin hasn't been initialized yet.
+ // Try to get the persisted flag from directory metadata on disk.
+
+ nsCOMPtr directory;
+ nsresult rv = aQuotaManager->GetDirectoryForOrigin(mPersistenceType.Value(),
+ mOriginScope.GetOrigin(),
+ getter_AddRefs(directory));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ bool exists;
+ rv = directory->Exists(&exists);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (exists) {
+ // Get the persisted flag.
+ bool persisted;
+ rv =
+ aQuotaManager->GetDirectoryMetadata2WithRestore(directory,
+ /* aPersistent */ false,
+ /* aTimestamp */ nullptr,
+ &persisted);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mPersisted = persisted;
+ } else {
+ // The directory has not been created yet.
+ mPersisted = false;
+ }
+
+ return NS_OK;
+}
+
+void
+PersistedOp::GetResponse(RequestResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+
+ PersistedResponse persistedResponse;
+ persistedResponse.persisted() = mPersisted;
+
+ aResponse = persistedResponse;
+}
+
+PersistOp::PersistOp(const RequestParams& aParams)
+ : PersistRequestBase(aParams.get_PersistParams().principalInfo())
+{
+ MOZ_ASSERT(aParams.type() == RequestParams::TPersistParams);
+}
+
+nsresult
+PersistOp::DoDirectoryWork(QuotaManager* aQuotaManager)
+{
+ AssertIsOnIOThread();
+ MOZ_ASSERT(!mPersistenceType.IsNull());
+ MOZ_ASSERT(mPersistenceType.Value() == PERSISTENCE_TYPE_DEFAULT);
+ MOZ_ASSERT(mOriginScope.IsOrigin());
+
+ PROFILER_LABEL("Quota", "PersistOp::DoDirectoryWork",
+ js::ProfileEntry::Category::OTHER);
+
+ // Update directory metadata on disk first.
+ nsCOMPtr directory;
+ nsresult rv = aQuotaManager->GetDirectoryForOrigin(mPersistenceType.Value(),
+ mOriginScope.GetOrigin(),
+ getter_AddRefs(directory));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ bool created;
+ rv = EnsureOriginDirectory(directory, &created);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (created) {
+ rv = CreateDirectoryMetadataFiles(directory,
+ /* aPersisted */ true,
+ mSuffix,
+ mGroup,
+ mOriginScope.GetOrigin(),
+ /* aTimestamp */ nullptr);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ } else {
+ // Get the persisted flag (restore the metadata file if necessary).
+ bool persisted;
+ rv =
+ aQuotaManager->GetDirectoryMetadata2WithRestore(directory,
+ /* aPersistent */ false,
+ /* aTimestamp */ nullptr,
+ &persisted);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!persisted) {
+ nsCOMPtr file;
+ nsresult rv = directory->Clone(getter_AddRefs(file));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = file->Append(NS_LITERAL_STRING(METADATA_V2_FILE_NAME));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr stream;
+ rv = GetBinaryOutputStream(file, kUpdateFileFlag, getter_AddRefs(stream));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ MOZ_ASSERT(stream);
+
+ // Update origin access time while we are here.
+ rv = stream->Write64(PR_Now());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Set the persisted flag to true.
+ rv = stream->WriteBoolean(true);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+ }
+
+ // Directory metadata has been successfully created/updated, try to update
+ // OriginInfo too (it's ok if OriginInfo doesn't exist yet).
+ aQuotaManager->PersistOrigin(mGroup, mOriginScope.GetOrigin());
+
+ return NS_OK;
+}
+
+void
+PersistOp::GetResponse(RequestResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+
+ aResponse = PersistResponse();
+}
+
nsresult
StorageDirectoryHelper::GetDirectoryMetadata(nsIFile* aDirectory,
int64_t& aTimestamp,
@@ -8125,6 +8538,7 @@ UpgradeStorageFrom0_0To1_0Helper::ProcessOriginDirectory(
rv = CreateDirectoryMetadata2(aOriginProps.mDirectory,
aOriginProps.mTimestamp,
+ /* aPersisted */ false,
aOriginProps.mSuffix,
aOriginProps.mGroup,
aOriginProps.mOrigin);
@@ -8407,6 +8821,7 @@ UpgradeStorageFrom1_0To2_0Helper::MaybeStripObsoleteOriginAttributes(
rv = CreateDirectoryMetadata2(aOriginProps.mDirectory,
aOriginProps.mTimestamp,
+ /* aPersisted */ false,
aOriginProps.mSuffix,
aOriginProps.mGroup,
aOriginProps.mOrigin);
@@ -8478,6 +8893,7 @@ UpgradeStorageFrom1_0To2_0Helper::ProcessOriginDirectory(
if (aOriginProps.mNeedsRestore2) {
rv = CreateDirectoryMetadata2(aOriginProps.mDirectory,
aOriginProps.mTimestamp,
+ /* aPersisted */ false,
aOriginProps.mSuffix,
aOriginProps.mGroup,
aOriginProps.mOrigin);
@@ -8520,8 +8936,10 @@ RestoreDirectoryMetadata2Helper::ProcessOriginDirectory(
{
AssertIsOnIOThread();
+ // We don't have any approach to restore aPersisted, so reset it to false.
nsresult rv = CreateDirectoryMetadata2(aOriginProps.mDirectory,
aOriginProps.mTimestamp,
+ /* aPersisted */ false,
aOriginProps.mSuffix,
aOriginProps.mGroup,
aOriginProps.mOrigin);
diff --git a/dom/quota/PQuota.ipdl b/dom/quota/PQuota.ipdl
index 0574f4b039b7..88333e5bee6b 100644
--- a/dom/quota/PQuota.ipdl
+++ b/dom/quota/PQuota.ipdl
@@ -59,6 +59,16 @@ struct ResetAllParams
{
};
+struct PersistedParams
+{
+ PrincipalInfo principalInfo;
+};
+
+struct PersistParams
+{
+ PrincipalInfo principalInfo;
+};
+
union RequestParams
{
InitParams;
@@ -67,6 +77,8 @@ union RequestParams
ClearDataParams;
ClearAllParams;
ResetAllParams;
+ PersistedParams;
+ PersistParams;
};
protocol PQuota
diff --git a/dom/quota/PQuotaRequest.ipdl b/dom/quota/PQuotaRequest.ipdl
index d560fe184cc1..4b162a559992 100644
--- a/dom/quota/PQuotaRequest.ipdl
+++ b/dom/quota/PQuotaRequest.ipdl
@@ -33,6 +33,15 @@ struct ResetAllResponse
{
};
+struct PersistedResponse
+{
+ bool persisted;
+};
+
+struct PersistResponse
+{
+};
+
union RequestResponse
{
nsresult;
@@ -42,6 +51,8 @@ union RequestResponse
ClearDataResponse;
ClearAllResponse;
ResetAllResponse;
+ PersistedResponse;
+ PersistResponse;
};
protocol PQuotaRequest
diff --git a/dom/quota/QuotaManager.h b/dom/quota/QuotaManager.h
index 8984a23c8453..176ec5c3fe33 100644
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -185,6 +185,14 @@ public:
const nsACString& aOrigin,
const nsAString& aPath);
+ Nullable
+ OriginPersisted(const nsACString& aGroup,
+ const nsACString& aOrigin);
+
+ void
+ PersistOrigin(const nsACString& aGroup,
+ const nsACString& aOrigin);
+
// Called when a process is being shot down. Aborts any running operations
// for the given process.
void
@@ -201,6 +209,7 @@ public:
nsresult
GetDirectoryMetadata2(nsIFile* aDirectory,
int64_t* aTimestamp,
+ bool* aPersisted,
nsACString& aSuffix,
nsACString& aGroup,
nsACString& aOrigin);
@@ -209,17 +218,21 @@ public:
GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
bool aPersistent,
int64_t* aTimestamp,
+ bool* aPersisted,
nsACString& aSuffix,
nsACString& aGroup,
nsACString& aOrigin);
nsresult
- GetDirectoryMetadata2(nsIFile* aDirectory, int64_t* aTimestamp);
+ GetDirectoryMetadata2(nsIFile* aDirectory,
+ int64_t* aTimestamp,
+ bool* aPersisted);
nsresult
GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
bool aPersistent,
- int64_t* aTimestamp);
+ int64_t* aTimestamp,
+ bool* aPersisted);
// This is the main entry point into the QuotaManager API.
// Any storage API implementation (quota client) that participates in
@@ -438,6 +451,11 @@ private:
const nsACString& aGroup,
const nsACString& aOrigin);
+ already_AddRefed
+ LockedGetOriginInfo(PersistenceType aPersistenceType,
+ const nsACString& aGroup,
+ const nsACString& aOrigin);
+
nsresult
MaybeUpgradeIndexedDBDirectory();
diff --git a/dom/quota/QuotaManagerService.cpp b/dom/quota/QuotaManagerService.cpp
index c71970c15882..659d87a9e102 100644
--- a/dom/quota/QuotaManagerService.cpp
+++ b/dom/quota/QuotaManagerService.cpp
@@ -60,6 +60,25 @@ TestingPrefChangedCallback(const char* aPrefName,
gTestingMode = Preferences::GetBool(aPrefName);
}
+nsresult
+CheckedPrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
+ PrincipalInfo& aPrincipalInfo)
+{
+ MOZ_ASSERT(aPrincipal);
+
+ nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aPrincipalInfo);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
+ aPrincipalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
class AbortOperationsRunnable final
: public Runnable
{
@@ -540,18 +559,12 @@ QuotaManagerService::InitStoragesForPrincipal(
InitOriginParams params;
- PrincipalInfo& principalInfo = params.principalInfo();
-
- nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
+ nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
+ params.principalInfo());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
- if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
- principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
- return NS_ERROR_UNEXPECTED;
- }
-
Nullable persistenceType;
rv = NullablePersistenceTypeFromText(aPersistenceType, &persistenceType);
if (NS_WARN_IF(NS_FAILED(rv)) || persistenceType.IsNull()) {
@@ -585,17 +598,12 @@ QuotaManagerService::GetUsageForPrincipal(nsIPrincipal* aPrincipal,
UsageParams params;
- PrincipalInfo& principalInfo = params.principalInfo();
- nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
+ nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
+ params.principalInfo());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
- if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
- principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
- return NS_ERROR_UNEXPECTED;
- }
-
params.getGroupUsage() = aGetGroupUsage;
nsAutoPtr info(new UsageRequestInfo(request, params));
@@ -655,18 +663,12 @@ QuotaManagerService::ClearStoragesForPrincipal(nsIPrincipal* aPrincipal,
ClearOriginParams params;
- PrincipalInfo& principalInfo = params.principalInfo();
-
- nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
+ nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
+ params.principalInfo());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
- if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
- principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
- return NS_ERROR_UNEXPECTED;
- }
-
Nullable persistenceType;
rv = NullablePersistenceTypeFromText(aPersistenceType, &persistenceType);
if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -717,6 +719,64 @@ QuotaManagerService::Reset(nsIQuotaRequest** _retval)
return NS_OK;
}
+NS_IMETHODIMP
+QuotaManagerService::Persisted(nsIPrincipal* aPrincipal,
+ nsIQuotaRequest** _retval)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aPrincipal);
+ MOZ_ASSERT(_retval);
+
+ RefPtr request = new Request(aPrincipal);
+
+ PersistedParams params;
+
+ nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
+ params.principalInfo());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsAutoPtr info(new RequestInfo(request, params));
+
+ rv = InitiateRequest(info);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ request.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+QuotaManagerService::Persist(nsIPrincipal* aPrincipal,
+ nsIQuotaRequest** _retval)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aPrincipal);
+ MOZ_ASSERT(_retval);
+
+ RefPtr request = new Request(aPrincipal);
+
+ PersistParams params;
+
+ nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
+ params.principalInfo());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsAutoPtr info(new RequestInfo(request, params));
+
+ rv = InitiateRequest(info);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ request.forget(_retval);
+ return NS_OK;
+}
+
NS_IMETHODIMP
QuotaManagerService::Observe(nsISupports* aSubject,
const char* aTopic,
diff --git a/dom/quota/nsIQuotaManagerService.idl b/dom/quota/nsIQuotaManagerService.idl
index b529d345567c..99a44476c6ec 100644
--- a/dom/quota/nsIQuotaManagerService.idl
+++ b/dom/quota/nsIQuotaManagerService.idl
@@ -100,4 +100,22 @@ interface nsIQuotaManagerService : nsISupports
*/
[must_use] nsIQuotaRequest
reset();
+
+ /**
+ * Check if given origin is persisted.
+ *
+ * @param aPrincipal
+ * A principal for the origin which we want to check.
+ */
+ [must_use] nsIQuotaRequest
+ persisted(in nsIPrincipal aPrincipal);
+
+ /**
+ * Persist given origin.
+ *
+ * @param aPrincipal
+ * A principal for the origin which we want to persist.
+ */
+ [must_use] nsIQuotaRequest
+ persist(in nsIPrincipal aPrincipal);
};
diff --git a/dom/quota/test/unit/head.js b/dom/quota/test/unit/head.js
index 72114d4d6245..e3606c47dece 100644
--- a/dom/quota/test/unit/head.js
+++ b/dom/quota/test/unit/head.js
@@ -6,6 +6,7 @@
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
const NS_OK = Cr.NS_OK;
+const NS_ERROR_FAILURE = Cr.NS_ERROR_FAILURE;
const NS_ERROR_UNEXPECTED = Cr.NS_ERROR_UNEXPECTED;
function is(a, b, msg)
@@ -146,6 +147,20 @@ function reset(callback)
return request;
}
+function persist(principal, callback) {
+ let request = SpecialPowers._getQuotaManager().persist(principal);
+ request.callback = callback;
+
+ return request;
+}
+
+function persisted(principal, callback) {
+ let request = SpecialPowers._getQuotaManager().persisted(principal);
+ request.callback = callback;
+
+ return request;
+}
+
function installPackage(packageName)
{
let directoryService = Cc["@mozilla.org/file/directory_service;1"]
@@ -237,6 +252,15 @@ function compareBuffers(buffer1, buffer2)
return true;
}
+function getPersistedFromMetadata(readBuffer)
+{
+ const persistedPosition = 8; // Persisted state is stored in the 9th byte
+ let view =
+ readBuffer instanceof Uint8Array ? readBuffer : new Uint8Array(readBuffer);
+
+ return !!view[persistedPosition];
+}
+
function grabUsageAndContinueHandler(request)
{
testGenerator.next(request.usage);
diff --git a/dom/quota/test/unit/test_persist.js b/dom/quota/test/unit/test_persist.js
new file mode 100644
index 000000000000..c2943930677d
--- /dev/null
+++ b/dom/quota/test/unit/test_persist.js
@@ -0,0 +1,150 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function* testSteps()
+{
+ const origins = [
+ {
+ url: "http://default.test.persist",
+ path: "storage/default/http+++default.test.persist",
+ persistence: "default"
+ },
+
+ {
+ url: "ftp://ftp.invalid.origin",
+ path: "storage/default/ftp+++ftp.invalid.origin",
+ persistence: "default"
+ },
+ ];
+
+ const metadataFileName = ".metadata-v2";
+
+ let principal = getPrincipal(origins[0].url);
+
+ info("Persisting an uninitialized origin");
+
+ // Origin directory doesn't exist yet, so only check the result for
+ // persisted().
+ let request = persisted(principal, continueToNextStepSync);
+ yield undefined;
+
+ ok(request.resultCode === NS_OK, "Persisted() succeeded");
+ ok(!request.result, "The origin is not persisted");
+
+ info("Verifying persist() does update the metadata");
+
+ request = persist(principal, continueToNextStepSync);
+ yield undefined;
+
+ ok(request.resultCode === NS_OK, "Persist() succeeded");
+
+ let originDir = getRelativeFile(origins[0].path);
+ let exists = originDir.exists();
+ ok(exists, "Origin directory does exist");
+
+ info("Reading out contents of metadata file");
+
+ let metadataFile = originDir.clone();
+ metadataFile.append(metadataFileName);
+
+ File.createFromNsIFile(metadataFile).then(grabArgAndContinueHandler);
+ let file = yield undefined;
+
+ let fileReader = new FileReader();
+ fileReader.onload = continueToNextStepSync;
+ fileReader.readAsArrayBuffer(file);
+ yield undefined;
+
+ let originPersisted = getPersistedFromMetadata(fileReader.result);
+ ok(originPersisted, "The origin is persisted");
+
+ info("Verifying persisted()");
+
+ request = persisted(principal, continueToNextStepSync);
+ yield undefined;
+
+ ok(request.resultCode === NS_OK, "Persisted() succeeded");
+ ok(request.result === originPersisted, "Persisted() concurs with metadata");
+
+ info("Clearing the origin");
+
+ // Clear the origin since we'll test the same directory again under different
+ // circumstances.
+ clearOrigin(principal, origins[0].persistence, continueToNextStepSync);
+ yield undefined;
+
+ info("Persisting an already initialized origin");
+
+ initOrigin(principal, origins[0].persistence, continueToNextStepSync);
+ yield undefined;
+
+ info("Reading out contents of metadata file");
+
+ fileReader = new FileReader();
+ fileReader.onload = continueToNextStepSync;
+ fileReader.readAsArrayBuffer(file);
+ yield undefined;
+
+ originPersisted = getPersistedFromMetadata(fileReader.result);
+ ok(!originPersisted, "The origin isn't persisted after clearing");
+
+ info("Verifying persisted()");
+
+ request = persisted(principal, continueToNextStepSync);
+ yield undefined;
+
+ ok(request.resultCode === NS_OK, "Persisted() succeeded");
+ ok(request.result === originPersisted, "Persisted() concurs with metadata");
+
+ info("Verifying persist() does update the metadata");
+
+ request = persist(principal, continueToNextStepSync);
+ yield undefined;
+
+ ok(request.resultCode === NS_OK, "Persist() succeeded");
+
+ info("Reading out contents of metadata file");
+
+ fileReader = new FileReader();
+ fileReader.onload = continueToNextStepSync;
+ fileReader.readAsArrayBuffer(file);
+ yield undefined;
+
+ originPersisted = getPersistedFromMetadata(fileReader.result);
+ ok(originPersisted, "The origin is persisted");
+
+ info("Verifying persisted()");
+
+ request = persisted(principal, continueToNextStepSync);
+ yield undefined;
+
+ ok(request.resultCode === NS_OK, "Persisted() succeeded");
+ ok(request.result === originPersisted, "Persisted() concurs with metadata");
+
+ info("Persisting an invalid origin");
+
+ let invalidPrincipal = getPrincipal(origins[1].url);
+
+ request = persist(invalidPrincipal, continueToNextStepSync);
+ yield undefined;
+
+ ok(request.resultCode === NS_ERROR_FAILURE,
+ "Persist() failed because of the invalid origin");
+
+ originDir = getRelativeFile(origins[1].path);
+ exists = originDir.exists();
+ ok(!exists, "Directory for invalid origin doesn't exist");
+
+ request = persisted(invalidPrincipal, continueToNextStepSync);
+ yield undefined;
+
+ ok(request.resultCode === NS_OK, "Persisted() succeeded");
+ ok(!request.result,
+ "The origin isn't persisted since the operation failed");
+
+ finishTest();
+}
diff --git a/dom/quota/test/unit/xpcshell.ini b/dom/quota/test/unit/xpcshell.ini
index c75bcc596282..1ad1b8ff0edb 100644
--- a/dom/quota/test/unit/xpcshell.ini
+++ b/dom/quota/test/unit/xpcshell.ini
@@ -22,6 +22,7 @@ support-files =
[test_morgueCleanup.js]
[test_obsoleteOriginAttributesUpgrade.js]
[test_originAttributesUpgrade.js]
+[test_persist.js]
[test_removeAppsUpgrade.js]
[test_storagePersistentUpgrade.js]
[test_tempMetadataCleanup.js]
diff --git a/dom/security/test/csp/file_iframe_sandbox_srcdoc.html b/dom/security/test/csp/file_iframe_sandbox_srcdoc.html
new file mode 100644
index 000000000000..bc700ed68fb3
--- /dev/null
+++ b/dom/security/test/csp/file_iframe_sandbox_srcdoc.html
@@ -0,0 +1,11 @@
+
+
+
+
+ Bug 1073952 - CSP should restrict scripts in srcdoc iframe even if sandboxed
+
+
+
+
+
diff --git a/dom/security/test/csp/file_iframe_sandbox_srcdoc.html^headers^ b/dom/security/test/csp/file_iframe_sandbox_srcdoc.html^headers^
new file mode 100644
index 000000000000..cf869e07d402
--- /dev/null
+++ b/dom/security/test/csp/file_iframe_sandbox_srcdoc.html^headers^
@@ -0,0 +1 @@
+content-security-policy: default-src *;
diff --git a/dom/security/test/csp/file_iframe_srcdoc.sjs b/dom/security/test/csp/file_iframe_srcdoc.sjs
new file mode 100644
index 000000000000..6de8a029ef1b
--- /dev/null
+++ b/dom/security/test/csp/file_iframe_srcdoc.sjs
@@ -0,0 +1,79 @@
+// Custom *.sjs file specifically for the needs of
+// https://bugzilla.mozilla.org/show_bug.cgi?id=1073952
+
+"use strict";
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+
+const SCRIPT = `
+ `;
+
+const SIMPLE_IFRAME_SRCDOC = `
+
+
+
+
+
+
+ `;
+
+const INNER_SRCDOC_IFRAME = `
+ `;
+
+const NESTED_IFRAME_SRCDOC = `
+
+
+
+
+
+
+ `;
+
+
+const INNER_DATAURI_IFRAME = `
+ `;
+
+const NESTED_IFRAME_SRCDOC_DATAURI = `
+
+
+
+
+
+
+ `;
+
+function handleRequest(request, response) {
+ const query = new URLSearchParams(request.queryString);
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ if (typeof query.get("csp") === "string") {
+ response.setHeader("Content-Security-Policy", query.get("csp"), false);
+ }
+ response.setHeader("Content-Type", "text/html", false);
+
+ if (query.get("action") === "simple_iframe_srcdoc") {
+ response.write(SIMPLE_IFRAME_SRCDOC);
+ return;
+ }
+
+ if (query.get("action") === "nested_iframe_srcdoc") {
+ response.write(NESTED_IFRAME_SRCDOC);
+ return;
+ }
+
+ if (query.get("action") === "nested_iframe_srcdoc_datauri") {
+ response.write(NESTED_IFRAME_SRCDOC_DATAURI);
+ return;
+ }
+
+ // we should never get here, but just in case
+ // return something unexpected
+ response.write("do'h");
+}
diff --git a/dom/security/test/csp/mochitest.ini b/dom/security/test/csp/mochitest.ini
index 58ac777ea5d7..4d3e02b67cd8 100644
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -205,6 +205,9 @@ support-files =
file_upgrade_insecure_navigation.sjs
file_punycode_host_src.sjs
file_punycode_host_src.js
+ file_iframe_srcdoc.sjs
+ file_iframe_sandbox_srcdoc.html
+ file_iframe_sandbox_srcdoc.html^headers^
[test_base-uri.html]
[test_blob_data_schemes.html]
@@ -293,3 +296,5 @@ tags = mcb
[test_strict_dynamic_default_src.html]
[test_upgrade_insecure_navigation.html]
[test_punycode_host_src.html]
+[test_iframe_sandbox_srcdoc.html]
+[test_iframe_srcdoc.html]
diff --git a/dom/security/test/csp/test_iframe_sandbox_srcdoc.html b/dom/security/test/csp/test_iframe_sandbox_srcdoc.html
new file mode 100644
index 000000000000..53beafcacd9c
--- /dev/null
+++ b/dom/security/test/csp/test_iframe_sandbox_srcdoc.html
@@ -0,0 +1,62 @@
+
+
+
+
+ Bug 1073952 - CSP should restrict scripts in srcdoc iframe even if sandboxed
+
+
+
+
+Bug 1073952
+
+
+
+
diff --git a/dom/security/test/csp/test_iframe_srcdoc.html b/dom/security/test/csp/test_iframe_srcdoc.html
new file mode 100644
index 000000000000..95b924a5e387
--- /dev/null
+++ b/dom/security/test/csp/test_iframe_srcdoc.html
@@ -0,0 +1,140 @@
+
+
+
+ Bug 1073952 - Test CSP enforcement within iframe srcdoc
+
+
+
+
+
+
+
+
+
+
diff --git a/dom/smil/nsSMILAnimationController.cpp b/dom/smil/nsSMILAnimationController.cpp
index 45f58bca8c97..ebe142a808f3 100644
--- a/dom/smil/nsSMILAnimationController.cpp
+++ b/dom/smil/nsSMILAnimationController.cpp
@@ -385,6 +385,8 @@ nsSMILAnimationController::DoSample(bool aSkipUnchangedContainers)
// Create the compositor table
nsAutoPtr
currentCompositorTable(new nsSMILCompositorTable(0));
+ nsTArray>
+ animElems(mAnimationElementTable.Count());
for (auto iter = mAnimationElementTable.Iter(); !iter.Done(); iter.Next()) {
SVGAnimationElement* animElem = iter.Get()->GetKey();
@@ -392,6 +394,7 @@ nsSMILAnimationController::DoSample(bool aSkipUnchangedContainers)
AddAnimationToCompositorTable(animElem,
currentCompositorTable,
isStyleFlushNeeded);
+ animElems.AppendElement(animElem);
}
activeContainers.Clear();
diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp
index 321764025900..6095a116d9b6 100644
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -658,7 +658,7 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle aValu
nsCOMPtr responseBody;
rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody));
- if (NS_WARN_IF(NS_FAILED(rv))) {
+ if (NS_WARN_IF(NS_FAILED(rv)) || !responseBody) {
return;
}
diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp
index eb091d26c5b2..7a6827ac7660 100644
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -1580,7 +1580,8 @@ private:
nsresult rv2 =
DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
event, nullptr);
- if (NS_WARN_IF(NS_FAILED(rv2)) || !event->WaitToRespond()) {
+ if ((NS_WARN_IF(NS_FAILED(rv2)) && rv2 != NS_ERROR_XPC_JS_THREW_EXCEPTION) ||
+ !event->WaitToRespond()) {
nsCOMPtr runnable;
MOZ_ASSERT(!aWorkerPrivate->UsesSystemPrincipal(),
"We don't support system-principal serviceworkers");
diff --git a/dom/workers/ServiceWorkerRegistrar.cpp b/dom/workers/ServiceWorkerRegistrar.cpp
index fd44329de034..c1fce972814d 100644
--- a/dom/workers/ServiceWorkerRegistrar.cpp
+++ b/dom/workers/ServiceWorkerRegistrar.cpp
@@ -354,7 +354,7 @@ ServiceWorkerRegistrar::ReadData()
GET_LINE(entry->scope());
entry->principal() =
- mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
+ mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
GET_LINE(entry->currentWorkerURL());
@@ -395,7 +395,7 @@ ServiceWorkerRegistrar::ReadData()
GET_LINE(entry->scope());
entry->principal() =
- mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
+ mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
GET_LINE(entry->currentWorkerURL());
@@ -428,7 +428,7 @@ ServiceWorkerRegistrar::ReadData()
GET_LINE(entry->scope());
entry->principal() =
- mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
+ mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
GET_LINE(entry->currentWorkerURL());
@@ -458,7 +458,7 @@ ServiceWorkerRegistrar::ReadData()
GET_LINE(entry->scope());
entry->principal() =
- mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
+ mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
GET_LINE(entry->currentWorkerURL());
@@ -488,7 +488,7 @@ ServiceWorkerRegistrar::ReadData()
GET_LINE(entry->scope());
entry->principal() =
- mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
+ mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
// scriptSpec is no more used in latest version.
GET_LINE(unused);
diff --git a/dom/workers/test/gtest/TestReadWrite.cpp b/dom/workers/test/gtest/TestReadWrite.cpp
index e53a72af72a7..39baa63bda24 100644
--- a/dom/workers/test/gtest/TestReadWrite.cpp
+++ b/dom/workers/test/gtest/TestReadWrite.cpp
@@ -243,7 +243,8 @@ TEST(ServiceWorkerRegistrar, TestWriteData)
nsAutoCString spec;
spec.AppendPrintf("spec write %d", i);
reg.principal() =
- mozilla::ipc::ContentPrincipalInfo(mozilla::OriginAttributes(i, i % 2), spec);
+ mozilla::ipc::ContentPrincipalInfo(mozilla::OriginAttributes(i, i % 2),
+ mozilla::void_t(), spec);
swr->TestRegisterServiceWorker(reg);
}
@@ -597,7 +598,8 @@ TEST(ServiceWorkerRegistrar, TestDedupeWrite)
nsAutoCString spec;
spec.AppendPrintf("spec write dedupe/%d", i);
reg.principal() =
- mozilla::ipc::ContentPrincipalInfo(mozilla::OriginAttributes(0, false), spec);
+ mozilla::ipc::ContentPrincipalInfo(mozilla::OriginAttributes(0, false),
+ mozilla::void_t(), spec);
swr->TestRegisterServiceWorker(reg);
}
diff --git a/dom/xbl/nsXBLService.cpp b/dom/xbl/nsXBLService.cpp
index 497245b4d5a9..481af11a4f8f 100644
--- a/dom/xbl/nsXBLService.cpp
+++ b/dom/xbl/nsXBLService.cpp
@@ -416,8 +416,10 @@ public:
~AutoStyleNewChildren()
{
nsIPresShell* presShell = mElement->OwnerDoc()->GetShell();
- ServoStyleSet* servoSet = presShell ? presShell->StyleSet()->GetAsServo() : nullptr;
- if (servoSet) {
+ if (!presShell || !presShell->DidInitialize()) {
+ return;
+ }
+ if (ServoStyleSet* servoSet = presShell->StyleSet()->GetAsServo()) {
// In general the element is always styled by the time we're applying XBL
// bindings, because we need to style the element to know what the binding
// URI is. However, programmatic consumers of the XBL service (like the
diff --git a/gfx/angle/src/compiler/translator/glslang_lex.cpp b/gfx/angle/src/compiler/translator/glslang_lex.cpp
index ef9d59c93197..2dcc1bc0f0db 100755
--- a/gfx/angle/src/compiler/translator/glslang_lex.cpp
+++ b/gfx/angle/src/compiler/translator/glslang_lex.cpp
@@ -2338,7 +2338,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
else
{
- yy_size_t num_to_read =
+ int num_to_read =
YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
while ( num_to_read <= 0 )
diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp
index 690ceeb91174..11c691ea53bb 100644
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1273,6 +1273,9 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
flingVelocity.Length().value, gfxPrefs::APZFlingMinVelocityThreshold());
if (flingVelocity.Length() < gfxPrefs::APZFlingMinVelocityThreshold()) {
+ // Relieve overscroll now if needed, since we will not transition to a fling
+ // animation and then an overscroll animation, and relieve it then.
+ GetCurrentTouchBlock()->GetOverscrollHandoffChain()->SnapBackOverscrolledApzc(this);
return nsEventStatus_eConsumeNoDefault;
}
diff --git a/gfx/layers/apz/test/gtest/TestBasic.cpp b/gfx/layers/apz/test/gtest/TestBasic.cpp
index 251b6f92d45a..cb110f6b3b42 100644
--- a/gfx/layers/apz/test/gtest/TestBasic.cpp
+++ b/gfx/layers/apz/test/gtest/TestBasic.cpp
@@ -314,6 +314,23 @@ TEST_F(APZCBasicTester, OverScroll_Bug1152051b) {
SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
+// Tests that the page doesn't get stuck in an
+// overscroll animation after a low-velocity pan.
+TEST_F(APZCBasicTester, OverScrollAfterLowVelocityPan_Bug1343775) {
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ // Pan into overscroll with a velocity less than the
+ // apz.fling_min_velocity_threshold preference.
+ Pan(apzc, 10, 30);
+
+ EXPECT_TRUE(apzc->IsOverscrolled());
+
+ apzc->AdvanceAnimationsUntilEnd();
+
+ // Check that we recovered from overscroll.
+ EXPECT_FALSE(apzc->IsOverscrolled());
+}
+
TEST_F(APZCBasicTester, OverScrollAbort) {
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
diff --git a/gfx/layers/apz/test/mochitest/helper_bug1346632.html b/gfx/layers/apz/test/mochitest/helper_bug1346632.html
new file mode 100644
index 000000000000..ab3653887c0f
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug1346632.html
@@ -0,0 +1,77 @@
+
+
+
+
+
+ Dragging the scrollbar on a page with a fixed-positioned element just past the right edge of the content
+
+
+
+
+
+
+
+
+
+
diff --git a/gfx/layers/apz/test/mochitest/mochitest.ini b/gfx/layers/apz/test/mochitest/mochitest.ini
index 5f7807fb510e..0696805561e2 100644
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -10,6 +10,7 @@
helper_bug1280013.html
helper_bug1285070.html
helper_bug1299195.html
+ helper_bug1346632.html
helper_click.html
helper_div_pan.html
helper_drag_click.html
diff --git a/gfx/layers/apz/test/mochitest/test_group_mouseevents.html b/gfx/layers/apz/test/mochitest/test_group_mouseevents.html
index dcf71f0cc023..afe89d4d4490 100644
--- a/gfx/layers/apz/test/mochitest/test_group_mouseevents.html
+++ b/gfx/layers/apz/test/mochitest/test_group_mouseevents.html
@@ -17,7 +17,9 @@ var subtests = [
// Sanity test for click but with some mouse movement between the down and up
{'file': 'helper_drag_click.html'},
// Test for dragging on a fake-scrollbar element that scrolls the page
- {'file': 'helper_drag_scroll.html'}
+ {'file': 'helper_drag_scroll.html'},
+ // Test for dragging the scrollbar with a fixed-pos element overlaying it
+ {'file': 'helper_bug1346632.html'}
];
if (isApzEnabled()) {
diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp
index 382d0f04d3b4..8ad1d144b101 100644
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -628,8 +628,15 @@ PrepareForSetTargetAPZCNotification(nsIWidget* aWidget,
ScrollableLayerGuid guid(aGuid.mLayersId, 0, FrameMetrics::NULL_SCROLL_ID);
nsPoint point =
nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aRefPoint, aRootFrame);
+ uint32_t flags = 0;
+#ifdef MOZ_WIDGET_ANDROID
+ // On Android, we need IGNORE_ROOT_SCROLL_FRAME for correct hit testing
+ // when zoomed out. On desktop, don't use it because it interferes with
+ // hit testing for some purposes such as scrollbar dragging.
+ flags = nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME;
+#endif
nsIFrame* target =
- nsLayoutUtils::GetFrameForPoint(aRootFrame, point, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
+ nsLayoutUtils::GetFrameForPoint(aRootFrame, point, flags);
nsIScrollableFrame* scrollAncestor = target
? nsLayoutUtils::GetAsyncScrollableAncestorFrame(target)
: aRootFrame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable();
diff --git a/gfx/layers/apz/util/APZThreadUtils.cpp b/gfx/layers/apz/util/APZThreadUtils.cpp
index e1975a2a067f..ee100add452d 100644
--- a/gfx/layers/apz/util/APZThreadUtils.cpp
+++ b/gfx/layers/apz/util/APZThreadUtils.cpp
@@ -74,6 +74,7 @@ APZThreadUtils::IsControllerThread()
}
NS_IMPL_ISUPPORTS(GenericTimerCallbackBase, nsITimerCallback)
+NS_IMPL_ISUPPORTS(GenericNamedTimerCallbackBase, nsITimerCallback, nsINamed)
} // namespace layers
} // namespace mozilla
diff --git a/gfx/layers/apz/util/APZThreadUtils.h b/gfx/layers/apz/util/APZThreadUtils.h
index 4b9b2c0d0072..3ac5bdfbf966 100644
--- a/gfx/layers/apz/util/APZThreadUtils.h
+++ b/gfx/layers/apz/util/APZThreadUtils.h
@@ -7,6 +7,7 @@
#define mozilla_layers_APZThreadUtils_h
#include "base/message_loop.h"
+#include "nsINamed.h"
#include "nsITimer.h"
namespace mozilla {
@@ -98,6 +99,68 @@ GenericTimerCallback* NewTimerCallback(const Function& aFunction)
return new GenericTimerCallback(aFunction);
}
+// A base class for GenericNamedTimerCallback.
+// This is necessary because NS_IMPL_ISUPPORTS doesn't work for a class
+// template.
+class GenericNamedTimerCallbackBase : public nsITimerCallback,
+ public nsINamed
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+protected:
+ virtual ~GenericNamedTimerCallbackBase() {}
+};
+
+// An nsITimerCallback implementation with nsINamed that can be used with any
+// function object that's callable with no arguments.
+template
+class GenericNamedTimerCallback final : public GenericNamedTimerCallbackBase
+{
+public:
+ explicit GenericNamedTimerCallback(const Function& aFunction,
+ const char* aName)
+ : mFunction(aFunction)
+ , mName(aName)
+ {
+ }
+
+ NS_IMETHOD Notify(nsITimer*) override
+ {
+ mFunction();
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetName(nsACString& aName) override
+ {
+ aName = mName;
+ return NS_OK;
+ }
+
+ NS_IMETHOD SetName(const char * aName) override
+ {
+ mName.Assign(aName);
+ return NS_OK;
+ }
+
+private:
+ Function mFunction;
+ nsCString mName;
+};
+
+// Convenience function for constructing a GenericNamedTimerCallback.
+// Returns a raw pointer, suitable for passing directly as an argument to
+// nsITimer::InitWithCallback(). The intention is to enable the following
+// terse inline usage:
+// timer->InitWithCallback(NewNamedTimerCallback([](){ ... }, name), delay);
+template
+GenericNamedTimerCallback*
+ NewNamedTimerCallback(const Function& aFunction,
+ const char* aName)
+{
+ return new GenericNamedTimerCallback(aFunction, aName);
+}
+
} // namespace layers
} // namespace mozilla
diff --git a/gfx/vr/gfxVROculus.cpp b/gfx/vr/gfxVROculus.cpp
index a72b9d736daa..b9d1bd4972c8 100644
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -98,6 +98,7 @@ static pfn_ovr_GetFloatArray ovr_GetFloatArray = nullptr;
static pfn_ovr_SetFloatArray ovr_SetFloatArray = nullptr;
static pfn_ovr_GetString ovr_GetString = nullptr;
static pfn_ovr_SetString ovr_SetString = nullptr;
+static pfn_ovr_GetBoundaryDimensions ovr_GetBoundaryDimensions = nullptr;
#ifdef XP_WIN
static pfn_ovr_CreateTextureSwapChainDX ovr_CreateTextureSwapChainDX = nullptr;
@@ -286,6 +287,7 @@ InitializeOculusCAPI()
REQUIRE_FUNCTION(ovr_SetFloatArray);
REQUIRE_FUNCTION(ovr_GetString);
REQUIRE_FUNCTION(ovr_SetString);
+ REQUIRE_FUNCTION(ovr_GetBoundaryDimensions);
#ifdef XP_WIN
@@ -356,6 +358,7 @@ VRDisplayOculus::VRDisplayOculus(ovrSession aSession)
, mVertexBuffer(nullptr)
, mInputLayout(nullptr)
, mIsPresenting(false)
+ , mEyeHeight(OVR_DEFAULT_EYE_HEIGHT)
{
MOZ_COUNT_CTOR_INHERITED(VRDisplayOculus, VRDisplayHost);
@@ -373,6 +376,7 @@ VRDisplayOculus::VRDisplayOculus(ovrSession aSession)
if (mDesc.AvailableTrackingCaps & ovrTrackingCap_Position) {
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Position;
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
+ mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_StageParameters;
}
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_External;
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
@@ -401,6 +405,8 @@ VRDisplayOculus::VRDisplayOculus(ovrSession aSession)
// take the max of both for eye resolution
mDisplayInfo.mEyeResolution.width = std::max(texSize[VRDisplayInfo::Eye_Left].w, texSize[VRDisplayInfo::Eye_Right].w);
mDisplayInfo.mEyeResolution.height = std::max(texSize[VRDisplayInfo::Eye_Left].h, texSize[VRDisplayInfo::Eye_Right].h);
+
+ UpdateStageParameters();
}
VRDisplayOculus::~VRDisplayOculus() {
@@ -418,10 +424,49 @@ VRDisplayOculus::Destroy()
}
}
+void
+VRDisplayOculus::UpdateStageParameters()
+{
+ ovrVector3f playArea;
+ ovrResult res = ovr_GetBoundaryDimensions(mSession, ovrBoundary_PlayArea, &playArea);
+ if (res == ovrSuccess) {
+ mDisplayInfo.mStageSize.width = playArea.x;
+ mDisplayInfo.mStageSize.height = playArea.z;
+ } else {
+ // If we fail, fall back to reasonable defaults.
+ // 1m x 1m space
+ mDisplayInfo.mStageSize.width = 1.0f;
+ mDisplayInfo.mStageSize.height = 1.0f;
+ }
+
+ mEyeHeight = ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
+
+ mDisplayInfo.mSittingToStandingTransform._11 = 1.0f;
+ mDisplayInfo.mSittingToStandingTransform._12 = 0.0f;
+ mDisplayInfo.mSittingToStandingTransform._13 = 0.0f;
+ mDisplayInfo.mSittingToStandingTransform._14 = 0.0f;
+
+ mDisplayInfo.mSittingToStandingTransform._21 = 0.0f;
+ mDisplayInfo.mSittingToStandingTransform._22 = 1.0f;
+ mDisplayInfo.mSittingToStandingTransform._23 = 0.0f;
+ mDisplayInfo.mSittingToStandingTransform._24 = 0.0f;
+
+ mDisplayInfo.mSittingToStandingTransform._31 = 0.0f;
+ mDisplayInfo.mSittingToStandingTransform._32 = 0.0f;
+ mDisplayInfo.mSittingToStandingTransform._33 = 1.0f;
+ mDisplayInfo.mSittingToStandingTransform._34 = 0.0f;
+
+ mDisplayInfo.mSittingToStandingTransform._41 = 0.0f;
+ mDisplayInfo.mSittingToStandingTransform._42 = mEyeHeight;
+ mDisplayInfo.mSittingToStandingTransform._43 = 0.0f;
+ mDisplayInfo.mSittingToStandingTransform._44 = 1.0f;
+}
+
void
VRDisplayOculus::ZeroSensor()
{
ovr_RecenterTrackingOrigin(mSession);
+ UpdateStageParameters();
}
VRHMDSensorState
@@ -440,6 +485,7 @@ VRDisplayOculus::GetSensorState()
result = GetSensorState(frameDelta);
result.inputFrameID = mInputFrameID;
mLastSensorState[result.inputFrameID % kMaxLatencyFrames] = result;
+ result.position[1] -= mEyeHeight;
return result;
}
@@ -988,6 +1034,11 @@ VRSystemManagerOculus::GetHMDs(nsTArray>& aHMDResult)
ovrResult orv = ovr_Create(&session, &luid);
if (orv == ovrSuccess) {
mSession = session;
+ orv = ovr_SetTrackingOriginType(session, ovrTrackingOrigin_FloorLevel);
+ if (orv != ovrSuccess) {
+ NS_WARNING("ovr_SetTrackingOriginType failed.\n");
+ }
+
mHMDInfo = new VRDisplayOculus(session);
}
}
@@ -1099,6 +1150,9 @@ VRSystemManagerOculus::HandleInput()
poseState.linearAcceleration[0] = pose.LinearAcceleration.x;
poseState.linearAcceleration[1] = pose.LinearAcceleration.y;
poseState.linearAcceleration[2] = pose.LinearAcceleration.z;
+
+ float eyeHeight = ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
+ poseState.position[1] -= eyeHeight;
}
HandlePoseTracking(i, poseState, controller);
}
diff --git a/gfx/vr/gfxVROculus.h b/gfx/vr/gfxVROculus.h
index 7a233925773d..3d99138f5a93 100644
--- a/gfx/vr/gfxVROculus.h
+++ b/gfx/vr/gfxVROculus.h
@@ -48,6 +48,7 @@ protected:
const VRHMDSensorState& aSensorState,
const gfx::Rect& aLeftEyeRect,
const gfx::Rect& aRightEyeRect) override;
+ void UpdateStageParameters();
public:
explicit VRDisplayOculus(ovrSession aSession);
@@ -82,7 +83,8 @@ protected:
RefPtr mInputLayout;
bool mIsPresenting;
-
+ float mEyeHeight;
+
bool UpdateConstantBuffers();
struct Vertex
diff --git a/gfx/vr/ipc/PVRManager.ipdl b/gfx/vr/ipc/PVRManager.ipdl
index 61e1bff271a6..2920a352560c 100644
--- a/gfx/vr/ipc/PVRManager.ipdl
+++ b/gfx/vr/ipc/PVRManager.ipdl
@@ -45,10 +45,6 @@ parent:
// asynchronously to children via UpdateDisplayInfo.
async RefreshDisplays();
- // GetDisplays synchronously returns the VR displays that have already been
- // enumerated by RefreshDisplays() but does not enumerate new ones.
- sync GetDisplays() returns(VRDisplayInfo[] aDisplayInfo);
-
// Reset the sensor of the display identified by aDisplayID so that the current
// sensor state is the "Zero" position.
async ResetSensor(uint32_t aDisplayID);
diff --git a/gfx/vr/ipc/VRManagerChild.cpp b/gfx/vr/ipc/VRManagerChild.cpp
index 5ee0b9af4207..d33951cb4d77 100644
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -293,16 +293,6 @@ VRManagerChild::RecvUpdateDisplayInfo(nsTArray&& aDisplayUpdates)
bool
VRManagerChild::GetVRDisplays(nsTArray>& aDisplays)
{
- if (!mDisplaysInitialized) {
- /**
- * If we haven't received any asynchronous callback after requesting
- * display enumeration with RefreshDisplays, get the existing displays
- * that have already been enumerated by other VRManagerChild instances.
- */
- nsTArray displays;
- Unused << SendGetDisplays(&displays);
- UpdateDisplayInfo(displays);
- }
aDisplays = mDisplays;
return true;
}
diff --git a/gfx/vr/ipc/VRManagerParent.cpp b/gfx/vr/ipc/VRManagerParent.cpp
index e413f24d8b4d..af72b18a7253 100644
--- a/gfx/vr/ipc/VRManagerParent.cpp
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -241,14 +241,6 @@ VRManagerParent::RecvRefreshDisplays()
return IPC_OK();
}
-mozilla::ipc::IPCResult
-VRManagerParent::RecvGetDisplays(nsTArray *aDisplays)
-{
- VRManager* vm = VRManager::Get();
- vm->GetVRDisplayInfo(*aDisplays);
- return IPC_OK();
-}
-
mozilla::ipc::IPCResult
VRManagerParent::RecvResetSensor(const uint32_t& aDisplayID)
{
diff --git a/gfx/vr/ipc/VRManagerParent.h b/gfx/vr/ipc/VRManagerParent.h
index 28d12d5a2200..0eaa18669729 100644
--- a/gfx/vr/ipc/VRManagerParent.h
+++ b/gfx/vr/ipc/VRManagerParent.h
@@ -86,7 +86,6 @@ protected:
void OnChannelConnected(int32_t pid) override;
virtual mozilla::ipc::IPCResult RecvRefreshDisplays() override;
- virtual mozilla::ipc::IPCResult RecvGetDisplays(nsTArray *aDisplays) override;
virtual mozilla::ipc::IPCResult RecvResetSensor(const uint32_t& aDisplayID) override;
virtual mozilla::ipc::IPCResult RecvGetSensorState(const uint32_t& aDisplayID, VRHMDSensorState* aState) override;
virtual mozilla::ipc::IPCResult RecvSetHaveEventListener(const bool& aHaveEventListener) override;
diff --git a/gfx/vr/ovr_capi_dynamic.h b/gfx/vr/ovr_capi_dynamic.h
index edf2b78a3d94..9a4ae06ba056 100644
--- a/gfx/vr/ovr_capi_dynamic.h
+++ b/gfx/vr/ovr_capi_dynamic.h
@@ -567,8 +567,15 @@ typedef enum {
ovrDebugHudStereo_EnumSize = 0x7fffffff
} ovrDebugHudStereoMode;
+typedef enum {
+ // Outer boundary - closely represents user setup walls
+ ovrBoundary_Outer = 0x0001,
+ // Play area - safe rectangular area inside outer boundary which can optionally be used to restrict user interactions and motion.
+ ovrBoundary_PlayArea = 0x0100,
+} ovrBoundaryType;
+
typedef ovrBool(OVR_PFN* pfn_ovr_GetBool)(ovrSession session, const char* propertyName, ovrBool defaultVal);
-typedef ovrBool(OVR_PFN* pfn_ovr_SetBool)(ovrSession session, const char* propertyName, ovrBool value);
+typedef ovrBool(OVR_PFN* pfn_ovr_SetBool)(ovrSession session, const char* propertyName, ovrBool value);
typedef int (OVR_PFN* pfn_ovr_GetInt)(ovrSession session, const char* propertyName, int defaultVal);
typedef ovrBool (OVR_PFN* pfn_ovr_SetInt)(ovrSession session, const char* propertyName, int value);
typedef float (OVR_PFN* pfn_ovr_GetFloat)(ovrSession session, const char* propertyName, float defaultVal);
@@ -581,8 +588,9 @@ typedef const char* (OVR_PFN* pfn_ovr_GetString)(ovrSession session, const char*
const char* defaultVal);
typedef ovrBool (OVR_PFN* pfn_ovr_SetString)(ovrSession session, const char* propertyName,
const char* value);
-
-
+typedef ovrResult (OVR_PFN* pfn_ovr_GetBoundaryDimensions)(ovrSession session,
+ ovrBoundaryType boundaryType,
+ ovrVector3f* outDimensions);
typedef enum {
ovrError_MemoryAllocationFailure = -1000,
@@ -714,7 +722,10 @@ typedef ovrResult (OVR_PFN* pfn_ovr_GetMirrorTextureBufferGL)(ovrSession session
ovrMirrorTexture mirrorTexture,
unsigned int* out_TexId);
-#ifdef __cplusplus
+#define OVR_KEY_EYE_HEIGHT "EyeHeight" // float meters
+#define OVR_DEFAULT_EYE_HEIGHT 1.675f
+
+#ifdef __cplusplus
}
#endif
diff --git a/image/imgFrame.h b/image/imgFrame.h
index 34d19447620a..77ff3507b177 100644
--- a/image/imgFrame.h
+++ b/image/imgFrame.h
@@ -478,6 +478,9 @@ public:
explicit DrawableFrameRef(imgFrame* aFrame)
: mFrame(aFrame)
{
+ MOZ_ASSERT(aFrame);
+ MonitorAutoLock lock(aFrame->mMonitor);
+
// Paletted images won't have a surface so there is no strong reference
// to hold on to. Since Draw() and GetSourceSurface() calls will not work
// in that case, we should be using RawAccessFrameRef exclusively instead.
diff --git a/intl/locale/LocaleService.cpp b/intl/locale/LocaleService.cpp
index 6374fada5227..a35c71d6bbf6 100644
--- a/intl/locale/LocaleService.cpp
+++ b/intl/locale/LocaleService.cpp
@@ -463,7 +463,7 @@ LocaleService::NegotiateLanguages(const char** aRequested,
// Check that the given string contains only ASCII characters valid in tags
// (i.e. alphanumerics, plus '-' and '_'), and is non-empty.
auto validTagChars = [](const char* s) {
- if (!*s) {
+ if (!s || !*s) {
return false;
}
while (*s) {
diff --git a/intl/locale/tests/unit/test_localeService_negotiateLanguages.js b/intl/locale/tests/unit/test_localeService_negotiateLanguages.js
index 29af067708d1..8a5b33f7d6fc 100644
--- a/intl/locale/tests/unit/test_localeService_negotiateLanguages.js
+++ b/intl/locale/tests/unit/test_localeService_negotiateLanguages.js
@@ -86,6 +86,11 @@ const data = {
],
"should not crash on invalid input": [
[null, ["fr-FR"], []],
+ [[null], [], []],
+ [[undefined], [], []],
+ [[undefined], [null], []],
+ [[undefined], [undefined], []],
+ [[null], [null], null, null, []],
[undefined, ["fr-FR"], []],
[2, ["fr-FR"], []],
["fr-FR", ["fr-FR"], []],
diff --git a/ipc/glue/BackgroundUtils.cpp b/ipc/glue/BackgroundUtils.cpp
index 341b9ecba22a..8d6014e4749f 100644
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -94,6 +94,18 @@ PrincipalInfoToPrincipal(const PrincipalInfo& aPrincipalInfo,
return nullptr;
}
+ // When the principal is serialized, the origin is extract from it. This
+ // can fail, and in case, here we will havea Tvoid_t. If we have a string,
+ // it must match with what the_new_principal.getOrigin returns.
+ if (info.originNoSuffix().type() == ContentPrincipalInfoOriginNoSuffix::TnsCString) {
+ nsAutoCString originNoSuffix;
+ rv = principal->GetOriginNoSuffix(originNoSuffix);
+ if (NS_WARN_IF(NS_FAILED(rv)) ||
+ !info.originNoSuffix().get_nsCString().Equals(originNoSuffix)) {
+ MOZ_CRASH("If the origin was in the contentPrincipalInfo, it must be available when deserialized");
+ }
+ }
+
return principal.forget();
}
@@ -222,8 +234,18 @@ PrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
return rv;
}
+ ContentPrincipalInfoOriginNoSuffix infoOriginNoSuffix;
+
+ nsCString originNoSuffix;
+ rv = aPrincipal->GetOriginNoSuffix(originNoSuffix);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ infoOriginNoSuffix = void_t();
+ } else {
+ infoOriginNoSuffix = originNoSuffix;
+ }
+
*aPrincipalInfo = ContentPrincipalInfo(aPrincipal->OriginAttributesRef(),
- spec);
+ infoOriginNoSuffix, spec);
return NS_OK;
}
diff --git a/ipc/glue/IPCStreamUtils.cpp b/ipc/glue/IPCStreamUtils.cpp
index f9dc1e6aede2..4cae37fc701e 100644
--- a/ipc/glue/IPCStreamUtils.cpp
+++ b/ipc/glue/IPCStreamUtils.cpp
@@ -392,7 +392,7 @@ DeserializeIPCStream(const IPCStream& aValue)
Unused << fdSetActor->Send__delete__(fdSetActor);
}
- return DeserializeInputStream(streamWithFds.stream(), fds);
+ return InputStreamHelper::DeserializeInputStream(streamWithFds.stream(), fds);
}
already_AddRefed
diff --git a/ipc/glue/InputStreamUtils.cpp b/ipc/glue/InputStreamUtils.cpp
index bf4ee02fa7e9..883738859b11 100644
--- a/ipc/glue/InputStreamUtils.cpp
+++ b/ipc/glue/InputStreamUtils.cpp
@@ -40,9 +40,9 @@ namespace mozilla {
namespace ipc {
void
-SerializeInputStream(nsIInputStream* aInputStream,
- InputStreamParams& aParams,
- nsTArray& aFileDescriptors)
+InputStreamHelper::SerializeInputStream(nsIInputStream* aInputStream,
+ InputStreamParams& aParams,
+ nsTArray& aFileDescriptors)
{
MOZ_ASSERT(aInputStream);
@@ -59,24 +59,9 @@ SerializeInputStream(nsIInputStream* aInputStream,
}
}
-void
-SerializeInputStream(nsIInputStream* aInputStream,
- OptionalInputStreamParams& aParams,
- nsTArray& aFileDescriptors)
-{
- if (aInputStream) {
- InputStreamParams params;
- SerializeInputStream(aInputStream, params, aFileDescriptors);
- aParams = params;
- }
- else {
- aParams = mozilla::void_t();
- }
-}
-
already_AddRefed
-DeserializeInputStream(const InputStreamParams& aParams,
- const nsTArray& aFileDescriptors)
+InputStreamHelper::DeserializeInputStream(const InputStreamParams& aParams,
+ const nsTArray& aFileDescriptors)
{
nsCOMPtr stream;
nsCOMPtr serializable;
@@ -166,28 +151,5 @@ DeserializeInputStream(const InputStreamParams& aParams,
return stream.forget();
}
-already_AddRefed
-DeserializeInputStream(const OptionalInputStreamParams& aParams,
- const nsTArray& aFileDescriptors)
-{
- nsCOMPtr stream;
-
- switch (aParams.type()) {
- case OptionalInputStreamParams::Tvoid_t:
- // Leave stream null.
- break;
-
- case OptionalInputStreamParams::TInputStreamParams:
- stream = DeserializeInputStream(aParams.get_InputStreamParams(),
- aFileDescriptors);
- break;
-
- default:
- MOZ_ASSERT(false, "Unknown params!");
- }
-
- return stream.forget();
-}
-
} // namespace ipc
} // namespace mozilla
diff --git a/ipc/glue/InputStreamUtils.h b/ipc/glue/InputStreamUtils.h
index 215a8cb23d0a..b00da0170207 100644
--- a/ipc/glue/InputStreamUtils.h
+++ b/ipc/glue/InputStreamUtils.h
@@ -17,23 +17,19 @@ namespace ipc {
class FileDescriptor;
-void
-SerializeInputStream(nsIInputStream* aInputStream,
- InputStreamParams& aParams,
- nsTArray& aFileDescriptors);
+// If you want to serialize an inputStream, please use AutoIPCStream.
+class InputStreamHelper
+{
+public:
+ static void
+ SerializeInputStream(nsIInputStream* aInputStream,
+ InputStreamParams& aParams,
+ nsTArray& aFileDescriptors);
-void
-SerializeInputStream(nsIInputStream* aInputStream,
- OptionalInputStreamParams& aParams,
- nsTArray& aFileDescriptors);
-
-already_AddRefed
-DeserializeInputStream(const InputStreamParams& aParams,
- const nsTArray& aFileDescriptors);
-
-already_AddRefed
-DeserializeInputStream(const OptionalInputStreamParams& aParams,
- const nsTArray& aFileDescriptors);
+ static already_AddRefed
+ DeserializeInputStream(const InputStreamParams& aParams,
+ const nsTArray& aFileDescriptors);
+};
} // namespace ipc
} // namespace mozilla
diff --git a/ipc/glue/PBackgroundSharedTypes.ipdlh b/ipc/glue/PBackgroundSharedTypes.ipdlh
index d29737f07bdc..2e5e835a3139 100644
--- a/ipc/glue/PBackgroundSharedTypes.ipdlh
+++ b/ipc/glue/PBackgroundSharedTypes.ipdlh
@@ -8,9 +8,21 @@ using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
namespace mozilla {
namespace ipc {
+union ContentPrincipalInfoOriginNoSuffix
+{
+ nsCString;
+ void_t;
+};
+
struct ContentPrincipalInfo
{
OriginAttributes attrs;
+
+ // nsIPrincipal.originNoSuffix can fail. In case this happens, this value
+ // will be set to void_t. So far, this is used only for dom/media.
+ // It will be removed in bug 1347817.
+ ContentPrincipalInfoOriginNoSuffix originNoSuffix;
+
nsCString spec;
};
diff --git a/ipc/ipdl/sync-messages.ini b/ipc/ipdl/sync-messages.ini
index 22682b2489bc..be20e076a070 100644
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -949,8 +949,6 @@ description =
description =
[PWebRenderBridge::DPGetSnapshot]
description =
-[PVRManager::GetDisplays]
-description =
[PVRManager::GetSensorState]
description =
[PVRManager::SetHaveEventListener]
diff --git a/js/src/builtin/.eslintrc.js b/js/src/builtin/.eslintrc.js
index 26d8b2205a2d..83bb7ce846ec 100644
--- a/js/src/builtin/.eslintrc.js
+++ b/js/src/builtin/.eslintrc.js
@@ -5,6 +5,10 @@ module.exports = {
"../../../toolkit/.eslintrc.js"
],
+ "plugins": [
+ "spidermonkey-js"
+ ],
+
"rules": {
// We should fix those at some point, but we use this to detect NaNs.
"no-self-compare": "off",
diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js
index 16e4ebdcd4fa..c369ee81a1d1 100644
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -10,10 +10,6 @@ var GCSuppressionTypes = [];
var ignoreIndirectCalls = {
"mallocSizeOf" : true,
"aMallocSizeOf" : true,
- "_malloc_message" : true,
- "je_malloc_message" : true,
- "chunk_dalloc" : true,
- "chunk_alloc" : true,
"__conv" : true,
"__convf" : true,
"prerrortable.c:callback_newtable" : true,
@@ -175,6 +171,12 @@ var ignoreFunctions = {
// Bug 1056410 - devirtualization prevents the standard nsISupports::Release heuristic from working
"uint32 nsXPConnect::Release()" : true,
+ // Allocation API
+ "malloc": true,
+ "calloc": true,
+ "realloc": true,
+ "free": true,
+
// FIXME!
"NS_LogInit": true,
"NS_LogTerm": true,
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index 4b5c8f416858..38d3031bc1c5 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1010,8 +1010,10 @@ Parser::reportMissingClosing(unsigned errorNumber, unsigned noteNu
uint32_t openedPos)
{
auto notes = MakeUnique();
- if (!notes)
+ if (!notes) {
+ ReportOutOfMemory(pc->sc()->context);
return;
+ }
uint32_t line, column;
tokenStream.srcCoords.lineNumAndColumnIndex(openedPos, &line, &column);
@@ -1048,8 +1050,10 @@ Parser::reportRedeclaration(HandlePropertyName name, DeclarationKi
}
auto notes = MakeUnique();
- if (!notes)
+ if (!notes) {
+ ReportOutOfMemory(pc->sc()->context);
return;
+ }
uint32_t line, column;
tokenStream.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column);
diff --git a/js/src/jit-test/tests/asm.js/testZOOB.js b/js/src/jit-test/tests/asm.js/testZOOB.js
index ae59611d6e68..3ab740fb427a 100644
--- a/js/src/jit-test/tests/asm.js/testZOOB.js
+++ b/js/src/jit-test/tests/asm.js/testZOOB.js
@@ -5,6 +5,11 @@ load(libdir + "asserts.js");
if (!isAsmJSCompilationAvailable())
quit();
+// This test runs a lot of code and is very slow with --ion-eager. Use a minimum
+// Ion warmup trigger of 2 to avoid timeouts.
+if (getJitCompilerOptions()["ion.warmup.trigger"] < 5)
+ setJitCompilerOption("ion.warmup.trigger", 5);
+
var ab = new ArrayBuffer(BUF_MIN);
// Compute a set of interesting indices.
diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp
index 2663fcb38061..6e41d489b48b 100644
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -381,6 +381,126 @@ BaselineCacheIRCompiler::emitLoadDynamicSlotResult()
return true;
}
+bool
+BaselineCacheIRCompiler::emitMegamorphicLoadSlotResult()
+{
+ AutoOutputRegister output(*this);
+
+ Register obj = allocator.useRegister(masm, reader.objOperandId());
+ Address nameAddr = stubAddress(reader.stubOffset());
+ bool handleMissing = reader.readBool();
+
+ AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output);
+ AutoScratchRegister scratch2(allocator, masm);
+ AutoScratchRegister scratch3(allocator, masm);
+
+ FailurePath* failure;
+ if (!addFailurePath(&failure))
+ return false;
+
+ masm.Push(UndefinedValue());
+ masm.moveStackPtrTo(scratch3.get());
+
+ LiveRegisterSet volatileRegs(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
+ volatileRegs.takeUnchecked(scratch1);
+ volatileRegs.takeUnchecked(scratch2);
+ volatileRegs.takeUnchecked(scratch3);
+ masm.PushRegsInMask(volatileRegs);
+
+ masm.setupUnalignedABICall(scratch1);
+ masm.loadJSContext(scratch1);
+ masm.passABIArg(scratch1);
+ masm.passABIArg(obj);
+ masm.loadPtr(nameAddr, scratch2);
+ masm.passABIArg(scratch2);
+ masm.passABIArg(scratch3);
+ if (handleMissing)
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, (GetNativeDataProperty)));
+ else
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, (GetNativeDataProperty)));
+ masm.mov(ReturnReg, scratch2);
+ masm.PopRegsInMask(volatileRegs);
+
+ masm.loadTypedOrValue(Address(masm.getStackPointer(), 0), output);
+ masm.adjustStack(sizeof(Value));
+
+ masm.branchIfFalseBool(scratch2, failure->label());
+ return true;
+}
+
+bool
+BaselineCacheIRCompiler::emitMegamorphicStoreSlot()
+{
+ Register obj = allocator.useRegister(masm, reader.objOperandId());
+ Address nameAddr = stubAddress(reader.stubOffset());
+ ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+
+ AutoScratchRegister scratch1(allocator, masm);
+ AutoScratchRegister scratch2(allocator, masm);
+
+ FailurePath* failure;
+ if (!addFailurePath(&failure))
+ return false;
+
+ masm.Push(val);
+ masm.moveStackPtrTo(val.scratchReg());
+
+ LiveRegisterSet volatileRegs(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
+ volatileRegs.takeUnchecked(scratch1);
+ volatileRegs.takeUnchecked(scratch2);
+ volatileRegs.takeUnchecked(val);
+ masm.PushRegsInMask(volatileRegs);
+
+ masm.setupUnalignedABICall(scratch1);
+ masm.loadJSContext(scratch1);
+ masm.passABIArg(scratch1);
+ masm.passABIArg(obj);
+ masm.loadPtr(nameAddr, scratch2);
+ masm.passABIArg(scratch2);
+ masm.passABIArg(val.scratchReg());
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, SetNativeDataProperty));
+ masm.mov(ReturnReg, scratch1);
+ masm.PopRegsInMask(volatileRegs);
+
+ masm.loadValue(Address(masm.getStackPointer(), 0), val);
+ masm.adjustStack(sizeof(Value));
+
+ masm.branchIfFalseBool(scratch1, failure->label());
+ return true;
+}
+
+bool
+BaselineCacheIRCompiler::emitGuardHasGetterSetter()
+{
+ Register obj = allocator.useRegister(masm, reader.objOperandId());
+ Address shapeAddr = stubAddress(reader.stubOffset());
+
+ AutoScratchRegister scratch1(allocator, masm);
+ AutoScratchRegister scratch2(allocator, masm);
+
+ FailurePath* failure;
+ if (!addFailurePath(&failure))
+ return false;
+
+ LiveRegisterSet volatileRegs(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
+ volatileRegs.takeUnchecked(scratch1);
+ volatileRegs.takeUnchecked(scratch2);
+ masm.PushRegsInMask(volatileRegs);
+
+ masm.setupUnalignedABICall(scratch1);
+ masm.loadJSContext(scratch1);
+ masm.passABIArg(scratch1);
+ masm.passABIArg(obj);
+ masm.loadPtr(shapeAddr, scratch2);
+ masm.passABIArg(scratch2);
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ObjectHasGetterSetter));
+ masm.mov(ReturnReg, scratch1);
+ masm.PopRegsInMask(volatileRegs);
+
+ masm.branchIfFalseBool(scratch1, failure->label());
+ return true;
+}
+
bool
BaselineCacheIRCompiler::emitCallScriptedGetterResult()
{
@@ -1737,12 +1857,14 @@ static const size_t MaxOptimizedCacheIRStubs = 16;
ICStub*
jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
CacheKind kind, ICStubEngine engine, JSScript* outerScript,
- ICFallbackStub* stub)
+ ICFallbackStub* stub, bool* attached)
{
// We shouldn't GC or report OOM (or any other exception) here.
AutoAssertNoPendingException aanpe(cx);
JS::AutoCheckCannotGC nogc;
+ MOZ_ASSERT(!*attached);
+
if (writer.failed())
return nullptr;
@@ -1810,6 +1932,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
// for some reason and the IR generator doesn't check for exactly the same
// conditions.
for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
+ bool updated = false;
switch (stubKind) {
case CacheIRStubKind::Regular: {
if (!iter->isCacheIR_Regular())
@@ -1817,7 +1940,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
auto otherStub = iter->toCacheIR_Regular();
if (otherStub->stubInfo() != stubInfo)
continue;
- if (!writer.stubDataEqualsMaybeUpdate(otherStub->stubDataStart()))
+ if (!writer.stubDataEqualsMaybeUpdate(otherStub->stubDataStart(), &updated))
continue;
break;
}
@@ -1827,7 +1950,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
auto otherStub = iter->toCacheIR_Monitored();
if (otherStub->stubInfo() != stubInfo)
continue;
- if (!writer.stubDataEqualsMaybeUpdate(otherStub->stubDataStart()))
+ if (!writer.stubDataEqualsMaybeUpdate(otherStub->stubDataStart(), &updated))
continue;
break;
}
@@ -1837,7 +1960,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
auto otherStub = iter->toCacheIR_Updated();
if (otherStub->stubInfo() != stubInfo)
continue;
- if (!writer.stubDataEqualsMaybeUpdate(otherStub->stubDataStart()))
+ if (!writer.stubDataEqualsMaybeUpdate(otherStub->stubDataStart(), &updated))
continue;
break;
}
@@ -1846,6 +1969,8 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
// We found a stub that's exactly the same as the stub we're about to
// attach. Just return nullptr, the caller should do nothing in this
// case.
+ if (updated)
+ *attached = true;
return nullptr;
}
@@ -1864,6 +1989,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
auto newStub = new(newStubMem) ICCacheIR_Regular(code, stubInfo);
writer.copyStubData(newStub->stubDataStart());
stub->addNewStub(newStub);
+ *attached = true;
return newStub;
}
case CacheIRStubKind::Monitored: {
@@ -1872,6 +1998,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
auto newStub = new(newStubMem) ICCacheIR_Monitored(code, monitorStub, stubInfo);
writer.copyStubData(newStub->stubDataStart());
stub->addNewStub(newStub);
+ *attached = true;
return newStub;
}
case CacheIRStubKind::Updated: {
@@ -1882,6 +2009,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
}
writer.copyStubData(newStub->stubDataStart());
stub->addNewStub(newStub);
+ *attached = true;
return newStub;
}
}
diff --git a/js/src/jit/BaselineCacheIRCompiler.h b/js/src/jit/BaselineCacheIRCompiler.h
index d55c32d631c2..8ad48da7221e 100644
--- a/js/src/jit/BaselineCacheIRCompiler.h
+++ b/js/src/jit/BaselineCacheIRCompiler.h
@@ -19,7 +19,7 @@ class ICStub;
ICStub* AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
CacheKind kind, ICStubEngine engine, JSScript* outerScript,
- ICFallbackStub* stub);
+ ICFallbackStub* stub, bool* attached);
} // namespace jit
} // namespace js
diff --git a/js/src/jit/BaselineDebugModeOSR.cpp b/js/src/jit/BaselineDebugModeOSR.cpp
index 17871f16e46f..dc4c56dbd27c 100644
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -699,8 +699,7 @@ RecompileBaselineScriptForDebugMode(JSContext* cx, JSScript* script,
_(Call_ClassHook) \
_(Call_ScriptedApplyArray) \
_(Call_ScriptedApplyArguments) \
- _(Call_ScriptedFunCall) \
- _(GetProp_Generic)
+ _(Call_ScriptedFunCall)
static bool
CloneOldBaselineStub(JSContext* cx, DebugModeOSREntryVector& entries, size_t entryIndex)
diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp
index 7e0270a4eac1..42b7e8649afb 100644
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -803,30 +803,29 @@ DoGetElemFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_
}
bool attached = false;
- if (stub->numOptimizedStubs() >= ICGetElem_Fallback::MAX_OPTIMIZED_STUBS) {
- // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
- // But for now we just bail.
- stub->noteUnoptimizableAccess();
- attached = true;
- }
-
bool isTemporarilyUnoptimizable = false;
- if (!attached && !JitOptions.disableCacheIR) {
+
+ if (stub->state().maybeTransition())
+ stub->discardStubs(cx);
+
+ if (stub->state().canAttachStub()) {
ICStubEngine engine = ICStubEngine::Baseline;
- GetPropIRGenerator gen(cx, script, pc, CacheKind::GetElem,
+ GetPropIRGenerator gen(cx, script, pc, CacheKind::GetElem, stub->state().mode(),
&isTemporarilyUnoptimizable, lhs, rhs, CanAttachGetter::Yes);
if (gen.tryAttachStub()) {
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
- engine, info.outerScript(cx), stub);
+ engine, info.outerScript(cx), stub,
+ &attached);
if (newStub) {
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
- attached = true;
if (gen.shouldNotePreliminaryObjectStub())
newStub->toCacheIR_Monitored()->notePreliminaryObject();
else if (gen.shouldUnlinkPreliminaryObjectStubs())
StripPreliminaryObjectStubs(cx, stub);
}
}
+ if (!attached && !isTemporarilyUnoptimizable)
+ stub->state().trackNotAttached();
}
if (!isOptimizedArgs) {
@@ -957,19 +956,20 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
}
bool isTemporarilyUnoptimizable = false;
-
bool attached = false;
- if (stub->numOptimizedStubs() < ICSetElem_Fallback::MAX_OPTIMIZED_STUBS &&
- !JitOptions.disableCacheIR)
- {
- SetPropIRGenerator gen(cx, script, pc, CacheKind::SetElem, &isTemporarilyUnoptimizable,
- objv, index, rhs);
+
+ if (stub->state().maybeTransition())
+ stub->discardStubs(cx);
+
+ if (stub->state().canAttachStub()) {
+ SetPropIRGenerator gen(cx, script, pc, CacheKind::SetElem, stub->state().mode(),
+ &isTemporarilyUnoptimizable, objv, index, rhs);
if (gen.tryAttachStub()) {
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
- ICStubEngine::Baseline, frame->script(), stub);
+ ICStubEngine::Baseline, frame->script(),
+ stub, &attached);
if (newStub) {
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
- attached = true;
SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
@@ -1019,27 +1019,23 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
if (attached)
return true;
- if (stub->numOptimizedStubs() >= ICSetElem_Fallback::MAX_OPTIMIZED_STUBS) {
- // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
- // But for now we just bail.
- return true;
- }
-
- if (!JitOptions.disableCacheIR) {
- SetPropIRGenerator gen(cx, script, pc, CacheKind::SetElem, &isTemporarilyUnoptimizable,
- objv, index, rhs);
+ if (stub->state().canAttachStub()) {
+ SetPropIRGenerator gen(cx, script, pc, CacheKind::SetElem, stub->state().mode(),
+ &isTemporarilyUnoptimizable, objv, index, rhs);
if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
- ICStubEngine::Baseline, frame->script(), stub);
+ ICStubEngine::Baseline, frame->script(),
+ stub, &attached);
if (newStub) {
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
- attached = true;
SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
return true;
}
} else {
gen.trackNotAttached();
}
+ if (!attached && !isTemporarilyUnoptimizable)
+ stub->state().trackNotAttached();
}
return true;
@@ -1208,26 +1204,26 @@ DoInFallback(JSContext* cx, BaselineFrame* frame, ICIn_Fallback* stub_,
return false;
}
- bool attached = false;
-
- if (stub->numOptimizedStubs() >= ICIn_Fallback::MAX_OPTIMIZED_STUBS)
- attached = true;
-
- RootedScript script(cx, frame->script());
RootedObject obj(cx, &objValue.toObject());
- jsbytecode* pc = stub->icEntry()->pc(script);
- if (!attached && !JitOptions.disableCacheIR) {
+ if (stub->state().maybeTransition())
+ stub->discardStubs(cx);
+
+ if (stub->state().canAttachStub()) {
+ RootedScript script(cx, frame->script());
+ jsbytecode* pc = stub->icEntry()->pc(script);
+
ICStubEngine engine = ICStubEngine::Baseline;
- InIRGenerator gen(cx, script, pc, key, obj);
+ InIRGenerator gen(cx, script, pc, stub->state().mode(), key, obj);
+ bool attached = false;
if (gen.tryAttachStub()) {
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
- engine, script, stub);
- if (newStub) {
+ engine, script, stub, &attached);
+ if (newStub)
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
- attached = true;
- }
}
+ if (!attached)
+ stub->state().trackNotAttached();
}
bool cond = false;
@@ -1282,23 +1278,21 @@ DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_
RootedPropertyName name(cx, script->getName(pc));
bool attached = false;
- // Attach new stub.
- if (stub->numOptimizedStubs() >= ICGetName_Fallback::MAX_OPTIMIZED_STUBS) {
- // TODO: Discard all stubs in this IC and replace with generic stub.
- attached = true;
- }
+ if (stub->state().maybeTransition())
+ stub->discardStubs(cx);
- if (!attached && !JitOptions.disableCacheIR) {
+ if (stub->state().canAttachStub()) {
ICStubEngine engine = ICStubEngine::Baseline;
- GetNameIRGenerator gen(cx, script, pc, envChain, name);
+ GetNameIRGenerator gen(cx, script, pc, stub->state().mode(), envChain, name);
if (gen.tryAttachStub()) {
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
- engine, info.outerScript(cx), stub);
- if (newStub) {
+ engine, info.outerScript(cx), stub,
+ &attached);
+ if (newStub)
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
- attached = true;
- }
}
+ if (!attached)
+ stub->state().trackNotAttached();
}
static_assert(JSOP_GETGNAME_LENGTH == JSOP_GETNAME_LENGTH,
@@ -1518,18 +1512,19 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_
bool isTemporarilyUnoptimizable = false;
bool attached = false;
- if (stub->numOptimizedStubs() < ICSetProp_Fallback::MAX_OPTIMIZED_STUBS &&
- !JitOptions.disableCacheIR)
- {
+ if (stub->state().maybeTransition())
+ stub->discardStubs(cx);
+
+ if (stub->state().canAttachStub()) {
RootedValue idVal(cx, StringValue(name));
- SetPropIRGenerator gen(cx, script, pc, CacheKind::SetProp, &isTemporarilyUnoptimizable,
- lhs, idVal, rhs);
+ SetPropIRGenerator gen(cx, script, pc, CacheKind::SetProp, stub->state().mode(),
+ &isTemporarilyUnoptimizable, lhs, idVal, rhs);
if (gen.tryAttachStub()) {
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
- ICStubEngine::Baseline, frame->script(), stub);
+ ICStubEngine::Baseline, frame->script(),
+ stub, &attached);
if (newStub) {
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
- attached = true;
SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
@@ -1583,24 +1578,23 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_
if (stub.invalid())
return true;
- if (!attached &&
- stub->numOptimizedStubs() < ICSetProp_Fallback::MAX_OPTIMIZED_STUBS &&
- !JitOptions.disableCacheIR)
- {
+ if (!attached && stub->state().canAttachStub()) {
RootedValue idVal(cx, StringValue(name));
- SetPropIRGenerator gen(cx, script, pc, CacheKind::SetProp, &isTemporarilyUnoptimizable,
- lhs, idVal, rhs);
+ SetPropIRGenerator gen(cx, script, pc, CacheKind::SetProp, stub->state().mode(),
+ &isTemporarilyUnoptimizable, lhs, idVal, rhs);
if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
- ICStubEngine::Baseline, frame->script(), stub);
+ ICStubEngine::Baseline, frame->script(),
+ stub, &attached);
if (newStub) {
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
- attached = true;
SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
}
} else {
gen.trackNotAttached();
}
+ if (!attached && !isTemporarilyUnoptimizable)
+ stub->state().trackNotAttached();
}
if (!attached && !isTemporarilyUnoptimizable)
diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h
index efd59985f3e6..e200aaf65594 100644
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -376,8 +376,6 @@ class ICGetElem_Fallback : public ICMonitoredFallbackStub
static const uint16_t EXTRA_UNOPTIMIZABLE_ACCESS = 0x4;
public:
- static const uint32_t MAX_OPTIMIZED_STUBS = 16;
-
void noteNonNativeAccess() {
extra_ |= EXTRA_NON_NATIVE;
}
@@ -435,8 +433,6 @@ class ICSetElem_Fallback : public ICFallbackStub
static const size_t HasTypedArrayOOBFlag = 0x2;
public:
- static const uint32_t MAX_OPTIMIZED_STUBS = 8;
-
void noteHasDenseAdd() { extra_ |= HasDenseAddFlag; }
bool hasDenseAdd() const { return extra_ & HasDenseAddFlag; }
@@ -470,8 +466,6 @@ class ICIn_Fallback : public ICFallbackStub
{ }
public:
- static const uint32_t MAX_OPTIMIZED_STUBS = 8;
-
class Compiler : public ICStubCompiler {
protected:
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
@@ -500,7 +494,6 @@ class ICGetName_Fallback : public ICMonitoredFallbackStub
{ }
public:
- static const uint32_t MAX_OPTIMIZED_STUBS = 8;
static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0;
void noteUnoptimizableAccess() {
@@ -634,8 +627,6 @@ class ICSetProp_Fallback : public ICFallbackStub
{ }
public:
- static const uint32_t MAX_OPTIMIZED_STUBS = 8;
-
static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0;
void noteUnoptimizableAccess() {
extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT);
diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp
index 7485585db3fa..8161dd68dca5 100644
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -1018,6 +1018,87 @@ BaselineInspector::commonGetPropFunction(jsbytecode* pc, bool innerized,
return true;
}
+static JSFunction*
+GetMegamorphicGetterSetterFunction(ICStub* stub, const CacheIRStubInfo* stubInfo, bool isGetter)
+{
+ // We match:
+ //
+ // GuardIsObject objId
+ // GuardHasGetterSetter objId propShape
+ //
+ // propShape has the getter/setter we're interested in.
+
+ CacheIRReader reader(stubInfo);
+
+ ObjOperandId objId = ObjOperandId(0);
+ if (!reader.matchOp(CacheOp::GuardIsObject, objId))
+ return nullptr;
+
+ if (!reader.matchOp(CacheOp::GuardHasGetterSetter, objId))
+ return nullptr;
+ Shape* propShape = stubInfo->getStubField(stub, reader.stubOffset());
+
+ JSObject* obj = isGetter ? propShape->getterObject() : propShape->setterObject();
+ return &obj->as();
+}
+
+bool
+BaselineInspector::megamorphicGetterSetterFunction(jsbytecode* pc, bool isGetter,
+ JSFunction** getterOrSetter)
+{
+ if (!hasBaselineScript())
+ return false;
+
+ *getterOrSetter = nullptr;
+ const ICEntry& entry = icEntryFromPC(pc);
+
+ for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
+ if (stub->isCacheIR_Monitored()) {
+ MOZ_ASSERT(isGetter);
+ JSFunction* getter =
+ GetMegamorphicGetterSetterFunction(stub,
+ stub->toCacheIR_Monitored()->stubInfo(),
+ isGetter);
+ if (!getter || (*getterOrSetter && *getterOrSetter != getter))
+ return false;
+ *getterOrSetter = getter;
+ continue;
+ }
+ if (stub->isCacheIR_Updated()) {
+ MOZ_ASSERT(!isGetter);
+ JSFunction* setter =
+ GetMegamorphicGetterSetterFunction(stub,
+ stub->toCacheIR_Updated()->stubInfo(),
+ isGetter);
+ if (!setter || (*getterOrSetter && *getterOrSetter != setter))
+ return false;
+ *getterOrSetter = setter;
+ continue;
+ }
+ if (stub->isGetProp_Fallback()) {
+ if (stub->toGetProp_Fallback()->hadUnoptimizableAccess())
+ return false;
+ if (stub->toGetProp_Fallback()->state().mode() != ICState::Mode::Megamorphic)
+ return false;
+ continue;
+ }
+ if (stub->isSetProp_Fallback()) {
+ if (stub->toSetProp_Fallback()->hadUnoptimizableAccess())
+ return false;
+ if (stub->toSetProp_Fallback()->state().mode() != ICState::Mode::Megamorphic)
+ return false;
+ continue;
+ }
+
+ return false;
+ }
+
+ if (!*getterOrSetter)
+ return false;
+
+ return true;
+}
+
static bool
AddCacheIRSetPropFunction(ICCacheIR_Updated* stub, JSObject** holder, Shape** holderShape,
JSFunction** commonSetter, bool* isOwnProperty,
@@ -1187,9 +1268,6 @@ BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc)
return MIRType::Value;
continue;
- case ICStub::GetProp_Generic:
- return MIRType::Value;
-
case ICStub::CacheIR_Monitored:
stubType = GetCacheIRExpectedInputType(stub->toCacheIR_Monitored());
if (stubType == MIRType::Value)
diff --git a/js/src/jit/BaselineInspector.h b/js/src/jit/BaselineInspector.h
index a1bc6153f3e4..04c1c598ecff 100644
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -137,6 +137,9 @@ class BaselineInspector
bool* isOwnProperty, ReceiverVector& receivers,
ObjectGroupVector& convertUnboxedGroups);
+ MOZ_MUST_USE bool megamorphicGetterSetterFunction(jsbytecode* pc, bool isGetter,
+ JSFunction** getterOrSetter);
+
MOZ_MUST_USE bool commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
JSFunction** commonSetter, bool* isOwnProperty,
ReceiverVector& receivers,
diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp
index be13fe23f669..aef1ff2c6f2d 100644
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -31,19 +31,21 @@ const char* js::jit::CacheKindNames[] = {
};
-IRGenerator::IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind)
+IRGenerator::IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
+ ICState::Mode mode)
: writer(cx),
cx_(cx),
script_(script),
pc_(pc),
- cacheKind_(cacheKind)
+ cacheKind_(cacheKind),
+ mode_(mode)
{}
GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
- CacheKind cacheKind, bool* isTemporarilyUnoptimizable,
- HandleValue val, HandleValue idVal,
- CanAttachGetter canAttachGetter)
- : IRGenerator(cx, script, pc, cacheKind),
+ CacheKind cacheKind, ICState::Mode mode,
+ bool* isTemporarilyUnoptimizable, HandleValue val,
+ HandleValue idVal, CanAttachGetter canAttachGetter)
+ : IRGenerator(cx, script, pc, cacheKind, mode),
val_(val),
idVal_(idVal),
isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable),
@@ -505,22 +507,49 @@ EmitCallGetterResultNoGuards(CacheIRWriter& writer, JSObject* obj, JSObject* hol
static void
EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
- Shape* shape, ObjOperandId objId)
+ Shape* shape, ObjOperandId objId, ICState::Mode mode)
{
- Maybe expandoId;
- TestMatchingReceiver(writer, obj, shape, objId, &expandoId);
+ // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
+ // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
+ // require outerizing).
+ if (mode == ICState::Mode::Specialized || IsWindow(obj)) {
+ Maybe expandoId;
+ TestMatchingReceiver(writer, obj, shape, objId, &expandoId);
- if (obj != holder) {
- GeneratePrototypeGuards(writer, obj, holder, objId);
+ if (obj != holder) {
+ GeneratePrototypeGuards(writer, obj, holder, objId);
- // Guard on the holder's shape.
- ObjOperandId holderId = writer.loadObject(holder);
- writer.guardShape(holderId, holder->as().lastProperty());
+ // Guard on the holder's shape.
+ ObjOperandId holderId = writer.loadObject(holder);
+ writer.guardShape(holderId, holder->as().lastProperty());
+ }
+ } else {
+ writer.guardHasGetterSetter(objId, shape);
}
EmitCallGetterResultNoGuards(writer, obj, holder, shape, objId);
}
+void
+GetPropIRGenerator::attachMegamorphicNativeSlot(ObjOperandId objId, jsid id, bool handleMissing)
+{
+ MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
+
+ // The stub handles the missing-properties case only if we're seeing one
+ // now, to make sure Ion ICs correctly monitor the undefined type.
+
+ if (cacheKind_ == CacheKind::GetProp) {
+ writer.megamorphicLoadSlotResult(objId, JSID_TO_ATOM(id)->asPropertyName(),
+ handleMissing);
+ } else {
+ MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
+ writer.megamorphicLoadSlotByValueResult(objId, getElemKeyValueId(), handleMissing);
+ }
+ writer.typeMonitorResult();
+
+ trackAttached("MegamorphicNativeSlot");
+}
+
bool
GetPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId, HandleId id)
{
@@ -536,6 +565,11 @@ GetPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId, Handle
case CanAttachNone:
return false;
case CanAttachReadSlot:
+ if (mode_ == ICState::Mode::Megamorphic) {
+ attachMegamorphicNativeSlot(objId, id, holder == nullptr);
+ return true;
+ }
+
maybeEmitIdGuard(id);
if (holder) {
EnsureTrackPropertyTypes(cx_, holder, id);
@@ -554,7 +588,7 @@ GetPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId, Handle
return true;
case CanAttachCallGetter:
maybeEmitIdGuard(id);
- EmitCallGetterResult(writer, obj, holder, shape, objId);
+ EmitCallGetterResult(writer, obj, holder, shape, objId, mode_);
trackAttached("NativeGetter");
return true;
@@ -572,6 +606,11 @@ GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, H
if (!IsWindowProxy(obj))
return false;
+ // If we're megamorphic prefer a generic proxy stub that handles a lot more
+ // cases.
+ if (mode_ == ICState::Mode::Megamorphic)
+ return false;
+
// This must be a WindowProxy for the current Window/global. Else it would
// be a cross-compartment wrapper and IsWindowProxy returns false for
// those.
@@ -617,7 +656,7 @@ GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, H
maybeEmitIdGuard(id);
writer.guardClass(objId, GuardClassKind::WindowProxy);
ObjOperandId windowObjId = writer.loadObject(windowObj);
- EmitCallGetterResult(writer, windowObj, holder, shape, windowObjId);
+ EmitCallGetterResult(writer, windowObj, holder, shape, windowObjId, mode_);
trackAttached("WindowProxyGetter");
return true;
@@ -636,6 +675,11 @@ GetPropIRGenerator::tryAttachCrossCompartmentWrapper(HandleObject obj, ObjOperan
if (!IsWrapper(obj) || Wrapper::wrapperHandler(obj) != &CrossCompartmentWrapper::singleton)
return false;
+ // If we're megamorphic prefer a generic proxy stub that handles a lot more
+ // cases.
+ if (mode_ == ICState::Mode::Megamorphic)
+ return false;
+
RootedObject unwrapped(cx_, Wrapper::wrappedObject(obj));
MOZ_ASSERT(unwrapped == UnwrapOneChecked(obj));
@@ -700,23 +744,26 @@ GetPropIRGenerator::tryAttachCrossCompartmentWrapper(HandleObject obj, ObjOperan
}
bool
-GetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id)
+GetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
+ bool handleDOMProxies)
{
MOZ_ASSERT(obj->is());
writer.guardIsProxy(objId);
- // Ensure that the incoming object is not a DOM proxy, so that we can get to
- // the specialized stubs
- writer.guardNotDOMProxy(objId);
+ if (!handleDOMProxies) {
+ // Ensure that the incoming object is not a DOM proxy, so that we can get to
+ // the specialized stubs
+ writer.guardNotDOMProxy(objId);
+ }
- if (cacheKind_ == CacheKind::GetProp) {
+ if (cacheKind_ == CacheKind::GetProp || mode_ == ICState::Mode::Specialized) {
+ maybeEmitIdGuard(id);
writer.callProxyGetResult(objId, id);
} else {
- // We could call maybeEmitIdGuard here and then emit CallProxyGetResult,
- // but for GetElem we prefer to attach a stub that can handle any Value
- // so we don't attach a new stub for every id.
+ // Attach a stub that handles every id.
MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
+ MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
writer.callProxyGetByValueResult(objId, getElemKeyValueId());
}
@@ -898,9 +945,16 @@ GetPropIRGenerator::tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId o
bool
GetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id)
{
- switch (GetProxyStubType(cx_, obj, id)) {
- case ProxyStubType::None:
+ ProxyStubType type = GetProxyStubType(cx_, obj, id);
+ if (type == ProxyStubType::None)
return false;
+
+ if (mode_ == ICState::Mode::Megamorphic)
+ return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ true);
+
+ switch (type) {
+ case ProxyStubType::None:
+ break;
case ProxyStubType::DOMExpando:
if (tryAttachDOMProxyExpando(obj, objId, id))
return true;
@@ -912,9 +966,15 @@ GetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleI
case ProxyStubType::DOMShadowed:
return tryAttachDOMProxyShadowed(obj, objId, id);
case ProxyStubType::DOMUnshadowed:
- return tryAttachDOMProxyUnshadowed(obj, objId, id);
+ if (tryAttachDOMProxyUnshadowed(obj, objId, id))
+ return true;
+ if (*isTemporarilyUnoptimizable_) {
+ // Scripted getter without JIT code. Just wait.
+ return false;
+ }
+ return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ true);
case ProxyStubType::Generic:
- return tryAttachGenericProxy(obj, objId, id);
+ return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ false);
}
MOZ_CRASH("Unexpected ProxyStubType");
@@ -1530,8 +1590,9 @@ SetPropIRGenerator::maybeEmitIdGuard(jsid id)
}
GetNameIRGenerator::GetNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
- HandleObject env, HandlePropertyName name)
- : IRGenerator(cx, script, pc, CacheKind::GetName),
+ ICState::Mode mode, HandleObject env,
+ HandlePropertyName name)
+ : IRGenerator(cx, script, pc, CacheKind::GetName, mode),
env_(env),
name_(name)
{}
@@ -1751,8 +1812,8 @@ GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
}
InIRGenerator::InIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
- HandleValue key, HandleObject obj)
- : IRGenerator(cx, script, pc, CacheKind::In),
+ ICState::Mode mode, HandleValue key, HandleObject obj)
+ : IRGenerator(cx, script, pc, CacheKind::In, mode),
key_(key), obj_(obj)
{ }
@@ -1955,10 +2016,11 @@ IRGenerator::maybeGuardInt32Index(const Value& index, ValOperandId indexId,
}
SetPropIRGenerator::SetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
- CacheKind cacheKind, bool* isTemporarilyUnoptimizable,
+ CacheKind cacheKind, ICState::Mode mode,
+ bool* isTemporarilyUnoptimizable,
HandleValue lhsVal, HandleValue idVal, HandleValue rhsVal,
bool needsTypeBarrier, bool maybeHasExtraIndexedProps)
- : IRGenerator(cx, script, pc, cacheKind),
+ : IRGenerator(cx, script, pc, cacheKind, mode),
lhsVal_(lhsVal),
idVal_(idVal),
rhsVal_(rhsVal),
@@ -2093,6 +2155,16 @@ SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId,
return false;
}
+ if (mode_ == ICState::Mode::Megamorphic &&
+ cacheKind_ == CacheKind::SetProp &&
+ !IsPreliminaryObject(obj))
+ {
+ writer.megamorphicStoreSlot(objId, JSID_TO_ATOM(id)->asPropertyName(), rhsId);
+ writer.returnFromIC();
+ trackAttached("MegamorphicNativeSlot");
+ return true;
+ }
+
maybeEmitIdGuard(id);
// If we need a property type barrier (always in Baseline, sometimes in
@@ -2333,15 +2405,22 @@ SetPropIRGenerator::tryAttachSetter(HandleObject obj, ObjOperandId objId, Handle
maybeEmitIdGuard(id);
- Maybe expandoId;
- TestMatchingReceiver(writer, obj, propShape, objId, &expandoId);
+ // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
+ // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
+ // require outerizing).
+ if (mode_ == ICState::Mode::Specialized || IsWindow(obj)) {
+ Maybe expandoId;
+ TestMatchingReceiver(writer, obj, propShape, objId, &expandoId);
- if (obj != holder) {
- GeneratePrototypeGuards(writer, obj, holder, objId);
+ if (obj != holder) {
+ GeneratePrototypeGuards(writer, obj, holder, objId);
- // Guard on the holder's shape.
- ObjOperandId holderId = writer.loadObject(holder);
- writer.guardShape(holderId, holder->as().lastProperty());
+ // Guard on the holder's shape.
+ ObjOperandId holderId = writer.loadObject(holder);
+ writer.guardShape(holderId, holder->as().lastProperty());
+ }
+ } else {
+ writer.guardHasGetterSetter(objId, propShape);
}
EmitCallSetterNoGuards(writer, obj, holder, propShape, objId, rhsId);
@@ -2654,13 +2733,13 @@ SetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId,
writer.guardNotDOMProxy(objId);
}
- if (cacheKind_ == CacheKind::SetProp) {
+ if (cacheKind_ == CacheKind::SetProp || mode_ == ICState::Mode::Specialized) {
+ maybeEmitIdGuard(id);
writer.callProxySet(objId, id, rhsId, IsStrictSetPC(pc_));
} else {
- // We could call maybeEmitIdGuard here and then emit CallProxySet, but
- // for SetElem we prefer to attach a stub that can handle any Value
- // so we don't attach a new stub for every id.
+ // Attach a stub that handles every id.
MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
+ MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
writer.callProxySetByValue(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
}
@@ -2732,15 +2811,26 @@ SetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleI
// Don't attach a proxy stub for ops like JSOP_INITELEM.
MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
- switch (GetProxyStubType(cx_, obj, id)) {
- case ProxyStubType::None:
+ ProxyStubType type = GetProxyStubType(cx_, obj, id);
+ if (type == ProxyStubType::None)
return false;
+
+ if (mode_ == ICState::Mode::Megamorphic)
+ return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ true);
+
+ switch (type) {
+ case ProxyStubType::None:
+ break;
case ProxyStubType::DOMExpando:
case ProxyStubType::DOMShadowed:
return tryAttachDOMProxyShadowed(obj, objId, id, rhsId);
case ProxyStubType::DOMUnshadowed:
if (tryAttachDOMProxyUnshadowed(obj, objId, id, rhsId))
return true;
+ if (*isTemporarilyUnoptimizable_) {
+ // Scripted setter without JIT code. Just wait.
+ return false;
+ }
return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ true);
case ProxyStubType::Generic:
return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ false);
diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h
index 9a89b1fa6588..da0a9a3e6e65 100644
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -13,6 +13,7 @@
#include "gc/Rooting.h"
#include "jit/CompactBuffer.h"
+#include "jit/ICState.h"
#include "jit/SharedIC.h"
namespace js {
@@ -174,11 +175,16 @@ extern const char* CacheKindNames[];
_(GuardNoUnboxedExpando) \
_(GuardAndLoadUnboxedExpando) \
_(GuardAndGetIndexFromString) \
+ _(GuardHasGetterSetter) \
_(LoadObject) \
_(LoadProto) \
_(LoadEnclosingEnvironment) \
_(LoadWrapperTarget) \
\
+ _(MegamorphicLoadSlotResult) \
+ _(MegamorphicLoadSlotByValueResult) \
+ _(MegamorphicStoreSlot) \
+ \
/* See CacheIR.cpp 'DOM proxies' comment. */ \
_(LoadDOMExpandoValue) \
_(LoadDOMExpandoValueGuardGeneration) \
@@ -417,7 +423,7 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
return stubDataSize_;
}
void copyStubData(uint8_t* dest) const;
- bool stubDataEqualsMaybeUpdate(uint8_t* stubData) const;
+ bool stubDataEqualsMaybeUpdate(uint8_t* stubData, bool* updated) const;
bool operandIsDead(uint32_t operandId, uint32_t currentInstruction) const {
if (operandId >= operandLastUsed_.length())
@@ -535,6 +541,11 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
return res;
}
+ void guardHasGetterSetter(ObjOperandId obj, Shape* shape) {
+ writeOpWithOperandId(CacheOp::GuardHasGetterSetter, obj);
+ addStubField(uintptr_t(shape), StubField::Type::Shape);
+ }
+
void loadFrameCalleeResult() {
writeOp(CacheOp::LoadFrameCalleeResult);
}
@@ -748,6 +759,22 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
buffer_.writeByte(uint32_t(strict));
}
+ void megamorphicLoadSlotResult(ObjOperandId obj, PropertyName* name, bool handleMissing) {
+ writeOpWithOperandId(CacheOp::MegamorphicLoadSlotResult, obj);
+ addStubField(uintptr_t(name), StubField::Type::String);
+ buffer_.writeByte(uint32_t(handleMissing));
+ }
+ void megamorphicLoadSlotByValueResult(ObjOperandId obj, ValOperandId id, bool handleMissing) {
+ writeOpWithOperandId(CacheOp::MegamorphicLoadSlotByValueResult, obj);
+ writeOperandId(id);
+ buffer_.writeByte(uint32_t(handleMissing));
+ }
+ void megamorphicStoreSlot(ObjOperandId obj, PropertyName* name, ValOperandId rhs) {
+ writeOpWithOperandId(CacheOp::MegamorphicStoreSlot, obj);
+ addStubField(uintptr_t(name), StubField::Type::String);
+ writeOperandId(rhs);
+ }
+
void loadBooleanResult(bool val) {
writeOp(CacheOp::LoadBooleanResult);
buffer_.writeByte(uint32_t(val));
@@ -945,6 +972,7 @@ class MOZ_RAII IRGenerator
HandleScript script_;
jsbytecode* pc_;
CacheKind cacheKind_;
+ ICState::Mode mode_;
IRGenerator(const IRGenerator&) = delete;
IRGenerator& operator=(const IRGenerator&) = delete;
@@ -957,7 +985,8 @@ class MOZ_RAII IRGenerator
friend class CacheIRSpewer;
public:
- explicit IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind);
+ explicit IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
+ ICState::Mode mode);
const CacheIRWriter& writerRef() const { return writer; }
CacheKind cacheKind() const { return cacheKind_; }
@@ -986,7 +1015,8 @@ class MOZ_RAII GetPropIRGenerator : public IRGenerator
bool tryAttachCrossCompartmentWrapper(HandleObject obj, ObjOperandId objId, HandleId id);
bool tryAttachFunction(HandleObject obj, ObjOperandId objId, HandleId id);
- bool tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id);
+ bool tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
+ bool handleDOMProxies);
bool tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id);
bool tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id);
bool tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id);
@@ -1012,6 +1042,8 @@ class MOZ_RAII GetPropIRGenerator : public IRGenerator
bool tryAttachProxyElement(HandleObject obj, ObjOperandId objId);
+ void attachMegamorphicNativeSlot(ObjOperandId objId, jsid id, bool handleMissing);
+
ValOperandId getElemKeyValueId() const {
MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
return ValOperandId(1);
@@ -1030,8 +1062,8 @@ class MOZ_RAII GetPropIRGenerator : public IRGenerator
public:
GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
- bool* isTemporarilyUnoptimizable, HandleValue val,HandleValue idVal,
- CanAttachGetter canAttachGetter);
+ ICState::Mode mode, bool* isTemporarilyUnoptimizable, HandleValue val,
+ HandleValue idVal, CanAttachGetter canAttachGetter);
bool tryAttachStub();
bool tryAttachIdempotentStub();
@@ -1055,7 +1087,7 @@ class MOZ_RAII GetNameIRGenerator : public IRGenerator
bool tryAttachEnvironmentName(ObjOperandId objId, HandleId id);
public:
- GetNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
+ GetNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode,
HandleObject env, HandlePropertyName name);
bool tryAttachStub();
@@ -1159,8 +1191,8 @@ class MOZ_RAII SetPropIRGenerator : public IRGenerator
public:
SetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
- bool* isTemporarilyUnoptimizable, HandleValue lhsVal, HandleValue idVal,
- HandleValue rhsVal, bool needsTypeBarrier = true,
+ ICState::Mode mode, bool* isTemporarilyUnoptimizable, HandleValue lhsVal,
+ HandleValue idVal, HandleValue rhsVal, bool needsTypeBarrier = true,
bool maybeHasExtraIndexedProps = true);
bool tryAttachStub();
@@ -1202,7 +1234,8 @@ class MOZ_RAII InIRGenerator : public IRGenerator
void trackNotAttached();
public:
- InIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, HandleValue key, HandleObject obj);
+ InIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode mode, HandleValue key,
+ HandleObject obj);
bool tryAttachStub();
};
diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp
index b41b475e7f75..cdab525cc8db 100644
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -961,10 +961,11 @@ template
void jit::TraceCacheIRStub(JSTracer* trc, IonICStub* stub, const CacheIRStubInfo* stubInfo);
bool
-CacheIRWriter::stubDataEqualsMaybeUpdate(uint8_t* stubData) const
+CacheIRWriter::stubDataEqualsMaybeUpdate(uint8_t* stubData, bool* updated) const
{
MOZ_ASSERT(!failed());
+ *updated = false;
const uintptr_t* stubDataWords = reinterpret_cast(stubData);
// If DOMExpandoGeneration fields are different but all other stub fields
@@ -990,8 +991,10 @@ CacheIRWriter::stubDataEqualsMaybeUpdate(uint8_t* stubData) const
stubDataWords += sizeof(uint64_t) / sizeof(uintptr_t);
}
- if (expandoGenerationIsDifferent)
+ if (expandoGenerationIsDifferent) {
copyStubData(stubData);
+ *updated = true;
+ }
return true;
}
@@ -2202,3 +2205,55 @@ CacheIRCompiler::emitWrapResult()
masm.bind(&done);
return true;
}
+
+bool
+CacheIRCompiler::emitMegamorphicLoadSlotByValueResult()
+{
+ AutoOutputRegister output(*this);
+
+ Register obj = allocator.useRegister(masm, reader.objOperandId());
+ ValueOperand idVal = allocator.useValueRegister(masm, reader.valOperandId());
+ bool handleMissing = reader.readBool();
+
+ AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
+
+ FailurePath* failure;
+ if (!addFailurePath(&failure))
+ return false;
+
+ // idVal will be in vp[0], result will be stored in vp[1].
+ masm.reserveStack(sizeof(Value));
+ masm.Push(idVal);
+ masm.moveStackPtrTo(idVal.scratchReg());
+
+ LiveRegisterSet volatileRegs(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
+ volatileRegs.takeUnchecked(scratch);
+ volatileRegs.takeUnchecked(idVal);
+ masm.PushRegsInMask(volatileRegs);
+
+ masm.setupUnalignedABICall(scratch);
+ masm.loadJSContext(scratch);
+ masm.passABIArg(scratch);
+ masm.passABIArg(obj);
+ masm.passABIArg(idVal.scratchReg());
+ if (handleMissing)
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, (GetNativeDataPropertyByValue)));
+ else
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, (GetNativeDataPropertyByValue)));
+ masm.mov(ReturnReg, scratch);
+ masm.PopRegsInMask(volatileRegs);
+
+ masm.Pop(idVal);
+
+ Label ok;
+ uint32_t framePushed = masm.framePushed();
+ masm.branchIfTrueBool(scratch, &ok);
+ masm.adjustStack(sizeof(Value));
+ masm.jump(failure->label());
+
+ masm.bind(&ok);
+ masm.setFramePushed(framePushed);
+ masm.loadTypedOrValue(Address(masm.getStackPointer(), 0), output);
+ masm.adjustStack(sizeof(Value));
+ return true;
+}
diff --git a/js/src/jit/CacheIRCompiler.h b/js/src/jit/CacheIRCompiler.h
index 0c96f75337f2..f0e415772545 100644
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -51,6 +51,7 @@ namespace jit {
_(LoadDenseElementHoleExistsResult) \
_(LoadUnboxedArrayElementResult) \
_(LoadTypedElementResult) \
+ _(MegamorphicLoadSlotByValueResult) \
_(WrapResult)
// Represents a Value on the Baseline frame's expression stack. Slot 0 is the
diff --git a/js/src/jit/ICState.h b/js/src/jit/ICState.h
new file mode 100644
index 000000000000..01b52f8f7000
--- /dev/null
+++ b/js/src/jit/ICState.h
@@ -0,0 +1,127 @@
+/* -*- 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/. */
+
+#ifndef jit_ICState_h
+#define jit_ICState_h
+
+#include "jit/JitOptions.h"
+
+namespace js {
+namespace jit {
+
+// ICState stores information about a Baseline or Ion IC.
+class ICState
+{
+ public:
+ // When we attach the maximum number of stubs, we discard all stubs and
+ // transition the IC to Megamorphic to attach stubs that are more generic
+ // (handle more cases). If we again attach the maximum number of stubs, we
+ // transition to Generic and (depending on the IC) will either attach a
+ // single stub that handles everything or stop attaching new stubs.
+ //
+ // We also transition to Generic when we repeatedly fail to attach a stub,
+ // to avoid wasting time trying.
+ enum class Mode : uint8_t { Specialized = 0, Megamorphic, Generic };
+
+ private:
+ Mode mode_;
+
+ // Number of optimized stubs currently attached to this IC.
+ uint8_t numOptimizedStubs_;
+
+ // Number of times we failed to attach a stub.
+ uint8_t numFailures_;
+
+ // This is only used for shared Baseline ICs and stored here to save space.
+ bool invalid_ : 1;
+
+ static const size_t MaxOptimizedStubs = 6;
+
+ void transition(Mode mode) {
+ MOZ_ASSERT(mode > mode_);
+ mode_ = mode;
+ numFailures_ = 0;
+ }
+
+ MOZ_ALWAYS_INLINE size_t maxFailures() const {
+ // Allow more failures if we attached stubs.
+ static_assert(MaxOptimizedStubs == 6,
+ "numFailures_/maxFailures should fit in uint8_t");
+ size_t res = 5 + size_t(40) * numOptimizedStubs_;
+ MOZ_ASSERT(res <= UINT8_MAX, "numFailures_ should not overflow");
+ return res;
+ }
+
+ public:
+ ICState()
+ : invalid_(false)
+ {
+ reset();
+ }
+
+ Mode mode() const { return mode_; }
+ size_t numOptimizedStubs() const { return numOptimizedStubs_; }
+
+ MOZ_ALWAYS_INLINE bool canAttachStub() const {
+ MOZ_ASSERT(numOptimizedStubs_ <= MaxOptimizedStubs);
+ if (mode_ == Mode::Generic || JitOptions.disableCacheIR)
+ return false;
+ return true;
+ }
+
+ bool invalid() const { return invalid_; }
+ void setInvalid() { invalid_ = true; }
+
+ // If this returns true, we transitioned to a new mode and the caller
+ // should discard all stubs.
+ MOZ_MUST_USE MOZ_ALWAYS_INLINE bool maybeTransition() {
+ MOZ_ASSERT(numOptimizedStubs_ <= MaxOptimizedStubs);
+ if (mode_ == Mode::Generic)
+ return false;
+ if (numOptimizedStubs_ < MaxOptimizedStubs && numFailures_ < maxFailures())
+ return false;
+ if (numFailures_ == maxFailures() || mode_ == Mode::Megamorphic) {
+ transition(Mode::Generic);
+ return true;
+ }
+ MOZ_ASSERT(mode_ == Mode::Specialized);
+ transition(Mode::Megamorphic);
+ return true;
+ }
+ void reset() {
+ mode_ = Mode::Specialized;
+ numOptimizedStubs_ = 0;
+ numFailures_ = 0;
+ }
+ void trackAttached() {
+ // We'd like to assert numOptimizedStubs_ < MaxOptimizedStubs, but
+ // since this code is also used for non-CacheIR Baseline stubs, assert
+ // < 16 for now. Note that we do have the stronger assert in other
+ // methods, because they are only used by CacheIR ICs.
+ MOZ_ASSERT(numOptimizedStubs_ < 16);
+ numOptimizedStubs_++;
+ numFailures_ = 0;
+ }
+ void trackNotAttached() {
+ // Note: we can't assert numFailures_ < maxFailures() because
+ // maxFailures() depends on numOptimizedStubs_ and it's possible a
+ // GC discarded stubs before we got here.
+ numFailures_++;
+ MOZ_ASSERT(numFailures_ > 0, "numFailures_ should not overflow");
+ }
+ void trackUnlinkedStub() {
+ MOZ_ASSERT(numOptimizedStubs_ > 0);
+ numOptimizedStubs_--;
+ }
+ void trackUnlinkedAllStubs() {
+ numOptimizedStubs_ = 0;
+ }
+};
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_ICState_h */
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index 0180c79377b5..2569f4a6e28b 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -10797,6 +10797,15 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName
if (!obj)
return abort(AbortReason::Alloc);
}
+ } else if (inspector->megamorphicGetterSetterFunction(pc, /* isGetter = */ true,
+ &commonGetter))
+ {
+ // Try to use TI to guard on this getter.
+ if (!testCommonGetterSetter(objTypes, name, /* isGetter = */ true,
+ commonGetter, &guard))
+ {
+ return Ok();
+ }
} else {
// The Baseline IC didn't have any information we can use.
return Ok();
@@ -11349,6 +11358,15 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj,
if (!obj)
return abort(AbortReason::Alloc);
}
+ } else if (inspector->megamorphicGetterSetterFunction(pc, /* isGetter = */ false,
+ &commonSetter))
+ {
+ // Try to use TI to guard on this setter.
+ if (!testCommonGetterSetter(objTypes, name, /* isGetter = */ false,
+ commonSetter, &guard))
+ {
+ return Ok();
+ }
} else {
// The Baseline IC didn't have any information we can use.
return Ok();
diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp
index 0ec9b84ff600..6eea32c39b57 100644
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -654,6 +654,126 @@ IonCacheIRCompiler::emitLoadDynamicSlotResult()
return true;
}
+bool
+IonCacheIRCompiler::emitMegamorphicLoadSlotResult()
+{
+ AutoOutputRegister output(*this);
+
+ Register obj = allocator.useRegister(masm, reader.objOperandId());
+ PropertyName* name = stringStubField(reader.stubOffset())->asAtom().asPropertyName();
+ bool handleMissing = reader.readBool();
+
+ AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output);
+ AutoScratchRegister scratch2(allocator, masm);
+ AutoScratchRegister scratch3(allocator, masm);
+
+ FailurePath* failure;
+ if (!addFailurePath(&failure))
+ return false;
+
+ masm.Push(UndefinedValue());
+ masm.moveStackPtrTo(scratch3.get());
+
+ LiveRegisterSet volatileRegs(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
+ volatileRegs.takeUnchecked(scratch1);
+ volatileRegs.takeUnchecked(scratch2);
+ volatileRegs.takeUnchecked(scratch3);
+ masm.PushRegsInMask(volatileRegs);
+
+ masm.setupUnalignedABICall(scratch1);
+ masm.loadJSContext(scratch1);
+ masm.passABIArg(scratch1);
+ masm.passABIArg(obj);
+ masm.movePtr(ImmGCPtr(name), scratch2);
+ masm.passABIArg(scratch2);
+ masm.passABIArg(scratch3);
+ if (handleMissing)
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, (GetNativeDataProperty)));
+ else
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, (GetNativeDataProperty)));
+ masm.mov(ReturnReg, scratch2);
+ masm.PopRegsInMask(volatileRegs);
+
+ masm.loadTypedOrValue(Address(masm.getStackPointer(), 0), output);
+ masm.adjustStack(sizeof(Value));
+
+ masm.branchIfFalseBool(scratch2, failure->label());
+ return true;
+}
+
+bool
+IonCacheIRCompiler::emitMegamorphicStoreSlot()
+{
+ Register obj = allocator.useRegister(masm, reader.objOperandId());
+ PropertyName* name = stringStubField(reader.stubOffset())->asAtom().asPropertyName();
+ ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+
+ AutoScratchRegister scratch1(allocator, masm);
+ AutoScratchRegister scratch2(allocator, masm);
+
+ FailurePath* failure;
+ if (!addFailurePath(&failure))
+ return false;
+
+ masm.Push(val);
+ masm.moveStackPtrTo(val.scratchReg());
+
+ LiveRegisterSet volatileRegs(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
+ volatileRegs.takeUnchecked(scratch1);
+ volatileRegs.takeUnchecked(scratch2);
+ volatileRegs.takeUnchecked(val);
+ masm.PushRegsInMask(volatileRegs);
+
+ masm.setupUnalignedABICall(scratch1);
+ masm.loadJSContext(scratch1);
+ masm.passABIArg(scratch1);
+ masm.passABIArg(obj);
+ masm.movePtr(ImmGCPtr(name), scratch2);
+ masm.passABIArg(scratch2);
+ masm.passABIArg(val.scratchReg());
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, SetNativeDataProperty));
+ masm.mov(ReturnReg, scratch1);
+ masm.PopRegsInMask(volatileRegs);
+
+ masm.loadValue(Address(masm.getStackPointer(), 0), val);
+ masm.adjustStack(sizeof(Value));
+
+ masm.branchIfFalseBool(scratch1, failure->label());
+ return true;
+}
+
+bool
+IonCacheIRCompiler::emitGuardHasGetterSetter()
+{
+ Register obj = allocator.useRegister(masm, reader.objOperandId());
+ Shape* shape = shapeStubField(reader.stubOffset());
+
+ AutoScratchRegister scratch1(allocator, masm);
+ AutoScratchRegister scratch2(allocator, masm);
+
+ FailurePath* failure;
+ if (!addFailurePath(&failure))
+ return false;
+
+ LiveRegisterSet volatileRegs(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
+ volatileRegs.takeUnchecked(scratch1);
+ volatileRegs.takeUnchecked(scratch2);
+ masm.PushRegsInMask(volatileRegs);
+
+ masm.setupUnalignedABICall(scratch1);
+ masm.loadJSContext(scratch1);
+ masm.passABIArg(scratch1);
+ masm.passABIArg(obj);
+ masm.movePtr(ImmGCPtr(shape), scratch2);
+ masm.passABIArg(scratch2);
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ObjectHasGetterSetter));
+ masm.mov(ReturnReg, scratch1);
+ masm.PopRegsInMask(volatileRegs);
+
+ masm.branchIfFalseBool(scratch1, failure->label());
+ return true;
+}
+
bool
IonCacheIRCompiler::emitCallScriptedGetterResult()
{
@@ -1766,21 +1886,24 @@ IonCacheIRCompiler::emitLoadDOMExpandoValueGuardGeneration()
return true;
}
-bool
+void
IonIC::attachCacheIRStub(JSContext* cx, const CacheIRWriter& writer, CacheKind kind,
- IonScript* ionScript, const PropertyTypeCheckInfo* typeCheckInfo)
+ IonScript* ionScript, bool* attached,
+ const PropertyTypeCheckInfo* typeCheckInfo)
{
// We shouldn't GC or report OOM (or any other exception) here.
AutoAssertNoPendingException aanpe(cx);
JS::AutoCheckCannotGC nogc;
+ MOZ_ASSERT(!*attached);
+
// SetProp/SetElem stubs must have non-null typeCheckInfo.
MOZ_ASSERT(!!typeCheckInfo == (kind == CacheKind::SetProp || kind == CacheKind::SetElem));
// Do nothing if the IR generator failed or triggered a GC that invalidated
// the script.
if (writer.failed() || ionScript->invalidated())
- return false;
+ return;
JitZone* jitZone = cx->zone()->jitZone();
uint32_t stubDataOffset = sizeof(IonICStub);
@@ -1800,11 +1923,11 @@ IonIC::attachCacheIRStub(JSContext* cx, const CacheIRWriter& writer, CacheKind k
stubInfo = CacheIRStubInfo::New(kind, ICStubEngine::IonIC, makesGCCalls,
stubDataOffset, writer);
if (!stubInfo)
- return false;
+ return;
CacheIRStubKey key(stubInfo);
if (!jitZone->putIonCacheIRStubInfo(lookup, key))
- return false;
+ return;
}
MOZ_ASSERT(stubInfo);
@@ -1815,9 +1938,16 @@ IonIC::attachCacheIRStub(JSContext* cx, const CacheIRWriter& writer, CacheKind k
for (IonICStub* stub = firstStub_; stub; stub = stub->next()) {
if (stub->stubInfo() != stubInfo)
continue;
- if (!writer.stubDataEqualsMaybeUpdate(stub->stubDataStart()))
+ bool updated = false;
+ if (!writer.stubDataEqualsMaybeUpdate(stub->stubDataStart(), &updated))
continue;
- return true;
+ if (updated || (typeCheckInfo && typeCheckInfo->needsTypeBarrier())) {
+ // We updated a stub or have a stub that requires property type
+ // checks. In this case the stub will likely handle more cases in
+ // the future and we shouldn't deoptimize.
+ *attached = true;
+ }
+ return;
}
size_t bytesNeeded = stubInfo->stubDataOffset() + stubInfo->stubDataSize();
@@ -1830,7 +1960,7 @@ IonIC::attachCacheIRStub(JSContext* cx, const CacheIRWriter& writer, CacheKind k
ICStubSpace* stubSpace = cx->zone()->jitZone()->optimizedStubSpace();
void* newStubMem = stubSpace->alloc(bytesNeeded);
if (!newStubMem)
- return false;
+ return;
IonICStub* newStub = new(newStubMem) IonICStub(fallbackLabel_.raw(), stubInfo);
writer.copyStubData(newStub->stubDataStart());
@@ -1838,12 +1968,12 @@ IonIC::attachCacheIRStub(JSContext* cx, const CacheIRWriter& writer, CacheKind k
JitContext jctx(cx, nullptr);
IonCacheIRCompiler compiler(cx, writer, this, ionScript, newStub, typeCheckInfo);
if (!compiler.init())
- return false;
+ return;
JitCode* code = compiler.compile();
if (!code)
- return false;
+ return;
attachStub(newStub, code);
- return true;
+ *attached = true;
}
diff --git a/js/src/jit/IonIC.cpp b/js/src/jit/IonIC.cpp
index 1fe486058686..15bdbc73aae1 100644
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -53,7 +53,7 @@ IonIC::scratchRegisterForEntryJump()
}
void
-IonIC::reset(Zone* zone)
+IonIC::discardStubs(Zone* zone)
{
if (firstStub_ && zone->needsIncrementalBarrier()) {
// We are removing edges from IonIC to gcthings. Perform one final trace
@@ -72,7 +72,14 @@ IonIC::reset(Zone* zone)
firstStub_ = nullptr;
codeRaw_ = fallbackLabel_.raw();
- numStubs_ = 0;
+ state_.trackUnlinkedAllStubs();
+}
+
+void
+IonIC::reset(Zone* zone)
+{
+ discardStubs(zone);
+ state_.reset();
}
void
@@ -107,29 +114,6 @@ IonIC::togglePreBarriers(bool enabled, ReprotectCode reprotect)
MOZ_ASSERT(nextCodeRaw == fallbackLabel_.raw());
}
-void
-IonGetPropertyIC::maybeDisable(Zone* zone, bool attached)
-{
- if (attached) {
- failedUpdates_ = 0;
- return;
- }
-
- if (!canAttachStub() && kind() == CacheKind::GetProp) {
- // Don't disable the cache (and discard stubs) if we have a GETPROP and
- // attached the maximum number of stubs. This can happen when JS code
- // uses an AST-like data structure and accesses a field of a "base
- // class", like node.nodeType. This should be temporary until we handle
- // this case better, see bug 1107515.
- return;
- }
-
- if (++failedUpdates_ > MAX_FAILED_UPDATES) {
- JitSpew(JitSpew_IonIC, "Disable inline cache");
- disable(zone);
- }
-}
-
/* static */ bool
IonGetPropertyIC::update(JSContext* cx, HandleScript outerScript, IonGetPropertyIC* ic,
HandleValue val, HandleValue idVal, MutableHandleValue res)
@@ -142,24 +126,26 @@ IonGetPropertyIC::update(JSContext* cx, HandleScript outerScript, IonGetProperty
if (ic->idempotent())
adi.disable();
+ if (ic->state().maybeTransition())
+ ic->discardStubs(cx->zone());
+
bool attached = false;
- if (!JitOptions.disableCacheIR && !ic->disabled()) {
- if (ic->canAttachStub()) {
- // IonBuilder calls PropertyReadNeedsTypeBarrier to determine if it
- // needs a type barrier. Unfortunately, PropertyReadNeedsTypeBarrier
- // does not account for getters, so we should only attach a getter
- // stub if we inserted a type barrier.
- CanAttachGetter canAttachGetter =
- ic->monitoredResult() ? CanAttachGetter::Yes : CanAttachGetter::No;
- jsbytecode* pc = ic->idempotent() ? nullptr : ic->pc();
- bool isTemporarilyUnoptimizable;
- GetPropIRGenerator gen(cx, outerScript, pc, ic->kind(), &isTemporarilyUnoptimizable,
- val, idVal, canAttachGetter);
- if (ic->idempotent() ? gen.tryAttachIdempotentStub() : gen.tryAttachStub()) {
- attached = ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript);
- }
- }
- ic->maybeDisable(cx->zone(), attached);
+ if (ic->state().canAttachStub()) {
+ // IonBuilder calls PropertyReadNeedsTypeBarrier to determine if it
+ // needs a type barrier. Unfortunately, PropertyReadNeedsTypeBarrier
+ // does not account for getters, so we should only attach a getter
+ // stub if we inserted a type barrier.
+ CanAttachGetter canAttachGetter =
+ ic->monitoredResult() ? CanAttachGetter::Yes : CanAttachGetter::No;
+ jsbytecode* pc = ic->idempotent() ? nullptr : ic->pc();
+ bool isTemporarilyUnoptimizable = false;
+ GetPropIRGenerator gen(cx, outerScript, pc, ic->kind(), ic->state().mode(),
+ &isTemporarilyUnoptimizable, val, idVal, canAttachGetter);
+ if (ic->idempotent() ? gen.tryAttachIdempotentStub() : gen.tryAttachStub())
+ ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
+
+ if (!attached && !isTemporarilyUnoptimizable)
+ ic->state().trackNotAttached();
}
if (!attached && ic->idempotent()) {
@@ -210,7 +196,12 @@ IonSetPropertyIC::update(JSContext* cx, HandleScript outerScript, IonSetProperty
IonScript* ionScript = outerScript->ionScript();
bool attached = false;
- if (!JitOptions.disableCacheIR && ic->canAttachStub()) {
+ bool isTemporarilyUnoptimizable = false;
+
+ if (ic->state().maybeTransition())
+ ic->discardStubs(cx->zone());
+
+ if (ic->state().canAttachStub()) {
oldShape = obj->maybeShape();
oldGroup = JSObject::getGroup(cx, obj);
if (!oldGroup)
@@ -225,11 +216,12 @@ IonSetPropertyIC::update(JSContext* cx, HandleScript outerScript, IonSetProperty
RootedScript script(cx, ic->script());
jsbytecode* pc = ic->pc();
bool isTemporarilyUnoptimizable;
- SetPropIRGenerator gen(cx, script, pc, ic->kind(), &isTemporarilyUnoptimizable,
+ SetPropIRGenerator gen(cx, script, pc, ic->kind(), ic->state().mode(),
+ &isTemporarilyUnoptimizable,
objv, idVal, rhs, ic->needsTypeBarrier(), ic->guardHoles());
if (gen.tryAttachStub()) {
- attached = ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
- ionScript, gen.typeCheckInfo());
+ ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached,
+ gen.typeCheckInfo());
}
}
@@ -257,19 +249,22 @@ IonSetPropertyIC::update(JSContext* cx, HandleScript outerScript, IonSetProperty
}
}
- if (!attached && !JitOptions.disableCacheIR && ic->canAttachStub()) {
+ if (!attached && ic->state().canAttachStub()) {
RootedValue objv(cx, ObjectValue(*obj));
RootedScript script(cx, ic->script());
jsbytecode* pc = ic->pc();
- bool isTemporarilyUnoptimizable;
- SetPropIRGenerator gen(cx, script, pc, ic->kind(), &isTemporarilyUnoptimizable,
+ SetPropIRGenerator gen(cx, script, pc, ic->kind(), ic->state().mode(),
+ &isTemporarilyUnoptimizable,
objv, idVal, rhs, ic->needsTypeBarrier(), ic->guardHoles());
if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
- attached = ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
- ionScript, gen.typeCheckInfo());
+ ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached,
+ gen.typeCheckInfo());
} else {
gen.trackNotAttached();
}
+
+ if (!attached && !isTemporarilyUnoptimizable)
+ ic->state().trackNotAttached();
}
return true;
@@ -284,7 +279,6 @@ IonICStub::stubDataStart()
void
IonIC::attachStub(IonICStub* newStub, JitCode* code)
{
- MOZ_ASSERT(canAttachStub());
MOZ_ASSERT(newStub);
MOZ_ASSERT(code);
@@ -298,5 +292,5 @@ IonIC::attachStub(IonICStub* newStub, JitCode* code)
codeRaw_ = code->raw();
}
- numStubs_++;
+ state_.trackAttached();
}
diff --git a/js/src/jit/IonIC.h b/js/src/jit/IonIC.h
index 07420f7beaed..d3ca3e768d22 100644
--- a/js/src/jit/IonIC.h
+++ b/js/src/jit/IonIC.h
@@ -79,9 +79,8 @@ class IonIC
jsbytecode* pc_;
CacheKind kind_;
- uint8_t numStubs_;
bool idempotent_ : 1;
- bool disabled_ : 1;
+ ICState state_;
protected:
explicit IonIC(CacheKind kind)
@@ -92,9 +91,8 @@ class IonIC
script_(nullptr),
pc_(nullptr),
kind_(kind),
- numStubs_(0),
idempotent_(false),
- disabled_(false)
+ state_()
{}
void attachStub(IonICStub* newStub, JitCode* code);
@@ -112,20 +110,16 @@ class IonIC
CodeLocationLabel rejoinLabel() const { return rejoinLabel_; }
- static const size_t MAX_STUBS = 16;
-
- bool canAttachStub() const { return numStubs_ < MAX_STUBS; }
-
- void disable(Zone* zone) {
- reset(zone);
- disabled_ = true;
- }
-
- bool disabled() const { return disabled_; }
-
// Discard all stubs.
+ void discardStubs(Zone* zone);
+
+ // Discard all stubs and reset the ICState.
void reset(Zone* zone);
+ ICState& state() {
+ return state_;
+ }
+
void togglePreBarriers(bool enabled, ReprotectCode reprotect);
CacheKind kind() const { return kind_; }
@@ -154,8 +148,8 @@ class IonIC
void trace(JSTracer* trc);
- bool attachCacheIRStub(JSContext* cx, const CacheIRWriter& writer, CacheKind kind,
- IonScript* ionScript,
+ void attachCacheIRStub(JSContext* cx, const CacheIRWriter& writer, CacheKind kind,
+ IonScript* ionScript, bool* attached,
const PropertyTypeCheckInfo* typeCheckInfo = nullptr);
};
@@ -168,9 +162,6 @@ class IonGetPropertyIC : public IonIC
TypedOrValueRegister output_;
Register maybeTemp_; // Might be InvalidReg.
- static const size_t MAX_FAILED_UPDATES = 16;
- uint16_t failedUpdates_;
-
bool monitoredResult_ : 1;
bool allowDoubleResult_ : 1;
@@ -184,7 +175,6 @@ class IonGetPropertyIC : public IonIC
id_(id),
output_(output),
maybeTemp_(maybeTemp),
- failedUpdates_(0),
monitoredResult_(monitoredResult),
allowDoubleResult_(allowDoubleResult)
{ }
@@ -197,8 +187,6 @@ class IonGetPropertyIC : public IonIC
LiveRegisterSet liveRegs() const { return liveRegs_; }
bool allowDoubleResult() const { return allowDoubleResult_; }
- void maybeDisable(Zone* zone, bool attached);
-
static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonGetPropertyIC* ic,
HandleValue val, HandleValue idVal, MutableHandleValue res);
};
diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp
index 3f1b437ff6c8..6d85b2ed4d82 100644
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -175,7 +175,6 @@ ICStub::NonCacheIRStubMakesGCCalls(Kind kind)
case Call_ScriptedFunCall:
case Call_StringSplit:
case WarmUpCounter_Fallback:
- case GetProp_Generic:
case RetSub_Fallback:
// These two fallback stubs don't actually make non-tail calls,
// but the fallback code for the bailout path needs to pop the stub frame
@@ -355,8 +354,7 @@ ICFallbackStub::unlinkStub(Zone* zone, ICStub* prev, ICStub* stub)
}
}
- MOZ_ASSERT(numOptimizedStubs_ > 0);
- numOptimizedStubs_--;
+ state_.trackUnlinkedStub();
if (zone->needsIncrementalBarrier()) {
// We are removing edges from ICStub to gcthings. Perform one final trace
@@ -392,6 +390,13 @@ ICFallbackStub::unlinkStubsWithKind(JSContext* cx, ICStub::Kind kind)
}
}
+void
+ICFallbackStub::discardStubs(JSContext* cx)
+{
+ for (ICStubIterator iter = beginChain(); !iter.atEnd(); iter++)
+ iter.unlink(cx);
+}
+
void
ICTypeMonitor_Fallback::resetMonitorStubChain(Zone* zone)
{
@@ -1993,52 +1998,36 @@ DoGetPropFallback(JSContext* cx, BaselineFrame* frame, ICGetProp_Fallback* stub_
op == JSOP_LENGTH ||
op == JSOP_GETBOUNDNAME);
- // Grab our old shape before it goes away.
- RootedShape oldShape(cx);
- if (val.isObject())
- oldShape = val.toObject().maybeShape();
+ RootedPropertyName name(cx, script->getName(pc));
- bool attached = false;
// There are some reasons we can fail to attach a stub that are temporary.
// We want to avoid calling noteUnoptimizableAccess() if the reason we
// failed to attach a stub is one of those temporary reasons, since we might
// end up attaching a stub for the exact same access later.
bool isTemporarilyUnoptimizable = false;
- RootedPropertyName name(cx, script->getName(pc));
+ if (stub->state().maybeTransition())
+ stub->discardStubs(cx);
- // After the Genericstub was added, we should never reach the Fallbackstub again.
- MOZ_ASSERT(!stub->hasStub(ICStub::GetProp_Generic));
-
- if (stub->numOptimizedStubs() >= ICGetProp_Fallback::MAX_OPTIMIZED_STUBS && !stub.invalid()) {
- // Discard all stubs in this IC and replace with generic getprop stub.
- for (ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++)
- iter.unlink(cx);
- ICGetProp_Generic::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub());
- ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
- if (!newStub)
- return false;
- stub->addNewStub(newStub);
- attached = true;
- }
-
- if (!attached && !JitOptions.disableCacheIR) {
+ bool attached = false;
+ if (stub->state().canAttachStub()) {
RootedValue idVal(cx, StringValue(name));
- GetPropIRGenerator gen(cx, script, pc, CacheKind::GetProp, &isTemporarilyUnoptimizable,
- val, idVal, CanAttachGetter::Yes);
+ GetPropIRGenerator gen(cx, script, pc, CacheKind::GetProp, stub->state().mode(),
+ &isTemporarilyUnoptimizable, val, idVal, CanAttachGetter::Yes);
if (gen.tryAttachStub()) {
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
ICStubEngine::Baseline, script,
- stub);
+ stub, &attached);
if (newStub) {
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
- attached = true;
if (gen.shouldNotePreliminaryObjectStub())
newStub->toCacheIR_Monitored()->notePreliminaryObject();
else if (gen.shouldUnlinkPreliminaryObjectStubs())
StripPreliminaryObjectStubs(cx, stub);
}
}
+ if (!attached && !isTemporarilyUnoptimizable)
+ stub->state().trackNotAttached();
}
if (!ComputeGetPropResult(cx, frame, op, name, val, res))
@@ -2122,60 +2111,6 @@ ICGetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm, Handle<
}
}
-/* static */ ICGetProp_Generic*
-ICGetProp_Generic::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
- ICGetProp_Generic& other)
-{
- return New(cx, space, other.jitCode(), firstMonitorStub);
-}
-
-static bool
-DoGetPropGeneric(JSContext* cx, BaselineFrame* frame, ICGetProp_Generic* stub,
- MutableHandleValue val, MutableHandleValue res)
-{
- ICFallbackStub* fallback = stub->getChainFallback();
- JSScript* script = frame->script();
- jsbytecode* pc = fallback->icEntry()->pc(script);
- JSOp op = JSOp(*pc);
- RootedPropertyName name(cx, script->getName(pc));
- return ComputeGetPropResult(cx, frame, op, name, val, res);
-}
-
-typedef bool (*DoGetPropGenericFn)(JSContext*, BaselineFrame*, ICGetProp_Generic*,
- MutableHandleValue, MutableHandleValue);
-static const VMFunction DoGetPropGenericInfo =
- FunctionInfo(DoGetPropGeneric, "DoGetPropGeneric");
-
-bool
-ICGetProp_Generic::Compiler::generateStubCode(MacroAssembler& masm)
-{
- AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
-
- Register scratch = regs.takeAnyExcluding(ICTailCallReg);
-
- // Sync for the decompiler.
- if (engine_ == Engine::Baseline)
- EmitStowICValues(masm, 1);
-
- enterStubFrame(masm, scratch);
-
- // Push arguments.
- masm.Push(R0);
- masm.Push(ICStubReg);
- PushStubPayload(masm, R0.scratchReg());
-
- if (!callVM(DoGetPropGenericInfo, masm))
- return false;
-
- leaveStubFrame(masm);
-
- if (engine_ == Engine::Baseline)
- EmitUnstowICValues(masm, 1, /* discard = */ true);
-
- EmitEnterTypeMonitorIC(masm);
- return true;
-}
-
void
CheckForTypedObjectWithDetachedStorage(JSContext* cx, MacroAssembler& masm, Label* failure)
{
diff --git a/js/src/jit/SharedIC.h b/js/src/jit/SharedIC.h
index 9ca6e386f20b..d384233acfca 100644
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -13,6 +13,7 @@
#include "jit/BaselineICList.h"
#include "jit/BaselineJIT.h"
+#include "jit/ICState.h"
#include "jit/MacroAssembler.h"
#include "jit/SharedICList.h"
#include "jit/SharedICRegisters.h"
@@ -735,8 +736,7 @@ class ICFallbackStub : public ICStub
ICEntry* icEntry_;
// The number of stubs kept in the IC entry.
- uint32_t numOptimizedStubs_ : 31;
- uint32_t invalid_ : 1;
+ ICState state_;
// A pointer to the location stub pointer that needs to be
// changed to add a new "last" stub immediately before the fallback
@@ -748,15 +748,13 @@ class ICFallbackStub : public ICStub
ICFallbackStub(Kind kind, JitCode* stubCode)
: ICStub(kind, ICStub::Fallback, stubCode),
icEntry_(nullptr),
- numOptimizedStubs_(0),
- invalid_(false),
+ state_(),
lastStubPtrAddr_(nullptr) {}
ICFallbackStub(Kind kind, Trait trait, JitCode* stubCode)
: ICStub(kind, trait, stubCode),
icEntry_(nullptr),
- numOptimizedStubs_(0),
- invalid_(false),
+ state_(),
lastStubPtrAddr_(nullptr)
{
MOZ_ASSERT(trait == ICStub::Fallback ||
@@ -769,15 +767,19 @@ class ICFallbackStub : public ICStub
}
inline size_t numOptimizedStubs() const {
- return (size_t) numOptimizedStubs_;
+ return state_.numOptimizedStubs();
}
void setInvalid() {
- invalid_ = 1;
+ state_.setInvalid();
}
bool invalid() const {
- return invalid_;
+ return state_.invalid();
+ }
+
+ ICState& state() {
+ return state_;
}
// The icEntry and lastStubPtrAddr_ fields can't be initialized when the stub is
@@ -799,7 +801,7 @@ class ICFallbackStub : public ICStub
stub->setNext(this);
*lastStubPtrAddr_ = stub;
lastStubPtrAddr_ = stub->addressOfNext();
- numOptimizedStubs_++;
+ state_.trackAttached();
}
ICStubConstIterator beginChainConst() const {
@@ -827,6 +829,8 @@ class ICFallbackStub : public ICStub
return count;
}
+ void discardStubs(JSContext* cx);
+
void unlinkStub(Zone* zone, ICStub* prev, ICStub* stub);
void unlinkStubsWithKind(JSContext* cx, ICStub::Kind kind);
};
@@ -2324,7 +2328,6 @@ class ICGetProp_Fallback : public ICMonitoredFallbackStub
{ }
public:
- static const uint32_t MAX_OPTIMIZED_STUBS = 16;
static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0;
static const size_t ACCESSED_GETTER_BIT = 1;
@@ -2367,35 +2370,6 @@ class ICGetProp_Fallback : public ICMonitoredFallbackStub
};
};
-// Stub for sites, which are too polymorphic (i.e. MAX_OPTIMIZED_STUBS was reached)
-class ICGetProp_Generic : public ICMonitoredStub
-{
- friend class ICStubSpace;
-
- protected:
- explicit ICGetProp_Generic(JitCode* stubCode, ICStub* firstMonitorStub)
- : ICMonitoredStub(ICStub::GetProp_Generic, stubCode, firstMonitorStub) {}
-
- public:
- static ICGetProp_Generic* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
- ICGetProp_Generic& other);
-
- class Compiler : public ICStubCompiler {
- protected:
- MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
- ICStub* firstMonitorStub_;
- public:
- explicit Compiler(JSContext* cx, ICStub* firstMonitorStub)
- : ICStubCompiler(cx, ICStub::GetProp_Generic, ICStubEngine::Baseline),
- firstMonitorStub_(firstMonitorStub)
- {}
-
- ICStub* getStub(ICStubSpace* space) {
- return newStub(space, getStubCode(), firstMonitorStub_);
- }
- };
-};
-
static inline uint32_t
SimpleTypeDescrKey(SimpleTypeDescr* descr)
{
diff --git a/js/src/jit/SharedICList.h b/js/src/jit/SharedICList.h
index aac54ce39cb9..b86274d70799 100644
--- a/js/src/jit/SharedICList.h
+++ b/js/src/jit/SharedICList.h
@@ -36,7 +36,6 @@ namespace jit {
_(Compare_Int32WithBoolean) \
\
_(GetProp_Fallback) \
- _(GetProp_Generic) \
\
_(CacheIR_Regular) \
_(CacheIR_Monitored) \
diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp
index b9531fa13a9a..87b9d86a2509 100644
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1527,5 +1527,184 @@ CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind)
return true;
}
+template
+static MOZ_ALWAYS_INLINE bool
+GetNativeDataProperty(JSContext* cx, NativeObject* obj, jsid id, Value* vp)
+{
+ // Fast path used by megamorphic IC stubs. Unlike our other property
+ // lookup paths, this is optimized to be as fast as possible for simple
+ // data property lookups.
+
+ JS::AutoCheckCannotGC nogc;
+
+ MOZ_ASSERT(JSID_IS_ATOM(id) || JSID_IS_SYMBOL(id));
+
+ while (true) {
+ if (Shape* shape = obj->lastProperty()->search(cx, id)) {
+ if (!shape->hasSlot() || !shape->hasDefaultGetter())
+ return false;
+
+ *vp = obj->getSlot(shape->slot());
+ return true;
+ }
+
+ // Property not found. Watch out for Class hooks.
+ if (MOZ_UNLIKELY(!obj->is())) {
+ if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj) ||
+ obj->getClass()->getGetProperty())
+ {
+ return false;
+ }
+ }
+
+ JSObject* proto = obj->staticPrototype();
+ if (!proto) {
+ if (HandleMissing) {
+ vp->setUndefined();
+ return true;
+ }
+ return false;
+ }
+
+ if (!proto->isNative())
+ return false;
+ obj = &proto->as();
+ }
+}
+
+template
+bool
+GetNativeDataProperty(JSContext* cx, JSObject* obj, PropertyName* name, Value* vp)
+{
+ if (MOZ_UNLIKELY(!obj->isNative()))
+ return false;
+ return GetNativeDataProperty(cx, &obj->as(), NameToId(name), vp);
+}
+
+template bool
+GetNativeDataProperty(JSContext* cx, JSObject* obj, PropertyName* name, Value* vp);
+
+template bool
+GetNativeDataProperty(JSContext* cx, JSObject* obj, PropertyName* name, Value* vp);
+
+template
+bool
+GetNativeDataPropertyByValue(JSContext* cx, JSObject* obj, Value* vp)
+{
+ JS::AutoCheckCannotGC nogc;
+
+ if (MOZ_UNLIKELY(!obj->isNative()))
+ return false;
+
+ // vp[0] contains the id, result will be stored in vp[1].
+ Value idVal = vp[0];
+
+ jsid id;
+ if (MOZ_LIKELY(idVal.isString())) {
+ JSString* s = idVal.toString();
+ JSAtom* atom;
+ if (s->isAtom()) {
+ atom = &s->asAtom();
+ } else {
+ atom = AtomizeString(cx, s);
+ if (!atom)
+ return false;
+ }
+ id = AtomToId(atom);
+ } else if (idVal.isSymbol()) {
+ id = SYMBOL_TO_JSID(idVal.toSymbol());
+ } else {
+ if (!ValueToIdPure(idVal, &id))
+ return false;
+ }
+
+ // Watch out for ids that may be stored in dense elements.
+ static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT < JSID_INT_MAX,
+ "All dense elements must have integer jsids");
+ if (MOZ_UNLIKELY(JSID_IS_INT(id)))
+ return false;
+
+ Value* res = vp + 1;
+ return GetNativeDataProperty(cx, &obj->as(), id, res);
+}
+
+template bool
+GetNativeDataPropertyByValue(JSContext* cx, JSObject* obj, Value* vp);
+
+template bool
+GetNativeDataPropertyByValue(JSContext* cx, JSObject* obj, Value* vp);
+
+bool
+SetNativeDataProperty(JSContext* cx, JSObject* obj, PropertyName* name, Value* val)
+{
+ JS::AutoCheckCannotGC nogc;
+
+ if (MOZ_UNLIKELY(!obj->isNative()))
+ return false;
+
+ NativeObject* nobj = &obj->as();
+ Shape* shape = nobj->lastProperty()->search(cx, NameToId(name));
+ if (!shape ||
+ !shape->hasSlot() ||
+ !shape->hasDefaultSetter() ||
+ !shape->writable() ||
+ nobj->watched())
+ {
+ return false;
+ }
+
+ if (!HasTypePropertyId(nobj, NameToId(name), *val))
+ return false;
+
+ nobj->setSlot(shape->slot(), *val);
+ return true;
+}
+
+bool
+ObjectHasGetterSetter(JSContext* cx, JSObject* objArg, Shape* propShape)
+{
+ JS::AutoCheckCannotGC nogc;
+
+ MOZ_ASSERT(propShape->hasGetterObject() || propShape->hasSetterObject());
+
+ // Window objects may require outerizing (passing the WindowProxy to the
+ // getter/setter), so we don't support them here.
+ if (MOZ_UNLIKELY(!objArg->isNative() || IsWindow(objArg)))
+ return false;
+
+ NativeObject* nobj = &objArg->as();
+ jsid id = propShape->propid();
+
+ while (true) {
+ if (Shape* shape = nobj->lastProperty()->search(cx, id)) {
+ if (shape == propShape)
+ return true;
+ if (shape->getterOrUndefined() == propShape->getterOrUndefined() &&
+ shape->setterOrUndefined() == propShape->setterOrUndefined())
+ {
+ return true;
+ }
+ return false;
+ }
+
+ // Property not found. Watch out for Class hooks.
+ if (!nobj->is()) {
+ if (ClassMayResolveId(cx->names(), nobj->getClass(), id, nobj) ||
+ nobj->getClass()->getGetProperty())
+ {
+ return false;
+ }
+ }
+
+ JSObject* proto = nobj->staticPrototype();
+ if (!proto)
+ return false;
+
+ if (!proto->isNative())
+ return false;
+ nobj = &proto->as();
+ }
+}
+
} // namespace jit
} // namespace js
diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h
index a5fad9054fdb..62fcdb9677d2 100644
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -843,6 +843,20 @@ EqualStringsHelper(JSString* str1, JSString* str2);
MOZ_MUST_USE bool
CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind);
+template
+bool
+GetNativeDataProperty(JSContext* cx, JSObject* obj, PropertyName* name, Value* vp);
+
+template
+bool
+GetNativeDataPropertyByValue(JSContext* cx, JSObject* obj, Value* vp);
+
+bool
+SetNativeDataProperty(JSContext* cx, JSObject* obj, PropertyName* name, Value* val);
+
+bool
+ObjectHasGetterSetter(JSContext* cx, JSObject* obj, Shape* propShape);
+
} // namespace jit
} // namespace js
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index ee7a38064f62..7b8278820e79 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6341,8 +6341,10 @@ CreateErrorNoteVA(JSContext* cx,
ErrorArgumentsType argumentsType, va_list ap)
{
auto note = MakeUnique();
- if (!note)
+ if (!note) {
+ ReportOutOfMemory(cx);
return nullptr;
+ }
note->errorNumber = errorNumber;
note->filename = filename;
@@ -6424,8 +6426,10 @@ UniquePtr
JSErrorNotes::copy(JSContext* cx)
{
auto copiedNotes = MakeUnique();
- if (!copiedNotes)
+ if (!copiedNotes) {
+ ReportOutOfMemory(cx);
return nullptr;
+ }
for (auto&& note : *this) {
js::UniquePtr copied(CopyErrorNote(cx, note.get()));
diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp
index 5270c17eecb9..33feced1aee4 100644
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -552,7 +552,7 @@ PrintSingleError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
const char* kindPrefix = nullptr;
switch (kind) {
case PrintErrorKind::Error:
- break;
+ MOZ_CRASH("unreachable");
case PrintErrorKind::Warning:
kindPrefix = "warning";
break;
diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp
index e3439bad7d19..7820d0295f72 100644
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3879,12 +3879,13 @@ JSObject::traceChildren(JSTracer* trc)
{
TraceEdge(trc, &group_, "group");
+ if (is())
+ as().traceShape(trc);
+
const Class* clasp = group_->clasp();
if (clasp->isNative()) {
NativeObject* nobj = &as();
- TraceEdge(trc, &nobj->shape_, "shape");
-
{
GetObjectSlotNameFunctor func(nobj);
JS::AutoTracingDetails ctx(trc, func);
diff --git a/js/src/vm/Scope.cpp b/js/src/vm/Scope.cpp
index 6087b0ecd809..809334dbc237 100644
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -630,6 +630,7 @@ FunctionScope::create(JSContext* cx, Handle data,
return nullptr;
copy->hasParameterExprs = hasParameterExprs;
+ copy->canonicalFunction.init(fun);
// An environment may be needed regardless of existence of any closed over
// bindings:
@@ -647,8 +648,6 @@ FunctionScope::create(JSContext* cx, Handle data,
if (!scope)
return nullptr;
- copy->canonicalFunction.init(fun);
-
funScope = &scope->as();
funScope->initData(Move(copy.get()));
}
@@ -701,12 +700,12 @@ FunctionScope::clone(JSContext* cx, Handle scope, HandleFunction
if (!dataClone)
return nullptr;
- Scope* scopeClone= Scope::create(cx, scope->kind(), enclosing, envShape);
+ dataClone->canonicalFunction.init(fun);
+
+ Scope* scopeClone = Scope::create(cx, scope->kind(), enclosing, envShape);
if (!scopeClone)
return nullptr;
- dataClone->canonicalFunction.init(fun);
-
funScopeClone = &scopeClone->as();
funScopeClone->initData(Move(dataClone.get()));
}
diff --git a/js/src/vm/ShapedObject.h b/js/src/vm/ShapedObject.h
index b8b665e9d2ff..408649bd8269 100644
--- a/js/src/vm/ShapedObject.h
+++ b/js/src/vm/ShapedObject.h
@@ -39,6 +39,10 @@ class ShapedObject : public JSObject
Shape* shape() const { return this->shape_; }
+ void traceShape(JSTracer* trc) {
+ TraceEdge(trc, &shape_, "shape");
+ }
+
static size_t offsetOfShape() { return offsetof(ShapedObject, shape_); }
private:
diff --git a/js/src/vm/TypeInference-inl.h b/js/src/vm/TypeInference-inl.h
index 445cddf5d152..a8ae436156aa 100644
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -414,7 +414,7 @@ HasTypePropertyId(JSObject* obj, jsid id, TypeSet::Type type)
return true;
if (HeapTypeSet* types = obj->group()->maybeGetProperty(IdToTypeId(id)))
- return types->hasType(type);
+ return types->hasType(type) && !types->nonConstantProperty();
return false;
}
diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp
index 393d10763cf4..f49e2ddbf5ea 100644
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -768,27 +768,33 @@ class FunctionCompiler
load = MWasmLoad::New(alloc(), memoryBase, base, *access, ToMIRType(result));
}
- curBlock_->add(load);
+ if (load)
+ curBlock_->add(load);
+
return load;
}
- void store(MDefinition* base, MemoryAccessDesc* access, MDefinition* v)
+ MOZ_MUST_USE bool store(MDefinition* base, MemoryAccessDesc* access, MDefinition* v)
{
if (inDeadCode())
- return;
+ return true;
MWasmLoadTls* memoryBase = maybeLoadMemoryBase();
MInstruction* store = nullptr;
if (access->isPlainAsmJS()) {
MOZ_ASSERT(access->offset() == 0);
MWasmLoadTls* boundsCheckLimit = maybeLoadBoundsCheckLimit();
- store = MAsmJSStoreHeap::New(alloc(), memoryBase, base, boundsCheckLimit, access->type(), v);
+ store = MAsmJSStoreHeap::New(alloc(), memoryBase, base, boundsCheckLimit,
+ access->type(), v);
} else {
checkOffsetAndBounds(access, &base);
store = MWasmStore::New(alloc(), memoryBase, base, *access, v);
}
- curBlock_->add(store);
+ if (store)
+ curBlock_->add(store);
+
+ return !!store;
}
MDefinition* atomicCompareExchangeHeap(MDefinition* base, MemoryAccessDesc* access,
@@ -799,8 +805,10 @@ class FunctionCompiler
checkOffsetAndBounds(access, &base);
MWasmLoadTls* memoryBase = maybeLoadMemoryBase();
- auto* cas = MAsmJSCompareExchangeHeap::New(alloc(), memoryBase, base, *access, oldv, newv, tlsPointer_);
- curBlock_->add(cas);
+ auto* cas = MAsmJSCompareExchangeHeap::New(alloc(), memoryBase, base, *access, oldv, newv,
+ tlsPointer_);
+ if (cas)
+ curBlock_->add(cas);
return cas;
}
@@ -813,21 +821,23 @@ class FunctionCompiler
checkOffsetAndBounds(access, &base);
MWasmLoadTls* memoryBase = maybeLoadMemoryBase();
auto* cas = MAsmJSAtomicExchangeHeap::New(alloc(), memoryBase, base, *access, value, tlsPointer_);
- curBlock_->add(cas);
+ if (cas)
+ curBlock_->add(cas);
return cas;
}
MDefinition* atomicBinopHeap(js::jit::AtomicOp op,
- MDefinition* base, MemoryAccessDesc* access,
- MDefinition* v)
+ MDefinition* base, MemoryAccessDesc* access, MDefinition* v)
{
if (inDeadCode())
return nullptr;
checkOffsetAndBounds(access, &base);
MWasmLoadTls* memoryBase = maybeLoadMemoryBase();
- auto* binop = MAsmJSAtomicBinopHeap::New(alloc(), op, memoryBase, base, *access, v, tlsPointer_);
- curBlock_->add(binop);
+ auto* binop = MAsmJSAtomicBinopHeap::New(alloc(), op, memoryBase, base, *access, v,
+ tlsPointer_);
+ if (binop)
+ curBlock_->add(binop);
return binop;
}
@@ -2455,7 +2465,11 @@ EmitLoad(FunctionCompiler& f, ValType type, Scalar::Type viewType)
return false;
MemoryAccessDesc access(viewType, addr.align, addr.offset, f.trapIfNotAsmJS());
- f.iter().setResult(f.load(addr.base, &access, type));
+ auto* ins = f.load(addr.base, &access, type);
+ if (!f.inDeadCode() && !ins)
+ return false;
+
+ f.iter().setResult(ins);
return true;
}
@@ -2469,8 +2483,7 @@ EmitStore(FunctionCompiler& f, ValType resultType, Scalar::Type viewType)
MemoryAccessDesc access(viewType, addr.align, addr.offset, f.trapIfNotAsmJS());
- f.store(addr.base, &access, value);
- return true;
+ return f.store(addr.base, &access, value);
}
static bool
@@ -2483,8 +2496,7 @@ EmitTeeStore(FunctionCompiler& f, ValType resultType, Scalar::Type viewType)
MemoryAccessDesc access(viewType, addr.align, addr.offset, f.trapIfNotAsmJS());
- f.store(addr.base, &access, value);
- return true;
+ return f.store(addr.base, &access, value);
}
static bool
@@ -2504,8 +2516,7 @@ EmitTeeStoreWithCoercion(FunctionCompiler& f, ValType resultType, Scalar::Type v
MemoryAccessDesc access(viewType, addr.align, addr.offset, f.trapIfNotAsmJS());
- f.store(addr.base, &access, value);
- return true;
+ return f.store(addr.base, &access, value);
}
static bool
@@ -2599,7 +2610,11 @@ EmitAtomicsLoad(FunctionCompiler& f)
MemoryAccessDesc access(viewType, addr.align, addr.offset, Some(f.trapOffset()), 0,
MembarBeforeLoad, MembarAfterLoad);
- f.iter().setResult(f.load(addr.base, &access, ValType::I32));
+ auto* ins = f.load(addr.base, &access, ValType::I32);
+ if (!f.inDeadCode() && !ins)
+ return false;
+
+ f.iter().setResult(ins);
return true;
}
@@ -2615,7 +2630,9 @@ EmitAtomicsStore(FunctionCompiler& f)
MemoryAccessDesc access(viewType, addr.align, addr.offset, Some(f.trapOffset()), 0,
MembarBeforeStore, MembarAfterStore);
- f.store(addr.base, &access, value);
+ if (!f.store(addr.base, &access, value))
+ return false;
+
f.iter().setResult(value);
return true;
}
@@ -2632,7 +2649,11 @@ EmitAtomicsBinOp(FunctionCompiler& f)
MemoryAccessDesc access(viewType, addr.align, addr.offset, Some(f.trapOffset()));
- f.iter().setResult(f.atomicBinopHeap(op, addr.base, &access, value));
+ auto* ins = f.atomicBinopHeap(op, addr.base, &access, value);
+ if (!f.inDeadCode() && !ins)
+ return false;
+
+ f.iter().setResult(ins);
return true;
}
@@ -2648,7 +2669,11 @@ EmitAtomicsCompareExchange(FunctionCompiler& f)
MemoryAccessDesc access(viewType, addr.align, addr.offset, Some(f.trapOffset()));
- f.iter().setResult(f.atomicCompareExchangeHeap(addr.base, &access, oldValue, newValue));
+ auto* ins = f.atomicCompareExchangeHeap(addr.base, &access, oldValue, newValue);
+ if (!f.inDeadCode() && !ins)
+ return false;
+
+ f.iter().setResult(ins);
return true;
}
@@ -2663,7 +2688,11 @@ EmitAtomicsExchange(FunctionCompiler& f)
MemoryAccessDesc access(viewType, addr.align, addr.offset, Some(f.trapOffset()));
- f.iter().setResult(f.atomicExchangeHeap(addr.base, &access, value));
+ auto* ins = f.atomicExchangeHeap(addr.base, &access, value);
+ if (!f.inDeadCode() && !ins)
+ return false;
+
+ f.iter().setResult(ins);
return true;
}
@@ -2886,7 +2915,11 @@ EmitSimdLoad(FunctionCompiler& f, ValType resultType, unsigned numElems)
MemoryAccessDesc access(viewType, addr.align, addr.offset, Some(f.trapOffset()), numElems);
- f.iter().setResult(f.load(addr.base, &access, resultType));
+ auto* ins = f.load(addr.base, &access, resultType);
+ if (!f.inDeadCode() && !ins)
+ return false;
+
+ f.iter().setResult(ins);
return true;
}
@@ -2906,8 +2939,7 @@ EmitSimdStore(FunctionCompiler& f, ValType resultType, unsigned numElems)
MemoryAccessDesc access(viewType, addr.align, addr.offset, Some(f.trapOffset()), numElems);
- f.store(addr.base, &access, value);
- return true;
+ return f.store(addr.base, &access, value);
}
static bool
diff --git a/layout/base/GeckoRestyleManager.cpp b/layout/base/GeckoRestyleManager.cpp
index f20b84c88486..3b161914df97 100644
--- a/layout/base/GeckoRestyleManager.cpp
+++ b/layout/base/GeckoRestyleManager.cpp
@@ -200,14 +200,14 @@ ElementForStyleContext(nsIContent* aParentContent,
// Forwarded nsIDocumentObserver method, to handle restyling (and
// passing the notification to the frame).
-nsresult
+void
GeckoRestyleManager::ContentStateChanged(nsIContent* aContent,
EventStates aStateMask)
{
// XXXbz it would be good if this function only took Elements, but
// we'd have to make ESM guarantee that usefully.
if (!aContent->IsElement()) {
- return NS_OK;
+ return;
}
Element* aElement = aContent->AsElement();
@@ -217,7 +217,6 @@ GeckoRestyleManager::ContentStateChanged(nsIContent* aContent,
ContentStateChangedInternal(aElement, aStateMask, &changeHint, &restyleHint);
PostRestyleEvent(aElement, restyleHint, changeHint);
- return NS_OK;
}
// Forwarded nsIMutationObserver method, to handle restyling.
@@ -1515,7 +1514,7 @@ ElementRestyler::ConditionallyRestyleUndisplayedNodes(
}
for (UndisplayedNode* undisplayed = aUndisplayed; undisplayed;
- undisplayed = undisplayed->mNext) {
+ undisplayed = undisplayed->getNext()) {
if (!undisplayed->mContent->IsElement()) {
continue;
@@ -3174,7 +3173,7 @@ ElementRestyler::RestyleUndisplayedNodes(nsRestyleHint aChildRestyleHint,
if (undisplayed) {
pusher.PushAncestorAndStyleScope(undisplayedParent);
}
- for (; undisplayed; undisplayed = undisplayed->mNext) {
+ for (; undisplayed; undisplayed = undisplayed->getNext()) {
NS_ASSERTION(undisplayedParent ||
undisplayed->mContent ==
mPresContext->Document()->GetRootElement(),
diff --git a/layout/base/GeckoRestyleManager.h b/layout/base/GeckoRestyleManager.h
index cf599a4d2076..b228d119b0c4 100644
--- a/layout/base/GeckoRestyleManager.h
+++ b/layout/base/GeckoRestyleManager.h
@@ -54,8 +54,8 @@ protected:
public:
// Forwarded nsIDocumentObserver method, to handle restyling (and
// passing the notification to the frame).
- nsresult ContentStateChanged(nsIContent* aContent,
- EventStates aStateMask);
+ void ContentStateChanged(nsIContent* aContent,
+ EventStates aStateMask);
// Forwarded nsIMutationObserver method, to handle restyling.
void AttributeWillChange(Element* aElement,
diff --git a/layout/base/MobileViewportManager.cpp b/layout/base/MobileViewportManager.cpp
index ec28cdc8650d..6c359048eac1 100644
--- a/layout/base/MobileViewportManager.cpp
+++ b/layout/base/MobileViewportManager.cpp
@@ -305,6 +305,13 @@ MobileViewportManager::UpdateDisplayPortMargins()
// comment 1).
return;
}
+ nsRect displayportBase =
+ nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(root));
+ // We only create MobileViewportManager for root content documents. If that ever changes
+ // we'd need to limit the size of this displayport base rect because non-toplevel documents
+ // have no limit on their size.
+ MOZ_ASSERT(mPresShell->GetPresContext()->IsRootContentDocument());
+ nsLayoutUtils::SetDisplayPortBaseIfNotSet(root->GetContent(), displayportBase);
nsIScrollableFrame* scrollable = do_QueryFrame(root);
nsLayoutUtils::CalculateAndSetDisplayPortMargins(scrollable,
nsLayoutUtils::RepaintMode::DoNotRepaint);
diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
index 5b1fd8ce68c7..3e96f4d6a2af 100644
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -4526,14 +4526,14 @@ PresShell::NotifyCounterStylesAreDirty()
mFrameConstructor->EndUpdate();
}
-nsresult
-PresShell::ReconstructFrames(void)
+void
+PresShell::ReconstructFrames()
{
NS_PRECONDITION(!mFrameConstructor->GetRootFrame() || mDidInitialize,
"Must not have root frame before initial reflow");
if (!mDidInitialize || mIsDestroying) {
// Nothing to do here
- return NS_OK;
+ return;
}
nsCOMPtr kungFuDeathGrip(this);
@@ -4543,16 +4543,14 @@ PresShell::ReconstructFrames(void)
mDocument->FlushPendingNotifications(FlushType::ContentAndNotify);
if (mIsDestroying) {
- return NS_OK;
+ return;
}
nsAutoCauseReflowNotifier crNotifier(this);
mFrameConstructor->BeginUpdate();
- nsresult rv = mFrameConstructor->ReconstructDocElementHierarchy();
+ mFrameConstructor->ReconstructDocElementHierarchy();
VERIFY_STYLE_TREE;
mFrameConstructor->EndUpdate();
-
- return rv;
}
void
diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h
index 67248e23724b..6b83c4a7976a 100644
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -188,7 +188,7 @@ public:
virtual void NotifyCounterStylesAreDirty() override;
- virtual nsresult ReconstructFrames(void) override;
+ virtual void ReconstructFrames(void) override;
virtual void Freeze() override;
virtual void Thaw() override;
virtual void FireOrClearDelayedEvents(bool aFireEvents) override;
diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp
index 93c3c5e2edf0..d89d122c2f4a 100644
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -1172,12 +1172,8 @@ SyncViewsAndInvalidateDescendants(nsIFrame* aFrame, nsChangeHint aChange)
nsChangeHint_SchedulePaint)),
"Invalid change flag");
- nsView* view = aFrame->GetView();
- if (view) {
- if (aChange & nsChangeHint_SyncFrameView) {
- nsContainerFrame::SyncFrameViewProperties(aFrame->PresContext(), aFrame,
- nullptr, view);
- }
+ if (aChange & nsChangeHint_SyncFrameView) {
+ aFrame->SyncFrameViewProperties();
}
nsIFrame::ChildListIterator lists(aFrame);
diff --git a/layout/base/RestyleManager.h b/layout/base/RestyleManager.h
index f973323cc279..39e06efd2435 100644
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -174,8 +174,8 @@ public:
inline void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
nsRestyleHint aRestyleHint);
inline void ProcessPendingRestyles();
- inline nsresult ContentStateChanged(nsIContent* aContent,
- EventStates aStateMask);
+ inline void ContentStateChanged(nsIContent* aContent,
+ EventStates aStateMask);
inline void AttributeWillChange(dom::Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
diff --git a/layout/base/RestyleManagerInlines.h b/layout/base/RestyleManagerInlines.h
index 921739fa893f..0c6aabe824c1 100644
--- a/layout/base/RestyleManagerInlines.h
+++ b/layout/base/RestyleManagerInlines.h
@@ -44,7 +44,7 @@ RestyleManager::ProcessPendingRestyles()
MOZ_STYLO_FORWARD(ProcessPendingRestyles, ());
}
-nsresult
+void
RestyleManager::ContentStateChanged(nsIContent* aContent,
EventStates aStateMask)
{
diff --git a/layout/base/ServoRestyleManager.cpp b/layout/base/ServoRestyleManager.cpp
index f3dd2a11c0f4..1addf4cf08f7 100644
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -462,12 +462,12 @@ ServoRestyleManager::ContentRemoved(nsINode* aContainer,
NS_WARNING("stylo: ServoRestyleManager::ContentRemoved not implemented");
}
-nsresult
+void
ServoRestyleManager::ContentStateChanged(nsIContent* aContent,
EventStates aChangedBits)
{
if (!aContent->IsElement()) {
- return NS_OK;
+ return;
}
Element* aElement = aContent->AsElement();
@@ -502,8 +502,6 @@ ServoRestyleManager::ContentStateChanged(nsIContent* aContent,
snapshot->AddState(previousState);
PostRestyleEvent(aElement, restyleHint, changeHint);
}
-
- return NS_OK;
}
void
diff --git a/layout/base/ServoRestyleManager.h b/layout/base/ServoRestyleManager.h
index c82075394944..e34db1040194 100644
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -63,8 +63,7 @@ public:
nsIContent* aChild);
void RestyleForAppend(nsIContent* aContainer,
nsIContent* aFirstNewContent);
- nsresult ContentStateChanged(nsIContent* aContent,
- EventStates aStateMask);
+ void ContentStateChanged(nsIContent* aContent, EventStates aStateMask);
void AttributeWillChange(dom::Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp
index 60aea641c4f8..1b18cb2477a9 100644
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -2732,8 +2732,7 @@ nsCSSFrameConstructor::ConstructRootFrame()
nsView* rootView = mPresShell->GetViewManager()->GetRootView();
viewportFrame->SetView(rootView);
- nsContainerFrame::SyncFrameViewProperties(mPresShell->GetPresContext(), viewportFrame,
- viewportPseudoStyle, rootView);
+ viewportFrame->SyncFrameViewProperties(rootView);
nsContainerFrame::SyncWindowProperties(mPresShell->GetPresContext(), viewportFrame,
rootView, nullptr, nsContainerFrame::SET_ASYNC);
@@ -3215,7 +3214,7 @@ nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState,
* But the select tag should really be fixed to use GFX scrollbars that can
* be create with BuildScrollFrame.
*/
-nsresult
+void
nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState,
nsContainerFrame* scrollFrame,
nsContainerFrame* scrolledFrame,
@@ -3241,10 +3240,6 @@ nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState,
aStyleContext, aParentFrame);
}
- if (aBuildCombobox) {
- nsContainerFrame::CreateViewForFrame(scrollFrame, true);
- }
-
BuildScrollFrame(aState, aContent, aStyleContext, scrolledFrame,
geometricParent, scrollFrame);
@@ -3261,7 +3256,6 @@ nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState,
// Set the scrolled frame's initial child lists
scrolledFrame->SetInitialChildList(kPrincipalList, childItems);
- return NS_OK;
}
nsIFrame*
@@ -3930,8 +3924,6 @@ nsCSSFrameConstructor::ConstructFrameFromItemInternal(FrameConstructionItem& aIt
frameToAddToList = scrollframe;
} else {
InitAndRestoreFrame(aState, content, geometricParent, newFrame);
- // See whether we need to create a view
- nsContainerFrame::CreateViewForFrame(newFrame, false);
frameToAddToList = newFrame;
}
@@ -6264,16 +6256,16 @@ IsRootBoxFrame(nsIFrame *aFrame)
return (aFrame->GetType() == nsGkAtoms::rootFrame);
}
-nsresult
+void
nsCSSFrameConstructor::ReconstructDocElementHierarchy()
{
Element* rootElement = mDocument->GetRootElement();
if (!rootElement) {
/* nothing to do */
- return NS_OK;
+ return;
}
- return RecreateFramesForContent(rootElement, false, REMOVE_FOR_RECONSTRUCTION,
- nullptr);
+ RecreateFramesForContent(rootElement, false, REMOVE_FOR_RECONSTRUCTION,
+ nullptr);
}
nsContainerFrame*
@@ -6523,7 +6515,7 @@ GetInsertNextSibling(nsIFrame* aParentFrame, nsIFrame* aPrevSibling)
* appending flowed frames to a parent's principal child list. It handles the
* case where the parent is the trailing inline of an {ib} split.
*/
-nsresult
+void
nsCSSFrameConstructor::AppendFramesToParent(nsFrameConstructorState& aState,
nsContainerFrame* aParentFrame,
nsFrameItems& aFrameList,
@@ -6606,15 +6598,13 @@ nsCSSFrameConstructor::AppendFramesToParent(nsFrameConstructorState& aStat
// Recurse so we create new ib siblings as needed for aParentFrame's parent
return AppendFramesToParent(aState, aParentFrame->GetParent(), ibSiblings,
- aParentFrame, true);
+ aParentFrame, true);
}
-
- return NS_OK;
+ return;
}
// Insert the frames after our aPrevSibling
InsertFrames(aParentFrame, kPrincipalList, aPrevSibling, aFrameList);
- return NS_OK;
}
#define UNSET_DISPLAY static_cast(255)
@@ -7383,7 +7373,7 @@ nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
return false;
}
-nsresult
+void
nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
nsIContent* aFirstNewContent,
bool aAllowLazyConstruction,
@@ -7429,8 +7419,7 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
if (tag == nsGkAtoms::treechildren ||
tag == nsGkAtoms::treeitem ||
tag == nsGkAtoms::treerow)
- return NS_OK;
-
+ return;
}
#endif // MOZ_XUL
@@ -7443,10 +7432,10 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
//XXXsmaug This is super unefficient!
nsIContent* bindingParent = aContainer->GetBindingParent();
LAYOUT_PHASE_TEMP_EXIT();
- nsresult rv = RecreateFramesForContent(bindingParent, false,
- REMOVE_FOR_RECONSTRUCTION, nullptr);
+ RecreateFramesForContent(bindingParent, false,
+ REMOVE_FOR_RECONSTRUCTION, nullptr);
LAYOUT_PHASE_TEMP_REENTER();
- return rv;
+ return;
}
// The frame constructor uses this codepath both for bonafide newly-added
@@ -7466,7 +7455,7 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
if (isNewlyAddedContentForServo) {
aContainer->AsElement()->NoteDirtyDescendantsForServo();
}
- return NS_OK;
+ return;
}
if (aAllowLazyConstruction &&
@@ -7474,7 +7463,7 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
if (isNewlyAddedContentForServo) {
aContainer->AsElement()->NoteDirtyDescendantsForServo();
}
- return NS_OK;
+ return;
}
// We couldn't construct lazily. Make Servo eagerly traverse the subtree.
@@ -7489,13 +7478,13 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
nsContainerFrame*& parentFrame = insertion.mParentFrame;
LAYOUT_PHASE_TEMP_REENTER();
if (!parentFrame) {
- return NS_OK;
+ return;
}
LAYOUT_PHASE_TEMP_EXIT();
if (MaybeRecreateForFrameset(parentFrame, aFirstNewContent, nullptr)) {
LAYOUT_PHASE_TEMP_REENTER();
- return NS_OK;
+ return;
}
LAYOUT_PHASE_TEMP_REENTER();
@@ -7503,15 +7492,15 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
// Nothing to do here; we shouldn't be constructing kids of leaves
// Clear lazy bits so we don't try to construct again.
ClearLazyBits(aFirstNewContent, nullptr);
- return NS_OK;
+ return;
}
if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) {
LAYOUT_PHASE_TEMP_EXIT();
- nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false,
- REMOVE_FOR_RECONSTRUCTION, nullptr);
+ RecreateFramesForContent(parentFrame->GetContent(), false,
+ REMOVE_FOR_RECONSTRUCTION, nullptr);
LAYOUT_PHASE_TEMP_REENTER();
- return rv;
+ return;
}
// If the frame we are manipulating is a ib-split frame (that is, one
@@ -7618,7 +7607,7 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
if (WipeContainingBlock(state, containingBlock, parentFrame, items,
true, prevSibling)) {
LAYOUT_PHASE_TEMP_REENTER();
- return NS_OK;
+ return;
}
LAYOUT_PHASE_TEMP_REENTER();
@@ -7704,8 +7693,6 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
aFirstNewContent, nullptr);
}
#endif
-
- return NS_OK;
}
#ifdef MOZ_XUL
@@ -7748,17 +7735,17 @@ bool NotifyListBoxBody(nsPresContext* aPresContext,
}
#endif // MOZ_XUL
-nsresult
+void
nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer,
nsIContent* aChild,
nsILayoutHistoryState* aFrameState,
bool aAllowLazyConstruction)
{
- return ContentRangeInserted(aContainer,
- aChild,
- aChild->GetNextSibling(),
- aFrameState,
- aAllowLazyConstruction);
+ ContentRangeInserted(aContainer,
+ aChild,
+ aChild->GetNextSibling(),
+ aFrameState,
+ aAllowLazyConstruction);
}
// ContentRangeInserted handles creating frames for a range of nodes that
@@ -7779,7 +7766,7 @@ nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer,
// in the caption list, while skipping any nodes in the range being inserted
// (because when we treat the caption frames the other nodes have had their
// frames constructed but not yet inserted into the frame tree).
-nsresult
+void
nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
nsIContent* aStartChild,
nsIContent* aEndChild,
@@ -7837,7 +7824,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
// The insert case in NotifyListBoxBody
// doesn't use "old next sibling".
aStartChild, nullptr, nullptr, CONTENT_INSERTED)) {
- return NS_OK;
+ return;
}
} else {
// We don't handle a range insert to a listbox parent, issue single
@@ -7846,7 +7833,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
aAllowLazyConstruction);
LAYOUT_PHASE_TEMP_REENTER();
- return NS_OK;
+ return;
}
}
#endif // MOZ_XUL
@@ -7861,7 +7848,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
if (aStartChild != docElement) {
// Not the root element; just bail out
- return NS_OK;
+ return;
}
NS_PRECONDITION(nullptr == mRootElementFrame,
@@ -7898,7 +7885,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
}
#endif
- return NS_OK;
+ return;
}
if (aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE) &&
@@ -7911,10 +7898,10 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
//XXXsmaug This is super unefficient!
nsIContent* bindingParent = aContainer->GetBindingParent();
LAYOUT_PHASE_TEMP_EXIT();
- nsresult rv = RecreateFramesForContent(bindingParent, false,
- REMOVE_FOR_RECONSTRUCTION, nullptr);
+ RecreateFramesForContent(bindingParent, false,
+ REMOVE_FOR_RECONSTRUCTION, nullptr);
LAYOUT_PHASE_TEMP_REENTER();
- return rv;
+ return;
}
// The frame constructor uses this codepath both for bonafide newly-added
@@ -7940,7 +7927,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
if (isNewlyAddedContentForServo) {
aContainer->AsElement()->NoteDirtyDescendantsForServo();
}
- return NS_OK;
+ return;
}
// Otherwise, we've got parent content. Find its frame.
@@ -7952,7 +7939,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
if (isNewlyAddedContentForServo) {
aContainer->AsElement()->NoteDirtyDescendantsForServo();
}
- return NS_OK;
+ return;
}
}
@@ -7977,7 +7964,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
}
if (!insertion.mParentFrame) {
- return NS_OK;
+ return;
}
bool isAppend, isRangeInsertSafe;
@@ -7991,7 +7978,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
aAllowLazyConstruction);
LAYOUT_PHASE_TEMP_REENTER();
- return NS_OK;
+ return;
}
nsIContent* container = insertion.mParentFrame->GetContent();
@@ -8000,7 +7987,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
LAYOUT_PHASE_TEMP_EXIT();
if (MaybeRecreateForFrameset(insertion.mParentFrame, aStartChild, aEndChild)) {
LAYOUT_PHASE_TEMP_REENTER();
- return NS_OK;
+ return;
}
LAYOUT_PHASE_TEMP_REENTER();
@@ -8017,10 +8004,10 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
// and if so, proceed. But we'd have to extend nsFieldSetFrame
// to locate this legend in the inserted frames and extract it.
LAYOUT_PHASE_TEMP_EXIT();
- nsresult rv = RecreateFramesForContent(insertion.mParentFrame->GetContent(), false,
- REMOVE_FOR_RECONSTRUCTION, nullptr);
+ RecreateFramesForContent(insertion.mParentFrame->GetContent(), false,
+ REMOVE_FOR_RECONSTRUCTION, nullptr);
LAYOUT_PHASE_TEMP_REENTER();
- return rv;
+ return;
}
// We should only get here with details when doing a single insertion because
@@ -8032,26 +8019,25 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
// expensive to recreate the entire details frame, but it's the simplest way
// to handle the insertion.
LAYOUT_PHASE_TEMP_EXIT();
- nsresult rv =
- RecreateFramesForContent(insertion.mParentFrame->GetContent(), false,
- REMOVE_FOR_RECONSTRUCTION, nullptr);
+ RecreateFramesForContent(insertion.mParentFrame->GetContent(), false,
+ REMOVE_FOR_RECONSTRUCTION, nullptr);
LAYOUT_PHASE_TEMP_REENTER();
- return rv;
+ return;
}
// Don't construct kids of leaves
if (insertion.mParentFrame->IsLeaf()) {
// Clear lazy bits so we don't try to construct again.
ClearLazyBits(aStartChild, aEndChild);
- return NS_OK;
+ return;
}
if (insertion.mParentFrame->IsFrameOfType(nsIFrame::eMathML)) {
LAYOUT_PHASE_TEMP_EXIT();
- nsresult rv = RecreateFramesForContent(insertion.mParentFrame->GetContent(), false,
- REMOVE_FOR_RECONSTRUCTION, nullptr);
+ RecreateFramesForContent(insertion.mParentFrame->GetContent(), false,
+ REMOVE_FOR_RECONSTRUCTION, nullptr);
LAYOUT_PHASE_TEMP_REENTER();
- return rv;
+ return;
}
Maybe matchContext;
@@ -8132,7 +8118,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
aAllowLazyConstruction);
LAYOUT_PHASE_TEMP_REENTER();
- return NS_OK;
+ return;
}
container = insertion.mParentFrame->GetContent();
@@ -8200,7 +8186,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
if (WipeContainingBlock(state, containingBlock, insertion.mParentFrame, items,
isAppend, prevSibling)) {
LAYOUT_PHASE_TEMP_REENTER();
- return NS_OK;
+ return;
}
LAYOUT_PHASE_TEMP_REENTER();
@@ -8371,11 +8357,9 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
aStartChild, aEndChild);
}
#endif
-
- return NS_OK;
}
-nsresult
+void
nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
nsIContent* aChild,
nsIContent* aOldNextSibling,
@@ -8415,7 +8399,6 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
}
#endif
- nsresult rv = NS_OK;
nsIFrame* childFrame = aChild->GetPrimaryFrame();
if (!childFrame || childFrame->GetContent() != aChild) {
// XXXbz the GetContent() != aChild check is needed due to bug 135040.
@@ -8442,7 +8425,7 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
// XXX Perhaps even only those that belong to the aChild sub-tree?
RecreateFramesForContent(ancestor, false, aFlags, aDestroyedFramesFor);
LAYOUT_PHASE_TEMP_REENTER();
- return NS_OK;
+ return;
}
}
@@ -8450,11 +8433,10 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
if (c->GetPrimaryFrame() || GetDisplayContentsStyleFor(c)) {
LAYOUT_PHASE_TEMP_EXIT();
- rv = ContentRemoved(aChild, c, nullptr, aFlags, aDidReconstruct, aDestroyedFramesFor);
+ ContentRemoved(aChild, c, nullptr, aFlags, aDidReconstruct, aDestroyedFramesFor);
LAYOUT_PHASE_TEMP_REENTER();
- NS_ENSURE_SUCCESS(rv, rv);
if (aFlags != REMOVE_DESTROY_FRAMES && *aDidReconstruct) {
- return rv;
+ return;
}
}
}
@@ -8468,7 +8450,7 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
if (aFlags == REMOVE_DESTROY_FRAMES) {
CaptureStateForFramesOf(aChild, mTempFrameTreeState);
}
- return NS_OK;
+ return;
}
#endif // MOZ_XUL
@@ -8506,10 +8488,9 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
nsIContent* bindingParent = aContainer->GetBindingParent();
*aDidReconstruct = true;
LAYOUT_PHASE_TEMP_EXIT();
- nsresult rv = RecreateFramesForContent(bindingParent, false,
- aFlags, aDestroyedFramesFor);
+ RecreateFramesForContent(bindingParent, false, aFlags, aDestroyedFramesFor);
LAYOUT_PHASE_TEMP_REENTER();
- return rv;
+ return;
}
if (aFlags == REMOVE_DESTROY_FRAMES) {
@@ -8522,14 +8503,14 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
// See whether we need to remove more than just childFrame
LAYOUT_PHASE_TEMP_EXIT();
nsIContent* container;
- if (MaybeRecreateContainerForFrameRemoval(childFrame, aFlags, &rv, &container)) {
+ if (MaybeRecreateContainerForFrameRemoval(childFrame, aFlags, &container)) {
LAYOUT_PHASE_TEMP_REENTER();
MOZ_ASSERT(container);
*aDidReconstruct = true;
if (aDestroyedFramesFor) {
*aDestroyedFramesFor = container;
}
- return rv;
+ return;
}
LAYOUT_PHASE_TEMP_REENTER();
@@ -8542,10 +8523,10 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
// Just reframe the parent, since framesets are weird like that.
*aDidReconstruct = true;
LAYOUT_PHASE_TEMP_EXIT();
- nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false,
- aFlags, aDestroyedFramesFor);
+ RecreateFramesForContent(parentFrame->GetContent(), false,
+ aFlags, aDestroyedFramesFor);
LAYOUT_PHASE_TEMP_REENTER();
- return rv;
+ return;
}
// If we're a child of MathML, then we should reframe the MathML content.
@@ -8556,10 +8537,10 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
if (possibleMathMLAncestor->IsFrameOfType(nsIFrame::eMathML)) {
*aDidReconstruct = true;
LAYOUT_PHASE_TEMP_EXIT();
- nsresult rv = RecreateFramesForContent(possibleMathMLAncestor->GetContent(),
- false, aFlags, aDestroyedFramesFor);
+ RecreateFramesForContent(possibleMathMLAncestor->GetContent(),
+ false, aFlags, aDestroyedFramesFor);
LAYOUT_PHASE_TEMP_REENTER();
- return rv;
+ return;
}
// Undo XUL wrapping if it's no longer needed.
@@ -8573,10 +8554,10 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
!AnyKidsNeedBlockParent(childFrame->GetNextSibling())) {
*aDidReconstruct = true;
LAYOUT_PHASE_TEMP_EXIT();
- nsresult rv = RecreateFramesForContent(grandparentFrame->GetContent(), true,
- aFlags, aDestroyedFramesFor);
+ RecreateFramesForContent(grandparentFrame->GetContent(), true,
+ aFlags, aDestroyedFramesFor);
LAYOUT_PHASE_TEMP_REENTER();
- return rv;
+ return;
}
#ifdef ACCESSIBILITY
@@ -8620,7 +8601,7 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
// XXXbz the GetContent() != aChild check is needed due to bug 135040.
// Remove it once that's fixed.
ClearUndisplayedContentIn(aChild, aContainer);
- return NS_OK;
+ return;
}
parentFrame = childFrame->GetParent();
parentType = parentFrame->GetType();
@@ -8708,8 +8689,6 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
}
#endif
}
-
- return rv;
}
/**
@@ -8771,12 +8750,11 @@ nsCSSFrameConstructor::EnsureFrameForTextNode(nsGenericDOMDataNode* aContent)
return aContent->GetPrimaryFrame();
}
-nsresult
+void
nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent,
CharacterDataChangeInfo* aInfo)
{
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
- nsresult rv = NS_OK;
if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&
!aContent->TextIsOnlyWhitespace()) ||
@@ -8788,10 +8766,10 @@ nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent,
"Bit should never be set on generated content");
#endif
LAYOUT_PHASE_TEMP_EXIT();
- nsresult rv = RecreateFramesForContent(aContent, false,
- REMOVE_FOR_RECONSTRUCTION, nullptr);
+ RecreateFramesForContent(aContent, false,
+ REMOVE_FOR_RECONSTRUCTION, nullptr);
LAYOUT_PHASE_TEMP_REENTER();
- return rv;
+ return;
}
// Find the child frame
@@ -8839,8 +8817,6 @@ nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent,
RecoverLetterFrames(block);
}
}
-
- return rv;
}
void
@@ -9507,12 +9483,10 @@ FindPreviousNonWhitespaceSibling(nsIFrame* aFrame)
bool
nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
RemoveFlags aFlags,
- nsresult* aResult,
nsIContent** aDestroyedFramesFor)
{
NS_PRECONDITION(aFrame, "Must have a frame");
NS_PRECONDITION(aFrame->GetParent(), "Frame shouldn't be root");
- NS_PRECONDITION(aResult, "Null out param?");
NS_PRECONDITION(aFrame == aFrame->FirstContinuation(),
"aFrame not the result of GetPrimaryFrame()?");
@@ -9530,7 +9504,7 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
}
#endif
- *aResult = ReframeContainingBlock(aFrame, aFlags, aDestroyedFramesFor);
+ ReframeContainingBlock(aFrame, aFlags, aDestroyedFramesFor);
return true;
}
@@ -9539,8 +9513,8 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
aFrame->GetParent()->GetType() == nsGkAtoms::fieldSetFrame) {
// When we remove the legend for a fieldset, we should reframe
// the fieldset to ensure another legend is used, if there is one
- *aResult = RecreateFramesForContent(aFrame->GetParent()->GetContent(), false,
- aFlags, aDestroyedFramesFor);
+ RecreateFramesForContent(aFrame->GetParent()->GetContent(), false,
+ aFlags, aDestroyedFramesFor);
return true;
}
@@ -9553,9 +9527,9 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
// When removing a summary, we should reframe the parent details frame to
// ensure that another summary is used or the default summary is
// generated.
- *aResult = RecreateFramesForContent(aFrame->GetParent()->GetContent(),
- false, REMOVE_FOR_RECONSTRUCTION,
- aDestroyedFramesFor);
+ RecreateFramesForContent(aFrame->GetParent()->GetContent(),
+ false, REMOVE_FOR_RECONSTRUCTION,
+ aDestroyedFramesFor);
return true;
}
}
@@ -9587,8 +9561,8 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
parent->GetChildList(nsIFrame::kCaptionList).FirstChild() == inFlowFrame)) {
// We're the first or last frame in the pseudo. Need to reframe.
// Good enough to recreate frames for |parent|'s content
- *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags,
- aDestroyedFramesFor);
+ RecreateFramesForContent(parent->GetContent(), true, aFlags,
+ aDestroyedFramesFor);
return true;
}
}
@@ -9617,8 +9591,8 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
#endif
// Good enough to recreate frames for aFrame's parent's content; even if
// aFrame's parent is a pseudo, that'll be the right content node.
- *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags,
- aDestroyedFramesFor);
+ RecreateFramesForContent(parent->GetContent(), true, aFlags,
+ aDestroyedFramesFor);
return true;
}
}
@@ -9634,8 +9608,8 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
// frames may be constructed or destroyed accordingly.
// 2. The type of the first child of a ruby frame determines
// whether a pseudo ruby base container should exist.
- *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags,
- aDestroyedFramesFor);
+ RecreateFramesForContent(parent->GetContent(), true, aFlags,
+ aDestroyedFramesFor);
return true;
}
@@ -9658,8 +9632,8 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
}
#endif // DEBUG
// Recreate frames for the flex container (the removed frame's parent)
- *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags,
- aDestroyedFramesFor);
+ RecreateFramesForContent(parent->GetContent(), true, aFlags,
+ aDestroyedFramesFor);
return true;
}
@@ -9677,8 +9651,8 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
}
#endif // DEBUG
// Recreate frames for the flex container (the removed frame's grandparent)
- *aResult = RecreateFramesForContent(parent->GetParent()->GetContent(), true,
- aFlags, aDestroyedFramesFor);
+ RecreateFramesForContent(parent->GetParent()->GetContent(), true,
+ aFlags, aDestroyedFramesFor);
return true;
}
@@ -9686,7 +9660,7 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
if (aFrame->GetType() == nsGkAtoms::popupSetFrame) {
nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell);
if (rootBox && rootBox->GetPopupSetFrame() == aFrame) {
- *aResult = ReconstructDocElementHierarchy();
+ ReconstructDocElementHierarchy();
return true;
}
}
@@ -9698,8 +9672,8 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
!inFlowFrame->GetNextSibling() &&
((parent->GetPrevContinuation() && !parent->GetPrevInFlow()) ||
(parent->GetNextContinuation() && !parent->GetNextInFlow()))) {
- *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags,
- aDestroyedFramesFor);
+ RecreateFramesForContent(parent->GetContent(), true, aFlags,
+ aDestroyedFramesFor);
return true;
}
@@ -9735,11 +9709,11 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
}
#endif
- *aResult = ReframeContainingBlock(parent, aFlags, aDestroyedFramesFor);
+ ReframeContainingBlock(parent, aFlags, aDestroyedFramesFor);
return true;
}
-nsresult
+void
nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent,
bool aAsyncInsert,
RemoveFlags aFlags,
@@ -9752,7 +9726,9 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent,
// anyway).
// Rebuilding the frame tree can have bad effects, especially if it's the
// frame tree for chrome (see bug 157322).
- NS_ENSURE_TRUE(aContent->GetComposedDoc(), NS_ERROR_FAILURE);
+ if (NS_WARN_IF(!aContent->GetComposedDoc())) {
+ return;
+ }
// Is the frame ib-split? If so, we need to reframe the containing
// block *here*, rather than trying to remove and re-insert the
@@ -9816,15 +9792,14 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent,
}
}
- nsresult rv = NS_OK;
nsIContent* container;
- if (frame && MaybeRecreateContainerForFrameRemoval(frame, aFlags, &rv,
+ if (frame && MaybeRecreateContainerForFrameRemoval(frame, aFlags,
&container)) {
MOZ_ASSERT(container);
if (aDestroyedFramesFor) {
*aDestroyedFramesFor = container;
}
- return rv;
+ return;
}
nsINode* containerNode = aContent->GetParentNode();
@@ -9844,11 +9819,8 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent,
nullptr : aContent->GetNextSibling();
const bool reconstruct = aFlags != REMOVE_DESTROY_FRAMES;
RemoveFlags flags = reconstruct ? REMOVE_FOR_RECONSTRUCTION : aFlags;
- rv = ContentRemoved(container, aContent, nextSibling, flags,
- &didReconstruct, aDestroyedFramesFor);
- if (NS_FAILED(rv)) {
- return rv;
- }
+ ContentRemoved(container, aContent, nextSibling, flags,
+ &didReconstruct, aDestroyedFramesFor);
if (reconstruct && !didReconstruct) {
// Now, recreate the frames associated with this content object. If
// ContentRemoved triggered reconstruction, then we don't need to do this
@@ -9858,12 +9830,10 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent,
RestyleManager()->PostRestyleEvent(
aContent->AsElement(), nsRestyleHint(0), nsChangeHint_ReconstructFrame);
} else {
- rv = ContentInserted(container, aContent, mTempFrameTreeState, false);
+ ContentInserted(container, aContent, mTempFrameTreeState, false);
}
}
}
-
- return rv;
}
void
@@ -11271,7 +11241,7 @@ nsCSSFrameConstructor::AppendFirstLineFrames(
// Special routine to handle inserting a new frame into a block
// frame's child list. Takes care of placing the new frame into the
// right place when first-line style is present.
-nsresult
+void
nsCSSFrameConstructor::InsertFirstLineFrames(
nsFrameConstructorState& aState,
nsIContent* aContent,
@@ -11280,7 +11250,6 @@ nsCSSFrameConstructor::InsertFirstLineFrames(
nsIFrame* aPrevSibling,
nsFrameItems& aFrameItems)
{
- nsresult rv = NS_OK;
// XXXbz If you make this method actually do something, check to
// make sure that the caller is passing what you expect. In
// particular, which content is aContent? And audit the rest of
@@ -11410,7 +11379,6 @@ nsCSSFrameConstructor::InsertFirstLineFrames(
}
#endif
- return rv;
}
//----------------------------------------------------------------------
@@ -11737,7 +11705,7 @@ FindFirstLetterFrame(nsIFrame* aFrame, nsIFrame::ChildListID aListID)
return nullptr;
}
-nsresult
+void
nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames(
nsIPresShell* aPresShell,
nsIFrame* aBlockFrame)
@@ -11749,7 +11717,7 @@ nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames(
floatFrame =
::FindFirstLetterFrame(aBlockFrame, nsIFrame::kPushedFloatsList);
if (!floatFrame) {
- return NS_OK;
+ return;
}
}
@@ -11757,19 +11725,19 @@ nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames(
// destroyed when we destroy the letter frame).
nsIFrame* textFrame = floatFrame->PrincipalChildList().FirstChild();
if (!textFrame) {
- return NS_OK;
+ return;
}
// Discover the placeholder frame for the letter frame
nsPlaceholderFrame* placeholderFrame = GetPlaceholderFrameFor(floatFrame);
if (!placeholderFrame) {
// Somethings really wrong
- return NS_OK;
+ return;
}
nsContainerFrame* parentFrame = placeholderFrame->GetParent();
if (!parentFrame) {
// Somethings really wrong
- return NS_OK;
+ return;
}
// Create a new text frame with the right style context that maps
@@ -11778,7 +11746,7 @@ nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames(
nsStyleContext* parentSC = parentFrame->StyleContext();
nsIContent* textContent = textFrame->GetContent();
if (!textContent) {
- return NS_OK;
+ return;
}
RefPtr newSC = aPresShell->StyleSet()->
ResolveStyleForText(textContent, parentSC);
@@ -11823,11 +11791,9 @@ nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames(
if (offsetsNeedFixing) {
prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING);
}
-
- return NS_OK;
}
-nsresult
+void
nsCSSFrameConstructor::RemoveFirstLetterFrames(nsIPresShell* aPresShell,
nsContainerFrame* aFrame,
nsContainerFrame* aBlockFrame,
@@ -11900,11 +11866,9 @@ nsCSSFrameConstructor::RemoveFirstLetterFrames(nsIPresShell* aPresShell,
prevSibling = kid;
kid = kid->GetNextSibling();
}
-
- return NS_OK;
}
-nsresult
+void
nsCSSFrameConstructor::RemoveLetterFrames(nsIPresShell* aPresShell,
nsContainerFrame* aBlockFrame)
{
@@ -11913,20 +11877,16 @@ nsCSSFrameConstructor::RemoveLetterFrames(nsIPresShell* aPresShell,
nsContainerFrame* continuation = aBlockFrame;
bool stopLooking = false;
- nsresult rv;
do {
- rv = RemoveFloatingFirstLetterFrames(aPresShell, continuation);
- if (NS_SUCCEEDED(rv)) {
- rv = RemoveFirstLetterFrames(aPresShell,
- continuation, aBlockFrame, &stopLooking);
- }
+ RemoveFloatingFirstLetterFrames(aPresShell, continuation);
+ RemoveFirstLetterFrames(aPresShell, continuation, aBlockFrame,
+ &stopLooking);
if (stopLooking) {
break;
}
continuation =
static_cast(continuation->GetNextContinuation());
} while (continuation);
- return rv;
}
// Fixup the letter frame situation for the given block
@@ -11969,7 +11929,7 @@ nsCSSFrameConstructor::RecoverLetterFrames(nsContainerFrame* aBlockFrame)
// listbox Widget Routines
-nsresult
+void
nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame,
nsIFrame* aPrevFrame,
nsIContent* aChild,
@@ -11977,8 +11937,6 @@ nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame,
bool aIsAppend)
{
#ifdef MOZ_XUL
- nsresult rv = NS_OK;
-
// Construct a new frame
if (nullptr != aParentFrame) {
nsFrameItems frameItems;
@@ -12002,7 +11960,7 @@ nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame,
if (StyleDisplay::None == display->mDisplay) {
*aNewFrame = nullptr;
- return NS_OK;
+ return;
}
BeginUpdate();
@@ -12021,9 +11979,9 @@ nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame,
if (newFrame) {
// Notify the parent frame
if (aIsAppend)
- rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(frameItems);
+ ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(frameItems);
else
- rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxInsertFrames(aPrevFrame, frameItems);
+ ((nsListBoxBodyFrame*)aParentFrame)->ListBoxInsertFrames(aPrevFrame, frameItems);
}
EndUpdate();
@@ -12038,10 +11996,6 @@ nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame,
}
#endif
}
-
- return rv;
-#else
- return NS_ERROR_FAILURE;
#endif
}
@@ -12815,7 +12769,7 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState,
return true;
}
-nsresult
+void
nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame,
RemoveFlags aFlags,
nsIContent** aDestroyedFramesFor)
@@ -12838,7 +12792,7 @@ nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame,
// don't ReframeContainingBlock, this will result in a crash
// if we remove a tree that's in reflow - see bug 121368 for testcase
NS_ERROR("Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a Reflow!!!");
- return NS_OK;
+ return;
}
// Get the first "normal" ancestor of the target frame.
@@ -12870,7 +12824,7 @@ nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame,
true, aFlags, nullptr);
}
-nsresult
+void
nsCSSFrameConstructor::GenerateChildFrames(nsContainerFrame* aFrame)
{
{
@@ -12905,8 +12859,6 @@ nsCSSFrameConstructor::GenerateChildFrames(nsContainerFrame* aFrame)
// call XBL constructors after the frames are created
mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue();
-
- return NS_OK;
}
//////////////////////////////////////////////////////////
diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h
index 3a6394f002af..9f71e06b0725 100644
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -48,7 +48,7 @@ class FlattenedChildIterator;
} // namespace dom
} // namespace mozilla
-class nsCSSFrameConstructor : public nsFrameManager
+class nsCSSFrameConstructor final : public nsFrameManager
{
public:
typedef mozilla::CSSPseudoElementType CSSPseudoElementType;
@@ -60,7 +60,7 @@ public:
nsCSSFrameConstructor(nsIDocument* aDocument, nsIPresShell* aPresShell);
~nsCSSFrameConstructor(void) {
- NS_ASSERTION(mUpdateCount == 0, "Dying in the middle of our own update?");
+ MOZ_ASSERT(mUpdateCount == 0, "Dying in the middle of our own update?");
}
// get the alternate text for a content node
@@ -78,7 +78,7 @@ public:
nsIFrame* ConstructRootFrame();
- nsresult ReconstructDocElementHierarchy();
+ void ReconstructDocElementHierarchy();
// Create frames for content nodes that are marked as needing frames. This
// should be called before ProcessPendingRestyles.
@@ -208,17 +208,17 @@ public:
// much easier way than nsFrameConstructorState, and thus, we're allowed to
// provide a TreeMatchContext to avoid calling InitAncestors repeatedly deep
// in the DOM.
- nsresult ContentAppended(nsIContent* aContainer,
- nsIContent* aFirstNewContent,
- bool aAllowLazyConstruction,
- TreeMatchContext* aProvidedTreeMatchContext = nullptr);
+ void ContentAppended(nsIContent* aContainer,
+ nsIContent* aFirstNewContent,
+ bool aAllowLazyConstruction,
+ TreeMatchContext* aProvidedTreeMatchContext = nullptr);
// If aAllowLazyConstruction is true then frame construction of the new child
// can be done lazily.
- nsresult ContentInserted(nsIContent* aContainer,
- nsIContent* aChild,
- nsILayoutHistoryState* aFrameState,
- bool aAllowLazyConstruction);
+ void ContentInserted(nsIContent* aContainer,
+ nsIContent* aChild,
+ nsILayoutHistoryState* aFrameState,
+ bool aAllowLazyConstruction);
// Like ContentInserted but handles inserting the children of aContainer in
// the range [aStartChild, aEndChild). aStartChild must be non-null.
@@ -231,12 +231,12 @@ public:
//
// See ContentAppended to see why we allow passing an already initialized
// TreeMatchContext.
- nsresult ContentRangeInserted(nsIContent* aContainer,
- nsIContent* aStartChild,
- nsIContent* aEndChild,
- nsILayoutHistoryState* aFrameState,
- bool aAllowLazyConstruction,
- TreeMatchContext* aProvidedTreeMatchContext = nullptr);
+ void ContentRangeInserted(nsIContent* aContainer,
+ nsIContent* aStartChild,
+ nsIContent* aEndChild,
+ nsILayoutHistoryState* aFrameState,
+ bool aAllowLazyConstruction,
+ TreeMatchContext* aProvidedTreeMatchContext = nullptr);
enum RemoveFlags {
REMOVE_CONTENT, REMOVE_FOR_RECONSTRUCTION, REMOVE_DESTROY_FRAMES };
@@ -256,15 +256,15 @@ public:
* only when aFlags == REMOVE_DESTROY_FRAMES, otherwise it will only be
* captured if we reconstructed frames for an ancestor.
*/
- nsresult ContentRemoved(nsIContent* aContainer,
- nsIContent* aChild,
- nsIContent* aOldNextSibling,
- RemoveFlags aFlags,
- bool* aDidReconstruct,
- nsIContent** aDestroyedFramesFor = nullptr);
+ void ContentRemoved(nsIContent* aContainer,
+ nsIContent* aChild,
+ nsIContent* aOldNextSibling,
+ RemoveFlags aFlags,
+ bool* aDidReconstruct,
+ nsIContent** aDestroyedFramesFor = nullptr);
- nsresult CharacterDataChanged(nsIContent* aContent,
- CharacterDataChangeInfo* aInfo);
+ void CharacterDataChanged(nsIContent* aContent,
+ CharacterDataChangeInfo* aInfo);
// If aContent is a text node that has been optimized away due to being
// whitespace next to a block boundary (or for some other reason), stop
@@ -273,8 +273,8 @@ public:
// Returns the frame for aContent if there is one.
nsIFrame* EnsureFrameForTextNode(nsGenericDOMDataNode* aContent);
- // generate the child frames and process bindings
- nsresult GenerateChildFrames(nsContainerFrame* aFrame);
+ // Generate the child frames and process bindings
+ void GenerateChildFrames(nsContainerFrame* aFrame);
// Should be called when a frame is going to be destroyed and
// WillDestroyFrameTree hasn't been called yet.
@@ -316,11 +316,11 @@ public:
*/
InsertionPoint GetInsertionPoint(nsIContent* aContainer, nsIContent* aChild);
- nsresult CreateListBoxContent(nsContainerFrame* aParentFrame,
- nsIFrame* aPrevFrame,
- nsIContent* aChild,
- nsIFrame** aResult,
- bool aIsAppend);
+ void CreateListBoxContent(nsContainerFrame* aParentFrame,
+ nsIFrame* aPrevFrame,
+ nsIContent* aChild,
+ nsIFrame** aResult,
+ bool aIsAppend);
// GetInitialContainingBlock() is deprecated in favor of GetRootElementFrame();
// nsIFrame* GetInitialContainingBlock() { return mRootElementFrame; }
@@ -432,14 +432,14 @@ private:
* @param [out] aNewContent the content node we create
* @param [out] aNewFrame the new frame we create
*/
- nsresult CreateAttributeContent(nsIContent* aParentContent,
- nsIFrame* aParentFrame,
- int32_t aAttrNamespace,
- nsIAtom* aAttrName,
- nsStyleContext* aStyleContext,
- nsCOMArray& aGeneratedContent,
- nsIContent** aNewContent,
- nsIFrame** aNewFrame);
+ void CreateAttributeContent(nsIContent* aParentContent,
+ nsIFrame* aParentFrame,
+ int32_t aAttrNamespace,
+ nsIAtom* aAttrName,
+ nsStyleContext* aStyleContext,
+ nsCOMArray& aGeneratedContent,
+ nsIContent** aNewContent,
+ nsIFrame** aNewFrame);
/**
* Create a text node containing the given string. If aText is non-null
@@ -477,11 +477,11 @@ private:
// aParentFrame. aPrevSibling must be the frame after which aFrameList is to
// be placed on aParentFrame's principal child list. It may be null if
// aFrameList is being added at the beginning of the child list.
- nsresult AppendFramesToParent(nsFrameConstructorState& aState,
- nsContainerFrame* aParentFrame,
- nsFrameItems& aFrameList,
- nsIFrame* aPrevSibling,
- bool aIsRecursiveCall = false);
+ void AppendFramesToParent(nsFrameConstructorState& aState,
+ nsContainerFrame* aParentFrame,
+ nsFrameItems& aFrameList,
+ nsIFrame* aPrevSibling,
+ bool aIsRecursiveCall = false);
// BEGIN TABLE SECTION
/**
@@ -1703,7 +1703,7 @@ private:
// InitializeSelectFrame puts scrollFrame in aFrameItems if aBuildCombobox is false
// aBuildCombobox indicates if we are building a combobox that has a dropdown
// popup widget or not.
- nsresult
+ void
InitializeSelectFrame(nsFrameConstructorState& aState,
nsContainerFrame* aScrollFrame,
nsContainerFrame* aScrolledFrame,
@@ -1732,7 +1732,7 @@ private:
* @param aDestroyedFramesFor if non-null, it will contain the content that
* was actually reframed - it may be different than aContent.
*/
- nsresult
+ void
RecreateFramesForContent(nsIContent* aContent,
bool aAsyncInsert,
RemoveFlags aFlags,
@@ -1750,7 +1750,6 @@ private:
// content that was reframed.
bool MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
RemoveFlags aFlags,
- nsresult* aResult,
nsIContent** aDestroyedFramesFor);
nsIFrame* CreateContinuingOuterTableFrame(nsIPresShell* aPresShell,
@@ -1875,9 +1874,9 @@ private:
bool aIsAppend,
nsIFrame* aPrevSibling);
- nsresult ReframeContainingBlock(nsIFrame* aFrame,
- RemoveFlags aFlags,
- nsIContent** aReframeContent);
+ void ReframeContainingBlock(nsIFrame* aFrame,
+ RemoveFlags aFlags,
+ nsIContent** aReframeContent);
//----------------------------------------
@@ -1931,18 +1930,18 @@ private:
void RecoverLetterFrames(nsContainerFrame* aBlockFrame);
//
- nsresult RemoveLetterFrames(nsIPresShell* aPresShell,
- nsContainerFrame* aBlockFrame);
+ void RemoveLetterFrames(nsIPresShell* aPresShell,
+ nsContainerFrame* aBlockFrame);
// Recursive helper for RemoveLetterFrames
- nsresult RemoveFirstLetterFrames(nsIPresShell* aPresShell,
- nsContainerFrame* aFrame,
- nsContainerFrame* aBlockFrame,
- bool* aStopLooking);
+ void RemoveFirstLetterFrames(nsIPresShell* aPresShell,
+ nsContainerFrame* aFrame,
+ nsContainerFrame* aBlockFrame,
+ bool* aStopLooking);
// Special remove method for those pesky floating first-letter frames
- nsresult RemoveFloatingFirstLetterFrames(nsIPresShell* aPresShell,
- nsIFrame* aBlockFrame);
+ void RemoveFloatingFirstLetterFrames(nsIPresShell* aPresShell,
+ nsIFrame* aBlockFrame);
// Capture state for the frame tree rooted at the frame associated with the
// content object, aContent
@@ -1973,12 +1972,12 @@ private:
nsContainerFrame* aBlockFrame,
nsFrameItems& aFrameItems);
- nsresult InsertFirstLineFrames(nsFrameConstructorState& aState,
- nsIContent* aContent,
- nsIFrame* aBlockFrame,
- nsContainerFrame** aParentFrame,
- nsIFrame* aPrevSibling,
- nsFrameItems& aFrameItems);
+ void InsertFirstLineFrames(nsFrameConstructorState& aState,
+ nsIContent* aContent,
+ nsIFrame* aBlockFrame,
+ nsContainerFrame** aParentFrame,
+ nsIFrame* aPrevSibling,
+ nsFrameItems& aFrameItems);
/**
* Find the right frame to use for aContent when looking for sibling
diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp
index 18b2e7daa91d..5b363899116d 100644
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -2294,8 +2294,8 @@ nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument)
nsAutoString sheets;
elt->GetAttribute(NS_LITERAL_STRING("usechromesheets"), sheets);
if (!sheets.IsEmpty() && baseURI) {
- RefPtr cssLoader =
- new mozilla::css::Loader(backendType);
+ RefPtr cssLoader =
+ new css::Loader(backendType, aDocument->GetDocGroup());
char *str = ToNewCString(sheets);
char *newStr = str;
diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp
index 79c43f99ae7a..210924d7dedb 100644
--- a/layout/base/nsFrameManager.cpp
+++ b/layout/base/nsFrameManager.cpp
@@ -38,13 +38,8 @@
#include "nsIStatefulFrame.h"
#include "nsContainerFrame.h"
- #ifdef DEBUG
- //#define DEBUG_UNDISPLAYED_MAP
- //#define DEBUG_DISPLAY_CONTENTS_MAP
- #else
- #undef DEBUG_UNDISPLAYED_MAP
- #undef DEBUG_DISPLAY_CONTENTS_MAP
- #endif
+// #define DEBUG_UNDISPLAYED_MAP
+// #define DEBUG_DISPLAY_CONTENTS_MAP
using namespace mozilla;
using namespace mozilla::dom;
@@ -87,40 +82,49 @@ nsFrameManagerBase::nsFrameManagerBase()
//----------------------------------------------------------------------
-// XXXldb This seems too complicated for what I think it's doing, and it
-// should also be using PLDHashTable rather than plhash to use less memory.
+/**
+ * The undisplayed map is a class that maps a parent content node to the
+ * undisplayed content children, and their style contexts.
+ *
+ * The linked list of nodes holds strong references to the style contexts and
+ * the content.
+ */
+class nsFrameManagerBase::UndisplayedMap :
+ private nsClassHashtable,
+ LinkedList>
+{
+ typedef nsClassHashtable, LinkedList> base_type;
-class nsFrameManagerBase::UndisplayedMap {
public:
- explicit UndisplayedMap(uint32_t aNumBuckets = 16);
- ~UndisplayedMap(void);
+ UndisplayedMap();
+ ~UndisplayedMap();
UndisplayedNode* GetFirstNode(nsIContent* aParentContent);
- nsresult AddNodeFor(nsIContent* aParentContent,
- nsIContent* aChild, nsStyleContext* aStyle);
+ void AddNodeFor(nsIContent* aParentContent,
+ nsIContent* aChild,
+ nsStyleContext* aStyle);
- void RemoveNodeFor(nsIContent* aParentContent,
- UndisplayedNode* aNode);
+ void RemoveNodeFor(nsIContent* aParentContent, UndisplayedNode* aNode);
void RemoveNodesFor(nsIContent* aParentContent);
- UndisplayedNode* UnlinkNodesFor(nsIContent* aParentContent);
+
+ nsAutoPtr>
+ UnlinkNodesFor(nsIContent* aParentContent);
// Removes all entries from the hash table
- void Clear(void);
+ void Clear();
protected:
+ LinkedList* GetListFor(nsIContent** aParentContent);
+ LinkedList* GetOrCreateListFor(nsIContent** aParentContent);
+ void AppendNodeFor(UndisplayedNode* aNode, nsIContent* aParentContent);
/**
- * Gets the entry for the provided parent content. If the content
- * is a element, |**aParentContent| is set to
- * the parent of the children element.
+ * Get the applicable parent for the map lookup. This is almost always the
+ * provided argument, except if it's a element, in which case
+ * it's the parent of the children element.
*/
- PLHashEntry** GetEntryFor(nsIContent** aParentContent);
- void AppendNodeFor(UndisplayedNode* aNode,
- nsIContent* aParentContent);
-
- PLHashTable* mTable;
- PLHashEntry** mLastLookup;
+ nsIContent* GetApplicableParent(nsIContent* aParent);
};
//----------------------------------------------------------------------
@@ -145,7 +149,7 @@ nsFrameManager::Destroy()
mRootFrame->Destroy();
mRootFrame = nullptr;
}
-
+
delete mUndisplayedMap;
mUndisplayedMap = nullptr;
delete mDisplayContentsMap;
@@ -234,7 +238,7 @@ nsFrameManager::GetUndisplayedNodeInMapFor(UndisplayedMap* aMap,
}
nsIContent* parent = ParentForUndisplayedMap(aContent);
for (UndisplayedNode* node = aMap->GetFirstNode(parent);
- node; node = node->mNext) {
+ node; node = node->getNext()) {
if (node->mContent == aContent)
return node;
}
@@ -261,16 +265,16 @@ nsFrameManager::SetStyleContextInMap(UndisplayedMap* aMap,
nsIContent* aContent,
nsStyleContext* aStyleContext)
{
- NS_PRECONDITION(!aStyleContext->GetPseudo(),
- "Should only have actual elements here");
+ MOZ_ASSERT(!aStyleContext->GetPseudo(),
+ "Should only have actual elements here");
#if defined(DEBUG_UNDISPLAYED_MAP) || defined(DEBUG_DISPLAY_BOX_CONTENTS_MAP)
static int i = 0;
printf("SetStyleContextInMap(%d): p=%p \n", i++, (void *)aContent);
#endif
- NS_ASSERTION(!GetStyleContextInMap(aMap, aContent),
- "Already have an entry for aContent");
+ MOZ_ASSERT(!GetStyleContextInMap(aMap, aContent),
+ "Already have an entry for aContent");
nsIContent* parent = ParentForUndisplayedMap(aContent);
#ifdef DEBUG
@@ -306,7 +310,7 @@ nsFrameManager::ChangeStyleContextInMap(UndisplayedMap* aMap,
#endif
for (UndisplayedNode* node = aMap->GetFirstNode(aContent->GetParent());
- node; node = node->mNext) {
+ node; node = node->getNext()) {
if (node->mContent == aContent) {
node->mStyle = aStyleContext;
return;
@@ -324,26 +328,26 @@ nsFrameManager::ClearUndisplayedContentIn(nsIContent* aContent,
static int i = 0;
printf("ClearUndisplayedContent(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent);
#endif
-
- if (mUndisplayedMap) {
- UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent);
- while (node) {
- if (node->mContent == aContent) {
- mUndisplayedMap->RemoveNodeFor(aParentContent, node);
+
+ if (!mUndisplayedMap) {
+ return;
+ }
+
+ for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent);
+ node; node = node->getNext()) {
+ if (node->mContent == aContent) {
+ mUndisplayedMap->RemoveNodeFor(aParentContent, node);
#ifdef DEBUG_UNDISPLAYED_MAP
- printf( "REMOVED!\n");
+ printf( "REMOVED!\n");
#endif
-#ifdef DEBUG
- // make sure that there are no more entries for the same content
- nsStyleContext *context = GetUndisplayedContent(aContent);
- NS_ASSERTION(context == nullptr, "Found more undisplayed content data after removal");
-#endif
- return;
- }
- node = node->mNext;
+ // make sure that there are no more entries for the same content
+ MOZ_ASSERT(!GetUndisplayedContent(aContent),
+ "Found more undisplayed content data after removal");
+ return;
}
}
+
#ifdef DEBUG_UNDISPLAYED_MAP
printf( "not found.\n");
#endif
@@ -399,26 +403,25 @@ nsFrameManager::ClearDisplayContentsIn(nsIContent* aContent,
static int i = 0;
printf("ClearDisplayContents(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent);
#endif
-
- if (mDisplayContentsMap) {
- UndisplayedNode* node = mDisplayContentsMap->GetFirstNode(aParentContent);
- while (node) {
- if (node->mContent == aContent) {
- mDisplayContentsMap->RemoveNodeFor(aParentContent, node);
+
+ if (!mDisplayContentsMap) {
+ return;
+ }
+
+ for (UndisplayedNode* node = mDisplayContentsMap->GetFirstNode(aParentContent);
+ node; node = node->getNext()) {
+ if (node->mContent == aContent) {
+ mDisplayContentsMap->RemoveNodeFor(aParentContent, node);
#ifdef DEBUG_DISPLAY_CONTENTS_MAP
- printf( "REMOVED!\n");
+ printf( "REMOVED!\n");
#endif
-#ifdef DEBUG
- // make sure that there are no more entries for the same content
- nsStyleContext* context = GetDisplayContentsStyleFor(aContent);
- NS_ASSERTION(context == nullptr, "Found more entries for aContent after removal");
-#endif
- ClearAllDisplayContentsIn(aContent);
- ClearAllUndisplayedContentIn(aContent);
- return;
- }
- node = node->mNext;
+ // make sure that there are no more entries for the same content
+ MOZ_ASSERT(!GetDisplayContentsStyleFor(aContent),
+ "Found more entries for aContent after removal");
+ ClearAllDisplayContentsIn(aContent);
+ ClearAllUndisplayedContentIn(aContent);
+ return;
}
}
#ifdef DEBUG_DISPLAY_CONTENTS_MAP
@@ -435,14 +438,14 @@ nsFrameManager::ClearAllDisplayContentsIn(nsIContent* aParentContent)
#endif
if (mDisplayContentsMap) {
- UndisplayedNode* cur = mDisplayContentsMap->UnlinkNodesFor(aParentContent);
- while (cur) {
- UndisplayedNode* next = cur->mNext;
- cur->mNext = nullptr;
- ClearAllDisplayContentsIn(cur->mContent);
- ClearAllUndisplayedContentIn(cur->mContent);
- delete cur;
- cur = next;
+ nsAutoPtr> list =
+ mDisplayContentsMap->UnlinkNodesFor(aParentContent);
+ if (list) {
+ while (UndisplayedNode* node = list->popFirst()) {
+ ClearAllDisplayContentsIn(node->mContent);
+ ClearAllUndisplayedContentIn(node->mContent);
+ delete node;
+ }
}
}
@@ -673,180 +676,132 @@ nsFrameManager::RestoreFrameState(nsIFrame* aFrame,
//----------------------------------------------------------------------
-static PLHashNumber
-HashKey(void* key)
-{
- return NS_PTR_TO_INT32(key);
-}
-
-static int
-CompareKeys(void* key1, void* key2)
-{
- return key1 == key2;
-}
-
-//----------------------------------------------------------------------
-
-nsFrameManagerBase::UndisplayedMap::UndisplayedMap(uint32_t aNumBuckets)
+nsFrameManagerBase::UndisplayedMap::UndisplayedMap()
{
MOZ_COUNT_CTOR(nsFrameManagerBase::UndisplayedMap);
- mTable = PL_NewHashTable(aNumBuckets, (PLHashFunction)HashKey,
- (PLHashComparator)CompareKeys,
- (PLHashComparator)nullptr,
- nullptr, nullptr);
- mLastLookup = nullptr;
}
nsFrameManagerBase::UndisplayedMap::~UndisplayedMap(void)
{
MOZ_COUNT_DTOR(nsFrameManagerBase::UndisplayedMap);
Clear();
- PL_HashTableDestroy(mTable);
}
-PLHashEntry**
-nsFrameManagerBase::UndisplayedMap::GetEntryFor(nsIContent** aParentContent)
+void
+nsFrameManagerBase::UndisplayedMap::Clear()
{
- nsIContent* parentContent = *aParentContent;
-
- if (mLastLookup && (parentContent == (*mLastLookup)->key)) {
- return mLastLookup;
+ for (auto iter = Iter(); !iter.Done(); iter.Next()) {
+ auto* list = iter.UserData();
+ while (auto* node = list->popFirst()) {
+ delete node;
+ }
+ iter.Remove();
}
+}
+
+nsIContent*
+nsFrameManagerBase::UndisplayedMap::GetApplicableParent(nsIContent* aParent)
+{
// In the case of XBL default content, elements do not get a
// frame causing a mismatch between the content tree and the frame tree.
// |GetEntryFor| is sometimes called with the content tree parent (which may
// be a element) but the parent in the frame tree would be the
// insertion parent (parent of the element). Here the children
// elements are normalized to the insertion parent to correct for the mismatch.
- if (parentContent && nsContentUtils::IsContentInsertionPoint(parentContent)) {
- parentContent = parentContent->GetParent();
- // Change the caller's pointer for the parent content to be the insertion parent.
- *aParentContent = parentContent;
+ if (aParent && nsContentUtils::IsContentInsertionPoint(aParent)) {
+ return aParent->GetParent();
}
- PLHashNumber hashCode = NS_PTR_TO_INT32(parentContent);
- PLHashEntry** entry = PL_HashTableRawLookup(mTable, hashCode, parentContent);
- if (*entry && !ServoStyleSet::IsInServoTraversal()) {
- mLastLookup = entry;
- }
- return entry;
+ return aParent;
}
-UndisplayedNode*
-nsFrameManagerBase::UndisplayedMap::GetFirstNode(nsIContent* aParentContent)
+LinkedList*
+nsFrameManagerBase::UndisplayedMap::GetListFor(nsIContent** aParent)
{
- PLHashEntry** entry = GetEntryFor(&aParentContent);
- if (*entry) {
- return (UndisplayedNode*)((*entry)->value);
+ *aParent = GetApplicableParent(*aParent);
+
+ LinkedList* list;
+ if (Get(*aParent, &list)) {
+ return list;
}
+
return nullptr;
}
+LinkedList*
+nsFrameManagerBase::UndisplayedMap::GetOrCreateListFor(nsIContent** aParent)
+{
+ *aParent = GetApplicableParent(*aParent);
+ return LookupOrAdd(*aParent);
+}
+
+
+UndisplayedNode*
+nsFrameManagerBase::UndisplayedMap::GetFirstNode(nsIContent* aParentContent)
+{
+ auto* list = GetListFor(&aParentContent);
+ return list ? list->getFirst() : nullptr;
+}
+
+
void
nsFrameManagerBase::UndisplayedMap::AppendNodeFor(UndisplayedNode* aNode,
nsIContent* aParentContent)
{
- PLHashEntry** entry = GetEntryFor(&aParentContent);
- if (*entry) {
- UndisplayedNode* node = (UndisplayedNode*)((*entry)->value);
- while (node->mNext) {
- if (node->mContent == aNode->mContent) {
- // We actually need to check this in optimized builds because
- // there are some callers that do this. See bug 118014, bug
- // 136704, etc.
- NS_NOTREACHED("node in map twice");
- delete aNode;
- return;
- }
- node = node->mNext;
- }
- node->mNext = aNode;
- }
- else {
- PLHashNumber hashCode = NS_PTR_TO_INT32(aParentContent);
- PL_HashTableRawAdd(mTable, entry, hashCode, aParentContent, aNode);
- mLastLookup = nullptr; // hashtable may have shifted bucket out from under us
+ LinkedList* list = GetOrCreateListFor(&aParentContent);
+
+#ifdef DEBUG
+ for (UndisplayedNode* node = list->getFirst(); node; node = node->getNext()) {
+ // NOTE: In the original code there was a work around for this case, I want
+ // to check it still happens before hacking around it the same way.
+ MOZ_ASSERT(node->mContent != aNode->mContent,
+ "Duplicated content in undisplayed list!");
}
+#endif
+
+ list->insertBack(aNode);
}
-nsresult
+void
nsFrameManagerBase::UndisplayedMap::AddNodeFor(nsIContent* aParentContent,
- nsIContent* aChild,
+ nsIContent* aChild,
nsStyleContext* aStyle)
{
UndisplayedNode* node = new UndisplayedNode(aChild, aStyle);
-
AppendNodeFor(node, aParentContent);
- return NS_OK;
}
void
nsFrameManagerBase::UndisplayedMap::RemoveNodeFor(nsIContent* aParentContent,
UndisplayedNode* aNode)
{
- PLHashEntry** entry = GetEntryFor(&aParentContent);
- NS_ASSERTION(*entry, "content not in map");
- if (*entry) {
- if ((UndisplayedNode*)((*entry)->value) == aNode) { // first node
- if (aNode->mNext) {
- (*entry)->value = aNode->mNext;
- aNode->mNext = nullptr;
- }
- else {
- PL_HashTableRawRemove(mTable, entry, *entry);
- mLastLookup = nullptr; // hashtable may have shifted bucket out from under us
- }
- }
- else {
- UndisplayedNode* node = (UndisplayedNode*)((*entry)->value);
- while (node->mNext) {
- if (node->mNext == aNode) {
- node->mNext = aNode->mNext;
- aNode->mNext = nullptr;
- break;
- }
- node = node->mNext;
- }
- }
- }
+#ifdef DEBUG
+ auto list = GetListFor(&aParentContent);
+ MOZ_ASSERT(list, "content not in map");
+ aNode->removeFrom(*list);
+#else
+ aNode->remove();
+#endif
delete aNode;
}
-UndisplayedNode*
+nsAutoPtr>
nsFrameManagerBase::UndisplayedMap::UnlinkNodesFor(nsIContent* aParentContent)
{
- PLHashEntry** entry = GetEntryFor(&aParentContent);
- NS_ASSERTION(entry, "content not in map");
- if (*entry) {
- UndisplayedNode* node = (UndisplayedNode*)((*entry)->value);
- NS_ASSERTION(node, "null node for non-null entry in UndisplayedMap");
- PL_HashTableRawRemove(mTable, entry, *entry);
- mLastLookup = nullptr; // hashtable may have shifted bucket out from under us
- return node;
- }
- return nullptr;
+ nsAutoPtr> list;
+ RemoveAndForget(GetApplicableParent(aParentContent), list);
+ return list;
}
void
nsFrameManagerBase::UndisplayedMap::RemoveNodesFor(nsIContent* aParentContent)
{
- delete UnlinkNodesFor(aParentContent);
-}
-
-static int
-RemoveUndisplayedEntry(PLHashEntry* he, int i, void* arg)
-{
- UndisplayedNode* node = (UndisplayedNode*)(he->value);
- delete node;
- // Remove and free this entry and continue enumerating
- return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT;
-}
-
-void
-nsFrameManagerBase::UndisplayedMap::Clear(void)
-{
- mLastLookup = nullptr;
- PL_HashTableEnumerateEntries(mTable, RemoveUndisplayedEntry, 0);
+ nsAutoPtr> list = UnlinkNodesFor(aParentContent);
+ if (list) {
+ while (auto* node = list->popFirst()) {
+ delete node;
+ }
+ }
}
diff --git a/layout/base/nsFrameManager.h b/layout/base/nsFrameManager.h
index 1086e331b28a..0d9184734640 100644
--- a/layout/base/nsFrameManager.h
+++ b/layout/base/nsFrameManager.h
@@ -33,32 +33,19 @@ namespace mozilla {
* Node in a linked list, containing the style for an element that
* does not have a frame but whose parent does have a frame.
*/
-struct UndisplayedNode {
+struct UndisplayedNode : public LinkedListElement
+{
UndisplayedNode(nsIContent* aContent, nsStyleContext* aStyle)
- : mContent(aContent),
- mStyle(aStyle),
- mNext(nullptr)
+ : mContent(aContent)
+ , mStyle(aStyle)
{
MOZ_COUNT_CTOR(mozilla::UndisplayedNode);
}
- ~UndisplayedNode()
- {
- MOZ_COUNT_DTOR(mozilla::UndisplayedNode);
+ ~UndisplayedNode() { MOZ_COUNT_DTOR(mozilla::UndisplayedNode); }
- // Delete mNext iteratively to avoid blowing up the stack (bug 460461).
- UndisplayedNode* cur = mNext;
- while (cur) {
- UndisplayedNode* next = cur->mNext;
- cur->mNext = nullptr;
- delete cur;
- cur = next;
- }
- }
-
- nsCOMPtr mContent;
- RefPtr mStyle;
- UndisplayedNode* mNext;
+ nsCOMPtr mContent;
+ RefPtr mStyle;
};
} // namespace mozilla
@@ -75,7 +62,6 @@ struct UndisplayedNode {
* else you'll break the validity of the reinterpret_cast in nsIPresShell's
* FrameManager() method.
*/
-
class nsFrameManager : public nsFrameManagerBase
{
typedef mozilla::layout::FrameChildListID ChildListID;
@@ -99,7 +85,7 @@ public:
void RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame);
void UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame);
- void ClearPlaceholderFrameMap();
+ void ClearPlaceholderFrameMap();
// Mapping undisplayed content
nsStyleContext* GetUndisplayedContent(const nsIContent* aContent)
@@ -156,8 +142,7 @@ public:
/**
* Register aContent having a display:contents style context.
*/
- void SetDisplayContents(nsIContent* aContent,
- nsStyleContext* aStyleContext);
+ void SetDisplayContents(nsIContent* aContent, nsStyleContext* aStyleContext);
/**
* Change the registered style context for aContent to aStyleContext.
*/
@@ -172,28 +157,26 @@ public:
* If found, then also unregister any display:contents and display:none
* style contexts for its descendants.
*/
- void ClearDisplayContentsIn(nsIContent* aContent,
- nsIContent* aParentContent);
+ void ClearDisplayContentsIn(nsIContent* aContent, nsIContent* aParentContent);
void ClearAllDisplayContentsIn(nsIContent* aParentContent);
// Functions for manipulating the frame model
void AppendFrames(nsContainerFrame* aParentFrame,
- ChildListID aListID,
- nsFrameList& aFrameList);
+ ChildListID aListID,
+ nsFrameList& aFrameList);
void InsertFrames(nsContainerFrame* aParentFrame,
- ChildListID aListID,
- nsIFrame* aPrevFrame,
- nsFrameList& aFrameList);
+ ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList);
- void RemoveFrame(ChildListID aListID,
- nsIFrame* aOldFrame);
+ void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame);
/*
* Notification that a frame is about to be destroyed. This allows any
* outstanding references to the frame to be cleaned up.
*/
- void NotifyDestroyingFrame(nsIFrame* aFrame);
+ void NotifyDestroyingFrame(nsIFrame* aFrame);
/*
* Capture/restore frame state for the frame subtree rooted at aFrame.
@@ -204,20 +187,17 @@ public:
* of aFrame.
*/
- void CaptureFrameState(nsIFrame* aFrame,
- nsILayoutHistoryState* aState);
+ void CaptureFrameState(nsIFrame* aFrame, nsILayoutHistoryState* aState);
- void RestoreFrameState(nsIFrame* aFrame,
- nsILayoutHistoryState* aState);
+ void RestoreFrameState(nsIFrame* aFrame, nsILayoutHistoryState* aState);
/*
* Add/restore state for one frame
*/
- void CaptureFrameStateFor(nsIFrame* aFrame,
- nsILayoutHistoryState* aState);
+ void CaptureFrameStateFor(nsIFrame* aFrame, nsILayoutHistoryState* aState);
+
+ void RestoreFrameStateFor(nsIFrame* aFrame, nsILayoutHistoryState* aState);
- void RestoreFrameStateFor(nsIFrame* aFrame,
- nsILayoutHistoryState* aState);
protected:
static nsStyleContext* GetStyleContextInMap(UndisplayedMap* aMap,
const nsIContent* aContent);
diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h
index 087c04547919..daaad818e3de 100644
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1022,7 +1022,7 @@ public:
/**
* Reconstruct frames for all elements in the document
*/
- virtual nsresult ReconstructFrames() = 0;
+ virtual void ReconstructFrames() = 0;
/**
* Notify that a content node's state has changed
diff --git a/layout/base/nsStyleSheetService.cpp b/layout/base/nsStyleSheetService.cpp
index d75c330069e0..dc6548ed8ce0 100644
--- a/layout/base/nsStyleSheetService.cpp
+++ b/layout/base/nsStyleSheetService.cpp
@@ -218,7 +218,7 @@ LoadSheet(nsIURI* aURI,
StyleBackendType aType,
RefPtr* aResult)
{
- RefPtr loader = new css::Loader(aType);
+ RefPtr loader = new css::Loader(aType, nullptr);
return loader->LoadSheetSync(aURI, aParsingMode, true, aResult);
}
diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp
index 0622b5d818d4..4e10c617b805 100644
--- a/layout/forms/nsListControlFrame.cpp
+++ b/layout/forms/nsListControlFrame.cpp
@@ -93,6 +93,7 @@ NS_IMPL_FRAMEARENA_HELPERS(nsListControlFrame)
//---------------------------------------------------------
nsListControlFrame::nsListControlFrame(nsStyleContext* aContext)
: nsHTMLScrollFrame(aContext, false),
+ mView(nullptr),
mMightNeedSecondPass(false),
mHasPendingInterruptAtStartOfReflow(false),
mDropdownCanGrow(false),
@@ -975,6 +976,11 @@ nsListControlFrame::Init(nsIContent* aContent,
{
nsHTMLScrollFrame::Init(aContent, aParent, aPrevInFlow);
+ if (IsInDropDownMode()) {
+ AddStateBits(NS_FRAME_IN_POPUP);
+ CreateView();
+ }
+
// we shouldn't have to unregister this listener because when
// our frame goes away all these content node go away as well
// because our frame is the only one who references them.
@@ -996,10 +1002,6 @@ nsListControlFrame::Init(nsIContent* aContent,
mEndSelectionIndex = kNothingSelected;
mLastDropdownBackstopColor = PresContext()->DefaultBackgroundColor();
-
- if (IsInDropDownMode()) {
- AddStateBits(NS_FRAME_IN_POPUP);
- }
}
dom::HTMLOptionsCollection*
diff --git a/layout/forms/nsListControlFrame.h b/layout/forms/nsListControlFrame.h
index 152130ea4ff2..e4e177b322e6 100644
--- a/layout/forms/nsListControlFrame.h
+++ b/layout/forms/nsListControlFrame.h
@@ -239,11 +239,6 @@ public:
*/
bool GetDropdownCanGrow() const { return mDropdownCanGrow; }
- /**
- * Dropdowns need views
- */
- virtual bool NeedsView() override { return IsInDropDownMode(); }
-
/**
* Frees statics owned by this class.
*/
@@ -398,16 +393,23 @@ protected:
*/
uint32_t GetNumberOfRows();
+ nsView* GetViewInternal() const override { return mView; }
+ void SetViewInternal(nsView* aView) override { mView = aView; }
+
// Data Members
int32_t mStartSelectionIndex;
int32_t mEndSelectionIndex;
- nsIComboboxControlFrame *mComboboxFrame;
- uint32_t mNumDisplayRows;
+ nsIComboboxControlFrame* mComboboxFrame;
+
+ // The view is only created (& non-null) if IsInDropDownMode() is true.
+ nsView* mView;
+
+ uint32_t mNumDisplayRows;
bool mChangesSinceDragStart:1;
bool mButtonDown:1;
- // Has the user selected a visible item since we showed the
- // dropdown?
+
+ // Has the user selected a visible item since we showed the dropdown?
bool mItemSelectionStarted:1;
bool mIsAllContentHere:1;
diff --git a/layout/generic/Selection.h b/layout/generic/Selection.h
index ef3ce0fda57d..bb3e3b6e49f9 100644
--- a/layout/generic/Selection.h
+++ b/layout/generic/Selection.h
@@ -101,12 +101,15 @@ public:
enum {
SCROLL_SYNCHRONOUS = 1<<1,
SCROLL_FIRST_ANCESTOR_ONLY = 1<<2,
- SCROLL_DO_FLUSH = 1<<3,
+ SCROLL_DO_FLUSH = 1<<3, // only matters if SCROLL_SYNCHRONOUS is passed too
SCROLL_OVERFLOW_HIDDEN = 1<<5,
SCROLL_FOR_CARET_MOVE = 1<<6
};
- // aDoFlush only matters if aIsSynchronous is true. If not, we'll just flush
- // when the scroll event fires so we make sure to scroll to the right place.
+ // If aFlags doesn't contain SCROLL_SYNCHRONOUS, then we'll flush when
+ // the scroll event fires so we make sure to scroll to the right place.
+ // Otherwise, if SCROLL_DO_FLUSH is also in aFlags, then this method will
+ // flush layout and you MUST hold a strong ref on 'this' for the duration
+ // of this call. This might destroy arbitrary layout objects.
nsresult ScrollIntoView(SelectionRegion aRegion,
nsIPresShell::ScrollAxis aVertical =
nsIPresShell::ScrollAxis(),
diff --git a/layout/generic/ViewportFrame.cpp b/layout/generic/ViewportFrame.cpp
index a8638621f96e..f370de4c1fa6 100644
--- a/layout/generic/ViewportFrame.cpp
+++ b/layout/generic/ViewportFrame.cpp
@@ -39,6 +39,8 @@ ViewportFrame::Init(nsIContent* aContent,
nsIFrame* aPrevInFlow)
{
nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
+ // No need to call CreateView() here - the frame ctor will call SetView()
+ // with the ViewManager's root view, so we'll assign it in SetViewInternal().
nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(this);
if (parent) {
diff --git a/layout/generic/ViewportFrame.h b/layout/generic/ViewportFrame.h
index cf650b6e418f..a0c39a27e11a 100644
--- a/layout/generic/ViewportFrame.h
+++ b/layout/generic/ViewportFrame.h
@@ -31,6 +31,7 @@ public:
explicit ViewportFrame(nsStyleContext* aContext)
: nsContainerFrame(aContext)
+ , mView(nullptr)
{}
virtual ~ViewportFrame() { } // useful for debugging
@@ -83,9 +84,6 @@ public:
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
-private:
- virtual mozilla::layout::FrameChildListID GetAbsoluteListID() const override { return kFixedList; }
-
protected:
/**
* Calculate how much room is available for fixed frames. That means
@@ -95,6 +93,14 @@ protected:
* @return the current scroll position, or 0,0 if not scrollable
*/
nsPoint AdjustReflowInputForScrollbars(ReflowInput* aReflowInput) const;
+
+ nsView* GetViewInternal() const override { return mView; }
+ void SetViewInternal(nsView* aView) override { mView = aView; }
+
+private:
+ virtual mozilla::layout::FrameChildListID GetAbsoluteListID() const override { return kFixedList; }
+
+ nsView* mView;
};
} // namespace mozilla
diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp
index aeef0de0bd44..be579c802189 100644
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -377,92 +377,6 @@ nsContainerFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset,
/////////////////////////////////////////////////////////////////////////////
// Helper member functions
-static void
-ReparentFrameViewTo(nsIFrame* aFrame,
- nsViewManager* aViewManager,
- nsView* aNewParentView,
- nsView* aOldParentView)
-{
- if (aFrame->HasView()) {
-#ifdef MOZ_XUL
- if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) {
- // This view must be parented by the root view, don't reparent it.
- return;
- }
-#endif
- nsView* view = aFrame->GetView();
- // Verify that the current parent view is what we think it is
- //nsView* parentView;
- //NS_ASSERTION(parentView == aOldParentView, "unexpected parent view");
-
- aViewManager->RemoveChild(view);
-
- // The view will remember the Z-order and other attributes that have been set on it.
- nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, aFrame);
- aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nullptr);
- } else if (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW) {
- nsIFrame::ChildListIterator lists(aFrame);
- for (; !lists.IsDone(); lists.Next()) {
- // Iterate the child frames, and check each child frame to see if it has
- // a view
- nsFrameList::Enumerator childFrames(lists.CurrentList());
- for (; !childFrames.AtEnd(); childFrames.Next()) {
- ReparentFrameViewTo(childFrames.get(), aViewManager,
- aNewParentView, aOldParentView);
- }
- }
- }
-}
-
-void
-nsContainerFrame::CreateViewForFrame(nsIFrame* aFrame,
- bool aForce)
-{
- if (aFrame->HasView()) {
- return;
- }
-
- // If we don't yet have a view, see if we need a view
- if (!aForce && !aFrame->NeedsView()) {
- // don't need a view
- return;
- }
-
- nsView* parentView = aFrame->GetParent()->GetClosestView();
- NS_ASSERTION(parentView, "no parent with view");
-
- nsViewManager* viewManager = parentView->GetViewManager();
- NS_ASSERTION(viewManager, "null view manager");
-
- // Create a view
- nsView* view = viewManager->CreateView(aFrame->GetRect(), parentView);
-
- SyncFrameViewProperties(aFrame->PresContext(), aFrame, nullptr, view);
-
- nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, aFrame);
- // we insert this view 'above' the insertBefore view, unless insertBefore is null,
- // in which case we want to call with aAbove == false to insert at the beginning
- // in document order
- viewManager->InsertChild(parentView, view, insertBefore, insertBefore != nullptr);
-
- // REVIEW: Don't create a widget for fixed-pos elements anymore.
- // ComputeRepaintRegionForCopy will calculate the right area to repaint
- // when we scroll.
- // Reparent views on any child frames (or their descendants) to this
- // view. We can just call ReparentFrameViewTo on this frame because
- // we know this frame has no view, so it will crawl the children. Also,
- // we know that any descendants with views must have 'parentView' as their
- // parent view.
- ReparentFrameViewTo(aFrame, viewManager, view, parentView);
-
- // Remember our view
- aFrame->SetView(view);
-
- NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
- ("nsContainerFrame::CreateViewForFrame: frame=%p view=%p",
- aFrame, view));
-}
-
/**
* Position the view associated with |aKidFrame|, if there is one. A
* container frame should call this method after positioning a frame,
@@ -543,8 +457,9 @@ nsContainerFrame::ReparentFrameView(nsIFrame* aChildFrame,
// anything
if (oldParentView != newParentView) {
// They're not so we need to reparent any child views
- ReparentFrameViewTo(aChildFrame, oldParentView->GetViewManager(), newParentView,
- oldParentView);
+ aChildFrame->ReparentFrameViewTo(oldParentView->GetViewManager(),
+ newParentView,
+ oldParentView);
}
return NS_OK;
@@ -605,7 +520,7 @@ nsContainerFrame::ReparentFrameViewList(const nsFrameList& aChildFrameList,
// They're not so we need to reparent any child views
for (nsFrameList::Enumerator e(aChildFrameList); !e.AtEnd(); e.Next()) {
- ReparentFrameViewTo(e.get(), viewManager, newParentView, oldParentView);
+ e.get()->ReparentFrameViewTo(viewManager, newParentView, oldParentView);
}
}
@@ -769,54 +684,6 @@ nsContainerFrame::SyncFrameViewAfterReflow(nsPresContext* aPresContext,
}
}
-void
-nsContainerFrame::SyncFrameViewProperties(nsPresContext* aPresContext,
- nsIFrame* aFrame,
- nsStyleContext* aStyleContext,
- nsView* aView,
- uint32_t aFlags)
-{
- NS_ASSERTION(!aStyleContext || aFrame->StyleContext() == aStyleContext,
- "Wrong style context for frame?");
-
- if (!aView) {
- return;
- }
-
- nsViewManager* vm = aView->GetViewManager();
-
- if (nullptr == aStyleContext) {
- aStyleContext = aFrame->StyleContext();
- }
-
- // Make sure visibility is correct. This only affects nsSubdocumentFrame.
- if (0 == (aFlags & NS_FRAME_NO_VISIBILITY) &&
- !aFrame->SupportsVisibilityHidden()) {
- // See if the view should be hidden or visible
- vm->SetViewVisibility(aView,
- aStyleContext->StyleVisibility()->IsVisible()
- ? nsViewVisibility_kShow : nsViewVisibility_kHide);
- }
-
- int32_t zIndex = 0;
- bool autoZIndex = false;
-
- if (aFrame->IsAbsPosContainingBlock()) {
- // Make sure z-index is correct
- const nsStylePosition* position = aStyleContext->StylePosition();
-
- if (position->mZIndex.GetUnit() == eStyleUnit_Integer) {
- zIndex = position->mZIndex.GetIntValue();
- } else if (position->mZIndex.GetUnit() == eStyleUnit_Auto) {
- autoZIndex = true;
- }
- } else {
- autoZIndex = true;
- }
-
- vm->SetViewZIndex(aView, autoZIndex, zIndex);
-}
-
static nscoord GetCoord(const nsStyleCoord& aCoord, nscoord aIfNotCoord)
{
if (aCoord.ConvertsToLength()) {
diff --git a/layout/generic/nsContainerFrame.h b/layout/generic/nsContainerFrame.h
index 9d1edd0141c0..2f51a6f52050 100644
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -155,13 +155,6 @@ public:
virtual void DeleteNextInFlowChild(nsIFrame* aNextInFlow,
bool aDeletingEmptyFrames);
- /**
- * Helper method to wrap views around frames. Used by containers
- * under special circumstances (can be used by leaf frames as well)
- */
- static void CreateViewForFrame(nsIFrame* aFrame,
- bool aForce);
-
// Positions the frame's view based on the frame's origin
static void PositionFrameView(nsIFrame* aKidFrame);
@@ -198,18 +191,6 @@ public:
nsRenderingContext* aRC,
uint32_t aFlags);
- // Sets the view's attributes from the frame style.
- // - visibility
- // - clip
- // Call this when one of these styles changes or when the view has just
- // been created.
- // @param aStyleContext can be null, in which case the frame's style context is used
- static void SyncFrameViewProperties(nsPresContext* aPresContext,
- nsIFrame* aFrame,
- nsStyleContext* aStyleContext,
- nsView* aView,
- uint32_t aFlags = 0);
-
/**
* Converts the minimum and maximum sizes given in inner window app units to
* outer window device pixel sizes and assigns these constraints to the widget.
diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp
index 94ff50a135a0..956c85c505af 100644
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -690,12 +690,8 @@ nsFrame::DestroyFrom(nsIFrame* aDestructRoot)
}
}
- // Get the view pointer now before the frame properties disappear
- // when we call NotifyDestroyingFrame()
- nsView* view = GetView();
nsPresContext* presContext = PresContext();
-
- nsIPresShell *shell = presContext->GetPresShell();
+ nsIPresShell* shell = presContext->GetPresShell();
if (mState & NS_FRAME_OUT_OF_FLOW) {
nsPlaceholderFrame* placeholder =
shell->FrameManager()->GetPlaceholderFrameFor(this);
@@ -785,11 +781,9 @@ nsFrame::DestroyFrom(nsIFrame* aDestructRoot)
shell->ClearFrameRefs(this);
}
+ nsView* view = GetView();
if (view) {
- // Break association between view and frame
view->SetFrame(nullptr);
-
- // Destroy the view
view->Destroy();
}
@@ -986,6 +980,120 @@ nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
RemoveStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS);
}
+void
+nsIFrame::ReparentFrameViewTo(nsViewManager* aViewManager,
+ nsView* aNewParentView,
+ nsView* aOldParentView)
+{
+ if (HasView()) {
+#ifdef MOZ_XUL
+ if (GetType() == nsGkAtoms::menuPopupFrame) {
+ // This view must be parented by the root view, don't reparent it.
+ return;
+ }
+#endif
+ nsView* view = GetView();
+ // Verify that the current parent view is what we think it is
+ //nsView* parentView;
+ //NS_ASSERTION(parentView == aOldParentView, "unexpected parent view");
+
+ aViewManager->RemoveChild(view);
+
+ // The view will remember the Z-order and other attributes that have been set on it.
+ nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, this);
+ aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nullptr);
+ } else if (GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW) {
+ nsIFrame::ChildListIterator lists(this);
+ for (; !lists.IsDone(); lists.Next()) {
+ // Iterate the child frames, and check each child frame to see if it has
+ // a view
+ nsFrameList::Enumerator childFrames(lists.CurrentList());
+ for (; !childFrames.AtEnd(); childFrames.Next()) {
+ childFrames.get()->ReparentFrameViewTo(aViewManager, aNewParentView,
+ aOldParentView);
+ }
+ }
+ }
+}
+
+void
+nsIFrame::SyncFrameViewProperties(nsView* aView)
+{
+ if (!aView) {
+ aView = GetView();
+ if (!aView) {
+ return;
+ }
+ }
+
+ nsViewManager* vm = aView->GetViewManager();
+
+ // Make sure visibility is correct. This only affects nsSubDocumentFrame.
+ if (!SupportsVisibilityHidden()) {
+ // See if the view should be hidden or visible
+ nsStyleContext* sc = StyleContext();
+ vm->SetViewVisibility(aView,
+ sc->StyleVisibility()->IsVisible()
+ ? nsViewVisibility_kShow : nsViewVisibility_kHide);
+ }
+
+ int32_t zIndex = 0;
+ bool autoZIndex = false;
+
+ if (IsAbsPosContainingBlock()) {
+ // Make sure z-index is correct
+ nsStyleContext* sc = StyleContext();
+ const nsStylePosition* position = sc->StylePosition();
+ if (position->mZIndex.GetUnit() == eStyleUnit_Integer) {
+ zIndex = position->mZIndex.GetIntValue();
+ } else if (position->mZIndex.GetUnit() == eStyleUnit_Auto) {
+ autoZIndex = true;
+ }
+ } else {
+ autoZIndex = true;
+ }
+
+ vm->SetViewZIndex(aView, autoZIndex, zIndex);
+}
+
+void
+nsFrame::CreateView()
+{
+ MOZ_ASSERT(!HasView());
+
+ nsView* parentView = GetParent()->GetClosestView();
+ MOZ_ASSERT(parentView, "no parent with view");
+
+ nsViewManager* viewManager = parentView->GetViewManager();
+ MOZ_ASSERT(viewManager, "null view manager");
+
+ nsView* view = viewManager->CreateView(GetRect(), parentView);
+ SyncFrameViewProperties(view);
+
+ nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, this);
+ // we insert this view 'above' the insertBefore view, unless insertBefore is null,
+ // in which case we want to call with aAbove == false to insert at the beginning
+ // in document order
+ viewManager->InsertChild(parentView, view, insertBefore, insertBefore != nullptr);
+
+ // REVIEW: Don't create a widget for fixed-pos elements anymore.
+ // ComputeRepaintRegionForCopy will calculate the right area to repaint
+ // when we scroll.
+ // Reparent views on any child frames (or their descendants) to this
+ // view. We can just call ReparentFrameViewTo on this frame because
+ // we know this frame has no view, so it will crawl the children. Also,
+ // we know that any descendants with views must have 'parentView' as their
+ // parent view.
+ ReparentFrameViewTo(viewManager, view, parentView);
+
+ // Remember our view
+ SetView(view);
+
+ NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
+ ("nsFrame::CreateView: frame=%p view=%p",
+ this, view));
+}
+
// MSVC fails with link error "one or more multiply defined symbols found",
// gcc fails with "hidden symbol `nsIFrame::kPrincipalList' isn't defined"
// etc if they are not defined.
@@ -5850,23 +5958,8 @@ nsIFrame* nsIFrame::GetTailContinuation()
return frame;
}
-NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(ViewProperty, nsView)
-
// Associated view object
-nsView*
-nsIFrame::GetView() const
-{
- // Check the frame state bit and see if the frame has a view
- if (!(GetStateBits() & NS_FRAME_HAS_VIEW))
- return nullptr;
-
- // Check for a property on the frame
- nsView* value = Properties().Get(ViewProperty());
- NS_ASSERTION(value, "frame state bit was set but frame has no view");
- return value;
-}
-
-nsresult
+void
nsIFrame::SetView(nsView* aView)
{
if (aView) {
@@ -5874,8 +5967,7 @@ nsIFrame::SetView(nsView* aView)
#ifdef DEBUG
nsIAtom* frameType = GetType();
- NS_ASSERTION(frameType == nsGkAtoms::scrollFrame ||
- frameType == nsGkAtoms::subDocumentFrame ||
+ NS_ASSERTION(frameType == nsGkAtoms::subDocumentFrame ||
frameType == nsGkAtoms::listControlFrame ||
frameType == nsGkAtoms::objectFrame ||
frameType == nsGkAtoms::viewportFrame ||
@@ -5883,8 +5975,8 @@ nsIFrame::SetView(nsView* aView)
"Only specific frame types can have an nsView");
#endif
- // Set a property on the frame
- Properties().Set(ViewProperty(), aView);
+ // Store the view on the frame.
+ SetViewInternal(aView);
// Set the frame state bit that says the frame has a view
AddStateBits(NS_FRAME_HAS_VIEW);
@@ -5894,9 +5986,11 @@ nsIFrame::SetView(nsView* aView)
f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW);
f = f->GetParent())
f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
+ } else {
+ MOZ_ASSERT_UNREACHABLE("Destroying a view while the frame is alive?");
+ RemoveStateBits(NS_FRAME_HAS_VIEW);
+ SetViewInternal(nullptr);
}
-
- return NS_OK;
}
// Find the first geometric parent that has a view
diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h
index afe152047d22..4750a1b0bab4 100644
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -595,6 +595,12 @@ protected:
void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
public:
+ /**
+ * Helper method to create a view for a frame. Only used by a few sub-classes
+ * that need a view.
+ */
+ void CreateView();
+
//given a frame five me the first/last leaf available
//XXX Robert O'Callahan wants to move these elsewhere
static void GetLastLeaf(nsPresContext* aPresContext, nsIFrame **aFrame);
diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h
index c2423c29e602..f4f583ab917c 100644
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -613,8 +613,8 @@ public:
* If the frame is a continuing frame, then aPrevInFlow indicates the previous
* frame (the frame that was split).
*
- * If you want a view associated with your frame, you should create the view
- * after Init() has returned.
+ * Each subclass that need a view should override this method and call
+ * CreateView() after calling its base class Init().
*
* @param aContent the content object associated with the frame
* @param aParent the parent frame
@@ -1607,11 +1607,6 @@ public:
const nsDisplayListSet& aLists,
uint32_t aFlags = 0);
- /**
- * Does this frame need a view?
- */
- virtual bool NeedsView() { return false; }
-
bool RefusedAsyncAnimation() const
{
return Properties().Get(RefusedAsyncAnimationProperty());
@@ -2445,14 +2440,31 @@ public:
virtual bool HasAnyNoncollapsedCharacters()
{ return false; }
- /**
- * Accessor functions to get/set the associated view object
- *
- * GetView returns non-null if and only if |HasView| returns true.
- */
+ //
+ // Accessor functions to an associated view object:
+ //
bool HasView() const { return !!(mState & NS_FRAME_HAS_VIEW); }
- nsView* GetView() const;
- nsresult SetView(nsView* aView);
+protected:
+ virtual nsView* GetViewInternal() const
+ {
+ MOZ_ASSERT_UNREACHABLE("method should have been overridden by subclass");
+ return nullptr;
+ }
+ virtual void SetViewInternal(nsView* aView)
+ {
+ MOZ_ASSERT_UNREACHABLE("method should have been overridden by subclass");
+ }
+public:
+ nsView* GetView() const
+ {
+ if (MOZ_LIKELY(!HasView())) {
+ return nullptr;
+ }
+ nsView* view = GetViewInternal();
+ MOZ_ASSERT(view, "GetViewInternal() should agree with HasView()");
+ return view;
+ }
+ void SetView(nsView* aView);
/**
* Find the closest view (on |this| or an ancestor).
@@ -2466,6 +2478,14 @@ public:
*/
nsIFrame* GetAncestorWithView() const;
+ /**
+ * Sets the view's attributes from the frame style.
+ * Call this for nsChangeHint_SyncFrameView style changes or when the view
+ * has just been created.
+ * @param aView the frame's view or use GetView() if nullptr is given
+ */
+ void SyncFrameViewProperties(nsView* aView = nullptr);
+
/**
* Get the offset between the coordinate systems of |this| and aOther.
* Adding the return value to a point in the coordinate system of |this|
@@ -3616,6 +3636,13 @@ public:
const nsStyleCoord& aCoord,
ComputeSizeFlags aFlags = eDefault);
protected:
+ /**
+ * Reparent this frame's view if it has one.
+ */
+ void ReparentFrameViewTo(nsViewManager* aViewManager,
+ nsView* aNewParentView,
+ nsView* aOldParentView);
+
// Members
nsRect mRect;
nsIContent* mContent;
diff --git a/layout/generic/nsPluginFrame.cpp b/layout/generic/nsPluginFrame.cpp
index 12f3b2805e8b..5a5c8f2e3fef 100644
--- a/layout/generic/nsPluginFrame.cpp
+++ b/layout/generic/nsPluginFrame.cpp
@@ -150,6 +150,7 @@ protected:
nsPluginFrame::nsPluginFrame(nsStyleContext* aContext)
: nsFrame(aContext)
, mInstanceOwner(nullptr)
+ , mOuterView(nullptr)
, mInnerView(nullptr)
, mBackgroundSink(nullptr)
, mReflowCallbackPosted(false)
@@ -194,6 +195,7 @@ nsPluginFrame::Init(nsIContent* aContent,
("Initializing nsPluginFrame %p for content %p\n", this, aContent));
nsFrame::Init(aContent, aParent, aPrevInFlow);
+ CreateView();
}
void
diff --git a/layout/generic/nsPluginFrame.h b/layout/generic/nsPluginFrame.h
index 5d9f9f4757cc..b21ad43b3436 100644
--- a/layout/generic/nsPluginFrame.h
+++ b/layout/generic/nsPluginFrame.h
@@ -96,8 +96,6 @@ public:
~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing));
}
- virtual bool NeedsView() override { return true; }
-
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
@@ -271,6 +269,9 @@ protected:
friend class nsDisplayPlugin;
friend class PluginBackgroundSink;
+ nsView* GetViewInternal() const override { return mOuterView; }
+ void SetViewInternal(nsView* aView) override { mOuterView = aView; }
+
private:
// Registers the plugin for a geometry update, and requests a geometry
// update. This caches the root pres context in
@@ -303,7 +304,8 @@ private:
};
nsPluginInstanceOwner* mInstanceOwner; // WEAK
- nsView* mInnerView;
+ nsView* mOuterView;
+ nsView* mInnerView;
nsCOMPtr mWidget;
nsIntRect mWindowlessRect;
/**
diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp
index 58d18b46d64b..c1e213453933 100644
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -1974,10 +1974,9 @@ nsFrameSelection::ScrollSelectionIntoView(SelectionType aSelectionType,
// After ScrollSelectionIntoView(), the pending notifications might be
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
- return mDomSelections[index]->ScrollIntoView(aRegion,
- verticalScroll,
- nsIPresShell::ScrollAxis(),
- flags);
+ RefPtr sel = mDomSelections[index];
+ return sel->ScrollIntoView(aRegion, verticalScroll,
+ nsIPresShell::ScrollAxis(), flags);
}
nsresult
@@ -6171,6 +6170,8 @@ Selection::ScrollSelectionIntoViewEvent::Run()
int32_t flags = Selection::SCROLL_DO_FLUSH |
Selection::SCROLL_SYNCHRONOUS;
+ Selection* sel = mSelection; // workaround to satisfy static analysis
+ RefPtr kungFuDeathGrip(sel);
mSelection->mScrollEvent.Forget();
mSelection->ScrollIntoView(mRegion, mVerticalScroll,
mHorizontalScroll, mFlags | flags);
diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp
index a9bcb63470c5..3575cd6f5ee3 100644
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -60,6 +60,7 @@ GetDocumentFromView(nsView* aView)
nsSubDocumentFrame::nsSubDocumentFrame(nsStyleContext* aContext)
: nsAtomicContainerFrame(aContext)
+ , mOuterView(nullptr)
, mInnerView(nullptr)
, mIsInline(false)
, mPostedReflowCallback(false)
@@ -121,17 +122,10 @@ nsSubDocumentFrame::Init(nsIContent* aContent,
nsAtomicContainerFrame::Init(aContent, aParent, aPrevInFlow);
- // We are going to create an inner view. If we need a view for the
- // OuterFrame but we wait for the normal view creation path in
- // nsCSSFrameConstructor, then we will lose because the inner view's
- // parent will already have been set to some outer view (e.g., the
- // canvas) when it really needs to have this frame's view as its
- // parent. So, create this frame's view right away, whether we
- // really need it or not, and the inner view will get it as the
- // parent.
- if (!HasView()) {
- nsContainerFrame::CreateViewForFrame(this, true);
- }
+ // CreateView() creates this frame's view, stored in mOuterView. It needs to
+ // be created first since it's the parent of the inner view, stored in
+ // mInnerView.
+ CreateView();
EnsureInnerView();
// Set the primary frame now so that nsDocumentViewer::FindContainerView
diff --git a/layout/generic/nsSubDocumentFrame.h b/layout/generic/nsSubDocumentFrame.h
index 634e2ab3b2c5..5b3bb815ec1f 100644
--- a/layout/generic/nsSubDocumentFrame.h
+++ b/layout/generic/nsSubDocumentFrame.h
@@ -157,7 +157,11 @@ protected:
*/
nsIFrame* ObtainIntrinsicSizeFrame();
+ nsView* GetViewInternal() const override { return mOuterView; }
+ void SetViewInternal(nsView* aView) override { mOuterView = aView; }
+
RefPtr mFrameLoader;
+ nsView* mOuterView;
nsView* mInnerView;
bool mIsInline;
bool mPostedReflowCallback;
diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp
index cf08c3ee4516..aad044f0edbd 100644
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -1785,23 +1785,6 @@ GetSpacingFlags(nsIFrame* aFrame, const nsStyleText* aStyleText = nullptr)
return nonStandardSpacing ? gfxTextRunFactory::TEXT_ENABLE_SPACING : 0;
}
-static bool
-IsBaselineAligned(const nsStyleCoord& aCoord)
-{
- switch (aCoord.GetUnit()) {
- case eStyleUnit_Enumerated:
- return aCoord.GetIntValue() == NS_STYLE_VERTICAL_ALIGN_BASELINE;
- case eStyleUnit_Coord:
- return aCoord.GetCoordValue() == 0;
- case eStyleUnit_Percent:
- return aCoord.GetPercentValue() == 0;
- case eStyleUnit_Calc:
- return aCoord.GetCalcValue()->IsDefinitelyZero();
- default:
- return false;
- }
-}
-
bool
BuildTextRunsScanner::ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, nsTextFrame* aFrame2)
{
@@ -1831,10 +1814,6 @@ BuildTextRunsScanner::ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, nsTextFr
if (textStyle1->NewlineIsSignificant(aFrame1) && HasTerminalNewline(aFrame1))
return false;
- if (!IsBaselineAligned(sc1->StyleDisplay()->mVerticalAlign)) {
- return false;
- }
-
if (aFrame1->GetContent() == aFrame2->GetContent() &&
aFrame1->GetNextInFlow() != aFrame2) {
// aFrame2 must be a non-fluid continuation of aFrame1. This can happen
@@ -1851,10 +1830,6 @@ BuildTextRunsScanner::ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, nsTextFr
if (sc1 == sc2)
return true;
- if (!IsBaselineAligned(sc1->StyleDisplay()->mVerticalAlign)) {
- return false;
- }
-
const nsStyleFont* fontStyle1 = sc1->StyleFont();
const nsStyleFont* fontStyle2 = sc2->StyleFont();
nscoord letterSpacing1 = LetterSpacing(aFrame1);
diff --git a/layout/reftests/bugs/reftest-stylo.list b/layout/reftests/bugs/reftest-stylo.list
index bcff09e25d08..3ba1f024e7dc 100644
--- a/layout/reftests/bugs/reftest-stylo.list
+++ b/layout/reftests/bugs/reftest-stylo.list
@@ -386,10 +386,10 @@ fails == 315920-14.html 315920-14.html
== 315920-15.html 315920-15.html
fails == 315920-16.html 315920-16.html
fails == 315920-17.html 315920-17.html
-== 315920-18a.html 315920-18a.html
-fails == 315920-18b.html 315920-18b.html
-== 315920-18c.html 315920-18c.html
-== 315920-18d.html 315920-18d.html
+skip-if(stylo) == 315920-18a.html 315920-18a.html # bug 1338982, which makes this timing-dependent
+skip-if(stylo) == 315920-18b.html 315920-18b.html # bug 1338982, which makes this timing-dependent
+skip-if(stylo) == 315920-18c.html 315920-18c.html # bug 1338982, which makes this timing-dependent
+skip-if(stylo) == 315920-18d.html 315920-18d.html # bug 1338982, which makes this timing-dependent
fails == 315920-18e.html 315920-18e.html
fails == 315920-18f.html 315920-18f.html
fails == 315920-18g.html 315920-18g.html
diff --git a/layout/reftests/image/moz-broken-matching-1-ref.html b/layout/reftests/image/moz-broken-matching-1-ref.html
new file mode 100644
index 000000000000..94bb5e4962b7
--- /dev/null
+++ b/layout/reftests/image/moz-broken-matching-1-ref.html
@@ -0,0 +1,7 @@
+
+
+
diff --git a/layout/reftests/image/moz-broken-matching-1.html b/layout/reftests/image/moz-broken-matching-1.html
new file mode 100644
index 000000000000..eb63194e140b
--- /dev/null
+++ b/layout/reftests/image/moz-broken-matching-1.html
@@ -0,0 +1,7 @@
+
+
+
diff --git a/layout/reftests/image/reftest.list b/layout/reftests/image/reftest.list
index 588034fb0a4b..e4e4d7b2f266 100644
--- a/layout/reftests/image/reftest.list
+++ b/layout/reftests/image/reftest.list
@@ -126,3 +126,5 @@ fuzzy(1,1) == image-orientation-background.html?90&flip image-orientation-r
== image-resize-percent-height.html image-resize-ref.html
== image-resize-percent-width.html image-resize-ref.html
+
+== moz-broken-matching-1.html moz-broken-matching-1-ref.html
diff --git a/layout/style/CSSStyleSheet.cpp b/layout/style/CSSStyleSheet.cpp
index afb8e7081e04..84d0f7c9f476 100644
--- a/layout/style/CSSStyleSheet.cpp
+++ b/layout/style/CSSStyleSheet.cpp
@@ -988,7 +988,7 @@ CSSStyleSheet::ReparseSheet(const nsAString& aInput)
loader = mDocument->CSSLoader();
NS_ASSERTION(loader, "Document with no CSS loader!");
} else {
- loader = new css::Loader(StyleBackendType::Gecko);
+ loader = new css::Loader(StyleBackendType::Gecko, nullptr);
}
mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
diff --git a/layout/style/ErrorReporter.cpp b/layout/style/ErrorReporter.cpp
index b57814eb7479..4775a68ef324 100644
--- a/layout/style/ErrorReporter.cpp
+++ b/layout/style/ErrorReporter.cpp
@@ -11,6 +11,7 @@
#include "mozilla/css/Loader.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
+#include "mozilla/SystemGroup.h"
#include "nsCSSScanner.h"
#include "nsIConsoleService.h"
#include "nsIDocument.h"
@@ -153,7 +154,11 @@ ErrorReporter::~ErrorReporter()
// balance between performance and memory usage, so we only allow
// short-term caching.
if (sSpecCache && sSpecCache->IsInUse() && !sSpecCache->IsPending()) {
- if (NS_FAILED(NS_DispatchToCurrentThread(sSpecCache))) {
+ nsCOMPtr runnable(sSpecCache);
+ nsresult rv =
+ SystemGroup::Dispatch("ShortTermURISpecCache", TaskCategory::Other,
+ runnable.forget());
+ if (NS_FAILED(rv)) {
// Peform the "deferred" cleanup immediately if the dispatch fails.
sSpecCache->Run();
} else {
diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp
index a35556c8587b..b126e88b66f8 100644
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -104,6 +104,8 @@ FontFaceSet::FontFaceSet(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument)
, mHasLoadingFontFacesIsDirty(false)
, mDelayedLoadCheck(false)
{
+ MOZ_ASSERT(mDocument, "We should get a valid document from the caller!");
+
nsCOMPtr global = do_QueryInterface(aWindow);
// If the pref is not set, don't create the Promise (which the page wouldn't
@@ -1473,9 +1475,9 @@ FontFaceSet::OnFontFaceStatusChanged(FontFace* aFontFace)
if (!mDelayedLoadCheck) {
mDelayedLoadCheck = true;
nsCOMPtr checkTask =
- NewRunnableMethod("FontFaceSet::CheckLoadingFinishedAfterDelay",
- this, &FontFaceSet::CheckLoadingFinishedAfterDelay);
- NS_DispatchToMainThread(checkTask);
+ NewRunnableMethod(this, &FontFaceSet::CheckLoadingFinishedAfterDelay);
+ mDocument->Dispatch("FontFaceSet::CheckLoadingFinishedAfterDelay",
+ TaskCategory::Other, checkTask.forget());
}
}
}
diff --git a/layout/style/FontFaceSet.h b/layout/style/FontFaceSet.h
index d08fbaf8c8cf..a1473dad61db 100644
--- a/layout/style/FontFaceSet.h
+++ b/layout/style/FontFaceSet.h
@@ -162,6 +162,8 @@ public:
return set ? set->GetPresContext() : nullptr;
}
+ nsIDocument* Document() const { return mDocument; }
+
// -- Web IDL --------------------------------------------------------------
IMPL_EVENT_HANDLER(loading)
diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp
index f6e1f34368a6..10cf9af0acd5 100644
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -19,11 +19,12 @@
#include "mozilla/css/Loader.h"
#include "mozilla/ArrayUtils.h"
+#include "mozilla/dom/DocGroup.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/MemoryReporting.h"
-
#include "mozilla/StyleSheetInlines.h"
+#include "mozilla/SystemGroup.h"
#include "nsIRunnable.h"
#include "nsIUnicharStreamLoader.h"
#include "nsSyncLoadService.h"
@@ -519,8 +520,9 @@ LoaderReusableStyleSheets::FindReusableStyleSheet(nsIURI* aURL,
* Loader Implementation *
*************************/
-Loader::Loader(StyleBackendType aType)
+Loader::Loader(StyleBackendType aType, DocGroup* aDocGroup)
: mDocument(nullptr)
+ , mDocGroup(aDocGroup)
, mDatasToNotifyOn(0)
, mCompatMode(eCompatibility_FullStandards)
, mStyleBackendType(Some(aType))
@@ -542,6 +544,8 @@ Loader::Loader(nsIDocument* aDocument)
, mSyncCallback(false)
#endif
{
+ MOZ_ASSERT(mDocument, "We should get a valid document from the caller!");
+
// We can just use the preferred set, since there are no sheets in the
// document yet (if there are, how did they get there? _we_ load the sheets!)
// and hence the selected set makes no sense at this time.
@@ -2471,13 +2475,24 @@ Loader::PostLoadEvent(nsIURI* aURI,
aObserver,
nullptr,
mDocument);
- NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY);
if (!mPostedEvents.AppendElement(evt)) {
return NS_ERROR_OUT_OF_MEMORY;
}
- nsresult rv = NS_DispatchToCurrentThread(evt);
+ nsresult rv;
+ RefPtr runnable(evt);
+ if (mDocument) {
+ rv = mDocument->Dispatch("SheetLoadData", TaskCategory::Other,
+ runnable.forget());
+ } else if (mDocGroup) {
+ rv = mDocGroup->Dispatch("SheetLoadData", TaskCategory::Other,
+ runnable.forget());
+ } else {
+ rv = SystemGroup::Dispatch("SheetLoadData", TaskCategory::Other,
+ runnable.forget());
+ }
+
if (NS_FAILED(rv)) {
NS_WARNING("failed to dispatch stylesheet load event");
mPostedEvents.RemoveElement(evt);
diff --git a/layout/style/Loader.h b/layout/style/Loader.h
index 9059e8e6f886..dbeb5e610324 100644
--- a/layout/style/Loader.h
+++ b/layout/style/Loader.h
@@ -36,6 +36,7 @@ class nsIStyleSheetLinkingElement;
namespace mozilla {
namespace dom {
+class DocGroup;
class Element;
} // namespace dom
} // namespace mozilla
@@ -191,7 +192,11 @@ class Loader final {
typedef mozilla::net::ReferrerPolicy ReferrerPolicy;
public:
- explicit Loader(StyleBackendType aType);
+ // aDocGroup is used for dispatching SheetLoadData in PostLoadEvent(). It
+ // can be null if you want to use this constructor, and there's no
+ // document when the Loader is constructed.
+ Loader(StyleBackendType aType, mozilla::dom::DocGroup* aDocGroup);
+
explicit Loader(nsIDocument*);
private:
@@ -574,6 +579,8 @@ private:
// DropDocumentReference().
nsIDocument* MOZ_NON_OWNING_REF mDocument; // the document we live for
+ // For dispatching events via DocGroup::Dispatch() when mDocument is nullptr.
+ RefPtr mDocGroup;
// Number of datas still waiting to be notified on if we're notifying on a
// whole bunch at once (e.g. in one of the stop methods). This is used to
diff --git a/layout/style/PreloadedStyleSheet.cpp b/layout/style/PreloadedStyleSheet.cpp
index e39057fd59ab..276112e7cbd1 100644
--- a/layout/style/PreloadedStyleSheet.cpp
+++ b/layout/style/PreloadedStyleSheet.cpp
@@ -71,7 +71,7 @@ PreloadedStyleSheet::GetSheet(StyleBackendType aType, StyleSheet** aResult)
aType == StyleBackendType::Gecko ? mGecko : mServo;
if (!sheet) {
- RefPtr loader = new css::Loader(aType);
+ RefPtr loader = new css::Loader(aType, nullptr);
nsresult rv = loader->LoadSheetSync(mURI, mParsingMode, true, &sheet);
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(sheet);
diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp
index 9461a75110bd..1aa3e1563d67 100644
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -41,6 +41,7 @@
#include "mozilla/ServoElementSnapshot.h"
#include "mozilla/ServoRestyleManager.h"
#include "mozilla/StyleAnimationValue.h"
+#include "mozilla/SystemGroup.h"
#include "mozilla/DeclarationBlockInlines.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ElementInlines.h"
@@ -344,7 +345,8 @@ Gecko_DropElementSnapshot(ServoElementSnapshotOwned aSnapshot)
// descendants of a new display:none root).
if (MOZ_UNLIKELY(!NS_IsMainThread())) {
nsCOMPtr task = NS_NewRunnableFunction([=]() { delete aSnapshot; });
- NS_DispatchToMainThread(task.forget());
+ SystemGroup::Dispatch("Gecko_DropElementSnapshot", TaskCategory::Other,
+ task.forget());
} else {
delete aSnapshot;
}
diff --git a/layout/style/nsCSSValue.cpp b/layout/style/nsCSSValue.cpp
index 16a5432f93db..5f30efbc529a 100644
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -1680,13 +1680,17 @@ nsCSSValue::AppendToString(nsCSSPropertyID aProperty, nsAString& aResult,
unit == eCSSUnit_RGBAColor) {
nscolor color = GetColorValue();
// For brevity, we omit the alpha component if it's equal to 255 (full
- // opaque). Also, we try to preserve the author-specified function name,
- // unless it's rgba() and we're omitting the alpha component - then we
- // use rgb().
+ // opaque). Also, we use "rgba" rather than "rgb" when the color includes
+ // the non-opaque alpha value, for backwards-compat (even though they're
+ // aliases as of css-color-4).
+ // e.g.:
+ // rgba(1, 2, 3, 1.0) => rgb(1, 2, 3)
+ // rgba(1, 2, 3, 0.5) => rgba(1, 2, 3, 0.5)
+
uint8_t a = NS_GET_A(color);
bool showAlpha = (a != 255);
- if (unit == eCSSUnit_RGBAColor && showAlpha) {
+ if (showAlpha) {
aResult.AppendLiteral("rgba(");
} else {
aResult.AppendLiteral("rgb(");
@@ -3057,7 +3061,9 @@ css::ImageValue::Initialize(nsIDocument* aDocument)
css::ImageValue::~ImageValue()
{
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(NS_IsMainThread() || mRequests.Count() == 0,
+ "Destructor should run on main thread, or on non-main thread "
+ "when mRequest is empty!");
for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) {
nsIDocument* doc = iter.Key();
diff --git a/layout/style/nsFontFaceLoader.cpp b/layout/style/nsFontFaceLoader.cpp
index 2f0ccd634e88..d8bdee0dd063 100644
--- a/layout/style/nsFontFaceLoader.cpp
+++ b/layout/style/nsFontFaceLoader.cpp
@@ -11,8 +11,6 @@
#include "nsFontFaceLoader.h"
-#include "mozilla/Logging.h"
-
#include "nsError.h"
#include "nsContentUtils.h"
#include "mozilla/Preferences.h"
@@ -55,6 +53,8 @@ nsFontFaceLoader::nsFontFaceLoader(gfxUserFontEntry* aUserFontEntry,
mFontFaceSet(aFontFaceSet),
mChannel(aChannel)
{
+ MOZ_ASSERT(mFontFaceSet,
+ "We should get a valid FontFaceSet from the caller!");
mStartTime = TimeStamp::Now();
}
@@ -87,10 +87,13 @@ nsFontFaceLoader::StartedLoading(nsIStreamLoader* aStreamLoader)
if (loadTimeout > 0) {
mLoadTimer = do_CreateInstance("@mozilla.org/timer;1");
if (mLoadTimer) {
- mLoadTimer->InitWithFuncCallback(LoadTimerCallback,
- static_cast(this),
- loadTimeout,
- nsITimer::TYPE_ONE_SHOT);
+ mLoadTimer->SetTarget(
+ mFontFaceSet->Document()->EventTargetFor(TaskCategory::Other));
+ mLoadTimer->InitWithNamedFuncCallback(LoadTimerCallback,
+ static_cast(this),
+ loadTimeout,
+ nsITimer::TYPE_ONE_SHOT,
+ "LoadTimerCallback");
}
} else {
mUserFontEntry->mFontDataLoadingState = gfxUserFontEntry::LOADING_SLOWLY;
diff --git a/layout/style/nsLayoutStylesheetCache.cpp b/layout/style/nsLayoutStylesheetCache.cpp
index a28d943410a5..1cfed5e77a7e 100644
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -773,7 +773,7 @@ nsLayoutStylesheetCache::LoadSheet(nsIURI* aURI,
gCSSLoader_Servo;
if (!loader) {
- loader = new mozilla::css::Loader(mBackendType);
+ loader = new Loader(mBackendType, nullptr);
if (!loader) {
ErrorLoadingSheet(aURI, "no Loader", eCrash);
return;
diff --git a/layout/style/nsStyleContext.h b/layout/style/nsStyleContext.h
index ff885f2ee5e9..2d2300217d21 100644
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -688,6 +688,8 @@ private:
Servo_GetStyle##name_(mSource.AsServoComputedValues()); \
/* perform any remaining main thread work on the struct */ \
if (needToCompute) { \
+ MOZ_ASSERT(NS_IsMainThread()); \
+ MOZ_ASSERT(!mozilla::ServoStyleSet::IsInServoTraversal()); \
const_cast(data)->FinishStyle(PresContext()); \
/* the Servo-backed StyleContextSource owns the struct */ \
AddStyleBit(NS_STYLE_INHERIT_BIT(name_)); \
diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp
index 007df3cdfbbe..5bb6d7564940 100644
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -33,6 +33,7 @@
#include "CounterStyleManager.h"
#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for PlaybackDirection
+#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/ImageTracker.h"
#include "mozilla/Likely.h"
#include "nsIURI.h"
@@ -1890,7 +1891,7 @@ nsStyleGradient::HasCalc()
/**
* Runnable to release the nsStyleImageRequest's mRequestProxy,
- * mImageValue and mImageValue on the main thread, and to perform
+ * mImageValue and mImageTracker on the main thread, and to perform
* any necessary unlocking and untracking of the image.
*/
class StyleImageRequestCleanupTask : public mozilla::Runnable
@@ -1911,7 +1912,8 @@ public:
NS_IMETHOD Run() final
{
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mRequestProxy || NS_IsMainThread(),
+ "If mRequestProxy is non-null, we need to run on main thread!");
if (!mRequestProxy) {
return NS_OK;
@@ -1932,7 +1934,15 @@ public:
}
protected:
- virtual ~StyleImageRequestCleanupTask() { MOZ_ASSERT(NS_IsMainThread()); }
+ virtual ~StyleImageRequestCleanupTask()
+ {
+ MOZ_ASSERT(mImageValue->mRequests.Count() == 0 || NS_IsMainThread(),
+ "If mImageValue has any mRequests, we need to run on main "
+ "thread to release ImageValues!");
+ MOZ_ASSERT((!mRequestProxy && !mImageTracker) || NS_IsMainThread(),
+ "mRequestProxy and mImageTracker's destructor need to run "
+ "on the main thread!");
+ }
private:
Mode mModeFlags;
@@ -1986,10 +1996,13 @@ nsStyleImageRequest::~nsStyleImageRequest()
mRequestProxy.forget(),
mImageValue.forget(),
mImageTracker.forget());
- if (NS_IsMainThread()) {
+ if (NS_IsMainThread() || !IsResolved()) {
task->Run();
} else {
- NS_DispatchToMainThread(task.forget());
+ MOZ_ASSERT(IsResolved() == bool(mDocGroup),
+ "We forgot to cache mDocGroup in Resolve()?");
+ mDocGroup->Dispatch("StyleImageRequestCleanupTask",
+ TaskCategory::Other, task.forget());
}
}
@@ -2003,8 +2016,10 @@ nsStyleImageRequest::Resolve(nsPresContext* aPresContext)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsResolved(), "already resolved");
+ MOZ_ASSERT(aPresContext);
mResolved = true;
+ mDocGroup = aPresContext->Document()->GetDocGroup();
// For now, just have unique nsCSSValue/ImageValue objects. We should
// really store the ImageValue on the Servo specified value, so that we can
diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h
index 500e7cdfc111..1f856654b271 100644
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -385,6 +385,9 @@ private:
RefPtr mImageValue;
RefPtr mImageTracker;
+ // Cache DocGroup for dispatching events in the destructor.
+ RefPtr mDocGroup;
+
Mode mModeFlags;
bool mResolved;
};
diff --git a/layout/style/test/stylo-failures.md b/layout/style/test/stylo-failures.md
index 604116f8abaa..38f65ce361e7 100644
--- a/layout/style/test/stylo-failures.md
+++ b/layout/style/test/stylo-failures.md
@@ -104,7 +104,8 @@ to mochitest command.
* test_bug798843_pref.html: conditional opentype svg support [7]
* test_computed_style.html `gradient`: -moz-prefixed radient value [9]
* ... `mask`: mask-image isn't set properly bug 1347398 [2]
-* ... `rgba`: svg paint should distinguish whether there is fallback bug 1347409 [4]
+* ... `fill`: svg paint should distinguish whether there is fallback bug 1347409 [2]
+* ... `stroke`: svg paint should distinguish whether there is fallback bug 1347409 [2]
* ... `#foo`: local ref url should be preserved bug 1347412 [5]
* character not properly escaped servo/servo#15947
* test_parse_url.html [4]
@@ -320,6 +321,8 @@ to mochitest command.
* new syntax of rgba?() and hsla?() functions servo/rust-cssparser#113
* test_value_storage.html `'color'` [35]
* ... `rgb(100, 100.0, 100)` [1]
+ * test_computed_style.html `css-color-4` [8]
+ * test_specified_value_serialization.html `css-color-4` [8]
* color interpolation hint not supported servo/servo#15166
* test_value_storage.html `'linear-gradient` [50]
* two-keyword form of background-repeat/mask-repeat servo/servo#14954
@@ -363,7 +366,7 @@ to mochitest command.
* test_value_storage.html `'transform` [104]
* ... `"transform` [66]
* ... `-webkit-transform` [109]
- * test_specified_value_serialization.html [27]
+ * test_specified_value_serialization.html `bug-721136` [27]
* test_units_angle.html [3]
* {background,mask}-position lacks comma for serialization servo/servo#15200
* test_value_storage.html `background-position` [81]
@@ -378,7 +381,6 @@ to mochitest command.
* background-position invalid 3-value form **issue to be filed**
* test_shorthand_property_getters.html `should serialize to 4-value` [2]
* test_variables.html `--weird`: name of custom property is not escaped properly servo/servo#15399 [1]
- * ... `got "--`: CSS-wide keywords in custom properties servo/servo#15401 [3]
* image-layer values should omit some of its parts when they are initial servo/servo#15951
* test_shorthand_property_getters.html `background` [2]
* counter-{reset,increment} doesn't serialize none servo/servo#15977
@@ -407,7 +409,7 @@ to mochitest command.
* :-moz-window-inactive bug 1348489
* test_selectors.html `:-moz-window-inactive` [2]
* :-moz-{first,last}-node
- * test_selectors.html `:-moz-` [13]
+ * test_selectors.html `:-moz-` [6]
* ... `unexpected rule index` [5]
* :dir
* test_selectors.html `:dir` [10]
diff --git a/layout/style/test/test_computed_style.html b/layout/style/test/test_computed_style.html
index 77e91b9dfddc..9ba9403af841 100644
--- a/layout/style/test/test_computed_style.html
+++ b/layout/style/test/test_computed_style.html
@@ -375,6 +375,45 @@ var noframe_container = document.getElementById("content");
p.remove();
})();
+(function test_bug_1347164() {
+ // Test that computed color values are serialized as "rgb()"
+ // IFF they're fully-opaque (and otherwise as "rgba()").
+ var color = [
+ ["rgba(0, 0, 0, 1)", "rgb(0, 0, 0)"],
+ ["rgba(0, 0, 0, 0.5)", "rgba(0, 0, 0, 0.5)"],
+ ["hsla(0, 0%, 0%, 1)", "rgb(0, 0, 0)"],
+ ["hsla(0, 0%, 0%, 0.5)", "rgba(0, 0, 0, 0.5)"],
+ ];
+
+ var css_color_4 = [
+ ["rgba(0 0 0 / 1)", "rgb(0, 0, 0)"],
+ ["rgba(0 0 0 / 0.1)", "rgba(0, 0, 0, 0.1)"],
+ ["rgb(0 0 0 / 1)", "rgb(0, 0, 0)"],
+ ["rgb(0 0 0 / 0.2)", "rgba(0, 0, 0, 0.2)"],
+ ["hsla(0 0% 0% / 1)", "rgb(0, 0, 0)"],
+ ["hsla(0deg 0% 0% / 0.3)", "rgba(0, 0, 0, 0.3)"],
+ ["hsl(0 0% 0% / 1)", "rgb(0, 0, 0)"],
+ ["hsl(0 0% 0% / 0.4)", "rgba(0, 0, 0, 0.4)"],
+ ];
+
+ var p = document.createElement("p");
+ var cs = getComputedStyle(p, "");
+ frame_container.appendChild(p);
+
+ for (var i = 0; i < color.length; ++i) {
+ var test = color[i];
+ p.style.color = test[0];
+ is(cs.color, test[1], "computed value of " + test[0]);
+ }
+ for (var i = 0; i < css_color_4.length; ++i) {
+ var test = css_color_4[i];
+ p.style.color = test[0];
+ is(cs.color, test[1], "css-color-4 computed value of " + test[0]);
+ }
+
+ p.remove();
+})();
+
+Mozilla Bug
+
+
+
+
-
+
diff --git a/layout/style/test/test_specified_value_serialization.html b/layout/style/test/test_specified_value_serialization.html
index 9cc2f9b2f14a..73bf62edfa51 100644
--- a/layout/style/test/test_specified_value_serialization.html
+++ b/layout/style/test/test_specified_value_serialization.html
@@ -1,62 +1,112 @@
-
-