author | Andreea Pavel <apavel@mozilla.com> |
Thu, 15 Feb 2018 12:34:12 +0200 | |
changeset 404012 | 3e332972119f8f76c5c523157c7e5cb2b987fab2 |
parent 404011 | e8d05c7218c66151e2e30dcff2fabdf9bccae40e (current diff) |
parent 403921 | 9b69cc60e5848f2f8802c911fd00771b50eed41f (diff) |
child 404013 | d4937a53d8261e603b9fbb3f1393e2a509b3e96c |
push id | 99924 |
push user | ebalazs@mozilla.com |
push date | Thu, 15 Feb 2018 20:43:51 +0000 |
treeherder | mozilla-inbound@a7d2a49f46fb [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 60.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/.eslintignore +++ b/.eslintignore @@ -319,16 +319,17 @@ js/src/Y.js # Third-party media/webrtc/trunk/** # mobile/android/ exclusions mobile/android/tests/browser/chrome/tp5/** # Uses `#filter substitution` mobile/android/app/mobile.js +mobile/android/app/geckoview-prefs.js # Uses `#expand` mobile/android/chrome/content/about.js # Not much JS to lint and non-standard at that mobile/android/installer/ mobile/android/locales/
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1249,16 +1249,28 @@ var gBrowserInit = { remoteType, sameProcessAsFrameLoader }); gUIDensity.init(); if (AppConstants.CAN_DRAW_IN_TITLEBAR) { gDragSpaceObserver.init(); } + + // Hack to ensure that the about:home favicon is loaded + // instantaneously, to avoid flickering and improve perceived performance. + this._callWithURIToLoad(uriToLoad => { + if (uriToLoad == "about:home") { + gBrowser.setIcon(gBrowser.selectedTab, "chrome://branding/content/icon32.png"); + } else if (uriToLoad == "about:privatebrowsing") { + gBrowser.setIcon(gBrowser.selectedTab, "chrome://browser/skin/privatebrowsing/favicon.svg"); + } + }); + + this._setInitialFocus(); }, onLoad() { gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver); Services.obs.addObserver(gPluginHandler.NPAPIPluginCrashed, "plugin-crashed"); window.addEventListener("AppCommand", HandleAppCommandEvent, true); @@ -1349,28 +1361,16 @@ var gBrowserInit = { try { gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToOpen); } catch (e) { Cu.reportError(e); } } - this._setInitialFocus(); - - // Hack to ensure that the about:home favicon is loaded - // instantaneously, to avoid flickering and improve perceived performance. - this._uriToLoadPromise.then(uriToLoad => { - if (uriToLoad == "about:home") { - gBrowser.setIcon(gBrowser.selectedTab, "chrome://branding/content/icon32.png"); - } else if (uriToLoad == "about:privatebrowsing") { - gBrowser.setIcon(gBrowser.selectedTab, "chrome://browser/skin/privatebrowsing/favicon.svg"); - } - }); - // Wait until chrome is painted before executing code not critical to making the window visible this._boundDelayedStartup = this._delayedStartup.bind(this); window.addEventListener("MozAfterPaint", this._boundDelayedStartup); this._loadHandled = true; }, _cancelDelayedStartup() { @@ -1587,19 +1587,25 @@ var gBrowserInit = { let initialBrowser = gBrowser.selectedBrowser; mm.addMessageListener("Browser:FirstNonBlankPaint", function onFirstNonBlankPaint() { mm.removeMessageListener("Browser:FirstNonBlankPaint", onFirstNonBlankPaint); initialBrowser.removeAttribute("blank"); }); - this._uriToLoadPromise.then(uriToLoad => { + // To prevent flickering of the urlbar-history-dropmarker in the general + // case, the urlbar has the 'focused' attribute set by default. + // If we are not fully sure the urlbar will be focused in this window, + // we should remove the attribute before first paint. + let shouldRemoveFocusedAttribute = true; + this._callWithURIToLoad(uriToLoad => { if ((isBlankPageURL(uriToLoad) || uriToLoad == "about:privatebrowsing") && focusAndSelectUrlBar()) { + shouldRemoveFocusedAttribute = false; return; } if (gBrowser.selectedBrowser.isRemoteBrowser) { // If the initial browser is remote, in order to optimize for first paint, // we'll defer switching focus to that browser until it has painted. firstBrowserPaintDeferred.promise.then(() => { // If focus didn't move while we were waiting for first paint, we're okay @@ -1609,20 +1615,22 @@ var gBrowserInit = { } }); } else { // If the initial browser is not remote, we can focus the browser // immediately with no paint performance impact. gBrowser.selectedBrowser.focus(); } }); + if (shouldRemoveFocusedAttribute) + gURLBar.removeAttribute("focused"); }, _handleURIToLoad() { - this._uriToLoadPromise.then(uriToLoad => { + this._callWithURIToLoad(uriToLoad => { if (!uriToLoad || uriToLoad == "about:blank") { return; } // We don't check if uriToLoad is a XULElement because this case has // already been handled before first paint, and the argument cleared. if (uriToLoad instanceof Ci.nsIArray) { let count = uriToLoad.length; @@ -1731,47 +1739,59 @@ var gBrowserInit = { ChromeUtils.import("resource:///modules/DownloadsTaskbar.jsm", {}) .DownloadsTaskbar.registerIndicator(window); } catch (ex) { Cu.reportError(ex); } }, {timeout: 10000}); }, - // Returns the URI(s) to load at startup. + // Returns the URI(s) to load at startup if it is immediately known, or a + // promise resolving to the URI to load. get _uriToLoadPromise() { delete this._uriToLoadPromise; - return this._uriToLoadPromise = new Promise(resolve => { + return this._uriToLoadPromise = function() { // window.arguments[0]: URI to load (string), or an nsIArray of // nsISupportsStrings to load, or a xul:tab of // a tabbrowser, which will be replaced by this // window (for this case, all other arguments are // ignored). if (!window.arguments || !window.arguments[0]) { - resolve(null); - return; + return null; } let uri = window.arguments[0]; let defaultArgs = Cc["@mozilla.org/browser/clh;1"] .getService(Ci.nsIBrowserHandler) .defaultArgs; // If the given URI is different from the homepage, we want to load it. if (uri != defaultArgs) { - resolve(uri); - return; + return uri; } // The URI appears to be the the homepage. We want to load it only if // session restore isn't about to override the homepage. - SessionStartup.willOverrideHomepagePromise.then(willOverrideHomepage => { - resolve(willOverrideHomepage ? null : uri); - }); - }); + let willOverride = SessionStartup.willOverrideHomepage; + if (!(willOverride instanceof Promise)) { + return willOverride ? null : uri; + } + return willOverride.then(willOverrideHomepage => + willOverrideHomepage ? null : uri); + }(); + }, + + // Calls the given callback with the URI to load at startup. + // Synchronously if possible, or after _uriToLoadPromise resolves otherwise. + _callWithURIToLoad(callback) { + let uriToLoad = this._uriToLoadPromise; + if (uriToLoad instanceof Promise) + uriToLoad.then(callback); + else + callback(uriToLoad); }, onUnload() { gUIDensity.uninit(); if (AppConstants.CAN_DRAW_IN_TITLEBAR) { gDragSpaceObserver.uninit(); }
--- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -769,16 +769,17 @@ cui-areatype="toolbar" aboutHomeOverrideTooltip="&abouthome.pageTitle;"/> <toolbarspring cui-areatype="toolbar" class="chromeclass-toolbar-additional"/> <toolbaritem id="urlbar-container" flex="400" persist="width" removable="false" class="chromeclass-location" overflows="false"> <textbox id="urlbar" flex="1" placeholder="&urlbar.placeholder2;" + focused="true" type="autocomplete" autocompletesearch="unifiedcomplete" autocompletesearchparam="enable-actions" autocompletepopup="PopupAutoCompleteRichResult" completeselectedindex="true" shrinkdelay="250" tabscrolling="true" newlines="stripsurroundingwhitespace"
--- a/browser/base/content/test/performance/browser_startup_flicker.js +++ b/browser/base/content/test/performance/browser_startup_flicker.js @@ -31,35 +31,22 @@ add_task(async function() { alreadyFocused = true; // This is likely an issue caused by the test harness, but log it anyway. todo(false, "the window should be focused at first paint, " + rects.toSource()); continue; } rects = rects.filter(rect => { - let inRange = (val, min, max) => min <= val && val <= max; let width = frame.width; let exceptions = [ - {name: "bug 1403648 - urlbar down arrow shouldn't flicker", - condition: r => // 5x9px area, sometimes less at the end of the opacity transition - inRange(r.h, 3, 5) && inRange(r.w, 7, 9) && - inRange(r.y1, 40, 80) && // in the toolbar - // at ~80% of the window width - inRange(r.x1, width * .75, width * .9) - }, - - {name: "bug 1403648 - urlbar should be focused at first paint", - condition: r => inRange(r.y2, 60, 80) && // in the toolbar - // taking 50% to 75% of the window width - inRange(r.w, width * .5, width * .75) && - // starting at 15 to 25% of the window width - inRange(r.x1, width * .15, width * .25) - }, + /** + * Nothing here! Please don't add anything new! + */ ]; let rectText = `${rect.toSource()}, window width: ${width}`; for (let e of exceptions) { if (e.condition(rect)) { todo(false, e.name + ", " + rectText); return false; }
--- a/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js +++ b/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js @@ -116,18 +116,17 @@ add_task(async function setup() { await addDummyHistoryEntries(); }); /** * This test ensures that there are no unexpected uninterruptible reflows when * typing into the URL bar with the default values in Places. */ add_task(async function() { - let win = await BrowserTestUtils.openNewBrowserWindow(); - await ensureNoPreloadedBrowser(win); + let win = await prepareSettledWindow(); let URLBar = win.gURLBar; let popup = URLBar.popup; URLBar.focus(); URLBar.value = ""; await withReflowObserver(async function(dirtyFrameFn) {
--- a/browser/base/content/test/performance/browser_urlbar_search_reflows.js +++ b/browser/base/content/test/performance/browser_urlbar_search_reflows.js @@ -133,18 +133,17 @@ add_task(async function setup() { }); /** * This test ensures that there are no unexpected * uninterruptible reflows when typing into the URL bar * with the default values in Places. */ add_task(async function() { - let win = await BrowserTestUtils.openNewBrowserWindow(); - await ensureNoPreloadedBrowser(win); + let win = await prepareSettledWindow(); let URLBar = win.gURLBar; let popup = URLBar.popup; URLBar.focus(); URLBar.value = SEARCH_TERM; let testFn = async function(dirtyFrameFn) { let oldInvalidate = popup.invalidate.bind(popup);
--- a/browser/base/content/test/performance/browser_windowopen_flicker.js +++ b/browser/base/content/test/performance/browser_windowopen_flicker.js @@ -80,54 +80,26 @@ add_task(async function() { continue; } ignoreTinyPaint = false; let rects = compareFrames(frame, previousFrame).filter(rect => { let inRange = (val, min, max) => min <= val && val <= max; let width = frame.width; - const spaceBeforeFirstTab = AppConstants.platform == "macosx" ? 100 : 0; - let inFirstTab = r => - inRange(r.x1, spaceBeforeFirstTab, spaceBeforeFirstTab + 50) && r.y1 < 30; - let exceptions = [ - {name: "bug 1403648 - urlbar down arrow shouldn't flicker", - condition: r => // 5x9px area, sometimes less at the end of the opacity transition - inRange(r.h, 3, 5) && inRange(r.w, 7, 9) && - inRange(r.y1, 40, 80) && // in the toolbar - // at ~80% of the window width - inRange(r.x1, width * .75, width * .9) - }, - - {name: "bug 1403648 - urlbar should be focused at first paint", - condition: r => inRange(r.y2, 60, 80) && // in the toolbar - // taking 50% to 75% of the window width - inRange(r.w, width * .5, width * .75) && - // starting at 15 to 25% of the window width - inRange(r.x1, width * .15, width * .25) - }, - {name: "bug 1421463 - reload toolbar icon shouldn't flicker", condition: r => r.h == 13 && inRange(r.w, 14, 16) && // icon size inRange(r.y1, 40, 80) && // in the toolbar // near the left side of the screen // The reload icon is shifted on devedition builds // where there's an additional devtools toolbar icon. AppConstants.MOZ_DEV_EDITION ? inRange(r.x1, 100, 120) : inRange(r.x1, 65, 100) }, - - {name: "bug 1401955 - about:home favicon should be visible at first paint", - condition: r => inFirstTab(r) && inRange(r.h, 14, 15) && inRange(r.w, 14, 15) - }, - - {name: "bug 1401955 - space for about:home favicon should be there at first paint", - condition: r => inFirstTab(r) && inRange(r.w, 60, 80) && inRange(r.h, 8, 15) - }, ]; let rectText = `${rect.toSource()}, window width: ${width}`; for (let e of exceptions) { if (e.condition(rect)) { todo(false, e.name + ", " + rectText); return false; }
--- a/browser/base/content/test/performance/browser_windowopen_reflows.js +++ b/browser/base/content/test/performance/browser_windowopen_reflows.js @@ -31,22 +31,16 @@ if (Services.appinfo.OS == "WINNT") { }, ); } if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") { EXPECTED_REFLOWS.push( { stack: [ - "select@chrome://global/content/bindings/textbox.xml", - "focusAndSelectUrlBar@chrome://browser/content/browser.js", - ], - }, - { - stack: [ "rect@chrome://browser/content/browser-tabsintitlebar.js", "_update@chrome://browser/content/browser-tabsintitlebar.js", "init@chrome://browser/content/browser-tabsintitlebar.js", "handleEvent@chrome://browser/content/tabbrowser.xml", ], // These numbers should only ever go down - never up. times: Services.appinfo.OS == "WINNT" ? 5 : 4, },
--- a/browser/base/content/test/performance/head.js +++ b/browser/base/content/test/performance/head.js @@ -158,23 +158,23 @@ async function withReflowObserver(testFn Assert.ok(true, "All expected reflows were observed"); } Services.els.removeListenerForAllEvents(win, dirtyFrameFn, true); docShell.removeWeakReflowObserver(observer); } } -async function ensureNoPreloadedBrowser() { +async function ensureNoPreloadedBrowser(win = window) { // If we've got a preloaded browser, get rid of it so that it // doesn't interfere with the test if it's loading. We have to // do this before we disable preloading or changing the new tab // URL, otherwise _getPreloadedBrowser will return null, despite // the preloaded browser existing. - let preloaded = gBrowser._getPreloadedBrowser(); + let preloaded = win.gBrowser._getPreloadedBrowser(); if (preloaded) { preloaded.remove(); } await SpecialPowers.pushPrefEnv({ set: [["browser.newtab.preload", false]], }); @@ -182,16 +182,31 @@ async function ensureNoPreloadedBrowser( .getService(Ci.nsIAboutNewTabService); aboutNewTabService.newTabURL = "about:blank"; registerCleanupFunction(() => { aboutNewTabService.resetNewTabURL(); }); } +async function prepareSettledWindow() { + let win = await BrowserTestUtils.openNewBrowserWindow(); + + await ensureNoPreloadedBrowser(win); + + let overflowableToolbar = win.document.getElementById("nav-bar").overflowable; + if (overflowableToolbar._lazyResizeHandler && overflowableToolbar._lazyResizeHandler.isArmed) { + info("forcing deferred overflow handling of the navigation toolbar to happen immediately"); + overflowableToolbar._lazyResizeHandler.disarm(); + overflowableToolbar._onLazyResize(); + } + + return win; +} + /** * Calculate and return how many additional tabs can be fit into the * tabstrip without causing it to overflow. * * @return int * The maximum additional tabs that can be fit into the * tabstrip without causing it to overflow. */
--- a/browser/components/places/tests/browser/browser.ini +++ b/browser/components/places/tests/browser/browser.ini @@ -77,27 +77,27 @@ skip-if = (os == 'win' && ccov) # Bug 14 skip-if = (os == 'win' && ccov) # Bug 1423667 [browser_library_middleclick.js] skip-if = (os == 'win' && ccov) # Bug 1423667 [browser_library_open_leak.js] [browser_library_openFlatContainer.js] skip-if = (os == 'win' && ccov) # Bug 1423667 [browser_library_panel_leak.js] skip-if = (os == 'win' && ccov) # Bug 1423667 -[browser_library_remove_bookmark.js] -skip-if = (os == 'win' && ccov) # Bug 1423667 [browser_library_search.js] skip-if = (os == 'win' && ccov) # Bug 1423667 [browser_library_views_liveupdate.js] [browser_markPageAsFollowedLink.js] [browser_paste_bookmarks.js] skip-if = (os == 'win' && ccov) # Bug 1423667 subsuite = clipboard [browser_paste_into_tags.js] skip-if = (os == 'win' && ccov) # Bug 1423667 +[browser_remove_bookmarks.js] +skip-if = (os == 'win' && ccov) # Bug 1423667 subsuite = clipboard [browser_sidebarpanels_click.js] skip-if = true # temporarily disabled for breaking the treeview - bug 658744 [browser_sort_in_library.js] skip-if = (os == 'win' && ccov) # Bug 1423667 [browser_stayopenmenu.js] skip-if = os == "mac" && debug # bug 1400323 [browser_toolbar_drop_text.js]
deleted file mode 100644 --- a/browser/components/places/tests/browser/browser_library_remove_bookmark.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Test deleting bookmarks from library. - */ -"use strict"; - -add_task(async function test_remove_bookmark() { - const uris = [ - "http://example.com/1", - "http://example.com/2", - "http://example.com/3", - ]; - - let children = uris.map((uri, index) => { - return { - title: `bm${index}`, - url: uri, - }; - }); - - // Insert Bookmarks. - await PlacesUtils.bookmarks.insertTree({ - guid: PlacesUtils.bookmarks.unfiledGuid, - children - }); - - // Open the Library and select the "UnfiledBookmarks". - let library = await promiseLibrary("UnfiledBookmarks"); - - let PO = library.PlacesOrganizer; - - Assert.equal(PlacesUtils.getConcreteItemGuid(PO._places.selectedNode), - PlacesUtils.bookmarks.unfiledGuid, "Should have selected unfiled bookmarks."); - - let contextMenu = library.document.getElementById("placesContext"); - let contextMenuDeleteItem = library.document.getElementById("placesContext_delete"); - - let popupShownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown"); - - let treeBoxObject = library.ContentTree.view.treeBoxObject; - let firstColumn = library.ContentTree.view.columns[0]; - let firstBookmarkRect = treeBoxObject.getCoordsForCellItem(0, firstColumn, "bm0"); - - EventUtils.synthesizeMouse( - library.ContentTree.view.body, - firstBookmarkRect.x, - firstBookmarkRect.y, - { type: "contextmenu", button: 2 }, - library - ); - - await popupShownPromise; - - Assert.equal(library.ContentTree.view.result.root.childCount, 3, "Number of bookmarks before removal is right"); - - let removePromise = PlacesTestUtils.waitForNotification("onItemRemoved", (itemId, parentId, index, type, uri, guid) => uri.spec == uris[0]); - EventUtils.synthesizeMouseAtCenter(contextMenuDeleteItem, {}, library); - - await removePromise; - - Assert.equal(library.ContentTree.view.result.root.childCount, 2, "Should have removed the bookmark from the display"); - - // Cleanup - registerCleanupFunction(async function() { - await promiseLibraryClosed(library); - await PlacesUtils.bookmarks.eraseEverything(); - }); -});
new file mode 100644 --- /dev/null +++ b/browser/components/places/tests/browser/browser_remove_bookmarks.js @@ -0,0 +1,121 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/** + * Test removing bookmarks from the Bookmarks Toolbar and Library. + */ + +const TEST_URL = "about:mozilla"; + +add_task(async function setup() { + await PlacesUtils.bookmarks.eraseEverything(); + + let toolbar = document.getElementById("PersonalToolbar"); + let wasCollapsed = toolbar.collapsed; + + // Uncollapse the personal toolbar if needed. + if (wasCollapsed) { + await promiseSetToolbarVisibility(toolbar, true); + } + + registerCleanupFunction(async () => { + // Collapse the personal toolbar if needed. + if (wasCollapsed) { + await promiseSetToolbarVisibility(toolbar, false); + } + await PlacesUtils.bookmarks.eraseEverything(); + }); +}); + +add_task(async function test_remove_bookmark_from_toolbar() { + let toolbarBookmark = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + title: "Bookmark Title", + url: TEST_URL + }); + + let toolbarNode = getToolbarNodeForItemGuid(toolbarBookmark.guid); + + let contextMenu = document.getElementById("placesContext"); + let popupShownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown"); + + EventUtils.synthesizeMouseAtCenter(toolbarNode, { + button: 2, + type: "contextmenu" + }); + await popupShownPromise; + + let contextMenuDeleteItem = document.getElementById("placesContext_delete"); + + let removePromise = PlacesTestUtils.waitForNotification("onItemRemoved", (itemId, parentId, index, type, uri, guid) => uri.spec == TEST_URL); + + EventUtils.synthesizeMouseAtCenter(contextMenuDeleteItem, {}); + + await removePromise; + + Assert.deepEqual(PlacesUtils.bookmarks.fetch({ url: TEST_URL }), {}, "Should have removed the bookmark from the database"); +}); + +add_task(async function test_remove_bookmark_from_library() { + const uris = [ + "http://example.com/1", + "http://example.com/2", + "http://example.com/3", + ]; + + let children = uris.map((uri, index) => { + return { + title: `bm${index}`, + url: uri, + }; + }); + + // Insert bookmarks. + await PlacesUtils.bookmarks.insertTree({ + guid: PlacesUtils.bookmarks.unfiledGuid, + children + }); + + // Open the Library and select the "UnfiledBookmarks". + let library = await promiseLibrary("UnfiledBookmarks"); + + registerCleanupFunction(async function() { + await promiseLibraryClosed(library); + }); + + let PO = library.PlacesOrganizer; + + Assert.equal(PlacesUtils.getConcreteItemGuid(PO._places.selectedNode), + PlacesUtils.bookmarks.unfiledGuid, "Should have selected unfiled bookmarks."); + + let contextMenu = library.document.getElementById("placesContext"); + let contextMenuDeleteItem = library.document.getElementById("placesContext_delete"); + + let popupShownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown"); + + let treeBoxObject = library.ContentTree.view.treeBoxObject; + let firstColumn = library.ContentTree.view.columns[0]; + let firstBookmarkRect = treeBoxObject.getCoordsForCellItem(0, firstColumn, "bm0"); + + EventUtils.synthesizeMouse( + library.ContentTree.view.body, + firstBookmarkRect.x, + firstBookmarkRect.y, + { type: "contextmenu", button: 2 }, + library + ); + + await popupShownPromise; + + Assert.equal(library.ContentTree.view.result.root.childCount, 3, "Number of bookmarks before removal is right"); + + let removePromise = PlacesTestUtils.waitForNotification("onItemRemoved", (itemId, parentId, index, type, uri, guid) => uri.spec == uris[0]); + EventUtils.synthesizeMouseAtCenter(contextMenuDeleteItem, {}, library); + + await removePromise; + + Assert.equal(library.ContentTree.view.result.root.childCount, 2, "Should have removed the bookmark from the display"); +});
--- a/browser/components/sessionstore/nsISessionStartup.idl +++ b/browser/components/sessionstore/nsISessionStartup.idl @@ -31,23 +31,24 @@ interface nsISessionStartup: nsISupports /** * Determines whether automatic session restoration is enabled for this * launch of the browser. This does not include crash restoration, and will * return false if restoration will only be caused by a crash. */ boolean isAutomaticRestoreEnabled(); /** - * Returns a promise that resolves to a boolean, indicating whether we will - * restore a session that ends up replacing the homepage. True guarantees - * that we'll restore a session; false means that we /probably/ won't do so. + * Returns a boolean or a promise that resolves to a boolean, indicating + * whether we will restore a session that ends up replacing the homepage. + * True guarantees that we'll restore a session; false means that we + * /probably/ won't do so. * The browser uses this to avoid unnecessarily loading the homepage when * restoring a session. */ - readonly attribute jsval willOverrideHomepagePromise; + readonly attribute jsval willOverrideHomepage; /** * What type of session we're restoring. * NO_SESSION There is no data available from the previous session * RECOVER_SESSION The last session crashed. It will either be restored or * about:sessionrestore will be shown. * RESUME_SESSION The previous session should be restored at startup * DEFER_SESSION The previous session is fine, but it shouldn't be restored
--- a/browser/components/sessionstore/nsSessionStartup.js +++ b/browser/components/sessionstore/nsSessionStartup.js @@ -305,30 +305,31 @@ SessionStartup.prototype = { * @returns bool */ _willRestore() { return this._sessionType == Ci.nsISessionStartup.RECOVER_SESSION || this._sessionType == Ci.nsISessionStartup.RESUME_SESSION; }, /** - * Returns a promise that resolves to a boolean, indicating whether we will - * restore a session that ends up replacing the homepage. True guarantees - * that we'll restore a session; false means that we /probably/ won't do so. + * Returns a boolean or a promise that resolves to a boolean, indicating + * whether we will restore a session that ends up replacing the homepage. + * True guarantees that we'll restore a session; false means that we + * /probably/ won't do so. * The browser uses this to avoid unnecessarily loading the homepage when * restoring a session. */ - get willOverrideHomepagePromise() { + get willOverrideHomepage() { // If the session file hasn't been read yet and resuming the session isn't // enabled via prefs, go ahead and load the homepage. We may still replace // it when recovering from a crash, which we'll only know after reading the // session file, but waiting for that would delay loading the homepage in // the non-crash case. if (!this._initialState && !this._resumeSessionEnabled) { - return Promise.resolve(false); + return false; } return new Promise(resolve => { this.onceInitialized.then(() => { // If there are valid windows with not only pinned tabs, signal that we // will override the default homepage by restoring a session. resolve(this._willRestore() && this._initialState &&
new file mode 100644 --- /dev/null +++ b/browser/modules/ThemeVariableMap.jsm @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var EXPORTED_SYMBOLS = ["ThemeVariableMap"]; + +const ThemeVariableMap = [ + ["--lwt-accent-color-inactive", "accentcolorInactive"], + ["--lwt-background-alignment", "backgroundsAlignment"], + ["--lwt-background-tiling", "backgroundsTiling"], + ["--tab-loading-fill", "tab_loading", "tabbrowser-tabs"], + ["--lwt-tab-text", "tab_text"], + ["--toolbar-bgcolor", "toolbarColor"], + ["--toolbar-color", "toolbar_text"], + ["--url-and-searchbar-background-color", "toolbar_field"], + ["--url-and-searchbar-color", "toolbar_field_text"], + ["--lwt-toolbar-field-border-color", "toolbar_field_border"], + ["--urlbar-separator-color", "toolbar_field_separator"], + ["--tabs-border-color", "toolbar_top_separator", "navigator-toolbox"], + ["--lwt-toolbar-vertical-separator", "toolbar_vertical_separator"], + ["--toolbox-border-bottom-color", "toolbar_bottom_separator"], + ["--lwt-toolbarbutton-icon-fill", "icon_color"], + ["--lwt-toolbarbutton-icon-fill-attention", "icon_attention_color"], + ["--lwt-toolbarbutton-hover-background", "button_background_hover"], + ["--lwt-toolbarbutton-active-background", "button_background_active"], +];
--- a/browser/modules/moz.build +++ b/browser/modules/moz.build @@ -83,16 +83,19 @@ with Files("ReaderParent.jsm"): BUG_COMPONENT = ("Toolkit", "Reader Mode") with Files("Sanitizer.jsm"): BUG_COMPONENT = ("Firefox", "Preferences") with Files("SitePermissions.jsm"): BUG_COMPONENT = ("Firefox", "Site Identity and Permission Panels") +with Files("ThemeVariableMap.jsm"): + BUG_COMPONENT = ("Toolkit", "WebExtensions: Themes") + with Files("TransientPrefs.jsm"): BUG_COMPONENT = ("Firefox", "Preferences") with Files("Windows8WindowFrameColor.jsm"): BUG_COMPONENT = ("Firefox", "Theme") with Files("WindowsJumpLists.jsm"): BUG_COMPONENT = ("Firefox", "Shell Integration") @@ -147,16 +150,17 @@ EXTRA_JS_MODULES += [ 'PluginContent.jsm', 'ProcessHangMonitor.jsm', 'ReaderParent.jsm', 'RecentWindow.jsm', 'RemotePrompt.jsm', 'Sanitizer.jsm', 'SchedulePressure.jsm', 'SitePermissions.jsm', + 'ThemeVariableMap.jsm', 'TransientPrefs.jsm', 'UpdateTopLevelContentWindowIDHelper.jsm', 'webrtcUI.jsm', 'ZoomUI.jsm', ] if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': EXTRA_JS_MODULES += [
--- a/config/check_spidermonkey_style.py +++ b/config/check_spidermonkey_style.py @@ -143,35 +143,35 @@ js/src/tests/style/BadIncludes.h:8: erro <tests/style/BadIncludes2.h> should be included using the #include "..." form js/src/tests/style/BadIncludes.h:10: error: "stdio.h" is included using the wrong path; did you forget a prefix, or is the file not yet committed? js/src/tests/style/BadIncludesOrder-inl.h:5:6: error: - "vm/Interpreter-inl.h" should be included after "jsscriptinlines.h" + "vm/JSScript-inl.h" should be included after "vm/Interpreter-inl.h" js/src/tests/style/BadIncludesOrder-inl.h:6:7: error: - "jsscriptinlines.h" should be included after "js/Value.h" + "vm/Interpreter-inl.h" should be included after "js/Value.h" js/src/tests/style/BadIncludesOrder-inl.h:7:8: error: "js/Value.h" should be included after "ds/LifoAlloc.h" js/src/tests/style/BadIncludesOrder-inl.h:8:9: error: "ds/LifoAlloc.h" should be included after "jsapi.h" js/src/tests/style/BadIncludesOrder-inl.h:9:10: error: "jsapi.h" should be included after <stdio.h> js/src/tests/style/BadIncludesOrder-inl.h:10:11: error: <stdio.h> should be included after "mozilla/HashFunctions.h" -js/src/tests/style/BadIncludesOrder-inl.h:27:28: error: - "jsobj.h" should be included after "jsfun.h" +js/src/tests/style/BadIncludesOrder-inl.h:28:29: error: + "vm/JSScript.h" should be included after "vm/JSFunction.h" (multiple files): error: header files form one or more cycles tests/style/HeaderCycleA1.h -> tests/style/HeaderCycleA2.h -> tests/style/HeaderCycleA3.h -> tests/style/HeaderCycleA1.h @@ -231,17 +231,17 @@ class FileKind(object): return FileKind.TBL if filename.endswith('.msg'): return FileKind.MSG error(filename, None, 'unknown file kind') -def check_style(): +def check_style(enable_fixup): # We deal with two kinds of name. # - A "filename" is a full path to a file from the repository root. # - An "inclname" is how a file is referred to in a #include statement. # # Examples (filename -> inclname) # - "mfbt/Attributes.h" -> "mozilla/Attributes.h" # - "mfbt/decimal/Decimal.h -> "mozilla/Decimal.h" # - "mozglue/misc/TimeStamp.h -> "mozilla/TimeStamp.h" @@ -287,18 +287,26 @@ def check_style(): inclname = js_names[filename] file_kind = FileKind.get(filename) if file_kind == FileKind.C or file_kind == FileKind.CPP or \ file_kind == FileKind.H or file_kind == FileKind.INL_H: included_h_inclnames = set() # type: set(inclname) # This script is run in js/src/, so prepend '../../' to get to the root of the Mozilla # source tree. - with open(os.path.join(repo.path, filename)) as f: - do_file(filename, inclname, file_kind, f, all_inclnames, included_h_inclnames) + filepath = os.path.join(repo.path, filename) + with open(filepath) as f: + code = read_file(f) + + if enable_fixup: + code = code.sorted(inclname) + with open(filepath, 'w') as f: + f.write(code.to_source()) + + check_file(filename, inclname, file_kind, code, all_inclnames, included_h_inclnames) edges[inclname] = included_h_inclnames find_cycles(all_inclnames, edges) # Compare expected and actual output. difflines = difflib.unified_diff(expected_output, actual_output, fromfile='check_spidermonkey_style.py expected output', @@ -333,22 +341,26 @@ def is_module_header(enclosing_inclname, return True return False class Include(object): '''Important information for a single #include statement.''' - def __init__(self, inclname, linenum, is_system): + def __init__(self, include_prefix, inclname, line_suffix, linenum, is_system): + self.include_prefix = include_prefix + self.line_suffix = line_suffix self.inclname = inclname self.linenum = linenum self.is_system = is_system - def isLeaf(self): + def is_style_relevant(self): + # Includes are style-relevant; that is, they're checked by the pairwise + # style-checking algorithm in check_file. return True def section(self, enclosing_inclname): '''Identify which section inclname belongs to. The section numbers are as follows. 0. Module header (e.g. jsfoo.h or jsfooinlines.h within jsfoo.cpp) 1. mozilla/Foo.h @@ -386,74 +398,210 @@ class Include(object): return 3 def quote(self): if self.is_system: return '<' + self.inclname + '>' else: return '"' + self.inclname + '"' + def sort_key(self, enclosing_inclname): + return (self.section(enclosing_inclname), self.inclname.lower()) -class HashIfBlock(object): - '''Important information about a #if/#endif block. + def to_source(self): + return self.include_prefix + self.quote() + self.line_suffix + '\n' + + +class CppBlock(object): + '''C preprocessor block: a whole file or a single #if/#elif/#else block. A #if/#endif block is the contents of a #if/#endif (or similar) section. The top-level block, which is not within a #if/#endif pair, is also considered a block. - Each leaf is either an Include (representing a #include), or another - nested HashIfBlock.''' - def __init__(self): + Each kid is either an Include (representing a #include), OrdinaryCode, or + a nested CppBlock.''' + def __init__(self, start_line=""): + self.start = start_line + self.end = '' self.kids = [] - def isLeaf(self): - return False + def is_style_relevant(self): + return True + + def append_ordinary_line(self, line): + if len(self.kids) == 0 or not isinstance(self.kids[-1], OrdinaryCode): + self.kids.append(OrdinaryCode()) + self.kids[-1].lines.append(line) + + def style_relevant_kids(self): + """ Return a list of kids in this block that are style-relevant. """ + return [kid for kid in self.kids if kid.is_style_relevant()] + + def sorted(self, enclosing_inclname): + """Return a hopefully-sorted copy of this block. Implements --fixup. + + When in doubt, this leaves the code unchanged. + """ + + def pretty_sorted_includes(includes): + """ Return a new list containing the given includes, in order, + with blank lines separating sections. """ + keys = [inc.sort_key(enclosing_inclname) for inc in includes] + if sorted(keys) == keys: + return includes # if nothing is out of order, don't touch anything + + output = [] + current_section = None + for (section, _), inc in sorted(zip(keys, includes)): + if current_section is not None and section != current_section: + output.append(OrdinaryCode(["\n"])) # blank line + output.append(inc) + current_section = section + return output + + def should_try_to_sort(includes): + if 'tests/style/BadIncludes' in enclosing_inclname: + return False # don't straighten the counterexample + if any(inc.inclname in oddly_ordered_inclnames for inc in includes): + return False # don't sort batches containing odd includes + if includes == sorted(includes, key=lambda inc: inc.sort_key(enclosing_inclname)): + return False # it's already sorted, avoid whitespace-only fixups + return True + + # The content of the eventual output of this method. + output = [] + + # The current batch of includes to sort. This list only ever contains Include objects + # and whitespace OrdinaryCode objects. + batch = [] + + def flush_batch(): + """Sort the contents of `batch` and move it to `output`.""" + + assert all(isinstance(item, Include) + or (isinstance(item, OrdinaryCode) and "".join(item.lines).isspace()) + for item in batch) + + # Here we throw away the blank lines. + # `pretty_sorted_includes` puts them back. + includes = [] + last_include_index = -1 + for i, item in enumerate(batch): + if isinstance(item, Include): + includes.append(item) + last_include_index = i + cutoff = last_include_index + 1 + + if should_try_to_sort(includes): + output.extend(pretty_sorted_includes(includes) + batch[cutoff:]) + else: + output.extend(batch) + del batch[:] + + for kid in self.kids: + if isinstance(kid, CppBlock): + flush_batch() + output.append(kid.sorted(enclosing_inclname)) + elif isinstance(kid, Include): + batch.append(kid) + else: + assert isinstance(kid, OrdinaryCode) + if kid.to_source().isspace(): + batch.append(kid) + else: + flush_batch() + output.append(kid) + flush_batch() + + result = CppBlock() + result.start = self.start + result.end = self.end + result.kids = output + return result + + def to_source(self): + return self.start + ''.join(kid.to_source() for kid in self.kids) + self.end -def do_file(filename, inclname, file_kind, f, all_inclnames, included_h_inclnames): - block_stack = [HashIfBlock()] +class OrdinaryCode(object): + ''' A list of lines of code that aren't #include/#if/#else/#endif lines. ''' + def __init__(self, lines=None): + self.lines = lines if lines is not None else [] + + def is_style_relevant(self): + return False - # Extract the #include statements as a tree of IBlocks and IIncludes. - for linenum, line in enumerate(f, start=1): - # We're only interested in lines that contain a '#'. - if not '#' in line: - continue + def to_source(self): + return ''.join(self.lines) + + +# A "snippet" is one of: +# +# * Include - representing an #include line +# * CppBlock - a whole file or #if/#elif/#else block; contains a list of snippets +# * OrdinaryCode - representing lines of non-#include-relevant code - # Look for a |#include "..."| line. - m = re.match(r'\s*#\s*include\s+"([^"]*)"', line) - if m is not None: - block_stack[-1].kids.append(Include(m.group(1), linenum, False)) +def read_file(f): + block_stack = [CppBlock()] - # Look for a |#include <...>| line. - m = re.match(r'\s*#\s*include\s+<([^>]*)>', line) - if m is not None: - block_stack[-1].kids.append(Include(m.group(1), linenum, True)) + # Extract the #include statements as a tree of snippets. + for linenum, line in enumerate(f, start=1): + if line.lstrip().startswith('#'): + # Look for a |#include "..."| line. + m = re.match(r'(\s*#\s*include\s+)"([^"]*)"(.*)', line) + if m is not None: + prefix, inclname, suffix = m.groups() + block_stack[-1].kids.append(Include(prefix, inclname, suffix, linenum, is_system=False)) + continue + + # Look for a |#include <...>| line. + m = re.match(r'(\s*#\s*include\s+)<([^>]*)>(.*)', line) + if m is not None: + prefix, inclname, suffix = m.groups() + block_stack[-1].kids.append(Include(prefix, inclname, suffix, linenum, is_system=True)) + continue - # Look for a |#{if,ifdef,ifndef}| line. - m = re.match(r'\s*#\s*(if|ifdef|ifndef)\b', line) - if m is not None: - # Open a new block. - new_block = HashIfBlock() - block_stack[-1].kids.append(new_block) - block_stack.append(new_block) + # Look for a |#{if,ifdef,ifndef}| line. + m = re.match(r'\s*#\s*(if|ifdef|ifndef)\b', line) + if m is not None: + # Open a new block. + new_block = CppBlock(line) + block_stack[-1].kids.append(new_block) + block_stack.append(new_block) + continue + + # Look for a |#{elif,else}| line. + m = re.match(r'\s*#\s*(elif|else)\b', line) + if m is not None: + # Close the current block, and open an adjacent one. + block_stack.pop() + new_block = CppBlock(line) + block_stack[-1].kids.append(new_block) + block_stack.append(new_block) + continue - # Look for a |#{elif,else}| line. - m = re.match(r'\s*#\s*(elif|else)\b', line) - if m is not None: - # Close the current block, and open an adjacent one. - block_stack.pop() - new_block = HashIfBlock() - block_stack[-1].kids.append(new_block) - block_stack.append(new_block) + # Look for a |#endif| line. + m = re.match(r'\s*#\s*endif\b', line) + if m is not None: + # Close the current block. + block_stack.pop().end = line + if len(block_stack) == 0: + raise ValueError("#endif without #if at line " + str(linenum)) + continue - # Look for a |#endif| line. - m = re.match(r'\s*#\s*endif\b', line) - if m is not None: - # Close the current block. - block_stack.pop() + # Otherwise, we have an ordinary line. + block_stack[-1].append_ordinary_line(line) + + if len(block_stack) > 1: + raise ValueError("unmatched #if") + return block_stack[-1] + + +def check_file(filename, inclname, file_kind, code, all_inclnames, included_h_inclnames): def check_include_statement(include): '''Check the style of a single #include statement.''' if include.is_system: # Check it is not a known local file (in which case it's probably a system header). if include.inclname in included_inclnames_to_ignore or \ include.inclname in all_inclnames: @@ -498,25 +646,26 @@ def do_file(filename, inclname, file_kin if (section1 > section2) or \ ((section1 == section2) and (include1.inclname.lower() > include2.inclname.lower())): error(filename, str(include1.linenum) + ':' + str(include2.linenum), include1.quote() + ' should be included after ' + include2.quote()) # Check the extracted #include statements, both individually, and the ordering of # adjacent pairs that live in the same block. def pair_traverse(prev, this): - if this.isLeaf(): + if isinstance(this, Include): check_include_statement(this) - if prev is not None and prev.isLeaf(): + if isinstance(prev, Include): check_includes_order(prev, this) else: - for prev2, this2 in zip([None] + this.kids[0:-1], this.kids): + kids = this.style_relevant_kids() + for prev2, this2 in zip([None] + kids[0:-1], kids): pair_traverse(prev2, this2) - pair_traverse(None, block_stack[-1]) + pair_traverse(None, code) def find_cycles(all_inclnames, edges): '''Find and draw any cycles.''' SCCs = tarjan(all_inclnames, edges) # The various sorted() calls below ensure the output is deterministic. @@ -583,20 +732,39 @@ def tarjan(V, E): for v in V: if v not in vertex_index: index = strongconnect(v, index) return all_SCCs def main(): - ok = check_style() + if sys.argv[1:] == ["--fixup"]: + # Sort #include directives in-place. Fixup mode doesn't solve + # all possible silliness that the script checks for; it's just a + # hack for the common case where renaming a header causes style + # errors. + fixup = True + elif sys.argv[1:] == []: + fixup = False + else: + print("TEST-UNEXPECTED-FAIL | check_spidermonkey_style.py | unexpected command line options: " + + repr(sys.argv[1:])) + sys.exit(1) + + ok = check_style(fixup) if ok: print('TEST-PASS | check_spidermonkey_style.py | ok') else: - print('TEST-UNEXPECTED-FAIL | check_spidermonkey_style.py | actual output does not match expected output; diff is above') + print('TEST-UNEXPECTED-FAIL | check_spidermonkey_style.py | ' + + 'actual output does not match expected output; diff is above.') + print('TEST-UNEXPECTED-FAIL | check_spidermonkey_style.py | ' + + 'Hint: If the problem is that you renamed a header, and many #includes ' + + 'are no longer in alphabetical order, commit your work and then try ' + + '`check_spidermonkey_style.py --fixup`. ' + + 'You need to commit first because --fixup modifies your files in place.') sys.exit(0 if ok else 1) if __name__ == '__main__': main()
--- a/devtools/client/debugger/new/test/mochitest/browser.ini +++ b/devtools/client/debugger/new/test/mochitest/browser.ini @@ -96,19 +96,18 @@ skip-if = !e10s # This test is only vali [browser_dbg-content-script-sources.js] [browser_dbg-debugger-buttons.js] [browser_dbg-editor-gutter.js] [browser_dbg-editor-select.js] [browser_dbg-editor-highlight.js] [browser_dbg-expressions.js] [browser_dbg-expressions-error.js] [browser_dbg-iframes.js] -[browser_dbg_keyboard_navigation.js] -skip-if = true # regular failures during release in Bug 1415300 -[browser_dbg_keyboard-shortcuts.js] +[browser_dbg-keyboard-navigation.js] +[browser_dbg-keyboard-shortcuts.js] skip-if = os == "linux" # bug 1351952 [browser_dbg-layout-changes.js] [browser_dbg-outline.js] [browser_dbg-pause-exceptions.js] [browser_dbg-pause-ux.js] skip-if = os == "win" [browser_dbg-navigation.js] [browser_dbg-minified.js] @@ -119,25 +118,24 @@ skip-if = os == "win" skip-if = os == "win" [browser_dbg-preview-source-maps.js] skip-if = os == "win" [browser_dbg-returnvalues.js] [browser_dbg-scopes-mutations.js] [browser_dbg-search-file.js] skip-if = os == "win" # Bug 1393121 [browser_dbg-quick-open.js] -skip-if = true # regular failures during release in Bug 1415300 +skip-if = os == "win" [browser_dbg-search-project.js] [browser_dbg-sourcemaps.js] [browser_dbg-sourcemaps-reload.js] [browser_dbg-sourcemaps-reloading.js] [browser_dbg-sourcemaps2.js] [browser_dbg-sourcemaps3.js] [browser_dbg-sourcemaps-bogus.js] [browser_dbg-sources.js] [browser_dbg-sources-named-eval.js] [browser_dbg-tabs.js] [browser_dbg-tabs-pretty-print.js] [browser_dbg-toggling-tools.js] -skip-if = true # Bug 1414124 [browser_dbg-wasm-sourcemaps.js] skip-if = true [browser_dbg-reload.js]
rename from devtools/client/debugger/new/test/mochitest/browser_dbg_keyboard_navigation.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg-keyboard-navigation.js
rename from devtools/client/debugger/new/test/mochitest/browser_dbg_keyboard-shortcuts.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg-keyboard-shortcuts.js
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-quick-open.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-quick-open.js @@ -57,63 +57,58 @@ function quickOpen(dbg, query, shortcut add_task(async function() { const dbg = await initDebugger("doc-script-switching.html"); info("test opening and closing"); quickOpen(dbg, ""); pressKey(dbg, "Escape"); assertDisabled(dbg); + await waitForSource(dbg, "switching-01"); quickOpen(dbg, "sw"); pressKey(dbg, "Enter"); - let source = dbg.selectors.getSelectedSource(dbg.getState()); - ok(source.get("url").match(/switching-01/), "first source is selected"); await waitForSelectedSource(dbg, "switching-01"); - info("Arrow keys and check to see if source is selected"); quickOpen(dbg, "sw"); is(resultCount(dbg), 2, "two file results"); pressKey(dbg, "Down"); pressKey(dbg, "Enter"); - source = dbg.selectors.getSelectedSource(dbg.getState()); - ok(source.get("url").match(/switching-02/), "second source is selected"); await waitForSelectedSource(dbg, "switching-02"); quickOpen(dbg, "sw"); pressKey(dbg, "Tab"); assertDisabled(dbg); info("Testing function search"); quickOpen(dbg, "", "quickOpenFunc"); is(resultCount(dbg), 2, "two function results"); - type(dbg, "x"); + type(dbg, "@x"); is(resultCount(dbg), 0, "no functions with 'x' in name"); pressKey(dbg, "Escape"); assertDisabled(dbg); info("Testing variable search"); quickOpen(dbg, "sw2"); pressKey(dbg, "Enter"); quickOpen(dbg, "#"); is(resultCount(dbg), 1, "one variable result"); const results = findAllElements(dbg, "resultItems"); - results.forEach(result => is(result.textContent, "x13")); + results.forEach(result => is(result.textContent, "13")); await waitToClose(dbg); info("Testing goto line:column"); - assertLine(dbg, undefined); - assertColumn(dbg, undefined); + assertLine(dbg, 0); + assertColumn(dbg, null); quickOpen(dbg, ":7:12"); pressKey(dbg, "Enter"); assertLine(dbg, 7); assertColumn(dbg, 12); info("Testing gotoSource"); quickOpen(dbg, "sw1:5"); pressKey(dbg, "Enter"); - source = dbg.selectors.getSelectedSource(dbg.getState()); - ok(source.get("url").match(/switching-01/), "first source is selected"); + await waitForSelectedSource(dbg, "switching-01"); assertLine(dbg, 5); });
--- a/devtools/client/framework/components/toolbox-toolbar.js +++ b/devtools/client/framework/components/toolbox-toolbar.js @@ -109,24 +109,26 @@ function renderToolboxButtons({toolboxBu return null; } return div({id: `toolbox-buttons-${isStart ? "start" : "end"}`}, ...visibleButtons.map(command => { const { id, description, + disabled, onClick, isChecked, className: buttonClass, onKeyDown } = command; return button({ id, title: description, + disabled, className: ( "command-button devtools-button " + buttonClass + (isChecked ? " checked" : "") ), onClick: (event) => { onClick(event); focusButton(id); },
--- a/devtools/client/framework/toolbox.js +++ b/devtools/client/framework/toolbox.js @@ -149,16 +149,17 @@ function Toolbox(target, selectedTool, h this._onToolbarFocus = this._onToolbarFocus.bind(this); this._onToolbarArrowKeypress = this._onToolbarArrowKeypress.bind(this); this._onPickerClick = this._onPickerClick.bind(this); this._onPickerKeypress = this._onPickerKeypress.bind(this); this._onPickerStarted = this._onPickerStarted.bind(this); this._onPickerStopped = this._onPickerStopped.bind(this); this._onInspectObject = this._onInspectObject.bind(this); this._onNewSelectedNodeFront = this._onNewSelectedNodeFront.bind(this); + this._updatePickerButton = this._updatePickerButton.bind(this); this.selectTool = this.selectTool.bind(this); this._target.on("close", this.destroy); if (!selectedTool) { selectedTool = Services.prefs.getCharPref(this._prefs.LAST_TOOL); } this._defaultToolId = selectedTool; @@ -172,16 +173,17 @@ function Toolbox(target, selectedTool, h this._target.on("will-navigate", this._onWillNavigate); this._target.on("navigate", this._refreshHostTitle); this._target.on("frame-update", this._updateFrames); this._target.on("inspect-object", this._onInspectObject); this.on("host-changed", this._refreshHostTitle); this.on("select", this._refreshHostTitle); + this.on("select", this._updatePickerButton); this.on("ready", this._showDevEditionPromo); gDevTools.on("tool-registered", this._toolRegistered); gDevTools.on("tool-unregistered", this._toolUnregistered); this.on("picker-started", this._onPickerStarted); this.on("picker-stopped", this._onPickerStopped); @@ -718,16 +720,17 @@ Toolbox.prototype = { * components to listen and respond to updates. * * @param {Object} options: * * @property {String} id - The id of the button or command. * @property {String} className - An optional additional className for the button. * @property {String} description - The value that will display as a tooltip and in * the options panel for enabling/disabling. + * @property {Boolean} disabled - An optional disabled state for the button. * @property {Function} onClick - The function to run when the button is activated by * click or keyboard shortcut. First argument will be the 'click' * event, and second argument is the toolbox instance. * @property {Boolean} isInStartContainer - Buttons can either be placed at the start * of the toolbar, or at the end. * @property {Function} setup - Function run immediately to listen for events changing * whenever the button is checked or unchecked. The toolbox object * is passed as first argument and a callback is passed as second @@ -744,29 +747,31 @@ Toolbox.prototype = { * the button should be displayed as toggled on. */ _createButtonState: function (options) { let isCheckedValue = false; const { id, className, description, + disabled, onClick, isInStartContainer, setup, teardown, isTargetSupported, isChecked, onKeyDown } = options; const toolbox = this; const button = { id, className, description, + disabled, onClick(event) { if (typeof onClick == "function") { onClick(event, toolbox); } }, onKeyDown(event) { if (typeof onKeyDown == "function") { onKeyDown(event, toolbox); @@ -1304,30 +1309,43 @@ Toolbox.prototype = { return this.autohideButton; }), /** * Toggle the picker, but also decide whether or not the highlighter should * focus the window. This is only desirable when the toolbox is mounted to the * window. When devtools is free floating, then the target window should not * pop in front of the viewer when the picker is clicked. + * + * Note: Toggle picker can be overwritten by panel other than the inspector to + * allow for custom picker behaviour. */ _onPickerClick: function () { let focus = this.hostType === Toolbox.HostType.BOTTOM || this.hostType === Toolbox.HostType.SIDE; - this.highlighterUtils.togglePicker(focus); + let currentPanel = this.getCurrentPanel(); + if (currentPanel.togglePicker) { + currentPanel.togglePicker(focus); + } else { + this.highlighterUtils.togglePicker(focus); + } }, /** * If the picker is activated, then allow the Escape key to deactivate the * functionality instead of the default behavior of toggling the console. */ _onPickerKeypress: function (event) { if (event.keyCode === KeyCodes.DOM_VK_ESCAPE) { - this.highlighterUtils.cancelPicker(); + let currentPanel = this.getCurrentPanel(); + if (currentPanel.cancelPicker) { + currentPanel.cancelPicker(); + } else { + this.highlighterUtils.cancelPicker(); + } // Stop the console from toggling. event.stopImmediatePropagation(); } }, _onPickerStarted: function () { this.doc.addEventListener("keypress", this._onPickerKeypress, true); }, @@ -1393,16 +1411,39 @@ Toolbox.prototype = { updateToolboxButtonsVisibility() { this.toolbarButtons.forEach(button => { button.isVisible = this._commandIsVisible(button); }); this.component.setToolboxButtons(this.toolbarButtons); }, /** + * Visually update picker button. + * This function is called on every "select" event. Newly selected panel can + * update the visual state of the picker button such as disabled state, + * additional CSS classes (className), and tooltip (description). + */ + _updatePickerButton() { + const button = this.pickerButton; + let currentPanel = this.getCurrentPanel(); + + if (currentPanel && currentPanel.updatePickerButton) { + currentPanel.updatePickerButton(); + } else { + // If the current panel doesn't define a custom updatePickerButton, + // revert the button to its default state + button.description = L10N.getStr("pickButton.tooltip"); + button.className = null; + button.disabled = null; + } + + this.component.setToolboxButtons(this.toolbarButtons); + }, + + /** * Ensure the visibility of each toolbox button matches the preference value. */ _commandIsVisible: function (button) { const { isTargetSupported, visibilityswitch } = button; @@ -2578,17 +2619,23 @@ Toolbox.prototype = { if (!this._inspector) { return; } // Ensure that the inspector isn't still being initiated, otherwise race conditions // in the initialization process can throw errors. yield this._initInspector; - yield this.highlighterUtils.stopPicker(); + let currentPanel = this.getCurrentPanel(); + if (currentPanel.stopPicker) { + yield currentPanel.stopPicker(); + } else { + yield this.highlighterUtils.stopPicker(); + } + yield this._inspector.destroy(); if (this._highlighter) { // Note that if the toolbox is closed, this will work fine, but will fail // in case the browser is closed and will trigger a noSuchActor message. // We ignore the promise that |_hideBoxModel| returns, since we should still // proceed with the rest of destruction if it fails. // FF42+ now does the cleanup from the actor. if (!this.highlighter.traits.autoHideOnDestroy) { @@ -2637,16 +2684,17 @@ Toolbox.prototype = { this.emit("destroy"); this._target.off("inspect-object", this._onInspectObject); this._target.off("will-navigate", this._onWillNavigate); this._target.off("navigate", this._refreshHostTitle); this._target.off("frame-update", this._updateFrames); this.off("select", this._refreshHostTitle); + this.off("select", this._updatePickerButton); this.off("host-changed", this._refreshHostTitle); this.off("ready", this._showDevEditionPromo); gDevTools.off("tool-registered", this._toolRegistered); gDevTools.off("tool-unregistered", this._toolUnregistered); Services.prefs.removeObserver("devtools.cache.disabled", this._applyCacheSettings); Services.prefs.removeObserver("devtools.serviceWorkers.testing.enabled",
--- a/devtools/client/inspector/markup/views/element-editor.js +++ b/devtools/client/inspector/markup/views/element-editor.js @@ -38,17 +38,17 @@ const HTML_VOID_ELEMENTS = [ // Contains only valid computed display property types of the node to display in the // element markup and their respective title tooltip text. const DISPLAY_TYPES = { "flex": INSPECTOR_L10N.getStr("markupView.display.flex.tooltiptext"), "inline-flex": INSPECTOR_L10N.getStr("markupView.display.flex.tooltiptext"), "grid": INSPECTOR_L10N.getStr("markupView.display.grid.tooltiptext"), "inline-grid": INSPECTOR_L10N.getStr("markupView.display.inlineGrid.tooltiptext"), "flow-root": INSPECTOR_L10N.getStr("markupView.display.flowRoot.tooltiptext"), - "contents": INSPECTOR_L10N.getStr("markupView.display.contents.tooltiptext"), + "contents": INSPECTOR_L10N.getStr("markupView.display.contents.tooltiptext2"), }; /** * Creates an editor for an Element node. * * @param {MarkupContainer} container * The container owning this editor. * @param {Element} node
--- a/devtools/client/locales/en-US/inspector.properties +++ b/devtools/client/locales/en-US/inspector.properties @@ -58,20 +58,20 @@ markupView.display.grid.tooltiptext=This # the markup view. markupView.display.inlineGrid.tooltiptext=This element behaves like an inline element and lays out its content according to the grid model. # LOCALIZATION NOTE (markupView.display.flowRoot.tooltiptext) # Used in a tooltip that appears when the user hovers over the display type button in # the markup view. markupView.display.flowRoot.tooltiptext=This element generates a block element box that establishes a new block formatting context. -# LOCALIZATION NOTE (markupView.display.contents.tooltiptext) +# LOCALIZATION NOTE (markupView.display.contents.tooltiptext2) # Used in a tooltip that appears when the user hovers over the display type button in # the markup view. -markupView.display.contents.tooltiptext=This element doesn’t produce a specific box by themselves, but renders its contents. +markupView.display.contents.tooltiptext2=This element doesn’t produce a specific box by itself, but renders its contents. # LOCALIZATION NOTE (markupView.event.tooltiptext) # Used in a tooltip that appears when the user hovers over 'ev' button in # the markup view. markupView.event.tooltiptext=Event listener #LOCALIZATION NOTE: Used in the image preview tooltip when the image could not be loaded previewTooltip.image.brokenImage=Could not load the image
--- a/dom/base/nsGlobalWindowInner.cpp +++ b/dom/base/nsGlobalWindowInner.cpp @@ -238,16 +238,17 @@ #include "nsITabChild.h" #include "mozilla/dom/MediaQueryList.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/NavigatorBinding.h" #include "mozilla/dom/ImageBitmap.h" #include "mozilla/dom/ImageBitmapBinding.h" #include "mozilla/dom/ServiceWorker.h" #include "mozilla/dom/ServiceWorkerRegistration.h" +#include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h" #include "mozilla/dom/U2F.h" #include "mozilla/dom/WebIDLGlobalNameHash.h" #include "mozilla/dom/Worklet.h" #ifdef HAVE_SIDEBAR #include "mozilla/dom/ExternalBinding.h" #endif #ifdef MOZ_WEBSPEECH @@ -5168,24 +5169,25 @@ nsGlobalWindowInner::GetCaches(ErrorResu forceTrustedOrigin, aRv); } RefPtr<CacheStorage> ref = mCacheStorage; return ref.forget(); } already_AddRefed<ServiceWorkerRegistration> -nsPIDOMWindowInner::GetServiceWorkerRegistration(const nsAString& aScope) -{ +nsPIDOMWindowInner::GetServiceWorkerRegistration(const ServiceWorkerRegistrationDescriptor& aDescriptor) +{ + NS_ConvertUTF8toUTF16 scope(aDescriptor.Scope()); RefPtr<ServiceWorkerRegistration> registration; - if (!mServiceWorkerRegistrationTable.Get(aScope, + if (!mServiceWorkerRegistrationTable.Get(scope, getter_AddRefs(registration))) { registration = - ServiceWorkerRegistration::CreateForMainThread(this, aScope); - mServiceWorkerRegistrationTable.Put(aScope, registration); + ServiceWorkerRegistration::CreateForMainThread(this, aDescriptor); + mServiceWorkerRegistrationTable.Put(scope, registration); } return registration.forget(); } void nsPIDOMWindowInner::InvalidateServiceWorkerRegistration(const nsAString& aScope) { mServiceWorkerRegistrationTable.Remove(aScope);
--- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -51,16 +51,17 @@ class ClientState; class DocGroup; class TabGroup; class Element; class Navigator; class Performance; class ServiceWorker; class ServiceWorkerDescriptor; class ServiceWorkerRegistration; +class ServiceWorkerRegistrationDescriptor; class Timeout; class TimeoutManager; class CustomElementRegistry; enum class CallerType : uint32_t; } // namespace dom } // namespace mozilla // Popup control state enum. The values in this enum must go from most @@ -182,17 +183,17 @@ public: void RemoveAudioContext(mozilla::dom::AudioContext* aAudioContext); void MuteAudioContexts(); void UnmuteAudioContexts(); bool GetAudioCaptured() const; nsresult SetAudioCapture(bool aCapture); already_AddRefed<mozilla::dom::ServiceWorkerRegistration> - GetServiceWorkerRegistration(const nsAString& aScope); + GetServiceWorkerRegistration(const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor); void InvalidateServiceWorkerRegistration(const nsAString& aScope); mozilla::dom::Performance* GetPerformance(); bool HasMutationListeners(uint32_t aMutationEventType) const { if (!mOuterWindow) { NS_ERROR("HasMutationListeners() called on orphan inner window!");
--- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -846,18 +846,19 @@ FetchDriver::OnStartRequest(nsIRequest* response->Headers()->FillResponseHeaders(httpChannel); // If Content-Encoding or Transfer-Encoding headers are set, then the actual // Content-Length (which refer to the decoded data) is obscured behind the encodings. ErrorResult result; if (response->Headers()->Has(NS_LITERAL_CSTRING("content-encoding"), result) || response->Headers()->Has(NS_LITERAL_CSTRING("transfer-encoding"), result)) { - NS_WARNING("Cannot know response Content-Length due to presence of Content-Encoding " - "or Transfer-Encoding headers."); + // We cannot trust the content-length when content-encoding or + // transfer-encoding are set. There are many servers which just + // get this wrong. contentLength = InternalResponse::UNKNOWN_BODY_SIZE; } MOZ_ASSERT(!result.Failed()); } else { response = new InternalResponse(200, NS_LITERAL_CSTRING("OK")); ErrorResult result; nsAutoCString contentType;
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl +++ b/dom/interfaces/base/nsIServiceWorkerManager.idl @@ -152,26 +152,16 @@ interface nsIServiceWorkerManager : nsIS nsIServiceWorkerRegistrationInfo getRegistrationByPrincipal(in nsIPrincipal aPrincipal, in DOMString aScope); [notxpcom, nostdcall] bool StartControlling(in const_ClientInfoRef aClientInfo, in const_ServiceWorkerDescriptorRef aServiceWorker); /* - * Returns a ServiceWorker. - * window is the window of the caller. scope is the registration's scope and must be - * a valid entry that window is allowed to load, otherwise this will return nullptr. - * These are only meant to be called from ServiceWorkerRegistration instances. - */ - [noscript] nsISupports GetInstalling(in nsPIDOMWindowInner aWindow, in DOMString aScope); - [noscript] nsISupports GetWaiting(in nsPIDOMWindowInner aWindow, in DOMString aScope); - [noscript] nsISupports GetActive(in nsPIDOMWindowInner aWindow, in DOMString aScope); - - /* * Clears ServiceWorker registrations from memory and disk for the specified * host. * - All ServiceWorker instances change their state to redundant. * - Existing ServiceWorker instances handling fetches will keep running. * - All documents will immediately stop being controlled. * - Unregister jobs will be queued for all registrations. * This eventually results in the registration being deleted from disk too. */
--- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -39,16 +39,17 @@ #include "mozilla/dom/PLoginReputationChild.h" #include "mozilla/dom/ProcessGlobal.h" #include "mozilla/dom/PushNotifier.h" #include "mozilla/dom/ServiceWorkerManager.h" #include "mozilla/dom/TabGroup.h" #include "mozilla/dom/nsIContentChild.h" #include "mozilla/dom/URLClassifierChild.h" #include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/Logging.h" #include "mozilla/psm/PSMContentListener.h" #include "mozilla/hal_sandbox/PHalChild.h" #include "mozilla/ipc/BackgroundChild.h" #include "mozilla/ipc/FileDescriptorSetChild.h" #include "mozilla/ipc/FileDescriptorUtils.h" #include "mozilla/ipc/GeckoChildProcessHost.h" #include "mozilla/ipc/ProcessChild.h" #include "mozilla/ipc/PChildToParentStreamChild.h" @@ -1316,35 +1317,57 @@ ContentChild::RecvGMPsChanged(nsTArray<G mozilla::ipc::IPCResult ContentChild::RecvInitProcessHangMonitor(Endpoint<PProcessHangMonitorChild>&& aHangMonitor) { CreateHangMonitorChild(Move(aHangMonitor)); return IPC_OK(); } mozilla::ipc::IPCResult +ContentChild::GetResultForRenderingInitFailure(base::ProcessId aOtherPid) +{ + if (aOtherPid == base::GetCurrentProcId() || aOtherPid == OtherPid()) { + // If we are talking to ourselves, or the UI process, then that is a fatal + // protocol error. + return IPC_FAIL_NO_REASON(this); + } + + // If we are talking to the GPU process, then we should recover from this on + // the next ContentChild::RecvReinitRendering call. + gfxCriticalNote << "Could not initialize rendering with GPU process"; + return IPC_OK(); +} + +mozilla::ipc::IPCResult ContentChild::RecvInitRendering(Endpoint<PCompositorManagerChild>&& aCompositor, Endpoint<PImageBridgeChild>&& aImageBridge, Endpoint<PVRManagerChild>&& aVRBridge, Endpoint<PVideoDecoderManagerChild>&& aVideoManager, nsTArray<uint32_t>&& namespaces) { MOZ_ASSERT(namespaces.Length() == 3); + // Note that for all of the methods below, if it can fail, it should only + // return false if the failure is an IPDL error. In such situations, + // ContentChild can reason about whether or not to wait for + // RecvReinitRendering (because we surmised the GPU process crashed), or if it + // should crash itself (because we are actually talking to the UI process). If + // there are localized failures (e.g. failed to spawn a thread), then it + // should MOZ_RELEASE_ASSERT or MOZ_CRASH as necessary instead. if (!CompositorManagerChild::Init(Move(aCompositor), namespaces[0])) { - return IPC_FAIL_NO_REASON(this); + return GetResultForRenderingInitFailure(aCompositor.OtherPid()); } if (!CompositorManagerChild::CreateContentCompositorBridge(namespaces[1])) { - return IPC_FAIL_NO_REASON(this); + return GetResultForRenderingInitFailure(aCompositor.OtherPid()); } if (!ImageBridgeChild::InitForContent(Move(aImageBridge), namespaces[2])) { - return IPC_FAIL_NO_REASON(this); + return GetResultForRenderingInitFailure(aImageBridge.OtherPid()); } if (!gfx::VRManagerChild::InitForContent(Move(aVRBridge))) { - return IPC_FAIL_NO_REASON(this); + return GetResultForRenderingInitFailure(aVRBridge.OtherPid()); } VideoDecoderManagerChild::InitForContent(Move(aVideoManager)); return IPC_OK(); } mozilla::ipc::IPCResult ContentChild::RecvReinitRendering(Endpoint<PCompositorManagerChild>&& aCompositor, Endpoint<PImageBridgeChild>&& aImageBridge, @@ -1359,26 +1382,26 @@ ContentChild::RecvReinitRendering(Endpoi for (const auto& tabChild : tabs) { if (tabChild->LayersId()) { tabChild->InvalidateLayers(); } } // Re-establish singleton bridges to the compositor. if (!CompositorManagerChild::Init(Move(aCompositor), namespaces[0])) { - return IPC_FAIL_NO_REASON(this); + return GetResultForRenderingInitFailure(aCompositor.OtherPid()); } if (!CompositorManagerChild::CreateContentCompositorBridge(namespaces[1])) { - return IPC_FAIL_NO_REASON(this); + return GetResultForRenderingInitFailure(aCompositor.OtherPid()); } if (!ImageBridgeChild::ReinitForContent(Move(aImageBridge), namespaces[2])) { - return IPC_FAIL_NO_REASON(this); + return GetResultForRenderingInitFailure(aImageBridge.OtherPid()); } if (!gfx::VRManagerChild::ReinitForContent(Move(aVRBridge))) { - return IPC_FAIL_NO_REASON(this); + return GetResultForRenderingInitFailure(aVRBridge.OtherPid()); } gfxPlatform::GetPlatform()->CompositorUpdated(); // Establish new PLayerTransactions. for (const auto& tabChild : tabs) { if (tabChild->LayersId()) { tabChild->ReinitRendering(); }
--- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -724,16 +724,19 @@ public: #endif private: static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure); void StartForceKillTimer(); void ShutdownInternal(); + mozilla::ipc::IPCResult + GetResultForRenderingInitFailure(base::ProcessId aOtherPid); + virtual void ActorDestroy(ActorDestroyReason why) override; virtual void ProcessingError(Result aCode, const char* aReason) override; virtual already_AddRefed<nsIEventTarget> GetConstructedEventTarget(const Message& aMsg) override; virtual already_AddRefed<nsIEventTarget>
--- a/dom/ipc/ContentPrefs.cpp +++ b/dom/ipc/ContentPrefs.cpp @@ -309,17 +309,16 @@ const char* mozilla::dom::ContentPrefs:: "security.sandbox.content.tempDirSuffix", "security.sandbox.logging.enabled", "security.sandbox.mac.track.violations", "security.sandbox.windows.log.stackTraceDepth", "svg.disabled", "svg.display-lists.hit-testing.enabled", "svg.display-lists.painting.enabled", "svg.new-getBBox.enabled", - "svg.paint-order.enabled", "svg.path-caching.enabled", "svg.transform-box.enabled", "toolkit.asyncshutdown.crash_timeout", "toolkit.asyncshutdown.log", "toolkit.osfile.log", "toolkit.osfile.log.redirect", "toolkit.telemetry.enabled", "toolkit.telemetry.idleTimeout",
--- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -1115,18 +1115,20 @@ TabChild::ActorDestroy(ActorDestroyReaso // The messageManager relays messages via the TabChild which // no longer exists. static_cast<nsFrameMessageManager*> (mTabChildGlobal->mMessageManager.get())->Disconnect(); mTabChildGlobal->mMessageManager = nullptr; } } - CompositorBridgeChild* compositorChild = static_cast<CompositorBridgeChild*>(CompositorBridgeChild::Get()); - compositorChild->CancelNotifyAfterRemotePaint(this); + CompositorBridgeChild* compositorChild = CompositorBridgeChild::Get(); + if (compositorChild) { + compositorChild->CancelNotifyAfterRemotePaint(this); + } if (GetTabId() != 0) { NestedTabChildMap().erase(GetTabId()); } } TabChild::~TabChild() {
--- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -3193,16 +3193,21 @@ ScriptLoader::PreloadURI(nsIURI* aURI, } // TODO: Preload module scripts. if (aType.LowerCaseEqualsASCII("module")) { return; } } + if (!aType.IsEmpty() && !nsContentUtils::IsJavascriptMIMEType(aType)) { + // Unknown type. Don't load it. + return; + } + SRIMetadata sriMetadata; GetSRIMetadata(aIntegrity, &sriMetadata); RefPtr<ScriptLoadRequest> request = CreateLoadRequest(ScriptKind::eClassic, aURI, nullptr, Element::StringToCORSMode(aCrossOrigin), sriMetadata, aReferrerPolicy); request->mTriggeringPrincipal = mDocument->NodePrincipal();
deleted file mode 100644 --- a/dom/serviceworkers/ServiceWorkerCommon.h +++ /dev/null @@ -1,25 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_ServiceWorkerCommon_h -#define mozilla_dom_ServiceWorkerCommon_h - -namespace mozilla { -namespace dom { - -// Use multiples of 2 since they can be bitwise ORed when calling -// InvalidateServiceWorkerRegistrationWorker. -enum class WhichServiceWorker { - INSTALLING_WORKER = 1, - WAITING_WORKER = 2, - ACTIVE_WORKER = 4, -}; -MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(WhichServiceWorker) - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_ServiceWorkerCommon_h
--- a/dom/serviceworkers/ServiceWorkerDescriptor.cpp +++ b/dom/serviceworkers/ServiceWorkerDescriptor.cpp @@ -47,16 +47,19 @@ ServiceWorkerDescriptor::ServiceWorkerDe ServiceWorkerDescriptor::ServiceWorkerDescriptor(const ServiceWorkerDescriptor& aRight) { operator=(aRight); } ServiceWorkerDescriptor& ServiceWorkerDescriptor::operator=(const ServiceWorkerDescriptor& aRight) { + if (this == &aRight) { + return *this; + } mData.reset(); mData = MakeUnique<IPCServiceWorkerDescriptor>(*aRight.mData); return *this; } ServiceWorkerDescriptor::ServiceWorkerDescriptor(ServiceWorkerDescriptor&& aRight) : mData(Move(aRight.mData)) {
--- a/dom/serviceworkers/ServiceWorkerDescriptor.h +++ b/dom/serviceworkers/ServiceWorkerDescriptor.h @@ -1,16 +1,17 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef _mozilla_dom_ServiceWorkerDescriptor_h #define _mozilla_dom_ServiceWorkerDescriptor_h +#include "mozilla/UniquePtr.h" #include "nsString.h" class nsIPrincipal; namespace mozilla { namespace ipc { class PrincipalInfo;
--- a/dom/serviceworkers/ServiceWorkerManager.cpp +++ b/dom/serviceworkers/ServiceWorkerManager.cpp @@ -75,16 +75,17 @@ #include "ServiceWorkerContainer.h" #include "ServiceWorkerInfo.h" #include "ServiceWorkerJobQueue.h" #include "ServiceWorkerManagerChild.h" #include "ServiceWorkerPrivate.h" #include "ServiceWorkerRegisterJob.h" #include "ServiceWorkerRegistrar.h" #include "ServiceWorkerRegistration.h" +#include "ServiceWorkerRegistrationListener.h" #include "ServiceWorkerScriptCache.h" #include "ServiceWorkerEvents.h" #include "ServiceWorkerUnregisterJob.h" #include "ServiceWorkerUpdateJob.h" #include "ServiceWorkerUpdaterChild.h" #ifdef PostMessage #undef PostMessage @@ -460,18 +461,25 @@ class ServiceWorkerResolveWindowPromiseO } MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Register); RefPtr<ServiceWorkerRegisterJob> registerJob = static_cast<ServiceWorkerRegisterJob*>(aJob); RefPtr<ServiceWorkerRegistrationInfo> reg = registerJob->GetRegistration(); RefPtr<ServiceWorkerRegistration> swr = - window->GetServiceWorkerRegistration(NS_ConvertUTF8toUTF16(reg->Scope())); - promise->MaybeResolve(swr); + window->GetServiceWorkerRegistration(reg->Descriptor()); + + nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( + "ServiceWorkerResolveWindowPromiseOnRegisterCallback::JobFinished", + [promise = Move(promise), swr = Move(swr)] () { + promise->MaybeResolve(swr); + }); + MOZ_ALWAYS_SUCCEEDS( + window->EventTargetFor(TaskCategory::Other)->Dispatch(r.forget())); } public: ServiceWorkerResolveWindowPromiseOnRegisterCallback(nsPIDOMWindowInner* aWindow, Promise* aPromise) : mPromise(aWindow, aPromise) {} @@ -1018,17 +1026,17 @@ public: rv = principal->CheckMayLoad(scopeURI, true /* report */, false /* allowIfInheritsPrincipal */); if (NS_WARN_IF(NS_FAILED(rv))) { continue; } RefPtr<ServiceWorkerRegistration> swr = - mWindow->GetServiceWorkerRegistration(scope); + mWindow->GetServiceWorkerRegistration(info->Descriptor()); array.AppendElement(swr); } mPromise->MaybeResolve(array); return NS_OK; } }; @@ -1141,19 +1149,18 @@ public: RefPtr<ServiceWorkerRegistrationInfo> registration = swm->GetServiceWorkerRegistrationInfo(principal, uri); if (!registration) { mPromise->MaybeResolveWithUndefined(); return NS_OK; } - NS_ConvertUTF8toUTF16 scope(registration->Scope()); RefPtr<ServiceWorkerRegistration> swr = - mWindow->GetServiceWorkerRegistration(scope); + mWindow->GetServiceWorkerRegistration(registration->Descriptor()); mPromise->MaybeResolve(swr); return NS_OK; } }; // If we return an error code here, the ServiceWorkerContainer will // automatically reject the Promise. @@ -1458,19 +1465,18 @@ ServiceWorkerManager::CheckReadyPromise( nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal(); MOZ_ASSERT(principal); RefPtr<ServiceWorkerRegistrationInfo> registration = GetServiceWorkerRegistrationInfo(principal, aURI); if (registration && registration->GetActive()) { - NS_ConvertUTF8toUTF16 scope(registration->Scope()); RefPtr<ServiceWorkerRegistration> swr = - aWindow->GetServiceWorkerRegistration(scope); + aWindow->GetServiceWorkerRegistration(registration->Descriptor()); aPromise->MaybeResolve(swr); return true; } return false; } ServiceWorkerInfo* @@ -2303,82 +2309,16 @@ ServiceWorkerManager::FireUpdateFoundOnS NS_ConvertUTF16toUTF8 utf8Scope(regScope); if (utf8Scope.Equals(aRegistration->Scope())) { target->UpdateFound(); } } } -/* - * This is used for installing, waiting and active. - */ -nsresult -ServiceWorkerManager::GetServiceWorkerForScope(nsPIDOMWindowInner* aWindow, - const nsAString& aScope, - WhichServiceWorker aWhichWorker, - nsISupports** aServiceWorker) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!aWindow)) { - return NS_ERROR_DOM_INVALID_STATE_ERR; - } - - nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc(); - MOZ_ASSERT(doc); - - /////////////////////////////////////////// - // Security check - nsAutoCString scope = NS_ConvertUTF16toUTF8(aScope); - nsCOMPtr<nsIURI> scopeURI; - // We pass nullptr as the base URI since scopes obtained from - // ServiceWorkerRegistrations MUST be fully qualified URIs. - nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), scope, nullptr, nullptr); - if (NS_WARN_IF(NS_FAILED(rv))) { - return NS_ERROR_DOM_SECURITY_ERR; - } - - nsCOMPtr<nsIPrincipal> documentPrincipal = doc->NodePrincipal(); - rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */, - false /* allowIfInheritsPrinciple */); - if (NS_WARN_IF(NS_FAILED(rv))) { - return NS_ERROR_DOM_SECURITY_ERR; - } - //////////////////////////////////////////// - - RefPtr<ServiceWorkerRegistrationInfo> registration = - GetRegistration(documentPrincipal, scope); - if (NS_WARN_IF(!registration)) { - return NS_ERROR_FAILURE; - } - - RefPtr<ServiceWorkerInfo> info; - if (aWhichWorker == WhichServiceWorker::INSTALLING_WORKER) { - info = registration->GetInstalling(); - } else if (aWhichWorker == WhichServiceWorker::WAITING_WORKER) { - info = registration->GetWaiting(); - } else if (aWhichWorker == WhichServiceWorker::ACTIVE_WORKER) { - info = registration->GetActive(); - } else { - MOZ_CRASH("Invalid worker type"); - } - - if (NS_WARN_IF(!info)) { - return NS_ERROR_DOM_NOT_FOUND_ERR; - } - - RefPtr<ServiceWorker> serviceWorker = - aWindow->GetOrCreateServiceWorker(info->Descriptor()); - - serviceWorker->SetState(info->State()); - serviceWorker.forget(aServiceWorker); - return NS_OK; -} - namespace { class ContinueDispatchFetchEventRunnable : public Runnable { RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate; nsCOMPtr<nsIInterceptedChannel> mChannel; nsCOMPtr<nsILoadGroup> mLoadGroup; bool mIsReload; @@ -2621,82 +2561,25 @@ ServiceWorkerManager::GetClientRegistrat return NS_ERROR_NOT_AVAILABLE; } RefPtr<ServiceWorkerRegistrationInfo> ref = data->mRegistrationInfo; ref.forget(aRegistrationInfo); return NS_OK; } -NS_IMETHODIMP -ServiceWorkerManager::GetInstalling(nsPIDOMWindowInner* aWindow, - const nsAString& aScope, - nsISupports** aServiceWorker) -{ - return GetServiceWorkerForScope(aWindow, aScope, - WhichServiceWorker::INSTALLING_WORKER, - aServiceWorker); -} - -NS_IMETHODIMP -ServiceWorkerManager::GetWaiting(nsPIDOMWindowInner* aWindow, - const nsAString& aScope, - nsISupports** aServiceWorker) -{ - return GetServiceWorkerForScope(aWindow, aScope, - WhichServiceWorker::WAITING_WORKER, - aServiceWorker); -} - -NS_IMETHODIMP -ServiceWorkerManager::GetActive(nsPIDOMWindowInner* aWindow, - const nsAString& aScope, - nsISupports** aServiceWorker) -{ - return GetServiceWorkerForScope(aWindow, aScope, - WhichServiceWorker::ACTIVE_WORKER, - aServiceWorker); -} - void -ServiceWorkerManager::TransitionServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration, - WhichServiceWorker aWhichOne) +ServiceWorkerManager::UpdateRegistrationListeners(ServiceWorkerRegistrationInfo* aReg) { MOZ_ASSERT(NS_IsMainThread()); nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners); while (it.HasMore()) { RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext(); - nsAutoString regScope; - target->GetScope(regScope); - MOZ_ASSERT(!regScope.IsEmpty()); - - NS_ConvertUTF16toUTF8 utf8Scope(regScope); - - if (utf8Scope.Equals(aRegistration->Scope())) { - target->TransitionWorker(aWhichOne); - } - } -} - -void -ServiceWorkerManager::InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration, - WhichServiceWorker aWhichOnes) -{ - MOZ_ASSERT(NS_IsMainThread()); - nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners); - while (it.HasMore()) { - RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext(); - nsAutoString regScope; - target->GetScope(regScope); - MOZ_ASSERT(!regScope.IsEmpty()); - - NS_ConvertUTF16toUTF8 utf8Scope(regScope); - - if (utf8Scope.Equals(aRegistration->Scope())) { - target->InvalidateWorkers(aWhichOnes); + if (target->MatchesDescriptor(aReg->Descriptor())) { + target->UpdateState(aReg->Descriptor()); } } } void ServiceWorkerManager::NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration) { MOZ_ASSERT(NS_IsMainThread());
--- a/dom/serviceworkers/ServiceWorkerManager.h +++ b/dom/serviceworkers/ServiceWorkerManager.h @@ -18,17 +18,16 @@ #include "mozilla/MozPromise.h" #include "mozilla/Preferences.h" #include "mozilla/TypedEnumBits.h" #include "mozilla/UniquePtr.h" #include "mozilla/WeakPtr.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/ClientHandle.h" #include "mozilla/dom/Promise.h" -#include "mozilla/dom/ServiceWorkerCommon.h" #include "mozilla/dom/ServiceWorkerRegistrar.h" #include "mozilla/dom/ServiceWorkerRegistrarTypes.h" #include "mozilla/dom/ServiceWorkerRegistrationInfo.h" #include "mozilla/ipc/BackgroundUtils.h" #include "nsClassHashtable.h" #include "nsDataHashtable.h" #include "nsRefPtrHashtable.h" #include "nsTArrayForwardDeclare.h" @@ -347,35 +346,25 @@ private: nsresult Update(ServiceWorkerRegistrationInfo* aRegistration); nsresult GetClientRegistration(const ClientInfo& aClientInfo, ServiceWorkerRegistrationInfo** aRegistrationInfo); - nsresult - GetServiceWorkerForScope(nsPIDOMWindowInner* aWindow, - const nsAString& aScope, - WhichServiceWorker aWhichWorker, - nsISupports** aServiceWorker); - ServiceWorkerInfo* GetActiveWorkerInfoForScope(const OriginAttributes& aOriginAttributes, const nsACString& aScope); ServiceWorkerInfo* GetActiveWorkerInfoForDocument(nsIDocument* aDocument); void - TransitionServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration, - WhichServiceWorker aWhichOne); - void - InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration, - WhichServiceWorker aWhichOnes); + UpdateRegistrationListeners(ServiceWorkerRegistrationInfo* aReg); void NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration); void StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration); already_AddRefed<ServiceWorkerRegistrationInfo>
--- a/dom/serviceworkers/ServiceWorkerPrivate.cpp +++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp @@ -1828,16 +1828,23 @@ ServiceWorkerPrivate::SpawnWorkerIfNeede // spin up a new worker. MOZ_ASSERT(mSupportsArray.IsEmpty()); if (NS_WARN_IF(!mInfo)) { NS_WARNING("Trying to wake up a dead service worker."); return NS_ERROR_FAILURE; } + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + NS_ENSURE_TRUE(swm, NS_ERROR_FAILURE); + + RefPtr<ServiceWorkerRegistrationInfo> reg = + swm->GetRegistration(mInfo->Principal(), mInfo->Scope()); + NS_ENSURE_TRUE(reg, NS_ERROR_FAILURE); + // TODO(catalinb): Bug 1192138 - Add telemetry for service worker wake-ups. // Ensure that the IndexedDatabaseManager is initialized Unused << NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate()); WorkerLoadInfo info; nsresult rv = NS_NewURI(getter_AddRefs(info.mBaseURI), mInfo->ScriptSpec(), nullptr, nullptr); @@ -1845,21 +1852,18 @@ ServiceWorkerPrivate::SpawnWorkerIfNeede if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } info.mResolvedScriptURI = info.mBaseURI; MOZ_ASSERT(!mInfo->CacheName().IsEmpty()); info.mServiceWorkerCacheName = mInfo->CacheName(); - PrincipalInfo principalInfo; - rv = PrincipalToPrincipalInfo(mInfo->Principal(), &principalInfo); - NS_ENSURE_SUCCESS(rv, rv); - info.mServiceWorkerDescriptor.emplace(mInfo->Descriptor()); + info.mServiceWorkerRegistrationDescriptor.emplace(reg->Descriptor()); info.mLoadGroup = aLoadGroup; info.mLoadFailedAsyncRunnable = aLoadFailedRunnable; // If we are loading a script for a ServiceWorker then we must not // try to intercept it. If the interception matches the current // ServiceWorker's scope then we could deadlock the load. info.mLoadFlags = mInfo->GetImportsLoadFlags() |
--- a/dom/serviceworkers/ServiceWorkerRegistration.cpp +++ b/dom/serviceworkers/ServiceWorkerRegistration.cpp @@ -1,1380 +1,242 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ServiceWorkerRegistration.h" -#include "ipc/ErrorIPCUtils.h" -#include "mozilla/dom/DOMPrefs.h" -#include "mozilla/dom/Notification.h" #include "mozilla/dom/Promise.h" -#include "mozilla/dom/PromiseWindowProxy.h" -#include "mozilla/dom/PromiseWorkerProxy.h" -#include "mozilla/dom/PushManagerBinding.h" #include "mozilla/dom/PushManager.h" +#include "mozilla/dom/ServiceWorker.h" #include "mozilla/dom/ServiceWorkerRegistrationBinding.h" #include "mozilla/dom/WorkerPrivate.h" -#include "mozilla/dom/WorkerCommon.h" -#include "mozilla/dom/WorkerScope.h" -#include "mozilla/Services.h" -#include "mozilla/Unused.h" #include "nsCycleCollectionParticipant.h" -#include "nsNetUtil.h" -#include "nsServiceManagerUtils.h" -#include "ServiceWorker.h" -#include "ServiceWorkerManager.h" - -#include "nsIDocument.h" -#include "nsIServiceWorkerManager.h" #include "nsISupportsPrimitives.h" #include "nsPIDOMWindow.h" -#include "nsContentUtils.h" +#include "ServiceWorkerRegistrationImpl.h" namespace mozilla { namespace dom { -//////////////////////////////////////////////////// -// Main Thread implementation - -class ServiceWorkerRegistrationMainThread final : public ServiceWorkerRegistration, - public ServiceWorkerRegistrationListener -{ - friend nsPIDOMWindowInner; -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistrationMainThread, - ServiceWorkerRegistration) - - ServiceWorkerRegistrationMainThread(nsPIDOMWindowInner* aWindow, - const nsAString& aScope); - - already_AddRefed<Promise> - Update(ErrorResult& aRv) override; - - already_AddRefed<Promise> - Unregister(ErrorResult& aRv) override; - - // Partial interface from Notification API. - already_AddRefed<Promise> - ShowNotification(JSContext* aCx, - const nsAString& aTitle, - const NotificationOptions& aOptions, - ErrorResult& aRv) override; - - already_AddRefed<Promise> - GetNotifications(const GetNotificationOptions& aOptions, - ErrorResult& aRv) override; - - already_AddRefed<ServiceWorker> - GetInstalling() override; - - already_AddRefed<ServiceWorker> - GetWaiting() override; - - already_AddRefed<ServiceWorker> - GetActive() override; - - already_AddRefed<PushManager> - GetPushManager(JSContext* aCx, ErrorResult& aRv) override; - - // DOMEventTargethelper - void DisconnectFromOwner() override - { - StopListeningForEvents(); - ServiceWorkerRegistration::DisconnectFromOwner(); - } - - // ServiceWorkerRegistrationListener - void - UpdateFound() override; - - void - InvalidateWorkers(WhichServiceWorker aWhichOnes) override; - - void - TransitionWorker(WhichServiceWorker aWhichOne) override; - - void - RegistrationRemoved() override; - - void - GetScope(nsAString& aScope) const override - { - aScope = mScope; - } - - ServiceWorkerUpdateViaCache - GetUpdateViaCache(ErrorResult& aRv) const override - { - RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); - MOZ_ASSERT(swm); - - nsCOMPtr<nsPIDOMWindowInner> window = GetOwner(); - MOZ_ASSERT(window); - - nsCOMPtr<nsIDocument> doc = window->GetExtantDoc(); - MOZ_ASSERT(doc); - - nsCOMPtr<nsIServiceWorkerRegistrationInfo> registration; - nsresult rv = swm->GetRegistrationByPrincipal(doc->NodePrincipal(), mScope, - getter_AddRefs(registration)); - - /* - * xxx: We should probably store the `updateViaCache` value on the - * ServiceWorkerRegistration object and update it when necessary. - * We don't have a good way to reach all ServiceWorkerRegistration objects - * from the ServiceWorkerRegistratinInfo right now, though. - * This is a short term fix to avoid crashing. - */ - if (NS_FAILED(rv) || !registration) { - aRv = NS_ERROR_DOM_INVALID_STATE_ERR; - return ServiceWorkerUpdateViaCache::None; - } - - uint16_t updateViaCache; - rv = registration->GetUpdateViaCache(&updateViaCache); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - - // Silence possible compiler warnings. - Unused << rv; - - return static_cast<ServiceWorkerUpdateViaCache>(updateViaCache); - } - -private: - ~ServiceWorkerRegistrationMainThread(); - - already_AddRefed<ServiceWorker> - GetWorkerReference(WhichServiceWorker aWhichOne); - - void - StartListeningForEvents(); - - void - StopListeningForEvents(); - - bool mListeningForEvents; - - // The following properties are cached here to ensure JS equality is satisfied - // instead of acquiring a new worker instance from the ServiceWorkerManager - // for every access. A null value is considered a cache miss. - // These three may change to a new worker at any time. - RefPtr<ServiceWorker> mInstallingWorker; - RefPtr<ServiceWorker> mWaitingWorker; - RefPtr<ServiceWorker> mActiveWorker; - - RefPtr<PushManager> mPushManager; -}; - -NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistrationMainThread, ServiceWorkerRegistration) -NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistrationMainThread, ServiceWorkerRegistration) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerRegistrationMainThread) -NS_INTERFACE_MAP_END_INHERITING(ServiceWorkerRegistration) - -NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistrationMainThread, - ServiceWorkerRegistration, - mPushManager, - mInstallingWorker, mWaitingWorker, mActiveWorker); - -ServiceWorkerRegistrationMainThread::ServiceWorkerRegistrationMainThread(nsPIDOMWindowInner* aWindow, - const nsAString& aScope) - : ServiceWorkerRegistration(aWindow, aScope) - , mListeningForEvents(false) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aWindow); - StartListeningForEvents(); -} - -ServiceWorkerRegistrationMainThread::~ServiceWorkerRegistrationMainThread() -{ - StopListeningForEvents(); - MOZ_ASSERT(!mListeningForEvents); -} - - -already_AddRefed<ServiceWorker> -ServiceWorkerRegistrationMainThread::GetWorkerReference(WhichServiceWorker aWhichOne) -{ - nsCOMPtr<nsPIDOMWindowInner> window = GetOwner(); - if (!window) { - return nullptr; - } - - nsresult rv; - nsCOMPtr<nsIServiceWorkerManager> swm = - do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - - nsCOMPtr<nsISupports> serviceWorker; - switch(aWhichOne) { - case WhichServiceWorker::INSTALLING_WORKER: - rv = swm->GetInstalling(window, mScope, getter_AddRefs(serviceWorker)); - break; - case WhichServiceWorker::WAITING_WORKER: - rv = swm->GetWaiting(window, mScope, getter_AddRefs(serviceWorker)); - break; - case WhichServiceWorker::ACTIVE_WORKER: - rv = swm->GetActive(window, mScope, getter_AddRefs(serviceWorker)); - break; - default: - MOZ_CRASH("Invalid enum value"); - } - - NS_WARNING_ASSERTION( - NS_SUCCEEDED(rv) || rv == NS_ERROR_DOM_NOT_FOUND_ERR, - "Unexpected error getting service worker instance from " - "ServiceWorkerManager"); - if (NS_FAILED(rv)) { - return nullptr; - } - - RefPtr<ServiceWorker> ref = - static_cast<ServiceWorker*>(serviceWorker.get()); - return ref.forget(); -} - -// XXXnsm, maybe this can be optimized to only add when a event handler is -// registered. -void -ServiceWorkerRegistrationMainThread::StartListeningForEvents() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mListeningForEvents); - RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); - if (swm) { - swm->AddRegistrationEventListener(mScope, this); - mListeningForEvents = true; - } -} - -void -ServiceWorkerRegistrationMainThread::StopListeningForEvents() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (!mListeningForEvents) { - return; - } - - RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); - if (swm) { - swm->RemoveRegistrationEventListener(mScope, this); - } - mListeningForEvents = false; -} - -already_AddRefed<ServiceWorker> -ServiceWorkerRegistrationMainThread::GetInstalling() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (!mInstallingWorker) { - mInstallingWorker = GetWorkerReference(WhichServiceWorker::INSTALLING_WORKER); - } - - RefPtr<ServiceWorker> ret = mInstallingWorker; - return ret.forget(); -} - -already_AddRefed<ServiceWorker> -ServiceWorkerRegistrationMainThread::GetWaiting() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (!mWaitingWorker) { - mWaitingWorker = GetWorkerReference(WhichServiceWorker::WAITING_WORKER); - } - - RefPtr<ServiceWorker> ret = mWaitingWorker; - return ret.forget(); -} - -already_AddRefed<ServiceWorker> -ServiceWorkerRegistrationMainThread::GetActive() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (!mActiveWorker) { - mActiveWorker = GetWorkerReference(WhichServiceWorker::ACTIVE_WORKER); - } - - RefPtr<ServiceWorker> ret = mActiveWorker; - return ret.forget(); -} - -void -ServiceWorkerRegistrationMainThread::UpdateFound() -{ - DispatchTrustedEvent(NS_LITERAL_STRING("updatefound")); -} - -void -ServiceWorkerRegistrationMainThread::TransitionWorker(WhichServiceWorker aWhichOne) -{ - MOZ_ASSERT(NS_IsMainThread()); - - // We assert the worker's previous state because the 'statechange' - // event is dispatched in a queued runnable. - if (aWhichOne == WhichServiceWorker::INSTALLING_WORKER) { - MOZ_ASSERT_IF(mInstallingWorker, mInstallingWorker->State() == ServiceWorkerState::Installing); - mWaitingWorker = mInstallingWorker.forget(); - } else if (aWhichOne == WhichServiceWorker::WAITING_WORKER) { - MOZ_ASSERT_IF(mWaitingWorker, mWaitingWorker->State() == ServiceWorkerState::Installed); - mActiveWorker = mWaitingWorker.forget(); - } else { - MOZ_ASSERT_UNREACHABLE("Invalid transition!"); - } -} - -void -ServiceWorkerRegistrationMainThread::InvalidateWorkers(WhichServiceWorker aWhichOnes) -{ - MOZ_ASSERT(NS_IsMainThread()); - if (aWhichOnes & WhichServiceWorker::INSTALLING_WORKER) { - mInstallingWorker = nullptr; - } - - if (aWhichOnes & WhichServiceWorker::WAITING_WORKER) { - mWaitingWorker = nullptr; - } - - if (aWhichOnes & WhichServiceWorker::ACTIVE_WORKER) { - mActiveWorker = nullptr; - } - -} - -void -ServiceWorkerRegistrationMainThread::RegistrationRemoved() -{ - // If the registration is being removed completely, remove it from the - // window registration hash table so that a new registration would get a new - // wrapper JS object. - if (nsCOMPtr<nsPIDOMWindowInner> window = GetOwner()) { - window->InvalidateServiceWorkerRegistration(mScope); - } -} - -namespace { - -void -UpdateInternal(nsIPrincipal* aPrincipal, - const nsAString& aScope, - ServiceWorkerUpdateFinishCallback* aCallback) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aPrincipal); - MOZ_ASSERT(aCallback); - - RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); - if (!swm) { - // browser shutdown - return; - } - - swm->Update(aPrincipal, NS_ConvertUTF16toUTF8(aScope), aCallback); -} - -class MainThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback -{ - PromiseWindowProxy mPromise; - - ~MainThreadUpdateCallback() - { } - -public: - explicit MainThreadUpdateCallback(nsPIDOMWindowInner* aWindow, - Promise* aPromise) - : mPromise(aWindow, aPromise) - { - MOZ_ASSERT(NS_IsMainThread()); - } - - void - UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override - { - if (RefPtr<Promise> promise = mPromise.Get()) { - promise->MaybeResolveWithUndefined(); - } - } - - void - UpdateFailed(ErrorResult& aStatus) override - { - if (RefPtr<Promise> promise = mPromise.Get()) { - promise->MaybeReject(aStatus); - } - } -}; - -class UpdateResultRunnable final : public WorkerRunnable -{ - RefPtr<PromiseWorkerProxy> mPromiseProxy; - IPC::Message mSerializedErrorResult; - - ~UpdateResultRunnable() - {} - -public: - UpdateResultRunnable(PromiseWorkerProxy* aPromiseProxy, ErrorResult& aStatus) - : WorkerRunnable(aPromiseProxy->GetWorkerPrivate()) - , mPromiseProxy(aPromiseProxy) - { - // ErrorResult is not thread safe. Serialize it for transfer across - // threads. - IPC::WriteParam(&mSerializedErrorResult, aStatus); - aStatus.SuppressException(); - } - - bool - WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override - { - // Deserialize the ErrorResult now that we are back in the worker - // thread. - ErrorResult status; - PickleIterator iter = PickleIterator(mSerializedErrorResult); - Unused << IPC::ReadParam(&mSerializedErrorResult, &iter, &status); - - Promise* promise = mPromiseProxy->WorkerPromise(); - if (status.Failed()) { - promise->MaybeReject(status); - } else { - promise->MaybeResolveWithUndefined(); - } - status.SuppressException(); - mPromiseProxy->CleanUp(); - return true; - } -}; - -class WorkerThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback -{ - RefPtr<PromiseWorkerProxy> mPromiseProxy; - - ~WorkerThreadUpdateCallback() - { - } - -public: - explicit WorkerThreadUpdateCallback(PromiseWorkerProxy* aPromiseProxy) - : mPromiseProxy(aPromiseProxy) - { - MOZ_ASSERT(NS_IsMainThread()); - } - - void - UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override - { - ErrorResult rv(NS_OK); - Finish(rv); - } - - void - UpdateFailed(ErrorResult& aStatus) override - { - Finish(aStatus); - } - - void - Finish(ErrorResult& aStatus) - { - if (!mPromiseProxy) { - return; - } - - RefPtr<PromiseWorkerProxy> proxy = mPromiseProxy.forget(); - - MutexAutoLock lock(proxy->Lock()); - if (proxy->CleanedUp()) { - return; - } - - RefPtr<UpdateResultRunnable> r = - new UpdateResultRunnable(proxy, aStatus); - r->Dispatch(); - } -}; - -class SWRUpdateRunnable final : public Runnable -{ -public: - SWRUpdateRunnable(PromiseWorkerProxy* aPromiseProxy, const nsAString& aScope) - : Runnable("dom::SWRUpdateRunnable") - , mPromiseProxy(aPromiseProxy) - , mScope(aScope) - {} - - NS_IMETHOD - Run() override - { - MOZ_ASSERT(NS_IsMainThread()); - ErrorResult result; - - nsCOMPtr<nsIPrincipal> principal; - // UpdateInternal may try to reject the promise synchronously leading - // to a deadlock. - { - MutexAutoLock lock(mPromiseProxy->Lock()); - if (mPromiseProxy->CleanedUp()) { - return NS_OK; - } - - principal = mPromiseProxy->GetWorkerPrivate()->GetPrincipal(); - } - MOZ_ASSERT(principal); - - RefPtr<WorkerThreadUpdateCallback> cb = - new WorkerThreadUpdateCallback(mPromiseProxy); - UpdateInternal(principal, mScope, cb); - return NS_OK; - } - -private: - ~SWRUpdateRunnable() - {} - - RefPtr<PromiseWorkerProxy> mPromiseProxy; - const nsString mScope; -}; - -class UnregisterCallback final : public nsIServiceWorkerUnregisterCallback -{ - PromiseWindowProxy mPromise; - -public: - NS_DECL_ISUPPORTS - - explicit UnregisterCallback(nsPIDOMWindowInner* aWindow, - Promise* aPromise) - : mPromise(aWindow, aPromise) - { - MOZ_ASSERT(aPromise); - } - - NS_IMETHOD - UnregisterSucceeded(bool aState) override - { - MOZ_ASSERT(NS_IsMainThread()); - if (RefPtr<Promise> promise = mPromise.Get()) { - promise->MaybeResolve(aState); - } - return NS_OK; - } - - NS_IMETHOD - UnregisterFailed() override - { - MOZ_ASSERT(NS_IsMainThread()); - - if (RefPtr<Promise> promise = mPromise.Get()) { - promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); - } - return NS_OK; - } - -private: - ~UnregisterCallback() - { } -}; - -NS_IMPL_ISUPPORTS(UnregisterCallback, nsIServiceWorkerUnregisterCallback) - -class FulfillUnregisterPromiseRunnable final : public WorkerRunnable -{ - RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy; - Maybe<bool> mState; -public: - FulfillUnregisterPromiseRunnable(PromiseWorkerProxy* aProxy, - const Maybe<bool>& aState) - : WorkerRunnable(aProxy->GetWorkerPrivate()) - , mPromiseWorkerProxy(aProxy) - , mState(aState) - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mPromiseWorkerProxy); - } - - bool - WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override - { - RefPtr<Promise> promise = mPromiseWorkerProxy->WorkerPromise(); - if (mState.isSome()) { - promise->MaybeResolve(mState.value()); - } else { - promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); - } - - mPromiseWorkerProxy->CleanUp(); - return true; - } -}; - -class WorkerUnregisterCallback final : public nsIServiceWorkerUnregisterCallback -{ - RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy; -public: - NS_DECL_ISUPPORTS - - explicit WorkerUnregisterCallback(PromiseWorkerProxy* aProxy) - : mPromiseWorkerProxy(aProxy) - { - MOZ_ASSERT(aProxy); - } - - NS_IMETHOD - UnregisterSucceeded(bool aState) override - { - MOZ_ASSERT(NS_IsMainThread()); - Finish(Some(aState)); - return NS_OK; - } - - NS_IMETHOD - UnregisterFailed() override - { - MOZ_ASSERT(NS_IsMainThread()); - Finish(Nothing()); - return NS_OK; - } - -private: - ~WorkerUnregisterCallback() - {} - - void - Finish(const Maybe<bool>& aState) - { - MOZ_ASSERT(NS_IsMainThread()); - if (!mPromiseWorkerProxy) { - return; - } - - RefPtr<PromiseWorkerProxy> proxy = mPromiseWorkerProxy.forget(); - MutexAutoLock lock(proxy->Lock()); - if (proxy->CleanedUp()) { - return; - } - - RefPtr<WorkerRunnable> r = - new FulfillUnregisterPromiseRunnable(proxy, aState); - - r->Dispatch(); - } -}; - -NS_IMPL_ISUPPORTS(WorkerUnregisterCallback, nsIServiceWorkerUnregisterCallback); - -/* - * If the worker goes away, we still continue to unregister, but we don't try to - * resolve the worker Promise (which doesn't exist by that point). - */ -class StartUnregisterRunnable final : public Runnable -{ - RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy; - const nsString mScope; - -public: - StartUnregisterRunnable(PromiseWorkerProxy* aProxy, const nsAString& aScope) - : Runnable("dom::StartUnregisterRunnable") - , mPromiseWorkerProxy(aProxy) - , mScope(aScope) - { - MOZ_ASSERT(aProxy); - } - - NS_IMETHOD - Run() override - { - MOZ_ASSERT(NS_IsMainThread()); - - // XXXnsm: There is a rare chance of this failing if the worker gets - // destroyed. In that case, unregister() called from a SW is no longer - // guaranteed to run. We should fix this by having a main thread proxy - // maintain a strongref to ServiceWorkerRegistrationInfo and use its - // principal. Can that be trusted? - nsCOMPtr<nsIPrincipal> principal; - { - MutexAutoLock lock(mPromiseWorkerProxy->Lock()); - if (mPromiseWorkerProxy->CleanedUp()) { - return NS_OK; - } - - WorkerPrivate* worker = mPromiseWorkerProxy->GetWorkerPrivate(); - MOZ_ASSERT(worker); - principal = worker->GetPrincipal(); - } - MOZ_ASSERT(principal); - - RefPtr<WorkerUnregisterCallback> cb = - new WorkerUnregisterCallback(mPromiseWorkerProxy); - nsCOMPtr<nsIServiceWorkerManager> swm = - mozilla::services::GetServiceWorkerManager(); - nsresult rv = swm->Unregister(principal, cb, mScope); - if (NS_WARN_IF(NS_FAILED(rv))) { - cb->UnregisterFailed(); - } - - return NS_OK; - } -}; -} // namespace - -already_AddRefed<Promise> -ServiceWorkerRegistrationMainThread::Update(ErrorResult& aRv) -{ - MOZ_ASSERT(NS_IsMainThread()); - nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(GetOwner()); - if (!go) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - RefPtr<Promise> promise = Promise::Create(go, aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - nsCOMPtr<nsIDocument> doc = GetOwner()->GetExtantDoc(); - MOZ_ASSERT(doc); - - RefPtr<MainThreadUpdateCallback> cb = - new MainThreadUpdateCallback(GetOwner(), promise); - UpdateInternal(doc->NodePrincipal(), mScope, cb); - - return promise.forget(); -} - -already_AddRefed<Promise> -ServiceWorkerRegistrationMainThread::Unregister(ErrorResult& aRv) -{ - MOZ_ASSERT(NS_IsMainThread()); - nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(GetOwner()); - if (!go) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - // Although the spec says that the same-origin checks should also be done - // asynchronously, we do them in sync because the Promise created by the - // WebIDL infrastructure due to a returned error will be resolved - // asynchronously. We aren't making any internal state changes in these - // checks, so ordering of multiple calls is not affected. - nsCOMPtr<nsIDocument> document = GetOwner()->GetExtantDoc(); - if (!document) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - nsCOMPtr<nsIURI> scopeURI; - nsCOMPtr<nsIURI> baseURI = document->GetBaseURI(); - // "If the origin of scope is not client's origin..." - nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), mScope, nullptr, baseURI); - if (NS_WARN_IF(NS_FAILED(rv))) { - aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); - return nullptr; - } - - nsCOMPtr<nsIPrincipal> documentPrincipal = document->NodePrincipal(); - rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */, - false /* allowIfInheritsPrinciple */); - if (NS_FAILED(rv)) { - aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); - return nullptr; - } - - nsAutoCString uriSpec; - aRv = scopeURI->GetSpecIgnoringRef(uriSpec); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - nsCOMPtr<nsIServiceWorkerManager> swm = - mozilla::services::GetServiceWorkerManager(); - - RefPtr<Promise> promise = Promise::Create(go, aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - RefPtr<UnregisterCallback> cb = new UnregisterCallback(GetOwner(), promise); - - NS_ConvertUTF8toUTF16 scope(uriSpec); - aRv = swm->Unregister(documentPrincipal, cb, scope); - if (aRv.Failed()) { - return nullptr; - } - - return promise.forget(); -} - -// Notification API extension. -already_AddRefed<Promise> -ServiceWorkerRegistrationMainThread::ShowNotification(JSContext* aCx, - const nsAString& aTitle, - const NotificationOptions& aOptions, - ErrorResult& aRv) -{ - MOZ_ASSERT(NS_IsMainThread()); - nsCOMPtr<nsPIDOMWindowInner> window = GetOwner(); - if (NS_WARN_IF(!window)) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - nsCOMPtr<nsIDocument> doc = window->GetExtantDoc(); - if (NS_WARN_IF(!doc)) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - RefPtr<ServiceWorker> worker = GetActive(); - if (!worker) { - aRv.ThrowTypeError<MSG_NO_ACTIVE_WORKER>(mScope); - return nullptr; - } - - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window); - RefPtr<Promise> p = - Notification::ShowPersistentNotification(aCx, global, mScope, aTitle, - aOptions, aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - return p.forget(); -} - -already_AddRefed<Promise> -ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv) -{ - MOZ_ASSERT(NS_IsMainThread()); - nsCOMPtr<nsPIDOMWindowInner> window = GetOwner(); - if (NS_WARN_IF(!window)) { - aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return nullptr; - } - return Notification::Get(window, aOptions, mScope, aRv); -} - -already_AddRefed<PushManager> -ServiceWorkerRegistrationMainThread::GetPushManager(JSContext* aCx, - ErrorResult& aRv) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!mPushManager) { - nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(GetOwner()); - - if (!globalObject) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - GlobalObject global(aCx, globalObject->GetGlobalJSObject()); - mPushManager = PushManager::Constructor(global, mScope, aRv); - if (aRv.Failed()) { - return nullptr; - } - } - - RefPtr<PushManager> ret = mPushManager; - return ret.forget(); -} - -//////////////////////////////////////////////////// -// Worker Thread implementation - -class ServiceWorkerRegistrationWorkerThread final : public ServiceWorkerRegistration - , public WorkerHolder -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistrationWorkerThread, - ServiceWorkerRegistration) - - ServiceWorkerRegistrationWorkerThread(WorkerPrivate* aWorkerPrivate, - const nsAString& aScope); - - already_AddRefed<Promise> - Update(ErrorResult& aRv) override; - - already_AddRefed<Promise> - Unregister(ErrorResult& aRv) override; - - // Partial interface from Notification API. - already_AddRefed<Promise> - ShowNotification(JSContext* aCx, - const nsAString& aTitle, - const NotificationOptions& aOptions, - ErrorResult& aRv) override; - - already_AddRefed<Promise> - GetNotifications(const GetNotificationOptions& aOptions, - ErrorResult& aRv) override; - - already_AddRefed<ServiceWorker> - GetInstalling() override; - - already_AddRefed<ServiceWorker> - GetWaiting() override; - - already_AddRefed<ServiceWorker> - GetActive() override; - - void - GetScope(nsAString& aScope) const override - { - aScope = mScope; - } - - ServiceWorkerUpdateViaCache - GetUpdateViaCache(ErrorResult& aRv) const override - { - // FIXME(hopang): Will be implemented after Bug 1113522. - return ServiceWorkerUpdateViaCache::Imports; - } - - bool - Notify(WorkerStatus aStatus) override; - - already_AddRefed<PushManager> - GetPushManager(JSContext* aCx, ErrorResult& aRv) override; - -private: - ~ServiceWorkerRegistrationWorkerThread(); - - void - InitListener(); - - void - ReleaseListener(); - - WorkerPrivate* mWorkerPrivate; - RefPtr<WorkerListener> mListener; - - RefPtr<PushManager> mPushManager; -}; - -class WorkerListener final : public ServiceWorkerRegistrationListener -{ - // Accessed on the main thread. - WorkerPrivate* mWorkerPrivate; - nsString mScope; - bool mListeningForEvents; - - // Accessed on the worker thread. - ServiceWorkerRegistrationWorkerThread* mRegistration; - -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerListener, override) - - WorkerListener(WorkerPrivate* aWorkerPrivate, - ServiceWorkerRegistrationWorkerThread* aReg) - : mWorkerPrivate(aWorkerPrivate) - , mListeningForEvents(false) - , mRegistration(aReg) - { - MOZ_ASSERT(mWorkerPrivate); - mWorkerPrivate->AssertIsOnWorkerThread(); - MOZ_ASSERT(mRegistration); - // Copy scope so we can return it on the main thread. - mRegistration->GetScope(mScope); - } - - void - StartListeningForEvents() - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mListeningForEvents); - MOZ_ASSERT(mWorkerPrivate); - RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); - if (swm) { - // FIXME(nsm): Maybe the function shouldn't take an explicit scope. - swm->AddRegistrationEventListener(mScope, this); - mListeningForEvents = true; - } - } - - void - StopListeningForEvents() - { - MOZ_ASSERT(NS_IsMainThread()); - - MOZ_ASSERT(mListeningForEvents); - - RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); - - // We aren't going to need this anymore and we shouldn't hold on since the - // worker will go away soon. - mWorkerPrivate = nullptr; - - if (swm) { - // FIXME(nsm): Maybe the function shouldn't take an explicit scope. - swm->RemoveRegistrationEventListener(mScope, this); - mListeningForEvents = false; - } - } - - // ServiceWorkerRegistrationListener - void - UpdateFound() override; - - void - TransitionWorker(WhichServiceWorker aWhichOne) override - { - MOZ_ASSERT(NS_IsMainThread()); - NS_WARNING("FIXME: Not implemented!"); - } - - void - InvalidateWorkers(WhichServiceWorker aWhichOnes) override - { - MOZ_ASSERT(NS_IsMainThread()); - // FIXME(nsm); - } - - void - RegistrationRemoved() override - { - MOZ_ASSERT(NS_IsMainThread()); - } - - void - GetScope(nsAString& aScope) const override - { - aScope = mScope; - } - - ServiceWorkerRegistrationWorkerThread* - GetRegistration() const - { - if (mWorkerPrivate) { - mWorkerPrivate->AssertIsOnWorkerThread(); - } - return mRegistration; - } - - void - ClearRegistration() - { - if (mWorkerPrivate) { - mWorkerPrivate->AssertIsOnWorkerThread(); - } - mRegistration = nullptr; - } - -private: - ~WorkerListener() - { - MOZ_ASSERT(!mListeningForEvents); - } -}; - -NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistrationWorkerThread, ServiceWorkerRegistration) -NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistrationWorkerThread, ServiceWorkerRegistration) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerRegistrationWorkerThread) -NS_INTERFACE_MAP_END_INHERITING(ServiceWorkerRegistration) - -// Expanded macros since we need special behaviour to release the proxy. -NS_IMPL_CYCLE_COLLECTION_CLASS(ServiceWorkerRegistrationWorkerThread) - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServiceWorkerRegistrationWorkerThread, - ServiceWorkerRegistration) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPushManager) - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ServiceWorkerRegistrationWorkerThread, - ServiceWorkerRegistration) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mPushManager) - tmp->ReleaseListener(); -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -ServiceWorkerRegistrationWorkerThread::ServiceWorkerRegistrationWorkerThread(WorkerPrivate* aWorkerPrivate, - const nsAString& aScope) - : ServiceWorkerRegistration(nullptr, aScope) - , WorkerHolder("ServiceWorkerRegistrationWorkerThread") - , mWorkerPrivate(aWorkerPrivate) -{ - InitListener(); -} - -ServiceWorkerRegistrationWorkerThread::~ServiceWorkerRegistrationWorkerThread() -{ - ReleaseListener(); - MOZ_ASSERT(!mListener); -} - -already_AddRefed<ServiceWorker> -ServiceWorkerRegistrationWorkerThread::GetInstalling() -{ - // FIXME(nsm): Will be implemented after Bug 1113522. - return nullptr; -} - -already_AddRefed<ServiceWorker> -ServiceWorkerRegistrationWorkerThread::GetWaiting() -{ - // FIXME(nsm): Will be implemented after Bug 1113522. - return nullptr; -} - -already_AddRefed<ServiceWorker> -ServiceWorkerRegistrationWorkerThread::GetActive() -{ - // FIXME(nsm): Will be implemented after Bug 1113522. - return nullptr; -} - -already_AddRefed<Promise> -ServiceWorkerRegistrationWorkerThread::Update(ErrorResult& aRv) -{ - WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); - MOZ_ASSERT(worker); - worker->AssertIsOnWorkerThread(); - - RefPtr<Promise> promise = Promise::Create(worker->GlobalScope(), aRv); - if (aRv.Failed()) { - return nullptr; - } - - // Avoid infinite update loops by ignoring update() calls during top - // level script evaluation. See: - // https://github.com/slightlyoff/ServiceWorker/issues/800 - if (worker->LoadScriptAsPartOfLoadingServiceWorkerScript()) { - promise->MaybeResolveWithUndefined(); - return promise.forget(); - } - - RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise); - if (!proxy) { - aRv.Throw(NS_ERROR_DOM_ABORT_ERR); - return nullptr; - } - - RefPtr<SWRUpdateRunnable> r = new SWRUpdateRunnable(proxy, mScope); - MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget())); - - return promise.forget(); -} - -already_AddRefed<Promise> -ServiceWorkerRegistrationWorkerThread::Unregister(ErrorResult& aRv) -{ - WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); - MOZ_ASSERT(worker); - worker->AssertIsOnWorkerThread(); - - if (!worker->IsServiceWorker()) { - // For other workers, the registration probably originated from - // getRegistration(), so we may have to validate origin etc. Let's do this - // this later. - aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); - return nullptr; - } - - RefPtr<Promise> promise = Promise::Create(worker->GlobalScope(), aRv); - if (aRv.Failed()) { - return nullptr; - } - - RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise); - if (!proxy) { - aRv.Throw(NS_ERROR_DOM_ABORT_ERR); - return nullptr; - } - - RefPtr<StartUnregisterRunnable> r = new StartUnregisterRunnable(proxy, mScope); - MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget())); - - return promise.forget(); -} - -void -ServiceWorkerRegistrationWorkerThread::InitListener() -{ - MOZ_ASSERT(!mListener); - WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); - MOZ_ASSERT(worker); - worker->AssertIsOnWorkerThread(); - - mListener = new WorkerListener(worker, this); - if (!HoldWorker(worker, Closing)) { - mListener = nullptr; - NS_WARNING("Could not add feature"); - return; - } - - nsCOMPtr<nsIRunnable> r = - NewRunnableMethod("dom::WorkerListener::StartListeningForEvents", - mListener, - &WorkerListener::StartListeningForEvents); - MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget())); -} - -void -ServiceWorkerRegistrationWorkerThread::ReleaseListener() -{ - if (!mListener) { - return; - } - - // We can assert worker here, because: - // 1) We always HoldWorker, so if the worker has shutdown already, we'll - // have received Notify and removed it. If HoldWorker had failed, - // mListener will be null and we won't reach here. - // 2) Otherwise, worker is still around even if we are going away. - mWorkerPrivate->AssertIsOnWorkerThread(); - ReleaseWorker(); - - mListener->ClearRegistration(); - - nsCOMPtr<nsIRunnable> r = - NewRunnableMethod("dom::WorkerListener::StopListeningForEvents", - mListener, - &WorkerListener::StopListeningForEvents); - MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(r.forget())); - - mListener = nullptr; - mWorkerPrivate = nullptr; -} - -bool -ServiceWorkerRegistrationWorkerThread::Notify(WorkerStatus aStatus) -{ - ReleaseListener(); - return true; -} - -class FireUpdateFoundRunnable final : public WorkerRunnable -{ - RefPtr<WorkerListener> mListener; -public: - FireUpdateFoundRunnable(WorkerPrivate* aWorkerPrivate, - WorkerListener* aListener) - : WorkerRunnable(aWorkerPrivate) - , mListener(aListener) - { - // Need this assertion for now since runnables which modify busy count can - // only be dispatched from parent thread to worker thread and we don't deal - // with nested workers. SW threads can't be nested. - MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); - } - - bool - WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override - { - MOZ_ASSERT(aWorkerPrivate); - aWorkerPrivate->AssertIsOnWorkerThread(); - - ServiceWorkerRegistrationWorkerThread* reg = mListener->GetRegistration(); - if (reg) { - reg->DispatchTrustedEvent(NS_LITERAL_STRING("updatefound")); - } - return true; - } -}; - -void -WorkerListener::UpdateFound() -{ - MOZ_ASSERT(NS_IsMainThread()); - if (mWorkerPrivate) { - RefPtr<FireUpdateFoundRunnable> r = - new FireUpdateFoundRunnable(mWorkerPrivate, this); - Unused << NS_WARN_IF(!r->Dispatch()); - } -} - -// Notification API extension. -already_AddRefed<Promise> -ServiceWorkerRegistrationWorkerThread::ShowNotification(JSContext* aCx, - const nsAString& aTitle, - const NotificationOptions& aOptions, - ErrorResult& aRv) -{ - // Until Bug 1131324 exposes ServiceWorkerContainer on workers, - // ShowPersistentNotification() checks for valid active worker while it is - // also verifying scope so that we block the worker on the main thread only - // once. - RefPtr<Promise> p = - Notification::ShowPersistentNotification(aCx, mWorkerPrivate->GlobalScope(), - mScope, aTitle, aOptions, aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - return p.forget(); -} - -already_AddRefed<Promise> -ServiceWorkerRegistrationWorkerThread::GetNotifications(const GetNotificationOptions& aOptions, - ErrorResult& aRv) -{ - return Notification::WorkerGet(mWorkerPrivate, aOptions, mScope, aRv); -} - -already_AddRefed<PushManager> -ServiceWorkerRegistrationWorkerThread::GetPushManager(JSContext* aCx, ErrorResult& aRv) -{ - if (!mPushManager) { - mPushManager = new PushManager(mScope); - } - - RefPtr<PushManager> ret = mPushManager; - return ret.forget(); -} - -//////////////////////////////////////////////////// -// Base class implementation +NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistration, + DOMEventTargetHelper, + mInstallingWorker, + mWaitingWorker, + mActiveWorker, + mPushManager); NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerRegistration) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) -ServiceWorkerRegistration::ServiceWorkerRegistration(nsPIDOMWindowInner* aWindow, - const nsAString& aScope) - : DOMEventTargetHelper(aWindow) - , mScope(aScope) -{} +ServiceWorkerRegistration::ServiceWorkerRegistration(nsIGlobalObject* aGlobal, + const ServiceWorkerRegistrationDescriptor& aDescriptor, + ServiceWorkerRegistration::Inner* aInner) + : DOMEventTargetHelper(aGlobal) + , mDescriptor(aDescriptor) + , mInner(aInner) +{ + MOZ_DIAGNOSTIC_ASSERT(mInner); + UpdateState(mDescriptor); + mInner->SetServiceWorkerRegistration(this); +} + +ServiceWorkerRegistration::~ServiceWorkerRegistration() +{ + if (mInner) { + mInner->ClearServiceWorkerRegistration(this); + } +} JSObject* ServiceWorkerRegistration::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return ServiceWorkerRegistrationBinding::Wrap(aCx, this, aGivenProto); } /* static */ already_AddRefed<ServiceWorkerRegistration> ServiceWorkerRegistration::CreateForMainThread(nsPIDOMWindowInner* aWindow, - const nsAString& aScope) + const ServiceWorkerRegistrationDescriptor& aDescriptor) { MOZ_ASSERT(aWindow); MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr<nsIGlobalObject> global(do_QueryInterface(aWindow)); + + RefPtr<Inner> inner = new ServiceWorkerRegistrationMainThread(aDescriptor); + RefPtr<ServiceWorkerRegistration> registration = - new ServiceWorkerRegistrationMainThread(aWindow, aScope); + new ServiceWorkerRegistration(global, aDescriptor, inner); return registration.forget(); } /* static */ already_AddRefed<ServiceWorkerRegistration> ServiceWorkerRegistration::CreateForWorker(WorkerPrivate* aWorkerPrivate, - const nsAString& aScope) + const ServiceWorkerRegistrationDescriptor& aDescriptor) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); + RefPtr<Inner> inner = + new ServiceWorkerRegistrationWorkerThread(aWorkerPrivate, aDescriptor); + RefPtr<ServiceWorkerRegistration> registration = - new ServiceWorkerRegistrationWorkerThread(aWorkerPrivate, aScope); + new ServiceWorkerRegistration(aWorkerPrivate->GlobalScope(), aDescriptor, + inner); return registration.forget(); } +void +ServiceWorkerRegistration::DisconnectFromOwner() +{ + mInner->ClearServiceWorkerRegistration(this); + mInner = nullptr; + DOMEventTargetHelper::DisconnectFromOwner(); +} + +already_AddRefed<ServiceWorker> +ServiceWorkerRegistration::GetInstalling() const +{ + RefPtr<ServiceWorker> ref = mInstallingWorker; + return ref.forget(); +} + +already_AddRefed<ServiceWorker> +ServiceWorkerRegistration::GetWaiting() const +{ + RefPtr<ServiceWorker> ref = mWaitingWorker; + return ref.forget(); +} + +already_AddRefed<ServiceWorker> +ServiceWorkerRegistration::GetActive() const +{ + RefPtr<ServiceWorker> ref = mActiveWorker; + return ref.forget(); +} + +void +ServiceWorkerRegistration::UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor) +{ + MOZ_DIAGNOSTIC_ASSERT(MatchesDescriptor(aDescriptor)); + + mDescriptor = aDescriptor; + + nsCOMPtr<nsIGlobalObject> global = GetParentObject(); + if (!global) { + mInstallingWorker = nullptr; + mWaitingWorker = nullptr; + mActiveWorker = nullptr; + return; + } + + Maybe<ServiceWorkerDescriptor> active = aDescriptor.GetActive(); + if (active.isSome()) { + mActiveWorker = global->GetOrCreateServiceWorker(active.ref()); + } else { + mActiveWorker = nullptr; + } + + Maybe<ServiceWorkerDescriptor> waiting = aDescriptor.GetWaiting(); + if (waiting.isSome()) { + mWaitingWorker = global->GetOrCreateServiceWorker(waiting.ref()); + } else { + mWaitingWorker = nullptr; + } + + Maybe<ServiceWorkerDescriptor> installing = aDescriptor.GetInstalling(); + if (installing.isSome()) { + mInstallingWorker = global->GetOrCreateServiceWorker(installing.ref()); + } else { + mInstallingWorker = nullptr; + } +} + +bool +ServiceWorkerRegistration::MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) const +{ + return aDescriptor.PrincipalInfo() == mDescriptor.PrincipalInfo() && + aDescriptor.Scope() == mDescriptor.Scope(); +} + +void +ServiceWorkerRegistration::GetScope(nsAString& aScope) const +{ + CopyUTF8toUTF16(mDescriptor.Scope(), aScope); +} + +ServiceWorkerUpdateViaCache +ServiceWorkerRegistration::GetUpdateViaCache(ErrorResult& aRv) const +{ + return mDescriptor.UpdateViaCache(); +} + +already_AddRefed<Promise> +ServiceWorkerRegistration::Update(ErrorResult& aRv) +{ + if (!mInner) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + return mInner->Update(aRv); +} + +already_AddRefed<Promise> +ServiceWorkerRegistration::Unregister(ErrorResult& aRv) +{ + if (!mInner) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + return mInner->Unregister(aRv); +} + +already_AddRefed<PushManager> +ServiceWorkerRegistration::GetPushManager(JSContext* aCx, ErrorResult& aRv) +{ + if (!mInner) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + if (!mPushManager) { + mPushManager = mInner->GetPushManager(aCx, aRv); + if (aRv.Failed()) { + return nullptr; + } + } + RefPtr<PushManager> ret = mPushManager; + return ret.forget(); +} + +already_AddRefed<Promise> +ServiceWorkerRegistration::ShowNotification(JSContext* aCx, + const nsAString& aTitle, + const NotificationOptions& aOptions, + ErrorResult& aRv) +{ + if (!mInner) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + return mInner->ShowNotification(aCx, aTitle, aOptions, aRv); +} + +already_AddRefed<Promise> +ServiceWorkerRegistration::GetNotifications(const GetNotificationOptions& aOptions, + ErrorResult& aRv) +{ + if (!mInner) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + return mInner->GetNotifications(aOptions, aRv); +} + } // dom namespace } // mozilla namespace
--- a/dom/serviceworkers/ServiceWorkerRegistration.h +++ b/dom/serviceworkers/ServiceWorkerRegistration.h @@ -5,114 +5,136 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_ServiceWorkerRegistration_h #define mozilla_dom_ServiceWorkerRegistration_h #include "mozilla/DOMEventTargetHelper.h" #include "mozilla/dom/DOMPrefs.h" #include "mozilla/dom/ServiceWorkerBinding.h" -#include "mozilla/dom/ServiceWorkerCommon.h" #include "mozilla/dom/ServiceWorkerRegistrationBinding.h" -#include "mozilla/dom/WorkerHolder.h" +#include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h" // Support for Notification API extension. #include "mozilla/dom/NotificationBinding.h" -class nsPIDOMWindowInner; +class nsIGlobalObject; namespace mozilla { namespace dom { class Promise; class PushManager; -class WorkerListener; +class WorkerPrivate; +class ServiceWorker; -// Used by ServiceWorkerManager to notify ServiceWorkerRegistrations of -// updatefound event and invalidating ServiceWorker instances. -class ServiceWorkerRegistrationListener +class ServiceWorkerRegistration final : public DOMEventTargetHelper { public: - NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + class Inner + { + public: + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING - virtual void - UpdateFound() = 0; + virtual void + SetServiceWorkerRegistration(ServiceWorkerRegistration* aReg) = 0; - virtual void - InvalidateWorkers(WhichServiceWorker aWhichOnes) = 0; + virtual void + ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg) = 0; - virtual void - TransitionWorker(WhichServiceWorker aWhichOne) = 0; + virtual already_AddRefed<Promise> + Update(ErrorResult& aRv) = 0; + + virtual already_AddRefed<Promise> + Unregister(ErrorResult& aRv) = 0; - virtual void - RegistrationRemoved() = 0; + virtual already_AddRefed<Promise> + ShowNotification(JSContext* aCx, + const nsAString& aTitle, + const NotificationOptions& aOptions, + ErrorResult& aRv) = 0; - virtual void - GetScope(nsAString& aScope) const = 0; -}; + virtual already_AddRefed<Promise> + GetNotifications(const GetNotificationOptions& aOptions, + ErrorResult& aRv) = 0; -class ServiceWorkerRegistration : public DOMEventTargetHelper -{ -public: + virtual already_AddRefed<PushManager> + GetPushManager(JSContext* aCx, ErrorResult& aRv) = 0; + }; + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper) IMPL_EVENT_HANDLER(updatefound) static already_AddRefed<ServiceWorkerRegistration> CreateForMainThread(nsPIDOMWindowInner* aWindow, - const nsAString& aScope); + const ServiceWorkerRegistrationDescriptor& aDescriptor); static already_AddRefed<ServiceWorkerRegistration> CreateForWorker(WorkerPrivate* aWorkerPrivate, - const nsAString& aScope); + const ServiceWorkerRegistrationDescriptor& aDescriptor); JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; - virtual already_AddRefed<ServiceWorker> - GetInstalling() = 0; + void DisconnectFromOwner() override; - virtual already_AddRefed<ServiceWorker> - GetWaiting() = 0; + already_AddRefed<ServiceWorker> + GetInstalling() const; + + already_AddRefed<ServiceWorker> + GetWaiting() const; - virtual already_AddRefed<ServiceWorker> - GetActive() = 0; + already_AddRefed<ServiceWorker> + GetActive() const; - virtual void - GetScope(nsAString& aScope) const = 0; + void + UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor); + + bool + MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) const; - virtual ServiceWorkerUpdateViaCache - GetUpdateViaCache(ErrorResult& aRv) const = 0; + void + GetScope(nsAString& aScope) const; - virtual already_AddRefed<Promise> - Update(ErrorResult& aRv) = 0; + ServiceWorkerUpdateViaCache + GetUpdateViaCache(ErrorResult& aRv) const; - virtual already_AddRefed<Promise> - Unregister(ErrorResult& aRv) = 0; + already_AddRefed<Promise> + Update(ErrorResult& aRv); - virtual already_AddRefed<PushManager> - GetPushManager(JSContext* aCx, ErrorResult& aRv) = 0; + already_AddRefed<Promise> + Unregister(ErrorResult& aRv); - virtual already_AddRefed<Promise> + already_AddRefed<PushManager> + GetPushManager(JSContext* aCx, ErrorResult& aRv); + + already_AddRefed<Promise> ShowNotification(JSContext* aCx, const nsAString& aTitle, const NotificationOptions& aOptions, - ErrorResult& aRv) = 0; + ErrorResult& aRv); - virtual already_AddRefed<Promise> + already_AddRefed<Promise> GetNotifications(const GetNotificationOptions& aOptions, - ErrorResult& aRv) = 0; + ErrorResult& aRv); -protected: - ServiceWorkerRegistration(nsPIDOMWindowInner* aWindow, - const nsAString& aScope); +private: + ServiceWorkerRegistration(nsIGlobalObject* aGlobal, + const ServiceWorkerRegistrationDescriptor& aDescriptor, + Inner* aInner); + + ~ServiceWorkerRegistration(); - virtual ~ServiceWorkerRegistration() - { } - - const nsString mScope; + ServiceWorkerRegistrationDescriptor mDescriptor; + RefPtr<Inner> mInner; + RefPtr<ServiceWorker> mInstallingWorker; + RefPtr<ServiceWorker> mWaitingWorker; + RefPtr<ServiceWorker> mActiveWorker; + RefPtr<PushManager> mPushManager; }; } // namespace dom } // namespace mozilla #endif /* mozilla_dom_ServiceWorkerRegistration_h */
--- a/dom/serviceworkers/ServiceWorkerRegistrationDescriptor.cpp +++ b/dom/serviceworkers/ServiceWorkerRegistrationDescriptor.cpp @@ -1,15 +1,17 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h" + +#include "mozilla/dom/IPCServiceWorkerRegistrationDescriptor.h" #include "mozilla/ipc/PBackgroundSharedTypes.h" #include "ServiceWorkerInfo.h" namespace mozilla { namespace dom { Maybe<IPCServiceWorkerDescriptor> ServiceWorkerRegistrationDescriptor::NewestInternal() const @@ -66,16 +68,19 @@ ServiceWorkerRegistrationDescriptor::Ser // on default copy construction. Use the assignment operator to // minimize duplication. operator=(aRight); } ServiceWorkerRegistrationDescriptor& ServiceWorkerRegistrationDescriptor::operator=(const ServiceWorkerRegistrationDescriptor& aRight) { + if (this == &aRight) { + return *this; + } mData.reset(); mData = MakeUnique<IPCServiceWorkerRegistrationDescriptor>(*aRight.mData); MOZ_DIAGNOSTIC_ASSERT(IsValid()); return *this; } ServiceWorkerRegistrationDescriptor::ServiceWorkerRegistrationDescriptor(ServiceWorkerRegistrationDescriptor&& aRight) : mData(Move(aRight.mData)) @@ -87,16 +92,21 @@ ServiceWorkerRegistrationDescriptor& ServiceWorkerRegistrationDescriptor::operator=(ServiceWorkerRegistrationDescriptor&& aRight) { mData.reset(); mData = Move(aRight.mData); MOZ_DIAGNOSTIC_ASSERT(IsValid()); return *this; } +ServiceWorkerRegistrationDescriptor::~ServiceWorkerRegistrationDescriptor() +{ + // Non-default destructor to avoid exposing the IPC type in the header. +} + bool ServiceWorkerRegistrationDescriptor::operator==(const ServiceWorkerRegistrationDescriptor& aRight) const { return *mData == *aRight.mData; } ServiceWorkerUpdateViaCache ServiceWorkerRegistrationDescriptor::UpdateViaCache() const @@ -244,19 +254,19 @@ ServiceWorkerRegistrationDescriptor::Set } else { mData->active() = void_t(); } MOZ_DIAGNOSTIC_ASSERT(IsValid()); } void -ServiceWorkerRegistrationDescriptor::SetWorkers(OptionalIPCServiceWorkerDescriptor& aInstalling, - OptionalIPCServiceWorkerDescriptor& aWaiting, - OptionalIPCServiceWorkerDescriptor& aActive) +ServiceWorkerRegistrationDescriptor::SetWorkers(const OptionalIPCServiceWorkerDescriptor& aInstalling, + const OptionalIPCServiceWorkerDescriptor& aWaiting, + const OptionalIPCServiceWorkerDescriptor& aActive) { mData->installing() = aInstalling; mData->waiting() = aWaiting; mData->active() = aActive; MOZ_DIAGNOSTIC_ASSERT(IsValid()); } const IPCServiceWorkerRegistrationDescriptor&
--- a/dom/serviceworkers/ServiceWorkerRegistrationDescriptor.h +++ b/dom/serviceworkers/ServiceWorkerRegistrationDescriptor.h @@ -2,29 +2,29 @@ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef _mozilla_dom_ServiceWorkerRegistrationDescriptor_h #define _mozilla_dom_ServiceWorkerRegistrationDescriptor_h #include "mozilla/Maybe.h" -#include "mozilla/dom/IPCServiceWorkerRegistrationDescriptor.h" #include "mozilla/dom/ServiceWorkerDescriptor.h" #include "mozilla/UniquePtr.h" namespace mozilla { namespace ipc { class PrincipalInfo; } // namespace ipc namespace dom { class IPCServiceWorkerRegistrationDescriptor; +class OptionalIPCServiceWorkerDescriptor; class ServiceWorkerInfo; enum class ServiceWorkerUpdateViaCache : uint8_t; // This class represents a snapshot of a particular // ServiceWorkerRegistrationInfo object. It is threadsafe and can be // transferred across processes. class ServiceWorkerRegistrationDescriptor final { @@ -52,17 +52,17 @@ public: ServiceWorkerRegistrationDescriptor& operator=(const ServiceWorkerRegistrationDescriptor& aRight); ServiceWorkerRegistrationDescriptor(ServiceWorkerRegistrationDescriptor&& aRight); ServiceWorkerRegistrationDescriptor& operator=(ServiceWorkerRegistrationDescriptor&& aRight); - ~ServiceWorkerRegistrationDescriptor() = default; + ~ServiceWorkerRegistrationDescriptor(); bool operator==(const ServiceWorkerRegistrationDescriptor& aRight) const; ServiceWorkerUpdateViaCache UpdateViaCache() const; const mozilla::ipc::PrincipalInfo& @@ -90,19 +90,19 @@ public: SetUpdateViaCache(ServiceWorkerUpdateViaCache aUpdateViaCache); void SetWorkers(ServiceWorkerInfo* aInstalling, ServiceWorkerInfo* aWaiting, ServiceWorkerInfo* aActive); void - SetWorkers(OptionalIPCServiceWorkerDescriptor& aInstalling, - OptionalIPCServiceWorkerDescriptor& aWaiting, - OptionalIPCServiceWorkerDescriptor& aActive); + SetWorkers(const OptionalIPCServiceWorkerDescriptor& aInstalling, + const OptionalIPCServiceWorkerDescriptor& aWaiting, + const OptionalIPCServiceWorkerDescriptor& aActive); // Expose the underlying IPC type so that it can be passed via IPC. const IPCServiceWorkerRegistrationDescriptor& ToIPC() const; }; } // namespace dom } // namespace mozilla
new file mode 100644 --- /dev/null +++ b/dom/serviceworkers/ServiceWorkerRegistrationImpl.cpp @@ -0,0 +1,1029 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ServiceWorkerRegistrationImpl.h" + +#include "ipc/ErrorIPCUtils.h" +#include "mozilla/dom/DOMPrefs.h" +#include "mozilla/dom/Notification.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/PromiseWindowProxy.h" +#include "mozilla/dom/PromiseWorkerProxy.h" +#include "mozilla/dom/PushManagerBinding.h" +#include "mozilla/dom/PushManager.h" +#include "mozilla/dom/ServiceWorkerRegistrationBinding.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/WorkerCommon.h" +#include "mozilla/dom/WorkerScope.h" +#include "mozilla/Services.h" +#include "mozilla/Unused.h" +#include "nsCycleCollectionParticipant.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" +#include "ServiceWorker.h" +#include "ServiceWorkerManager.h" +#include "ServiceWorkerRegistration.h" + +#include "nsIDocument.h" +#include "nsIServiceWorkerManager.h" +#include "nsISupportsPrimitives.h" +#include "nsPIDOMWindow.h" +#include "nsContentUtils.h" + +namespace mozilla { +namespace dom { + +//////////////////////////////////////////////////// +// Main Thread implementation + +ServiceWorkerRegistrationMainThread::ServiceWorkerRegistrationMainThread(const ServiceWorkerRegistrationDescriptor& aDescriptor) + : mOuter(nullptr) + , mScope(NS_ConvertUTF8toUTF16(aDescriptor.Scope())) + , mListeningForEvents(false) +{ + MOZ_ASSERT(NS_IsMainThread()); +} + +ServiceWorkerRegistrationMainThread::~ServiceWorkerRegistrationMainThread() +{ + MOZ_DIAGNOSTIC_ASSERT(!mListeningForEvents); + MOZ_DIAGNOSTIC_ASSERT(!mOuter); +} + +// XXXnsm, maybe this can be optimized to only add when a event handler is +// registered. +void +ServiceWorkerRegistrationMainThread::StartListeningForEvents() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mListeningForEvents); + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (swm) { + swm->AddRegistrationEventListener(mScope, this); + mListeningForEvents = true; + } +} + +void +ServiceWorkerRegistrationMainThread::StopListeningForEvents() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (!mListeningForEvents) { + return; + } + + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (swm) { + swm->RemoveRegistrationEventListener(mScope, this); + } + mListeningForEvents = false; +} + +void +ServiceWorkerRegistrationMainThread::UpdateFound() +{ + mOuter->DispatchTrustedEvent(NS_LITERAL_STRING("updatefound")); +} + +void +ServiceWorkerRegistrationMainThread::UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor) +{ + mOuter->UpdateState(aDescriptor); +} + +void +ServiceWorkerRegistrationMainThread::RegistrationRemoved() +{ + // If the registration is being removed completely, remove it from the + // window registration hash table so that a new registration would get a new + // wrapper JS object. + if (nsCOMPtr<nsPIDOMWindowInner> window = mOuter->GetOwner()) { + window->InvalidateServiceWorkerRegistration(mScope); + } +} + +bool +ServiceWorkerRegistrationMainThread::MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) +{ + return mOuter->MatchesDescriptor(aDescriptor); +} + +void +ServiceWorkerRegistrationMainThread::SetServiceWorkerRegistration(ServiceWorkerRegistration* aReg) +{ + MOZ_DIAGNOSTIC_ASSERT(aReg); + MOZ_DIAGNOSTIC_ASSERT(!mOuter); + mOuter = aReg; + StartListeningForEvents(); +} + +void +ServiceWorkerRegistrationMainThread::ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg) +{ + MOZ_DIAGNOSTIC_ASSERT(mOuter); + MOZ_DIAGNOSTIC_ASSERT(mOuter == aReg); + StopListeningForEvents(); + mOuter = nullptr; +} + +namespace { + +void +UpdateInternal(nsIPrincipal* aPrincipal, + const nsAString& aScope, + ServiceWorkerUpdateFinishCallback* aCallback) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(aCallback); + + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // browser shutdown + return; + } + + swm->Update(aPrincipal, NS_ConvertUTF16toUTF8(aScope), aCallback); +} + +class MainThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback +{ + PromiseWindowProxy mPromise; + + ~MainThreadUpdateCallback() + { } + +public: + explicit MainThreadUpdateCallback(nsPIDOMWindowInner* aWindow, + Promise* aPromise) + : mPromise(aWindow, aPromise) + { + MOZ_ASSERT(NS_IsMainThread()); + } + + void + UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override + { + RefPtr<Promise> promise = mPromise.Get(); + nsCOMPtr<nsPIDOMWindowInner> win = mPromise.GetWindow(); + if (!promise || !win) { + return; + } + + nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( + "MainThreadUpdateCallback::UpdateSucceeded", + [promise = Move(promise)] () { + promise->MaybeResolveWithUndefined(); + }); + MOZ_ALWAYS_SUCCEEDS( + win->EventTargetFor(TaskCategory::Other)->Dispatch(r.forget())); + } + + void + UpdateFailed(ErrorResult& aStatus) override + { + if (RefPtr<Promise> promise = mPromise.Get()) { + promise->MaybeReject(aStatus); + } + } +}; + +class UpdateResultRunnable final : public WorkerRunnable +{ + RefPtr<PromiseWorkerProxy> mPromiseProxy; + IPC::Message mSerializedErrorResult; + + ~UpdateResultRunnable() + {} + +public: + UpdateResultRunnable(PromiseWorkerProxy* aPromiseProxy, ErrorResult& aStatus) + : WorkerRunnable(aPromiseProxy->GetWorkerPrivate()) + , mPromiseProxy(aPromiseProxy) + { + // ErrorResult is not thread safe. Serialize it for transfer across + // threads. + IPC::WriteParam(&mSerializedErrorResult, aStatus); + aStatus.SuppressException(); + } + + bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + // Deserialize the ErrorResult now that we are back in the worker + // thread. + ErrorResult status; + PickleIterator iter = PickleIterator(mSerializedErrorResult); + Unused << IPC::ReadParam(&mSerializedErrorResult, &iter, &status); + + Promise* promise = mPromiseProxy->WorkerPromise(); + if (status.Failed()) { + promise->MaybeReject(status); + } else { + promise->MaybeResolveWithUndefined(); + } + status.SuppressException(); + mPromiseProxy->CleanUp(); + return true; + } +}; + +class WorkerThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback +{ + RefPtr<PromiseWorkerProxy> mPromiseProxy; + + ~WorkerThreadUpdateCallback() + { + } + +public: + explicit WorkerThreadUpdateCallback(PromiseWorkerProxy* aPromiseProxy) + : mPromiseProxy(aPromiseProxy) + { + MOZ_ASSERT(NS_IsMainThread()); + } + + void + UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override + { + ErrorResult rv(NS_OK); + Finish(rv); + } + + void + UpdateFailed(ErrorResult& aStatus) override + { + Finish(aStatus); + } + + void + Finish(ErrorResult& aStatus) + { + if (!mPromiseProxy) { + return; + } + + RefPtr<PromiseWorkerProxy> proxy = mPromiseProxy.forget(); + + MutexAutoLock lock(proxy->Lock()); + if (proxy->CleanedUp()) { + return; + } + + RefPtr<UpdateResultRunnable> r = + new UpdateResultRunnable(proxy, aStatus); + r->Dispatch(); + } +}; + +class SWRUpdateRunnable final : public Runnable +{ +public: + SWRUpdateRunnable(PromiseWorkerProxy* aPromiseProxy, const nsAString& aScope) + : Runnable("dom::SWRUpdateRunnable") + , mPromiseProxy(aPromiseProxy) + , mScope(aScope) + {} + + NS_IMETHOD + Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + ErrorResult result; + + nsCOMPtr<nsIPrincipal> principal; + // UpdateInternal may try to reject the promise synchronously leading + // to a deadlock. + { + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { + return NS_OK; + } + + principal = mPromiseProxy->GetWorkerPrivate()->GetPrincipal(); + } + MOZ_ASSERT(principal); + + RefPtr<WorkerThreadUpdateCallback> cb = + new WorkerThreadUpdateCallback(mPromiseProxy); + UpdateInternal(principal, mScope, cb); + return NS_OK; + } + +private: + ~SWRUpdateRunnable() + {} + + RefPtr<PromiseWorkerProxy> mPromiseProxy; + const nsString mScope; +}; + +class UnregisterCallback final : public nsIServiceWorkerUnregisterCallback +{ + PromiseWindowProxy mPromise; + +public: + NS_DECL_ISUPPORTS + + explicit UnregisterCallback(nsPIDOMWindowInner* aWindow, + Promise* aPromise) + : mPromise(aWindow, aPromise) + { + MOZ_ASSERT(aPromise); + } + + NS_IMETHOD + UnregisterSucceeded(bool aState) override + { + MOZ_ASSERT(NS_IsMainThread()); + RefPtr<Promise> promise = mPromise.Get(); + nsCOMPtr<nsPIDOMWindowInner> win = mPromise.GetWindow(); + if (!promise || !win) { + return NS_OK; + } + + nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( + "UnregisterCallback::UnregisterSucceeded", + [promise = Move(promise), aState] () { + promise->MaybeResolve(aState); + }); + MOZ_ALWAYS_SUCCEEDS( + win->EventTargetFor(TaskCategory::Other)->Dispatch(r.forget())); + return NS_OK; + } + + NS_IMETHOD + UnregisterFailed() override + { + MOZ_ASSERT(NS_IsMainThread()); + + if (RefPtr<Promise> promise = mPromise.Get()) { + promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); + } + return NS_OK; + } + +private: + ~UnregisterCallback() + { } +}; + +NS_IMPL_ISUPPORTS(UnregisterCallback, nsIServiceWorkerUnregisterCallback) + +class FulfillUnregisterPromiseRunnable final : public WorkerRunnable +{ + RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy; + Maybe<bool> mState; +public: + FulfillUnregisterPromiseRunnable(PromiseWorkerProxy* aProxy, + const Maybe<bool>& aState) + : WorkerRunnable(aProxy->GetWorkerPrivate()) + , mPromiseWorkerProxy(aProxy) + , mState(aState) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mPromiseWorkerProxy); + } + + bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + RefPtr<Promise> promise = mPromiseWorkerProxy->WorkerPromise(); + if (mState.isSome()) { + promise->MaybeResolve(mState.value()); + } else { + promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); + } + + mPromiseWorkerProxy->CleanUp(); + return true; + } +}; + +class WorkerUnregisterCallback final : public nsIServiceWorkerUnregisterCallback +{ + RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy; +public: + NS_DECL_ISUPPORTS + + explicit WorkerUnregisterCallback(PromiseWorkerProxy* aProxy) + : mPromiseWorkerProxy(aProxy) + { + MOZ_ASSERT(aProxy); + } + + NS_IMETHOD + UnregisterSucceeded(bool aState) override + { + MOZ_ASSERT(NS_IsMainThread()); + Finish(Some(aState)); + return NS_OK; + } + + NS_IMETHOD + UnregisterFailed() override + { + MOZ_ASSERT(NS_IsMainThread()); + Finish(Nothing()); + return NS_OK; + } + +private: + ~WorkerUnregisterCallback() + {} + + void + Finish(const Maybe<bool>& aState) + { + MOZ_ASSERT(NS_IsMainThread()); + if (!mPromiseWorkerProxy) { + return; + } + + RefPtr<PromiseWorkerProxy> proxy = mPromiseWorkerProxy.forget(); + MutexAutoLock lock(proxy->Lock()); + if (proxy->CleanedUp()) { + return; + } + + RefPtr<WorkerRunnable> r = + new FulfillUnregisterPromiseRunnable(proxy, aState); + + r->Dispatch(); + } +}; + +NS_IMPL_ISUPPORTS(WorkerUnregisterCallback, nsIServiceWorkerUnregisterCallback); + +/* + * If the worker goes away, we still continue to unregister, but we don't try to + * resolve the worker Promise (which doesn't exist by that point). + */ +class StartUnregisterRunnable final : public Runnable +{ + RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy; + const nsString mScope; + +public: + StartUnregisterRunnable(PromiseWorkerProxy* aProxy, const nsAString& aScope) + : Runnable("dom::StartUnregisterRunnable") + , mPromiseWorkerProxy(aProxy) + , mScope(aScope) + { + MOZ_ASSERT(aProxy); + } + + NS_IMETHOD + Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + // XXXnsm: There is a rare chance of this failing if the worker gets + // destroyed. In that case, unregister() called from a SW is no longer + // guaranteed to run. We should fix this by having a main thread proxy + // maintain a strongref to ServiceWorkerRegistrationInfo and use its + // principal. Can that be trusted? + nsCOMPtr<nsIPrincipal> principal; + { + MutexAutoLock lock(mPromiseWorkerProxy->Lock()); + if (mPromiseWorkerProxy->CleanedUp()) { + return NS_OK; + } + + WorkerPrivate* worker = mPromiseWorkerProxy->GetWorkerPrivate(); + MOZ_ASSERT(worker); + principal = worker->GetPrincipal(); + } + MOZ_ASSERT(principal); + + RefPtr<WorkerUnregisterCallback> cb = + new WorkerUnregisterCallback(mPromiseWorkerProxy); + nsCOMPtr<nsIServiceWorkerManager> swm = + mozilla::services::GetServiceWorkerManager(); + nsresult rv = swm->Unregister(principal, cb, mScope); + if (NS_WARN_IF(NS_FAILED(rv))) { + cb->UnregisterFailed(); + } + + return NS_OK; + } +}; +} // namespace + +already_AddRefed<Promise> +ServiceWorkerRegistrationMainThread::Update(ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mOuter->GetOwner()); + if (!go) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr<Promise> promise = Promise::Create(go, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + nsCOMPtr<nsIDocument> doc = mOuter->GetOwner()->GetExtantDoc(); + MOZ_ASSERT(doc); + + RefPtr<MainThreadUpdateCallback> cb = + new MainThreadUpdateCallback(mOuter->GetOwner(), promise); + UpdateInternal(doc->NodePrincipal(), mScope, cb); + + return promise.forget(); +} + +already_AddRefed<Promise> +ServiceWorkerRegistrationMainThread::Unregister(ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mOuter->GetOwner()); + if (!go) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + // Although the spec says that the same-origin checks should also be done + // asynchronously, we do them in sync because the Promise created by the + // WebIDL infrastructure due to a returned error will be resolved + // asynchronously. We aren't making any internal state changes in these + // checks, so ordering of multiple calls is not affected. + nsCOMPtr<nsIDocument> document = mOuter->GetOwner()->GetExtantDoc(); + if (!document) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr<nsIURI> scopeURI; + nsCOMPtr<nsIURI> baseURI = document->GetBaseURI(); + // "If the origin of scope is not client's origin..." + nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), mScope, nullptr, baseURI); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return nullptr; + } + + nsCOMPtr<nsIPrincipal> documentPrincipal = document->NodePrincipal(); + rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */, + false /* allowIfInheritsPrinciple */); + if (NS_FAILED(rv)) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return nullptr; + } + + nsAutoCString uriSpec; + aRv = scopeURI->GetSpecIgnoringRef(uriSpec); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + nsCOMPtr<nsIServiceWorkerManager> swm = + mozilla::services::GetServiceWorkerManager(); + + RefPtr<Promise> promise = Promise::Create(go, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + RefPtr<UnregisterCallback> cb = new UnregisterCallback(mOuter->GetOwner(), promise); + + NS_ConvertUTF8toUTF16 scope(uriSpec); + aRv = swm->Unregister(documentPrincipal, cb, scope); + if (aRv.Failed()) { + return nullptr; + } + + return promise.forget(); +} + +// Notification API extension. +already_AddRefed<Promise> +ServiceWorkerRegistrationMainThread::ShowNotification(JSContext* aCx, + const nsAString& aTitle, + const NotificationOptions& aOptions, + ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr<nsPIDOMWindowInner> window = mOuter->GetOwner(); + if (NS_WARN_IF(!window)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr<nsIDocument> doc = window->GetExtantDoc(); + if (NS_WARN_IF(!doc)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr<ServiceWorker> worker = mOuter->GetActive(); + if (!worker) { + aRv.ThrowTypeError<MSG_NO_ACTIVE_WORKER>(mScope); + return nullptr; + } + + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window); + RefPtr<Promise> p = + Notification::ShowPersistentNotification(aCx, global, mScope, aTitle, + aOptions, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return p.forget(); +} + +already_AddRefed<Promise> +ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr<nsPIDOMWindowInner> window = mOuter->GetOwner(); + if (NS_WARN_IF(!window)) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + return Notification::Get(window, aOptions, mScope, aRv); +} + +already_AddRefed<PushManager> +ServiceWorkerRegistrationMainThread::GetPushManager(JSContext* aCx, + ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(mOuter->GetOwner()); + + if (!globalObject) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + GlobalObject global(aCx, globalObject->GetGlobalJSObject()); + RefPtr<PushManager> ret = PushManager::Constructor(global, mScope, aRv); + if (aRv.Failed()) { + return nullptr; + } + + return ret.forget(); +} + +//////////////////////////////////////////////////// +// Worker Thread implementation + +class WorkerListener final : public ServiceWorkerRegistrationListener +{ + // Accessed on the main thread. + WorkerPrivate* mWorkerPrivate; + const nsString mScope; + bool mListeningForEvents; + + // Accessed on the worker thread. + ServiceWorkerRegistrationWorkerThread* mRegistration; + +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerListener, override) + + WorkerListener(WorkerPrivate* aWorkerPrivate, + ServiceWorkerRegistrationWorkerThread* aReg, + const nsAString& aScope) + : mWorkerPrivate(aWorkerPrivate) + , mScope(aScope) + , mListeningForEvents(false) + , mRegistration(aReg) + { + MOZ_ASSERT(mWorkerPrivate); + mWorkerPrivate->AssertIsOnWorkerThread(); + MOZ_ASSERT(mRegistration); + } + + void + StartListeningForEvents() + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mListeningForEvents); + MOZ_ASSERT(mWorkerPrivate); + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (swm) { + // FIXME(nsm): Maybe the function shouldn't take an explicit scope. + swm->AddRegistrationEventListener(mScope, this); + mListeningForEvents = true; + } + } + + void + StopListeningForEvents() + { + MOZ_ASSERT(NS_IsMainThread()); + + MOZ_ASSERT(mListeningForEvents); + + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + + // We aren't going to need this anymore and we shouldn't hold on since the + // worker will go away soon. + mWorkerPrivate = nullptr; + + if (swm) { + // FIXME(nsm): Maybe the function shouldn't take an explicit scope. + swm->RemoveRegistrationEventListener(mScope, this); + mListeningForEvents = false; + } + } + + // ServiceWorkerRegistrationListener + void + UpdateFound() override; + + void + UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor) override + { + MOZ_ASSERT(NS_IsMainThread()); + // TODO: Not implemented + } + + void + RegistrationRemoved() override + { + MOZ_ASSERT(NS_IsMainThread()); + } + + void + GetScope(nsAString& aScope) const override + { + aScope = mScope; + } + + bool + MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) override + { + // TODO: Not implemented + return false; + } + + ServiceWorkerRegistrationWorkerThread* + GetRegistration() const + { + if (mWorkerPrivate) { + mWorkerPrivate->AssertIsOnWorkerThread(); + } + return mRegistration; + } + + void + ClearRegistration() + { + if (mWorkerPrivate) { + mWorkerPrivate->AssertIsOnWorkerThread(); + } + mRegistration = nullptr; + } + +private: + ~WorkerListener() + { + MOZ_ASSERT(!mListeningForEvents); + } +}; + +ServiceWorkerRegistrationWorkerThread::ServiceWorkerRegistrationWorkerThread(WorkerPrivate* aWorkerPrivate, + const ServiceWorkerRegistrationDescriptor& aDescriptor) + : WorkerHolder("ServiceWorkerRegistrationWorkerThread") + , mOuter(nullptr) + , mWorkerPrivate(aWorkerPrivate) + , mScope(NS_ConvertUTF8toUTF16(aDescriptor.Scope())) +{ +} + +ServiceWorkerRegistrationWorkerThread::~ServiceWorkerRegistrationWorkerThread() +{ + MOZ_DIAGNOSTIC_ASSERT(!mListener); + MOZ_DIAGNOSTIC_ASSERT(!mOuter); +} + +void +ServiceWorkerRegistrationWorkerThread::SetServiceWorkerRegistration(ServiceWorkerRegistration* aReg) +{ + MOZ_DIAGNOSTIC_ASSERT(aReg); + MOZ_DIAGNOSTIC_ASSERT(!mOuter); + mOuter = aReg; + InitListener(); +} + +void +ServiceWorkerRegistrationWorkerThread::ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg) +{ + MOZ_DIAGNOSTIC_ASSERT(mOuter); + MOZ_DIAGNOSTIC_ASSERT(mOuter == aReg); + ReleaseListener(); + mOuter = nullptr; +} + +already_AddRefed<Promise> +ServiceWorkerRegistrationWorkerThread::Update(ErrorResult& aRv) +{ + WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(worker); + worker->AssertIsOnWorkerThread(); + + RefPtr<Promise> promise = Promise::Create(worker->GlobalScope(), aRv); + if (aRv.Failed()) { + return nullptr; + } + + // Avoid infinite update loops by ignoring update() calls during top + // level script evaluation. See: + // https://github.com/slightlyoff/ServiceWorker/issues/800 + if (worker->LoadScriptAsPartOfLoadingServiceWorkerScript()) { + promise->MaybeResolveWithUndefined(); + return promise.forget(); + } + + RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise); + if (!proxy) { + aRv.Throw(NS_ERROR_DOM_ABORT_ERR); + return nullptr; + } + + RefPtr<SWRUpdateRunnable> r = new SWRUpdateRunnable(proxy, mScope); + MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget())); + + return promise.forget(); +} + +already_AddRefed<Promise> +ServiceWorkerRegistrationWorkerThread::Unregister(ErrorResult& aRv) +{ + WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(worker); + worker->AssertIsOnWorkerThread(); + + if (!worker->IsServiceWorker()) { + // For other workers, the registration probably originated from + // getRegistration(), so we may have to validate origin etc. Let's do this + // this later. + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return nullptr; + } + + RefPtr<Promise> promise = Promise::Create(worker->GlobalScope(), aRv); + if (aRv.Failed()) { + return nullptr; + } + + RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise); + if (!proxy) { + aRv.Throw(NS_ERROR_DOM_ABORT_ERR); + return nullptr; + } + + RefPtr<StartUnregisterRunnable> r = new StartUnregisterRunnable(proxy, mScope); + MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget())); + + return promise.forget(); +} + +void +ServiceWorkerRegistrationWorkerThread::InitListener() +{ + MOZ_ASSERT(!mListener); + WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(worker); + worker->AssertIsOnWorkerThread(); + + mListener = new WorkerListener(worker, this, mScope); + if (!HoldWorker(worker, Closing)) { + mListener = nullptr; + NS_WARNING("Could not add feature"); + return; + } + + nsCOMPtr<nsIRunnable> r = + NewRunnableMethod("dom::WorkerListener::StartListeningForEvents", + mListener, + &WorkerListener::StartListeningForEvents); + MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget())); +} + +void +ServiceWorkerRegistrationWorkerThread::ReleaseListener() +{ + if (!mListener) { + return; + } + + // We can assert worker here, because: + // 1) We always HoldWorker, so if the worker has shutdown already, we'll + // have received Notify and removed it. If HoldWorker had failed, + // mListener will be null and we won't reach here. + // 2) Otherwise, worker is still around even if we are going away. + mWorkerPrivate->AssertIsOnWorkerThread(); + ReleaseWorker(); + + mListener->ClearRegistration(); + + nsCOMPtr<nsIRunnable> r = + NewRunnableMethod("dom::WorkerListener::StopListeningForEvents", + mListener, + &WorkerListener::StopListeningForEvents); + MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(r.forget())); + + mListener = nullptr; + mWorkerPrivate = nullptr; +} + +bool +ServiceWorkerRegistrationWorkerThread::Notify(WorkerStatus aStatus) +{ + ReleaseListener(); + return true; +} + +class FireUpdateFoundRunnable final : public WorkerRunnable +{ + RefPtr<WorkerListener> mListener; +public: + FireUpdateFoundRunnable(WorkerPrivate* aWorkerPrivate, + WorkerListener* aListener) + : WorkerRunnable(aWorkerPrivate) + , mListener(aListener) + { + // Need this assertion for now since runnables which modify busy count can + // only be dispatched from parent thread to worker thread and we don't deal + // with nested workers. SW threads can't be nested. + MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); + } + + bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + + ServiceWorkerRegistrationWorkerThread* reg = mListener->GetRegistration(); + if (reg) { + reg->UpdateFound(); + } + return true; + } +}; + +void +WorkerListener::UpdateFound() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (mWorkerPrivate) { + RefPtr<FireUpdateFoundRunnable> r = + new FireUpdateFoundRunnable(mWorkerPrivate, this); + Unused << NS_WARN_IF(!r->Dispatch()); + } +} + +// Notification API extension. +already_AddRefed<Promise> +ServiceWorkerRegistrationWorkerThread::ShowNotification(JSContext* aCx, + const nsAString& aTitle, + const NotificationOptions& aOptions, + ErrorResult& aRv) +{ + + // Until Bug 1131324 exposes ServiceWorkerContainer on workers, + // ShowPersistentNotification() checks for valid active worker while it is + // also verifying scope so that we block the worker on the main thread only + // once. + RefPtr<Promise> p = + Notification::ShowPersistentNotification(aCx, mWorkerPrivate->GlobalScope(), + mScope, aTitle, aOptions, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return p.forget(); +} + +already_AddRefed<Promise> +ServiceWorkerRegistrationWorkerThread::GetNotifications(const GetNotificationOptions& aOptions, + ErrorResult& aRv) +{ + return Notification::WorkerGet(mWorkerPrivate, aOptions, mScope, aRv); +} + +already_AddRefed<PushManager> +ServiceWorkerRegistrationWorkerThread::GetPushManager(JSContext* aCx, ErrorResult& aRv) +{ + RefPtr<PushManager> ret = new PushManager(mScope); + return ret.forget(); +} + +void +ServiceWorkerRegistrationWorkerThread::UpdateFound() +{ + mOuter->DispatchTrustedEvent(NS_LITERAL_STRING("updatefound")); +} + +} // dom namespace +} // mozilla namespace
new file mode 100644 --- /dev/null +++ b/dom/serviceworkers/ServiceWorkerRegistrationImpl.h @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/Unused.h" +#include "nsCycleCollectionParticipant.h" +#include "nsIDocument.h" +#include "nsPIDOMWindow.h" +#include "ServiceWorkerManager.h" +#include "ServiceWorkerRegistration.h" +#include "ServiceWorkerRegistrationListener.h" + +namespace mozilla { +namespace dom { + +class Promise; +class PushManager; +class ServiceWorker; + +//////////////////////////////////////////////////// +// Main Thread implementation + +class ServiceWorkerRegistrationMainThread final : public ServiceWorkerRegistration::Inner + , public ServiceWorkerRegistrationListener +{ +public: + NS_INLINE_DECL_REFCOUNTING(ServiceWorkerRegistrationMainThread, override) + + explicit ServiceWorkerRegistrationMainThread(const ServiceWorkerRegistrationDescriptor& aDescriptor); + + // ServiceWorkerRegistration::Inner + void + SetServiceWorkerRegistration(ServiceWorkerRegistration* aReg) override; + + void + ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg) override; + + already_AddRefed<Promise> + Update(ErrorResult& aRv) override; + + already_AddRefed<Promise> + Unregister(ErrorResult& aRv) override; + + already_AddRefed<Promise> + ShowNotification(JSContext* aCx, + const nsAString& aTitle, + const NotificationOptions& aOptions, + ErrorResult& aRv) override; + + already_AddRefed<Promise> + GetNotifications(const GetNotificationOptions& aOptions, + ErrorResult& aRv) override; + + already_AddRefed<PushManager> + GetPushManager(JSContext* aCx, ErrorResult& aRv) override; + + // ServiceWorkerRegistrationListener + void + UpdateFound() override; + + void + UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor) override; + + void + RegistrationRemoved() override; + + void + GetScope(nsAString& aScope) const override + { + aScope = mScope; + } + + bool + MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) override; + +private: + ~ServiceWorkerRegistrationMainThread(); + + void + StartListeningForEvents(); + + void + StopListeningForEvents(); + + ServiceWorkerRegistration* mOuter; + const nsString mScope; + bool mListeningForEvents; +}; + +//////////////////////////////////////////////////// +// Worker Thread implementation + +class WorkerListener; + +class ServiceWorkerRegistrationWorkerThread final : public ServiceWorkerRegistration::Inner + , public WorkerHolder +{ +public: + NS_INLINE_DECL_REFCOUNTING(ServiceWorkerRegistrationWorkerThread, override) + + ServiceWorkerRegistrationWorkerThread(WorkerPrivate* aWorkerPrivate, + const ServiceWorkerRegistrationDescriptor& aDescriptor); + + // ServiceWorkerRegistration::Inner + void + SetServiceWorkerRegistration(ServiceWorkerRegistration* aReg) override; + + void + ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg) override; + + already_AddRefed<Promise> + Update(ErrorResult& aRv) override; + + already_AddRefed<Promise> + Unregister(ErrorResult& aRv) override; + + already_AddRefed<Promise> + ShowNotification(JSContext* aCx, + const nsAString& aTitle, + const NotificationOptions& aOptions, + ErrorResult& aRv) override; + + already_AddRefed<Promise> + GetNotifications(const GetNotificationOptions& aOptions, + ErrorResult& aRv) override; + + already_AddRefed<PushManager> + GetPushManager(JSContext* aCx, ErrorResult& aRv) override; + + // WorkerHolder + bool + Notify(WorkerStatus aStatus) override; + + void + UpdateFound(); + +private: + ~ServiceWorkerRegistrationWorkerThread(); + + void + InitListener(); + + void + ReleaseListener(); + + ServiceWorkerRegistration* mOuter; + WorkerPrivate* mWorkerPrivate; + const nsString mScope; + RefPtr<WorkerListener> mListener; +}; + +} // dom namespace +} // mozilla namespace
--- a/dom/serviceworkers/ServiceWorkerRegistrationInfo.cpp +++ b/dom/serviceworkers/ServiceWorkerRegistrationInfo.cpp @@ -47,44 +47,41 @@ public: void ServiceWorkerRegistrationInfo::Clear() { if (mEvaluatingWorker) { mEvaluatingWorker = nullptr; } - UpdateRegistrationStateProperties(WhichServiceWorker::INSTALLING_WORKER | - WhichServiceWorker::WAITING_WORKER | - WhichServiceWorker::ACTIVE_WORKER, Invalidate); + RefPtr<ServiceWorkerInfo> installing = mInstallingWorker.forget(); + RefPtr<ServiceWorkerInfo> waiting = mWaitingWorker.forget(); + RefPtr<ServiceWorkerInfo> active = mActiveWorker.forget(); - if (mInstallingWorker) { - mInstallingWorker->UpdateState(ServiceWorkerState::Redundant); - mInstallingWorker->UpdateRedundantTime(); - mInstallingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo(); - mInstallingWorker = nullptr; + UpdateRegistrationState(); + + if (installing) { + installing->UpdateState(ServiceWorkerState::Redundant); + installing->UpdateRedundantTime(); + installing->WorkerPrivate()->NoteDeadServiceWorkerInfo(); // FIXME(nsm): Abort any inflight requests from installing worker. } - if (mWaitingWorker) { - mWaitingWorker->UpdateState(ServiceWorkerState::Redundant); - mWaitingWorker->UpdateRedundantTime(); - mWaitingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo(); - mWaitingWorker = nullptr; + if (waiting) { + waiting->UpdateState(ServiceWorkerState::Redundant); + waiting->UpdateRedundantTime(); + waiting->WorkerPrivate()->NoteDeadServiceWorkerInfo(); } - if (mActiveWorker) { - mActiveWorker->UpdateState(ServiceWorkerState::Redundant); - mActiveWorker->UpdateRedundantTime(); - mActiveWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo(); - mActiveWorker = nullptr; + if (active) { + active->UpdateState(ServiceWorkerState::Redundant); + active->UpdateRedundantTime(); + active->WorkerPrivate()->NoteDeadServiceWorkerInfo(); } - mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker); - NotifyChromeRegistrationListeners(); } ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo( const nsACString& aScope, nsIPrincipal* aPrincipal, ServiceWorkerUpdateViaCache aUpdateViaCache) : mPrincipal(aPrincipal) @@ -386,53 +383,32 @@ ServiceWorkerRegistrationInfo::IsLastUpd if (nowMicros < mLastUpdateTime || (nowMicros - mLastUpdateTime) / PR_USEC_PER_SEC > kSecondsPerDay) { return true; } return false; } void -ServiceWorkerRegistrationInfo::AsyncUpdateRegistrationStateProperties(WhichServiceWorker aWorker, - TransitionType aTransition) -{ - MOZ_ASSERT(NS_IsMainThread()); - RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); - if (!swm) { - // browser shutdown started during this async step - return; - } - - if (aTransition == Invalidate) { - swm->InvalidateServiceWorkerRegistrationWorker(this, aWorker); - } else { - MOZ_ASSERT(aTransition == TransitionToNextState); - swm->TransitionServiceWorkerRegistrationWorker(this, aWorker); - - if (aWorker == WhichServiceWorker::WAITING_WORKER) { - swm->CheckPendingReadyPromises(); - } - } -} - -void -ServiceWorkerRegistrationInfo::UpdateRegistrationStateProperties(WhichServiceWorker aWorker, - TransitionType aTransition) +ServiceWorkerRegistrationInfo::UpdateRegistrationState() { MOZ_ASSERT(NS_IsMainThread()); - nsCOMPtr<nsIRunnable> runnable = - NewRunnableMethod<WhichServiceWorker, TransitionType>( - "dom::ServiceWorkerRegistrationInfo::" - "AsyncUpdateRegistrationStateProperties", - this, - &ServiceWorkerRegistrationInfo::AsyncUpdateRegistrationStateProperties, - aWorker, - aTransition); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget())); + mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker); + + RefPtr<ServiceWorkerRegistrationInfo> self(this); + nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( + "ServiceWorkerRegistrationInfo::UpdateRegistrationState", + [self = Move(self)] { + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (swm) { + swm->UpdateRegistrationListeners(self); + } + }); + MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget())); } void ServiceWorkerRegistrationInfo::NotifyChromeRegistrationListeners() { nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> listeners(mListeners); for (size_t index = 0; index < listeners.Length(); ++index) { listeners[index]->OnChange(); @@ -565,38 +541,38 @@ void ServiceWorkerRegistrationInfo::ClearInstalling() { MOZ_ASSERT(NS_IsMainThread()); if (!mInstallingWorker) { return; } - UpdateRegistrationStateProperties(WhichServiceWorker::INSTALLING_WORKER, - Invalidate); - mInstallingWorker->UpdateState(ServiceWorkerState::Redundant); - mInstallingWorker->UpdateRedundantTime(); - mInstallingWorker = nullptr; + RefPtr<ServiceWorkerInfo> installing = mInstallingWorker.forget(); - mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker); + UpdateRegistrationState(); + + installing->UpdateState(ServiceWorkerState::Redundant); + installing->UpdateRedundantTime(); NotifyChromeRegistrationListeners(); } void ServiceWorkerRegistrationInfo::TransitionEvaluatingToInstalling() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mEvaluatingWorker); MOZ_ASSERT(!mInstallingWorker); mInstallingWorker = mEvaluatingWorker.forget(); - mInstallingWorker->UpdateState(ServiceWorkerState::Installing); - mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker); + UpdateRegistrationState(); + + mInstallingWorker->UpdateState(ServiceWorkerState::Installing); NotifyChromeRegistrationListeners(); } void ServiceWorkerRegistrationInfo::TransitionInstallingToWaiting() { MOZ_ASSERT(NS_IsMainThread()); @@ -604,23 +580,22 @@ ServiceWorkerRegistrationInfo::Transitio if (mWaitingWorker) { MOZ_ASSERT(mInstallingWorker->CacheName() != mWaitingWorker->CacheName()); mWaitingWorker->UpdateState(ServiceWorkerState::Redundant); mWaitingWorker->UpdateRedundantTime(); } mWaitingWorker = mInstallingWorker.forget(); - UpdateRegistrationStateProperties(WhichServiceWorker::INSTALLING_WORKER, - TransitionToNextState); + + UpdateRegistrationState(); + mWaitingWorker->UpdateState(ServiceWorkerState::Installed); mWaitingWorker->UpdateInstalledTime(); - mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker); - NotifyChromeRegistrationListeners(); RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); if (!swm) { // browser shutdown began return; } swm->StoreRegistration(mPrincipal, this); @@ -646,21 +621,20 @@ ServiceWorkerRegistrationInfo::SetActive mActiveWorker->UpdateRedundantTime(); } // The active worker is being overriden due to initial load or // another process activating a worker. Move straight to the // Activated state. mActiveWorker = aServiceWorker; mActiveWorker->SetActivateStateUncheckedWithoutEvent(ServiceWorkerState::Activated); + // We don't need to update activated time when we load registration from // registrar. - UpdateRegistrationStateProperties(WhichServiceWorker::ACTIVE_WORKER, Invalidate); - - mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker); + UpdateRegistrationState(); NotifyChromeRegistrationListeners(); } void ServiceWorkerRegistrationInfo::TransitionWaitingToActive() { MOZ_ASSERT(NS_IsMainThread()); @@ -670,21 +644,30 @@ ServiceWorkerRegistrationInfo::Transitio MOZ_ASSERT(mWaitingWorker->CacheName() != mActiveWorker->CacheName()); mActiveWorker->UpdateState(ServiceWorkerState::Redundant); mActiveWorker->UpdateRedundantTime(); } // We are transitioning from waiting to active normally, so go to // the activating state. mActiveWorker = mWaitingWorker.forget(); - UpdateRegistrationStateProperties(WhichServiceWorker::WAITING_WORKER, - TransitionToNextState); + + UpdateRegistrationState(); + mActiveWorker->UpdateState(ServiceWorkerState::Activating); - mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker); + nsCOMPtr<nsIRunnable> r = + NS_NewRunnableFunction("ServiceWorkerRegistrationInfo::TransitionWaitingToActive", + [] { + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (swm) { + swm->CheckPendingReadyPromises(); + } + }); + MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget())); NotifyChromeRegistrationListeners(); } bool ServiceWorkerRegistrationInfo::IsIdle() const { return !mActiveWorker || mActiveWorker->WorkerPrivate()->IsIdle(); @@ -696,16 +679,17 @@ ServiceWorkerRegistrationInfo::GetUpdate return mDescriptor.UpdateViaCache(); } void ServiceWorkerRegistrationInfo::SetUpdateViaCache( ServiceWorkerUpdateViaCache aUpdateViaCache) { mDescriptor.SetUpdateViaCache(aUpdateViaCache); + UpdateRegistrationState(); } int64_t ServiceWorkerRegistrationInfo::GetLastUpdateTime() const { return mLastUpdateTime; }
--- a/dom/serviceworkers/ServiceWorkerRegistrationInfo.h +++ b/dom/serviceworkers/ServiceWorkerRegistrationInfo.h @@ -3,17 +3,16 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_serviceworkerregistrationinfo_h #define mozilla_dom_serviceworkerregistrationinfo_h #include "mozilla/dom/ServiceWorkerInfo.h" -#include "mozilla/dom/ServiceWorkerCommon.h" #include "mozilla/dom/ServiceWorkerRegistrationBinding.h" #include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h" #include "nsProxyRelease.h" namespace mozilla { namespace dom { class ServiceWorkerRegistrationInfo final @@ -208,30 +207,21 @@ public: void SetLastUpdateTime(const int64_t aTime); const ServiceWorkerRegistrationDescriptor& Descriptor() const; private: - enum TransitionType { - TransitionToNextState = 0, - Invalidate - }; - - // Queued as a runnable from UpdateRegistrationStateProperties. - void - AsyncUpdateRegistrationStateProperties(WhichServiceWorker aWorker, TransitionType aType); - // Roughly equivalent to [[Update Registration State algorithm]]. Make sure // this is called *before* updating SW instances' state, otherwise they // may get CC-ed. void - UpdateRegistrationStateProperties(WhichServiceWorker aWorker, TransitionType aType); + UpdateRegistrationState(); // Used by devtools to track changes to the properties of *nsIServiceWorkerRegistrationInfo*. // Note, this doesn't necessarily need to be in sync with the DOM registration objects, but // it does need to be called in the same task that changed |mInstallingWorker|, // |mWaitingWorker| or |mActiveWorker|. void NotifyChromeRegistrationListeners(); };
new file mode 100644 --- /dev/null +++ b/dom/serviceworkers/ServiceWorkerRegistrationListener.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_ServiceWorkerRegistrationListener_h +#define mozilla_dom_ServiceWorkerRegistrationListener_h + +namespace mozilla { +namespace dom { + +class ServiceWorkerRegistrationDescriptor; + +// Used by ServiceWorkerManager to notify ServiceWorkerRegistrations of +// updatefound event and invalidating ServiceWorker instances. +class ServiceWorkerRegistrationListener +{ +public: + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + + virtual void + UpdateFound() = 0; + + virtual void + UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor) = 0; + + virtual void + RegistrationRemoved() = 0; + + virtual void + GetScope(nsAString& aScope) const = 0; + + virtual bool + MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) = 0; +}; + + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_ServiceWorkerRegistrationListener_h */
--- a/dom/serviceworkers/moz.build +++ b/dom/serviceworkers/moz.build @@ -5,17 +5,16 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. with Files("**"): BUG_COMPONENT = ("Core", "DOM: Service Workers") # Public stuff. EXPORTS.mozilla.dom += [ 'ServiceWorker.h', - 'ServiceWorkerCommon.h', 'ServiceWorkerContainer.h', 'ServiceWorkerDescriptor.h', 'ServiceWorkerEvents.h', 'ServiceWorkerInfo.h', 'ServiceWorkerInterceptController.h', 'ServiceWorkerIPCUtils.h', 'ServiceWorkerManager.h', 'ServiceWorkerManagerChild.h', @@ -40,16 +39,17 @@ UNIFIED_SOURCES += [ 'ServiceWorkerManagerChild.cpp', 'ServiceWorkerManagerParent.cpp', 'ServiceWorkerManagerService.cpp', 'ServiceWorkerPrivate.cpp', 'ServiceWorkerRegisterJob.cpp', 'ServiceWorkerRegistrar.cpp', 'ServiceWorkerRegistration.cpp', 'ServiceWorkerRegistrationDescriptor.cpp', + 'ServiceWorkerRegistrationImpl.cpp', 'ServiceWorkerRegistrationInfo.cpp', 'ServiceWorkerScriptCache.cpp', 'ServiceWorkerUnregisterJob.cpp', 'ServiceWorkerUpdateJob.cpp', 'ServiceWorkerUpdaterChild.cpp', 'ServiceWorkerUpdaterParent.cpp', 'ServiceWorkerUtils.cpp', ]
--- a/dom/serviceworkers/test/test_bug1408734.html +++ b/dom/serviceworkers/test/test_bug1408734.html @@ -43,23 +43,16 @@ add_task(async () => { registration = await navigator.serviceWorker.getRegistration("./"); ok(registration, "should get the registration under scope './'"); // call unregister() await registration.unregister(); // access registration.updateViaCache to trigger the bug // we really care that we don't crash. In the future we will fix - // updateViaCache to be accessible after unregister(). - try { - if (registration.updateViaCache) { - ok(false, - "Expected InvalidStateError when accessing registration.updateViaCache after unregister()"); - } - } catch (err) { - is(err.name, "InvalidStateError", "Expected InvalidStateError."); - } + is(registration.updateViaCache, "imports", + "registration.updateViaCache should work after unregister()"); }); </script> </pre> </body> </html>
--- a/dom/webidl/TreeBoxObject.webidl +++ b/dom/webidl/TreeBoxObject.webidl @@ -22,17 +22,17 @@ interface TreeBoxObject : BoxObject { */ readonly attribute TreeColumns? columns; /** * The view that backs the tree and that supplies it with its data. * It is dynamically settable, either using a view attribute on the * tree tag or by setting this attribute to a new value. */ - [SetterThrows] + [SetterThrows, NeedsCallerType] attribute MozTreeView? view; /** * Whether or not we are currently focused. */ attribute boolean focused; /**
--- a/dom/workers/WorkerLoadInfo.cpp +++ b/dom/workers/WorkerLoadInfo.cpp @@ -141,16 +141,17 @@ WorkerLoadInfo::StealFrom(WorkerLoadInfo MOZ_ASSERT(!mPrincipalInfo); mPrincipalInfo = aOther.mPrincipalInfo.forget(); mDomain = aOther.mDomain; mOrigin = aOther.mOrigin; mServiceWorkerCacheName = aOther.mServiceWorkerCacheName; mServiceWorkerDescriptor = aOther.mServiceWorkerDescriptor; + mServiceWorkerRegistrationDescriptor = aOther.mServiceWorkerRegistrationDescriptor; mLoadFlags = aOther.mLoadFlags; mWindowID = aOther.mWindowID; mReferrerPolicy = aOther.mReferrerPolicy; mFromWindow = aOther.mFromWindow; mEvalAllowed = aOther.mEvalAllowed; mReportCSPViolations = aOther.mReportCSPViolations; mXHRParamsAllowed = aOther.mXHRParamsAllowed; mPrincipalIsSystem = aOther.mPrincipalIsSystem;
--- a/dom/workers/WorkerLoadInfo.h +++ b/dom/workers/WorkerLoadInfo.h @@ -3,16 +3,17 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_workers_WorkerLoadInfo_h #define mozilla_dom_workers_WorkerLoadInfo_h #include "mozilla/dom/ChannelInfo.h" +#include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h" #include "mozilla/dom/WorkerCommon.h" #include "mozilla/net/ReferrerPolicy.h" #include "nsIInterfaceRequestor.h" #include "nsILoadContext.h" #include "nsIRequest.h" #include "nsISupportsImpl.h" #include "nsIWeakReferenceUtils.h" @@ -87,16 +88,17 @@ struct WorkerLoadInfo RefPtr<InterfaceRequestor> mInterfaceRequestor; nsAutoPtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo; nsCString mDomain; nsString mOrigin; // Derived from mPrincipal; can be used on worker thread. nsString mServiceWorkerCacheName; Maybe<ServiceWorkerDescriptor> mServiceWorkerDescriptor; + Maybe<ServiceWorkerRegistrationDescriptor> mServiceWorkerRegistrationDescriptor; Maybe<ServiceWorkerDescriptor> mParentController; ChannelInfo mChannelInfo; nsLoadFlags mLoadFlags; uint64_t mWindowID;
--- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -5135,17 +5135,19 @@ WorkerPrivate::GetOrCreateGlobalScope(JS { AssertIsOnWorkerThread(); if (!mScope) { RefPtr<WorkerGlobalScope> globalScope; if (IsSharedWorker()) { globalScope = new SharedWorkerGlobalScope(this, WorkerName()); } else if (IsServiceWorker()) { - globalScope = new ServiceWorkerGlobalScope(this, ServiceWorkerScope()); + globalScope = + new ServiceWorkerGlobalScope(this, + GetServiceWorkerRegistrationDescriptor()); } else { globalScope = new DedicatedWorkerGlobalScope(this, WorkerName()); } JS::Rooted<JSObject*> global(aCx); NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr); JSAutoCompartment ac(aCx, global);
--- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -837,16 +837,24 @@ public: const ServiceWorkerDescriptor& GetServiceWorkerDescriptor() const { MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker()); MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mServiceWorkerDescriptor.isSome()); return mLoadInfo.mServiceWorkerDescriptor.ref(); } + const ServiceWorkerRegistrationDescriptor& + GetServiceWorkerRegistrationDescriptor() const + { + MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker()); + MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mServiceWorkerRegistrationDescriptor.isSome()); + return mLoadInfo.mServiceWorkerRegistrationDescriptor.ref(); + } + void UpdateServiceWorkerState(ServiceWorkerState aState) { MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker()); MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mServiceWorkerDescriptor.isSome()); return mLoadInfo.mServiceWorkerDescriptor.ref().SetState(aState); }
--- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -628,19 +628,25 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(Servi mClients, mRegistration) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerGlobalScope) NS_INTERFACE_MAP_END_INHERITING(WorkerGlobalScope) NS_IMPL_ADDREF_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope) NS_IMPL_RELEASE_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope) ServiceWorkerGlobalScope::ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, - const nsACString& aScope) - : WorkerGlobalScope(aWorkerPrivate), - mScope(NS_ConvertUTF8toUTF16(aScope)) + const ServiceWorkerRegistrationDescriptor& aRegistrationDescriptor) + : WorkerGlobalScope(aWorkerPrivate) + , mScope(NS_ConvertUTF8toUTF16(aRegistrationDescriptor.Scope())) + + // Eagerly create the registration because we will need to receive updates + // about the state of the registration. We can't wait until first access + // to start receiving these. + , mRegistration(ServiceWorkerRegistration::CreateForWorker(aWorkerPrivate, + aRegistrationDescriptor)) { } ServiceWorkerGlobalScope::~ServiceWorkerGlobalScope() { } bool @@ -667,21 +673,16 @@ ServiceWorkerGlobalScope::GetClients() RefPtr<Clients> ref = mClients; return ref.forget(); } ServiceWorkerRegistration* ServiceWorkerGlobalScope::Registration() { - if (!mRegistration) { - mRegistration = - ServiceWorkerRegistration::CreateForWorker(mWorkerPrivate, mScope); - } - return mRegistration; } EventHandlerNonNull* ServiceWorkerGlobalScope::GetOnfetch() { MOZ_ASSERT(mWorkerPrivate); mWorkerPrivate->AssertIsOnWorkerThread();
--- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -298,17 +298,18 @@ class ServiceWorkerGlobalScope final : p public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope) IMPL_EVENT_HANDLER(notificationclick) IMPL_EVENT_HANDLER(notificationclose) - ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, const nsACString& aScope); + ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, + const ServiceWorkerRegistrationDescriptor& aRegistrationDescriptor); virtual bool WrapGlobalObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector) override; void GetScope(nsString& aScope) const {
deleted file mode 100644 --- a/gfx/cairo/libpixman/src/Makefile.in +++ /dev/null @@ -1,10 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -include $(topsrcdir)/config/rules.mk - -# The ARM asm functions here don't appreciate being called by functions -# compiled with -mapcs-frame. See bug 832752. -CXXFLAGS := $(filter-out -mapcs-frame,$(CXXFLAGS)) -CFLAGS := $(filter-out -mapcs-frame,$(CFLAGS))
--- a/gfx/layers/ipc/ImageBridgeChild.cpp +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -534,19 +534,18 @@ bool ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint, uint32_t aNamespace) { MOZ_ASSERT(NS_IsMainThread()); gfxPlatform::GetPlatform(); if (!sImageBridgeChildThread) { sImageBridgeChildThread = new Thread("ImageBridgeChild"); - if (!sImageBridgeChildThread->Start()) { - return false; - } + bool success = sImageBridgeChildThread->Start(); + MOZ_RELEASE_ASSERT(success, "Failed to start ImageBridgeChild thread!"); } RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace); RefPtr<Runnable> runnable = NewRunnableMethod<Endpoint<PImageBridgeChild>&&>( "layers::ImageBridgeChild::Bind", child, &ImageBridgeChild::Bind,
--- a/gfx/vr/ipc/VRManagerChild.cpp +++ b/gfx/vr/ipc/VRManagerChild.cpp @@ -85,17 +85,17 @@ VRManagerChild::IsCreated() /* static */ bool VRManagerChild::InitForContent(Endpoint<PVRManagerChild>&& aEndpoint) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!sVRManagerChildSingleton); RefPtr<VRManagerChild> child(new VRManagerChild()); if (!aEndpoint.Bind(child)) { - MOZ_CRASH("Couldn't Open() Compositor channel."); + return false; } sVRManagerChildSingleton = child; return true; } /* static */ bool VRManagerChild::ReinitForContent(Endpoint<PVRManagerChild>&& aEndpoint) {
--- a/gfx/webrender_bindings/RenderThread.cpp +++ b/gfx/webrender_bindings/RenderThread.cpp @@ -234,31 +234,31 @@ NotifyDidRender(layers::CompositorBridge while (wr_pipeline_info_next_removed_pipeline(aInfo, &pipeline)) { aBridge->NotifyPipelineRemoved(pipeline); } wr_pipeline_info_delete(aInfo); } void -RenderThread::UpdateAndRender(wr::WindowId aWindowId) +RenderThread::UpdateAndRender(wr::WindowId aWindowId, bool aReadback) { AUTO_PROFILER_TRACING("Paint", "Composite"); MOZ_ASSERT(IsInRenderThread()); auto it = mRenderers.find(aWindowId); MOZ_ASSERT(it != mRenderers.end()); if (it == mRenderers.end()) { return; } auto& renderer = it->second; TimeStamp start = TimeStamp::Now(); - bool ret = renderer->UpdateAndRender(); + bool ret = renderer->UpdateAndRender(aReadback); if (!ret) { // Render did not happen, do not call NotifyDidRender. return; } TimeStamp end = TimeStamp::Now(); auto info = renderer->FlushPipelineInfo();
--- a/gfx/webrender_bindings/RenderThread.h +++ b/gfx/webrender_bindings/RenderThread.h @@ -122,17 +122,17 @@ public: /// Automatically forwarded to the render thread. void PipelineSizeChanged(wr::WindowId aWindowId, uint64_t aPipelineId, float aWidth, float aHeight); /// Automatically forwarded to the render thread. void RunEvent(wr::WindowId aWindowId, UniquePtr<RendererEvent> aCallBack); /// Can only be called from the render thread. - void UpdateAndRender(wr::WindowId aWindowId); + void UpdateAndRender(wr::WindowId aWindowId, bool aReadback = false); void Pause(wr::WindowId aWindowId); bool Resume(wr::WindowId aWindowId); /// Can be called from any thread. void RegisterExternalImage(uint64_t aExternalImageId, already_AddRefed<RenderTextureHost> aTexture); /// Can be called from any thread.
--- a/gfx/webrender_bindings/RendererOGL.cpp +++ b/gfx/webrender_bindings/RendererOGL.cpp @@ -83,19 +83,23 @@ RendererOGL::GetExternalImageHandler() void RendererOGL::Update() { wr_renderer_update(mRenderer); } bool -RendererOGL::UpdateAndRender() +RendererOGL::UpdateAndRender(bool aReadback) { uint32_t flags = gfx::gfxVars::WebRenderDebugFlags(); + // Disable debug flags during readback + if (aReadback) { + flags = 0; + } if (mDebugFlags.mBits != flags) { mDebugFlags.mBits = flags; wr_renderer_set_debug_flags(mRenderer, mDebugFlags); } mozilla::widget::WidgetRenderingContext widgetContext;
--- a/gfx/webrender_bindings/RendererOGL.h +++ b/gfx/webrender_bindings/RendererOGL.h @@ -48,17 +48,17 @@ class RendererOGL public: wr::WrExternalImageHandler GetExternalImageHandler(); /// This can be called on the render thread only. void Update(); /// This can be called on the render thread only. - bool UpdateAndRender(); + bool UpdateAndRender(bool aReadback); /// This can be called on the render thread only. bool RenderToTarget(gfx::DrawTarget& aTarget); /// This can be called on the render thread only. void SetProfilerEnabled(bool aEnabled); /// This can be called on the render thread only.
--- a/gfx/webrender_bindings/WebRenderAPI.cpp +++ b/gfx/webrender_bindings/WebRenderAPI.cpp @@ -395,17 +395,17 @@ WebRenderAPI::Readback(gfx::IntSize size ~Readback() { MOZ_COUNT_DTOR(Readback); } virtual void Run(RenderThread& aRenderThread, WindowId aWindowId) override { - aRenderThread.UpdateAndRender(aWindowId); + aRenderThread.UpdateAndRender(aWindowId, /* aReadback */ true); wr_renderer_readback(aRenderThread.GetRenderer(aWindowId)->GetRenderer(), mSize.width, mSize.height, mBuffer, mBufferSize); layers::AutoCompleteTask complete(mTask); } layers::SynchronousTask* mTask; gfx::IntSize mSize; uint8_t *mBuffer;
--- a/js/public/Conversions.h +++ b/js/public/Conversions.h @@ -16,18 +16,16 @@ #include <math.h> #include "jspubtd.h" #include "js/RootingAPI.h" #include "js/Value.h" -struct JSContext; - namespace js { /* DO NOT CALL THIS. Use JS::ToBoolean. */ extern JS_PUBLIC_API(bool) ToBooleanSlow(JS::HandleValue v); /* DO NOT CALL THIS. Use JS::ToNumber. */ extern JS_PUBLIC_API(bool)
--- a/js/public/Date.h +++ b/js/public/Date.h @@ -30,18 +30,16 @@ */ #include "mozilla/FloatingPoint.h" #include "mozilla/MathAlgorithms.h" #include "js/Conversions.h" #include "js/Value.h" -struct JSContext; - namespace JS { /** * Re-query the system to determine the current time zone adjustment from UTC, * including any component due to DST. If the time zone has changed, this will * cause all Date object non-UTC methods and formatting functions to produce * appropriately adjusted results. *
--- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -10,25 +10,21 @@ #ifndef js_GCAPI_h #define js_GCAPI_h #include "mozilla/TimeStamp.h" #include "mozilla/Vector.h" #include "js/GCAnnotations.h" +#include "js/TypeDecls.h" #include "js/UniquePtr.h" #include "js/Utility.h" -struct JSCompartment; -struct JSContext; struct JSFreeOp; -class JSObject; -struct JSRuntime; -class JSString; #ifdef JS_BROKEN_GCC_ATTRIBUTE_WARNING #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wattributes" #endif // JS_BROKEN_GCC_ATTRIBUTE_WARNING class JS_PUBLIC_API(JSTracer); @@ -321,18 +317,16 @@ typedef void * can be called off the main thread. */ struct JSStringFinalizer { void (*finalize)(const JSStringFinalizer* fin, char16_t* chars); }; namespace JS { -struct Zone; - #define GCREASONS(D) \ /* Reasons internal to the JS engine */ \ D(API) \ D(EAGER_ALLOC_TRIGGER) \ D(DESTROY_RUNTIME) \ D(ROOTS_REMOVED) \ D(LAST_DITCH) \ D(TOO_MUCH_MALLOC) \
--- a/js/public/GCPolicyAPI.h +++ b/js/public/GCPolicyAPI.h @@ -40,16 +40,17 @@ #ifndef GCPolicyAPI_h #define GCPolicyAPI_h #include "mozilla/Maybe.h" #include "mozilla/UniquePtr.h" #include "js/TraceKind.h" #include "js/TracingAPI.h" +#include "js/TypeDecls.h" // Expand the given macro D for each public GC pointer. #define FOR_EACH_PUBLIC_GC_POINTER_TYPE(D) \ D(JS::Symbol*) \ D(JSAtom*) \ D(JSFunction*) \ D(JSObject*) \ D(JSScript*) \ @@ -58,25 +59,16 @@ // Expand the given macro D for each public tagged GC pointer type. #define FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(D) \ D(JS::Value) \ D(jsid) #define FOR_EACH_PUBLIC_AGGREGATE_GC_POINTER_TYPE(D) \ D(JSPropertyDescriptor) -class JSAtom; -class JSFunction; -class JSObject; -class JSScript; -class JSString; -namespace JS { -class Symbol; -} - namespace JS { // Defines a policy for container types with non-GC, i.e. C storage. This // policy dispatches to the underlying struct for GC interactions. template <typename T> struct StructGCPolicy { static T initial() {
--- a/js/public/HeapAPI.h +++ b/js/public/HeapAPI.h @@ -19,16 +19,22 @@ namespace js { JS_FRIEND_API(bool) CurrentThreadCanAccessZone(JS::Zone* zone); namespace gc { struct Cell; +/* + * The low bit is set so this should never equal a normal pointer, and the high + * bit is set so this should never equal the upper 32 bits of a 64-bit pointer. + */ +const uint32_t Relocated = uintptr_t(0xbad0bad1); + const size_t ArenaShift = 12; const size_t ArenaSize = size_t(1) << ArenaShift; const size_t ArenaMask = ArenaSize - 1; #ifdef JS_GC_SMALL_CHUNK_SIZE const size_t ChunkShift = 18; #else const size_t ChunkShift = 20; @@ -56,17 +62,17 @@ const size_t ChunkMarkBitmapOffset = 258 const size_t ChunkMarkBitmapBits = 31744; #else const size_t ChunkMarkBitmapOffset = 1032352; const size_t ChunkMarkBitmapBits = 129024; #endif const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*); const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t); const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize; -const size_t ChunkStoreBufferOffset = ChunkLocationOffset + sizeof(uint64_t); +const size_t ChunkStoreBufferOffset = ChunkSize - ChunkTrailerSize + sizeof(uint64_t); const size_t ArenaZoneOffset = sizeof(size_t); const size_t ArenaHeaderSize = sizeof(size_t) + 2 * sizeof(uintptr_t) + sizeof(size_t) + sizeof(uintptr_t); /* * Live objects are marked black or gray. Everything reachable from a JS root is * marked black. Objects marked gray are eligible for cycle collection. * @@ -455,20 +461,25 @@ namespace JS { static MOZ_ALWAYS_INLINE Zone* GetTenuredGCThingZone(GCCellPtr thing) { MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell())); return js::gc::detail::GetGCThingZone(thing.unsafeAsUIntPtr()); } +extern JS_PUBLIC_API(Zone*) +GetNurseryStringZone(JSString* str); + static MOZ_ALWAYS_INLINE Zone* GetStringZone(JSString* str) { - return js::gc::detail::GetGCThingZone(uintptr_t(str)); + if (!js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(str))) + return js::gc::detail::GetGCThingZone(reinterpret_cast<uintptr_t>(str)); + return GetNurseryStringZone(str); } extern JS_PUBLIC_API(Zone*) GetObjectZone(JSObject* obj); extern JS_PUBLIC_API(Zone*) GetValueZone(const Value& value); @@ -478,16 +489,22 @@ GCThingIsMarkedGray(GCCellPtr thing) if (thing.mayBeOwnedByOtherRuntime()) return false; return js::gc::detail::CellIsMarkedGrayIfKnown(thing.asCell()); } extern JS_PUBLIC_API(JS::TraceKind) GCThingTraceKind(void* thing); +extern JS_PUBLIC_API(void) +EnableNurseryStrings(JSContext* cx); + +extern JS_PUBLIC_API(void) +DisableNurseryStrings(JSContext* cx); + /* * Returns true when writes to GC thing pointers (and reads from weak pointers) * must call an incremental barrier. This is generally only true when running * mutator code in-between GC slices. At other times, the barrier may be elided * for performance. */ extern JS_PUBLIC_API(bool) IsIncrementalBarrierNeeded(JSContext* cx);
--- a/js/public/ProfilingFrameIterator.h +++ b/js/public/ProfilingFrameIterator.h @@ -10,20 +10,16 @@ #include "mozilla/Attributes.h" #include "mozilla/Maybe.h" #include "jsbytecode.h" #include "js/GCAPI.h" #include "js/TypeDecls.h" #include "js/Utility.h" -struct JSContext; -struct JSRuntime; -class JSScript; - namespace js { class Activation; namespace jit { class JitActivation; class JSJitProfilingFrameIterator; class JitcodeGlobalEntry; } // namespace jit namespace wasm {
--- a/js/public/ProfilingStack.h +++ b/js/public/ProfilingStack.h @@ -10,19 +10,17 @@ #include <algorithm> #include <stdint.h> #include "jsbytecode.h" #include "jstypes.h" #include "js/TypeDecls.h" #include "js/Utility.h" -struct JSRuntime; class JSTracer; - class PseudoStack; // This file defines the classes PseudoStack and ProfileEntry. // The PseudoStack manages an array of ProfileEntries. // Usage: // // PseudoStack* pseudoStack = ...; //
--- a/js/public/Result.h +++ b/js/public/Result.h @@ -113,18 +113,16 @@ * exist. */ #ifndef js_Result_h #define js_Result_h #include "mozilla/Result.h" -struct JSContext; - /** * Evaluate the boolean expression expr. If it's true, do nothing. * If it's false, return an error result. */ #define JS_TRY_BOOL_TO_RESULT(cx, expr) \ do { \ bool ok_ = (expr); \ if (!ok_) \
--- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -195,31 +195,32 @@ namespace JS { template <typename T> class Rooted; template <typename T> class PersistentRooted; /* This is exposing internal state of the GC for inlining purposes. */ JS_FRIEND_API(bool) isGCEnabled(); JS_FRIEND_API(void) HeapObjectPostBarrier(JSObject** objp, JSObject* prev, JSObject* next); +JS_FRIEND_API(void) HeapStringPostBarrier(JSString** objp, JSString* prev, JSString* next); #ifdef JS_DEBUG /** * For generational GC, assert that an object is in the tenured generation as * opposed to being in the nursery. */ extern JS_FRIEND_API(void) AssertGCThingMustBeTenured(JSObject* obj); extern JS_FRIEND_API(void) -AssertGCThingIsNotAnObjectSubclass(js::gc::Cell* cell); +AssertGCThingIsNotNurseryAllocable(js::gc::Cell* cell); #else inline void AssertGCThingMustBeTenured(JSObject* obj) {} inline void -AssertGCThingIsNotAnObjectSubclass(js::gc::Cell* cell) {} +AssertGCThingIsNotNurseryAllocable(js::gc::Cell* cell) {} #endif /** * The Heap<T> class is a heap-stored reference to a JS GC thing. All members of * heap classes that refer to GC things should use Heap<T> (or possibly * TenuredHeap<T>, described below). * * Heap<T> is an abstraction that hides some of the complexity required to @@ -620,17 +621,17 @@ struct BarrierMethods<T*> static gc::Cell* asGCThingOrNull(T* v) { if (!v) return nullptr; MOZ_ASSERT(uintptr_t(v) > 32); return reinterpret_cast<gc::Cell*>(v); } static void postBarrier(T** vp, T* prev, T* next) { if (next) - JS::AssertGCThingIsNotAnObjectSubclass(reinterpret_cast<js::gc::Cell*>(next)); + JS::AssertGCThingIsNotNurseryAllocable(reinterpret_cast<js::gc::Cell*>(next)); } static void exposeToJS(T* t) { if (t) js::gc::ExposeGCThingToActiveJS(JS::GCCellPtr(t)); } }; template <> @@ -668,16 +669,31 @@ struct BarrierMethods<JSFunction*> reinterpret_cast<JSObject*>(next)); } static void exposeToJS(JSFunction* fun) { if (fun) JS::ExposeObjectToActiveJS(reinterpret_cast<JSObject*>(fun)); } }; +template <> +struct BarrierMethods<JSString*> +{ + static JSString* initial() { return nullptr; } + static gc::Cell* asGCThingOrNull(JSString* v) { + if (!v) + return nullptr; + MOZ_ASSERT(uintptr_t(v) > 32); + return reinterpret_cast<gc::Cell*>(v); + } + static void postBarrier(JSString** vp, JSString* prev, JSString* next) { + JS::HeapStringPostBarrier(vp, prev, next); + } +}; + // Provide hash codes for Cell kinds that may be relocated and, thus, not have // a stable address to use as the base for a hash code. Instead of the address, // this hasher uses Cell::getUniqueId to provide exact matches and as a base // for generating hash codes. // // Note: this hasher, like PointerHasher can "hash" a nullptr. While a nullptr // would not likely be a useful key, there are some cases where being able to // hash a nullptr is useful, either on purpose or because of bugs:
--- a/js/public/StructuredClone.h +++ b/js/public/StructuredClone.h @@ -15,17 +15,16 @@ #include "jstypes.h" #include "js/RootingAPI.h" #include "js/TypeDecls.h" #include "js/Value.h" #include "js/Vector.h" -struct JSRuntime; struct JSStructuredCloneReader; struct JSStructuredCloneWriter; // API for the HTML5 internal structured cloning algorithm. namespace JS { enum class StructuredCloneScope : uint32_t {
--- a/js/public/TypeDecls.h +++ b/js/public/TypeDecls.h @@ -17,33 +17,39 @@ #ifndef js_TypeDecls_h #define js_TypeDecls_h #include <stddef.h> #include <stdint.h> #include "js-config.h" +class JSAtom; +struct JSCompartment; struct JSContext; class JSFunction; class JSObject; +struct JSRuntime; class JSScript; class JSString; class JSAddonId; struct JSFreeOp; struct jsid; namespace JS { typedef unsigned char Latin1Char; class Symbol; class Value; class Realm; +struct Runtime; +struct Zone; + template <typename T> class Handle; template <typename T> class MutableHandle; template <typename T> class Rooted; template <typename T> class PersistentRooted; typedef Handle<JSFunction*> HandleFunction; typedef Handle<jsid> HandleId; typedef Handle<JSObject*> HandleObject;
--- a/js/public/UbiNode.h +++ b/js/public/UbiNode.h @@ -158,18 +158,16 @@ // When ubi::Nodes refer to nodes deserialized from a heap snapshot, analyses // must be even more careful: since snapshots often come from potentially // compromised e10s content processes, even properties normally guaranteed by // the platform (the proper linking of DOM nodes, for example) might be // corrupted. While it is the deserializer's responsibility to check the basic // structure of the snapshot file, the analyses should be prepared for ubi::Node // graphs constructed from snapshots to be even more bizarre. -class JSAtom; - namespace JS { namespace ubi { class Edge; class EdgeRange; class StackFrame; } // namespace ubi
--- a/js/rust/src/conversions.rs +++ b/js/rust/src/conversions.rs @@ -629,53 +629,56 @@ impl<T: ToJSValConvertible> ToJSValConve JSPROP_ENUMERATE as _ )); } rval.set(ObjectValue(js_array.handle().get())); } } -/// Rooting guard for the iterator field of ForOfIterator. +/// Rooting guard for the iterator and nextMethod fields of ForOfIterator. /// Behaves like RootedGuard (roots on creation, unroots on drop), /// but borrows and allows access to the whole ForOfIterator, so /// that methods on ForOfIterator can still be used through it. struct ForOfIteratorGuard<'a> { root: &'a mut JS::ForOfIterator } impl<'a> ForOfIteratorGuard<'a> { fn new(cx: *mut JSContext, root: &'a mut JS::ForOfIterator) -> Self { unsafe { root.iterator.register_with_root_lists(cx); + root.nextMethod.register_with_root_lists(cx); } ForOfIteratorGuard { root: root } } } impl<'a> Drop for ForOfIteratorGuard<'a> { fn drop(&mut self) { unsafe { + self.root.nextMethod.remove_from_root_stack(); self.root.iterator.remove_from_root_stack(); } } } impl<C: Clone, T: FromJSValConvertible<Config=C>> FromJSValConvertible for Vec<T> { type Config = C; unsafe fn from_jsval(cx: *mut JSContext, value: JS::HandleValue, option: C) -> Result<ConversionResult<Vec<T>>, ()> { let mut iterator = JS::ForOfIterator { cx_: cx, iterator: JS::RootedObject::new_unrooted(), + nextMethod: JS::RootedValue::new_unrooted(), index: ::std::u32::MAX, // NOT_ARRAY }; let iterator = ForOfIteratorGuard::new(cx, &mut iterator); let iterator = &mut *iterator.root; if !iterator.init(value, JS::ForOfIterator_NonIterableBehavior::AllowNonIterable) { return Err(()) }
--- a/js/src/NamespaceImports.h +++ b/js/src/NamespaceImports.h @@ -43,17 +43,16 @@ template<typename T> class AutoHashSetRo class MOZ_STACK_CLASS SourceBufferHolder; class HandleValueArray; class ObjectOpResult; class PropertyResult; -class Symbol; enum class SymbolCode: uint32_t; } // namespace JS // Do the importing. namespace js { using JS::Value;
--- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -690,27 +690,24 @@ function ArrayIncludes(searchElement, fr k++; } // Step 11. return false; } // ES6 draft specification, section 22.1.5.1, version 2013-09-05. -function CreateArrayIteratorAt(obj, kind, n) { +function CreateArrayIterator(obj, kind) { var iteratedObject = ToObject(obj); var iterator = NewArrayIterator(); UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_TARGET, iteratedObject); - UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_NEXT_INDEX, n); + UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_NEXT_INDEX, 0); UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_ITEM_KIND, kind); return iterator; } -function CreateArrayIterator(obj, kind) { - return CreateArrayIteratorAt(obj, kind, 0); -} // ES6, 22.1.5.2.1 // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%.next function ArrayIteratorNext() { // Step 1-3. if (!IsObject(this) || !IsArrayIterator(this)) { return callFunction(CallArrayIteratorMethodIfWrapped, this, "ArrayIteratorNext"); @@ -771,20 +768,16 @@ function ArrayIteratorNext() { } // Step 12. assert(itemKind === ITEM_KIND_KEY, itemKind); result.value = index; return result; } -function ArrayValuesAt(n) { - return CreateArrayIteratorAt(this, ITEM_KIND_VALUE, n); -} - function ArrayValues() { return CreateArrayIterator(this, ITEM_KIND_VALUE); } _SetCanonicalName(ArrayValues, "values"); function ArrayEntries() { return CreateArrayIterator(this, ITEM_KIND_KEY_AND_VALUE); }
--- a/js/src/builtin/AtomicsObject.cpp +++ b/js/src/builtin/AtomicsObject.cpp @@ -61,17 +61,17 @@ #include "jit/AtomicOperations.h" #include "jit/InlinableNatives.h" #include "js/Class.h" #include "vm/GlobalObject.h" #include "vm/Time.h" #include "vm/TypedArrayObject.h" #include "wasm/WasmInstance.h" -#include "jsobjinlines.h" +#include "vm/JSObject-inl.h" using namespace js; const Class AtomicsObject::class_ = { "Atomics", JSCLASS_HAS_CACHED_PROTO(JSProto_Atomics) };
--- a/js/src/builtin/AtomicsObject.h +++ b/js/src/builtin/AtomicsObject.h @@ -5,19 +5,18 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef builtin_AtomicsObject_h #define builtin_AtomicsObject_h #include "mozilla/Maybe.h" #include "mozilla/TimeStamp.h" -#include "jsobj.h" - #include "threading/ConditionVariable.h" +#include "vm/JSObject.h" #include "vm/MutexIDs.h" #include "vm/NativeObject.h" namespace js { class AtomicsObject : public NativeObject { public:
--- a/js/src/builtin/DataViewObject.cpp +++ b/js/src/builtin/DataViewObject.cpp @@ -8,29 +8,29 @@ #include "mozilla/Alignment.h" #include "mozilla/Casting.h" #include <string.h> #include "jsapi.h" #include "jsarray.h" -#include "jscntxt.h" #include "jsnum.h" -#include "jsobj.h" #ifdef XP_WIN # include "jswin.h" #endif #include "jswrapper.h" #include "jit/AtomicOperations.h" #include "js/Conversions.h" #include "vm/ArrayBufferObject.h" #include "vm/GlobalObject.h" #include "vm/Interpreter.h" +#include "vm/JSContext.h" +#include "vm/JSObject.h" #include "vm/SharedMem.h" #include "vm/WrapperObject.h" #include "gc/Nursery-inl.h" #include "gc/StoreBuffer-inl.h" #include "vm/ArrayBufferObject-inl.h" #include "vm/NativeObject-inl.h"
--- a/js/src/builtin/DataViewObject.h +++ b/js/src/builtin/DataViewObject.h @@ -4,21 +4,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef vm_DataViewObject_h #define vm_DataViewObject_h #include "mozilla/Attributes.h" -#include "jsobj.h" - #include "gc/Barrier.h" #include "js/Class.h" #include "vm/ArrayBufferObject.h" +#include "vm/JSObject.h" #include "vm/SharedArrayObject.h" #include "vm/TypedArrayObject.h" namespace js { // In the DataViewObject, the private slot contains a raw pointer into // the buffer. The buffer may be shared memory and the raw pointer // should not be exposed without sharedness information accompanying
--- a/js/src/builtin/Eval.cpp +++ b/js/src/builtin/Eval.cpp @@ -4,22 +4,22 @@ * 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 "builtin/Eval.h" #include "mozilla/HashFunctions.h" #include "mozilla/Range.h" -#include "jscntxt.h" #include "jshashutil.h" #include "frontend/BytecodeCompiler.h" #include "vm/Debugger.h" #include "vm/GlobalObject.h" +#include "vm/JSContext.h" #include "vm/JSONParser.h" #include "vm/Interpreter-inl.h" using namespace js; using mozilla::AddToHash; using mozilla::HashString;
--- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -1,25 +1,25 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "builtin/MapObject.h" -#include "jscntxt.h" #include "jsiter.h" -#include "jsobj.h" #include "ds/OrderedHashTable.h" #include "gc/FreeOp.h" #include "js/Utility.h" #include "vm/GlobalObject.h" #include "vm/Interpreter.h" +#include "vm/JSContext.h" +#include "vm/JSObject.h" #include "vm/SelfHosting.h" #include "vm/Symbol.h" #include "gc/Marking-inl.h" #include "vm/Interpreter-inl.h" #include "vm/NativeObject-inl.h" using namespace js;
--- a/js/src/builtin/MapObject.h +++ b/js/src/builtin/MapObject.h @@ -2,20 +2,19 @@ * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef builtin_MapObject_h #define builtin_MapObject_h -#include "jsobj.h" - #include "builtin/SelfHostingDefines.h" #include "vm/GlobalObject.h" +#include "vm/JSObject.h" #include "vm/NativeObject.h" #include "vm/PIC.h" #include "vm/Runtime.h" namespace js { /* * Comparing two ropes for equality can fail. The js::HashTable template
--- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -12,18 +12,18 @@ #include "frontend/ParseNode.h" #include "frontend/SharedContext.h" #include "gc/FreeOp.h" #include "gc/Policy.h" #include "gc/Tracer.h" #include "vm/AsyncFunction.h" #include "vm/AsyncIteration.h" -#include "jsobjinlines.h" -#include "jsscriptinlines.h" +#include "vm/JSObject-inl.h" +#include "vm/JSScript-inl.h" using namespace js; using namespace js::frontend; static_assert(MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING && MODULE_STATUS_INSTANTIATING < MODULE_STATUS_INSTANTIATED && MODULE_STATUS_INSTANTIATED < MODULE_STATUS_EVALUATED && MODULE_STATUS_EVALUATED < MODULE_STATUS_EVALUATED_ERROR,
--- a/js/src/builtin/ModuleObject.h +++ b/js/src/builtin/ModuleObject.h @@ -10,16 +10,17 @@ #include "mozilla/Maybe.h" #include "jsapi.h" #include "builtin/SelfHostingDefines.h" #include "js/GCVector.h" #include "js/Id.h" #include "js/UniquePtr.h" +#include "vm/JSAtom.h" #include "vm/NativeObject.h" #include "vm/ProxyObject.h" namespace js { class ModuleEnvironmentObject; class ModuleObject;
--- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -4,30 +4,29 @@ * 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 "builtin/Object.h" #include "mozilla/ArrayUtils.h" #include "mozilla/MaybeOneOf.h" -#include "jscntxt.h" #include "jsstr.h" #include "builtin/Eval.h" #include "builtin/SelfHostingDefines.h" #include "frontend/BytecodeCompiler.h" #include "jit/InlinableNatives.h" #include "js/UniquePtr.h" #include "vm/AsyncFunction.h" +#include "vm/JSContext.h" #include "vm/RegExpObject.h" #include "vm/StringBuffer.h" -#include "jsobjinlines.h" - +#include "vm/JSObject-inl.h" #include "vm/NativeObject-inl.h" #include "vm/Shape-inl.h" #include "vm/UnboxedObject-inl.h" #ifdef FUZZING #include "builtin/TestingFunctions.h" #endif
--- a/js/src/builtin/Profilers.cpp +++ b/js/src/builtin/Profilers.cpp @@ -24,17 +24,17 @@ #ifdef XP_WIN # include <process.h> # define getpid _getpid #endif #include "vm/Probes.h" -#include "jscntxtinlines.h" +#include "vm/JSContext-inl.h" using namespace js; using mozilla::ArrayLength; /* Thread-unsafe error management */ static char gLastError[2000];
--- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -5,30 +5,29 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "builtin/Promise.h" #include "mozilla/Atomics.h" #include "mozilla/TimeStamp.h" -#include "jscntxt.h" #include "jsexn.h" #include "jsfriendapi.h" #include "jsiter.h" #include "gc/Heap.h" #include "js/Debug.h" #include "vm/AsyncFunction.h" #include "vm/AsyncIteration.h" #include "vm/Debugger.h" - -#include "jsobjinlines.h" +#include "vm/JSContext.h" #include "vm/Debugger-inl.h" +#include "vm/JSObject-inl.h" #include "vm/NativeObject-inl.h" using namespace js; static double MillisecondsSinceStartup() { auto now = mozilla::TimeStamp::Now(); @@ -2629,16 +2628,21 @@ js::AsyncFromSyncIteratorMethod(JSContex // Step 2. RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); if (!resultPromise) return false; // Step 3. if (!thisVal.isObject() || !thisVal.toObject().is<AsyncFromSyncIteratorObject>()) { + // NB: See https://github.com/tc39/proposal-async-iteration/issues/105 + // for why this check shouldn't be necessary as long as we can ensure + // the Async-from-Sync iterator can't be accessed directly by user + // code. + // Step 3.a. RootedValue badGeneratorError(cx); if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_ITERATOR, &badGeneratorError)) return false; // Step 3.b. if (!RejectMaybeWrappedPromise(cx, resultPromise, badGeneratorError)) return false; @@ -2653,18 +2657,17 @@ js::AsyncFromSyncIteratorMethod(JSContex // Step 4. RootedObject iter(cx, asyncIter->iterator()); RootedValue resultVal(cx); RootedValue func(cx); if (completionKind == CompletionKind::Normal) { // 11.1.3.2.1 steps 5-6 (partially). - if (!GetProperty(cx, iter, iter, cx->names().next, &func)) - return AbruptRejectPromise(cx, args, resultPromise, nullptr); + func.set(asyncIter->nextMethod()); } else if (completionKind == CompletionKind::Return) { // 11.1.3.2.2 steps 5-6. if (!GetProperty(cx, iter, iter, cx->names().return_, &func)) return AbruptRejectPromise(cx, args, resultPromise, nullptr); // Step 7. if (func.isNullOrUndefined()) { // Step 7.a.
--- a/js/src/builtin/Reflect.cpp +++ b/js/src/builtin/Reflect.cpp @@ -2,20 +2,20 @@ * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "builtin/Reflect.h" #include "jsarray.h" -#include "jscntxt.h" #include "jit/InlinableNatives.h" #include "vm/ArgumentsObject.h" +#include "vm/JSContext.h" #include "vm/Stack.h" #include "vm/Interpreter-inl.h" using namespace js; /*** Reflect methods *****************************************************************************/
--- a/js/src/builtin/Reflect.h +++ b/js/src/builtin/Reflect.h @@ -2,17 +2,17 @@ * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef builtin_Reflect_h #define builtin_Reflect_h -#include "jsobj.h" +#include "vm/JSObject.h" namespace js { extern JSObject* InitReflect(JSContext* cx, js::HandleObject obj); }
--- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -8,28 +8,28 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/DebugOnly.h" #include "mozilla/Move.h" #include <stdlib.h> #include "jsarray.h" -#include "jsobj.h" #include "jspubtd.h" #include "builtin/Reflect.h" #include "frontend/Parser.h" #include "frontend/TokenStream.h" #include "js/CharacterEncoding.h" +#include "vm/JSAtom.h" +#include "vm/JSObject.h" #include "vm/RegExpObject.h" -#include "jsobjinlines.h" - #include "frontend/ParseNode-inl.h" +#include "vm/JSObject-inl.h" using namespace js; using namespace js::frontend; using JS::AutoValueArray; using mozilla::ArrayLength; using mozilla::DebugOnly; using mozilla::Forward;
--- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -4,28 +4,26 @@ * 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 "builtin/RegExp.h" #include "mozilla/CheckedInt.h" #include "mozilla/TypeTraits.h" -#include "jscntxt.h" - #include "frontend/TokenStream.h" #include "irregexp/RegExpParser.h" #include "jit/InlinableNatives.h" +#include "vm/JSContext.h" #include "vm/RegExpStatics.h" #include "vm/SelfHosting.h" #include "vm/StringBuffer.h" #include "vm/Unicode.h" -#include "jsobjinlines.h" - +#include "vm/JSObject-inl.h" #include "vm/NativeObject-inl.h" #include "vm/UnboxedObject-inl.h" using namespace js; using namespace js::unicode; using mozilla::ArrayLength; using mozilla::CheckedInt;
--- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -23,17 +23,17 @@ #include "jsnum.h" #include "jsprf.h" #include "builtin/TypedObject.h" #include "jit/AtomicOperations.h" #include "jit/InlinableNatives.h" #include "js/Value.h" -#include "jsobjinlines.h" +#include "vm/JSObject-inl.h" using namespace js; using mozilla::ArrayLength; using mozilla::IsFinite; using mozilla::IsNaN; using mozilla::FloorLog2; using mozilla::NumberIsInt32;
--- a/js/src/builtin/Stream.cpp +++ b/js/src/builtin/Stream.cpp @@ -3,19 +3,18 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "builtin/Stream.h" #include "js/Stream.h" -#include "jscntxt.h" - #include "gc/Heap.h" +#include "vm/JSContext.h" #include "vm/SelfHosting.h" #include "vm/List-inl.h" #include "vm/NativeObject-inl.h" using namespace js; enum StreamSlots {
--- a/js/src/builtin/SymbolObject.cpp +++ b/js/src/builtin/SymbolObject.cpp @@ -4,18 +4,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "builtin/SymbolObject.h" #include "vm/StringBuffer.h" #include "vm/Symbol.h" -#include "jsobjinlines.h" - +#include "vm/JSObject-inl.h" #include "vm/NativeObject-inl.h" using JS::Symbol; using namespace js; const Class SymbolObject::class_ = { "Symbol", JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Symbol)
--- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -12,20 +12,18 @@ #include "mozilla/Sprintf.h" #include "mozilla/Unused.h" #include <cmath> #include <cstdlib> #include <ctime> #include "jsapi.h" -#include "jscntxt.h" #include "jsfriendapi.h" #include "jsiter.h" -#include "jsobj.h" #include "jsprf.h" #include "jswrapper.h" #include "builtin/Promise.h" #include "builtin/SelfHostingDefines.h" #ifdef DEBUG #include "frontend/TokenStream.h" #include "irregexp/RegExpAST.h" @@ -39,34 +37,35 @@ #include "js/UbiNode.h" #include "js/UbiNodeBreadthFirst.h" #include "js/UbiNodeShortestPaths.h" #include "js/UniquePtr.h" #include "js/Vector.h" #include "vm/Debugger.h" #include "vm/GlobalObject.h" #include "vm/Interpreter.h" +#include "vm/JSContext.h" +#include "vm/JSObject.h" #include "vm/ProxyObject.h" #include "vm/SavedStacks.h" #include "vm/Stack.h" #include "vm/StringBuffer.h" #include "vm/TraceLogging.h" #include "wasm/AsmJS.h" #include "wasm/WasmBinaryToText.h" #include "wasm/WasmJS.h" #include "wasm/WasmModule.h" #include "wasm/WasmSignalHandlers.h" #include "wasm/WasmTextToBinary.h" #include "wasm/WasmTypes.h" -#include "jscntxtinlines.h" -#include "jsobjinlines.h" - #include "vm/Debugger-inl.h" #include "vm/EnvironmentObject-inl.h" +#include "vm/JSContext-inl.h" +#include "vm/JSObject-inl.h" #include "vm/NativeObject-inl.h" using namespace js; using mozilla::ArrayLength; using mozilla::Move; // If fuzzingSafe is set, remove functionality that could cause problems with
--- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -1557,30 +1557,42 @@ function TypedArrayToStringTag() { return undefined; // Steps 4-6. // Modified to retrieve the [[TypedArrayName]] from the constructor. return _NameForTypedArray(O); } _SetCanonicalName(TypedArrayToStringTag, "get [Symbol.toStringTag]"); -// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e +// ES2018 draft rev 0525bb33861c7f4e9850f8a222c89642947c4b9c // 22.2.2.1.1 Runtime Semantics: IterableToList( items, method ) function IterableToList(items, method) { - // Step 1. - var iterator = GetIterator(items, method); + // Step 1 (Inlined GetIterator). + + // 7.4.1 GetIterator, step 1. + assert(IsCallable(method), "method argument is a function"); + + // 7.4.1 GetIterator, step 2. + var iterator = callContentFunction(method, items); + + // 7.4.1 GetIterator, step 3. + if (!IsObject(iterator)) + ThrowTypeError(JSMSG_GET_ITER_RETURNED_PRIMITIVE); + + // 7.4.1 GetIterator, step 4. + var nextMethod = iterator.next; // Step 2. var values = []; // Steps 3-4. var i = 0; while (true) { // Step 4.a. - var next = callContentFunction(iterator.next, iterator); + var next = callContentFunction(nextMethod, iterator); if (!IsObject(next)) ThrowTypeError(JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, "next"); // Step 4.b. if (next.done) break; _DefineDataProperty(values, i++, next.value); }
--- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -4,32 +4,32 @@ * 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 "builtin/TypedObject-inl.h" #include "mozilla/Casting.h" #include "mozilla/CheckedInt.h" -#include "jscompartment.h" -#include "jsfun.h" #include "jsutil.h" #include "builtin/SIMD.h" #include "gc/Marking.h" #include "js/Vector.h" #include "vm/GlobalObject.h" +#include "vm/JSCompartment.h" +#include "vm/JSFunction.h" #include "vm/String.h" #include "vm/StringBuffer.h" #include "vm/TypedArrayObject.h" -#include "jsobjinlines.h" - #include "gc/Nursery-inl.h" #include "gc/StoreBuffer-inl.h" +#include "vm/JSAtom-inl.h" +#include "vm/JSObject-inl.h" #include "vm/NativeObject-inl.h" #include "vm/Shape-inl.h" using mozilla::AssertedCast; using mozilla::CheckedInt32; using mozilla::DebugOnly; using mozilla::IsPowerOfTwo; using mozilla::PodCopy;
--- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -2,22 +2,22 @@ * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef builtin_TypedObject_h #define builtin_TypedObject_h -#include "jsobj.h" #include "jsweakmap.h" #include "builtin/TypedObjectConstants.h" #include "js/Conversions.h" #include "vm/ArrayBufferObject.h" +#include "vm/JSObject.h" #include "vm/ShapedObject.h" /* * ------------- * Typed Objects * ------------- * * Typed objects are a special kind of JS object where the data is
--- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -152,32 +152,16 @@ function GetMethod(V, P) { } /* Spec: ECMAScript Draft, 6th edition Dec 24, 2014, 7.2.7 */ function IsPropertyKey(argument) { var type = typeof argument; return type === "string" || type === "symbol"; } -/* Spec: ECMAScript Draft, 6th edition Dec 24, 2014, 7.4.1 */ -function GetIterator(obj, method) { - // Steps 1-2. - assert(IsCallable(method), "method argument is not optional"); - - // Steps 3-4. - var iterator = callContentFunction(method, obj); - - // Step 5. - if (!IsObject(iterator)) - ThrowTypeError(JSMSG_GET_ITER_RETURNED_PRIMITIVE); - - // Step 6. - return iterator; -} - #define TO_PROPERTY_KEY(name) \ (typeof name !== "string" && typeof name !== "number" && typeof name !== "symbol" ? ToPropertyKey(name) : name) var _builtinCtorsCache = {__proto__: null}; function GetBuiltinConstructor(builtinName) { var ctor = _builtinCtorsCache[builtinName] || (_builtinCtorsCache[builtinName] = GetBuiltinConstructorImpl(builtinName));
--- a/js/src/builtin/WeakMapObject.cpp +++ b/js/src/builtin/WeakMapObject.cpp @@ -2,20 +2,20 @@ * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "builtin/WeakMapObject-inl.h" #include "jsapi.h" -#include "jscntxt.h" #include "builtin/WeakSetObject.h" #include "gc/FreeOp.h" +#include "vm/JSContext.h" #include "vm/SelfHosting.h" #include "vm/Interpreter-inl.h" using namespace js; using namespace js::gc; MOZ_ALWAYS_INLINE bool
--- a/js/src/builtin/WeakMapObject.h +++ b/js/src/builtin/WeakMapObject.h @@ -2,19 +2,20 @@ * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef builtin_WeakMapObject_h #define builtin_WeakMapObject_h -#include "jsobj.h" #include "jsweakmap.h" +#include "vm/JSObject.h" + namespace js { // Abstract base class for WeakMapObject and WeakSetObject. class WeakCollectionObject : public NativeObject { public: ObjectValueMap* getMap() { return static_cast<ObjectValueMap*>(getPrivate()); }
--- a/js/src/builtin/WeakSetObject.cpp +++ b/js/src/builtin/WeakSetObject.cpp @@ -2,27 +2,26 @@ * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "builtin/WeakSetObject.h" #include "jsapi.h" -#include "jscntxt.h" #include "jsiter.h" #include "builtin/MapObject.h" #include "vm/GlobalObject.h" +#include "vm/JSContext.h" #include "vm/SelfHosting.h" -#include "jsobjinlines.h" - #include "builtin/WeakMapObject-inl.h" #include "vm/Interpreter-inl.h" +#include "vm/JSObject-inl.h" #include "vm/NativeObject-inl.h" using namespace js; MOZ_ALWAYS_INLINE bool IsWeakSet(HandleValue v) { return v.isObject() && v.toObject().is<WeakSetObject>();
--- a/js/src/builtin/intl/Collator.cpp +++ b/js/src/builtin/intl/Collator.cpp @@ -6,29 +6,29 @@ /* Intl.Collator implementation. */ #include "builtin/intl/Collator.h" #include "mozilla/Assertions.h" #include "jsapi.h" -#include "jscntxt.h" #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/ICUStubs.h" #include "builtin/intl/ScopedICUObject.h" #include "builtin/intl/SharedIntlData.h" #include "gc/FreeOp.h" #include "js/TypeDecls.h" #include "vm/GlobalObject.h" +#include "vm/JSContext.h" #include "vm/Runtime.h" #include "vm/String.h" -#include "jsobjinlines.h" +#include "vm/JSObject-inl.h" using namespace js; using js::intl::GetAvailableLocales; using js::intl::IcuLocale; using js::intl::ReportInternalError; using js::intl::SharedIntlData; using js::intl::StringsAreEqual;
--- a/js/src/builtin/intl/CommonFunctions.cpp +++ b/js/src/builtin/intl/CommonFunctions.cpp @@ -5,25 +5,25 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* Operations used to implement multiple Intl.* classes. */ #include "builtin/intl/CommonFunctions.h" #include "mozilla/Assertions.h" -#include "jscntxt.h" #include "jsfriendapi.h" // for GetErrorMessage, JSMSG_INTERNAL_INTL_ERROR -#include "jsobj.h" #include "js/Value.h" +#include "vm/JSContext.h" +#include "vm/JSObject.h" #include "vm/SelfHosting.h" #include "vm/Stack.h" -#include "jsobjinlines.h" +#include "vm/JSObject-inl.h" bool js::intl::InitializeObject(JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<PropertyName*> initializer, JS::Handle<JS::Value> locales, JS::Handle<JS::Value> options) { FixedInvokeArgs<3> args(cx);
--- a/js/src/builtin/intl/CommonFunctions.h +++ b/js/src/builtin/intl/CommonFunctions.h @@ -14,20 +14,16 @@ #include <stdint.h> #include <string.h> #include "builtin/intl/ICUStubs.h" #include "js/RootingAPI.h" #include "js/Vector.h" #include "vm/String.h" -namespace JS { class Value; } - -class JSObject; - namespace js { namespace intl { /** * Initialize a new Intl.* object using the named self-hosted function. */ extern bool
--- a/js/src/builtin/intl/DateTimeFormat.cpp +++ b/js/src/builtin/intl/DateTimeFormat.cpp @@ -6,30 +6,29 @@ /* Intl.DateTimeFormat implementation. */ #include "builtin/intl/DateTimeFormat.h" #include "mozilla/Assertions.h" #include "mozilla/Range.h" -#include "jscntxt.h" #include "jsfriendapi.h" #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/ICUStubs.h" #include "builtin/intl/ScopedICUObject.h" #include "builtin/intl/SharedIntlData.h" #include "builtin/intl/TimeZoneDataGenerated.h" #include "gc/FreeOp.h" #include "vm/GlobalObject.h" +#include "vm/JSContext.h" #include "vm/Runtime.h" -#include "jsobjinlines.h" - +#include "vm/JSObject-inl.h" #include "vm/NativeObject-inl.h" using namespace js; using JS::ClippedTime; using JS::TimeClip; using js::intl::CallICU;
--- a/js/src/builtin/intl/IntlObject.cpp +++ b/js/src/builtin/intl/IntlObject.cpp @@ -8,31 +8,31 @@ #include "builtin/intl/IntlObject.h" #include "mozilla/Assertions.h" #include "mozilla/Likely.h" #include "mozilla/Range.h" #include "jsapi.h" -#include "jscntxt.h" -#include "jsobj.h" #include "builtin/intl/Collator.h" #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/DateTimeFormat.h" #include "builtin/intl/ICUStubs.h" #include "builtin/intl/NumberFormat.h" #include "builtin/intl/PluralRules.h" #include "builtin/intl/ScopedICUObject.h" #include "js/Class.h" #include "vm/GlobalObject.h" +#include "vm/JSContext.h" +#include "vm/JSObject.h" #include "vm/String.h" -#include "jsobjinlines.h" +#include "vm/JSObject-inl.h" using namespace js; using mozilla::Range; using mozilla::RangedPtr; using js::intl::CallICU; using js::intl::DateTimeFormatOptions;
--- a/js/src/builtin/intl/IntlObject.h +++ b/js/src/builtin/intl/IntlObject.h @@ -5,21 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef builtin_intl_IntlObject_h #define builtin_intl_IntlObject_h #include "mozilla/Attributes.h" #include "js/RootingAPI.h" - -struct JSContext; -class JSObject; - -namespace JS { class Value; } +#include "js/TypeDecls.h" namespace js { /** * Initializes the Intl Object and its standard built-in properties. * Spec: ECMAScript Internationalization API Specification, 8.0, 8.1 */ extern JSObject*
--- a/js/src/builtin/intl/NumberFormat.cpp +++ b/js/src/builtin/intl/NumberFormat.cpp @@ -10,29 +10,28 @@ #include "mozilla/Assertions.h" #include "mozilla/FloatingPoint.h" #include <algorithm> #include <stddef.h> #include <stdint.h> -#include "jscntxt.h" - #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/ICUStubs.h" #include "builtin/intl/ScopedICUObject.h" #include "ds/Sort.h" #include "gc/FreeOp.h" #include "js/RootingAPI.h" #include "js/TypeDecls.h" +#include "vm/JSContext.h" #include "vm/SelfHosting.h" #include "vm/Stack.h" -#include "jsobjinlines.h" +#include "vm/JSObject-inl.h" using namespace js; using mozilla::AssertedCast; using mozilla::IsFinite; using mozilla::IsNaN; using mozilla::IsNegativeZero;
--- a/js/src/builtin/intl/PluralRules.cpp +++ b/js/src/builtin/intl/PluralRules.cpp @@ -6,27 +6,25 @@ /* Implementation of the Intl.PluralRules proposal. */ #include "builtin/intl/PluralRules.h" #include "mozilla/Assertions.h" #include "mozilla/Casting.h" -#include "jscntxt.h" - #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/ICUStubs.h" #include "builtin/intl/ScopedICUObject.h" #include "gc/FreeOp.h" #include "vm/GlobalObject.h" +#include "vm/JSContext.h" #include "vm/String.h" -#include "jsobjinlines.h" - +#include "vm/JSObject-inl.h" #include "vm/NativeObject-inl.h" using namespace js; using mozilla::AssertedCast; using js::intl::CallICU; using js::intl::DateTimeFormatOptions;
--- a/js/src/builtin/intl/RelativeTimeFormat.cpp +++ b/js/src/builtin/intl/RelativeTimeFormat.cpp @@ -6,23 +6,22 @@ /* Implementation of the Intl.RelativeTimeFormat proposal. */ #include "builtin/intl/RelativeTimeFormat.h" #include "mozilla/Assertions.h" #include "mozilla/Casting.h" -#include "jscntxt.h" - #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/ICUStubs.h" #include "builtin/intl/ScopedICUObject.h" #include "gc/FreeOp.h" #include "vm/GlobalObject.h" +#include "vm/JSContext.h" #include "vm/NativeObject-inl.h" using namespace js; using mozilla::IsNegativeZero; using mozilla::Range; using mozilla::RangedPtr;
--- a/js/src/builtin/intl/SharedIntlData.cpp +++ b/js/src/builtin/intl/SharedIntlData.cpp @@ -8,24 +8,24 @@ #include "builtin/intl/SharedIntlData.h" #include "mozilla/Assertions.h" #include "mozilla/HashFunctions.h" #include <stdint.h> -#include "jsatom.h" #include "jsstr.h" #include "builtin/intl/CommonFunctions.h" #include "builtin/intl/ICUStubs.h" #include "builtin/intl/ScopedICUObject.h" #include "builtin/intl/TimeZoneDataGenerated.h" #include "js/Utility.h" +#include "vm/JSAtom.h" using js::HashNumber; using js::intl::StringsAreEqual; template<typename Char> static constexpr Char ToUpperASCII(Char c) {
--- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -26,32 +26,32 @@ #ifdef HAVE_SSIZE_T #include <sys/types.h> #endif #if defined(XP_UNIX) #include <errno.h> #endif -#include "jscntxt.h" #include "jsexn.h" -#include "jsfun.h" #include "jsnum.h" #include "jsprf.h" #include "jswin.h" #include "builtin/TypedObject.h" #include "ctypes/Library.h" #include "gc/FreeOp.h" #include "gc/Policy.h" #include "gc/Zone.h" #include "jit/AtomicOperations.h" #include "js/Vector.h" - -#include "jsobjinlines.h" +#include "vm/JSContext.h" +#include "vm/JSFunction.h" + +#include "vm/JSObject-inl.h" using namespace std; using JS::AutoCheckCannotGC; namespace js { namespace ctypes {
--- a/js/src/devtools/automation/autospider.py +++ b/js/src/devtools/automation/autospider.py @@ -274,17 +274,17 @@ if word_bits == 32: else: sse_flags = '-msse -msse2 -mfpmath=sse' env['CCFLAGS'] = '{0} {1}'.format(env.get('CCFLAGS', ''), sse_flags) env['CXXFLAGS'] = '{0} {1}'.format(env.get('CXXFLAGS', ''), sse_flags) else: if platform.system() == 'Windows': CONFIGURE_ARGS += ' --target=x86_64-pc-mingw32 --host=x86_64-pc-mingw32' -if platform.system() == 'Linux': +if platform.system() == 'Linux' and AUTOMATION: CONFIGURE_ARGS = '--enable-stdcxx-compat ' + CONFIGURE_ARGS # Timeouts. ACTIVE_PROCESSES = set() def killall(): for proc in ACTIVE_PROCESSES:
--- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -4,33 +4,31 @@ * 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 "frontend/BytecodeCompiler.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/Maybe.h" -#include "jscntxt.h" -#include "jsscript.h" - #include "builtin/ModuleObject.h" #include "frontend/BytecodeEmitter.h" #include "frontend/ErrorReporter.h" #include "frontend/FoldConstants.h" #include "frontend/NameFunctions.h" #include "frontend/Parser.h" #include "vm/GlobalObject.h" +#include "vm/JSContext.h" +#include "vm/JSScript.h" #include "vm/TraceLogging.h" #include "wasm/AsmJS.h" -#include "jsobjinlines.h" -#include "jsscriptinlines.h" - #include "vm/EnvironmentObject-inl.h" +#include "vm/JSObject-inl.h" +#include "vm/JSScript-inl.h" using namespace js; using namespace js::frontend; using mozilla::Maybe; using mozilla::Nothing; // The BytecodeCompiler class contains resources common to compiling scripts and // function bodies.
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -14,36 +14,37 @@ #include "mozilla/DebugOnly.h" #include "mozilla/FloatingPoint.h" #include "mozilla/Maybe.h" #include "mozilla/PodOperations.h" #include <string.h> #include "jsapi.h" -#include "jscntxt.h" -#include "jsfun.h" #include "jsnum.h" #include "jsopcode.h" -#include "jsscript.h" #include "jstypes.h" #include "jsutil.h" #include "ds/Nestable.h" #include "frontend/Parser.h" #include "frontend/TokenStream.h" #include "vm/Debugger.h" #include "vm/GeneratorObject.h" +#include "vm/JSAtom.h" +#include "vm/JSContext.h" +#include "vm/JSFunction.h" +#include "vm/JSScript.h" #include "vm/Stack.h" #include "wasm/AsmJS.h" -#include "jsscriptinlines.h" - #include "frontend/ParseNode-inl.h" #include "vm/EnvironmentObject-inl.h" +#include "vm/JSAtom-inl.h" +#include "vm/JSScript-inl.h" #include "vm/NativeObject-inl.h" using namespace js; using namespace js::gc; using namespace js::frontend; using mozilla::AssertedCast; using mozilla::DebugOnly; @@ -247,22 +248,31 @@ class LoopControl : public BreakableCont MOZ_ASSERT(is<LoopControl>()); LoopControl* enclosingLoop = findNearest<LoopControl>(enclosing()); stackDepth_ = bce->stackDepth; loopDepth_ = enclosingLoop ? enclosingLoop->loopDepth_ + 1 : 1; int loopSlots; - if (loopKind == StatementKind::Spread) + if (loopKind == StatementKind::Spread) { + // The iterator next method, the iterator, the result array, and + // the current array index are on the stack. + loopSlots = 4; + } else if (loopKind == StatementKind::ForOfLoop) { + // The iterator next method, the iterator, and the current value + // are on the stack. loopSlots = 3; - else if (loopKind == StatementKind::ForInLoop || loopKind == StatementKind::ForOfLoop) + } else if (loopKind == StatementKind::ForInLoop) { + // The iterator and the current value are on the stack. loopSlots = 2; - else + } else { + // No additional loop values are on the stack. loopSlots = 0; + } MOZ_ASSERT(loopSlots <= stackDepth_); if (enclosingLoop) { canIonOsr_ = (enclosingLoop->canIonOsr_ && stackDepth_ == enclosingLoop->stackDepth_ + loopSlots); } else { canIonOsr_ = stackDepth_ == loopSlots; @@ -2092,35 +2102,43 @@ class ForOfLoopControl : public LoopCont return bce->tryNoteList.append(JSTRY_FOR_OF_ITERCLOSE, 0, start, end); } bool emitPrepareForNonLocalJump(BytecodeEmitter* bce, bool isTarget) { // Pop unnecessary value from the stack. Effectively this means // leaving try-catch block. However, the performing IteratorClose can // reach the depth for try-catch, and effectively re-enter the // try-catch block. + if (!bce->emit1(JSOP_POP)) // NEXT ITER + return false; + + // Pop the iterator's next method. + if (!bce->emit1(JSOP_SWAP)) // ITER NEXT + return false; if (!bce->emit1(JSOP_POP)) // ITER return false; // Clear ITER slot on the stack to tell catch block to avoid performing // IteratorClose again. if (!bce->emit1(JSOP_UNDEFINED)) // ITER UNDEF return false; if (!bce->emit1(JSOP_SWAP)) // UNDEF ITER return false; if (!emitIteratorClose(bce)) // UNDEF return false; if (isTarget) { // At the level of the target block, there's bytecode after the - // loop that will pop the iterator and the value, so push - // an undefined to balance the stack. + // loop that will pop the next method, the iterator, and the + // value, so push two undefineds to balance the stack. if (!bce->emit1(JSOP_UNDEFINED)) // UNDEF UNDEF return false; + if (!bce->emit1(JSOP_UNDEFINED)) // UNDEF UNDEF UNDEF + return false; } else { if (!bce->emit1(JSOP_POP)) // return false; } return true; } }; @@ -2783,17 +2801,19 @@ NonLocalExitControl::prepareForNonLocalJ if (emitIteratorClose) { if (!flushPops(bce_)) return false; ForOfLoopControl& loopinfo = control->as<ForOfLoopControl>(); if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ false)) // ... return false; } else { - npops += 2; + // The iterator next method, the iterator, and the current + // value are on the stack. + npops += 3; } break; case StatementKind::ForInLoop: if (!flushPops(bce_)) return false; // The iterator and the current value are on the stack. @@ -2808,17 +2828,17 @@ NonLocalExitControl::prepareForNonLocalJ } } if (!flushPops(bce_)) return false; if (target && emitIteratorCloseAtTarget && target->is<ForOfLoopControl>()) { ForOfLoopControl& loopinfo = target->as<ForOfLoopControl>(); - if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ true)) // ... UNDEF UNDEF + if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ true)) // ... UNDEF UNDEF UNDEF return false; } EmitterScope* targetEmitterScope = target ? target->emitterScope() : bce_->varEmitterScope; for (; es != targetEmitterScope; es = es->enclosingInFrame()) { if (!leaveScope(es)) return false; } @@ -5222,22 +5242,18 @@ BytecodeEmitter::emitSetOrInitializeDest bool BytecodeEmitter::emitIteratorNext(ParseNode* pn, IteratorKind iterKind /* = IteratorKind::Sync */, bool allowSelfHosted /* = false */) { MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting, ".next() iteration is prohibited in self-hosted code because it " "can run user-modifiable iteration code"); - if (!emit1(JSOP_DUP)) // ... ITER ITER - return false; - if (!emitAtomOp(cx->names().next, JSOP_CALLPROP)) // ... ITER NEXT - return false; - if (!emit1(JSOP_SWAP)) // ... NEXT ITER - return false; + MOZ_ASSERT(this->stackDepth >= 2); // ... NEXT ITER + if (!emitCall(JSOP_CALL, 0, pn)) // ... RESULT return false; if (iterKind == IteratorKind::Async) { if (!emitAwait()) // ... RESULT return false; } @@ -5539,68 +5555,69 @@ BytecodeEmitter::emitDestructuringOpsArr // Here's pseudo code for |let [a, b, , c=y, ...d] = x;| // // Lines that are annotated "covered by trynote" mean that upon throwing // an exception, IteratorClose is called on iter only if done is false. // // let x, y; // let a, b, c, d; - // let iter, lref, result, done, value; // stack values + // let iter, next, lref, result, done, value; // stack values // // iter = x[Symbol.iterator](); + // next = iter.next; // // // ==== emitted by loop for a ==== // lref = GetReference(a); // covered by trynote // - // result = iter.next(); + // result = Call(next, iter); // done = result.done; // // if (done) // value = undefined; // else // value = result.value; // // SetOrInitialize(lref, value); // covered by trynote // // // ==== emitted by loop for b ==== // lref = GetReference(b); // covered by trynote // // if (done) { // value = undefined; // } else { - // result = iter.next(); + // result = Call(next, iter); // done = result.done; // if (done) // value = undefined; // else // value = result.value; // } // // SetOrInitialize(lref, value); // covered by trynote // // // ==== emitted by loop for elision ==== // if (done) { // value = undefined; // } else { - // result = iter.next(); + // result = Call(next, iter); // done = result.done; // if (done) // value = undefined; // else // value = result.value; // } // // // ==== emitted by loop for c ==== // lref = GetReference(c); // covered by trynote // // if (done) { // value = undefined; // } else { - // result = iter.next(); + // result = Call(next, iter); // done = result.done; // if (done) // value = undefined; // else // value = result.value; // } // // if (value === undefined) @@ -5621,26 +5638,32 @@ BytecodeEmitter::emitDestructuringOpsArr // // === emitted after loop === // if (!done) // IteratorClose(iter); // Use an iterator to destructure the RHS, instead of index lookup. We // must leave the *original* value on the stack. if (!emit1(JSOP_DUP)) // ... OBJ OBJ return false; - if (!emitIterator()) // ... OBJ ITER + if (!emitIterator()) // ... OBJ NEXT ITER return false; // For an empty pattern [], call IteratorClose unconditionally. Nothing // else needs to be done. - if (!pattern->pn_head) + if (!pattern->pn_head) { + if (!emit1(JSOP_SWAP)) // ... OBJ ITER NEXT + return false; + if (!emit1(JSOP_POP)) // ... OBJ ITER + return false; + return emitIteratorClose(); // ... OBJ + } // Push an initial FALSE value for DONE. - if (!emit1(JSOP_FALSE)) // ... OBJ ITER FALSE + if (!emit1(JSOP_FALSE)) // ... OBJ NEXT ITER FALSE return false; // JSTRY_DESTRUCTURING_ITERCLOSE expects the iterator and the done value // to be the second to top and the top of the stack, respectively. // IteratorClose is called upon exception only if done is false. int32_t tryNoteDepth = stackDepth; for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) { @@ -5652,186 +5675,194 @@ BytecodeEmitter::emitDestructuringOpsArr // Spec requires LHS reference to be evaluated first. ParseNode* lhsPattern = member; if (lhsPattern->isKind(ParseNodeKind::Assign)) lhsPattern = lhsPattern->pn_left; bool isElision = lhsPattern->isKind(ParseNodeKind::Elision); if (!isElision) { auto emitLHSRef = [lhsPattern, &emitted](BytecodeEmitter* bce) { - return bce->emitDestructuringLHSRef(lhsPattern, &emitted); // ... OBJ ITER DONE *LREF + return bce->emitDestructuringLHSRef(lhsPattern, &emitted); // ... OBJ NEXT ITER DONE *LREF }; if (!wrapWithDestructuringIteratorCloseTryNote(tryNoteDepth, emitLHSRef)) return false; } // Pick the DONE value to the top of the stack. if (emitted) { - if (!emit2(JSOP_PICK, emitted)) // ... OBJ ITER *LREF DONE + if (!emit2(JSOP_PICK, emitted)) // ... OBJ NEXT ITER *LREF DONE return false; } if (isFirst) { // If this element is the first, DONE is always FALSE, so pop it. // // Non-first elements should emit if-else depending on the // member pattern, below. - if (!emit1(JSOP_POP)) // ... OBJ ITER *LREF + if (!emit1(JSOP_POP)) // ... OBJ NEXT ITER *LREF return false; } if (member->isKind(ParseNodeKind::Spread)) { IfThenElseEmitter ifThenElse(this); if (!isFirst) { // If spread is not the first element of the pattern, // iterator can already be completed. - // ... OBJ ITER *LREF DONE - if (!ifThenElse.emitIfElse()) // ... OBJ ITER *LREF + // ... OBJ NEXT ITER *LREF DONE + if (!ifThenElse.emitIfElse()) // ... OBJ NEXT ITER *LREF return false; - if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ ITER *LREF ARRAY + if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ NEXT ITER *LREF ARRAY return false; - if (!ifThenElse.emitElse()) // ... OBJ ITER *LREF + if (!ifThenElse.emitElse()) // ... OBJ NEXT ITER *LREF return false; } // If iterator is not completed, create a new array with the rest // of the iterator. - if (!emitDupAt(emitted)) // ... OBJ ITER *LREF ITER - return false; - if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ ITER *LREF ITER ARRAY - return false; - if (!emitNumberOp(0)) // ... OBJ ITER *LREF ITER ARRAY INDEX - return false; - if (!emitSpread()) // ... OBJ ITER *LREF ARRAY INDEX - return false; - if (!emit1(JSOP_POP)) // ... OBJ ITER *LREF ARRAY + if (!emitDupAt(emitted + 1)) // ... OBJ NEXT ITER *LREF NEXT + return false; + if (!emitDupAt(emitted + 1)) // ... OBJ NEXT ITER *LREF NEXT ITER + return false; + if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ NEXT ITER *LREF NEXT ITER ARRAY + return false; + if (!emitNumberOp(0)) // ... OBJ NEXT ITER *LREF NEXT ITER ARRAY INDEX + return false; + if (!emitSpread()) // ... OBJ NEXT ITER *LREF ARRAY INDEX + return false; + if (!emit1(JSOP_POP)) // ... OBJ NEXT ITER *LREF ARRAY return false; if (!isFirst) { if (!ifThenElse.emitEnd()) return false; MOZ_ASSERT(ifThenElse.pushed() == 1); } // At this point the iterator is done. Unpick a TRUE value for DONE above ITER. - if (!emit1(JSOP_TRUE)) // ... OBJ ITER *LREF ARRAY TRUE - return false; - if (!emit2(JSOP_UNPICK, emitted + 1)) // ... OBJ ITER TRUE *LREF ARRAY + if (!emit1(JSOP_TRUE)) // ... OBJ NEXT ITER *LREF ARRAY TRUE + return false; + if (!emit2(JSOP_UNPICK, emitted + 1)) // ... OBJ NEXT ITER TRUE *LREF ARRAY return false; auto emitAssignment = [member, flav](BytecodeEmitter* bce) { - return bce->emitSetOrInitializeDestructuring(member, flav); // ... OBJ ITER TRUE + return bce->emitSetOrInitializeDestructuring(member, flav); // ... OBJ NEXT ITER TRUE }; if (!wrapWithDestructuringIteratorCloseTryNote(tryNoteDepth, emitAssignment)) return false; MOZ_ASSERT(!hasNext); break; } ParseNode* pndefault = nullptr; if (member->isKind(ParseNodeKind::Assign)) pndefault = member->pn_right; MOZ_ASSERT(!member->isKind(ParseNodeKind::Spread)); IfThenElseEmitter ifAlreadyDone(this); if (!isFirst) { - // ... OBJ ITER *LREF DONE - if (!ifAlreadyDone.emitIfElse()) // ... OBJ ITER *LREF - return false; - - if (!emit1(JSOP_UNDEFINED)) // ... OBJ ITER *LREF UNDEF - return false; - if (!emit1(JSOP_NOP_DESTRUCTURING)) // ... OBJ ITER *LREF UNDEF + // ... OBJ NEXT ITER *LREF DONE + if (!ifAlreadyDone.emitIfElse()) // ... OBJ NEXT ITER *LREF + return false; + + if (!emit1(JSOP_UNDEFINED)) // ... OBJ NEXT ITER *LREF UNDEF + return false; + if (!emit1(JSOP_NOP_DESTRUCTURING)) // ... OBJ NEXT ITER *LREF UNDEF return false; // The iterator is done. Unpick a TRUE value for DONE above ITER. - if (!emit1(JSOP_TRUE)) // ... OBJ ITER *LREF UNDEF TRUE - return false; - if (!emit2(JSOP_UNPICK, emitted + 1)) // ... OBJ ITER TRUE *LREF UNDEF - return false; - - if (!ifAlreadyDone.emitElse()) // ... OBJ ITER *LREF - return false; - } - - if (!emitDupAt(emitted)) // ... OBJ ITER *LREF ITER - return false; - if (!emitIteratorNext(pattern)) // ... OBJ ITER *LREF RESULT - return false; - if (!emit1(JSOP_DUP)) // ... OBJ ITER *LREF RESULT RESULT - return false; - if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ... OBJ ITER *LREF RESULT DONE - return false; - - if (!emit1(JSOP_DUP)) // ... OBJ ITER *LREF RESULT DONE DONE - return false; - if (!emit2(JSOP_UNPICK, emitted + 2)) // ... OBJ ITER DONE *LREF RESULT DONE + if (!emit1(JSOP_TRUE)) // ... OBJ NEXT ITER *LREF UNDEF TRUE + return false; + if (!emit2(JSOP_UNPICK, emitted + 1)) // ... OBJ NEXT ITER TRUE *LREF UNDEF + return false; + + if (!ifAlreadyDone.emitElse()) // ... OBJ NEXT ITER *LREF + return false; + } + + if (!emitDupAt(emitted + 1)) // ... OBJ NEXT ITER *LREF NEXT + return false; + if (!emitDupAt(emitted + 1)) // ... OBJ NEXT ITER *LREF NEXT ITER + return false; + if (!emitIteratorNext(pattern)) // ... OBJ NEXT ITER *LREF RESULT + return false; + if (!emit1(JSOP_DUP)) // ... OBJ NEXT ITER *LREF RESULT RESULT + return false; + if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ... OBJ NEXT ITER *LREF RESULT DONE + return false; + + if (!emit1(JSOP_DUP)) // ... OBJ NEXT ITER *LREF RESULT DONE DONE + return false; + if (!emit2(JSOP_UNPICK, emitted + 2)) // ... OBJ NEXT ITER DONE *LREF RESULT DONE return false; IfThenElseEmitter ifDone(this); - if (!ifDone.emitIfElse()) // ... OBJ ITER DONE *LREF RESULT - return false; - - if (!emit1(JSOP_POP)) // ... OBJ ITER DONE *LREF - return false; - if (!emit1(JSOP_UNDEFINED)) // ... OBJ ITER DONE *LREF UNDEF - return false; - if (!emit1(JSOP_NOP_DESTRUCTURING)) // ... OBJ ITER DONE *LREF UNDEF - return false; - - if (!ifDone.emitElse()) // ... OBJ ITER DONE *LREF RESULT - return false; - - if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ... OBJ ITER DONE *LREF VALUE + if (!ifDone.emitIfElse()) // ... OBJ NEXT ITER DONE *LREF RESULT + return false; + + if (!emit1(JSOP_POP)) // ... OBJ NEXT ITER DONE *LREF + return false; + if (!emit1(JSOP_UNDEFINED)) // ... OBJ NEXT ITER DONE *LREF UNDEF + return false; + if (!emit1(JSOP_NOP_DESTRUCTURING)) // ... OBJ NEXT ITER DONE *LREF UNDEF + return false; + + if (!ifDone.emitElse()) // ... OBJ NEXT ITER DONE *LREF RESULT + return false; + + if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ... OBJ NEXT ITER DONE *LREF VALUE return false; if (!ifDone.emitEnd()) return false; MOZ_ASSERT(ifDone.pushed() == 0); if (!isFirst) { if (!ifAlreadyDone.emitEnd()) return false; MOZ_ASSERT(ifAlreadyDone.pushed() == 2); } if (pndefault) { auto emitDefault = [pndefault, lhsPattern](BytecodeEmitter* bce) { - return bce->emitDefault(pndefault, lhsPattern); // ... OBJ ITER DONE *LREF VALUE + return bce->emitDefault(pndefault, lhsPattern); // ... OBJ NEXT ITER DONE *LREF VALUE }; if (!wrapWithDestructuringIteratorCloseTryNote(tryNoteDepth, emitDefault)) return false; } if (!isElision) { auto emitAssignment = [lhsPattern, flav](BytecodeEmitter* bce) { - return bce->emitSetOrInitializeDestructuring(lhsPattern, flav); // ... OBJ ITER DONE + return bce->emitSetOrInitializeDestructuring(lhsPattern, flav); // ... OBJ NEXT ITER DONE }; if (!wrapWithDestructuringIteratorCloseTryNote(tryNoteDepth, emitAssignment)) return false; } else { - if (!emit1(JSOP_POP)) // ... OBJ ITER DONE + if (!emit1(JSOP_POP)) // ... OBJ NEXT ITER DONE return false; } } // The last DONE value is on top of the stack. If not DONE, call // IteratorClose. - // ... OBJ ITER DONE + // ... OBJ NEXT ITER DONE IfThenElseEmitter ifDone(this); - if (!ifDone.emitIfElse()) // ... OBJ ITER - return false; - if (!emit1(JSOP_POP)) // ... OBJ - return false; - if (!ifDone.emitElse()) // ... OBJ ITER + if (!ifDone.emitIfElse()) // ... OBJ NEXT ITER + return false; + if (!emitPopN(2)) // ... OBJ + return false; + if (!ifDone.emitElse()) // ... OBJ NEXT ITER + return false; + if (!emit1(JSOP_SWAP)) // ... OBJ ITER NEXT + return false; + if (!emit1(JSOP_POP)) // ... OBJ ITER return false; if (!emitIteratorClose()) // ... OBJ return false; if (!ifDone.emitEnd()) return false; return true; } @@ -6926,16 +6957,22 @@ BytecodeEmitter::emitIterator() return false; if (!emit1(JSOP_SWAP)) // ITERFN OBJ return false; if (!emitCall(JSOP_CALLITER, 0)) // ITER return false; checkTypeSet(JSOP_CALLITER); if (!emitCheckIsObj(CheckIsObjectKind::GetIterator)) // ITER return false; + if (!emit1(JSOP_DUP)) // ITER ITER + return false; + if (!emitAtomOp(cx->names().next, JSOP_GETPROP)) // ITER NEXT + return false; + if (!emit1(JSOP_SWAP)) // NEXT ITER + return false; return true; } bool BytecodeEmitter::emitAsyncIterator() { // Convert iterable to iterator. if (!emit1(JSOP_DUP)) // OBJ OBJ @@ -6964,16 +7001,21 @@ BytecodeEmitter::emitAsyncIterator() if (!emit1(JSOP_SWAP)) // ITERFN OBJ return false; if (!emitCall(JSOP_CALLITER, 0)) // ITER return false; checkTypeSet(JSOP_CALLITER); if (!emitCheckIsObj(CheckIsObjectKind::GetIterator)) // ITER return false; + if (!emit1(JSOP_DUP)) // ITER ITER + return false; + if (!emitAtomOp(cx->names().next, JSOP_GETPROP)) // ITER SYNCNEXT + return false; + if (!emit1(JSOP_TOASYNCITER)) // ITER return false; if (!ifAsyncIterIsUndefined.emitElse()) // OBJ ITERFN return false; if (!emit1(JSOP_SWAP)) // ITERFN OBJ return false; @@ -6981,16 +7023,23 @@ BytecodeEmitter::emitAsyncIterator() return false; checkTypeSet(JSOP_CALLITER); if (!emitCheckIsObj(CheckIsObjectKind::GetIterator)) // ITER return false; if (!ifAsyncIterIsUndefined.emitEnd()) // ITER return false; + if (!emit1(JSOP_DUP)) // ITER ITER + return false; + if (!emitAtomOp(cx->names().next, JSOP_GETPROP)) // ITER NEXT + return false; + if (!emit1(JSOP_SWAP)) // NEXT ITER + return false; + return true; } bool BytecodeEmitter::emitSpread(bool allowSelfHosted) { LoopControl loopInfo(this, StatementKind::Spread); @@ -7000,81 +7049,85 @@ BytecodeEmitter::emitSpread(bool allowSe unsigned noteIndex; if (!newSrcNote(SRC_FOR_OF, ¬eIndex)) return false; // Jump down to the loop condition to minimize overhead, assuming at least // one iteration. (This is also what we do for loops; whether this // assumption holds for spreads is an unanswered question.) JumpList initialJump; - if (!emitJump(JSOP_GOTO, &initialJump)) // ITER ARR I (during the goto) + if (!emitJump(JSOP_GOTO, &initialJump)) // NEXT ITER ARR I (during the goto) return false; JumpTarget top{ -1 }; - if (!emitLoopHead(nullptr, &top)) // ITER ARR I - return false; - - // When we enter the goto above, we have ITER ARR I on the stack. But when - // we reach this point on the loop backedge (if spreading produces at least - // one value), we've additionally pushed a RESULT iteration value. + if (!emitLoopHead(nullptr, &top)) // NEXT ITER ARR I + return false; + + // When we enter the goto above, we have NEXT ITER ARR I on the stack. But + // when we reach this point on the loop backedge (if spreading produces at + // least one value), we've additionally pushed a RESULT iteration value. // Increment manually to reflect this. this->stackDepth++; JumpList beq; JumpTarget breakTarget{ -1 }; { #ifdef DEBUG auto loopDepth = this->stackDepth; #endif // Emit code to assign result.value to the iteration variable. - if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER ARR I VALUE - return false; - if (!emit1(JSOP_INITELEM_INC)) // ITER ARR (I+1) + if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // NEXT ITER ARR I VALUE + return false; + if (!emit1(JSOP_INITELEM_INC)) // NEXT ITER ARR (I+1) return false; MOZ_ASSERT(this->stackDepth == loopDepth - 1); // Spread operations can't contain |continue|, so don't bother setting loop // and enclosing "update" offsets, as we do with for-loops. // COME FROM the beginning of the loop to here. - if (!emitLoopEntry(nullptr, initialJump)) // ITER ARR I - return false; - - if (!emitDupAt(2)) // ITER ARR I ITER + if (!emitLoopEntry(nullptr, initialJump)) // NEXT ITER ARR I + return false; + + if (!emitDupAt(3)) // NEXT ITER ARR I NEXT + return false; + if (!emitDupAt(3)) // NEXT ITER ARR I NEXT ITER return false; if (!emitIteratorNext(nullptr, IteratorKind::Sync, allowSelfHosted)) // ITER ARR I RESULT return false; - if (!emit1(JSOP_DUP)) // ITER ARR I RESULT RESULT - return false; - if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER ARR I RESULT DONE - return false; - - if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget)) // ITER ARR I RESULT + if (!emit1(JSOP_DUP)) // NEXT ITER ARR I RESULT RESULT + return false; + if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // NEXT ITER ARR I RESULT DONE + return false; + + if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget)) // NEXT ITER ARR I RESULT return false; MOZ_ASSERT(this->stackDepth == loopDepth); } // Let Ion know where the closing jump of this loop is. if (!setSrcNoteOffset(noteIndex, 0, beq.offset - initialJump.offset)) return false; // No breaks or continues should occur in spreads. MOZ_ASSERT(loopInfo.breaks.offset == -1); MOZ_ASSERT(loopInfo.continues.offset == -1); if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, top.offset, breakTarget.offset)) return false; - if (!emit2(JSOP_PICK, 3)) // ARR FINAL_INDEX RESULT ITER - return false; - - return emitPopN(2); // ARR FINAL_INDEX + if (!emit2(JSOP_PICK, 4)) // ITER ARR FINAL_INDEX RESULT NEXT + return false; + if (!emit2(JSOP_PICK, 4)) // ARR FINAL_INDEX RESULT NEXT ITER + return false; + + return emitPopN(3); // ARR FINAL_INDEX } bool BytecodeEmitter::emitInitializeForInOrOfTarget(ParseNode* forHead) { MOZ_ASSERT(forHead->isKind(ParseNodeKind::ForIn) || forHead->isKind(ParseNodeKind::ForOf)); MOZ_ASSERT(forHead->isArity(PN_TERNARY)); @@ -7164,43 +7217,44 @@ BytecodeEmitter::emitForOf(ParseNode* fo } // Evaluate the expression being iterated. The forHeadExpr should use a // distinct TDZCheckCache to evaluate since (abstractly) it runs in its own // LexicalEnvironment. if (!emitTreeInBranch(forHeadExpr)) // ITERABLE return false; if (iterKind == IteratorKind::Async) { - if (!emitAsyncIterator()) // ITER + if (!emitAsyncIterator()) // NEXT ITER return false; } else { - if (!emitIterator()) // ITER + if (!emitIterator()) // NEXT ITER return false; } int32_t iterDepth = stackDepth; - // For-of loops have both the iterator and the result.value on the stack. + // For-of loops have the iterator next method, the iterator itself, and + // the result.value on the stack. // Push an undefined to balance the stack. - if (!emit1(JSOP_UNDEFINED)) // ITER UNDEF + if (!emit1(JSOP_UNDEFINED)) // NEXT ITER UNDEF return false; ForOfLoopControl loopInfo(this, iterDepth, allowSelfHostedIter, iterKind); // Annotate so IonMonkey can find the loop-closing jump. unsigned noteIndex; if (!newSrcNote(SRC_FOR_OF, ¬eIndex)) return false; JumpList initialJump; - if (!emitJump(JSOP_GOTO, &initialJump)) // ITER UNDEF + if (!emitJump(JSOP_GOTO, &initialJump)) // NEXT ITER UNDEF return false; JumpTarget top{ -1 }; - if (!emitLoopHead(nullptr, &top)) // ITER UNDEF + if (!emitLoopHead(nullptr, &top)) // NEXT ITER UNDEF return false; // If the loop had an escaping lexical declaration, replace the current // environment with an dead zoned one to implement TDZ semantics. if (headLexicalEmitterScope) { // The environment chain only includes an environment for the for-of // loop head *if* a scope binding is captured, thereby requiring // recreation each iteration. If a lexical scope exists for the head, @@ -7208,17 +7262,17 @@ BytecodeEmitter::emitForOf(ParseNode* fo // bindings inducing an environment, recreate the current environment. DebugOnly<ParseNode*> forOfTarget = forOfHead->pn_kid1; MOZ_ASSERT(forOfTarget->isKind(ParseNodeKind::Let) || forOfTarget->isKind(ParseNodeKind::Const)); MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope); MOZ_ASSERT(headLexicalEmitterScope->scope(this)->kind() == ScopeKind::Lexical); if (headLexicalEmitterScope->hasEnvironment()) { - if (!emit1(JSOP_RECREATELEXICALENV)) // ITER UNDEF + if (!emit1(JSOP_RECREATELEXICALENV)) // NEXT ITER UNDEF return false; } // For uncaptured bindings, put them back in TDZ. if (!headLexicalEmitterScope->deadZoneFrameSlots(this)) return false; } @@ -7228,107 +7282,107 @@ BytecodeEmitter::emitForOf(ParseNode* fo #ifdef DEBUG auto loopDepth = this->stackDepth; #endif // Make sure this code is attributed to the "for". if (!updateSourceCoordNotes(forOfHead->pn_pos.begin)) return false; - if (!emit1(JSOP_POP)) // ITER - return false; - if (!emit1(JSOP_DUP)) // ITER ITER + if (!emit1(JSOP_POP)) // NEXT ITER + return false; + if (!emit1(JSOP_DUP2)) // NEXT ITER NEXT ITER return false; if (!emitIteratorNext(forOfHead, iterKind, allowSelfHostedIter)) - return false; // ITER RESULT - - if (!emit1(JSOP_DUP)) // ITER RESULT RESULT - return false; - if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT DONE + return false; // NEXT ITER RESULT + + if (!emit1(JSOP_DUP)) // NEXT ITER RESULT RESULT + return false; + if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // NEXT ITER RESULT DONE return false; IfThenElseEmitter ifDone(this); - if (!ifDone.emitIf()) // ITER RESULT + if (!ifDone.emitIf()) // NEXT ITER RESULT return false; // Remove RESULT from the stack to release it. - if (!emit1(JSOP_POP)) // ITER - return false; - if (!emit1(JSOP_UNDEFINED)) // ITER UNDEF + if (!emit1(JSOP_POP)) // NEXT ITER + return false; + if (!emit1(JSOP_UNDEFINED)) // NEXT ITER UNDEF return false; // If the iteration is done, leave loop here, instead of the branch at // the end of the loop. - if (!loopInfo.emitSpecialBreakForDone(this)) // ITER UNDEF - return false; - - if (!ifDone.emitEnd()) // ITER RESULT + if (!loopInfo.emitSpecialBreakForDone(this)) // NEXT ITER UNDEF + return false; + + if (!ifDone.emitEnd()) // NEXT ITER RESULT return false; // Emit code to assign result.value to the iteration variable. // // Note that ES 13.7.5.13, step 5.c says getting result.value does not // call IteratorClose, so start JSTRY_ITERCLOSE after the GETPROP. - if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER VALUE + if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // NEXT ITER VALUE return false; if (!loopInfo.emitBeginCodeNeedingIteratorClose(this)) return false; - if (!emitInitializeForInOrOfTarget(forOfHead)) // ITER VALUE + if (!emitInitializeForInOrOfTarget(forOfHead)) // NEXT ITER VALUE return false; MOZ_ASSERT(stackDepth == loopDepth, "the stack must be balanced around the initializing " "operation"); // Remove VALUE from the stack to release it. - if (!emit1(JSOP_POP)) // ITER - return false; - if (!emit1(JSOP_UNDEFINED)) // ITER UNDEF + if (!emit1(JSOP_POP)) // NEXT ITER + return false; + if (!emit1(JSOP_UNDEFINED)) // NEXT ITER UNDEF return false; // Perform the loop body. ParseNode* forBody = forOfLoop->pn_right; - if (!emitTree(forBody)) // ITER UNDEF + if (!emitTree(forBody)) // NEXT ITER UNDEF return false; MOZ_ASSERT(stackDepth == loopDepth, "the stack must be balanced around the for-of body"); if (!loopInfo.emitEndCodeNeedingIteratorClose(this)) return false; // Set offset for continues. loopInfo.continueTarget = { offset() }; - if (!emitLoopEntry(forHeadExpr, initialJump)) // ITER UNDEF - return false; - - if (!emit1(JSOP_FALSE)) // ITER UNDEF FALSE + if (!emitLoopEntry(forHeadExpr, initialJump)) // NEXT ITER UNDEF + return false; + + if (!emit1(JSOP_FALSE)) // NEXT ITER UNDEF FALSE return false; if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget)) - return false; // ITER UNDEF + return false; // NEXT ITER UNDEF MOZ_ASSERT(this->stackDepth == loopDepth); } // Let Ion know where the closing jump of this loop is. if (!setSrcNoteOffset(noteIndex, 0, beq.offset - initialJump.offset)) return false; if (!loopInfo.patchBreaksAndContinues(this)) return false; if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, top.offset, breakTarget.offset)) return false; - return emitPopN(2); // + return emitPopN(3); // } bool BytecodeEmitter::emitForIn(ParseNode* forInLoop, EmitterScope* headLexicalEmitterScope) { MOZ_ASSERT(forInLoop->isKind(ParseNodeKind::For)); MOZ_ASSERT(forInLoop->isArity(PN_BINARY)); MOZ_ASSERT(forInLoop->isOp(JSOP_ITER)); @@ -8377,113 +8431,113 @@ BytecodeEmitter::emitYieldStar(ParseNode MOZ_ASSERT(sc->isFunctionBox()); MOZ_ASSERT(sc->asFunctionBox()->isGenerator()); bool isAsyncGenerator = sc->asFunctionBox()->isAsync(); if (!emitTree(iter)) // ITERABLE return false; if (isAsyncGenerator) { - if (!emitAsyncIterator()) // ITER + if (!emitAsyncIterator()) // NEXT ITER return false; } else { - if (!emitIterator()) // ITER + if (!emitIterator()) // NEXT ITER return false; } // Initial send value is undefined. - if (!emit1(JSOP_UNDEFINED)) // ITER RECEIVED + if (!emit1(JSOP_UNDEFINED)) // NEXT ITER RECEIVED return false; int32_t savedDepthTemp; int32_t startDepth = stackDepth; - MOZ_ASSERT(startDepth >= 2); + MOZ_ASSERT(startDepth >= 3); TryEmitter tryCatch(this, TryEmitter::TryCatchFinally, TryEmitter::DontUseRetVal, TryEmitter::DontUseControl); - if (!tryCatch.emitJumpOverCatchAndFinally()) // ITER RESULT + if (!tryCatch.emitJumpOverCatchAndFinally()) // NEXT ITER RESULT return false; JumpTarget tryStart{ offset() }; - if (!tryCatch.emitTry()) // ITER RESULT + if (!tryCatch.emitTry()) // NEXT ITER RESULT return false; MOZ_ASSERT(this->stackDepth == startDepth); // 11.4.3.7 AsyncGeneratorYield step 5. if (isAsyncGenerator) { - if (!emitAwait()) // ITER RESULT + if (!emitAwait()) // NEXT ITER RESULT return false; } // Load the generator object. - if (!emitGetDotGenerator()) // ITER RESULT GENOBJ + if (!emitGetDotGenerator()) // NEXT ITER RESULT GENOBJ return false; // Yield RESULT as-is, without re-boxing. - if (!emitYieldOp(JSOP_YIELD)) // ITER RECEIVED - return false; - - if (!tryCatch.emitCatch()) // ITER RESULT - return false; - - stackDepth = startDepth; // ITER RESULT - if (!emit1(JSOP_EXCEPTION)) // ITER RESULT EXCEPTION - return false; - if (!emitDupAt(2)) // ITER RESULT EXCEPTION ITER - return false; - if (!emit1(JSOP_DUP)) // ITER RESULT EXCEPTION ITER ITER - return false; - if (!emitAtomOp(cx->names().throw_, JSOP_CALLPROP)) // ITER RESULT EXCEPTION ITER THROW - return false; - if (!emit1(JSOP_DUP)) // ITER RESULT EXCEPTION ITER THROW THROW - return false; - if (!emit1(JSOP_UNDEFINED)) // ITER RESULT EXCEPTION ITER THROW THROW UNDEFINED - return false; - if (!emit1(JSOP_EQ)) // ITER RESULT EXCEPTION ITER THROW ?EQL + if (!emitYieldOp(JSOP_YIELD)) // NEXT ITER RECEIVED + return false; + + if (!tryCatch.emitCatch()) // NEXT ITER RESULT + return false; + + stackDepth = startDepth; // NEXT ITER RESULT + if (!emit1(JSOP_EXCEPTION)) // NEXT ITER RESULT EXCEPTION + return false; + if (!emitDupAt(2)) // NEXT ITER RESULT EXCEPTION ITER + return false; + if (!emit1(JSOP_DUP)) // NEXT ITER RESULT EXCEPTION ITER ITER + return false; + if (!emitAtomOp(cx->names().throw_, JSOP_CALLPROP)) // NEXT ITER RESULT EXCEPTION ITER THROW + return false; + if (!emit1(JSOP_DUP)) // NEXT ITER RESULT EXCEPTION ITER THROW THROW + return false; + if (!emit1(JSOP_UNDEFINED)) // NEXT ITER RESULT EXCEPTION ITER THROW THROW UNDEFINED + return