diff --git a/accessible/ipc/win/HandlerProvider.cpp b/accessible/ipc/win/HandlerProvider.cpp index f359feff3b8c..c9c232ad7785 100644 --- a/accessible/ipc/win/HandlerProvider.cpp +++ b/accessible/ipc/win/HandlerProvider.cpp @@ -131,8 +131,10 @@ HandlerProvider::GetHandlerPayloadSize(NotNull aOutPayloadSize) GetAndSerializePayload(lock); - if (!mSerializer) { - return E_FAIL; + if (!mSerializer || !(*mSerializer)) { + // Failed payload serialization is non-fatal + *aOutPayloadSize = mscom::StructToStream::GetEmptySize(); + return S_OK; } *aOutPayloadSize = mSerializer->GetSize(); @@ -176,7 +178,8 @@ HandlerProvider::WriteHandlerPayload(NotNull aStream) { MutexAutoLock lock(mMutex); - if (!mSerializer) { + if (!mSerializer || !(*mSerializer)) { + // Failed payload serialization is non-fatal mscom::StructToStream emptyStruct; return emptyStruct.Write(aStream); } diff --git a/browser/base/content/test/alerts/browser_notification_open_settings.js b/browser/base/content/test/alerts/browser_notification_open_settings.js index f751754f0e1d..2a2ac143f0ac 100644 --- a/browser/base/content/test/alerts/browser_notification_open_settings.js +++ b/browser/base/content/test/alerts/browser_notification_open_settings.js @@ -1,6 +1,8 @@ "use strict"; var notificationURL = "http://example.org/browser/browser/base/content/test/alerts/file_dom_notifications.html"; +var expectedURL = Services.prefs.getBoolPref("browser.preferences.useOldOrganization") ? "about:preferences#content" + : "about:preferences#privacy"; add_task(async function test_settingsOpen_observer() { info("Opening a dummy tab so openPreferences=>switchToTabHavingURI doesn't use the blank tab."); @@ -11,7 +13,7 @@ add_task(async function test_settingsOpen_observer() { // Ensure preferences is loaded before removing the tab. let syncPaneLoadedPromise = TestUtils.topicObserved("sync-pane-loaded", () => true); - let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences#privacy"); + let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, expectedURL); info("simulate a notifications-open-settings notification"); let uri = NetUtil.newURI("https://example.com"); let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {}); @@ -47,7 +49,7 @@ add_task(async function test_settingsOpen_button() { } let closePromise = promiseWindowClosed(alertWindow); - let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences#privacy"); + let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, expectedURL); let openSettingsMenuItem = alertWindow.document.getElementById("openSettingsMenuItem"); openSettingsMenuItem.click(); diff --git a/browser/components/downloads/DownloadsCommon.jsm b/browser/components/downloads/DownloadsCommon.jsm index 4521e78e2c09..5b2a359ecab2 100644 --- a/browser/components/downloads/DownloadsCommon.jsm +++ b/browser/components/downloads/DownloadsCommon.jsm @@ -659,24 +659,30 @@ function DownloadsDataCtor(aPrivate) { // Contains all the available Download objects and their integer state. this.oldDownloadStates = new Map(); - // Array of view objects that should be notified when the available download - // data changes. - this._views = []; + // This defines "initializeDataLink" and "_promiseList" synchronously, then + // continues execution only when "initializeDataLink" is called, allowing the + // underlying data to be loaded only when actually needed. + this._promiseList = (async () => { + await new Promise(resolve => this.initializeDataLink = resolve); + + let list = await Downloads.getList(this._isPrivate ? Downloads.PRIVATE + : Downloads.PUBLIC); + await list.addView(this); + return list; + })(); } DownloadsDataCtor.prototype = { /** * Starts receiving events for current downloads. */ - initializeDataLink() { - if (!this._dataLinkInitialized) { - let promiseList = Downloads.getList(this._isPrivate ? Downloads.PRIVATE - : Downloads.PUBLIC); - promiseList.then(list => list.addView(this)).catch(Cu.reportError); - this._dataLinkInitialized = true; - } - }, - _dataLinkInitialized: false, + initializeDataLink() {}, + + /** + * Promise resolved with the underlying DownloadList object once we started + * receiving events for current downloads. + */ + _promiseList: null, /** * Iterator for all the available Download objects. This is empty until the @@ -700,13 +706,11 @@ DownloadsDataCtor.prototype = { }, /** - * Asks the back-end to remove finished downloads from the list. + * Asks the back-end to remove finished downloads from the list. This method + * is only called after the data link has been initialized. */ removeFinished() { - let promiseList = Downloads.getList(this._isPrivate ? Downloads.PRIVATE - : Downloads.PUBLIC); - promiseList.then(list => list.removeFinished()) - .catch(Cu.reportError); + this._promiseList.then(list => list.removeFinished()).catch(Cu.reportError); let indicatorData = this._isPrivate ? PrivateDownloadsIndicatorData : DownloadsIndicatorData; indicatorData.attention = DownloadsCommon.ATTENTION_NONE; @@ -723,10 +727,6 @@ DownloadsDataCtor.prototype = { this.oldDownloadStates.set(download, DownloadsCommon.stateOfDownload(download)); - - for (let view of this._views) { - view.onDownloadAdded(download, true); - } }, onDownloadChanged(download) { @@ -770,14 +770,6 @@ DownloadsDataCtor.prototype = { } } - for (let view of this._views) { - try { - view.onDownloadStateChanged(download); - } catch (ex) { - Cu.reportError(ex); - } - } - if (download.succeeded || (download.error && download.error.becauseBlocked)) { this._notifyDownloadEvent("finish"); @@ -788,18 +780,10 @@ DownloadsDataCtor.prototype = { download.newDownloadNotified = true; this._notifyDownloadEvent("start"); } - - for (let view of this._views) { - view.onDownloadChanged(download); - } }, onDownloadRemoved(download) { this.oldDownloadStates.delete(download); - - for (let view of this._views) { - view.onDownloadRemoved(download); - } }, // Registration of views @@ -813,8 +797,8 @@ DownloadsDataCtor.prototype = { * removeView before termination. */ addView(aView) { - this._views.push(aView); - this._updateView(aView); + this._promiseList.then(list => list.addView(aView)) + .catch(Cu.reportError); }, /** @@ -824,30 +808,8 @@ DownloadsDataCtor.prototype = { * DownloadsView object to be removed. */ removeView(aView) { - let index = this._views.indexOf(aView); - if (index != -1) { - this._views.splice(index, 1); - } - }, - - /** - * Ensures that the currently loaded data is added to the specified view. - * - * @param aView - * DownloadsView object to be initialized. - */ - _updateView(aView) { - // Indicate to the view that a batch loading operation is in progress. - aView.onDataLoadStarting(); - - // Sort backwards by start time, ensuring that the most recent - // downloads are added first regardless of their state. - let downloadsArray = [...this.downloads]; - downloadsArray.sort((a, b) => b.startTime - a.startTime); - downloadsArray.forEach(download => aView.onDownloadAdded(download, false)); - - // Notify the view that all data is available. - aView.onDataLoadCompleted(); + this._promiseList.then(list => list.removeView(aView)) + .catch(Cu.reportError); }, // Notifications sent to the most recent browser window only @@ -912,6 +874,13 @@ XPCOMUtils.defineLazyGetter(this, "DownloadsData", function() { * as a view is registered with it. */ const DownloadsViewPrototype = { + /** + * Contains all the available Download objects and their current state value. + * + * SUBCLASSES MUST OVERRIDE THIS PROPERTY. + */ + _oldDownloadStates: null, + // Registration of views /** @@ -987,7 +956,7 @@ const DownloadsViewPrototype = { } }, - // Callback functions from DownloadsData + // Callback functions from DownloadList /** * Indicates whether we are still loading downloads data asynchronously. @@ -997,14 +966,14 @@ const DownloadsViewPrototype = { /** * Called before multiple downloads are about to be loaded. */ - onDataLoadStarting() { + onDownloadBatchStarting() { this._loading = true; }, /** * Called after data loading finished. */ - onDataLoadCompleted() { + onDownloadBatchEnded() { this._loading = false; }, @@ -1014,17 +983,12 @@ const DownloadsViewPrototype = { * * @param download * Download object that was just added. - * @param newest - * When true, indicates that this item is the most recent and should be - * added in the topmost position. This happens when a new download is - * started. When false, indicates that the item is the least recent - * with regard to the items that have been already added. The latter - * generally happens during the asynchronous data load. * - * @note Subclasses should override this. + * @note Subclasses should override this and still call the base method. */ - onDownloadAdded(download, newest) { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + onDownloadAdded(download) { + this._oldDownloadStates.set(download, + DownloadsCommon.stateOfDownload(download)); }, /** @@ -1047,10 +1011,16 @@ const DownloadsViewPrototype = { * Note that progress notification changes are throttled at the Downloads.jsm * API level, and there is no throttling mechanism in the front-end. * - * @note Subclasses should override this. + * @note Subclasses should override this and still call the base method. */ onDownloadChanged(download) { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + let oldState = this._oldDownloadStates.get(download); + let newState = DownloadsCommon.stateOfDownload(download); + this._oldDownloadStates.set(download, newState); + + if (oldState != newState) { + this.onDownloadStateChanged(download); + } }, /** @@ -1099,6 +1069,7 @@ const DownloadsViewPrototype = { * the main browser window until the autostart timeout elapses. */ function DownloadsIndicatorDataCtor(aPrivate) { + this._oldDownloadStates = new WeakMap(); this._isPrivate = aPrivate; this._views = []; } @@ -1126,7 +1097,8 @@ DownloadsIndicatorDataCtor.prototype = { this._updateViews(); }, - onDownloadAdded(download, newest) { + onDownloadAdded(download) { + DownloadsViewPrototype.onDownloadAdded.call(this, download); this._itemCount++; this._updateViews(); }, @@ -1164,6 +1136,7 @@ DownloadsIndicatorDataCtor.prototype = { }, onDownloadChanged(download) { + DownloadsViewPrototype.onDownloadChanged.call(this, download); this._updateViews(); }, @@ -1329,6 +1302,7 @@ function DownloadsSummaryData(aIsPrivate, aNumToExclude) { this._numActive = 0; this._percentComplete = -1; + this._oldDownloadStates = new WeakMap(); this._isPrivate = aIsPrivate; this._views = []; } @@ -1361,13 +1335,9 @@ DownloadsSummaryData.prototype = { this._updateViews(); }, - onDownloadAdded(download, newest) { - if (newest) { - this._downloads.unshift(download); - } else { - this._downloads.push(download); - } - + onDownloadAdded(download) { + DownloadsViewPrototype.onDownloadAdded.call(this, download); + this._downloads.unshift(download); this._updateViews(); }, @@ -1377,7 +1347,8 @@ DownloadsSummaryData.prototype = { this._lastTimeLeft = -1; }, - onDownloadChanged() { + onDownloadChanged(download) { + DownloadsViewPrototype.onDownloadChanged.call(this, download); this._updateViews(); }, diff --git a/browser/components/downloads/content/allDownloadsViewOverlay.js b/browser/components/downloads/content/allDownloadsViewOverlay.js index 3084a98bad11..64052c0259f1 100644 --- a/browser/components/downloads/content/allDownloadsViewOverlay.js +++ b/browser/components/downloads/content/allDownloadsViewOverlay.js @@ -178,7 +178,7 @@ HistoryDownload.prototype = { * caller must do it and remove the element when it's no longer needed. * * The caller is also responsible for forwarding status notifications for - * session downloads, calling the onStateChanged and onChanged methods. + * session downloads, calling the onSessionDownloadChanged method. * * @param [optional] aSessionDownload * The session download, required if aHistoryDownload is not set. @@ -239,6 +239,9 @@ HistoryDownloadElementShell.prototype = { } this._sessionDownload = aValue; + if (aValue) { + this.sessionDownloadState = DownloadsCommon.stateOfDownload(aValue); + } this.ensureActive(); this._updateUI(); @@ -291,7 +294,13 @@ HistoryDownloadElementShell.prototype = { } }, - onChanged() { + onSessionDownloadChanged() { + let newState = DownloadsCommon.stateOfDownload(this.sessionDownload); + if (this.sessionDownloadState != newState) { + this.sessionDownloadState = newState; + this.onStateChanged(); + } + // This cannot be placed within onStateChanged because // when a download goes from hasBlockedData to !hasBlockedData // it will still remain in the same state. @@ -610,7 +619,7 @@ DownloadsPlacesView.prototype = { * @param [optional] aPlacesNode * The Places node for a history download, or null for session downloads. * @param [optional] aNewest - * @see onDownloadAdded. Ignored for history downloads. + * Whether the download should be added at the top of the list. * @param [optional] aDocumentFragment * To speed up the appending of multiple elements to the end of the * list which are coming in a single batch (i.e. invalidateContainer), @@ -938,7 +947,8 @@ DownloadsPlacesView.prototype = { }, get selectedNodes() { - return this._richlistbox.selectedItems.filter(element => element._placesNode); + return Array.filter(this._richlistbox.selectedItems, + element => element._placesNode); }, get selectedNode() { @@ -1095,21 +1105,16 @@ DownloadsPlacesView.prototype = { } }, - onDataLoadStarting() {}, - onDataLoadCompleted() { + onDownloadBatchEnded() { this._ensureInitialSelection(); }, - onDownloadAdded(download, newest) { - this._addDownloadData(download, null, newest); - }, - - onDownloadStateChanged(download) { - this._viewItemsForDownloads.get(download).onStateChanged(); + onDownloadAdded(download) { + this._addDownloadData(download, null, true); }, onDownloadChanged(download) { - this._viewItemsForDownloads.get(download).onChanged(); + this._viewItemsForDownloads.get(download).onSessionDownloadChanged(); }, onDownloadRemoved(download) { @@ -1171,7 +1176,8 @@ DownloadsPlacesView.prototype = { }, _copySelectedDownloadsToClipboard() { - let urls = this._richlistbox.selectedItems.map(element => element._shell.download.source.url); + let urls = Array.map(this._richlistbox.selectedItems, + element => element._shell.download.source.url); Cc["@mozilla.org/widget/clipboardhelper;1"] .getService(Ci.nsIClipboardHelper) diff --git a/browser/components/downloads/content/downloads.js b/browser/components/downloads/content/downloads.js index 116c660bf47e..951a34682062 100644 --- a/browser/components/downloads/content/downloads.js +++ b/browser/components/downloads/content/downloads.js @@ -739,16 +739,16 @@ const DownloadsView = { /** * Called before multiple downloads are about to be loaded. */ - onDataLoadStarting() { - DownloadsCommon.log("onDataLoadStarting called for DownloadsView."); + onDownloadBatchStarting() { + DownloadsCommon.log("onDownloadBatchStarting called for DownloadsView."); this.loading = true; }, /** * Called after data loading finished. */ - onDataLoadCompleted() { - DownloadsCommon.log("onDataLoadCompleted called for DownloadsView."); + onDownloadBatchEnded() { + DownloadsCommon.log("onDownloadBatchEnded called for DownloadsView."); this.loading = false; @@ -767,33 +767,17 @@ const DownloadsView = { * * @param aDownload * Download object that was just added. - * @param aNewest - * When true, indicates that this item is the most recent and should be - * added in the topmost position. This happens when a new download is - * started. When false, indicates that the item is the least recent - * and should be appended. The latter generally happens during the - * asynchronous data load. */ - onDownloadAdded(download, aNewest) { - DownloadsCommon.log("A new download data item was added - aNewest =", - aNewest); + onDownloadAdded(download) { + DownloadsCommon.log("A new download data item was added"); - if (aNewest) { - this._downloads.unshift(download); - } else { - this._downloads.push(download); - } + this._downloads.unshift(download); - let itemsNowOverflow = this._downloads.length > this.kItemCountLimit; - if (aNewest || !itemsNowOverflow) { - // The newly added item is visible in the panel and we must add the - // corresponding element. This is either because it is the first item, or - // because it was added at the bottom but the list still doesn't overflow. - this._addViewItem(download, aNewest); - } - if (aNewest && itemsNowOverflow) { - // If the list overflows, remove the last item from the panel to make room - // for the new one that we just added at the top. + // The newly added item is visible in the panel and we must add the + // corresponding element. If the list overflows, remove the last item from + // the panel to make room for the new one that we just added at the top. + this._addViewItem(download, true); + if (this._downloads.length > this.kItemCountLimit) { this._removeViewItem(this._downloads[this.kItemCountLimit]); } @@ -804,13 +788,6 @@ const DownloadsView = { } }, - onDownloadStateChanged(download) { - let viewItem = this._visibleViewItems.get(download); - if (viewItem) { - viewItem.onStateChanged(); - } - }, - onDownloadChanged(download) { let viewItem = this._visibleViewItems.get(download); if (viewItem) { @@ -1069,6 +1046,7 @@ XPCOMUtils.defineConstant(this, "DownloadsView", DownloadsView); */ function DownloadsViewItem(download, aElement) { this.download = download; + this.downloadState = DownloadsCommon.stateOfDownload(download); this.element = aElement; this.element._shell = this; @@ -1086,11 +1064,12 @@ DownloadsViewItem.prototype = { */ _element: null, - onStateChanged() { - this._updateState(); - }, - onChanged() { + let newState = DownloadsCommon.stateOfDownload(this.download); + if (this.downloadState != newState) { + this.downloadState = newState; + this._updateState(); + } this._updateProgress(); }, diff --git a/browser/components/originattributes/test/browser/browser_firstPartyIsolation_aboutPages.js b/browser/components/originattributes/test/browser/browser_firstPartyIsolation_aboutPages.js index bb0ea8e49035..0dd6e32aa612 100644 --- a/browser/components/originattributes/test/browser/browser_firstPartyIsolation_aboutPages.js +++ b/browser/components/originattributes/test/browser/browser_firstPartyIsolation_aboutPages.js @@ -83,11 +83,8 @@ add_task(async function test_remote_window_open_data_uri() { Assert.ok(content.document.nodePrincipal.isNullPrincipal, "The principal of data: document should be a NullPrincipal."); - let str = content.document.nodePrincipal.originNoSuffix; - let expectDomain = str.substring("moz-nullprincipal:{".length, str.length - 1) + ".mozilla"; - Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain, - expectDomain, - "data: URI should have firstPartyDomain set to " + expectDomain); + Assert.ok(content.document.nodePrincipal.originAttributes.firstPartyDomain != "", + "data: URI should have firstPartyDomain set."); }); win.close(); @@ -116,14 +113,11 @@ add_task(async function test_remote_window_open_data_uri2() { Assert.ok(content.document.nodePrincipal.isNullPrincipal, "The principal of data: document should be a NullPrincipal."); - let str = content.document.nodePrincipal.originNoSuffix; - let expectDomain = str.substring("moz-nullprincipal:{".length, str.length - 1) + ".mozilla"; - Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain, - expectDomain, - "data: URI should have firstPartyDomain set to " + expectDomain); + Assert.ok(content.document.nodePrincipal.originAttributes.firstPartyDomain != "", + "data: URI should have firstPartyDomain set."); Assert.equal(iframe.contentDocument.nodePrincipal.originAttributes.firstPartyDomain, - expectDomain, + content.document.nodePrincipal.originAttributes.firstPartyDomain, "iframe should inherit firstPartyDomain from parent document."); Assert.equal(iframe.contentDocument.cookie, "test2=foo", "iframe should have cookies"); }); diff --git a/build/gyp.mozbuild b/build/gyp.mozbuild index ed4043523060..9f5cd5caf014 100644 --- a/build/gyp.mozbuild +++ b/build/gyp.mozbuild @@ -88,6 +88,8 @@ gyp_vars.update({ 'include_pcm16b': 1, #'rtc_opus_variable_complexity': 1, + + 'apm_debug_dump': 1, }) if os == 'Android': diff --git a/build/moz.configure/windows.configure b/build/moz.configure/windows.configure index 3b01154a8508..2d6beca8573f 100644 --- a/build/moz.configure/windows.configure +++ b/build/moz.configure/windows.configure @@ -363,10 +363,10 @@ set_config('LIB', lib_path) option(env='MT', nargs=1, help='Path to the Microsoft Manifest Tool') -@depends(valid_windows_sdk_dir) +@depends(valid_windows_sdk_dir, valid_ucrt_sdk_dir) @imports(_from='os', _import='environ') @imports('platform') -def sdk_bin_path(valid_windows_sdk_dir): +def sdk_bin_path(valid_windows_sdk_dir, valid_ucrt_sdk_dir): if not valid_windows_sdk_dir: return @@ -375,13 +375,16 @@ def sdk_bin_path(valid_windows_sdk_dir): 'AMD64': 'x64', }.get(platform.machine()) + # From version 10.0.15063.0 onwards the bin path contains the version number. + versioned_bin = ('bin' if valid_ucrt_sdk_dir.version < '10.0.15063.0' + else os.path.join('bin', str(valid_ucrt_sdk_dir.version))) result = [ environ['PATH'], - os.path.join(valid_windows_sdk_dir.path, 'bin', vc_host) + os.path.join(valid_windows_sdk_dir.path, versioned_bin, vc_host) ] if vc_host == 'x64': result.append( - os.path.join(valid_windows_sdk_dir.path, 'bin', 'x86')) + os.path.join(valid_windows_sdk_dir.path, versioned_bin, 'x86')) return result diff --git a/devtools/client/responsive.html/app.js b/devtools/client/responsive.html/app.js index 96c4cfbc5c45..5826a9559bfe 100644 --- a/devtools/client/responsive.html/app.js +++ b/devtools/client/responsive.html/app.js @@ -39,7 +39,6 @@ let App = createClass({ devices: PropTypes.shape(Types.devices).isRequired, dispatch: PropTypes.func.isRequired, displayPixelRatio: Types.pixelRatio.value.isRequired, - location: Types.location.isRequired, networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired, screenshot: PropTypes.shape(Types.screenshot).isRequired, touchSimulation: PropTypes.shape(Types.touchSimulation).isRequired, @@ -144,7 +143,6 @@ let App = createClass({ let { devices, displayPixelRatio, - location, networkThrottling, screenshot, touchSimulation, @@ -203,7 +201,6 @@ let App = createClass({ }), Viewports({ devices, - location, screenshot, viewports, onBrowserMounted, diff --git a/devtools/client/responsive.html/components/browser.js b/devtools/client/responsive.html/components/browser.js index 56259e177ab3..d529bbbf2fd7 100644 --- a/devtools/client/responsive.html/components/browser.js +++ b/devtools/client/responsive.html/components/browser.js @@ -12,7 +12,6 @@ const { getToplevelWindow } = require("../utils/window"); const { DOM: dom, createClass, addons, PropTypes } = require("devtools/client/shared/vendor/react"); -const Types = require("../types"); const e10s = require("../utils/e10s"); const message = require("../utils/message"); @@ -27,7 +26,6 @@ module.exports = createClass({ displayName: "Browser", propTypes: { - location: Types.location.isRequired, swapAfterMount: PropTypes.bool.isRequired, onBrowserMounted: PropTypes.func.isRequired, onContentResize: PropTypes.func.isRequired, @@ -111,13 +109,6 @@ module.exports = createClass({ }), render() { - let { - location, - } = this.props; - - // innerHTML expects & to be an HTML entity - location = location.replace(/&/g, "&"); - return dom.div( { ref: "browserContainer", @@ -140,7 +131,7 @@ module.exports = createClass({ __html: `` } } diff --git a/devtools/client/responsive.html/components/resizable-viewport.js b/devtools/client/responsive.html/components/resizable-viewport.js index da511b7771f2..14572d8870e1 100644 --- a/devtools/client/responsive.html/components/resizable-viewport.js +++ b/devtools/client/responsive.html/components/resizable-viewport.js @@ -23,7 +23,6 @@ module.exports = createClass({ propTypes: { devices: PropTypes.shape(Types.devices).isRequired, - location: Types.location.isRequired, screenshot: PropTypes.shape(Types.screenshot).isRequired, swapAfterMount: PropTypes.bool.isRequired, viewport: PropTypes.shape(Types.viewport).isRequired, @@ -126,7 +125,6 @@ module.exports = createClass({ render() { let { devices, - location, screenshot, swapAfterMount, viewport, @@ -169,7 +167,6 @@ module.exports = createClass({ }, }, Browser({ - location, swapAfterMount, onBrowserMounted, onContentResize, diff --git a/devtools/client/responsive.html/components/viewport.js b/devtools/client/responsive.html/components/viewport.js index 1c131f15d428..9bec9a664a43 100644 --- a/devtools/client/responsive.html/components/viewport.js +++ b/devtools/client/responsive.html/components/viewport.js @@ -17,7 +17,6 @@ module.exports = createClass({ propTypes: { devices: PropTypes.shape(Types.devices).isRequired, - location: Types.location.isRequired, screenshot: PropTypes.shape(Types.screenshot).isRequired, swapAfterMount: PropTypes.bool.isRequired, viewport: PropTypes.shape(Types.viewport).isRequired, @@ -69,7 +68,6 @@ module.exports = createClass({ render() { let { devices, - location, screenshot, swapAfterMount, viewport, @@ -96,7 +94,6 @@ module.exports = createClass({ }), ResizableViewport({ devices, - location, screenshot, swapAfterMount, viewport, diff --git a/devtools/client/responsive.html/components/viewports.js b/devtools/client/responsive.html/components/viewports.js index 4be604823f54..82942c214e49 100644 --- a/devtools/client/responsive.html/components/viewports.js +++ b/devtools/client/responsive.html/components/viewports.js @@ -16,7 +16,6 @@ module.exports = createClass({ propTypes: { devices: PropTypes.shape(Types.devices).isRequired, - location: Types.location.isRequired, screenshot: PropTypes.shape(Types.screenshot).isRequired, viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired, onBrowserMounted: PropTypes.func.isRequired, @@ -31,7 +30,6 @@ module.exports = createClass({ render() { let { devices, - location, screenshot, viewports, onBrowserMounted, @@ -51,7 +49,6 @@ module.exports = createClass({ return Viewport({ key: viewport.id, devices, - location, screenshot, swapAfterMount: i == 0, viewport, diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 95b0e1352dd8..bdc55331d880 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -10959,21 +10959,25 @@ nsDocShell::DoURILoad(nsIURI* aURI, MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal"); bool isSandBoxed = mSandboxFlags & SANDBOXED_ORIGIN; - // only inherit if we have a aPrincipalToInherit - bool inherit = false; + + // We want to inherit aPrincipalToInherit when: + // 1. ChannelShouldInheritPrincipal returns true. + // 2. aURI is not data: URI, or data: URI is not configured as unique opaque + // origin. + bool inheritAttrs = false, inheritPrincipal = false; if (aPrincipalToInherit) { + inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal( + aPrincipalToInherit, + aURI, + true, // aInheritForAboutBlank + isSrcdoc); + bool isData; bool isURIUniqueOrigin = nsIOService::IsDataURIUniqueOpaqueOrigin() && NS_SUCCEEDED(aURI->SchemeIs("data", &isData)) && isData; - // If aURI is data: URI and is treated as a unique opaque origin, we don't - // want to inherit principal. - inherit = nsContentUtils::ChannelShouldInheritPrincipal( - aPrincipalToInherit, - aURI, - true, // aInheritForAboutBlank - isSrcdoc) && !isURIUniqueOrigin ; + inheritPrincipal = inheritAttrs && !isURIUniqueOrigin; } nsLoadFlags loadFlags = mDefaultLoadFlags; @@ -10991,7 +10995,7 @@ nsDocShell::DoURILoad(nsIURI* aURI, securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE; } - if (inherit) { + if (inheritPrincipal) { securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL; } if (isSandBoxed) { @@ -11040,14 +11044,9 @@ nsDocShell::DoURILoad(nsIURI* aURI, OriginAttributes attrs; - // If inherit is true, which means loadInfo will have SEC_FORCE_INHERIT_PRINCIPAL - // set, so later when we create principal of the document from - // nsScriptSecurityManager::GetChannelResultPrincipal, we will use - // principalToInherit of the loadInfo as the document principal. - // Therefore we use the origin attributes from aPrincipalToInherit. - // + // Inherit origin attributes from aPrincipalToInherit if inheritAttrs is true. // Otherwise we just use the origin attributes from docshell. - if (inherit) { + if (inheritAttrs) { MOZ_ASSERT(aPrincipalToInherit, "We should have aPrincipalToInherit here."); attrs = aPrincipalToInherit->OriginAttributesRef(); // If firstPartyIsolation is not enabled, then PrincipalToInherit should diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 4cf9b623b29a..bf6a736a4ef9 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -1659,6 +1659,14 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, return NS_ERROR_NOT_IMPLEMENTED; } + bool ourPaymentRequestAllowed = + ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowpaymentrequest); + bool otherPaymentRequestAllowed = + otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowpaymentrequest); + if (ourPaymentRequestAllowed != otherPaymentRequestAllowed) { + return NS_ERROR_NOT_IMPLEMENTED; + } + // Divert to a separate path for the remaining steps in the remote case if (IsRemoteFrame()) { MOZ_ASSERT(aOther->IsRemoteFrame()); diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index c671cf071d25..f1abbaed076a 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -83,6 +83,7 @@ GK_ATOM(allowforms,"allow-forms") GK_ATOM(allowfullscreen, "allowfullscreen") GK_ATOM(allowmodals, "allow-modals") GK_ATOM(alloworientationlock,"allow-orientation-lock") +GK_ATOM(allowpaymentrequest, "allowpaymentrequest") GK_ATOM(allowpointerlock,"allow-pointer-lock") GK_ATOM(allowpopupstoescapesandbox,"allow-popups-to-escape-sandbox") GK_ATOM(allowpopups,"allow-popups") diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 2bd4db321522..1012c8d7c987 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -680,8 +680,6 @@ skip-if = (toolkit == 'android') # Android: Bug 775227 [test_innersize_scrollport.html] [test_integer_attr_with_leading_zero.html] [test_intersectionobservers.html] -[test_intersectionobserver_no_root.html] -skip-if = true # bug 1369253 [test_link_prefetch.html] skip-if = !e10s # Track Bug 1281415 [test_link_stylesheet.html] diff --git a/dom/base/test/test_intersectionobserver_no_root.html b/dom/base/test/test_intersectionobserver_no_root.html deleted file mode 100644 index d7bcb6ddd7aa..000000000000 --- a/dom/base/test/test_intersectionobserver_no_root.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - Test for Bug 1313927 - - - - -Mozilla Bug 1313927 -

-
-
-
-
-
- - diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 977fd08590e0..a8339737699a 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -5362,10 +5362,18 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage, return; } + // Per spec, the smoothing setting applies only to scaling up a bitmap image. + // When down-scaling the user agent is free to choose whether or not to smooth + // the image. Nearest sampling when down-scaling is rarely desirable and + // smoothing when down-scaling matches chromium's behavior. + // If any dimension is up-scaled, we consider the image as being up-scaled. + auto scale = mTarget->GetTransform().ScaleFactors(true); + bool isDownScale = aDw * Abs(scale.width) < aSw && aDh * Abs(scale.height) < aSh; + SamplingFilter samplingFilter; AntialiasMode antialiasMode; - if (CurrentState().imageSmoothingEnabled) { + if (CurrentState().imageSmoothingEnabled || isDownScale) { samplingFilter = gfx::SamplingFilter::LINEAR; antialiasMode = AntialiasMode::DEFAULT; } else { diff --git a/dom/canvas/WebGLContextState.cpp b/dom/canvas/WebGLContextState.cpp index 8ca1aede845f..ac5def6f7b90 100644 --- a/dom/canvas/WebGLContextState.cpp +++ b/dom/canvas/WebGLContextState.cpp @@ -508,6 +508,8 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) case LOCAL_GL_POLYGON_OFFSET_FILL: case LOCAL_GL_SCISSOR_TEST: case LOCAL_GL_SAMPLE_COVERAGE_INVERT: + case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE: + case LOCAL_GL_SAMPLE_COVERAGE: case LOCAL_GL_DEPTH_WRITEMASK: { realGLboolean b = 0; gl->fGetBooleanv(pname, &b); diff --git a/dom/canvas/WebGLFramebuffer.cpp b/dom/canvas/WebGLFramebuffer.cpp index d824399a731f..e5150d5ddd43 100644 --- a/dom/canvas/WebGLFramebuffer.cpp +++ b/dom/canvas/WebGLFramebuffer.cpp @@ -1389,8 +1389,17 @@ WebGLFramebuffer::FramebufferRenderbuffer(const char* funcName, GLenum attachEnu } // `rb` - if (rb && !mContext->ValidateObject("framebufferRenderbuffer: rb", *rb)) - return; + if (rb) { + if (!mContext->ValidateObject("framebufferRenderbuffer: rb", *rb)) + return; + + if (!rb->mHasBeenBound) { + mContext->ErrorInvalidOperation("%s: bindRenderbuffer must be called before" + " attachment to %04x", + funcName, attachEnum); + return; + } + } // End of validation. diff --git a/dom/flyweb/FlyWebServerEvents.cpp b/dom/flyweb/FlyWebServerEvents.cpp index fe774ffb0803..ea57ddb7232e 100644 --- a/dom/flyweb/FlyWebServerEvents.cpp +++ b/dom/flyweb/FlyWebServerEvents.cpp @@ -86,6 +86,8 @@ FlyWebFetchEvent::ResolvedCallback(JSContext* aCx, JS::Handle aValue) RefPtr intResponse; if (response && response->Type() != ResponseType::Opaque) { intResponse = response->GetInternalResponse(); + + response->SetBodyUsed(); } if (!intResponse) { diff --git a/dom/html/HTMLIFrameElement.cpp b/dom/html/HTMLIFrameElement.cpp index f99ea993f6c8..2f7694ca6474 100644 --- a/dom/html/HTMLIFrameElement.cpp +++ b/dom/html/HTMLIFrameElement.cpp @@ -53,6 +53,7 @@ NS_IMPL_STRING_ATTR(HTMLIFrameElement, Scrolling, scrolling) NS_IMPL_URI_ATTR(HTMLIFrameElement, Src, src) NS_IMPL_STRING_ATTR(HTMLIFrameElement, Width, width) NS_IMPL_BOOL_ATTR(HTMLIFrameElement, AllowFullscreen, allowfullscreen) +NS_IMPL_BOOL_ATTR(HTMLIFrameElement, AllowPaymentRequest, allowpaymentrequest) NS_IMPL_STRING_ATTR(HTMLIFrameElement, Srcdoc, srcdoc) NS_IMETHODIMP diff --git a/dom/html/HTMLIFrameElement.h b/dom/html/HTMLIFrameElement.h index c630d4b2359e..320ccca9564d 100644 --- a/dom/html/HTMLIFrameElement.h +++ b/dom/html/HTMLIFrameElement.h @@ -83,6 +83,14 @@ public: { SetHTMLBoolAttr(nsGkAtoms::allowfullscreen, aAllow, aError); } + bool AllowPaymentRequest() const + { + return GetBoolAttr(nsGkAtoms::allowpaymentrequest); + } + void SetAllowPaymentRequest(bool aAllow, ErrorResult& aError) + { + SetHTMLBoolAttr(nsGkAtoms::allowpaymentrequest, aAllow, aError); + } void GetWidth(DOMString& aWidth) { GetHTMLAttr(nsGkAtoms::width, aWidth); diff --git a/dom/interfaces/html/nsIDOMHTMLIFrameElement.idl b/dom/interfaces/html/nsIDOMHTMLIFrameElement.idl index 2fc1384d60a8..aa7cbae822f5 100644 --- a/dom/interfaces/html/nsIDOMHTMLIFrameElement.idl +++ b/dom/interfaces/html/nsIDOMHTMLIFrameElement.idl @@ -34,4 +34,5 @@ interface nsIDOMHTMLIFrameElement : nsISupports readonly attribute nsIDOMDocument contentDocument; attribute boolean allowFullscreen; + attribute boolean allowPaymentRequest; }; diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index f3671d110448..16697b07b6ee 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -3018,15 +3018,16 @@ TabChild::ReinitRendering() SendEnsureLayersConnected(&options); mCompositorOptions = Some(options); + bool success = false; RefPtr cb = CompositorBridgeChild::Get(); if (gfxVars::UseWebRender()) { - RefPtr lm = mPuppetWidget->RecreateLayerManager(nullptr); - MOZ_ASSERT(lm->AsWebRenderLayerManager()); - lm->AsWebRenderLayerManager()->Initialize(cb, - wr::AsPipelineId(mLayersId), - &mTextureFactoryIdentifier); + success = mPuppetWidget->RecreateLayerManager([&] (LayerManager* aLayerManager) -> bool { + MOZ_ASSERT(aLayerManager->AsWebRenderLayerManager()); + return aLayerManager->AsWebRenderLayerManager()->Initialize(cb, + wr::AsPipelineId(mLayersId), + &mTextureFactoryIdentifier); + }); } else { - bool success = false; nsTArray ignored; PLayerTransactionChild* shadowManager = cb->SendPLayerTransactionConstructor(ignored, LayersId()); if (shadowManager && @@ -3040,9 +3041,17 @@ TabChild::ReinitRendering() return; } - RefPtr lm = mPuppetWidget->RecreateLayerManager(shadowManager); - ShadowLayerForwarder* lf = lm->AsShadowForwarder(); - lf->IdentifyTextureHost(mTextureFactoryIdentifier); + success = mPuppetWidget->RecreateLayerManager([&] (LayerManager* aLayerManager) -> bool { + ShadowLayerForwarder* lf = aLayerManager->AsShadowForwarder(); + lf->SetShadowManager(shadowManager); + lf->IdentifyTextureHost(mTextureFactoryIdentifier); + return true; + }); + } + + if (!success) { + NS_WARNING("failed to recreate layer manager"); + return; } ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier); diff --git a/dom/media/CubebUtils.cpp b/dom/media/CubebUtils.cpp index 78746a434307..3db30bd0c079 100644 --- a/dom/media/CubebUtils.cpp +++ b/dom/media/CubebUtils.cpp @@ -429,7 +429,7 @@ void InitLibrary() // calling this callback on init would immediately re-disble the logging. Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LOGGING_LEVEL); #ifndef MOZ_WIDGET_ANDROID - NS_DispatchToMainThread( + AbstractThread::MainThread()->Dispatch( NS_NewRunnableFunction("CubebUtils::InitLibrary", &InitBrandName)); #endif } diff --git a/dom/media/DOMMediaStream.cpp b/dom/media/DOMMediaStream.cpp index 948507d53a79..be82e0c42d51 100644 --- a/dom/media/DOMMediaStream.cpp +++ b/dom/media/DOMMediaStream.cpp @@ -147,8 +147,9 @@ public: void Forget() { mStream = nullptr; } - void DoNotifyTrackCreated(TrackID aTrackID, MediaSegment::Type aType, - MediaStream* aInputStream, TrackID aInputTrackID) + void DoNotifyTrackCreated(MediaStreamGraph* aGraph, TrackID aTrackID, + MediaSegment::Type aType, MediaStream* aInputStream, + TrackID aInputTrackID) { MOZ_ASSERT(NS_IsMainThread()); @@ -186,15 +187,15 @@ public: RefPtr newTrack = mStream->CreateDOMTrack(aTrackID, aType, source); - NS_DispatchToMainThread(NewRunnableMethod>( + aGraph->AbstractMainThread()->Dispatch(NewRunnableMethod>( "DOMMediaStream::AddTrackInternal", mStream, &DOMMediaStream::AddTrackInternal, newTrack)); } - void DoNotifyTrackEnded(MediaStream* aInputStream, TrackID aInputTrackID, - TrackID aTrackID) + void DoNotifyTrackEnded(MediaStreamGraph* aGraph, MediaStream* aInputStream, + TrackID aInputTrackID, TrackID aTrackID) { MOZ_ASSERT(NS_IsMainThread()); @@ -208,7 +209,7 @@ public: if (track) { LOG(LogLevel::Debug, ("DOMMediaStream %p MediaStreamTrack %p ended at the source. Marking it ended.", mStream, track.get())); - NS_DispatchToMainThread( + aGraph->AbstractMainThread()->Dispatch( NewRunnableMethod("dom::MediaStreamTrack::OverrideEnded", track, &MediaStreamTrack::OverrideEnded)); @@ -223,23 +224,25 @@ public: { if (aTrackEvents & TrackEventCommand::TRACK_EVENT_CREATED) { aGraph->DispatchToMainThreadAfterStreamStateUpdate( - NewRunnableMethod, TrackID>( "DOMMediaStream::OwnedStreamListener::DoNotifyTrackCreated", this, &OwnedStreamListener::DoNotifyTrackCreated, + aGraph, aID, aQueuedMedia.GetType(), aInputStream, aInputTrackID)); } else if (aTrackEvents & TrackEventCommand::TRACK_EVENT_ENDED) { aGraph->DispatchToMainThreadAfterStreamStateUpdate( - NewRunnableMethod, TrackID, TrackID>( + NewRunnableMethod, TrackID, TrackID>( "DOMMediaStream::OwnedStreamListener::DoNotifyTrackEnded", this, &OwnedStreamListener::DoNotifyTrackEnded, + aGraph, aInputStream, aInputTrackID, aID)); @@ -279,7 +282,8 @@ public: // The owned stream listener adds its tracks after another main thread // dispatch. We have to do the same to notify of created tracks to stay // in sync. (Or NotifyTracksCreated is called before tracks are added). - NS_DispatchToMainThread( + MOZ_ASSERT(mStream->GetPlaybackStream()); + mStream->GetPlaybackStream()->Graph()->AbstractMainThread()->Dispatch( NewRunnableMethod("DOMMediaStream::NotifyTracksCreated", mStream, &DOMMediaStream::NotifyTracksCreated)); @@ -293,9 +297,10 @@ public: return; } - NS_DispatchToMainThread(NewRunnableMethod("DOMMediaStream::NotifyFinished", - mStream, - &DOMMediaStream::NotifyFinished)); + mStream->GetPlaybackStream()->Graph()->AbstractMainThread()->Dispatch( + NewRunnableMethod("DOMMediaStream::NotifyFinished", + mStream, + &DOMMediaStream::NotifyFinished)); } // The methods below are called on the MediaStreamGraph thread. diff --git a/dom/media/GraphDriver.cpp b/dom/media/GraphDriver.cpp index 3a401f343a7c..a06cb5b9f092 100644 --- a/dom/media/GraphDriver.cpp +++ b/dom/media/GraphDriver.cpp @@ -165,9 +165,10 @@ ThreadedDriver::~ThreadedDriver() if (mThread) { nsCOMPtr event = new MediaStreamGraphShutdownThreadRunnable(mThread.forget()); - NS_DispatchToMainThread(event); + GraphImpl()->Dispatch(event.forget()); } } + class MediaStreamGraphInitThreadRunnable : public Runnable { public: explicit MediaStreamGraphInitThreadRunnable(ThreadedDriver* aDriver) @@ -493,10 +494,11 @@ AsyncCubebTask::EnsureThread() // since we don't know the order that the shutdown-threads observers // will run. ClearOnShutdown guarantees it runs first. if (!NS_IsMainThread()) { - NS_DispatchToMainThread( + nsCOMPtr runnable = NS_NewRunnableFunction("AsyncCubebTask::EnsureThread", []() -> void { ClearOnShutdown(&sThreadPool, ShutdownPhase::ShutdownThreads); - })); + }); + AbstractThread::MainThread()->Dispatch(runnable.forget()); } else { ClearOnShutdown(&sThreadPool, ShutdownPhase::ShutdownThreads); } diff --git a/dom/media/webaudio/WebAudioUtils.cpp b/dom/media/webaudio/WebAudioUtils.cpp index 9bf3465b642b..81b3c475166c 100644 --- a/dom/media/webaudio/WebAudioUtils.cpp +++ b/dom/media/webaudio/WebAudioUtils.cpp @@ -103,8 +103,9 @@ WebAudioUtils::LogToDeveloperConsole(uint64_t aWindowID, const char* aKey) if (!NS_IsMainThread()) { nsCOMPtr task = NS_NewRunnableFunction( "dom::WebAudioUtils::LogToDeveloperConsole", - [aWindowID, aKey]() { LogToDeveloperConsole(aWindowID, aKey); }); - NS_DispatchToMainThread(task.forget(), NS_DISPATCH_NORMAL); + [aWindowID, aKey] { LogToDeveloperConsole(aWindowID, aKey); }); + SystemGroup::Dispatch("dom::WebAudioUtils::LogToDeveloperConsole", + TaskCategory::Other, task.forget()); return; } diff --git a/dom/payments/PaymentRequest.cpp b/dom/payments/PaymentRequest.cpp index 6e38273fbc70..a56140ac0384 100644 --- a/dom/payments/PaymentRequest.cpp +++ b/dom/payments/PaymentRequest.cpp @@ -4,6 +4,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "mozilla/dom/Element.h" #include "mozilla/dom/PaymentRequest.h" #include "mozilla/dom/PaymentResponse.h" #include "nsContentUtils.h" @@ -225,7 +226,36 @@ PaymentRequest::Constructor(const GlobalObject& aGlobal, return nullptr; } - // [TODO] Bug 1318988 - Implement `allowPaymentRequest` on iframe + // the feature can only be used in an active document + if (!window->IsCurrentInnerWindow()) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return nullptr; + } + + // If the node has the same origin as the parent node, the feature is allowed-to-use. + // Otherwise, only allow-to-use this feature when the browsing context container is + // an iframe with "allowpaymentrequest" attribute. + nsCOMPtr doc = window->GetExtantDoc(); + nsINode* node = static_cast(doc); + if (!node) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + do { + nsINode* parentNode = nsContentUtils::GetCrossDocParentNode(node); + if (parentNode) { + nsresult rv = nsContentUtils::CheckSameOrigin(node, parentNode); + if (NS_FAILED(rv)) { + nsIContent* content = static_cast(parentNode); + if (!content->IsHTMLElement(nsGkAtoms::iframe) || + !content->HasAttr(kNameSpaceID_None, nsGkAtoms::allowpaymentrequest)) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return nullptr; + } + } + } + node = parentNode; + } while (node); // Check payment methods and details nsAutoString message; diff --git a/dom/payments/test/mochitest.ini b/dom/payments/test/mochitest.ini index f31a6fd31c2f..9a55662428a3 100644 --- a/dom/payments/test/mochitest.ini +++ b/dom/payments/test/mochitest.ini @@ -3,6 +3,7 @@ skip-if = !e10s scheme = https support-files = + simple_payment_request.html CanMakePaymentChromeScript.js ConstructorChromeScript.js ShowPaymentChromeScript.js @@ -12,3 +13,4 @@ support-files = [test_constructor.html] [test_showPayment.html] [test_validate_decimal_value.html] +[test_payment-request-in-iframe.html] diff --git a/dom/payments/test/simple_payment_request.html b/dom/payments/test/simple_payment_request.html index 315f6aa587b0..4f6bbd8d7696 100644 --- a/dom/payments/test/simple_payment_request.html +++ b/dom/payments/test/simple_payment_request.html @@ -7,6 +7,7 @@ diff --git a/dom/payments/test/test_payment-request-in-iframe.html b/dom/payments/test/test_payment-request-in-iframe.html new file mode 100644 index 000000000000..5c6bab2c4389 --- /dev/null +++ b/dom/payments/test/test_payment-request-in-iframe.html @@ -0,0 +1,102 @@ + + + + + + Test for Bug 1318988 + + + + + +Mozilla Bug 1318988 +

+ +
+
+ + diff --git a/dom/url/URLSearchParams.cpp b/dom/url/URLSearchParams.cpp index 0a8ad9cf1515..a63a35f2da4b 100644 --- a/dom/url/URLSearchParams.cpp +++ b/dom/url/URLSearchParams.cpp @@ -86,20 +86,16 @@ URLParams::Set(const nsAString& aName, const nsAString& aValue) param->mValue = aValue; } -bool +void URLParams::Delete(const nsAString& aName) { - bool found = false; for (uint32_t i = 0; i < mParams.Length();) { if (mParams[i].mKey.Equals(aName)) { mParams.RemoveElementAt(i); - found = true; } else { ++i; } } - - return found; } void @@ -375,9 +371,8 @@ URLSearchParams::Has(const nsAString& aName) void URLSearchParams::Delete(const nsAString& aName) { - if (mParams->Delete(aName)) { - NotifyObserver(); - } + mParams->Delete(aName); + NotifyObserver(); } void diff --git a/dom/url/URLSearchParams.h b/dom/url/URLSearchParams.h index c2d050ec55f6..a108a3c53055 100644 --- a/dom/url/URLSearchParams.h +++ b/dom/url/URLSearchParams.h @@ -77,8 +77,7 @@ public: bool Has(const nsAString& aName); - // Returns true if aName was found and deleted, false otherwise. - bool Delete(const nsAString& aName); + void Delete(const nsAString& aName); void DeleteAll() { diff --git a/dom/xul/templates/nsRuleNetwork.h b/dom/xul/templates/nsRuleNetwork.h index 0e38f733fc4f..9d19ddfa6c08 100644 --- a/dom/xul/templates/nsRuleNetwork.h +++ b/dom/xul/templates/nsRuleNetwork.h @@ -29,6 +29,7 @@ #define nsRuleNetwork_h__ #include "mozilla/Attributes.h" +#include "mozilla/HashFunctions.h" #include "nsCOMPtr.h" #include "nsCOMArray.h" #include "nsIAtom.h" @@ -208,9 +209,8 @@ public: return mVariable != aAssignment.mVariable || mValue != aAssignment.mValue; } PLHashNumber Hash() const { - // XXX I have no idea if this hashing function is good or not // XXX change this - PLHashNumber temp = PLHashNumber(NS_PTR_TO_INT32(mValue.get())) >> 2; // strip alignment bits - return (temp & 0xffff) | NS_PTR_TO_INT32(mVariable.get()); } + using mozilla::HashGeneric; + return HashGeneric(mVariable.get()) ^ HashGeneric(mValue.get()); } }; diff --git a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic index a282f038a690..26cd7353add7 100644 --- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic +++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic @@ -1,4 +1,4 @@ -52354 +52356 0/nm 0th/pt 1/n1 @@ -20021,6 +20021,7 @@ combined/U combiner/MS combings/M combo/SM +combust/DS combustibility/M combustible/MS combustion/M @@ -50702,6 +50703,7 @@ vicissitude/SM victim/MS victimization/M victimize/GDS +victimless victor/MS victorious/Y victory/SM diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index fb561b752c2d..5ee7755b85aa 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -27,6 +27,8 @@ // solution. #include "mozilla/RefPtr.h" #include "mozilla/ServoUtils.h" +#include "mozilla/StaticMutex.h" +#include "mozilla/StaticPtr.h" #include "mozilla/WeakPtr.h" #include "mozilla/DebugOnly.h" @@ -1662,11 +1664,12 @@ public: * Returns true on success, or false on failure and leaves the D2D1/Direct3D11 devices unset. */ static bool SetDirect3D11Device(ID3D11Device *aDevice); - static ID3D11Device *GetDirect3D11Device(); - static ID2D1Device *GetD2D1Device(); - static uint32_t GetD2D1DeviceSeq(); - static IDWriteFactory *GetDWriteFactory(); - static IDWriteFactory* EnsureDWriteFactory(); + static RefPtr GetDirect3D11Device(); + static RefPtr GetD2D1Device(uint32_t* aOutSeqNo = nullptr); + static bool HasD2D1Device(); + static RefPtr GetDWriteFactory(); + static bool SetDWriteFactory(IDWriteFactory *aFactory); + static RefPtr EnsureDWriteFactory(); static bool SupportsD2D1(); static uint64_t GetD2DVRAMUsageDrawTarget(); @@ -1687,13 +1690,20 @@ public: static void UpdateSystemTextQuality(); private: - static ID2D1Device *mD2D1Device; - static ID3D11Device *mD3D11Device; - static IDWriteFactory *mDWriteFactory; + static StaticRefPtr mD2D1Device; + static StaticRefPtr mD3D11Device; + static StaticRefPtr mDWriteFactory; static bool mDWriteFactoryInitialized; - static Mutex* mDWriteFactoryLock; + +protected: + // This guards access to the singleton devices above, as well as the + // singleton devices in DrawTargetD2D1. + static StaticMutex mDeviceLock; + + friend class DrawTargetD2D1; #endif +private: static DrawEventRecorder *mRecorder; }; diff --git a/gfx/2d/DrawTargetD2D1.cpp b/gfx/2d/DrawTargetD2D1.cpp index d099b652f707..a74fdbef14fc 100644 --- a/gfx/2d/DrawTargetD2D1.cpp +++ b/gfx/2d/DrawTargetD2D1.cpp @@ -16,6 +16,7 @@ #include "ExtendInputEffectD2D1.h" #include "Tools.h" #include "nsAppRunner.h" +#include "MainThreadUtils.h" #include "mozilla/Mutex.h" @@ -34,9 +35,9 @@ namespace gfx { uint64_t DrawTargetD2D1::mVRAMUsageDT; uint64_t DrawTargetD2D1::mVRAMUsageSS; -ID2D1Factory1* DrawTargetD2D1::mFactory = nullptr; +StaticRefPtr DrawTargetD2D1::mFactory; -ID2D1Factory1 *D2DFactory1() +RefPtr D2DFactory() { return DrawTargetD2D1::factory(); } @@ -1037,7 +1038,8 @@ DrawTargetD2D1::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, return nullptr; } - return MakeAndAddRef(stopCollection, Factory::GetDirect3D11Device()); + RefPtr device = Factory::GetDirect3D11Device(); + return MakeAndAddRef(stopCollection, device); } already_AddRefed @@ -1069,12 +1071,10 @@ DrawTargetD2D1::Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat) { HRESULT hr; - ID2D1Device* device = Factory::GetD2D1Device(); + RefPtr device = Factory::GetD2D1Device(&mDeviceSeq); if (!device) { gfxCriticalNote << "[D2D1.1] Failed to obtain a device for DrawTargetD2D1::Init(ID3D11Texture2D*, SurfaceFormat)."; return false; - } else { - mDeviceSeq = Factory::GetD2D1DeviceSeq(); } hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, getter_AddRefs(mDC)); @@ -1136,12 +1136,10 @@ DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat) { HRESULT hr; - ID2D1Device* device = Factory::GetD2D1Device(); + RefPtr device = Factory::GetD2D1Device(&mDeviceSeq); if (!device) { gfxCriticalNote << "[D2D1.1] Failed to obtain a device for DrawTargetD2D1::Init(IntSize, SurfaceFormat)."; return false; - } else { - mDeviceSeq = Factory::GetD2D1DeviceSeq(); } hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, getter_AddRefs(mDC)); @@ -1201,13 +1199,18 @@ DrawTargetD2D1::GetByteSize() const return mSize.width * mSize.height * BytesPerPixel(mFormat); } -ID2D1Factory1* +RefPtr DrawTargetD2D1::factory() { - if (mFactory) { + StaticMutexAutoLock lock(Factory::mDeviceLock); + + if (mFactory || !NS_IsMainThread()) { return mFactory; } + // We don't allow initializing the factory off the main thread. + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + RefPtr factory; D2D1CreateFactoryFunc createD2DFactory; HMODULE d2dModule = LoadLibraryW(L"d2d1.dll"); @@ -1237,11 +1240,14 @@ DrawTargetD2D1::factory() return nullptr; } - hr = factory->QueryInterface((ID2D1Factory1**)&mFactory); - if (FAILED(hr) || !mFactory) { + RefPtr factory1; + hr = factory->QueryInterface(__uuidof(ID2D1Factory1), getter_AddRefs(factory1)); + if (FAILED(hr) || !factory1) { return nullptr; } + mFactory = factory1; + ExtendInputEffectD2D1::Register(mFactory); RadialGradientEffectD2D1::Register(mFactory); @@ -1251,10 +1257,12 @@ DrawTargetD2D1::factory() void DrawTargetD2D1::CleanupD2D() { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + Factory::mDeviceLock.AssertCurrentThreadOwns(); + if (mFactory) { RadialGradientEffectD2D1::Unregister(mFactory); ExtendInputEffectD2D1::Unregister(mFactory); - mFactory->Release(); mFactory = nullptr; } } @@ -1997,7 +2005,8 @@ DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, bool DrawTargetD2D1::IsDeviceContextValid() { - return (mDeviceSeq == Factory::GetD2D1DeviceSeq()) && Factory::GetD2D1Device(); + uint32_t seqNo; + return Factory::GetD2D1Device(&seqNo) && seqNo == mDeviceSeq; } } diff --git a/gfx/2d/DrawTargetD2D1.h b/gfx/2d/DrawTargetD2D1.h index da6e8f28d52c..3d9ac21cba29 100644 --- a/gfx/2d/DrawTargetD2D1.h +++ b/gfx/2d/DrawTargetD2D1.h @@ -11,6 +11,7 @@ #include #include "PathD2D.h" #include "HelpersD2D.h" +#include "mozilla/StaticPtr.h" #include #include @@ -153,7 +154,7 @@ public: return GetImageForSurface(aSurface, mat, aExtendMode, nullptr); } - static ID2D1Factory1 *factory(); + static RefPtr factory(); static void CleanupD2D(); operator std::string() const { @@ -292,7 +293,7 @@ private: // we can avoid the subsequent hang. (See bug 1293586) bool mDidComplexBlendWithListInList; - static ID2D1Factory1 *mFactory; + static StaticRefPtr mFactory; // This value is uesed to verify if the DrawTarget is created by a stale device. uint32_t mDeviceSeq; diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp index 72087b6a0195..a22e4b383e35 100644 --- a/gfx/2d/Factory.cpp +++ b/gfx/2d/Factory.cpp @@ -67,6 +67,7 @@ #include "mozilla/Mutex.h" #endif +#include "MainThreadUtils.h" #if defined(MOZ_LOGGING) GFX2D_API mozilla::LogModule* @@ -200,12 +201,13 @@ Mutex* Factory::mFTLock = nullptr; #endif #ifdef WIN32 +// Note: mDeviceLock must be held when mutating these values. static uint32_t mDeviceSeq = 0; -ID3D11Device *Factory::mD3D11Device = nullptr; -ID2D1Device *Factory::mD2D1Device = nullptr; -IDWriteFactory *Factory::mDWriteFactory = nullptr; +StaticRefPtr Factory::mD3D11Device; +StaticRefPtr Factory::mD2D1Device; +StaticRefPtr Factory::mDWriteFactory; bool Factory::mDWriteFactoryInitialized = false; -Mutex* Factory::mDWriteFactoryLock = nullptr; +StaticMutex Factory::mDeviceLock; #endif DrawEventRecorder *Factory::mRecorder; @@ -221,10 +223,6 @@ Factory::Init(const Config& aConfig) #ifdef MOZ_ENABLE_FREETYPE mFTLock = new Mutex("Factory::mFTLock"); #endif - -#ifdef WIN32 - mDWriteFactoryLock = new Mutex("Factory::mDWriteFactoryLock"); -#endif } void @@ -243,13 +241,6 @@ Factory::ShutDown() mFTLock = nullptr; } #endif - -#ifdef WIN32 - if (mDWriteFactoryLock) { - delete mDWriteFactoryLock; - mDWriteFactoryLock = nullptr; - } -#endif } bool @@ -750,10 +741,17 @@ Factory::CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceForma bool Factory::SetDirect3D11Device(ID3D11Device *aDevice) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + // D2DFactory already takes the device lock, so we get the factory before + // entering the lock scope. + RefPtr factory = D2DFactory(); + + StaticMutexAutoLock lock(mDeviceLock); + mD3D11Device = aDevice; if (mD2D1Device) { - mD2D1Device->Release(); mD2D1Device = nullptr; } @@ -761,52 +759,57 @@ Factory::SetDirect3D11Device(ID3D11Device *aDevice) return true; } - RefPtr factory = D2DFactory1(); - RefPtr device; aDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(device)); - HRESULT hr = factory->CreateDevice(device, &mD2D1Device); + + RefPtr d2dDevice; + HRESULT hr = factory->CreateDevice(device, getter_AddRefs(d2dDevice)); if (FAILED(hr)) { gfxCriticalError() << "[D2D1] Failed to create gfx factory's D2D1 device, code: " << hexa(hr); mD3D11Device = nullptr; return false; - } else { - mDeviceSeq++; } + mDeviceSeq++; + mD2D1Device = d2dDevice; return true; } -ID3D11Device* +RefPtr Factory::GetDirect3D11Device() { + StaticMutexAutoLock lock(mDeviceLock); return mD3D11Device; } -ID2D1Device* -Factory::GetD2D1Device() +RefPtr +Factory::GetD2D1Device(uint32_t* aOutSeqNo) { - return mD2D1Device; + StaticMutexAutoLock lock(mDeviceLock); + if (aOutSeqNo) { + *aOutSeqNo = mDeviceSeq; + } + return mD2D1Device.get(); } -uint32_t -Factory::GetD2D1DeviceSeq() +bool +Factory::HasD2D1Device() { - return mDeviceSeq; + return !!GetD2D1Device(); } -IDWriteFactory* +RefPtr Factory::GetDWriteFactory() { + StaticMutexAutoLock lock(mDeviceLock); return mDWriteFactory; } -IDWriteFactory* +RefPtr Factory::EnsureDWriteFactory() { - MOZ_ASSERT(mDWriteFactoryLock); - MutexAutoLock lock(*mDWriteFactoryLock); + StaticMutexAutoLock lock(mDeviceLock); if (mDWriteFactoryInitialized) { return mDWriteFactory; @@ -836,7 +839,7 @@ Factory::EnsureDWriteFactory() bool Factory::SupportsD2D1() { - return !!D2DFactory1(); + return !!D2DFactory(); } BYTE sSystemTextQuality = CLEARTYPE_QUALITY; @@ -863,8 +866,8 @@ Factory::GetD2DVRAMUsageSourceSurface() void Factory::D2DCleanup() { + StaticMutexAutoLock lock(mDeviceLock); if (mD2D1Device) { - mD2D1Device->Release(); mD2D1Device = nullptr; } DrawTargetD2D1::CleanupD2D(); diff --git a/gfx/2d/HelpersD2D.h b/gfx/2d/HelpersD2D.h index 82f12abfcbd7..fe4c08a44358 100644 --- a/gfx/2d/HelpersD2D.h +++ b/gfx/2d/HelpersD2D.h @@ -25,8 +25,7 @@ namespace mozilla { namespace gfx { -ID2D1Factory1* D2DFactory1(); -static ID2D1Factory* D2DFactory() { return D2DFactory1(); } +RefPtr D2DFactory(); static inline D2D1_POINT_2F D2DPoint(const Point &aPoint) { diff --git a/gfx/2d/NativeFontResourceDWrite.cpp b/gfx/2d/NativeFontResourceDWrite.cpp index 4b934c23d8aa..9e661bcec19c 100644 --- a/gfx/2d/NativeFontResourceDWrite.cpp +++ b/gfx/2d/NativeFontResourceDWrite.cpp @@ -221,7 +221,7 @@ already_AddRefed NativeFontResourceDWrite::Create(uint8_t *aFontData, uint32_t aDataLength, bool aNeedsCairo) { - IDWriteFactory *factory = Factory::EnsureDWriteFactory(); + RefPtr factory = Factory::GetDWriteFactory(); if (!factory) { gfxWarning() << "Failed to get DWrite Factory."; return nullptr; diff --git a/gfx/2d/ScaledFontDWrite.cpp b/gfx/2d/ScaledFontDWrite.cpp index ed4b5d59dc6a..7d2e6901efde 100644 --- a/gfx/2d/ScaledFontDWrite.cpp +++ b/gfx/2d/ScaledFontDWrite.cpp @@ -151,7 +151,7 @@ SkTypeface* ScaledFontDWrite::GetSkTypeface() { if (!mTypeface) { - IDWriteFactory *factory = Factory::GetDWriteFactory(); + RefPtr factory = Factory::GetDWriteFactory(); if (!factory) { return nullptr; } diff --git a/gfx/2d/SourceSurfaceD2D1.cpp b/gfx/2d/SourceSurfaceD2D1.cpp index 92b2e4d1d267..cdfb4784be53 100644 --- a/gfx/2d/SourceSurfaceD2D1.cpp +++ b/gfx/2d/SourceSurfaceD2D1.cpp @@ -80,7 +80,7 @@ SourceSurfaceD2D1::EnsureRealizedBitmap() } // Why aren't we using mDevice here or anywhere else? - ID2D1Device* device = Factory::GetD2D1Device(); + RefPtr device = Factory::GetD2D1Device(); if (!device) { return false; } diff --git a/gfx/ipc/GPUProcessManager.cpp b/gfx/ipc/GPUProcessManager.cpp index a7aad1e27626..a19dc161a44e 100644 --- a/gfx/ipc/GPUProcessManager.cpp +++ b/gfx/ipc/GPUProcessManager.cpp @@ -38,6 +38,7 @@ #include "mozilla/dom/VideoDecoderManagerChild.h" #include "mozilla/dom/VideoDecoderManagerParent.h" #include "MediaPrefs.h" +#include "nsPrintfCString.h" #ifdef MOZ_CRASHREPORTER # include "nsExceptionHandler.h" @@ -366,6 +367,10 @@ GPUProcessManager::OnProcessLaunchComplete(GPUProcessHost* aHost) CrashReporter::AnnotateCrashReport( NS_LITERAL_CSTRING("GPUProcessStatus"), NS_LITERAL_CSTRING("Running")); + + CrashReporter::AnnotateCrashReport( + NS_LITERAL_CSTRING("GPUProcessLaunchCount"), + nsPrintfCString("%d", mNumProcessAttempts)); #endif } diff --git a/gfx/layers/client/ImageClient.cpp b/gfx/layers/client/ImageClient.cpp index 2d550222d749..2723edea0ca8 100644 --- a/gfx/layers/client/ImageClient.cpp +++ b/gfx/layers/client/ImageClient.cpp @@ -84,8 +84,6 @@ TextureInfo ImageClientSingle::GetTextureInfo() const void ImageClientSingle::FlushAllImages() { - MOZ_ASSERT(GetForwarder()->GetTextureForwarder()->UsesImageBridge()); - for (auto& b : mBuffers) { RemoveTexture(b.mTextureClient); } diff --git a/gfx/layers/wr/WebRenderBridgeChild.cpp b/gfx/layers/wr/WebRenderBridgeChild.cpp index 8ee419ce79c7..b6822d69b233 100644 --- a/gfx/layers/wr/WebRenderBridgeChild.cpp +++ b/gfx/layers/wr/WebRenderBridgeChild.cpp @@ -22,6 +22,7 @@ using namespace mozilla::gfx; WebRenderBridgeChild::WebRenderBridgeChild(const wr::PipelineId& aPipelineId) : mReadLockSequenceNumber(0) , mIsInTransaction(false) + , mIsInClearCachedResources(false) , mIdNamespace(0) , mResourceId(0) , mPipelineId(aPipelineId) @@ -57,7 +58,7 @@ WebRenderBridgeChild::ActorDestroy(ActorDestroyReason why) void WebRenderBridgeChild::AddWebRenderParentCommand(const WebRenderParentCommand& aCmd) { - MOZ_ASSERT(mIsInTransaction); + MOZ_ASSERT(mIsInTransaction || mIsInClearCachedResources); mParentCommands.AppendElement(aCmd); } @@ -460,5 +461,19 @@ WebRenderBridgeChild::RecvWrUpdated(const uint32_t& aNewIdNameSpace) return IPC_OK(); } +void +WebRenderBridgeChild::BeginClearCachedResources() +{ + mIsInClearCachedResources = true; + SendClearCachedResources(); +} + +void +WebRenderBridgeChild::EndClearCachedResources() +{ + ProcessWebRenderParentCommands(); + mIsInClearCachedResources = false; +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/wr/WebRenderBridgeChild.h b/gfx/layers/wr/WebRenderBridgeChild.h index a965ad5cd45a..c067541f94bb 100644 --- a/gfx/layers/wr/WebRenderBridgeChild.h +++ b/gfx/layers/wr/WebRenderBridgeChild.h @@ -110,6 +110,9 @@ public: void RemoveExpiredFontKeys(); void ClearReadLocks(); + void BeginClearCachedResources(); + void EndClearCachedResources(); + private: friend class CompositorBridgeChild; @@ -162,6 +165,7 @@ private: nsTArray> mReadLocks; uint64_t mReadLockSequenceNumber; bool mIsInTransaction; + bool mIsInClearCachedResources; uint32_t mIdNamespace; uint32_t mResourceId; wr::PipelineId mPipelineId; diff --git a/gfx/layers/wr/WebRenderCanvasLayer.cpp b/gfx/layers/wr/WebRenderCanvasLayer.cpp index 90fb309d3c1a..98a9166f0261 100644 --- a/gfx/layers/wr/WebRenderCanvasLayer.cpp +++ b/gfx/layers/wr/WebRenderCanvasLayer.cpp @@ -27,9 +27,15 @@ namespace layers { WebRenderCanvasLayer::~WebRenderCanvasLayer() { MOZ_COUNT_DTOR(WebRenderCanvasLayer); + ClearWrResources(); +} +void +WebRenderCanvasLayer::ClearWrResources() +{ if (mExternalImageId.isSome()) { WrBridge()->DeallocExternalImageId(mExternalImageId.ref()); + mExternalImageId = Nothing(); } } @@ -97,5 +103,17 @@ WebRenderCanvasLayer::GetForwarder() return WrManager()->WrBridge(); } +void +WebRenderCanvasLayer::ClearCachedResources() +{ + ClearWrResources(); + if (mBufferProvider) { + mBufferProvider->ClearCachedResources(); + } + if (mCanvasClient) { + mCanvasClient->Clear(); + } +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/wr/WebRenderCanvasLayer.h b/gfx/layers/wr/WebRenderCanvasLayer.h index 4d0bf83e5f1a..68c97e420281 100644 --- a/gfx/layers/wr/WebRenderCanvasLayer.h +++ b/gfx/layers/wr/WebRenderCanvasLayer.h @@ -33,9 +33,13 @@ public: virtual void AttachCompositable() override; + virtual void ClearCachedResources() override; + protected: virtual ~WebRenderCanvasLayer(); + void ClearWrResources(); + public: Layer* GetLayer() override { return this; } void RenderLayer(wr::DisplayListBuilder& aBuilder, diff --git a/gfx/layers/wr/WebRenderImageLayer.cpp b/gfx/layers/wr/WebRenderImageLayer.cpp index e3c73169b051..20cbabe40463 100644 --- a/gfx/layers/wr/WebRenderImageLayer.cpp +++ b/gfx/layers/wr/WebRenderImageLayer.cpp @@ -32,16 +32,23 @@ WebRenderImageLayer::WebRenderImageLayer(WebRenderLayerManager* aLayerManager) WebRenderImageLayer::~WebRenderImageLayer() { MOZ_COUNT_DTOR(WebRenderImageLayer); + ClearWrResources(); +} +void +WebRenderImageLayer::ClearWrResources() +{ if (mKey.isSome()) { WrManager()->AddImageKeyForDiscard(mKey.value()); + mKey = Nothing(); } - if (mExternalImageId.isSome()) { WrBridge()->DeallocExternalImageId(mExternalImageId.ref()); + mExternalImageId = Nothing(); } if (mPipelineId.isSome()) { WrBridge()->RemovePipelineIdForAsyncCompositable(mPipelineId.ref()); + mPipelineId = Nothing(); } } @@ -85,6 +92,7 @@ WebRenderImageLayer::GetAsSourceSurface() void WebRenderImageLayer::ClearCachedResources() { + ClearWrResources(); if (mImageClient) { mImageClient->ClearCachedResources(); } diff --git a/gfx/layers/wr/WebRenderImageLayer.h b/gfx/layers/wr/WebRenderImageLayer.h index c4b24ce30a0a..2d46a35666c3 100644 --- a/gfx/layers/wr/WebRenderImageLayer.h +++ b/gfx/layers/wr/WebRenderImageLayer.h @@ -38,6 +38,7 @@ public: protected: CompositableType GetImageClientType(); + void ClearWrResources(); void AddWRVideoImage(size_t aChannelNumber); diff --git a/gfx/layers/wr/WebRenderLayerManager.cpp b/gfx/layers/wr/WebRenderLayerManager.cpp index d1ed97bfecc7..8444be0cf506 100644 --- a/gfx/layers/wr/WebRenderLayerManager.cpp +++ b/gfx/layers/wr/WebRenderLayerManager.cpp @@ -47,7 +47,7 @@ WebRenderLayerManager::AsKnowsCompositor() return mWrChild; } -void +bool WebRenderLayerManager::Initialize(PCompositorBridgeChild* aCBChild, wr::PipelineId aLayersId, TextureFactoryIdentifier* aTextureFactoryIdentifier) @@ -62,12 +62,21 @@ WebRenderLayerManager::Initialize(PCompositorBridgeChild* aCBChild, size, &textureFactoryIdentifier, &id_namespace); - MOZ_ASSERT(bridge); + if (!bridge) { + // This should only fail if we attempt to access a layer we don't have + // permission for, or more likely, the GPU process crashed again during + // reinitialization. We can expect to be notified again to reinitialize + // (which may or may not be using WebRender). + gfxCriticalNote << "Failed to create WebRenderBridgeChild."; + return false; + } + mWrChild = static_cast(bridge); WrBridge()->SendCreate(size.ToUnknownSize()); WrBridge()->IdentifyTextureHost(textureFactoryIdentifier); WrBridge()->SetNamespace(id_namespace); *aTextureFactoryIdentifier = textureFactoryIdentifier; + return true; } void @@ -86,9 +95,12 @@ WebRenderLayerManager::DoDestroy(bool aIsSync) mWidget->CleanupWebRenderWindowOverlay(WrBridge()); LayerManager::Destroy(); - DiscardImages(); - DiscardCompositorAnimations(); - WrBridge()->Destroy(aIsSync); + + if (WrBridge()) { + DiscardImages(); + DiscardCompositorAnimations(); + WrBridge()->Destroy(aIsSync); + } if (mTransactionIdAllocator) { // Make sure to notify the refresh driver just in case it's waiting on a @@ -698,7 +710,9 @@ WebRenderLayerManager::Hold(Layer* aLayer) void WebRenderLayerManager::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch) { - WrBridge()->SendSetLayerObserverEpoch(aLayerObserverEpoch); + if (WrBridge()->IPCOpen()) { + WrBridge()->SendSetLayerObserverEpoch(aLayerObserverEpoch); + } } void @@ -732,6 +746,9 @@ void WebRenderLayerManager::ClearLayer(Layer* aLayer) { aLayer->ClearCachedResources(); + if (aLayer->GetMaskLayer()) { + aLayer->GetMaskLayer()->ClearCachedResources(); + } for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { ClearLayer(child); @@ -741,12 +758,13 @@ WebRenderLayerManager::ClearLayer(Layer* aLayer) void WebRenderLayerManager::ClearCachedResources(Layer* aSubtree) { - WrBridge()->SendClearCachedResources(); + WrBridge()->BeginClearCachedResources(); if (aSubtree) { ClearLayer(aSubtree); } else if (mRoot) { ClearLayer(mRoot); } + WrBridge()->EndClearCachedResources(); } void diff --git a/gfx/layers/wr/WebRenderLayerManager.h b/gfx/layers/wr/WebRenderLayerManager.h index 03ed79e5331f..868243a00583 100644 --- a/gfx/layers/wr/WebRenderLayerManager.h +++ b/gfx/layers/wr/WebRenderLayerManager.h @@ -35,7 +35,7 @@ class WebRenderLayerManager final : public LayerManager public: explicit WebRenderLayerManager(nsIWidget* aWidget); - void Initialize(PCompositorBridgeChild* aCBChild, wr::PipelineId aLayersId, TextureFactoryIdentifier* aTextureFactoryIdentifier); + bool Initialize(PCompositorBridgeChild* aCBChild, wr::PipelineId aLayersId, TextureFactoryIdentifier* aTextureFactoryIdentifier); virtual void Destroy() override; diff --git a/gfx/layers/wr/WebRenderPaintedLayer.cpp b/gfx/layers/wr/WebRenderPaintedLayer.cpp index a32b66c271fd..a0930d9045a2 100644 --- a/gfx/layers/wr/WebRenderPaintedLayer.cpp +++ b/gfx/layers/wr/WebRenderPaintedLayer.cpp @@ -163,5 +163,29 @@ WebRenderPaintedLayer::RenderLayer(wr::DisplayListBuilder& aBuilder, CreateWebRenderDisplayList(aBuilder, aSc); } +void +WebRenderPaintedLayer::ClearCachedResources() +{ + ClearWrResources(); + if (mImageClient) { + mImageClient->FlushAllImages(); + mImageClient->ClearCachedResources(); + } + if (mImageContainer) { + mImageContainer->ClearAllImages(); + mImageContainer->ClearCachedResources(); + } + ClearValidRegion(); +} + +void +WebRenderPaintedLayer::ClearWrResources() +{ + if (mExternalImageId.isSome()) { + WrBridge()->DeallocExternalImageId(mExternalImageId.ref()); + mExternalImageId = Nothing(); + } +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/wr/WebRenderPaintedLayer.h b/gfx/layers/wr/WebRenderPaintedLayer.h index eba6c44208b4..d2edaebe4df9 100644 --- a/gfx/layers/wr/WebRenderPaintedLayer.h +++ b/gfx/layers/wr/WebRenderPaintedLayer.h @@ -32,9 +32,7 @@ protected: virtual ~WebRenderPaintedLayer() { MOZ_COUNT_DTOR(WebRenderPaintedLayer); - if (mExternalImageId.isSome()) { - WrBridge()->DeallocExternalImageId(mExternalImageId.ref()); - } + ClearWrResources(); } wr::MaybeExternalImageId mExternalImageId; @@ -49,6 +47,8 @@ public: Layer* GetLayer() override { return this; } void RenderLayer(wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc) override; + virtual void ClearCachedResources() override; + RefPtr mImageContainer; RefPtr mImageClient; @@ -57,6 +57,7 @@ private: bool UpdateImageClient(); void CreateWebRenderDisplayList(wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc); + void ClearWrResources(); }; } // namespace layers diff --git a/gfx/layers/wr/WebRenderPaintedLayerBlob.h b/gfx/layers/wr/WebRenderPaintedLayerBlob.h index 747707edfa18..793bb349d4b0 100644 --- a/gfx/layers/wr/WebRenderPaintedLayerBlob.h +++ b/gfx/layers/wr/WebRenderPaintedLayerBlob.h @@ -32,11 +32,17 @@ protected: virtual ~WebRenderPaintedLayerBlob() { MOZ_COUNT_DTOR(WebRenderPaintedLayerBlob); + ClearWrResources(); + } + void ClearWrResources() + { if (mExternalImageId.isSome()) { WrBridge()->DeallocExternalImageId(mExternalImageId.ref()); + mExternalImageId = Nothing(); } if (mImageKey.isSome()) { WrManager()->AddImageKeyForDiscard(mImageKey.value()); + mImageKey = Nothing(); } } @@ -48,7 +54,10 @@ public: mInvalidRegion.Add(aRegion); UpdateValidRegionAfterInvalidRegionChanged(); } - + virtual void ClearCachedResources() override + { + ClearWrResources(); + } Layer* GetLayer() override { return this; } void RenderLayer(wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc) override; diff --git a/gfx/thebes/DeviceManagerDx.cpp b/gfx/thebes/DeviceManagerDx.cpp index e34a3fb71d02..69cd75a1783b 100644 --- a/gfx/thebes/DeviceManagerDx.cpp +++ b/gfx/thebes/DeviceManagerDx.cpp @@ -15,9 +15,11 @@ #include "mozilla/gfx/GPUParent.h" #include "mozilla/gfx/GraphicsMessages.h" #include "mozilla/gfx/Logging.h" +#include "mozilla/layers/CompositorBridgeChild.h" #include "mozilla/layers/CompositorThread.h" #include "mozilla/layers/DeviceAttachmentsD3D11.h" #include "mozilla/layers/MLGDeviceD3D11.h" +#include "mozilla/layers/PaintThread.h" #include "nsIGfxInfo.h" #include "nsString.h" #include @@ -701,6 +703,14 @@ DeviceManagerDx::CreateMLGDevice() void DeviceManagerDx::ResetDevices() { + // Flush the paint thread before revoking all these singletons. This + // should ensure that the paint thread doesn't start mixing and matching + // old and new objects together. + if (PaintThread::Get()) { + CompositorBridgeChild* cbc = CompositorBridgeChild::Get(); + cbc->FlushAsyncPaints(); + } + MutexAutoLock lock(mDeviceLock); mAdapter = nullptr; diff --git a/gfx/thebes/gfxDWriteCommon.cpp b/gfx/thebes/gfxDWriteCommon.cpp index 7d4db7582da4..1852a9c66c42 100644 --- a/gfx/thebes/gfxDWriteCommon.cpp +++ b/gfx/thebes/gfxDWriteCommon.cpp @@ -158,7 +158,7 @@ gfxDWriteFontFileLoader::CreateCustomFontFile(FallibleTArray& aFontData MOZ_ASSERT(aFontFile); MOZ_ASSERT(aFontFileStream); - IDWriteFactory *factory = mozilla::gfx::Factory::GetDWriteFactory(); + RefPtr factory = mozilla::gfx::Factory::GetDWriteFactory(); if (!factory) { gfxCriticalError() << "Failed to get DWrite Factory in CreateCustomFontFile."; return E_FAIL; diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 490641c8b3d6..f10ea9a02fa7 100755 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -59,6 +59,7 @@ #include #include +#include #include "nsIMemoryReporter.h" #include @@ -148,14 +149,13 @@ class GPUAdapterReporter final : public nsIMemoryReporter // Callers must Release the DXGIAdapter after use or risk mem-leak static bool GetDXGIAdapter(IDXGIAdapter **aDXGIAdapter) { - ID3D11Device *d3d11Device; - IDXGIDevice *dxgiDevice; + RefPtr d3d11Device; + RefPtr dxgiDevice; bool result = false; if ((d3d11Device = mozilla::gfx::Factory::GetDirect3D11Device())) { - if (d3d11Device->QueryInterface(__uuidof(IDXGIDevice), (void **)&dxgiDevice) == S_OK) { + if (d3d11Device->QueryInterface(__uuidof(IDXGIDevice), getter_AddRefs(dxgiDevice)) == S_OK) { result = (dxgiDevice->GetAdapter(aDXGIAdapter) == S_OK); - dxgiDevice->Release(); } } @@ -430,7 +430,7 @@ gfxWindowsPlatform::UpdateBackendPrefs() uint32_t contentMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA); BackendType defaultBackend = BackendType::SKIA; - if (gfxConfig::IsEnabled(Feature::DIRECT2D) && Factory::GetD2D1Device()) { + if (gfxConfig::IsEnabled(Feature::DIRECT2D) && Factory::HasD2D1Device()) { contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1); canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1); defaultBackend = BackendType::DIRECT2D1_1; @@ -456,9 +456,9 @@ gfxWindowsPlatform::UpdateRenderMode() CreateOffscreenContentDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8); if (!mScreenReferenceDrawTarget) { gfxCriticalNote << "Failed to update reference draw target after device reset" - << ", D3D11 device:" << hexa(Factory::GetDirect3D11Device()) + << ", D3D11 device:" << hexa(Factory::GetDirect3D11Device().get()) << ", D3D11 status:" << FeatureStatusToString(gfxConfig::GetValue(Feature::D3D11_COMPOSITING)) - << ", D2D1 device:" << hexa(Factory::GetD2D1Device()) + << ", D2D1 device:" << hexa(Factory::GetD2D1Device().get()) << ", D2D1 status:" << FeatureStatusToString(gfxConfig::GetValue(Feature::DIRECT2D)) << ", content:" << int(GetDefaultContentBackend()) << ", compositor:" << int(GetCompositorBackend()); diff --git a/gfx/thebes/gfxXlibSurface.cpp b/gfx/thebes/gfxXlibSurface.cpp index 12891c4549d5..bf330d7cd81c 100644 --- a/gfx/thebes/gfxXlibSurface.cpp +++ b/gfx/thebes/gfxXlibSurface.cpp @@ -11,6 +11,7 @@ #include /* For XESetCloseDisplay */ #undef max // Xlibint.h defines this and it breaks std::max #undef min // Xlibint.h defines this and it breaks std::min +#undef Data #include "nsTArray.h" #include "nsAlgorithm.h" diff --git a/js/public/TrackedOptimizationInfo.h b/js/public/TrackedOptimizationInfo.h index 64164c3169cc..b50d302a6e21 100644 --- a/js/public/TrackedOptimizationInfo.h +++ b/js/public/TrackedOptimizationInfo.h @@ -42,7 +42,8 @@ namespace JS { _(GetElem_TypedArray) \ _(GetElem_String) \ _(GetElem_Arguments) \ - _(GetElem_ArgumentsInlined) \ + _(GetElem_ArgumentsInlinedConstant) \ + _(GetElem_ArgumentsInlinedSwitch) \ _(GetElem_InlineCache) \ \ _(SetElem_TypedObject) \ diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index cfede76a072a..75cd8bbd09ce 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -181,12 +181,17 @@ function ArrayStaticSome(list, callbackfn/*, thisArg*/) { return callFunction(ArraySome, list, callbackfn, T); } -/* ES6 draft 2016-1-15 22.1.3.25 Array.prototype.sort (comparefn) */ +// ES2018 draft rev 3bbc87cd1b9d3bf64c3e68ca2fe9c5a3f2c304c0 +// 22.1.3.25 Array.prototype.sort ( comparefn ) function ArraySort(comparefn) { - /* Step 1. */ - var O = ToObject(this); + // Step 1. + assert(typeof comparefn === "function", "Only called when a comparator is present"); - /* Step 2. */ + // Step 2. + assert(IsObject(this), "|this| should be an object"); + var O = this; + + // Step 3. var len = ToLength(O.length); if (len <= 1) @@ -724,10 +729,13 @@ function ArrayIteratorNext() { // Step 8-9. var len; if (IsPossiblyWrappedTypedArray(a)) { - if (PossiblyWrappedTypedArrayHasDetachedBuffer(a)) - ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED); - len = PossiblyWrappedTypedArrayLength(a); + + // If the length is non-zero, the buffer can't be detached. + if (len === 0) { + if (PossiblyWrappedTypedArrayHasDetachedBuffer(a)) + ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED); + } } else { len = ToLength(a.length); } diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js index 3ecd0a164a3c..4c6bbf5648a9 100644 --- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -1155,14 +1155,21 @@ function TypedArrayCompareInt(x, y) { return 0; } -// ES6 draft 20151210 22.2.3.26 %TypedArray%.prototype.sort ( comparefn ). +// ES2018 draft rev 3bbc87cd1b9d3bf64c3e68ca2fe9c5a3f2c304c0 +// 22.2.3.26 %TypedArray%.prototype.sort ( comparefn ) function TypedArraySort(comparefn) { // This function is not generic. // Step 1. - var obj = this; + if (comparefn !== undefined) { + if (!IsCallable(comparefn)) + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, comparefn)); + } // Step 2. + var obj = this; + + // Step 3. var isTypedArray = IsObject(obj) && IsTypedArray(obj); var buffer; @@ -1172,7 +1179,7 @@ function TypedArraySort(comparefn) { buffer = callFunction(CallTypedArrayMethodIfWrapped, obj, obj, "GetAttachedArrayBuffer"); } - // Step 3. + // Step 4. var len; if (isTypedArray) { len = TypedArrayLength(obj); @@ -1180,6 +1187,10 @@ function TypedArraySort(comparefn) { len = callFunction(CallTypedArrayMethodIfWrapped, obj, obj, "TypedArrayLength"); } + // Arrays with less than two elements remain unchanged when sorted. + if (len <= 1) + return obj; + if (comparefn === undefined) { // CountingSort doesn't invoke the comparator function. if (IsUint8TypedArray(obj)) { @@ -1202,39 +1213,33 @@ function TypedArraySort(comparefn) { // To satisfy step 2 from TypedArray SortCompare described in 22.2.3.26 // the user supplied comparefn is wrapped. - var wrappedCompareFn = comparefn; - comparefn = function(x, y) { + var wrappedCompareFn = function(x, y) { // Step a. - var v = wrappedCompareFn(x, y); + var v = comparefn(x, y); + // Step b. - if (buffer === null) { - // A typed array previously using inline storage may acquire a - // buffer, so we must check with the source. - if (isTypedArray) { - buffer = GetAttachedArrayBuffer(obj); - } else { - buffer = callFunction(CallTypedArrayMethodIfWrapped, obj, obj, - "GetAttachedArrayBuffer"); - } - } - var bufferDetached; + var length; if (isTypedArray) { - bufferDetached = IsDetachedBuffer(buffer); + length = TypedArrayLength(obj); } else { - // This is totally cheating and only works because we know `obj` - // and `buffer` are same-compartment". - bufferDetached = callFunction(CallTypedArrayMethodIfWrapped, obj, - buffer, "IsDetachedBuffer"); + length = callFunction(CallTypedArrayMethodIfWrapped, obj, obj, "TypedArrayLength"); } - if (bufferDetached) + + // It's faster for us to check the typed array's length than to check + // for detached buffers. + if (length === 0) { + assert(PossiblyWrappedTypedArrayHasDetachedBuffer(obj), + "Length can only change from non-zero to zero when the buffer was detached"); ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED); + } + // Step c. is redundant, see: // https://bugzilla.mozilla.org/show_bug.cgi?id=1121937#c36 // Step d. return v; } - return QuickSort(obj, len, comparefn); + return QuickSort(obj, len, wrappedCompareFn); } // ES2017 draft rev f8a9be8ea4bd97237d176907a1e3080dce20c68f diff --git a/js/src/jit-test/tests/basic/bug1380962.js b/js/src/jit-test/tests/basic/bug1380962.js new file mode 100644 index 000000000000..9e9db7bb2d76 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1380962.js @@ -0,0 +1,5 @@ +// Source string has balanced parentheses even when the source code was discarded. + +setDiscardSource(true); +eval("var f = function() { return 0; };"); +assertEq(f.toSource(), "(function() {\n [sourceless code]\n})"); diff --git a/js/src/jit-test/tests/basic/bug858097.js b/js/src/jit-test/tests/basic/bug858097.js index bf51161ab347..9420eada4d11 100644 --- a/js/src/jit-test/tests/basic/bug858097.js +++ b/js/src/jit-test/tests/basic/bug858097.js @@ -1,4 +1,5 @@ // |jit-test| allow-oom; allow-unhandlable-oom +gczeal(0); function MyObject( value ) {} gcparam("maxBytes", gcparam("gcBytes") + 4*(1)); gczeal(4); diff --git a/js/src/jit-test/tests/ion/inlining/inline-getelem-args.js b/js/src/jit-test/tests/ion/inlining/inline-getelem-args.js new file mode 100644 index 000000000000..41acaf6a04dd --- /dev/null +++ b/js/src/jit-test/tests/ion/inlining/inline-getelem-args.js @@ -0,0 +1,59 @@ +function cat() { + var res = ""; + for (var i = 0; i < arguments.length; i++) + res += arguments[i]; + return res; +} + +function cat_m1(ion) { + var res = ""; + for (var i = (ion ? -1 : 0); i < arguments.length; i++) + res += arguments[i]; + return res; +} + +function cat_p1(ion) { + var res = ""; + for (var i = 0; i < arguments.length + (ion ? 1 : 0); i++) + res += arguments[i]; + return res; +} + +function sum() { + var res = 0; + for (var i = 0; i < arguments.length; i++) + res += arguments[i]; + return res; +} + +function wrapper() { + var res; + var i6 = { valueOf: () => 6 }, + i8 = 8.5, + s2 = { toString: () => "2" }, + s4 = {}, + s6 = "6", + s7 = undefined, + s8 = null; + for (var b = true; b; b = !inIon()) { + res = sum(1,2,3,4,5,i6,7,i8,9,10); + assertEq(res, 55.5); + + res = cat(true,s2,3,s4,5,s6,s7,s8); + assertEq(res, "true23[object Object]56undefinednull"); + + ion = inIon(); + if (typeof ion !== "boolean") break; + res = cat_m1(ion,1,s2,3,s4,5,s6,s7,s8); + if (ion) assertEq(res, "undefinedtrue123[object Object]56undefinednull"); + else assertEq(res, "false123[object Object]56undefinednull"); + + ion = inIon(); + if (typeof ion !== "boolean") break; + res = cat_p1(ion,1,s2,3,s4,5,s6,s7,s8); + if (ion) assertEq(res, "true123[object Object]56undefinednullundefined"); + else assertEq(res, "false123[object Object]56undefinednull"); + } +} + +wrapper(); diff --git a/js/src/jit/AliasAnalysisShared.cpp b/js/src/jit/AliasAnalysisShared.cpp index e21e2b311913..38574b719775 100644 --- a/js/src/jit/AliasAnalysisShared.cpp +++ b/js/src/jit/AliasAnalysisShared.cpp @@ -124,6 +124,7 @@ GetObject(const MDefinition* ins) case MDefinition::Op_TypedArrayElements: case MDefinition::Op_TypedObjectElements: case MDefinition::Op_CopyLexicalEnvironmentObject: + case MDefinition::Op_IsPackedArray: object = ins->getOperand(0); break; case MDefinition::Op_GetPropertyCache: diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp index c1e368543e18..533161739d0f 100644 --- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -1584,8 +1584,10 @@ jit::BailoutIonToBaseline(JSContext* cx, JitActivation* activation, JitFrameIter JitSpew(JitSpew_BaselineBailouts, " Incoming frame ptr = %p", builder.startFrame()); SnapshotIteratorForBailout snapIter(activation, iter); - if (!snapIter.init(cx)) + if (!snapIter.init(cx)) { + ReportOutOfMemory(cx); return BAILOUT_RETURN_FATAL_ERROR; + } #ifdef TRACK_SNAPSHOTS snapIter.spewBailingFrom(); diff --git a/js/src/jit/BaselineFrameInfo.cpp b/js/src/jit/BaselineFrameInfo.cpp index 9b9b991b66a5..a852ad8fe0ae 100644 --- a/js/src/jit/BaselineFrameInfo.cpp +++ b/js/src/jit/BaselineFrameInfo.cpp @@ -143,7 +143,7 @@ FrameInfo::popRegsAndSync(uint32_t uses) // clobbered by the first popValue. StackValue* val = peek(-2); if (val->kind() == StackValue::Register && val->reg() == R1) { - masm.moveValue(R1, R2); + masm.moveValue(R1, ValueOperand(R2)); val->setRegister(R2); } popValue(R1); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 9dde5aa82c75..6fff20d5cb6a 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -105,8 +105,10 @@ PrepareOsrTempData(JSContext* cx, ICWarmUpCounter_Fallback* stub, BaselineFrame* AlignBytes(ionOsrTempDataSpace, sizeof(Value)); IonOsrTempData* info = (IonOsrTempData*)cx->allocateOsrTempData(totalSpace); - if (!info) + if (!info) { + ReportOutOfMemory(cx); return nullptr; + } memset(info, 0, totalSpace); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index a44acbcaae24..8909d985309a 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -42,6 +42,7 @@ #include "jit/MoveEmitter.h" #include "jit/RangeAnalysis.h" #include "jit/SharedICHelpers.h" +#include "jit/StackSlotAllocator.h" #include "vm/AsyncFunction.h" #include "vm/AsyncIteration.h" #include "vm/MatchPairs.h" @@ -11023,6 +11024,238 @@ CodeGenerator::visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole* lir) masm.bind(&done); } +template +class OutOfLineSwitch : public OutOfLineCodeBase +{ + using LabelsVector = Vector; + using CodeLabelsVector = Vector; + LabelsVector labels_; + CodeLabelsVector codeLabels_; + CodeLabel start_; + bool isOutOfLine_; + + void accept(CodeGenerator* codegen) { + codegen->visitOutOfLineSwitch(this); + } + + public: + explicit OutOfLineSwitch(TempAllocator& alloc) + : labels_(alloc), + codeLabels_(alloc), + isOutOfLine_(false) + {} + + CodeLabel* start() { + return &start_; + } + + CodeLabelsVector& codeLabels() { + return codeLabels_; + } + LabelsVector& labels() { + return labels_; + } + + void jumpToCodeEntries(MacroAssembler& masm, Register index, Register temp) { + Register base; + if (tableType == SwitchTableType::Inline) { +#if defined(JS_CODEGEN_ARM) + base = ::js::jit::pc; +#else + MOZ_CRASH("NYI: SwitchTableType::Inline"); +#endif + } else { +#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) + MOZ_CRASH("NYI: SwitchTableType::OutOfLine"); +#else + masm.mov(start_.patchAt(), temp); + base = temp; +#endif + } + BaseIndex jumpTarget(base, index, ScalePointer); + masm.branchToComputedAddress(jumpTarget); + } + + // Register an entry in the switch table. + void addTableEntry(MacroAssembler& masm) { + if ((!isOutOfLine_ && tableType == SwitchTableType::Inline) || + (isOutOfLine_ && tableType == SwitchTableType::OutOfLine)) + { + CodeLabel cl; + masm.writeCodePointer(cl.patchAt()); + masm.propagateOOM(codeLabels_.append(mozilla::Move(cl))); + } + } + // Register the code, to which the table will jump to. + void addCodeEntry(MacroAssembler& masm) { + Label entry; + masm.bind(&entry); + masm.propagateOOM(labels_.append(mozilla::Move(entry))); + } + + void setOutOfLine() { + isOutOfLine_ = true; + } +}; + +template +void +CodeGenerator::visitOutOfLineSwitch(OutOfLineSwitch* jumpTable) +{ + jumpTable->setOutOfLine(); + if (tableType == SwitchTableType::OutOfLine) { +#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) + MOZ_CRASH("NYI: SwitchTableType::OutOfLine"); +#else + masm.haltingAlign(sizeof(void*)); + masm.use(jumpTable->start()->target()); + masm.addCodeLabel(*jumpTable->start()); +#endif + } + + // Add table entries if the table is inlined. + auto& labels = jumpTable->labels(); + for (size_t i = 0, e = labels.length(); i < e; i++) + jumpTable->addTableEntry(masm); + + auto& codeLabels = jumpTable->codeLabels(); + for (size_t i = 0, e = codeLabels.length(); i < e; i++) { + // The entries of the jump table need to be absolute addresses and thus + // must be patched after codegen is finished. + auto& cl = codeLabels[i]; + cl.target()->bind(labels[i].offset()); + masm.addCodeLabel(cl); + } +} + +template void CodeGenerator::visitOutOfLineSwitch(OutOfLineSwitch* jumpTable); +template void CodeGenerator::visitOutOfLineSwitch(OutOfLineSwitch* jumpTable); + +void +CodeGenerator::visitLoadElementFromStateV(LLoadElementFromStateV* lir) +{ + Register index = ToRegister(lir->index()); + Register temp0 = ToRegister(lir->temp0()); +#ifdef JS_NUNBOX32 + Register temp1 = ToRegister(lir->temp1()); +#endif + FloatRegister tempD = ToFloatRegister(lir->tempD()); + ValueOperand out = ToOutValue(lir); + + // For each element, load it and box it. + MArgumentState* array = lir->array()->toArgumentState(); + Label join; + + // Jump to the code which is loading the element, based on its index. +#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) + auto* jumpTable = new (alloc()) OutOfLineSwitch(alloc()); +#else + auto* jumpTable = new (alloc()) OutOfLineSwitch(alloc()); +#endif + + { +#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) + // Inhibit pools within the following sequence because we are indexing into + // a pc relative table. The region will have one instruction for ma_ldr, one + // for breakpoint, and each table case takes one word. + AutoForbidPools afp(&masm, 1 + 1 + array->numElements()); +#endif + jumpTable->jumpToCodeEntries(masm, index, temp0); + + // Add table entries if the table is inlined. + for (size_t i = 0, e = array->numElements(); i < e; i++) + jumpTable->addTableEntry(masm); + } + + // Add inlined code for loading arguments from where they are allocated. + for (size_t i = 0, e = array->numElements(); i < e; i++) { + MDefinition* elem = array->getElement(i); + ConstantOrRegister input; + + jumpTable->addCodeEntry(masm); + Register typeReg = Register::Invalid(); + const LAllocation* a = lir->getOperand(1 + BOX_PIECES * i); + if (a->isBogus()) { + if (elem->type() == MIRType::Null) { + input = NullValue(); + } else if (elem->type() == MIRType::Undefined) { + input = UndefinedValue(); + } else if (elem->isConstant() && elem->isEmittedAtUses()) { + input = elem->toConstant()->toJSValue(); + } else { + MOZ_CRASH("Unsupported element constant allocation."); + } + } else if (a->isMemory()) { + if (elem->type() == MIRType::Double) { + masm.loadDouble(ToAddress(a), tempD); + input = TypedOrValueRegister(elem->type(), AnyRegister(tempD)); + } else if (elem->type() == MIRType::Value) { + typeReg = temp0; + masm.loadPtr(ToAddress(a), temp0); +#ifdef JS_PUNBOX64 + input = TypedOrValueRegister(ValueOperand(temp0)); +#endif + } else { + typeReg = temp0; + size_t width = StackSlotAllocator::width(LDefinition::TypeFrom(elem->type())); + if (width == 4) + masm.load32(ToAddress(a), temp0); + else if (width == 8) + masm.loadPtr(ToAddress(a), temp0); + else + MOZ_CRASH("Unsupported load size"); + input = TypedOrValueRegister(elem->type(), AnyRegister(typeReg)); + } + } else if (a->isGeneralReg()) { + typeReg = ToRegister(a); + input = TypedOrValueRegister(elem->type(), AnyRegister(typeReg)); +#ifdef JS_PUNBOX64 + if (elem->type() != MIRType::Value) + input = TypedOrValueRegister(elem->type(), AnyRegister(typeReg)); + else + input = TypedOrValueRegister(ValueOperand(typeReg)); +#else + if (elem->type() != MIRType::Value) + input = TypedOrValueRegister(elem->type(), AnyRegister(typeReg)); +#endif + } else if (a->isFloatReg()) { + input = TypedOrValueRegister(elem->type(), AnyRegister(ToFloatRegister(a))); + } else if (a->isConstantValue()) { + input = a->toConstant()->toJSValue(); + } else { + MOZ_CRASH("Unsupported element allocation."); + } + +#ifdef JS_NUNBOX32 + if (elem->type() == MIRType::Value) { + static_assert(TYPE_INDEX == 0, "Unexpected type allocation index"); + static_assert(PAYLOAD_INDEX == 1, "Unexpected payload allocation index"); + const LAllocation* a1 = lir->getOperand(1 + BOX_PIECES * i + 1); + MOZ_ASSERT(!a1->isBogus()); + MOZ_ASSERT(typeReg != Register::Invalid()); + if (a1->isMemory()) { + masm.loadPtr(ToAddress(a1), temp1); + input = TypedOrValueRegister(ValueOperand(typeReg, temp1)); + } else if (a1->isGeneralReg()) { + input = TypedOrValueRegister(ValueOperand(typeReg, ToRegister(a1))); + } else { + MOZ_CRASH("Unsupported Value allocation."); + } + } else { + MOZ_ASSERT(lir->getOperand(1 + BOX_PIECES * i + 1)->isBogus()); + } +#endif + masm.moveValue(input, out); + + // For the last entry, fall-through. + if (i + 1 < e) + masm.jump(&join); + } + + addOutOfLineCode(jumpTable, lir->mir()); + masm.bind(&join); +} + template static inline void StoreToTypedArray(MacroAssembler& masm, Scalar::Type writeType, const LAllocation* value, @@ -12613,5 +12846,30 @@ CodeGenerator::visitFinishBoundFunctionInit(LFinishBoundFunctionInit* lir) masm.bind(ool->rejoin()); } +void +CodeGenerator::visitIsPackedArray(LIsPackedArray* lir) +{ + Register array = ToRegister(lir->array()); + Register output = ToRegister(lir->output()); + Register elementsTemp = ToRegister(lir->temp()); + + Label notPacked, done; + + // Load elements and length. + masm.loadPtr(Address(array, NativeObject::offsetOfElements()), elementsTemp); + masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), output); + + // Test length == initializedLength. + Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::NotEqual, initLength, output, ¬Packed); + + masm.move32(Imm32(1), output); + masm.jump(&done); + masm.bind(¬Packed); + masm.move32(Imm32(0), output); + + masm.bind(&done); +} + } // namespace jit } // namespace js diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 75add0667a3f..d186dd827f92 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -33,6 +33,12 @@ namespace js { namespace jit { +enum class SwitchTableType { + Inline, + OutOfLine +}; + +template class OutOfLineSwitch; class OutOfLineTestObject; class OutOfLineNewArray; class OutOfLineNewObject; @@ -307,6 +313,9 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitLoadUnboxedPointerV(LLoadUnboxedPointerV* lir); void visitLoadUnboxedPointerT(LLoadUnboxedPointerT* lir); void visitUnboxObjectOrNull(LUnboxObjectOrNull* lir); + template + void visitOutOfLineSwitch(OutOfLineSwitch* ool); + void visitLoadElementFromStateV(LLoadElementFromStateV* lir); void visitStoreElementT(LStoreElementT* lir); void visitStoreElementV(LStoreElementV* lir); template void emitStoreElementHoleT(T* lir); @@ -402,6 +411,7 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitNaNToZero(LNaNToZero* ins); void visitOutOfLineNaNToZero(OutOfLineNaNToZero* ool); void visitFinishBoundFunctionInit(LFinishBoundFunctionInit* lir); + void visitIsPackedArray(LIsPackedArray* lir); void visitCheckOverRecursed(LCheckOverRecursed* lir); void visitCheckOverRecursedFailure(CheckOverRecursedFailure* ool); diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index 6ca5db9245c3..5b810ca83c5c 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -122,6 +122,7 @@ _(IntrinsicSubstringKernel) \ _(IntrinsicObjectHasPrototype) \ _(IntrinsicFinishBoundFunctionInit) \ + _(IntrinsicIsPackedArray) \ \ _(IntrinsicIsArrayIterator) \ _(IntrinsicIsMapIterator) \ diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 6532f1e8621d..007c0d2e4976 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -7735,13 +7735,20 @@ IonBuilder::jsop_getelem() if (emitted) return Ok(); - trackOptimizationAttempt(TrackedStrategy::GetElem_ArgumentsInlined); - MOZ_TRY(getElemTryArgumentsInlined(&emitted, obj, index)); + trackOptimizationAttempt(TrackedStrategy::GetElem_ArgumentsInlinedConstant); + MOZ_TRY(getElemTryArgumentsInlinedConstant(&emitted, obj, index)); if (emitted) return Ok(); - if (script()->argumentsHasVarBinding()) + trackOptimizationAttempt(TrackedStrategy::GetElem_ArgumentsInlinedSwitch); + MOZ_TRY(getElemTryArgumentsInlinedIndex(&emitted, obj, index)); + if (emitted) + return Ok(); + + if (script()->argumentsHasVarBinding()) { + trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); return abort(AbortReason::Disable, "Type is not definitely lazy arguments."); + } } obj = maybeUnboxForPropertyAccess(obj); @@ -8393,7 +8400,7 @@ IonBuilder::getElemTryArguments(bool* emitted, MDefinition* obj, MDefinition* in } AbortReasonOr -IonBuilder::getElemTryArgumentsInlined(bool* emitted, MDefinition* obj, MDefinition* index) +IonBuilder::getElemTryArgumentsInlinedConstant(bool* emitted, MDefinition* obj, MDefinition* index) { MOZ_ASSERT(*emitted == false); @@ -8403,31 +8410,83 @@ IonBuilder::getElemTryArgumentsInlined(bool* emitted, MDefinition* obj, MDefinit if (obj->type() != MIRType::MagicOptimizedArguments) return Ok(); + MConstant* indexConst = index->maybeConstantValue(); + if (!indexConst || indexConst->type() != MIRType::Int32) + return Ok(); + // Emit inlined arguments. obj->setImplicitlyUsedUnchecked(); MOZ_ASSERT(!info().argsObjAliasesFormals()); // When the id is constant, we can just return the corresponding inlined argument - MConstant* indexConst = index->maybeConstantValue(); - if (indexConst && indexConst->type() == MIRType::Int32) { - MOZ_ASSERT(inliningDepth_ > 0); + MOZ_ASSERT(inliningDepth_ > 0); - int32_t id = indexConst->toInt32(); - index->setImplicitlyUsedUnchecked(); + int32_t id = indexConst->toInt32(); + index->setImplicitlyUsedUnchecked(); - if (id < (int32_t)inlineCallInfo_->argc() && id >= 0) - current->push(inlineCallInfo_->getArg(id)); - else - pushConstant(UndefinedValue()); + if (id < (int32_t)inlineCallInfo_->argc() && id >= 0) + current->push(inlineCallInfo_->getArg(id)); + else + pushConstant(UndefinedValue()); - trackOptimizationSuccess(); - *emitted = true; + trackOptimizationSuccess(); + *emitted = true; + return Ok(); +} + +AbortReasonOr +IonBuilder::getElemTryArgumentsInlinedIndex(bool* emitted, MDefinition* obj, MDefinition* index) +{ + MOZ_ASSERT(*emitted == false); + + if (inliningDepth_ == 0) return Ok(); + + if (obj->type() != MIRType::MagicOptimizedArguments) + return Ok(); + + if (!IsNumberType(index->type())) + return Ok(); + + // Currently, we do not support any arguments vector larger than 10, as this + // is being translated into code at the call site, and it would be better to + // store the arguments contiguously on the stack. + if (inlineCallInfo_->argc() > 10) { + trackOptimizationOutcome(TrackedOutcome::CantInlineBound); + return abort(AbortReason::Disable, "NYI get argument element with too many arguments"); } - // inlined not constant not supported, yet. - return abort(AbortReason::Disable, "NYI inlined not constant get argument element"); + // Emit inlined arguments. + obj->setImplicitlyUsedUnchecked(); + + MOZ_ASSERT(!info().argsObjAliasesFormals()); + + // Ensure index is an integer. + MInstruction* idInt32 = MToInt32::New(alloc(), index); + current->add(idInt32); + index = idInt32; + + // Bailout if we read more than the number of actual arguments. This bailout + // cannot re-enter because reading out of bounds arguments will disable the + // lazy arguments optimization for this script, when this code would be + // executed in Baseline. (see GetElemOptimizedArguments) + index = addBoundsCheck(index, constantInt(inlineCallInfo_->argc())); + + // Get an instruction to represent the state of the argument vector. + MInstruction* args = MArgumentState::New(alloc().fallible(), inlineCallInfo_->argv()); + if (!args) + return abort(AbortReason::Alloc); + current->add(args); + + // Select a value to pick from a vector. + MInstruction* load = MLoadElementFromState::New(alloc(), args, index); + current->add(load); + current->push(load); + + trackOptimizationSuccess(); + *emitted = true; + return Ok(); } AbortReasonOr diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 8e4acedb6807..5f92cc014f77 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -427,8 +427,10 @@ class IonBuilder AbortReasonOr getElemTryTypedObject(bool* emitted, MDefinition* obj, MDefinition* index); AbortReasonOr getElemTryString(bool* emitted, MDefinition* obj, MDefinition* index); AbortReasonOr getElemTryArguments(bool* emitted, MDefinition* obj, MDefinition* index); - AbortReasonOr getElemTryArgumentsInlined(bool* emitted, MDefinition* obj, - MDefinition* index); + AbortReasonOr getElemTryArgumentsInlinedConstant(bool* emitted, MDefinition* obj, + MDefinition* index); + AbortReasonOr getElemTryArgumentsInlinedIndex(bool* emitted, MDefinition* obj, + MDefinition* index); AbortReasonOr getElemAddCache(MDefinition* obj, MDefinition* index); AbortReasonOr getElemTryScalarElemOfTypedObject(bool* emitted, MDefinition* obj, @@ -779,6 +781,7 @@ class IonBuilder InliningResult inlineSubstringKernel(CallInfo& callInfo); InliningResult inlineObjectHasPrototype(CallInfo& callInfo); InliningResult inlineFinishBoundFunctionInit(CallInfo& callInfo); + InliningResult inlineIsPackedArray(CallInfo& callInfo); // Testing functions. InliningResult inlineBailout(CallInfo& callInfo); diff --git a/js/src/jit/LIR.h b/js/src/jit/LIR.h index 8d27bd308988..8e64fd3b1fb8 100644 --- a/js/src/jit/LIR.h +++ b/js/src/jit/LIR.h @@ -1177,6 +1177,14 @@ class LVariadicInstruction : public details::LInstructionFixedDefsTempsHelper diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 30f0887fa0ef..d8941e1ad310 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3235,6 +3235,73 @@ LIRGenerator::visitLoadUnboxedString(MLoadUnboxedString* ins) define(lir, ins); } +void +LIRGenerator::visitLoadElementFromState(MLoadElementFromState* ins) +{ + MOZ_ASSERT(ins->index()->type() == MIRType::Int32); + + LDefinition temp1 = LDefinition::BogusTemp(); +#ifdef JS_NUNBOX32 + temp1 = temp(); +#endif + LLoadElementFromStateV* lir = new(alloc()) LLoadElementFromStateV(temp(), temp1, tempDouble()); + MOZ_ASSERT(ins->array()->isArgumentState(), + "LIRGenerator::visitLoadElementFromState: Unsupported state object"); + MArgumentState* array = ins->array()->toArgumentState(); + + // 1 -- for the index as a register + // BOX_PIECES * array->numElements() -- for using as operand all the + // elements of the inlined array. + size_t numOperands = 1 + BOX_PIECES * array->numElements(); + if (!lir->init(alloc(), numOperands)) { + abort(AbortReason::Alloc, "OOM: LIRGenerator::visitLoadElementFromState"); + return; + } + lir->setOperand(0, useRegister(ins->index())); // index + for (size_t i = 0, e = array->numElements(); i < e; i++) { + MDefinition* elem = array->getElement(i); + if (elem->isConstant() && elem->isEmittedAtUses()) { + lir->setOperand(1 + BOX_PIECES * i, LAllocation()); +#ifdef JS_NUNBOX32 + lir->setOperand(1 + BOX_PIECES * i + 1, LAllocation()); +#endif + continue; + } + + switch (array->getElement(i)->type()) { + case MIRType::Value: + lir->setBoxOperand(1 + BOX_PIECES * i, useBox(elem, LUse::ANY)); + break; + // Anything which can be boxed: + case MIRType::Boolean: + case MIRType::Int32: + case MIRType::Double: + case MIRType::Object: + case MIRType::String: + case MIRType::Symbol: + lir->setOperand(1 + BOX_PIECES * i, use(elem)); +#ifdef JS_NUNBOX32 + // Bogus second operand. + lir->setOperand(1 + BOX_PIECES * i + 1, LAllocation()); +#endif + break; + case MIRType::Null: + case MIRType::Undefined: + // Bogus operand, as these can be inlined. + lir->setOperand(1 + BOX_PIECES * i, LAllocation()); +#ifdef JS_NUNBOX32 + lir->setOperand(1 + BOX_PIECES * i + 1, LAllocation()); +#endif + break; + default: + MOZ_CRASH("LIRGenerator::visitLoadElementFromState: Unsupported element type."); + return; + } + } + + defineBox(lir, ins); +} + void LIRGenerator::visitStoreElement(MStoreElement* ins) { @@ -4905,6 +4972,16 @@ LIRGenerator::visitFinishBoundFunctionInit(MFinishBoundFunctionInit* ins) assignSafepoint(lir, ins); } +void +LIRGenerator::visitIsPackedArray(MIsPackedArray* ins) +{ + MOZ_ASSERT(ins->array()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::Boolean); + + auto lir = new(alloc()) LIsPackedArray(useRegister(ins->array()), temp()); + define(lir, ins); +} + static void SpewResumePoint(MBasicBlock* block, MInstruction* ins, MResumePoint* resumePoint) { @@ -5130,6 +5207,12 @@ LIRGenerator::visitArrayState(MArrayState* objState) MOZ_CRASH("Unexpected ArrayState node during Lowering."); } +void +LIRGenerator::visitArgumentState(MArgumentState* objState) +{ + // ArgumentState nodes are always inlined at their uses. +} + void LIRGenerator::visitUnknownValue(MUnknownValue* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index d26af49cb6c0..b6f9d3d80570 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -232,6 +232,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitLoadElementHole(MLoadElementHole* ins); void visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull* ins); void visitLoadUnboxedString(MLoadUnboxedString* ins); + void visitLoadElementFromState(MLoadElementFromState* ins); void visitStoreElement(MStoreElement* ins); void visitStoreElementHole(MStoreElementHole* ins); void visitFallibleStoreElement(MFallibleStoreElement* ins); @@ -325,6 +326,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitBeta(MBeta* ins); void visitObjectState(MObjectState* ins); void visitArrayState(MArrayState* ins); + void visitArgumentState(MArgumentState* ins); void visitUnknownValue(MUnknownValue* ins); void visitLexicalCheck(MLexicalCheck* ins); void visitThrowRuntimeLexicalError(MThrowRuntimeLexicalError* ins); @@ -341,6 +343,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitCheckObjCoercible(MCheckObjCoercible* ins); void visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins); void visitFinishBoundFunctionInit(MFinishBoundFunctionInit* ins); + void visitIsPackedArray(MIsPackedArray* ins); }; } // namespace jit diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 6a34eab4d363..18c321f306ef 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -312,6 +312,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineObjectHasPrototype(callInfo); case InlinableNative::IntrinsicFinishBoundFunctionInit: return inlineFinishBoundFunctionInit(callInfo); + case InlinableNative::IntrinsicIsPackedArray: + return inlineIsPackedArray(callInfo); // Map intrinsics. case InlinableNative::IntrinsicIsMapObject: @@ -1745,6 +1747,47 @@ IonBuilder::inlineFinishBoundFunctionInit(CallInfo& callInfo) return InliningStatus_Inlined; } +IonBuilder::InliningResult +IonBuilder::inlineIsPackedArray(CallInfo& callInfo) +{ + MOZ_ASSERT(!callInfo.constructing()); + MOZ_ASSERT(callInfo.argc() == 1); + + if (getInlineReturnType() != MIRType::Boolean) + return InliningStatus_NotInlined; + + MDefinition* array = callInfo.getArg(0); + + if (array->type() != MIRType::Object) + return InliningStatus_NotInlined; + + TemporaryTypeSet* arrayTypes = array->resultTypeSet(); + if (!arrayTypes) + return InliningStatus_NotInlined; + + const Class* clasp = arrayTypes->getKnownClass(constraints()); + if (clasp != &ArrayObject::class_) + return InliningStatus_NotInlined; + + // Only inline if the array uses dense storage. + ObjectGroupFlags unhandledFlags = OBJECT_FLAG_SPARSE_INDEXES | + OBJECT_FLAG_LENGTH_OVERFLOW | + OBJECT_FLAG_NON_PACKED; + + if (arrayTypes->hasObjectFlags(constraints(), unhandledFlags)) { + trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); + return InliningStatus_NotInlined; + } + + callInfo.setImplicitlyUsedUnchecked(); + + auto* ins = MIsPackedArray::New(alloc(), array); + current->add(ins); + current->push(ins); + + return InliningStatus_Inlined; +} + IonBuilder::InliningResult IonBuilder::inlineStrCharCodeAt(CallInfo& callInfo) { diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index f7893a926ee4..06f24c65ff93 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -5098,6 +5098,28 @@ MArrayState::Copy(TempAllocator& alloc, MArrayState* state) return res; } +MArgumentState* +MArgumentState::New(TempAllocator::Fallible view, const MDefinitionVector& args) +{ + MArgumentState* res = new(view.alloc) MArgumentState(); + if (!res || !res->init(view.alloc, args.length())) + return nullptr; + for (size_t i = 0, e = args.length(); i < e; i++) + res->initOperand(i, args[i]); + return res; +} + +MArgumentState* +MArgumentState::Copy(TempAllocator& alloc, MArgumentState* state) +{ + MArgumentState* res = new(alloc) MArgumentState(); + if (!res || !res->init(alloc, state->numElements())) + return nullptr; + for (size_t i = 0, e = res->numOperands(); i < e; i++) + res->initOperand(i, state->getOperand(i)); + return res; +} + MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t length, MConstant* templateConst, gc::InitialHeap initialHeap, jsbytecode* pc, bool vmCall) : MUnaryInstruction(templateConst), @@ -6147,34 +6169,6 @@ PropertyReadNeedsTypeBarrier(CompilerConstraintList* constraints, return BarrierKind::NoBarrier; } -static bool -ObjectSubsumes(TypeSet::ObjectKey* first, TypeSet::ObjectKey* second) -{ - if (first->isSingleton() || - second->isSingleton() || - first->clasp() != second->clasp() || - first->unknownProperties() || - second->unknownProperties()) - { - return false; - } - - if (first->clasp() == &ArrayObject::class_) { - HeapTypeSetKey firstElements = first->property(JSID_VOID); - HeapTypeSetKey secondElements = second->property(JSID_VOID); - - return firstElements.maybeTypes() && secondElements.maybeTypes() && - firstElements.maybeTypes()->equals(secondElements.maybeTypes()); - } - - if (first->clasp() == &UnboxedArrayObject::class_) { - return first->group()->unboxedLayout().elementType() == - second->group()->unboxedLayout().elementType(); - } - - return false; -} - BarrierKind jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx, CompilerConstraintList* constraints, @@ -6216,30 +6210,6 @@ jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx, } while (obj); } - // If any objects which could be observed are similar to ones that have - // already been observed, add them to the observed type set. - if (!key->unknownProperties()) { - HeapTypeSetKey property = key->property(name ? NameToId(name) : JSID_VOID); - - if (property.maybeTypes() && !property.maybeTypes()->unknownObject()) { - for (size_t i = 0; i < property.maybeTypes()->getObjectCount(); i++) { - TypeSet::ObjectKey* key = property.maybeTypes()->getObject(i); - if (!key || observed->unknownObject()) - continue; - - for (size_t j = 0; j < observed->getObjectCount(); j++) { - TypeSet::ObjectKey* observedKey = observed->getObject(j); - if (observedKey && ObjectSubsumes(observedKey, key)) { - // Note: the return value here is ignored. - observed->addType(TypeSet::ObjectType(key), - GetJitContext()->temp->lifoAlloc()); - break; - } - } - } - } - } - return PropertyReadNeedsTypeBarrier(constraints, key, name, observed); } diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index bdfd84e0885c..238c57449f2a 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -3957,6 +3957,41 @@ class MArrayState } }; +// Hold the arguments of an inlined frame. At the moment this class is not +// recovered on bailout as it does not have an implementation and it should +// be inlined at all its uses. +class MArgumentState + : public MVariadicInstruction, + public NoFloatPolicyAfter<0>::Data +{ + private: + explicit MArgumentState() { + setResultType(MIRType::Object); + setMovable(); + } + + public: + INSTRUCTION_HEADER(ArgumentState) + + static MArgumentState* New(TempAllocator::Fallible view, const MDefinitionVector& args); + static MArgumentState* Copy(TempAllocator& alloc, MArgumentState* state); + + size_t numElements() const { + return numOperands(); + } + + MDefinition* getElement(uint32_t index) const { + return getOperand(index); + } + + bool congruentTo(const MDefinition* ins) const override { + return congruentIfOperandsEqual(ins); + } + AliasSet getAliasSet() const override { + return AliasSet::None(); + } +}; + // Setting __proto__ in an object literal. class MMutateProto : public MAryInstruction<2>, @@ -9656,6 +9691,30 @@ class MStoreElementCommon } }; +// This instruction is used to load an element of a non-escaped inlined array. +class MLoadElementFromState + : public MBinaryInstruction, + public SingleObjectPolicy::Data +{ + MLoadElementFromState(MDefinition* array, MDefinition* index) + : MBinaryInstruction(array, index) + { + MOZ_ASSERT(array->isArgumentState()); + MOZ_ASSERT(index->type() == MIRType::Int32); + setResultType(MIRType::Value); + setMovable(); + } + + public: + INSTRUCTION_HEADER(LoadElementFromState) + TRIVIAL_NEW_WRAPPERS + NAMED_OPERANDS((0, array), (1, index)); + + AliasSet getAliasSet() const override { + return AliasSet::None(); + } +}; + // Store a value to a dense array slots vector. class MStoreElement : public MAryInstruction<3>, @@ -12691,7 +12750,7 @@ class MArgumentsLength : public MNullaryInstruction AliasSet getAliasSet() const override { // Arguments |length| cannot be mutated by Ion Code. return AliasSet::None(); - } + } void computeRange(TempAllocator& alloc) override; @@ -13798,6 +13857,27 @@ class MFinishBoundFunctionInit NAMED_OPERANDS((0, bound), (1, target), (2, argCount)) }; +class MIsPackedArray + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + explicit MIsPackedArray(MDefinition* array) + : MUnaryInstruction(array) + { + setResultType(MIRType::Boolean); + setMovable(); + } + + public: + INSTRUCTION_HEADER(IsPackedArray) + TRIVIAL_NEW_WRAPPERS + NAMED_OPERANDS((0, array)) + + AliasSet getAliasSet() const override { + return AliasSet::Load(AliasSet::ObjectFields); + } +}; + // Flips the input's sign bit, independently of the rest of the number's // payload. Note this is different from multiplying by minus-one, which has // side-effects for e.g. NaNs. diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index c6858c7434f9..d1ca252fe7b0 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -144,6 +144,7 @@ namespace jit { _(NewStringObject) \ _(ObjectState) \ _(ArrayState) \ + _(ArgumentState) \ _(InitElem) \ _(InitElemGetterSetter) \ _(MutateProto) \ @@ -217,6 +218,7 @@ namespace jit { _(LoadUnboxedScalar) \ _(LoadUnboxedObjectOrNull) \ _(LoadUnboxedString) \ + _(LoadElementFromState) \ _(StoreElement) \ _(StoreElementHole) \ _(FallibleStoreElement) \ @@ -296,6 +298,7 @@ namespace jit { _(CheckObjCoercible) \ _(DebugCheckSelfHosted) \ _(FinishBoundFunctionInit) \ + _(IsPackedArray) \ _(AsmJSLoadHeap) \ _(AsmJSStoreHeap) \ _(AsmJSCompareExchangeHeap) \ diff --git a/js/src/jit/MacroAssembler-inl.h b/js/src/jit/MacroAssembler-inl.h index e73c8419fe90..37fcac4a73a2 100644 --- a/js/src/jit/MacroAssembler-inl.h +++ b/js/src/jit/MacroAssembler-inl.h @@ -327,6 +327,20 @@ MacroAssembler::hasSelfReference() const return selfReferencePatch_.bound(); } +// =============================================================== +// Move instructions + +void +MacroAssembler::moveValue(const ConstantOrRegister& src, const ValueOperand& dest) +{ + if (src.constant()) { + moveValue(src.value(), dest); + return; + } + + moveValue(src.reg(), dest); +} + // =============================================================== // Arithmetic functions diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 6ec4727be5c1..79ff7c6d89cd 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -986,7 +986,7 @@ MacroAssembler::fillSlotsWithConstantValue(Address base, Register temp, for (unsigned i = start; i < end; ++i, addr.offset += sizeof(GCPtrValue)) store32(temp, ToType(addr)); #else - moveValue(v, temp); + moveValue(v, ValueOperand(temp)); for (uint32_t i = start; i < end; ++i, base.offset += sizeof(GCPtrValue)) storePtr(temp, base); #endif diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index c07e42890447..e9e8f7110c0d 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -736,6 +736,14 @@ class MacroAssembler : public MacroAssemblerSpecific inline void move8SignExtend(Register src, Register dest) PER_SHARED_ARCH; inline void move16SignExtend(Register src, Register dest) PER_SHARED_ARCH; + // Copy a constant, typed-register, or a ValueOperand into a ValueOperand + // destination. + inline void moveValue(const ConstantOrRegister& src, const ValueOperand& dest); + void moveValue(const TypedOrValueRegister& src, const ValueOperand& dest) PER_ARCH; + void moveValue(const ValueOperand& src, const ValueOperand& dest) PER_ARCH; + void moveValue(const Value& src, const ValueOperand& dest) PER_ARCH; + + public: // =============================================================== // Logical instructions @@ -1258,6 +1266,9 @@ class MacroAssembler : public MacroAssemblerSpecific inline void branchTestStringTruthy(bool truthy, const ValueOperand& value, Label* label) DEFINED_ON(arm, arm64, mips32, mips64, x86_shared); + // Create an unconditional branch to the address given as argument. + inline void branchToComputedAddress(const BaseIndex& address) PER_ARCH; + private: // Implementation for branch* methods. diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp index 409a6cc732ae..8d4aca26291d 100644 --- a/js/src/jit/TypePolicy.cpp +++ b/js/src/jit/TypePolicy.cpp @@ -587,6 +587,7 @@ NoFloatPolicyAfter::adjustInputs(TempAllocator& alloc, MInstruction* de return true; } +template bool NoFloatPolicyAfter<0>::adjustInputs(TempAllocator& alloc, MInstruction* def); template bool NoFloatPolicyAfter<1>::adjustInputs(TempAllocator& alloc, MInstruction* def); template bool NoFloatPolicyAfter<2>::adjustInputs(TempAllocator& alloc, MInstruction* def); @@ -1268,6 +1269,7 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) _(MixPolicy, StringPolicy<1> >) \ _(MixPolicy, BoxPolicy<1> >) \ _(NoFloatPolicy<0>) \ + _(NoFloatPolicyAfter<0>) \ _(NoFloatPolicyAfter<1>) \ _(NoFloatPolicyAfter<2>) \ _(ObjectPolicy<0>) \ diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index cdf9f1e5eaf2..9b389dd8d509 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -1404,7 +1404,7 @@ CodeGeneratorARM::visitBox(LBox* box) MOZ_ASSERT(!box->getOperand(0)->isConstant()); - // On x86, the input operand and the output payload have the same virtual + // On arm, the input operand and the output payload have the same virtual // register. All that needs to be written is the type tag for the type // definition. masm.ma_mov(Imm32(MIRTypeToTag(box->type())), ToRegister(type)); @@ -1413,18 +1413,10 @@ CodeGeneratorARM::visitBox(LBox* box) void CodeGeneratorARM::visitBoxFloatingPoint(LBoxFloatingPoint* box) { - const LDefinition* payload = box->getDef(PAYLOAD_INDEX); - const LDefinition* type = box->getDef(TYPE_INDEX); - const LAllocation* in = box->getOperand(0); - FloatRegister reg = ToFloatRegister(in); + const AnyRegister in = ToAnyRegister(box->getOperand(0)); + const ValueOperand out = ToOutValue(box); - if (box->type() == MIRType::Float32) { - ScratchFloat32Scope scratch(masm); - masm.convertFloat32ToDouble(reg, scratch); - masm.ma_vxfer(VFPRegister(scratch), ToRegister(payload), ToRegister(type)); - } else { - masm.ma_vxfer(VFPRegister(reg), ToRegister(payload), ToRegister(type)); - } + masm.moveValue(TypedOrValueRegister(box->type(), in), out); } void diff --git a/js/src/jit/arm/MacroAssembler-arm-inl.h b/js/src/jit/arm/MacroAssembler-arm-inl.h index e77dc17b6253..aa7df8d95e0b 100644 --- a/js/src/jit/arm/MacroAssembler-arm-inl.h +++ b/js/src/jit/arm/MacroAssembler-arm-inl.h @@ -2057,6 +2057,21 @@ MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMag branch32(cond, ToPayload(valaddr), Imm32(why), label); } +void +MacroAssembler::branchToComputedAddress(const BaseIndex& addr) +{ + MOZ_ASSERT(addr.base == pc, "Unsupported jump from any other addresses."); + MOZ_ASSERT(addr.offset == 0, "NYI: offsets from pc should be shifted by the number of instructions."); + + Register base = addr.base; + uint32_t scale = Imm32::ShiftOf(addr.scale).value; + + ma_ldr(DTRAddr(base, DtrRegImmShift(addr.index, LSL, scale)), pc); + // When loading from pc, the pc is shifted to the next instruction, we + // add one extra instruction to accomodate for this shifted offset. + breakpoint(); +} + // ======================================================================== // Memory access primitives. void diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index 284e68bdebea..7b2e60695eac 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -3277,22 +3277,6 @@ MacroAssemblerARMCompat::extractTag(const BaseIndex& address, Register scratch) return extractTag(Address(scratch, address.offset), scratch); } -void -MacroAssemblerARMCompat::moveValue(const Value& val, Register type, Register data) -{ - ma_mov(Imm32(val.toNunboxTag()), type); - if (val.isGCThing()) - ma_mov(ImmGCPtr(val.toGCThing()), data); - else - ma_mov(Imm32(val.toNunboxPayload()), data); -} - -void -MacroAssemblerARMCompat::moveValue(const Value& val, const ValueOperand& dest) -{ - moveValue(val, dest.typeReg(), dest.payloadReg()); -} - ///////////////////////////////////////////////////////////////// // X86/X64-common (ARM too now) interface. ///////////////////////////////////////////////////////////////// @@ -3645,7 +3629,7 @@ MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler) // No exception handler. Load the error value, load the new stack pointer // and return from the entry frame. bind(&entryFrame); - moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); + asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); { ScratchRegisterScope scratch(asMasm()); ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp, scratch); @@ -5324,6 +5308,78 @@ MacroAssembler::pushFakeReturnAddress(Register scratch) return pseudoReturnOffset; } +// =============================================================== +// Move instructions + +void +MacroAssembler::moveValue(const TypedOrValueRegister& src, const ValueOperand& dest) +{ + if (src.hasValue()) { + moveValue(src.valueReg(), dest); + return; + } + + MIRType type = src.type(); + AnyRegister reg = src.typedReg(); + + if (!IsFloatingPointType(type)) { + mov(ImmWord(MIRTypeToTag(type)), dest.typeReg()); + if (reg.gpr() != dest.payloadReg()) + mov(reg.gpr(), dest.payloadReg()); + return; + } + + ScratchDoubleScope scratch(*this); + FloatRegister freg = reg.fpu(); + if (type == MIRType::Float32) { + convertFloat32ToDouble(freg, ScratchFloat32Reg); + freg = ScratchFloat32Reg; + } + ma_vxfer(freg, dest.payloadReg(), dest.typeReg()); +} + +void +MacroAssembler::moveValue(const ValueOperand& src, const ValueOperand& dest) +{ + Register s0 = src.typeReg(); + Register s1 = src.payloadReg(); + Register d0 = dest.typeReg(); + Register d1 = dest.payloadReg(); + + // Either one or both of the source registers could be the same as a + // destination register. + if (s1 == d0) { + if (s0 == d1) { + // If both are, this is just a swap of two registers. + ScratchRegisterScope scratch(*this); + MOZ_ASSERT(d1 != scratch); + MOZ_ASSERT(d0 != scratch); + ma_mov(d1, scratch); + ma_mov(d0, d1); + ma_mov(scratch, d0); + return; + } + // If only one is, copy that source first. + mozilla::Swap(s0, s1); + mozilla::Swap(d0, d1); + } + + if (s0 != d0) + ma_mov(s0, d0); + if (s1 != d1) + ma_mov(s1, d1); +} + +void +MacroAssembler::moveValue(const Value& src, const ValueOperand& dest) +{ + ma_mov(Imm32(src.toNunboxTag()), dest.typeReg()); + if (src.isGCThing()) + ma_mov(ImmGCPtr(src.toGCThing()), dest.payloadReg()); + else + ma_mov(Imm32(src.toNunboxPayload()), dest.payloadReg()); +} + // =============================================================== // Branch functions diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index c1c54e8f2522..41dcdf9aa34c 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -819,8 +819,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void int32ValueToFloat32(const ValueOperand& operand, FloatRegister dest); void loadConstantFloat32(float f, FloatRegister dest); - void moveValue(const Value& val, Register type, Register data); - CodeOffsetJump jumpWithPatch(RepatchLabel* label, Condition cond = Always, Label* documentation = nullptr); CodeOffsetJump backedgeJump(RepatchLabel* label, Label* documentation) { @@ -856,36 +854,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM } } - void moveValue(const Value& val, const ValueOperand& dest); - - void moveValue(const ValueOperand& src, const ValueOperand& dest) { - Register s0 = src.typeReg(), d0 = dest.typeReg(), - s1 = src.payloadReg(), d1 = dest.payloadReg(); - - // Either one or both of the source registers could be the same as a - // destination register. - if (s1 == d0) { - if (s0 == d1) { - // If both are, this is just a swap of two registers. - ScratchRegisterScope scratch(asMasm()); - MOZ_ASSERT(d1 != scratch); - MOZ_ASSERT(d0 != scratch); - ma_mov(d1, scratch); - ma_mov(d0, d1); - ma_mov(scratch, d0); - return; - } - // If only one is, copy that source first. - mozilla::Swap(s0, s1); - mozilla::Swap(d0, d1); - } - - if (s0 != d0) - ma_mov(s0, d0); - if (s1 != d1) - ma_mov(s1, d1); - } - void storeValue(ValueOperand val, const Address& dst); void storeValue(ValueOperand val, const BaseIndex& dest); void storeValue(JSValueType type, Register reg, BaseIndex dest) { diff --git a/js/src/jit/arm/Trampoline-arm.cpp b/js/src/jit/arm/Trampoline-arm.cpp index 9870685a4dc1..34c7b26986a3 100644 --- a/js/src/jit/arm/Trampoline-arm.cpp +++ b/js/src/jit/arm/Trampoline-arm.cpp @@ -516,7 +516,7 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut) } // Push undefined. - masm.moveValue(UndefinedValue(), r5, r4); + masm.moveValue(UndefinedValue(), ValueOperand(r5, r4)); { Label undefLoopTop; masm.bind(&undefLoopTop); diff --git a/js/src/jit/arm64/Assembler-arm64.h b/js/src/jit/arm64/Assembler-arm64.h index 01ef93a3842b..4843f6204604 100644 --- a/js/src/jit/arm64/Assembler-arm64.h +++ b/js/src/jit/arm64/Assembler-arm64.h @@ -387,6 +387,12 @@ class Assembler : public vixl::Assembler LabelBase* label = absoluteLabel; label->bind(off.getOffset()); } + void writeCodePointer(CodeOffset* label) { + uintptr_t x = LabelBase::INVALID_OFFSET; + BufferOffset off = EmitData(&x, sizeof(uintptr_t)); + label->bind(off.getOffset()); + } + void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, const Disassembler::HeapAccess& heapAccess) diff --git a/js/src/jit/arm64/MacroAssembler-arm64-inl.h b/js/src/jit/arm64/MacroAssembler-arm64-inl.h index 908fd60d92fa..8b0d8f477522 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h +++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h @@ -1656,6 +1656,12 @@ MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMag B(label, cond); } +void +MacroAssembler::branchToComputedAddress(const BaseIndex& addr) +{ + MOZ_CRASH("branchToComputedAddress"); +} + // ======================================================================== // Memory access primitives. void diff --git a/js/src/jit/arm64/MacroAssembler-arm64.cpp b/js/src/jit/arm64/MacroAssembler-arm64.cpp index 12a493d47e59..d1c9a36d4a16 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.cpp +++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp @@ -773,6 +773,54 @@ MacroAssembler::pushFakeReturnAddress(Register scratch) return pseudoReturnOffset; } +// =============================================================== +// Move instructions + +void +MacroAssembler::moveValue(const TypedOrValueRegister& src, const ValueOperand& dest) +{ + if (src.hasValue()) { + moveValue(src.valueReg(), dest); + return; + } + + MIRType type = src.type(); + AnyRegister reg = src.typedReg(); + + if (!IsFloatingPointType(type)) { + boxNonDouble(ValueTypeFromMIRType(type), reg.gpr(), dest); + return; + } + + FloatRegister scratch = ScratchDoubleReg; + FloatRegister freg = reg.fpu(); + if (type == MIRType::Float32) { + convertFloat32ToDouble(freg, scratch); + freg = scratch; + } + boxDouble(freg, dest, scratch); +} + +void +MacroAssembler::moveValue(const ValueOperand& src, const ValueOperand& dest) +{ + if (src == dest) + return; + movePtr(src.valueReg(), dest.valueReg()); +} + +void +MacroAssembler::moveValue(const Value& src, const ValueOperand& dest) +{ + if (!src.isGCThing()) { + movePtr(ImmWord(src.asRawBits()), dest.valueReg()); + return; + } + + BufferOffset load = movePatchablePtr(ImmPtr(src.bitsAsPunboxPointer()), dest.valueReg()); + writeDataRelocation(src, load); +} + // =============================================================== // Branch functions diff --git a/js/src/jit/arm64/MacroAssembler-arm64.h b/js/src/jit/arm64/MacroAssembler-arm64.h index 59656aada1cc..8f57991a30d5 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.h +++ b/js/src/jit/arm64/MacroAssembler-arm64.h @@ -359,10 +359,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler void moveValue(const Value& src, const ValueOperand& dest) { moveValue(src, dest.valueReg()); } - void moveValue(const ValueOperand& src, const ValueOperand& dest) { - if (src.valueReg() != dest.valueReg()) - movePtr(src.valueReg(), dest.valueReg()); - } CodeOffset pushWithPatch(ImmWord imm) { vixl::UseScratchRegisterScope temps(this); diff --git a/js/src/jit/arm64/Trampoline-arm64.cpp b/js/src/jit/arm64/Trampoline-arm64.cpp index 1592751ce3fe..bb41b9888f71 100644 --- a/js/src/jit/arm64/Trampoline-arm64.cpp +++ b/js/src/jit/arm64/Trampoline-arm64.cpp @@ -373,7 +373,7 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut) masm.Sub(w2, w6, w8); // Put an undefined in a register so it can be pushed. - masm.moveValue(UndefinedValue(), r4); + masm.moveValue(UndefinedValue(), ValueOperand(r4)); // Push undefined N times. { diff --git a/js/src/jit/mips32/MacroAssembler-mips32.cpp b/js/src/jit/mips32/MacroAssembler-mips32.cpp index f9c113309fe0..7611e9f98d40 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32.cpp +++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp @@ -1488,19 +1488,6 @@ MacroAssemblerMIPSCompat::moveData(const Value& val, Register data) ma_li(data, Imm32(val.toNunboxPayload())); } -void -MacroAssemblerMIPSCompat::moveValue(const Value& val, Register type, Register data) -{ - MOZ_ASSERT(type != data); - ma_li(type, Imm32(getType(val))); - moveData(val, data); -} -void -MacroAssemblerMIPSCompat::moveValue(const Value& val, const ValueOperand& dest) -{ - moveValue(val, dest.typeReg(), dest.payloadReg()); -} - /* There are 3 paths trough backedge jump. They are listed here in the order * in which instructions are executed. * - The short jump is simple: @@ -1866,7 +1853,7 @@ MacroAssemblerMIPSCompat::handleFailureWithHandlerTail(void* handler) // No exception handler. Load the error value, load the new stack pointer // and return from the entry frame. bind(&entryFrame); - moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); + asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), StackPointer); // We're going to be returning by the ion calling convention diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 7b56b6f85502..3bd047084e30 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -5744,6 +5744,47 @@ class LUnboxObjectOrNull : public LInstructionHelper<1, 1, 0> } }; +// Load an element from a non-allocated entity represented by its state object +// such as ArgumentState. The elements of the state object are set as operands +// of this variadic LIR instruction and inlined in the code generated for this +// instruction. +// +// Each element is represented with BOX_PIECES allocations, even if 1 (typed +// register) or 0 (constants) is enough. In such case, the unused allocations +// would be bogus. +class LLoadElementFromStateV : public LVariadicInstruction +{ + public: + LIR_HEADER(LoadElementFromStateV) + + LLoadElementFromStateV(const LDefinition& temp0, const LDefinition& temp1, + const LDefinition& tempD) + { + setTemp(0, temp0); + setTemp(1, temp1); + setTemp(2, tempD); + } + + const MLoadElementFromState* mir() const { + return mir_->toLoadElementFromState(); + } + const LAllocation* index() { + return getOperand(0); + } + const LDefinition* temp0() { + return getTemp(0); + } + const LDefinition* temp1() { + return getTemp(1); + } + const LDefinition* tempD() { + return getTemp(2); + } + MDefinition* array() { + return mir()->array(); + } +}; + // Store a boxed value to a dense array's element vector. class LStoreElementV : public LInstructionHelper<0, 2 + BOX_PIECES, 0> { @@ -9277,6 +9318,25 @@ class LFinishBoundFunctionInit : public LInstructionHelper<0, 3, 2> } }; +class LIsPackedArray : public LInstructionHelper<1, 1, 1> +{ + public: + LIR_HEADER(IsPackedArray) + + LIsPackedArray(const LAllocation& array, const LDefinition& temp) + { + setOperand(0, array); + setTemp(0, temp); + } + + const LAllocation* array() { + return getOperand(0); + } + const LDefinition* temp() { + return getTemp(0); + } +}; + } // namespace jit } // namespace js diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 97959e788cf3..eceeef79b5f8 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -283,6 +283,7 @@ _(LoadUnboxedScalar) \ _(LoadUnboxedPointerV) \ _(LoadUnboxedPointerT) \ + _(LoadElementFromStateV) \ _(UnboxObjectOrNull) \ _(StoreElementV) \ _(StoreElementT) \ @@ -414,6 +415,7 @@ _(CheckObjCoercible) \ _(DebugCheckSelfHosted) \ _(FinishBoundFunctionInit) \ + _(IsPackedArray) \ _(AsmJSLoadHeap) \ _(AsmJSStoreHeap) \ _(AsmJSCompareExchangeHeap) \ diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h index a89c95887816..e2c8e996bc4a 100644 --- a/js/src/jit/shared/Lowering-shared-inl.h +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -159,9 +159,9 @@ LIRGeneratorShared::defineInt64ReuseInput(LInstructionHelper void -LIRGeneratorShared::defineBox(LInstructionHelper* lir, MDefinition* mir, - LDefinition::Policy policy) +template void +LIRGeneratorShared::defineBox(details::LInstructionFixedDefsTempsHelper* lir, + MDefinition* mir, LDefinition::Policy policy) { // Call instructions should use defineReturn. MOZ_ASSERT(!lir->isCall()); diff --git a/js/src/jit/shared/Lowering-shared.h b/js/src/jit/shared/Lowering-shared.h index b3ecd6e9be0f..a4f14c68d5bc 100644 --- a/js/src/jit/shared/Lowering-shared.h +++ b/js/src/jit/shared/Lowering-shared.h @@ -157,9 +157,9 @@ class LIRGeneratorShared : public MDefinitionVisitor inline void defineFixed(LInstructionHelper<1, Ops, Temps>* lir, MDefinition* mir, const LAllocation& output); - template - inline void defineBox(LInstructionHelper* lir, MDefinition* mir, - LDefinition::Policy policy = LDefinition::REGISTER); + template + inline void defineBox(details::LInstructionFixedDefsTempsHelper* lir, + MDefinition* mir, LDefinition::Policy policy = LDefinition::REGISTER); template inline void defineInt64(LInstructionHelper* lir, MDefinition* mir, diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index 56b11ffe2c08..e5b6ce0f3959 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -75,27 +75,17 @@ FrameSizeClass::frameSize() const void CodeGeneratorX64::visitValue(LValue* value) { - LDefinition* reg = value->getDef(0); - masm.moveValue(value->value(), ToRegister(reg)); + ValueOperand result = GetValueOutput(value); + masm.moveValue(value->value(), result); } void CodeGeneratorX64::visitBox(LBox* box) { const LAllocation* in = box->getOperand(0); - const LDefinition* result = box->getDef(0); + ValueOperand result = GetValueOutput(box); - if (IsFloatingPointType(box->type())) { - ScratchDoubleScope scratch(masm); - FloatRegister reg = ToFloatRegister(in); - if (box->type() == MIRType::Float32) { - masm.convertFloat32ToDouble(reg, scratch); - reg = scratch; - } - masm.vmovq(reg, ToRegister(result)); - } else { - masm.boxValue(ValueTypeFromMIRType(box->type()), ToRegister(in), ToRegister(result)); - } + masm.moveValue(TypedOrValueRegister(box->type(), ToAnyRegister(in)), result); } void @@ -165,7 +155,7 @@ CodeGeneratorX64::visitCompareB(LCompareB* lir) // Load boxed boolean in ScratchReg. ScratchRegisterScope scratch(masm); if (rhs->isConstant()) - masm.moveValue(rhs->toConstant()->toJSValue(), scratch); + masm.moveValue(rhs->toConstant()->toJSValue(), ValueOperand(scratch)); else masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), scratch); @@ -187,7 +177,7 @@ CodeGeneratorX64::visitCompareBAndBranch(LCompareBAndBranch* lir) // Load boxed boolean in ScratchReg. ScratchRegisterScope scratch(masm); if (rhs->isConstant()) - masm.moveValue(rhs->toConstant()->toJSValue(), scratch); + masm.moveValue(rhs->toConstant()->toJSValue(), ValueOperand(scratch)); else masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), scratch); diff --git a/js/src/jit/x64/MacroAssembler-x64-inl.h b/js/src/jit/x64/MacroAssembler-x64-inl.h index 8c6e542ce7ca..adff1860729b 100644 --- a/js/src/jit/x64/MacroAssembler-x64-inl.h +++ b/js/src/jit/x64/MacroAssembler-x64-inl.h @@ -772,6 +772,13 @@ MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMag cmpPtr(valaddr, ImmWord(magic)); j(cond, label); } + +void +MacroAssembler::branchToComputedAddress(const BaseIndex& address) +{ + jmp(Operand(address)); +} + // ======================================================================== // Truncate floating point. diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index cd14304f287d..db8099cff3d2 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -327,7 +327,7 @@ MacroAssemblerX64::handleFailureWithHandlerTail(void* handler) // No exception handler. Load the error value, load the new stack pointer // and return from the entry frame. bind(&entryFrame); - moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); + asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); loadPtr(Address(rsp, offsetof(ResumeFromException, stackPointer)), rsp); ret(); @@ -560,6 +560,49 @@ MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result) callWithABIPost(stackAdjust, result); } +// =============================================================== +// Move instructions + +void +MacroAssembler::moveValue(const TypedOrValueRegister& src, const ValueOperand& dest) +{ + if (src.hasValue()) { + moveValue(src.valueReg(), dest); + return; + } + + MIRType type = src.type(); + AnyRegister reg = src.typedReg(); + + if (!IsFloatingPointType(type)) { + boxValue(ValueTypeFromMIRType(type), reg.gpr(), dest.valueReg()); + return; + } + + ScratchDoubleScope scratch(*this); + FloatRegister freg = reg.fpu(); + if (type == MIRType::Float32) { + convertFloat32ToDouble(freg, scratch); + freg = scratch; + } + vmovq(freg, dest.valueReg()); +} + +void +MacroAssembler::moveValue(const ValueOperand& src, const ValueOperand& dest) +{ + if (src == dest) + return; + movq(src.valueReg(), dest.valueReg()); +} + +void +MacroAssembler::moveValue(const Value& src, const ValueOperand& dest) +{ + movWithPatch(ImmWord(src.asRawBits()), dest.valueReg()); + writeDataRelocation(src); +} + // =============================================================== // Branch functions @@ -618,7 +661,7 @@ MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, MOZ_ASSERT(cond == Equal || cond == NotEqual); ScratchRegisterScope scratch(*this); MOZ_ASSERT(lhs.valueReg() != scratch); - moveValue(rhs, scratch); + moveValue(rhs, ValueOperand(scratch)); cmpPtr(lhs.valueReg(), scratch); j(cond, label); } diff --git a/js/src/jit/x64/MacroAssembler-x64.h b/js/src/jit/x64/MacroAssembler-x64.h index 74020c305ce8..811aae9b2669 100644 --- a/js/src/jit/x64/MacroAssembler-x64.h +++ b/js/src/jit/x64/MacroAssembler-x64.h @@ -189,17 +189,6 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared push(Operand(addr)); } - void moveValue(const Value& val, Register dest) { - movWithPatch(ImmWord(val.asRawBits()), dest); - writeDataRelocation(val); - } - void moveValue(const Value& src, const ValueOperand& dest) { - moveValue(src, dest.valueReg()); - } - void moveValue(const ValueOperand& src, const ValueOperand& dest) { - if (src.valueReg() != dest.valueReg()) - movq(src.valueReg(), dest.valueReg()); - } void boxValue(JSValueType type, Register src, Register dest); Condition testUndefined(Condition cond, Register tag) { diff --git a/js/src/jit/x64/Trampoline-x64.cpp b/js/src/jit/x64/Trampoline-x64.cpp index 2b1a1dd35496..e02e86eb65f5 100644 --- a/js/src/jit/x64/Trampoline-x64.cpp +++ b/js/src/jit/x64/Trampoline-x64.cpp @@ -462,7 +462,7 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut) // Copy the number of actual arguments masm.loadPtr(Address(rsp, RectifierFrameLayout::offsetOfNumActualArgs()), rdx); - masm.moveValue(UndefinedValue(), r10); + masm.moveValue(UndefinedValue(), ValueOperand(r10)); masm.movq(rsp, r9); // Save %rsp. diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 10cf37e62ad5..4b6532f948d1 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -1871,10 +1871,10 @@ CodeGeneratorX86Shared::emitTableSwitchDispatch(MTableSwitch* mir, Register inde // Compute the position where a pointer to the right case stands. masm.mov(ool->jumpLabel()->patchAt(), base); - Operand pointer = Operand(base, index, ScalePointer); + BaseIndex pointer(base, index, ScalePointer); // Jump to the right case - masm.jmp(pointer); + masm.branchToComputedAddress(pointer); } void diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp index 102d8260eb3d..c61fe928b6f9 100644 --- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -111,15 +111,10 @@ CodeGeneratorX86::visitBox(LBox* box) void CodeGeneratorX86::visitBoxFloatingPoint(LBoxFloatingPoint* box) { - const LAllocation* in = box->getOperand(0); + const AnyRegister in = ToAnyRegister(box->getOperand(0)); const ValueOperand out = ToOutValue(box); - FloatRegister reg = ToFloatRegister(in); - if (box->type() == MIRType::Float32) { - masm.convertFloat32ToDouble(reg, ScratchFloat32Reg); - reg = ScratchFloat32Reg; - } - masm.boxDouble(reg, out, reg); + masm.moveValue(TypedOrValueRegister(box->type(), in), out); } void diff --git a/js/src/jit/x86/MacroAssembler-x86-inl.h b/js/src/jit/x86/MacroAssembler-x86-inl.h index d4c4ea3a9b35..2c3ae4688f8a 100644 --- a/js/src/jit/x86/MacroAssembler-x86-inl.h +++ b/js/src/jit/x86/MacroAssembler-x86-inl.h @@ -922,6 +922,12 @@ MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMag branch32(cond, ToPayload(valaddr), Imm32(why), label); } +void +MacroAssembler::branchToComputedAddress(const BaseIndex& addr) +{ + jmp(Operand(addr)); +} + // ======================================================================== // Truncate floating point. diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index a674478f1f98..fa5db7ef9571 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -228,7 +228,7 @@ MacroAssemblerX86::handleFailureWithHandlerTail(void* handler) // No exception handler. Load the error value, load the new stack pointer // and return from the entry frame. bind(&entryFrame); - moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); + asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); loadPtr(Address(esp, offsetof(ResumeFromException, stackPointer)), esp); ret(); @@ -455,6 +455,73 @@ MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result) callWithABIPost(stackAdjust, result); } +// =============================================================== +// Move instructions + +void +MacroAssembler::moveValue(const TypedOrValueRegister& src, const ValueOperand& dest) +{ + if (src.hasValue()) { + moveValue(src.valueReg(), dest); + return; + } + + MIRType type = src.type(); + AnyRegister reg = src.typedReg(); + + if (!IsFloatingPointType(type)) { + mov(ImmWord(MIRTypeToTag(type)), dest.typeReg()); + if (reg.gpr() != dest.payloadReg()) + movl(reg.gpr(), dest.payloadReg()); + return; + } + + ScratchDoubleScope scratch(*this); + FloatRegister freg = reg.fpu(); + if (type == MIRType::Float32) { + convertFloat32ToDouble(freg, scratch); + freg = scratch; + } + boxDouble(freg, dest, scratch); +} + +void +MacroAssembler::moveValue(const ValueOperand& src, const ValueOperand& dest) +{ + Register s0 = src.typeReg(); + Register s1 = src.payloadReg(); + Register d0 = dest.typeReg(); + Register d1 = dest.payloadReg(); + + // Either one or both of the source registers could be the same as a + // destination register. + if (s1 == d0) { + if (s0 == d1) { + // If both are, this is just a swap of two registers. + xchgl(d0, d1); + return; + } + // If only one is, copy that source first. + mozilla::Swap(s0, s1); + mozilla::Swap(d0, d1); + } + + if (s0 != d0) + movl(s0, d0); + if (s1 != d1) + movl(s1, d1); +} + +void +MacroAssembler::moveValue(const Value& src, const ValueOperand& dest) +{ + movl(Imm32(src.toNunboxTag()), dest.typeReg()); + if (src.isGCThing()) + movl(ImmGCPtr(src.toGCThing()), dest.payloadReg()); + else + movl(Imm32(src.toNunboxPayload()), dest.payloadReg()); +} + // =============================================================== // Branch functions diff --git a/js/src/jit/x86/MacroAssembler-x86.h b/js/src/jit/x86/MacroAssembler-x86.h index 9981f58c2e7e..58d4122162cc 100644 --- a/js/src/jit/x86/MacroAssembler-x86.h +++ b/js/src/jit/x86/MacroAssembler-x86.h @@ -92,38 +92,6 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared Address ToType(Address base) { return ToType(Operand(base)).toAddress(); } - void moveValue(const Value& val, Register type, Register data) { - movl(Imm32(val.toNunboxTag()), type); - if (val.isGCThing()) - movl(ImmGCPtr(val.toGCThing()), data); - else - movl(Imm32(val.toNunboxPayload()), data); - } - void moveValue(const Value& val, const ValueOperand& dest) { - moveValue(val, dest.typeReg(), dest.payloadReg()); - } - void moveValue(const ValueOperand& src, const ValueOperand& dest) { - Register s0 = src.typeReg(), d0 = dest.typeReg(), - s1 = src.payloadReg(), d1 = dest.payloadReg(); - - // Either one or both of the source registers could be the same as a - // destination register. - if (s1 == d0) { - if (s0 == d1) { - // If both are, this is just a swap of two registers. - xchgl(d0, d1); - return; - } - // If only one is, copy that source first. - mozilla::Swap(s0, s1); - mozilla::Swap(d0, d1); - } - - if (s0 != d0) - movl(s0, d0); - if (s1 != d1) - movl(s1, d1); - } ///////////////////////////////////////////////////////////////// // X86/X64-common interface. diff --git a/js/src/jit/x86/Trampoline-x86.cpp b/js/src/jit/x86/Trampoline-x86.cpp index b3879caacbee..a352cc8d4b6e 100644 --- a/js/src/jit/x86/Trampoline-x86.cpp +++ b/js/src/jit/x86/Trampoline-x86.cpp @@ -440,7 +440,7 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut) // Copy the number of actual arguments. masm.loadPtr(Address(esp, RectifierFrameLayout::offsetOfNumActualArgs()), edx); - masm.moveValue(UndefinedValue(), ebx, edi); + masm.moveValue(UndefinedValue(), ValueOperand(ebx, edi)); // NOTE: The fact that x86 ArgumentsRectifier saves the FramePointer is relied upon // by the baseline bailout code. If this changes, fix that code! See diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index bd97afd7c3c0..d5db8356746c 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -2049,7 +2049,7 @@ js::array_sort(JSContext* cx, unsigned argc, Value* vp) RootedValue fval(cx); if (args.hasDefined(0)) { - if (args[0].isPrimitive()) { + if (!IsCallable(args[0])) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_SORT_ARG); return false; } diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index fc589d9730be..17db678d8d6a 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1026,8 +1026,6 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint) } } - bool funIsNonArrowLambda = fun->isLambda() && !fun->isArrow(); - // Default class constructors are self-hosted, but have their source // objects overridden to refer to the span of the class statement or // expression. Non-default class constructors are never self-hosted. So, @@ -1037,10 +1035,7 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint) // If we're not in pretty mode, put parentheses around lambda functions // so that eval returns lambda, not function statement. - if (haveSource && !prettyPrint && funIsNonArrowLambda) { - if (!out.append("(")) - return nullptr; - } + bool addParentheses = haveSource && !prettyPrint && (fun->isLambda() && !fun->isArrow()); if (haveSource && !script->scriptSource()->hasSourceData() && !JSScript::loadSource(cx, script->scriptSource(), &haveSource)) @@ -1048,76 +1043,69 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint) return nullptr; } - auto AppendPrelude = [cx, &out, &fun]() { + if (addParentheses) { + if (!out.append('(')) + return nullptr; + } + + if (haveSource) { + if (!script->appendSourceDataForToString(cx, out)) + return nullptr; + } else { if (fun->isAsync()) { if (!out.append("async ")) - return false; + return nullptr; } if (!fun->isArrow()) { if (!out.append("function")) - return false; + return nullptr; if (fun->isStarGenerator()) { if (!out.append('*')) - return false; + return nullptr; } } if (fun->explicitName()) { if (!out.append(' ')) - return false; + return nullptr; if (fun->isBoundFunction() && !fun->hasBoundFunctionNamePrefix()) { if (!out.append(cx->names().boundWithSpace)) - return false; + return nullptr; } if (!out.append(fun->explicitName())) - return false; - } - return true; - }; - - if (haveSource) { - if (!script->appendSourceDataForToString(cx, out)) - return nullptr; - - if (!prettyPrint && funIsNonArrowLambda) { - if (!out.append(")")) return nullptr; } - } else if (fun->isInterpreted() && - (!fun->isSelfHostedBuiltin() || - fun->infallibleIsDefaultClassConstructor(cx))) - { - // Default class constructors should always haveSource except; - // - // 1. Source has been discarded for the whole compartment. - // - // 2. The source is marked as "lazy", i.e., retrieved on demand, and - // the embedding has not provided a hook to retrieve sources. - MOZ_ASSERT_IF(fun->infallibleIsDefaultClassConstructor(cx), - !cx->runtime()->sourceHook.ref() || - !script->scriptSource()->sourceRetrievable() || - fun->compartment()->behaviors().discardSource()); - if (!AppendPrelude() || - !out.append("() {\n ") || - !out.append("[sourceless code]") || - !out.append("\n}")) + if (fun->isInterpreted() && + (!fun->isSelfHostedBuiltin() || + fun->infallibleIsDefaultClassConstructor(cx))) { - return nullptr; + // Default class constructors should always haveSource except; + // + // 1. Source has been discarded for the whole compartment. + // + // 2. The source is marked as "lazy", i.e., retrieved on demand, and + // the embedding has not provided a hook to retrieve sources. + MOZ_ASSERT_IF(fun->infallibleIsDefaultClassConstructor(cx), + !cx->runtime()->sourceHook.ref() || + !script->scriptSource()->sourceRetrievable() || + fun->compartment()->behaviors().discardSource()); + + if (!out.append("() {\n [sourceless code]\n}")) + return nullptr; + } else { + if (!out.append("() {\n [native code]\n}")) + return nullptr; } - } else { - if (!AppendPrelude() || - !out.append("() {\n ")) - return nullptr; + } - if (!out.append("[native code]")) - return nullptr; - - if (!out.append("\n}")) + if (addParentheses) { + if (!out.append(')')) return nullptr; } + return out.finishString(); } diff --git a/js/src/tests/ecma_2018/Array/browser.js b/js/src/tests/ecma_2018/Array/browser.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_2018/Array/shell.js b/js/src/tests/ecma_2018/Array/shell.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_2018/Array/sort-non-function.js b/js/src/tests/ecma_2018/Array/sort-non-function.js new file mode 100644 index 000000000000..91e304593231 --- /dev/null +++ b/js/src/tests/ecma_2018/Array/sort-non-function.js @@ -0,0 +1,22 @@ +// Array.prototype.sort throws if the comparator is neither undefined nor +// a callable object. + +// Use a zero length array, so we can provide any kind of callable object +// without worrying that the function is actually a valid comparator function. +const array = new Array(0); + +// Throws if the comparator is neither undefined nor callable. +for (let invalidComparator of [null, 0, true, Symbol(), {}, []]) { + assertThrowsInstanceOf(() => array.sort(invalidComparator), TypeError); +} + +// Doesn't throw if the comparator is undefined or a callable object. +for (let validComparator of [undefined, () => {}, Math.max, class {}, new Proxy(function(){}, {})]) { + array.sort(validComparator); +} + +// Also doesn't throw if no comparator was provided at all. +array.sort(); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_2018/TypedArray/sort-non-function.js b/js/src/tests/ecma_2018/TypedArray/sort-non-function.js new file mode 100644 index 000000000000..bea810457a2e --- /dev/null +++ b/js/src/tests/ecma_2018/TypedArray/sort-non-function.js @@ -0,0 +1,22 @@ +// %TypedArray%.prototype.sort throws if the comparator is neither undefined nor +// a callable object. + +// Use a zero length typed array, so we can provide any kind of callable object +// without worrying that the function is actually a valid comparator function. +const typedArray = new Int32Array(0); + +// Throws if the comparator is neither undefined nor callable. +for (let invalidComparator of [null, 0, true, Symbol(), {}, []]) { + assertThrowsInstanceOf(() => typedArray.sort(invalidComparator), TypeError); +} + +// Doesn't throw if the comparator is undefined or a callable object. +for (let validComparator of [undefined, () => {}, Math.max, class {}, new Proxy(function(){}, {})]) { + typedArray.sort(validComparator); +} + +// Also doesn't throw if no comparator was provided at all. +typedArray.sort(); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/TypedArray/sort_errors.js b/js/src/tests/ecma_6/TypedArray/sort_errors.js index 2b8921be95e9..603768e39241 100644 --- a/js/src/tests/ecma_6/TypedArray/sort_errors.js +++ b/js/src/tests/ecma_6/TypedArray/sort_errors.js @@ -43,7 +43,7 @@ if (typeof newGlobal === "function" && typeof detachArrayBuffer === "function") detachArrayBuffer(ta.buffer); return a - b; }); - }, TypeError); + }, otherGlobal.TypeError); } // Ensure that TypedArray.prototype.sort will not sort non-TypedArrays diff --git a/js/src/tests/js1_8_5/extensions/collect-gray.js b/js/src/tests/js1_8_5/extensions/collect-gray.js index 8b37a6193265..443b555a4e57 100644 --- a/js/src/tests/js1_8_5/extensions/collect-gray.js +++ b/js/src/tests/js1_8_5/extensions/collect-gray.js @@ -9,6 +9,9 @@ var summary = print(BUGNUMBER + ": " + summary); +if (typeof gczeal !== 'undefined') + gczeal(0); + grayRoot().x = Object.create(null); addMarkObservers([grayRoot(), grayRoot().x, this, Object.create(null)]); gc(); @@ -68,9 +71,6 @@ assertEq(marks[3], 'gray', 'black map, gray key => gray value'); // must be gray (unless otherwise reachable from black.) If neither a nor b is // marked at all, then they will not keep x alive. -if (typeof gczeal !== 'undefined') - gczeal(0); - clearMarkObservers(); // Black map, gray delegate => gray key diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index a0f2e5b0b76c..406706326583 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -2301,7 +2301,8 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_INLINABLE_FN("UnsafeGetBooleanFromReservedSlot", intrinsic_UnsafeGetBooleanFromReservedSlot,2,0, IntrinsicUnsafeGetBooleanFromReservedSlot), - JS_FN("IsPackedArray", intrinsic_IsPackedArray, 1,0), + JS_INLINABLE_FN("IsPackedArray", intrinsic_IsPackedArray, 1,0, + IntrinsicIsPackedArray), JS_FN("GetIteratorPrototype", intrinsic_GetIteratorPrototype, 0,0), diff --git a/js/xpconnect/src/XPCMaps.cpp b/js/xpconnect/src/XPCMaps.cpp index 539fcd887042..bb99b9f8c034 100644 --- a/js/xpconnect/src/XPCMaps.cpp +++ b/js/xpconnect/src/XPCMaps.cpp @@ -17,13 +17,13 @@ using namespace mozilla; /***************************************************************************/ // static shared... -// Note this is returning the bit pattern of the first part of the nsID, not -// the pointer to the nsID. +// Note this is returning the hash of the bit pattern of the first part of the nsID, not +// the hash of the pointer to the nsID. static PLDHashNumber HashIIDPtrKey(const void* key) { - return *((js::HashNumber*)key); + return HashGeneric(*((uintptr_t*)key)); } static bool diff --git a/js/xpconnect/src/XPCWrappedNativeInfo.cpp b/js/xpconnect/src/XPCWrappedNativeInfo.cpp index fe7dad265fa6..1cd3a2a0c0ad 100644 --- a/js/xpconnect/src/XPCWrappedNativeInfo.cpp +++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp @@ -13,6 +13,7 @@ #include "mozilla/XPTInterfaceInfoManager.h" #include "nsIScriptError.h" #include "nsPrintfCString.h" +#include "nsPointerHashKeys.h" using namespace JS; using namespace mozilla; @@ -429,7 +430,7 @@ XPCNativeInterface::DebugDump(int16_t depth) static PLDHashNumber HashPointer(const void* ptr) { - return NS_PTR_TO_UINT32(ptr) >> 2; + return nsPtrHashKey::HashKey(ptr); } PLDHashNumber diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 0e318fb97e4c..52a59575168f 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -1338,13 +1338,48 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList) { NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), "Someone forgot a script blocker"); - MOZ_ASSERT(!mDestroyedFrames); + + // See bug 1378219 comment 9: + // Recursive calls here are a bit worrying, but apparently do happen in the + // wild (although not currently in any of our automated tests). Try to get a + // stack from Nightly/Dev channel to figure out what's going on and whether + // it's OK. + MOZ_DIAGNOSTIC_ASSERT(!mDestroyedFrames, "ProcessRestyledFrames recursion"); if (aChangeList.IsEmpty()) { return; } - mDestroyedFrames = MakeUnique>>(); + // If mDestroyedFrames is null, we want to create a new hashtable here + // and destroy it on exit; but if it is already non-null (because we're in + // a recursive call), we will continue to use the existing table to + // accumulate destroyed frames, and NOT clear mDestroyedFrames on exit. + // We use a MaybeClearDestroyedFrames helper to conditionally reset the + // mDestroyedFrames pointer when this method returns. + typedef decltype(mDestroyedFrames) DestroyedFramesT; + class MOZ_RAII MaybeClearDestroyedFrames + { + private: + DestroyedFramesT& mDestroyedFramesRef; // ref to caller's mDestroyedFrames + const bool mResetOnDestruction; + public: + explicit MaybeClearDestroyedFrames(DestroyedFramesT& aTarget) + : mDestroyedFramesRef(aTarget) + , mResetOnDestruction(!aTarget) // reset only if target starts out null + { + } + ~MaybeClearDestroyedFrames() + { + if (mResetOnDestruction) { + mDestroyedFramesRef.reset(nullptr); + } + } + }; + + MaybeClearDestroyedFrames maybeClear(mDestroyedFrames); + if (!mDestroyedFrames) { + mDestroyedFrames = MakeUnique>>(); + } AUTO_PROFILER_LABEL("RestyleManager::ProcessRestyledFrames", CSS); @@ -1719,7 +1754,6 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList) } frameConstructor->EndUpdate(); - mDestroyedFrames.reset(nullptr); #ifdef DEBUG // Verify the style tree. Note that this needs to happen once we've diff --git a/media/webrtc/trunk/webrtc/modules/audio_processing/logging/apm_data_dumper.cc b/media/webrtc/trunk/webrtc/modules/audio_processing/logging/apm_data_dumper.cc index e19009d276ef..4629cad2fc77 100644 --- a/media/webrtc/trunk/webrtc/modules/audio_processing/logging/apm_data_dumper.cc +++ b/media/webrtc/trunk/webrtc/modules/audio_processing/logging/apm_data_dumper.cc @@ -29,11 +29,6 @@ std::string FormFileName(const char* name, int instance_index, int reinit_index, const std::string& suffix) { - char path[1024]; - AECDebugFilenameBase(path, sizeof(path)); - - char* end = path + strlen(path) - 1; - #ifdef WEBRTC_WIN char sep = '\\'; #else @@ -41,10 +36,13 @@ std::string FormFileName(const char* name, #endif std::stringstream ss; - ss << path; - if (*end != sep) { + std::string base = webrtc::Trace::aec_debug_filename(); + ss << base; + + if (base.length() && base.back() != sep) { ss << sep; } + ss << name << "_" << instance_index << "-" << reinit_index << suffix; return ss.str(); } diff --git a/media/webrtc/trunk/webrtc/modules/audio_processing/logging/apm_data_dumper.h b/media/webrtc/trunk/webrtc/modules/audio_processing/logging/apm_data_dumper.h index 8e558ccda3d4..8350179a9a7e 100644 --- a/media/webrtc/trunk/webrtc/modules/audio_processing/logging/apm_data_dumper.h +++ b/media/webrtc/trunk/webrtc/modules/audio_processing/logging/apm_data_dumper.h @@ -21,6 +21,7 @@ #include "webrtc/base/array_view.h" #include "webrtc/base/constructormagic.h" #include "webrtc/common_audio/wav_file.h" +#include "webrtc/system_wrappers/include/trace.h" // Check to verify that the define is properly set. #if !defined(WEBRTC_APM_DEBUG_DUMP) || \ @@ -28,15 +29,6 @@ #error "Set WEBRTC_APM_DEBUG_DUMP to either 0 or 1" #endif -#if WEBRTC_APM_DEBUG_DUMP == 1 -extern "C" { - extern int AECDebug(); - extern uint32_t AECDebugMaxSize(); - extern void AECDebugEnable(uint32_t enable); - extern void AECDebugFilenameBase(char *buffer, size_t size); -} -#endif - namespace webrtc { #if WEBRTC_APM_DEBUG_DUMP == 1 @@ -70,7 +62,7 @@ class ApmDataDumper { // various formats. void DumpRaw(const char* name, int v_length, const float* v) { #if WEBRTC_APM_DEBUG_DUMP == 1 - if (AECDebug()) { + if (webrtc::Trace::aec_debug()) { FILE* file = GetRawFile(name); if (file) { fwrite(v, sizeof(v[0]), v_length, file); @@ -87,7 +79,7 @@ class ApmDataDumper { void DumpRaw(const char* name, int v_length, const bool* v) { #if WEBRTC_APM_DEBUG_DUMP == 1 - if (AECDebug()) { + if (webrtc::Trace::aec_debug()) { FILE* file = GetRawFile(name); if (file) { for (int k = 0; k < v_length; ++k) { @@ -107,7 +99,7 @@ class ApmDataDumper { void DumpRaw(const char* name, int v_length, const int16_t* v) { #if WEBRTC_APM_DEBUG_DUMP == 1 - if (AECDebug()) { + if (webrtc::Trace::aec_debug()) { FILE* file = GetRawFile(name); if (file) { fwrite(v, sizeof(v[0]), v_length, file); @@ -124,7 +116,7 @@ class ApmDataDumper { void DumpRaw(const char* name, int v_length, const int32_t* v) { #if WEBRTC_APM_DEBUG_DUMP == 1 - if (AECDebug()) { + if (webrtc::Trace::aec_debug()) { FILE* file = GetRawFile(name); if (file) { fwrite(v, sizeof(v[0]), v_length, file); @@ -145,7 +137,7 @@ class ApmDataDumper { int sample_rate_hz, int num_channels) { #if WEBRTC_APM_DEBUG_DUMP == 1 - if (AECDebug()) { + if (webrtc::Trace::aec_debug()) { WavWriter* file = GetWavFile(name, sample_rate_hz, num_channels); file->WriteSamples(v, v_length); // Cheat and use aec_near as a stand-in for "size of the largest file" @@ -183,8 +175,8 @@ class ApmDataDumper { void updateDebugWritten(uint32_t amount) { debug_written_ += amount; // Limit largest files to a specific (rough) size, to avoid filling up disk. - if (debug_written_ >= AECDebugMaxSize()) { - AECDebugEnable(0); + if (debug_written_ >= webrtc::Trace::aec_debug_size()) { + webrtc::Trace::set_aec_debug(false); } } diff --git a/media/webrtc/trunk/webrtc/system_wrappers/include/trace.h b/media/webrtc/trunk/webrtc/system_wrappers/include/trace.h index 32359869e8b4..e6340fc64698 100644 --- a/media/webrtc/trunk/webrtc/system_wrappers/include/trace.h +++ b/media/webrtc/trunk/webrtc/system_wrappers/include/trace.h @@ -60,7 +60,7 @@ class Trace { static void set_aec_debug_size(uint32_t size) { aec_debug_size_ = size; } static bool aec_debug() { return aec_debug_; } static uint32_t aec_debug_size() { return aec_debug_size_; } - static void aec_debug_filename(char *buffer, size_t size); + static std::string aec_debug_filename(); static void set_aec_debug_filename(const char* filename) { aec_filename_base_ = filename; } @@ -98,13 +98,6 @@ class Trace { static std::string aec_filename_base_; }; -extern "C" { - extern int AECDebug(); - extern uint32_t AECDebugMaxSize(); - extern void AECDebugEnable(uint32_t enable); - extern void AECDebugFilenameBase(char *buffer, size_t size); -} - } // namespace webrtc #endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_TRACE_H_ diff --git a/media/webrtc/trunk/webrtc/system_wrappers/source/trace_impl.cc b/media/webrtc/trunk/webrtc/system_wrappers/source/trace_impl.cc index d80d45c83ad8..1d38f2c00ef2 100644 --- a/media/webrtc/trunk/webrtc/system_wrappers/source/trace_impl.cc +++ b/media/webrtc/trunk/webrtc/system_wrappers/source/trace_impl.cc @@ -29,15 +29,6 @@ #pragma warning(disable:4355) #endif // _WIN32 -extern "C" { - int AECDebug() { return (int) webrtc::Trace::aec_debug(); } - uint32_t AECDebugMaxSize() { return webrtc::Trace::aec_debug_size(); } - void AECDebugEnable(uint32_t enable) { webrtc::Trace::set_aec_debug(!!enable); } - void AECDebugFilenameBase(char *buffer, size_t size) { - webrtc::Trace::aec_debug_filename(buffer, size); - } -} - namespace webrtc { const int Trace::kBoilerplateLength = 71; @@ -48,9 +39,8 @@ bool Trace::aec_debug_ = false; uint32_t Trace::aec_debug_size_ = 4*1024*1024; std::string Trace::aec_filename_base_; -void Trace::aec_debug_filename(char *buffer, size_t size) { - strncpy(buffer, aec_filename_base_.c_str(), size-1); - buffer[size-1] = '\0'; +std::string Trace::aec_debug_filename() { + return aec_filename_base_; } // Construct On First Use idiom. Avoids "static initialization order fiasco". diff --git a/mfbt/HashFunctions.h b/mfbt/HashFunctions.h index eeb192c36432..1a88e86bfe00 100644 --- a/mfbt/HashFunctions.h +++ b/mfbt/HashFunctions.h @@ -122,11 +122,7 @@ AddU32ToHash(uint32_t aHash, uint32_t aValue) */ template inline uint32_t -AddUintptrToHash(uint32_t aHash, uintptr_t aValue); - -template<> -inline uint32_t -AddUintptrToHash<4>(uint32_t aHash, uintptr_t aValue) +AddUintptrToHash(uint32_t aHash, uintptr_t aValue) { return AddU32ToHash(aHash, static_cast(aValue)); } @@ -135,13 +131,6 @@ template<> inline uint32_t AddUintptrToHash<8>(uint32_t aHash, uintptr_t aValue) { - /* - * The static cast to uint64_t below is necessary because this function - * sometimes gets compiled on 32-bit platforms (yes, even though it's a - * template and we never call this particular override in a 32-bit build). If - * we do aValue >> 32 on a 32-bit machine, we're shifting a 32-bit uintptr_t - * right 32 bits, and the compiler throws an error. - */ uint32_t v1 = static_cast(aValue); uint32_t v2 = static_cast(static_cast(aValue) >> 32); return AddU32ToHash(AddU32ToHash(aHash, v1), v2); @@ -156,9 +145,11 @@ AddUintptrToHash<8>(uint32_t aHash, uintptr_t aValue) * Currently, we support hashing uint32_t's, values which we can implicitly * convert to uint32_t, data pointers, and function pointers. */ -template +template::value, + typename U = typename mozilla::EnableIf::Type> MOZ_MUST_USE inline uint32_t -AddToHash(uint32_t aHash, A aA) +AddToHash(uint32_t aHash, T aA) { /* * Try to convert |A| to uint32_t implicitly. If this works, great. If not, @@ -181,11 +172,15 @@ AddToHash(uint32_t aHash, A* aA) return detail::AddUintptrToHash(aHash, uintptr_t(aA)); } -template<> +// We use AddUintptrToHash() for hashing all integral types. 8-byte integral types +// are treated the same as 64-bit pointers, and smaller integral types are first +// implicitly converted to 32 bits and then passed to AddUintptrToHash() to be hashed. +template::value>::Type> MOZ_MUST_USE inline uint32_t -AddToHash(uint32_t aHash, uintptr_t aA) +AddToHash(uint32_t aHash, T aA) { - return detail::AddUintptrToHash(aHash, aA); + return detail::AddUintptrToHash(aHash, aA); } template diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index 2d036b867327..fa7b589bbdfc 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -1865,6 +1865,7 @@ class PackageFrontend(MachCommandBase): try: record.fetch_with(cache) except (requests.exceptions.HTTPError, + requests.exceptions.ChunkedEncodingError, requests.exceptions.ConnectionError) as e: if isinstance(e, requests.exceptions.ConnectionError): diff --git a/taskcluster/taskgraph/util/time.py b/taskcluster/taskgraph/util/time.py index 6aed30addf4f..af2f8726bb84 100644 --- a/taskcluster/taskgraph/util/time.py +++ b/taskcluster/taskgraph/util/time.py @@ -101,7 +101,9 @@ def json_time_from_now(input_str, now=None, datetime_format=False): else: # Sorta a big hack but the json schema validator for date does not like the # ISO dates until 'Z' (for timezone) is added... - return time.isoformat() + 'Z' + # the [:23] ensures only whole seconds or milliseconds are included, + # not microseconds (see bug 1381801) + return time.isoformat()[:23] + 'Z' def current_json_time(datetime_format=False): @@ -112,4 +114,6 @@ def current_json_time(datetime_format=False): if datetime_format is True: return datetime.datetime.utcnow() else: - return datetime.datetime.utcnow().isoformat() + 'Z' + # the [:23] ensures only whole seconds or milliseconds are included, + # not microseconds (see bug 1381801) + return datetime.datetime.utcnow().isoformat()[:23] + 'Z' diff --git a/testing/cppunittest.ini b/testing/cppunittest.ini index f9fb2098b2c8..2ff946d544ff 100644 --- a/testing/cppunittest.ini +++ b/testing/cppunittest.ini @@ -1,4 +1,5 @@ [ShowSSEConfig] +[TestPrintf] [TestAppShellSteadyState] [TestArray] [TestArrayUtils] diff --git a/toolkit/components/extensions/ExtensionXPCShellUtils.jsm b/toolkit/components/extensions/ExtensionXPCShellUtils.jsm index 49fe9f60d217..09cb101c2298 100644 --- a/toolkit/components/extensions/ExtensionXPCShellUtils.jsm +++ b/toolkit/components/extensions/ExtensionXPCShellUtils.jsm @@ -61,11 +61,6 @@ function frameScript() { const FRAME_SCRIPT = `data:text/javascript,(${encodeURI(frameScript)}).call(this)`; - -const XUL_URL = "data:application/vnd.mozilla.xul+xml;charset=utf-8," + encodeURI( - ` - `); - let kungFuDeathGrip = new Set(); function promiseBrowserLoaded(browser, url) { return new Promise(resolve => { @@ -110,7 +105,7 @@ class ContentPage { chromeShell.createAboutBlankContentViewer(system); chromeShell.useGlobalHistory = false; - chromeShell.loadURI(XUL_URL, 0, null, null, null); + chromeShell.loadURI("chrome://extensions/content/dummy.xul", 0, null, null, null); await promiseObserved("chrome-document-global-created", win => win.document == chromeShell.document); diff --git a/toolkit/components/jsdownloads/src/DownloadList.jsm b/toolkit/components/jsdownloads/src/DownloadList.jsm index 4cb8495746dd..262353040a1a 100644 --- a/toolkit/components/jsdownloads/src/DownloadList.jsm +++ b/toolkit/components/jsdownloads/src/DownloadList.jsm @@ -149,6 +149,12 @@ this.DownloadList.prototype = { * onDownloadRemoved: function (aDownload) { * // Called after aDownload is removed from the list. * }, + * onDownloadBatchStarting: function () { + * // Called before multiple changes are made at the same time. + * }, + * onDownloadBatchEnded: function () { + * // Called after all the changes have been made. + * }, * } * * @return {Promise} @@ -160,6 +166,7 @@ this.DownloadList.prototype = { this._views.add(aView); if ("onDownloadAdded" in aView) { + this._notifyAllViews("onDownloadBatchStarting"); for (let download of this._downloads) { try { aView.onDownloadAdded(download); @@ -167,6 +174,7 @@ this.DownloadList.prototype = { Cu.reportError(ex); } } + this._notifyAllViews("onDownloadBatchEnded"); } return Promise.resolve(); diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index eba94992169a..5c8ca9cc1593 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -13033,7 +13033,7 @@ "alert_emails": ["eisaacson@mozilla.com"], "bug_numbers": [1308030, 1324868], "releaseChannelCollection": "opt-out", - "expires_in_version": "56", + "expires_in_version": "60", "kind": "enumerated", "keyed": true, "n_values": 4, @@ -13044,7 +13044,7 @@ "alert_emails": ["eisaacson@mozilla.com"], "bug_numbers": [1308030], "releaseChannelCollection": "opt-out", - "expires_in_version": "56", + "expires_in_version": "60", "kind": "linear", "high": 300000, "n_buckets": 30, diff --git a/toolkit/components/telemetry/histogram_tools.py b/toolkit/components/telemetry/histogram_tools.py index d264acd31d10..e10a3e095433 100644 --- a/toolkit/components/telemetry/histogram_tools.py +++ b/toolkit/components/telemetry/histogram_tools.py @@ -286,10 +286,12 @@ associated with the histogram. Returns None if no guarding is necessary.""" name not in whitelists['expiry_default']: raise ParserError('New histogram "%s" cannot have "default" %s value.' % (name, field)) - if re.match(r'^[1-9][0-9]*$', expiration): - expiration = expiration + ".0a1" - elif re.match(r'^[1-9][0-9]*\.0$', expiration): - expiration = expiration + "a1" + if expiration != "default" and not utils.validate_expiration_version(expiration): + raise ParserError(('Error for histogram {} - invalid {}: {}.' + '\nSee: {}#expires-in-version') + .format(name, field, expiration, HISTOGRAMS_DOC_URL)) + + expiration = utils.add_expiration_postfix(expiration) definition[field] = expiration diff --git a/toolkit/components/telemetry/parse_events.py b/toolkit/components/telemetry/parse_events.py index b9d71bf2870f..3cf14de21c23 100644 --- a/toolkit/components/telemetry/parse_events.py +++ b/toolkit/components/telemetry/parse_events.py @@ -215,8 +215,11 @@ class EventData: definition['expiry_date'] = datetime.datetime.strptime(expiry_date, '%Y-%m-%d') # Finish setup. - definition['expiry_version'] = \ - utils.add_expiration_postfix(definition.get('expiry_version', 'never')) + expiry_version = definition.get('expiry_version', 'never') + if not utils.validate_expiration_version(expiry_version): + raise ParserError('{}: invalid expiry_version: {}.' + .format(self.identifier, expiry_version)) + definition['expiry_version'] = utils.add_expiration_postfix(expiry_version) @property def category(self): diff --git a/toolkit/components/telemetry/parse_scalars.py b/toolkit/components/telemetry/parse_scalars.py index feb108fd68d8..423d9b1e8046 100644 --- a/toolkit/components/telemetry/parse_scalars.py +++ b/toolkit/components/telemetry/parse_scalars.py @@ -38,7 +38,7 @@ class ScalarType: # Everything is ok, set the rest of the data. self._definition = definition - definition['expires'] = utils.add_expiration_postfix(definition['expires']) + self._expires = utils.add_expiration_postfix(definition['expires']) def validate_names(self, group_name, probe_name): """Validate the group and probe name: @@ -189,6 +189,12 @@ class ScalarType: raise ParserError(self._name + ' - unknown value in record_in_processes: ' + proc + '.\nSee: {}'.format(BASE_DOC_URL)) + # Validate the expiration version. + expires = definition.get('expires') + if not utils.validate_expiration_version(expires): + raise ParserError('{} - invalid expires: {}.\nSee: {}#required-fields' + .format(self._name, expires, BASE_DOC_URL)) + @property def name(self): """Get the scalar name""" @@ -222,7 +228,7 @@ class ScalarType: @property def expires(self): """Get the scalar expiration""" - return self._definition['expires'] + return self._expires @property def kind(self): diff --git a/toolkit/components/telemetry/shared_telemetry_utils.py b/toolkit/components/telemetry/shared_telemetry_utils.py index 213abe39dc7a..d3a5594f987e 100644 --- a/toolkit/components/telemetry/shared_telemetry_utils.py +++ b/toolkit/components/telemetry/shared_telemetry_utils.py @@ -118,6 +118,21 @@ def static_assert(output, expression, message): print("static_assert(%s, \"%s\");" % (expression, message), file=output) +def validate_expiration_version(expiration): + """ Makes sure the expiration version has the expected format. + + Allowed examples: "1.0", "20", "300.0a1", "60.0a1", "30.5a1", "never" + Disallowed examples: "Never", "asd", "4000000", "60a1" + + :param expiration: the expiration version string. + :return: True if the expiration validates correctly, False otherwise. + """ + if expiration != 'never' and not re.match(r'^\d{1,3}(\.\d|\.\da1)?$', expiration): + return False + + return True + + def add_expiration_postfix(expiration): """ Formats the expiration version and adds a version postfix if needed. diff --git a/toolkit/components/windowwatcher/test/file_named_window.html b/toolkit/components/windowwatcher/test/file_named_window.html new file mode 100644 index 000000000000..c66d89cd347c --- /dev/null +++ b/toolkit/components/windowwatcher/test/file_named_window.html @@ -0,0 +1,5 @@ + + +test_named_window.html new window + + diff --git a/toolkit/components/windowwatcher/test/mochitest.ini b/toolkit/components/windowwatcher/test/mochitest.ini index 42955d496e0c..e75b5dbb15b1 100644 --- a/toolkit/components/windowwatcher/test/mochitest.ini +++ b/toolkit/components/windowwatcher/test/mochitest.ini @@ -4,6 +4,8 @@ tags = openwindow [test_blank_named_window.html] skip-if = (os == 'android') # Fennec doesn't support web content opening new windows (See bug 1277544 for details) [test_named_window.html] +support-files = + file_named_window.html skip-if = (os == 'android') # Fennec doesn't support web content opening new windows (See bug 1277544 for details) [test_storage_copied.html] support-files = diff --git a/toolkit/components/windowwatcher/test/test_named_window.html b/toolkit/components/windowwatcher/test/test_named_window.html index 992f51f5f8fb..20a50db92acc 100644 --- a/toolkit/components/windowwatcher/test/test_named_window.html +++ b/toolkit/components/windowwatcher/test/test_named_window.html @@ -21,7 +21,8 @@ window. "use strict"; const NAME = "my_window"; - const TARGET_URL = "data:text/html,test_named_window.html new window"; + const TARGET_URL = location.href.replace("test_named_window.html", + "file_named_window.html"); const TARGET_URL_2 = TARGET_URL + "#2"; const TARGET_URL_3 = TARGET_URL + "#3"; diff --git a/toolkit/content/tests/mochitest/file_mousecapture.html b/toolkit/content/tests/mochitest/file_mousecapture.html new file mode 100644 index 000000000000..bc3d1869f62f --- /dev/null +++ b/toolkit/content/tests/mochitest/file_mousecapture.html @@ -0,0 +1 @@ +

One

Two

This is some text

diff --git a/toolkit/content/tests/mochitest/file_mousecapture2.html b/toolkit/content/tests/mochitest/file_mousecapture2.html new file mode 100644 index 000000000000..e746cf7807e6 --- /dev/null +++ b/toolkit/content/tests/mochitest/file_mousecapture2.html @@ -0,0 +1,8 @@ +

One

+

Two

+

This is some text

+ + + diff --git a/toolkit/content/tests/mochitest/file_mousecapture3.html b/toolkit/content/tests/mochitest/file_mousecapture3.html new file mode 100644 index 000000000000..ee3fad455aba --- /dev/null +++ b/toolkit/content/tests/mochitest/file_mousecapture3.html @@ -0,0 +1 @@ +.This is some text
.
diff --git a/toolkit/content/tests/mochitest/file_mousecapture4.html b/toolkit/content/tests/mochitest/file_mousecapture4.html new file mode 100644 index 000000000000..fbf30589c1ab --- /dev/null +++ b/toolkit/content/tests/mochitest/file_mousecapture4.html @@ -0,0 +1 @@ + diff --git a/toolkit/content/tests/mochitest/file_mousecapture5.html b/toolkit/content/tests/mochitest/file_mousecapture5.html new file mode 100644 index 000000000000..bc632b615607 --- /dev/null +++ b/toolkit/content/tests/mochitest/file_mousecapture5.html @@ -0,0 +1 @@ + diff --git a/toolkit/content/tests/mochitest/mochitest.ini b/toolkit/content/tests/mochitest/mochitest.ini index 1ef68c022dd6..87b04b7c7bcc 100644 --- a/toolkit/content/tests/mochitest/mochitest.ini +++ b/toolkit/content/tests/mochitest/mochitest.ini @@ -1,5 +1,11 @@ [test_autocomplete_change_after_focus.html] skip-if = toolkit == "android" [test_mousecapture.xhtml] +support-files = + file_mousecapture.html + file_mousecapture2.html + file_mousecapture3.html + file_mousecapture4.html + file_mousecapture5.html skip-if = toolkit == "android" diff --git a/toolkit/content/tests/mochitest/test_mousecapture.xhtml b/toolkit/content/tests/mochitest/test_mousecapture.xhtml index e4a25fc8a009..75edadf1a9ac 100644 --- a/toolkit/content/tests/mochitest/test_mousecapture.xhtml +++ b/toolkit/content/tests/mochitest/test_mousecapture.xhtml @@ -18,6 +18,8 @@ SimpleTest.expectAssertions(6, 12); SimpleTest.waitForExplicitFinish(); +const TEST_PATH = location.href.replace("test_mousecapture.xhtml", ""); + var captureRetargetMode = false; var cachedMouseDown = null; var previousWidth = 0, originalWidth = 0; @@ -95,7 +97,7 @@ function selectionScrollCheck() { synthesizeMouse(document.getElementById("custom"), 2, 2, { type: "mousedown" }); // check to ensure that selection dragging scrolls the right scrollable area - otherWindow = window.open("data:text/html,

One

Two

This is some text

", "_blank", "width=200,height=200,scrollbars=yes"); + otherWindow = window.open(TEST_PATH + "file_mousecapture.html", "_blank", "width=200,height=200,scrollbars=yes"); SimpleTest.waitForFocus(selectionScrollCheck, otherWindow); } } @@ -183,7 +185,7 @@ function runTests() { previousWidth = frames[1].frames[0].document.documentElement.clientWidth; originalWidth = previousWidth; - runCaptureTest(frames[1].document.documentElement.lastChild, framesetCallback); + runCaptureTest(frames[1].document.documentElement.lastElementChild, framesetCallback); // ensure that clicking on an element where the frame disappears doesn't crash synthesizeMouse(frames[2].document.getElementById("input"), 8, 8, { type: "mousedown" }, frames[2]); @@ -204,7 +206,7 @@ function runTests() { // This should open the page in a new tab. var topPos = window.innerHeight; - otherWindow = window.open("data:text/html,

One

Two

This is some text

", "_blank"); + otherWindow = window.open(TEST_PATH + "file_mousecapture2.html?topPos=" + topPos, "_blank"); SimpleTest.waitForFocus(selectionScrollCheck, otherWindow); } @@ -298,14 +300,9 @@ SimpleTest.waitForFocus(runTests); onmousedown="document.createElement('hbox').setCapture();"/> -