author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Wed, 11 Feb 2015 14:58:16 +0100 | |
changeset 228606 | 38058cb42a0ee28016d2cc619568b45249202799 |
parent 228504 | 117e52087be31a7cba51699f9b8e73b9b9e0cf25 (current diff) |
parent 228605 | 0ebc9ae84e41d2eb6d21f0670a433605ac017dce (diff) |
child 228617 | 2411b6e3a97d5723a62118451e7b5a166c28a7e6 |
child 228634 | db5d46d32a331545a5abfc9f9018037dca3cad59 |
child 228673 | 037072f411fe811230f90b3904bb0b456abd4aed |
push id | 28264 |
push user | cbook@mozilla.com |
push date | Wed, 11 Feb 2015 13:58:35 +0000 |
treeherder | mozilla-central@38058cb42a0e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 38.0a1 |
first release with | nightly linux32
38058cb42a0e
/
38.0a1
/
20150211143627
/
files
nightly linux64
38058cb42a0e
/
38.0a1
/
20150211143627
/
files
nightly win64
38058cb42a0e
/
38.0a1
/
20150211143627
/
files
nightly mac
nightly win32
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
38.0a1
/
20150211143627
/
pushlog to previous
nightly linux64
38.0a1
/
20150211143627
/
pushlog to previous
nightly win64
38.0a1
/
20150211143627
/
pushlog to previous
|
js/src/jsproxy.h | file | annotate | diff | comparison | revisions |
--- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -314,17 +314,17 @@ pref("media.cache_readahead_limit", 30); // Enable/Disable Gonk Decoder Module pref("media.fragmented-mp4.gonk.enabled", true); #endif // The default number of decoded video frames that are enqueued in // MediaDecoderReader's mVideoQueue. pref("media.video-queue.default-size", 3); // optimize images' memory usage -pref("image.mem.decodeondraw", false); +pref("image.mem.decodeondraw", true); pref("image.mem.allow_locking_in_content_processes", false); /* don't allow image locking */ // Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller. // Almost everything that was factored into 'max_decoded_image_kb' is now stored // in the surface cache. 1/8 of main memory is 32MB on a 256MB device, which is // about the same as the old 'max_decoded_image_kb'. pref("image.mem.surfacecache.max_size_kb", 131072); // 128MB pref("image.mem.surfacecache.size_factor", 8); // 1/8 of main memory pref("image.mem.surfacecache.discard_factor", 2); // Discard 1/2 of the surface cache at a time.
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1646,19 +1646,25 @@ pref("pdfjs.disabled", false); // Used by pdf.js to know the first time firefox is run with it installed so it // can become the default pdf viewer. pref("pdfjs.firstRun", true); // The values of preferredAction and alwaysAskBeforeHandling before pdf.js // became the default. pref("pdfjs.previousHandler.preferredAction", 0); pref("pdfjs.previousHandler.alwaysAskBeforeHandling", false); +// Shumway is only bundled in Nightly. #ifdef NIGHTLY_BUILD -// Shumway component (SWF player) is disabled by default. Also see bug 904346. +// By default, Shumway (SWF player) is only enabled for whitelisted SWFs on Windows + OS X. +#ifdef UNIX_BUT_NOT_MAC pref("shumway.disabled", true); +#else +pref("shumway.disabled", false); +pref("shumway.swf.whitelist", "http://g-ecx.images-amazon.com/*/AiryBasicRenderer*.swf"); +#endif #endif // The maximum amount of decoded image data we'll willingly keep around (we // might keep around more than this, but we'll try to get down to this value). // (This is intentionally on the high side; see bug 746055.) pref("image.mem.max_decoded_image_kb", 256000); pref("loop.enabled", true);
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -872,17 +872,32 @@ function LoadInOtherProcess(browser, loa // history into it. SessionStore.setTabState(tab, JSON.stringify(tabState)); } } // Called when a docshell has attempted to load a page in an incorrect process. // This function is responsible for loading the page in the correct process. function RedirectLoad({ target: browser, data }) { - LoadInOtherProcess(browser, data.loadOptions, data.historyIndex); + // We should only start the redirection if the browser window has finished + // starting up. Otherwise, we should wait until the startup is done. + if (gBrowserInit.delayedStartupFinished) { + LoadInOtherProcess(browser, data.loadOptions, data.historyIndex); + } else { + let delayedStartupFinished = (subject, topic) => { + if (topic == "browser-delayed-startup-finished" && + subject == window) { + Services.obs.removeObserver(delayedStartupFinished, topic); + LoadInOtherProcess(browser, data.loadOptions, data.historyIndex); + } + }; + Services.obs.addObserver(delayedStartupFinished, + "browser-delayed-startup-finished", + false); + } } var gBrowserInit = { delayedStartupFinished: false, onLoad: function() { var mustLoadSidebar = false; @@ -901,16 +916,18 @@ var gBrowserInit = { LanguageDetectionListener.init(); BrowserOnClick.init(); DevEdition.init(); let mm = window.getGroupMessageManager("browsers"); mm.loadFrameScript("chrome://browser/content/content.js", true); mm.loadFrameScript("chrome://browser/content/content-UITour.js", true); + window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad); + // initialize observers and listeners // and give C++ access to gBrowser XULBrowserWindow.init(); window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(nsIWebNavigation) .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIXULWindow) @@ -918,18 +935,16 @@ var gBrowserInit = { window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess(); if (!gMultiProcessBrowser) { // There is a Content:Click message manually sent from content. Cc["@mozilla.org/eventlistenerservice;1"] .getService(Ci.nsIEventListenerService) .addSystemEventListener(gBrowser, "click", contentAreaClick, true); - } else { - gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, true); } // hook up UI through progress listener gBrowser.addProgressListener(window.XULBrowserWindow); gBrowser.addTabsProgressListener(window.TabsProgressListener); // setup simple gestures support gGestureSupport.init(true); @@ -1176,23 +1191,39 @@ var gBrowserInit = { // so that we don't disrupt startup try { gBrowser.loadTabs(specs, false, true); } catch (e) {} } else if (uriToLoad instanceof XULElement) { // swap the given tab with the default about:blank tab and then close // the original tab in the other window. + let tabToOpen = uriToLoad; // Stop the about:blank load gBrowser.stop(); // make sure it has a docshell gBrowser.docShell; - gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad); + // If the browser that we're swapping in was remote, then we'd better + // be able to support remote browsers, and then make our selectedTab + // remote. + try { + if (tabToOpen.linkedBrowser.isRemoteBrowser) { + if (!gMultiProcessBrowser) { + throw new Error("Cannot drag a remote browser into a window " + + "without the remote tabs load context."); + } + + gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, true); + } + gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToOpen); + } catch(e) { + Cu.reportError(e); + } } // window.arguments[2]: referrer (nsIURI) // [3]: postData (nsIInputStream) // [4]: allowThirdPartyFixup (bool) else if (window.arguments.length >= 3) { loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null, window.arguments[4] || false); window.focus(); @@ -1211,17 +1242,16 @@ var gBrowserInit = { Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false); Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false); window.messageManager.addMessageListener("Browser:URIFixup", gKeywordURIFixup); - window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad); BrowserOffline.init(); OfflineApps.init(); IndexedDBPromptHelper.init(); #ifdef E10S_TESTING_ONLY gRemoteTabsUI.init(); #endif @@ -4077,16 +4107,23 @@ var XULBrowserWindow = { var securityUI = gBrowser.securityUI; this.onSecurityChange(null, null, securityUI.state); }, setJSStatus: function () { // unsupported }, + forceInitialBrowserRemote: function() { + let initBrowser = + document.getAnonymousElementByAttribute(gBrowser, "anonid", "initialBrowser"); + gBrowser.updateBrowserRemoteness(initBrowser, true); + return initBrowser.frameLoader.tabParent; + }, + setDefaultStatus: function (status) { this.defaultStatus = status; this.updateStatusField(); }, setOverLink: function (url, anchorElt) { // Encode bidirectional formatting characters. // (RFC 3987 sections 3.2 and 4.1 paragraph 6) @@ -4732,17 +4769,17 @@ var TabsProgressListener = { } } function nsBrowserAccess() { } nsBrowserAccess.prototype = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]), - _openURIInNewTab: function(aURI, aReferrer, aIsPrivate, aIsExternal) { + _openURIInNewTab: function(aURI, aReferrer, aIsPrivate, aIsExternal, aForceNotRemote=false) { let win, needToFocusWin; // try the current window. if we're in a popup, fall back on the most recent browser window if (window.toolbar.visible) win = window; else { win = RecentWindow.getMostRecentBrowserWindow({private: aIsPrivate}); needToFocusWin = true; @@ -4759,29 +4796,44 @@ nsBrowserAccess.prototype = { return win.gBrowser.selectedBrowser; } let loadInBackground = gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground"); let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", { referrerURI: aReferrer, fromExternal: aIsExternal, - inBackground: loadInBackground}); + inBackground: loadInBackground, + forceNotRemote: aForceNotRemote}); let browser = win.gBrowser.getBrowserForTab(tab); if (needToFocusWin || (!loadInBackground && aIsExternal)) win.focus(); return browser; }, openURI: function (aURI, aOpener, aWhere, aContext) { + // This function should only ever be called if we're opening a URI + // from a non-remote browser window (via nsContentTreeOwner). + if (aOpener && Cu.isCrossProcessWrapper(aOpener)) { + Cu.reportError("nsBrowserAccess.openURI was passed a CPOW for aOpener. " + + "openURI should only ever be called from non-remote browsers."); + throw Cr.NS_ERROR_FAILURE; + } + var newWindow = null; var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL); + if (aOpener && isExternal) { + Cu.reportError("nsBrowserAccess.openURI did not expect an opener to be " + + "passed if the context is OPEN_EXTERNAL."); + throw Cr.NS_ERROR_FAILURE; + } + if (isExternal && aURI && aURI.schemeIs("chrome")) { dump("use --chrome command-line option to load external chrome urls\n"); return null; } if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) { if (isExternal && gPrefService.prefHasUserValue("browser.link.open_newwindow.override.external")) @@ -4800,17 +4852,24 @@ nsBrowserAccess.prototype = { features += ",private"; } // Pass all params to openDialog to ensure that "url" isn't passed through // loadOneOrMoreURIs, which splits based on "|" newWindow = openDialog(getBrowserURL(), "_blank", features, url, null, null, null); break; case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB : let referrer = aOpener ? makeURI(aOpener.location.href) : null; - let browser = this._openURIInNewTab(aURI, referrer, isPrivate, isExternal); + // If we have an opener, that means that the caller is expecting access + // to the nsIDOMWindow of the opened tab right away. For e10s windows, + // this means forcing the newly opened browser to be non-remote so that + // we can hand back the nsIDOMWindow. The XULBrowserWindow.shouldLoadURI + // will do the job of shuttling off the newly opened browser to run in + // the right process once it starts loading a URI. + let forceNotRemote = !!aOpener; + let browser = this._openURIInNewTab(aURI, referrer, isPrivate, isExternal, forceNotRemote); if (browser) newWindow = browser.contentWindow; break; default : // OPEN_CURRENTWINDOW or an illegal value newWindow = content; if (aURI) { let referrer = aOpener ? makeURI(aOpener.location.href) : null; let loadflags = isExternal ?
--- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -327,19 +327,19 @@ nsContextMenu.prototype = { if (this.inFrame) { if (mimeTypeIsTextBased(this.target.ownerDocument.contentType)) this.isFrameImage.removeAttribute('hidden'); else this.isFrameImage.setAttribute('hidden', 'true'); } // BiDi UI - this.showItem("context-sep-bidi", top.gBidiUI); + this.showItem("context-sep-bidi", !this.onNumeric && top.gBidiUI); this.showItem("context-bidi-text-direction-toggle", - this.onTextInput && top.gBidiUI); + this.onTextInput && !this.onNumeric && top.gBidiUI); this.showItem("context-bidi-page-direction-toggle", !this.onTextInput && top.gBidiUI); // SocialMarks. Marks does not work with text selections, only links. If // there is more than MENU_LIMIT providers, we show a submenu for them, // otherwise we have a menuitem per provider (added in SocialMarks class). let markProviders = SocialMarks.getProviders(); let enablePageMarks = markProviders.length > 0 && !(this.onLink || this.onImage @@ -558,16 +558,17 @@ nsContextMenu.prototype = { this.onImage = false; this.onLoadedImage = false; this.onCompletedImage = false; this.imageDescURL = ""; this.onCanvas = false; this.onVideo = false; this.onAudio = false; this.onTextInput = false; + this.onNumeric = false; this.onKeywordField = false; this.mediaURL = ""; this.onLink = false; this.onMailtoLink = false; this.onSaveableLink = false; this.link = null; this.linkURL = ""; this.linkURI = null; @@ -653,16 +654,17 @@ nsContextMenu.prototype = { this.onAudio = true; let mediaURL = this.target.currentSrc || this.target.src; if (this.isMediaURLReusable(mediaURL)) { this.mediaURL = mediaURL; } } else if (editFlags & (SpellCheckHelper.INPUT | SpellCheckHelper.TEXTAREA)) { this.onTextInput = (editFlags & SpellCheckHelper.TEXTINPUT) !== 0; + this.onNumeric = (editFlags & SpellCheckHelper.NUMERIC) !== 0; this.onEditableArea = (editFlags & SpellCheckHelper.EDITABLE) !== 0; if (this.onEditableArea) { if (this.isRemote) { InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo); } else { InlineSpellCheckerUI.init(this.target.QueryInterface(Ci.nsIDOMNSEditableElement).editor); InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
--- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1361,44 +1361,47 @@ <parameter name="aLoadInBackground"/> <parameter name="aAllowThirdPartyFixup"/> <body> <![CDATA[ var aFromExternal; var aRelatedToCurrent; var aAllowMixedContent; var aSkipAnimation; + var aForceNotRemote; if (arguments.length == 2 && typeof arguments[1] == "object" && !(arguments[1] instanceof Ci.nsIURI)) { let params = arguments[1]; aReferrerURI = params.referrerURI; aCharset = params.charset; aPostData = params.postData; aLoadInBackground = params.inBackground; aAllowThirdPartyFixup = params.allowThirdPartyFixup; aFromExternal = params.fromExternal; aRelatedToCurrent = params.relatedToCurrent; aAllowMixedContent = params.allowMixedContent; aSkipAnimation = params.skipAnimation; + aForceNotRemote = params.forceNotRemote; } var bgLoad = (aLoadInBackground != null) ? aLoadInBackground : Services.prefs.getBoolPref("browser.tabs.loadInBackground"); var owner = bgLoad ? null : this.selectedTab; var tab = this.addTab(aURI, { referrerURI: aReferrerURI, charset: aCharset, postData: aPostData, ownerTab: owner, allowThirdPartyFixup: aAllowThirdPartyFixup, fromExternal: aFromExternal, relatedToCurrent: aRelatedToCurrent, skipAnimation: aSkipAnimation, - allowMixedContent: aAllowMixedContent }); + allowMixedContent: aAllowMixedContent, + forceNotRemote: aForceNotRemote }); if (!bgLoad) this.selectedTab = tab; return tab; ]]> </body> </method> @@ -1667,29 +1670,31 @@ <parameter name="aAllowThirdPartyFixup"/> <body> <![CDATA[ const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; var aFromExternal; var aRelatedToCurrent; var aSkipAnimation; var aAllowMixedContent; + var aForceNotRemote; if (arguments.length == 2 && typeof arguments[1] == "object" && !(arguments[1] instanceof Ci.nsIURI)) { let params = arguments[1]; aReferrerURI = params.referrerURI; aCharset = params.charset; aPostData = params.postData; aOwner = params.ownerTab; aAllowThirdPartyFixup = params.allowThirdPartyFixup; aFromExternal = params.fromExternal; aRelatedToCurrent = params.relatedToCurrent; aSkipAnimation = params.skipAnimation; aAllowMixedContent = params.allowMixedContent; + aForceNotRemote = params.forceNotRemote; } // if we're adding tabs, we're past interrupt mode, ditch the owner if (this.mCurrentTab.owner) this.mCurrentTab.owner = null; var t = document.createElementNS(NS_XUL, "tab"); @@ -1697,16 +1702,17 @@ t.setAttribute("crop", "end"); t.setAttribute("onerror", "this.removeAttribute('image');"); t.className = "tabbrowser-tab"; // The new browser should be remote if this is an e10s window and // the uri to load can be loaded remotely. let remote = gMultiProcessBrowser && + !aForceNotRemote && E10SUtils.canLoadURIInProcess(aURI, Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT); if (remote) t.setAttribute("remote", "true"); this.tabContainer._unlockTabSizing(); // When overflowing, new tabs are scrolled into view smoothly, which // doesn't go well together with the width transition. So we skip the
--- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -208,17 +208,16 @@ skip-if = e10s # Bug 1056146 - zoom test skip-if = e10s # Bug 1093373 - relies on browser.sessionHistory [browser_bug556061.js] [browser_bug559991.js] [browser_bug561623.js] skip-if = e10s [browser_bug561636.js] skip-if = e10s # Bug 1093677 - automated form submission from the test doesn't seem to quite work yet [browser_bug562649.js] -skip-if = e10s # Bug 940195 - XULBrowserWindow.isBusy is false as a remote tab starts loading [browser_bug563588.js] [browser_bug565575.js] skip-if = e10s [browser_bug565667.js] skip-if = toolkit != "cocoa" [browser_bug567306.js] skip-if = e10s # Bug XXX - Needs some massaging to run in e10s [browser_bug575561.js] @@ -255,35 +254,34 @@ skip-if = buildapp == 'mulet' || e10s # [browser_bug655584.js] skip-if = e10s [browser_bug664672.js] [browser_bug676619.js] skip-if = buildapp == 'mulet' || os == "mac" # mac: Intermittent failures, bug 925225 [browser_bug678392.js] skip-if = e10s # bug 1102331 - does focus things on the content window which break in e10s mode [browser_bug710878.js] -skip-if = e10s # Bug 1100653 - test uses waitForFocus on content [browser_bug719271.js] skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s [browser_bug724239.js] [browser_bug734076.js] skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode [browser_bug735471.js] [browser_bug749738.js] -skip-if = e10s # Bug 921935 - focusmanager issues with e10s [browser_bug763468_perwindowpb.js] skip-if = e10s [browser_bug767836_perwindowpb.js] [browser_bug771331.js] [browser_bug783614.js] [browser_bug817947.js] [browser_bug822367.js] [browser_bug832435.js] [browser_bug839103.js] [browser_bug880101.js] +skip-if = e10s # Bug 1126316 - New e10s windows erroneously fire initial about:blank location through nsIWebProgressListener [browser_bug882977.js] [browser_bug902156.js] [browser_bug906190.js] skip-if = buildapp == "mulet" || e10s # Bug 1093642 - test manipulates content and relies on content focus [browser_bug970746.js] skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode [browser_bug1015721.js] skip-if = os == 'win' || e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s @@ -422,16 +420,18 @@ skip-if = os == "linux" || os == "mac" # [browser_tabfocus.js] skip-if = e10s # Bug 921935 - focusmanager issues with e10s (test calls getFocusedElementForWindow with a content window) [browser_tabkeynavigation.js] skip-if = e10s [browser_tabopen_reflows.js] [browser_tabs_isActive.js] skip-if = e10s # Bug 1100664 - test relies on linkedBrowser.docShell [browser_tabs_owner.js] +[browser_testOpenNewRemoteTabsFromNonRemoteBrowsers.js] +skip-if = !e10s && os == "linux" # Bug 994541 - Need OMTC enabled by default on Linux, or else we get blocked by an alert dialog when opening e10s window. [browser_trackingUI.js] support-files = trackingPage.html benignPage.html [browser_typeAheadFind.js] skip-if = buildapp == 'mulet' || e10s # Bug 921935 - focusmanager issues with e10s (test calls waitForFocus) [browser_unloaddialogs.js] skip-if = e10s # Bug 1100700 - test relies on unload event firing on closed tabs, which it doesn't
--- a/browser/base/content/test/general/browser_bug562649.js +++ b/browser/base/content/test/general/browser_bug562649.js @@ -1,16 +1,15 @@ function test() { const URI = "data:text/plain,bug562649"; browserDOMWindow.openURI(makeURI(URI), null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB, Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL); - ok(XULBrowserWindow.isBusy, "window is busy loading a page"); is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI"); is(gURLBar.value, URI, "location bar value matches test URI"); gBrowser.selectedTab = gBrowser.addTab(); gBrowser.removeCurrentTab(); is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI after switching tabs"); is(gURLBar.value, URI, "location bar value matches test URI after switching tabs");
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/general/browser_testOpenNewRemoteTabsFromNonRemoteBrowsers.js @@ -0,0 +1,124 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const OPEN_LOCATION_PREF = "browser.link.open_newwindow"; +const NON_REMOTE_PAGE = "about:welcomeback"; + +Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); + +function frame_script() { + content.document.body.innerHTML = ` + <a href="about:home" target="_blank" id="testAnchor">Open a window</a> + `; + + let element = content.document.getElementById("testAnchor"); + element.click(); +} + +/** + * Takes some browser in some window, and forces that browser + * to become non-remote, and then navigates it to a page that + * we're not supposed to be displaying remotely. Returns a + * Promise that resolves when the browser is no longer remote. + */ +function prepareNonRemoteBrowser(aWindow, browser) { + browser.loadURI(NON_REMOTE_PAGE); + return waitForDocLoadComplete(browser); +} + +registerCleanupFunction(() => { + Services.prefs.clearUserPref(OPEN_LOCATION_PREF); +}); + +/** + * Test that if we open a new tab from a link in a non-remote + * browser in an e10s window, that the new tab will load properly. + */ +add_task(function* test_new_tab() { + let normalWindow = yield promiseOpenAndLoadWindow({ + remote: true + }, true); + let privateWindow = yield promiseOpenAndLoadWindow({ + remote: true, + private: true, + }, true); + + for (let testWindow of [normalWindow, privateWindow]) { + yield promiseWaitForFocus(testWindow); + let testBrowser = testWindow.gBrowser.selectedBrowser; + info("Preparing non-remote browser"); + yield prepareNonRemoteBrowser(testWindow, testBrowser); + info("Non-remote browser prepared - sending frame script"); + + // Get our framescript ready + let mm = testBrowser.messageManager; + mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", true); + + let tabOpenEvent = yield waitForNewTabEvent(testWindow.gBrowser); + let newTab = tabOpenEvent.target; + + yield promiseTabLoadEvent(newTab); + + // Our framescript opens to about:home which means that the + // tab should eventually become remote. + ok(newTab.linkedBrowser.isRemoteBrowser, + "The opened browser never became remote."); + + testWindow.gBrowser.removeTab(newTab); + } + + normalWindow.close(); + privateWindow.close(); +}); + +/** + * Test that if we open a new window from a link in a non-remote + * browser in an e10s window, that the new window is not an e10s + * window. Also tests with a private browsing window. + */ +add_task(function* test_new_window() { + let normalWindow = yield promiseOpenAndLoadWindow({ + remote: true + }, true); + let privateWindow = yield promiseOpenAndLoadWindow({ + remote: true, + private: true, + }, true); + + // Fiddle with the prefs so that we open target="_blank" links + // in new windows instead of new tabs. + Services.prefs.setIntPref(OPEN_LOCATION_PREF, + Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW); + + for (let testWindow of [normalWindow, privateWindow]) { + yield promiseWaitForFocus(testWindow); + let testBrowser = testWindow.gBrowser.selectedBrowser; + yield prepareNonRemoteBrowser(testWindow, testBrowser); + + // Get our framescript ready + let mm = testBrowser.messageManager; + mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", true); + + // Click on the link in the browser, and wait for the new window. + let {subject: newWindow} = + yield promiseTopicObserved("browser-delayed-startup-finished"); + + is(PrivateBrowsingUtils.isWindowPrivate(testWindow), + PrivateBrowsingUtils.isWindowPrivate(newWindow), + "Private browsing state of new window does not match the original!"); + + let newTab = newWindow.gBrowser.selectedTab; + + yield promiseTabLoadEvent(newTab); + + // Our framescript opens to about:home which means that the + // tab should eventually become remote. + ok(newTab.linkedBrowser.isRemoteBrowser, + "The opened browser never became remote."); + newWindow.close(); + } + + normalWindow.close(); + privateWindow.close(); +});
--- a/browser/base/content/test/general/head.js +++ b/browser/base/content/test/general/head.js @@ -118,16 +118,33 @@ function promiseWaitForEvent(object, eve resolve(event); } info("Waiting for " + eventName); object.addEventListener(eventName, listener, capturing, chrome); }); } +/** + * Allows setting focus on a window, and waiting for that window to achieve + * focus. + * + * @param aWindow + * The window to focus and wait for. + * + * @return {Promise} + * @resolves When the window is focused. + * @rejects Never. + */ +function promiseWaitForFocus(aWindow) { + return new Promise((resolve) => { + waitForFocus(resolve, aWindow); + }); +} + function getTestPlugin(aName) { var pluginName = aName || "Test Plug-in"; var ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); var tags = ph.getPluginTags(); // Find the test plugin for (var i = 0; i < tags.length; i++) { if (tags[i].name == pluginName) @@ -567,16 +584,31 @@ function promiseTabLoadEvent(tab, url, e }, 30000); tab.linkedBrowser.addEventListener(eventType, handle, true, true); if (url) tab.linkedBrowser.loadURI(url); return deferred.promise; } +/** + * Returns a Promise that resolves once a new tab has been opened in + * a xul:tabbrowser. + * + * @param aTabBrowser + * The xul:tabbrowser to monitor for a new tab. + * @return {Promise} + * Resolved when the new tab has been opened. + * @resolves to the TabOpen event that was fired. + * @rejects Never. + */ +function waitForNewTabEvent(aTabBrowser) { + return promiseWaitForEvent(aTabBrowser.tabContainer, "TabOpen"); +} + function assertWebRTCIndicatorStatus(expected) { let ui = Cu.import("resource:///modules/webrtcUI.jsm", {}).webrtcUI; let expectedState = expected ? "visible" : "hidden"; let msg = "WebRTC indicator " + expectedState; is(ui.showGlobalIndicator, !!expected, msg); let expectVideo = false, expectAudio = false, expectScreen = false; if (expected) { @@ -740,8 +772,31 @@ function promiseAutocompleteResultPopup( waitForFocus(() => { win.gURLBar.focus(); win.gURLBar.value = inputText; win.gURLBar.controller.startSearch(inputText); }, win); return promiseSearchComplete(win); } + +/** + * Allows waiting for an observer notification once. + * + * @param aTopic + * Notification topic to observe. + * + * @return {Promise} + * @resolves An object with subject and data properties from the observed + * notification. + * @rejects Never. + */ +function promiseTopicObserved(aTopic) +{ + return new Promise((resolve) => { + Services.obs.addObserver( + function PTO_observe(aSubject, aTopic, aData) { + Services.obs.removeObserver(PTO_observe, aTopic); + resolve({subject: aSubject, data: aData}); + }, aTopic, false); + }); +} +
--- a/browser/base/content/test/general/test_contextmenu_input.html +++ b/browser/base/content/test/general/test_contextmenu_input.html @@ -155,17 +155,18 @@ function runTest(testNum) { input.type = 'password'; openContextMenuFor(input); // Invoke context menu for next test. break; case 7: // password case 8: // email case 9: // url case 10: // tel - // Context menu for tel, password, email and url input fields. + case 11: // type='number' + // Context menu for tel, password, email, url and number input fields. checkContextMenu(["context-undo", false, "---", null, "context-cut", false, "context-copy", false, "context-paste", null, // ignore clipboard state "context-delete", false, "---", null, "context-selectall", false, @@ -176,25 +177,26 @@ function runTest(testNum) { if (testNum == 7) { input.type = 'email'; } else if (testNum == 8) { input.type = 'url'; } else if (testNum == 9) { input.type = 'tel'; } else if (testNum == 10) { + input.type = 'number'; + } else if (testNum == 11) { input.type = 'date'; } openContextMenuFor(input); // Invoke context menu for next test. break; - case 11: // type='date' - case 12: // type='time' - case 13: // type='number' + case 12: // type='date' + case 13: // type='time' case 14: // type='color' case 15: // type='range' checkContextMenu(["context-navigation", null, ["context-back", false, "context-forward", false, "context-reload", true, "context-bookmarkpage", true], null, "---", null, @@ -204,20 +206,18 @@ function runTest(testNum) { "context-selectall", null, "---", null, "context-viewsource", true, "context-viewinfo", true, "---", null, "context-inspect", true]); closeContextMenu(); - if (testNum == 11) { + if (testNum == 12) { input.type = 'time'; - } else if (testNum == 12) { - input.type = 'number'; } else if (testNum == 13) { input.type = 'color'; } else if (testNum == 14) { input.type = 'range'; } else { input.type = 'search'; }
--- a/browser/components/sessionstore/test/browser_394759_behavior.js +++ b/browser/components/sessionstore/test/browser_394759_behavior.js @@ -34,21 +34,33 @@ function test() { // hack to force window to be considered a popup (toolbar=no didn't work) let winData = windowsToOpen.shift(); let settings = "chrome,dialog=no," + (winData.isPopup ? "all=no" : "all"); let url = "http://example.com/?window=" + windowsToOpen.length; provideWindow(function onTestURLLoaded(win) { - win.close(); - // Give it time to close - executeSoon(function() { - openWindowRec(windowsToOpen, expectedResults, recCallback); - }); + let tabReady = () => { + win.close(); + // Give it time to close + executeSoon(function() { + openWindowRec(windowsToOpen, expectedResults, recCallback); + }); + }; + + if (win.gMultiProcessBrowser) { + let tab = win.gBrowser.selectedTab; + tab.addEventListener("SSTabRestored", function onTabRestored() { + tab.removeEventListener("SSTabRestored", onTabRestored); + tabReady(); + }); + } else { + tabReady(); + } }, url, settings); } let windowsToOpen = [{isPopup: false}, {isPopup: false}, {isPopup: true}, {isPopup: true}, {isPopup: true}];
--- a/browser/components/sessionstore/test/browser_423132.js +++ b/browser/components/sessionstore/test/browser_423132.js @@ -25,51 +25,60 @@ function test() { newWin.addEventListener("load", function (aEvent) { newWin.removeEventListener("load", arguments.callee, false); // Wait for sessionstore to be ready to restore this window executeSoon(function() { newWin.gBrowser.loadURI(testURL, null, null); promiseBrowserLoaded(newWin.gBrowser.selectedBrowser).then(() => { - // get the sessionstore state for the window - TabState.flush(newWin.gBrowser.selectedBrowser); - let state = ss.getWindowState(newWin); + let ready = () => { + // get the sessionstore state for the window + TabState.flush(newWin.gBrowser.selectedBrowser); + let state = ss.getWindowState(newWin); - // verify our cookie got set during pageload - let e = cs.enumerator; - let cookie; - let i = 0; - while (e.hasMoreElements()) { - cookie = e.getNext().QueryInterface(Ci.nsICookie); - i++; - } - is(i, 1, "expected one cookie"); + // verify our cookie got set during pageload + let e = cs.enumerator; + let cookie; + let i = 0; + while (e.hasMoreElements()) { + cookie = e.getNext().QueryInterface(Ci.nsICookie); + i++; + } + is(i, 1, "expected one cookie"); - // remove the cookie - cs.removeAll(); + // remove the cookie + cs.removeAll(); + + // restore the window state + ss.setWindowState(newWin, state, true); - // restore the window state - ss.setWindowState(newWin, state, true); + // at this point, the cookie should be restored... + e = cs.enumerator; + let cookie2; + while (e.hasMoreElements()) { + cookie2 = e.getNext().QueryInterface(Ci.nsICookie); + if (cookie.name == cookie2.name) + break; + } + is(cookie.name, cookie2.name, "cookie name successfully restored"); + is(cookie.value, cookie2.value, "cookie value successfully restored"); + is(cookie.path, cookie2.path, "cookie path successfully restored"); - // at this point, the cookie should be restored... - e = cs.enumerator; - let cookie2; - while (e.hasMoreElements()) { - cookie2 = e.getNext().QueryInterface(Ci.nsICookie); - if (cookie.name == cookie2.name) - break; + // clean up + if (gPrefService.prefHasUserValue("browser.sessionstore.interval")) + gPrefService.clearUserPref("browser.sessionstore.interval"); + cs.removeAll(); + newWin.close(); + finish(); + }; + + if (newWin.gMultiProcessBrowser) { + let tab = newWin.gBrowser.selectedTab; + promiseTabRestored(tab).then(ready); + } else { + ready(); } - is(cookie.name, cookie2.name, "cookie name successfully restored"); - is(cookie.value, cookie2.value, "cookie value successfully restored"); - is(cookie.path, cookie2.path, "cookie path successfully restored"); - - // clean up - if (gPrefService.prefHasUserValue("browser.sessionstore.interval")) - gPrefService.clearUserPref("browser.sessionstore.interval"); - cs.removeAll(); - newWin.close(); - finish(); }, true, testURL); }); }, false); }
--- a/browser/devtools/canvasdebugger/canvasdebugger.js +++ b/browser/devtools/canvasdebugger/canvasdebugger.js @@ -411,20 +411,27 @@ let SnapshotsListView = Heritage.extend( fp.init(window, L10N.getStr("snapshotsList.saveDialogTitle"), Ci.nsIFilePicker.modeOpen); fp.appendFilter(L10N.getStr("snapshotsList.saveDialogJSONFilter"), "*.json"); fp.appendFilter(L10N.getStr("snapshotsList.saveDialogAllFilter"), "*.*"); if (fp.show() != Ci.nsIFilePicker.returnOK) { return; } - let channel = NetUtil.newChannel(fp.file); + let channel = NetUtil.newChannel2(fp.file, + null, + null, + window.document, + null, // aLoadingPrincipal + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); channel.contentType = "text/plain"; - NetUtil.asyncFetch(channel, (inputStream, status) => { + NetUtil.asyncFetch2(channel, (inputStream, status) => { if (!Components.isSuccessCode(status)) { console.error("Could not import recorded animation frame snapshot file."); return; } try { let string = NetUtil.readInputStreamToString(inputStream, inputStream.available()); var data = JSON.parse(string); } catch (e) {
--- a/browser/devtools/profiler/ui-recordings.js +++ b/browser/devtools/profiler/ui-recordings.js @@ -321,20 +321,27 @@ function saveRecordingToFile(recordingIt * The file to import the data from. * @return object * A promise that is resolved once importing finishes, or rejected * if there was an error. */ function loadRecordingFromFile(file) { let deferred = promise.defer(); - let channel = NetUtil.newChannel(file); + let channel = NetUtil.newChannel2(file, + null, + null, + window.document, + null, // aLoadingPrincipal + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); channel.contentType = "text/plain"; - NetUtil.asyncFetch(channel, (inputStream, status) => { + NetUtil.asyncFetch2(channel, (inputStream, status) => { if (!Components.isSuccessCode(status)) { deferred.reject(new Error("Could not import recording data file.")); return; } try { let string = NetUtil.readInputStreamToString(inputStream, inputStream.available()); var recordingData = JSON.parse(string); } catch (e) {
--- a/browser/devtools/projecteditor/test/head.js +++ b/browser/devtools/projecteditor/test/head.js @@ -255,32 +255,37 @@ function getTempFile(path) { // https://developer.mozilla.org/en-US/Add-ons/Code_snippets/File_I_O#Writing_to_a_file function* getFileData(file) { if (typeof file === "string") { file = new FileUtils.File(file); } let def = promise.defer(); - NetUtil.asyncFetch(file, function(inputStream, status) { + NetUtil.asyncFetch2(file, function(inputStream, status) { if (!Components.isSuccessCode(status)) { info("ERROR READING TEMP FILE", status); } // Detect if an empty file is loaded try { inputStream.available(); } catch(e) { def.resolve(""); return; } var data = NetUtil.readInputStreamToString(inputStream, inputStream.available()); def.resolve(data); - }); + }, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); return def.promise; } function onceEditorCreated(projecteditor) { let def = promise.defer(); projecteditor.once("onEditorCreated", (editor) => { def.resolve(editor);
--- a/browser/devtools/scratchpad/scratchpad.js +++ b/browser/devtools/scratchpad/scratchpad.js @@ -1128,22 +1128,29 @@ var Scratchpad = { * Optional function you want to call when file load completes. It will * get the following arguments: * 1) the nsresult status code for the import operation. * 2) the data that was read from the file, if any. */ importFromFile: function SP_importFromFile(aFile, aSilentError, aCallback) { // Prevent file type detection. - let channel = NetUtil.newChannel(aFile); + let channel = NetUtil.newChannel2(aFile, + null, + null, + window.document, + null, // aLoadingPrincipal + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); channel.contentType = "application/javascript"; this.notificationBox.removeAllNotifications(false); - NetUtil.asyncFetch(channel, (aInputStream, aStatus) => { + NetUtil.asyncFetch2(channel, (aInputStream, aStatus) => { let content = null; if (Components.isSuccessCode(aStatus)) { let charsets = this._getApplicableCharsets(); content = NetUtil.readInputStreamToString(aInputStream, aInputStream.available()); content = this._getUnicodeContent(content, charsets); if (!content) {
--- a/browser/devtools/scratchpad/test/browser_scratchpad_confirm_close.js +++ b/browser/devtools/scratchpad/test/browser_scratchpad_confirm_close.js @@ -209,20 +209,27 @@ function writeFile(file, content, callba converter.charset = "UTF-8"; let fileContentStream = converter.convertToInputStream(content); NetUtil.asyncCopy(fileContentStream, fout, callback); } function readFile(file, callback) { - let channel = NetUtil.newChannel(file); + let channel = NetUtil.newChannel2(file, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); channel.contentType = "application/javascript"; - NetUtil.asyncFetch(channel, function(inputStream, status) { + NetUtil.asyncFetch2(channel, function(inputStream, status) { ok(Components.isSuccessCode(status), "file was read successfully"); let content = NetUtil.readInputStreamToString(inputStream, inputStream.available()); callback(content); }); }
--- a/browser/devtools/scratchpad/test/browser_scratchpad_files.js +++ b/browser/devtools/scratchpad/test/browser_scratchpad_files.js @@ -86,21 +86,28 @@ function fileExported(aStatus) fileExported2); gScratchpadWindow.confirm = oldConfirm; ok(askedConfirmation, "exportToFile() asked for overwrite confirmation"); gFileContent = oldContent; - let channel = NetUtil.newChannel(gFile); + let channel = NetUtil.newChannel2(gFile, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); channel.contentType = "application/javascript"; // Read back the temporary file. - NetUtil.asyncFetch(channel, fileRead); + NetUtil.asyncFetch2(channel, fileRead); } function fileExported2() { ok(false, "exportToFile() did not cancel file overwrite"); } function fileRead(aInputStream, aStatus)
--- a/browser/devtools/shared/AppCacheUtils.jsm +++ b/browser/devtools/shared/AppCacheUtils.jsm @@ -180,17 +180,24 @@ AppCacheUtils.prototype = { return deferred.promise; }, _getURIInfo: function ACU__getURIInfo(uri) { let inputStream = Cc["@mozilla.org/scriptableinputstream;1"] .createInstance(Ci.nsIScriptableInputStream); let deferred = promise.defer(); let buffer = ""; - let channel = Services.io.newChannel(uri, null, null); + let channel = Services.io.newChannel2(uri, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); // Avoid the cache: channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING; channel.asyncOpen({ onStartRequest: function (request, context) { // This empty method is needed in order for onDataAvailable to be
--- a/browser/devtools/sourceeditor/test/head.js +++ b/browser/devtools/sourceeditor/test/head.js @@ -82,17 +82,24 @@ function limit(source, [line, ch]) { return list[0].slice(0, ch); return [...list.slice(0, line - 1), list[line - 1].slice(0, ch)].join("\n"); } function read(url) { let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"] .getService(Ci.nsIScriptableInputStream); - let channel = Services.io.newChannel(url, null, null); + let channel = Services.io.newChannel2(url, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); let input = channel.open(); scriptableStream.init(input); let data = ""; while (input.available()) { data = data.concat(scriptableStream.read(input.available())); } scriptableStream.close();
--- a/browser/devtools/styleeditor/StyleEditorUI.jsm +++ b/browser/devtools/styleeditor/StyleEditorUI.jsm @@ -334,29 +334,33 @@ StyleEditorUI.prototype = { * Optional parent window for the file picker. */ _importFromFile: function(file, parentWindow) { let onFileSelected = (file) => { if (!file) { // nothing selected return; } - NetUtil.asyncFetch(file, (stream, status) => { + NetUtil.asyncFetch2(file, (stream, status) => { if (!Components.isSuccessCode(status)) { this.emit("error", { key: LOAD_ERROR }); return; } let source = NetUtil.readInputStreamToString(stream, stream.available()); stream.close(); this._debuggee.addStyleSheet(source).then((styleSheet) => { this._onStyleSheetCreated(styleSheet, file); }); - }); - + }, + this._window.document, + null, // aLoadingPrincipal + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); }; showFilePicker(file, false, parentWindow, onFileSelected); }, /** * When a new or imported stylesheet has been added to the document.
--- a/browser/devtools/styleeditor/test/browser_styleeditor_filesave.js +++ b/browser/devtools/styleeditor/test/browser_styleeditor_filesave.js @@ -60,17 +60,24 @@ function copy(aSrcChromeURL, aDestFileNa write(read(aSrcChromeURL), destFile, aCallback); } function read(aSrcChromeURL) { let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"] .getService(Ci.nsIScriptableInputStream); - let channel = Services.io.newChannel(aSrcChromeURL, null, null); + let channel = Services.io.newChannel2(aSrcChromeURL, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); let input = channel.open(); scriptableStream.init(input); let data = ""; while (input.available()) { data = data.concat(scriptableStream.read(input.available())); } scriptableStream.close();
--- a/browser/devtools/styleeditor/test/browser_styleeditor_sourcemap_watching.js +++ b/browser/devtools/styleeditor/test/browser_styleeditor_sourcemap_watching.js @@ -145,17 +145,24 @@ function copy(aSrcChromeURL, aDestFilePa return write(read(aSrcChromeURL), destFile); } function read(aSrcChromeURL) { let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"] .getService(Ci.nsIScriptableInputStream); - let channel = Services.io.newChannel(aSrcChromeURL, null, null); + let channel = Services.io.newChannel2(aSrcChromeURL, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); let input = channel.open(); scriptableStream.init(input); let data = ""; while (input.available()) { data = data.concat(scriptableStream.read(input.available())); } scriptableStream.close();
--- a/build/mach_bootstrap.py +++ b/build/mach_bootstrap.py @@ -43,16 +43,17 @@ SEARCH_PATHS = [ 'layout/tools/reftest', 'other-licenses/ply', 'xpcom/idl-parser', 'testing', 'testing/taskcluster', 'testing/xpcshell', 'testing/web-platform', 'testing/web-platform/harness', + 'testing/marionette/client', 'testing/marionette/client/marionette', 'testing/marionette/transport', 'testing/mozbase/mozcrash', 'testing/mozbase/mozdebug', 'testing/mozbase/mozdevice', 'testing/mozbase/mozfile', 'testing/mozbase/mozhttpd', 'testing/mozbase/mozlog',
--- a/build/moz-automation.mk +++ b/build/moz-automation.mk @@ -76,16 +76,20 @@ automation/upload: automation/package-te automation/upload: automation/buildsymbols automation/upload: automation/update-packaging # automation/{pretty-}package should depend on build (which is implicit due to # the way client.mk invokes automation/build), but buildsymbols changes the # binaries/libs, and that's what we package/test. automation/pretty-package: automation/buildsymbols +# The installer and packager both run stage-package, and may conflict +# with each other. +automation/installer: automation/package + # The 'pretty' versions of targets run before the regular ones to avoid # conflicts in writing to the same files. automation/installer: automation/pretty-installer automation/package: automation/pretty-package automation/package-tests: automation/pretty-package-tests automation/l10n-check: automation/pretty-l10n-check automation/update-packaging: automation/pretty-update-packaging
--- a/config/external/nss/nss.def +++ b/config/external/nss/nss.def @@ -672,12 +672,12 @@ SSL_VersionRangeSet SSL_VersionRangeSetDefault UTIL_SetForkState VFY_Begin VFY_CreateContext VFY_DestroyContext VFY_End VFY_Update VFY_VerifyData -VFY_VerifyDataDirect VFY_VerifyDataWithAlgorithmID +VFY_VerifyDigestDirect _SGN_VerifyPKCS1DigestInfo PK11_PQG_ParamGenV2
--- a/config/gcc-stl-wrapper.template.h +++ b/config/gcc-stl-wrapper.template.h @@ -5,17 +5,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/. */ #ifndef mozilla_${HEADER}_h #define mozilla_${HEADER}_h // For some reason, Apple's GCC refuses to honor -fno-exceptions when // compiling ObjC. -#if __EXCEPTIONS && !(__OBJC__ && __GNUC__ && XP_IOS) +#if defined(__EXCEPTIONS) && __EXCEPTIONS && !(__OBJC__ && __GNUC__ && XP_IOS) # error "STL code can only be used with -fno-exceptions" #endif // Silence "warning: #include_next is a GCC extension" #pragma GCC system_header #ifdef _WIN32 // Suppress windef.h min and max macros - they make std::min/max not compile.
--- a/configure.in +++ b/configure.in @@ -617,18 +617,18 @@ See https://developer.mozilla.org/en/Win [std::vector<int> v; return v.at(1);], ac_cv_have_dllimport_exception_bug="no", ac_cv_have_dllimport_exception_bug="yes") CXXFLAGS="$_SAVE_CXXFLAGS" AC_LANG_RESTORE ]) if test "$ac_cv_have_dllimport_exception_bug" = "no"; then WRAP_STL_INCLUDES=1 - MOZ_MSVC_STL_WRAP__Throw=1 - AC_DEFINE(MOZ_MSVC_STL_WRAP__Throw) + MOZ_MSVC_STL_WRAP_Throw=1 + AC_DEFINE(MOZ_MSVC_STL_WRAP_Throw) fi else AC_CACHE_CHECK(for overridable _RAISE, ac_cv_have__RAISE, [ AC_LANG_SAVE AC_LANG_CPLUSPLUS _SAVE_CXXFLAGS="$CXXFLAGS" @@ -641,18 +641,18 @@ See https://developer.mozilla.org/en/Win [std::vector<int> v; return v.at(1);], ac_cv_have__RAISE="no", ac_cv_have__RAISE="yes") CXXFLAGS="$_SAVE_CXXFLAGS" AC_LANG_RESTORE ]) if test "$ac_cv_have__RAISE" = "yes"; then WRAP_STL_INCLUDES=1 - MOZ_MSVC_STL_WRAP__RAISE=1 - AC_DEFINE(MOZ_MSVC_STL_WRAP__RAISE) + MOZ_MSVC_STL_WRAP_RAISE=1 + AC_DEFINE(MOZ_MSVC_STL_WRAP_RAISE) else AC_MSG_ERROR([Gecko exception wrapping doesn't understand your your MSVC/SDK. Please file a bug describing this error and your build configuration.]) fi fi if test "$WRAP_STL_INCLUDES" = "1"; then STL_FLAGS='-I$(DIST)/stl_wrappers' fi @@ -762,18 +762,18 @@ AC_SUBST(GNU_AS) AC_SUBST(GNU_LD) AC_SUBST(GNU_CC) AC_SUBST(GNU_CXX) AC_SUBST(INTEL_CC) AC_SUBST(INTEL_CXX) AC_SUBST(STL_FLAGS) AC_SUBST(WRAP_STL_INCLUDES) -AC_SUBST(MOZ_MSVC_STL_WRAP__Throw) -AC_SUBST(MOZ_MSVC_STL_WRAP__RAISE) +AC_SUBST(MOZ_MSVC_STL_WRAP_Throw) +AC_SUBST(MOZ_MSVC_STL_WRAP_RAISE) dnl ======================================================== dnl Checks for programs. dnl ======================================================== AC_PROG_INSTALL AC_PROG_LN_S AC_MSG_CHECKING([for minimum required perl version >= $PERL_VERSION]) @@ -7278,17 +7278,17 @@ if test -f "${srcdir}/${MOZ_BUILD_APP}/c rm -f $tmpscript fi dnl We need to wrap dlopen and related functions on Android because we use dnl our own linker. if test "$OS_TARGET" = Android; then MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=PR_GetEnv,--wrap=PR_SetEnv" if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then - MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2" + MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_mutex_trylock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2" fi if test "$MOZ_WIDGET_TOOLKIT" = android; then MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=getaddrinfo,--wrap=freeaddrinfo,--wrap=gai_strerror" fi fi AC_SUBST_LIST(MOZ_GLUE_WRAP_LDFLAGS) export MOZ_GLUE_WRAP_LDFLAGS
--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -13749,29 +13749,16 @@ nsDocShell::SetOpener(nsITabParent* aOpe nsITabParent* nsDocShell::GetOpener() { nsCOMPtr<nsITabParent> opener(do_QueryReferent(mOpener)); return opener; } -void -nsDocShell::SetOpenedRemote(nsITabParent* aOpenedRemote) -{ - mOpenedRemote = do_GetWeakReference(aOpenedRemote); -} - -nsITabParent* -nsDocShell::GetOpenedRemote() -{ - nsCOMPtr<nsITabParent> openedRemote(do_QueryReferent(mOpenedRemote)); - return openedRemote; -} - URLSearchParams* nsDocShell::GetURLSearchParams() { return mURLSearchParams; } void nsDocShell::NotifyJSRunToCompletionStart()
--- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -958,17 +958,16 @@ private: nsCString mParentCharset; int32_t mParentCharsetSource; nsCOMPtr<nsIPrincipal> mParentCharsetPrincipal; nsTObserverArray<nsWeakPtr> mPrivacyObservers; nsTObserverArray<nsWeakPtr> mReflowObservers; nsTObserverArray<nsWeakPtr> mScrollObservers; nsCString mOriginalUriString; nsWeakPtr mOpener; - nsWeakPtr mOpenedRemote; // A depth count of how many times NotifyRunToCompletionStart // has been called without a matching NotifyRunToCompletionStop. uint32_t mJSRunToCompletionDepth; // True if recording profiles. bool mProfileTimelineRecording;
--- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -49,17 +49,17 @@ interface nsIWebBrowserPrint; interface nsIVariant; interface nsIPrivacyTransitionObserver; interface nsIReflowObserver; interface nsIScrollObserver; interface nsITabParent; typedef unsigned long nsLoadFlags; -[scriptable, builtinclass, uuid(888fcf04-a69b-11e4-8d33-6fbb72d2eb03)] +[scriptable, builtinclass, uuid(f84b1ae4-2f78-4bad-b36a-6a8516ee6e40)] interface nsIDocShell : nsIDocShellTreeItem { /** * Loads a given URI. This will give priority to loading the requested URI * in the object implementing this interface. If it can't be loaded here * however, the URL dispatcher will go through its normal process of content * loading. * @@ -1020,23 +1020,16 @@ interface nsIDocShell : nsIDocShellTreeI * Regarding setOpener / getOpener - We can't use XPIDL's "attribute" * for notxpcom, so we're relegated to using explicit gets / sets. This * should be fine, considering that these methods should only ever be * called from native code. */ [noscript,notxpcom,nostdcall] void setOpener(in nsITabParent aOpener); [noscript,notxpcom,nostdcall] nsITabParent getOpener(); - /** - * See the documentation for setOpener and getOpener about why we - * don't use attribute here instead. - */ - [noscript,notxpcom,nostdcall] void setOpenedRemote(in nsITabParent aOpenedRemote); - [noscript,notxpcom,nostdcall] nsITabParent getOpenedRemote(); - // URLSearchParams for the window.location is owned by the docShell. [noscript,notxpcom] URLSearchParams getURLSearchParams(); /** * Notify DocShell when the browser is about to start executing JS, and after * that execution has stopped. This only occurs when the Timeline devtool * is collecting information. */
--- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -987,31 +987,35 @@ Element::CreateShadowRoot(ErrorResult& a protoBinding->SetInheritsStyle(false); // Calling SetPrototypeBinding takes ownership of protoBinding. docInfo->SetPrototypeBinding(NS_LITERAL_CSTRING("shadowroot"), protoBinding); nsRefPtr<ShadowRoot> shadowRoot = new ShadowRoot(this, nodeInfo.forget(), protoBinding); + shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc()); + // Replace the old ShadowRoot with the new one and let the old // ShadowRoot know about the younger ShadowRoot because the old // ShadowRoot is projected into the younger ShadowRoot's shadow // insertion point (if it exists). ShadowRoot* olderShadow = GetShadowRoot(); SetShadowRoot(shadowRoot); if (olderShadow) { olderShadow->SetYoungerShadow(shadowRoot); // Unbind children of older shadow root because they // are no longer in the composed tree. for (nsIContent* child = olderShadow->GetFirstChild(); child; child = child->GetNextSibling()) { child->UnbindFromTree(true, false); } + + olderShadow->SetIsComposedDocParticipant(false); } // xblBinding takes ownership of docInfo. nsRefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding); shadowRoot->SetAssociatedBinding(xblBinding); xblBinding->SetBoundElement(this); SetXBLBinding(xblBinding); @@ -1464,23 +1468,16 @@ Element::BindToTree(nsIDocument* aDocume // We no longer need to track the subtree pointer (and in fact we'll assert // if we do this any later). ClearSubtreeRootPointer(); // Being added to a document. SetInDocument(); - // Attached callback must be enqueued whenever custom element is inserted into a - // document and this document has a browsing context. - if (GetCustomElementData() && aDocument->GetDocShell()) { - // Enqueue an attached callback for the custom element. - aDocument->EnqueueLifecycleCallback(nsIDocument::eAttached, this); - } - // Unset this flag since we now really are in a document. UnsetFlags(NODE_FORCE_XBL_BINDINGS | // And clear the lazy frame construction bits. NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES | // And the restyle bits ELEMENT_ALL_RESTYLE_FLAGS); } else if (IsInShadowTree()) { // We're not in a document, but we did get inserted into a shadow tree. @@ -1493,16 +1490,26 @@ Element::BindToTree(nsIDocument* aDocume NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES | ELEMENT_ALL_RESTYLE_FLAGS); } else { // If we're not in the doc and not in a shadow tree, // update our subtree pointer. SetSubtreeRootPointer(aParent->SubtreeRoot()); } + nsIDocument* composedDoc = GetComposedDoc(); + if (composedDoc) { + // Attached callback must be enqueued whenever custom element is inserted into a + // document and this document has a browsing context. + if (GetCustomElementData() && composedDoc->GetDocShell()) { + // Enqueue an attached callback for the custom element. + composedDoc->EnqueueLifecycleCallback(nsIDocument::eAttached, this); + } + } + // Propagate scoped style sheet tracking bit. if (mParent->IsContent()) { nsIContent* parent; ShadowRoot* shadowRootParent = ShadowRoot::FromNode(mParent); if (shadowRootParent) { parent = shadowRootParent->GetHost(); } else { parent = mParent->AsContent(); @@ -1573,16 +1580,17 @@ Element::BindToTree(nsIDocument* aDocume if (sheet) { mAttrsAndChildren.SetMappedAttrStyleSheet(sheet); } } // Call BindToTree on shadow root children. ShadowRoot* shadowRoot = GetShadowRoot(); if (shadowRoot) { + shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc()); for (nsIContent* child = shadowRoot->GetFirstChild(); child; child = child->GetNextSibling()) { rv = child->BindToTree(nullptr, shadowRoot, shadowRoot->GetBindingParent(), aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); } } @@ -1630,18 +1638,17 @@ Element::UnbindFromTree(bool aDeep, bool NS_PRECONDITION(aDeep || (!GetUncomposedDoc() && !GetBindingParent()), "Shallow unbind won't clear document and binding parent on " "kids!"); RemoveFromIdTable(); // Make sure to unbind this node before doing the kids nsIDocument* document = - HasFlag(NODE_FORCE_XBL_BINDINGS) || IsInShadowTree() ? - OwnerDoc() : GetUncomposedDoc(); + HasFlag(NODE_FORCE_XBL_BINDINGS) ? OwnerDoc() : GetComposedDoc(); if (aNullParent) { if (IsFullScreenAncestor()) { // The element being removed is an ancestor of the full-screen element, // exit full-screen state. nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM"), OwnerDoc(), nsContentUtils::eDOM_PROPERTIES, @@ -1751,16 +1758,18 @@ Element::UnbindFromTree(bool aDeep, bool // Unbind children of shadow root. ShadowRoot* shadowRoot = GetShadowRoot(); if (shadowRoot) { for (nsIContent* child = shadowRoot->GetFirstChild(); child; child = child->GetNextSibling()) { child->UnbindFromTree(true, false); } + + shadowRoot->SetIsComposedDocParticipant(false); } } nsICSSDeclaration* Element::GetSMILOverrideStyle() { Element::nsDOMSlots *slots = DOMSlots();
--- a/dom/base/File.cpp +++ b/dom/base/File.cpp @@ -637,16 +637,20 @@ File::Constructor(const GlobalObject& aG nsRefPtr<MultipartFileImpl> impl = new MultipartFileImpl(EmptyString()); impl->InitializeChromeFile(aData, aBag, aRv); if (aRv.Failed()) { return nullptr; } MOZ_ASSERT(impl->IsFile()); + if (aBag.mLastModified.WasPassed()) { + impl->SetLastModified(aBag.mLastModified.Value()); + } + nsRefPtr<File> domFile = new File(aGlobal.GetAsSupports(), impl); return domFile.forget(); } /* static */ already_AddRefed<File> File::Constructor(const GlobalObject& aGlobal, nsIFile* aData, const ChromeFilePropertyBag& aBag, @@ -662,16 +666,20 @@ File::Constructor(const GlobalObject& aG nsRefPtr<MultipartFileImpl> impl = new MultipartFileImpl(EmptyString()); impl->InitializeChromeFile(window, aData, aBag, true, aRv); if (aRv.Failed()) { return nullptr; } MOZ_ASSERT(impl->IsFile()); + if (aBag.mLastModified.WasPassed()) { + impl->SetLastModified(aBag.mLastModified.Value()); + } + nsRefPtr<File> domFile = new File(aGlobal.GetAsSupports(), impl); return domFile.forget(); } /* static */ already_AddRefed<File> File::Constructor(const GlobalObject& aGlobal, const nsAString& aData, const ChromeFilePropertyBag& aBag, @@ -686,16 +694,20 @@ File::Constructor(const GlobalObject& aG nsRefPtr<MultipartFileImpl> impl = new MultipartFileImpl(EmptyString()); impl->InitializeChromeFile(window, aData, aBag, aRv); if (aRv.Failed()) { return nullptr; } MOZ_ASSERT(impl->IsFile()); + if (aBag.mLastModified.WasPassed()) { + impl->SetLastModified(aBag.mLastModified.Value()); + } + nsRefPtr<File> domFile = new File(aGlobal.GetAsSupports(), impl); return domFile.forget(); } //////////////////////////////////////////////////////////////////////////// // mozilla::dom::FileImpl implementation already_AddRefed<FileImpl>
--- a/dom/base/MultipartFileImpl.cpp +++ b/dom/base/MultipartFileImpl.cpp @@ -184,41 +184,54 @@ MultipartFileImpl::InitializeBlob( void MultipartFileImpl::SetLengthAndModifiedDate() { MOZ_ASSERT(mLength == UINT64_MAX); MOZ_ASSERT(mLastModificationDate == UINT64_MAX); uint64_t totalLength = 0; + uint64_t lastModified = 0; + bool lastModifiedSet = false; for (uint32_t index = 0, count = mBlobImpls.Length(); index < count; index++) { nsRefPtr<FileImpl>& blob = mBlobImpls[index]; #ifdef DEBUG MOZ_ASSERT(!blob->IsSizeUnknown()); MOZ_ASSERT(!blob->IsDateUnknown()); #endif ErrorResult error; uint64_t subBlobLength = blob->GetSize(error); MOZ_ALWAYS_TRUE(!error.Failed()); MOZ_ASSERT(UINT64_MAX - subBlobLength >= totalLength); totalLength += subBlobLength; + + if (blob->IsFile()) { + uint64_t partLastModified = blob->GetLastModified(error); + MOZ_ALWAYS_TRUE(!error.Failed()); + + if (lastModified < partLastModified) { + lastModified = partLastModified; + lastModifiedSet = true; + } + } } mLength = totalLength; if (mIsFile) { // We cannot use PR_Now() because bug 493756 and, for this reason: // var x = new Date(); var f = new File(...); // x.getTime() < f.dateModified.getTime() // could fail. - mLastModificationDate = JS_Now(); + mLastModificationDate = + lastModifiedSet ? lastModified * PR_USEC_PER_MSEC : JS_Now(); } } void MultipartFileImpl::GetMozFullPathInternal(nsAString& aFilename, ErrorResult& aRv) { if (!mIsFromNsIFile || mBlobImpls.Length() == 0) {
--- a/dom/base/ShadowRoot.cpp +++ b/dom/base/ShadowRoot.cpp @@ -62,17 +62,17 @@ NS_INTERFACE_MAP_END_INHERITING(Document NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment) NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment) ShadowRoot::ShadowRoot(nsIContent* aContent, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, nsXBLPrototypeBinding* aProtoBinding) : DocumentFragment(aNodeInfo), mPoolHost(aContent), mProtoBinding(aProtoBinding), mShadowElement(nullptr), - mInsertionPointChanged(false) + mInsertionPointChanged(false), mIsComposedDocParticipant(false) { SetHost(aContent); // Nodes in a shadow tree should never store a value // in the subtree root pointer, nodes in the shadow tree // track the subtree root using GetContainingShadow(). ClearSubtreeRootPointer();
--- a/dom/base/ShadowRoot.h +++ b/dom/base/ShadowRoot.h @@ -122,16 +122,22 @@ public: const nsAString& aLocalName); already_AddRefed<nsContentList> GetElementsByClassName(const nsAString& aClasses); void GetInnerHTML(nsAString& aInnerHTML); void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError); Element* Host(); ShadowRoot* GetOlderShadowRoot() { return mOlderShadow; } void StyleSheetChanged(); + + bool IsComposedDocParticipant() { return mIsComposedDocParticipant; } + void SetIsComposedDocParticipant(bool aIsComposedDocParticipant) + { + mIsComposedDocParticipant = aIsComposedDocParticipant; + } protected: virtual ~ShadowRoot(); // The pool host is the parent of the nodes that will be distributed // into the insertion points in this ShadowRoot. See |ChangeShadowRoot|. nsCOMPtr<nsIContent> mPoolHost; // An array of content insertion points that are a descendant of the ShadowRoot @@ -166,16 +172,22 @@ protected: // this ShadowRoot was created. nsRefPtr<ShadowRoot> mYoungerShadow; // A boolean that indicates that an insertion point was added or removed // from this ShadowRoot and that the nodes need to be redistributed into // the insertion points. After this flag is set, nodes will be distributed // on the next mutation event. bool mInsertionPointChanged; + + // Flag to indicate whether the descendants of this shadow root are part of the + // composed document. Ideally, we would use a node flag on nodes to + // mark whether it is in the composed document, but we have run out of flags + // so instead we track it here. + bool mIsComposedDocParticipant; }; class ShadowRootStyleSheetList : public StyleSheetList { public: explicit ShadowRootStyleSheetList(ShadowRoot* aShadowRoot); NS_DECL_ISUPPORTS_INHERITED
--- a/dom/base/nsContentList.cpp +++ b/dom/base/nsContentList.cpp @@ -219,17 +219,17 @@ NS_GetContentList(nsINode* aRootNode, PL_DHashTableInit(&gContentListHashTable, &hash_table_ops, sizeof(ContentListHashEntry)); } ContentListHashEntry *entry = nullptr; // First we look in our hashtable. Then we create a content list if needed if (gContentListHashTable.IsInitialized()) { entry = static_cast<ContentListHashEntry *> - (PL_DHashTableAdd(&gContentListHashTable, &hashKey, fallible)); + (PL_DHashTableAdd(&gContentListHashTable, &hashKey)); if (entry) list = entry->mContentList; } if (!list) { // We need to create a ContentList and add it to our new entry, if // we have an entry nsCOMPtr<nsIAtom> xmlAtom = do_GetAtom(aTagname); @@ -327,17 +327,18 @@ GetFuncStringContentList(nsINode* aRootN } FuncStringContentListHashEntry *entry = nullptr; // First we look in our hashtable. Then we create a content list if needed if (gFuncStringContentListHashTable.IsInitialized()) { nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString); entry = static_cast<FuncStringContentListHashEntry *> - (PL_DHashTableAdd(&gFuncStringContentListHashTable, &hashKey, fallible)); + (PL_DHashTableAdd(&gFuncStringContentListHashTable, + &hashKey)); if (entry) { list = entry->mContentList; #ifdef DEBUG MOZ_ASSERT_IF(list, list->mType == ListType::sType); #endif } }
--- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -3959,17 +3959,17 @@ nsContentUtils::GetListenerManagerForNod // We're already shut down, don't bother creating an event listener // manager. return nullptr; } EventListenerManagerMapEntry *entry = static_cast<EventListenerManagerMapEntry *> - (PL_DHashTableAdd(&sEventListenerManagersHash, aNode, fallible)); + (PL_DHashTableAdd(&sEventListenerManagersHash, aNode)); if (!entry) { return nullptr; } if (!entry->mListenerManager) { entry->mListenerManager = new EventListenerManager(aNode);
--- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -453,17 +453,17 @@ CustomElementCallback::Call() // The callback hasn't actually been invoked yet, but we need to flip // this now in order to enqueue the attached callback. This is a spec // bug (w3c bug 27437). mOwnerData->mCreatedCallbackInvoked = true; // If ELEMENT is in a document and this document has a browsing context, // enqueue attached callback for ELEMENT. - nsIDocument* document = mThisObject->GetUncomposedDoc(); + nsIDocument* document = mThisObject->GetComposedDoc(); if (document && document->GetDocShell()) { document->EnqueueLifecycleCallback(nsIDocument::eAttached, mThisObject); } static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv); mOwnerData->mElementIsBeingCreated = false; break; } @@ -1710,18 +1710,18 @@ nsDocument::~nsDocument() mAnimationController->Disconnect(); } mParentDocument = nullptr; // Kill the subdocument map, doing this will release its strong // references, if any. if (mSubDocuments) { - PL_DHashTableFinish(mSubDocuments); - delete mSubDocuments; + PL_DHashTableDestroy(mSubDocuments); + mSubDocuments = nullptr; } // Destroy link map now so we don't waste time removing // links one by one DestroyElementMaps(); nsAutoScriptBlocker scriptBlocker; @@ -2121,18 +2121,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets) if (tmp->mStyleSheetSetList) { tmp->mStyleSheetSetList->Disconnect(); tmp->mStyleSheetSetList = nullptr; } if (tmp->mSubDocuments) { - PL_DHashTableFinish(tmp->mSubDocuments); - delete tmp->mSubDocuments; + PL_DHashTableDestroy(tmp->mSubDocuments); tmp->mSubDocuments = nullptr; } tmp->mFrameRequestCallbacks.Clear(); tmp->mRadioGroups.Clear(); // nsDocument has a pretty complex destructor, so we're going to @@ -2327,18 +2326,18 @@ nsDocument::ResetToURI(nsIURI *aURI, nsI mSecurityInfo = nullptr; mDocumentLoadGroup = nullptr; // Delete references to sub-documents and kill the subdocument map, // if any. It holds strong references if (mSubDocuments) { - PL_DHashTableFinish(mSubDocuments); - delete mSubDocuments; + PL_DHashTableDestroy(mSubDocuments); + mSubDocuments = nullptr; } // Destroy link map now so we don't waste time removing // links one by one DestroyElementMaps(); bool oldVal = mInUnlinkOrDeletion; @@ -3997,23 +3996,26 @@ nsDocument::SetSubDocumentFor(Element* a { PL_DHashVoidPtrKeyStub, PL_DHashMatchEntryStub, PL_DHashMoveEntryStub, SubDocClearEntry, SubDocInitEntry }; - mSubDocuments = new PLDHashTable(); - PL_DHashTableInit(mSubDocuments, &hash_table_ops, sizeof(SubDocMapEntry)); + mSubDocuments = PL_NewDHashTable(&hash_table_ops, sizeof(SubDocMapEntry)); + if (!mSubDocuments) { + return NS_ERROR_OUT_OF_MEMORY; + } } // Add a mapping to the hash table - SubDocMapEntry *entry = static_cast<SubDocMapEntry*> - (PL_DHashTableAdd(mSubDocuments, aElement, fallible)); + SubDocMapEntry *entry = + static_cast<SubDocMapEntry*> + (PL_DHashTableAdd(mSubDocuments, aElement)); if (!entry) { return NS_ERROR_OUT_OF_MEMORY; } if (entry->mSubDocument) { entry->mSubDocument->SetParentDocument(nullptr);
--- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -164,24 +164,22 @@ nsFrameLoader::nsFrameLoader(Element* aO , mInSwap(false) , mInShow(false) , mHideCalled(false) , mNetworkCreated(aNetworkCreated) , mRemoteBrowserShown(false) , mRemoteFrame(false) , mClipSubdocument(true) , mClampScrollPosition(true) - , mRemoteBrowserInitialized(false) , mObservingOwnerContent(false) , mVisible(true) , mCurrentRemoteFrame(nullptr) , mRemoteBrowser(nullptr) , mChildID(0) , mEventMode(EVENT_MODE_NORMAL_DISPATCH) - , mPendingFrameSent(false) { ResetPermissionManagerStatus(); } nsFrameLoader::~nsFrameLoader() { mNeedsAsyncDestroy = true; if (mMessageManager) { @@ -358,25 +356,16 @@ nsFrameLoader::ReallyStartLoadingInterna nsresult rv = MaybeCreateDocShell(); if (NS_FAILED(rv)) { return rv; } if (mRemoteFrame) { if (!mRemoteBrowser) { - if (!mPendingFrameSent) { - nsCOMPtr<nsIObserverService> os = services::GetObserverService(); - if (os && !mRemoteBrowserInitialized) { - os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), - "remote-browser-pending", nullptr); - mPendingFrameSent = true; - } - } - TryRemoteBrowser(); if (!mRemoteBrowser) { NS_WARNING("Couldn't create child process for iframe."); return NS_ERROR_FAILURE; } } @@ -908,26 +897,21 @@ nsFrameLoader::ShowRemoteFrame(const nsI parentIsActive = topWin && topWin->IsActive(); } } mRemoteBrowser->Show(size, parentIsActive); mRemoteBrowserShown = true; EnsureMessageManager(); + InitializeBrowserAPI(); nsCOMPtr<nsIObserverService> os = services::GetObserverService(); - if (os && !mRemoteBrowserInitialized) { - if (!mPendingFrameSent) { - os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), - "remote-browser-pending", nullptr); - mPendingFrameSent = true; - } + if (os) { os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), "remote-browser-shown", nullptr); - mRemoteBrowserInitialized = true; } } else { nsIntRect dimensions; NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), false); // Don't show remote iframe if we are waiting for the completion of reflow. if (!aFrame || !(aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { nsIntPoint chromeDisp = aFrame->GetChromeDisplacement(); @@ -1780,16 +1764,17 @@ nsFrameLoader::MaybeCreateDocShell() uint32_t containingAppId = nsIScriptSecurityManager::NO_APP_ID; if (containingApp) { NS_ENSURE_SUCCESS(containingApp->GetLocalId(&containingAppId), NS_ERROR_FAILURE); } mDocShell->SetIsBrowserInsideApp(containingAppId); } + InitializeBrowserAPI(); nsCOMPtr<nsIObserverService> os = services::GetObserverService(); if (os) { os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), "inprocess-browser-shown", nullptr); } if (OwnerIsBrowserOrAppFrame() && mMessageManager) { mMessageManager->LoadFrameScript( @@ -2200,17 +2185,16 @@ nsFrameLoader::TryRemoteBrowser() mContentParent = mRemoteBrowser->Manager(); if (mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozpasspointerevents, nsGkAtoms::_true, eCaseMatters)) { unused << mRemoteBrowser->SendSetUpdateHitRegion(true); } - parentDocShell->SetOpenedRemote(mRemoteBrowser); } return true; } mozilla::dom::PBrowserParent* nsFrameLoader::GetRemoteBrowser() { return mRemoteBrowser; @@ -2744,8 +2728,17 @@ nsFrameLoader::GetLoadContext(nsILoadCon } else { nsCOMPtr<nsIDocShell> docShell; GetDocShell(getter_AddRefs(docShell)); loadContext = do_GetInterface(docShell); } loadContext.forget(aLoadContext); return NS_OK; } + +void +nsFrameLoader::InitializeBrowserAPI() +{ + nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent); + if (browserFrame) { + browserFrame->InitializeBrowserAPI(); + } +}
--- a/dom/base/nsFrameLoader.h +++ b/dom/base/nsFrameLoader.h @@ -307,16 +307,18 @@ private: nsIAtom* TypeAttrName() const { return mOwnerContent->IsXUL() ? nsGkAtoms::type : nsGkAtoms::mozframetype; } // Update the permission manager's app-id refcount based on mOwnerContent's // own-or-containing-app. void ResetPermissionManagerStatus(); + void InitializeBrowserAPI(); + nsCOMPtr<nsIDocShell> mDocShell; nsCOMPtr<nsIURI> mURIToLoad; mozilla::dom::Element* mOwnerContent; // WEAK // Note: this variable must be modified only by ResetPermissionManagerStatus() uint32_t mAppIdSentToPermissionManager; public: @@ -346,17 +348,16 @@ private: // created using NS_FROM_PARSER_NETWORK flag. If the element is modified, // it may lose the flag. bool mNetworkCreated : 1; bool mRemoteBrowserShown : 1; bool mRemoteFrame : 1; bool mClipSubdocument : 1; bool mClampScrollPosition : 1; - bool mRemoteBrowserInitialized : 1; bool mObservingOwnerContent : 1; // Backs nsIFrameLoader::{Get,Set}Visible. Visibility state here relates to // whether this frameloader's <iframe mozbrowser> is setVisible(true)'ed, and // doesn't necessarily correlate with docshell/document visibility. bool mVisible : 1; // The ContentParent associated with mRemoteBrowser. This was added as a @@ -364,14 +365,11 @@ private: nsRefPtr<mozilla::dom::nsIContentParent> mContentParent; RenderFrameParent* mCurrentRemoteFrame; TabParent* mRemoteBrowser; uint64_t mChildID; // See nsIFrameLoader.idl. EVENT_MODE_NORMAL_DISPATCH automatically // forwards some input events to out-of-process content. uint32_t mEventMode; - - // Indicate if we have sent 'remote-browser-pending'. - bool mPendingFrameSent; }; #endif
--- a/dom/base/nsGenericDOMDataNode.cpp +++ b/dom/base/nsGenericDOMDataNode.cpp @@ -108,16 +108,20 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_ if (slots) { slots->Traverse(cb); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericDOMDataNode) nsINode::Unlink(tmp); + // Clear flag here because unlinking slots will clear the + // containing shadow root pointer. + tmp->UnsetFlags(NODE_IS_IN_SHADOW_TREE); + nsDataSlots *slots = tmp->GetExistingDataSlots(); if (slots) { slots->Unlink(); } NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY @@ -557,18 +561,17 @@ nsGenericDOMDataNode::BindToTree(nsIDocu void nsGenericDOMDataNode::UnbindFromTree(bool aDeep, bool aNullParent) { // Unset frame flags; if we need them again later, they'll get set again. UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE | NS_REFRAME_IF_WHITESPACE); nsIDocument* document = - HasFlag(NODE_FORCE_XBL_BINDINGS) || IsInShadowTree() ? - OwnerDoc() : GetUncomposedDoc(); + HasFlag(NODE_FORCE_XBL_BINDINGS) ? OwnerDoc() : GetComposedDoc(); if (aNullParent) { if (GetParent()) { NS_RELEASE(mParent); } else { mParent = nullptr; } SetParentIsContent(false);
--- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -385,27 +385,18 @@ nsINode::GetTextContentInternal(nsAStrin } nsIDocument* nsINode::GetComposedDocInternal() const { MOZ_ASSERT(HasFlag(NODE_IS_IN_SHADOW_TREE) && IsContent(), "Should only be caled on nodes in the shadow tree."); - // Cross ShadowRoot boundary. ShadowRoot* containingShadow = AsContent()->GetContainingShadow(); - - nsIContent* poolHost = containingShadow->GetPoolHost(); - if (!poolHost) { - // This node is in an older shadow root that does not get projected into - // an insertion point, thus this node can not be in the composed document. - return nullptr; - } - - return poolHost->GetComposedDoc(); + return containingShadow->IsComposedDocParticipant() ? OwnerDoc() : nullptr; } #ifdef DEBUG void nsINode::CheckNotNativeAnonymous() const { if (!IsNodeOfType(eCONTENT)) return;
--- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -515,21 +515,19 @@ public: * @deprecated */ nsIDocument *GetCurrentDoc() const { return GetUncomposedDoc(); } /** - * This method gets the current doc of the node hosting this content - * or the current doc of this content if it is not being hosted. This - * method walks through ShadowRoot boundaries until it reach the host - * that is located in the root of the "tree of trees" (see Shadow DOM - * spec) and returns the current doc for that host. + * This method returns the owner doc if the node is in the + * composed document (as defined in the Shadow DOM spec), otherwise + * it returns null. */ nsIDocument* GetComposedDoc() const { return IsInShadowTree() ? GetComposedDocInternal() : GetUncomposedDoc(); } /**
--- a/dom/base/nsISlowScriptDebug.idl +++ b/dom/base/nsISlowScriptDebug.idl @@ -9,26 +9,26 @@ interface nsIDOMEventTarget; [scriptable, function, uuid(f7dbb80c-5d1e-4fd9-b55c-a9ffda4a75b1)] interface nsISlowScriptDebugCallback : nsISupports { void handleSlowScriptDebug(in nsIDOMWindow aWindow); }; [scriptable, function, uuid(b1c6ecd0-8fa4-11e4-b4a9-0800200c9a66)] -interface nsISlowScriptDebugerStartupCallback : nsISupports +interface nsISlowScriptDebuggerStartupCallback : nsISupports { void finishDebuggerStartup(); }; [scriptable, function, uuid(dbee14b0-8fa0-11e4-b4a9-0800200c9a66)] interface nsISlowScriptDebugRemoteCallback : nsISupports { void handleSlowScriptDebug(in nsIDOMEventTarget aBrowser, - in nsISlowScriptDebugerStartupCallback aCallback); + in nsISlowScriptDebuggerStartupCallback aCallback); }; [scriptable, uuid(f75d4164-3aa7-4395-ba44-a5f95b2e8427)] interface nsISlowScriptDebug : nsISupports { attribute nsISlowScriptDebugCallback activationHandler; attribute nsISlowScriptDebugRemoteCallback remoteActivationHandler; };
--- a/dom/base/nsPropertyTable.cpp +++ b/dom/base/nsPropertyTable.cpp @@ -225,17 +225,17 @@ nsPropertyTable::SetPropertyInternal(nsP propertyList->mNext = mPropertyList; mPropertyList = propertyList; } // The current property value (if there is one) is replaced and the current // value is destroyed nsresult result = NS_OK; PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*> - (PL_DHashTableAdd(&propertyList->mObjectValueMap, aObject, fallible)); + (PL_DHashTableAdd(&propertyList->mObjectValueMap, aObject)); if (!entry) return NS_ERROR_OUT_OF_MEMORY; // A nullptr entry->key is the sign that the entry has just been allocated // for us. If it's non-nullptr then we have an existing entry. if (entry->key) { if (aOldValue) *aOldValue = entry->value; else if (propertyList->mDtorFunc)
--- a/dom/base/nsScriptNameSpaceManager.cpp +++ b/dom/base/nsScriptNameSpaceManager.cpp @@ -117,35 +117,39 @@ GlobalNameHashInitEntry(PLDHashTable *ta NS_IMPL_ISUPPORTS( nsScriptNameSpaceManager, nsIObserver, nsISupportsWeakReference, nsIMemoryReporter) nsScriptNameSpaceManager::nsScriptNameSpaceManager() + : mIsInitialized(false) { MOZ_COUNT_CTOR(nsScriptNameSpaceManager); } nsScriptNameSpaceManager::~nsScriptNameSpaceManager() { - UnregisterWeakMemoryReporter(this); - // Destroy the hash - PL_DHashTableFinish(&mGlobalNames); - PL_DHashTableFinish(&mNavigatorNames); + if (mIsInitialized) { + UnregisterWeakMemoryReporter(this); + // Destroy the hash + PL_DHashTableFinish(&mGlobalNames); + PL_DHashTableFinish(&mNavigatorNames); + } MOZ_COUNT_DTOR(nsScriptNameSpaceManager); } nsGlobalNameStruct * nsScriptNameSpaceManager::AddToHash(PLDHashTable *aTable, const nsAString *aKey, const char16_t **aClassName) { - GlobalNameMapEntry *entry = static_cast<GlobalNameMapEntry *> - (PL_DHashTableAdd(aTable, aKey, fallible)); + GlobalNameMapEntry *entry = + static_cast<GlobalNameMapEntry *> + (PL_DHashTableAdd(aTable, aKey)); if (!entry) { return nullptr; } if (aClassName) { *aClassName = entry->mKey.get(); } @@ -317,23 +321,31 @@ nsScriptNameSpaceManager::Init() { GlobalNameHashHashKey, GlobalNameHashMatchEntry, PL_DHashMoveEntryStub, GlobalNameHashClearEntry, GlobalNameHashInitEntry }; - PL_DHashTableInit(&mGlobalNames, &hash_table_ops, - sizeof(GlobalNameMapEntry), - GLOBALNAME_HASHTABLE_INITIAL_LENGTH); + mIsInitialized = PL_DHashTableInit(&mGlobalNames, &hash_table_ops, + sizeof(GlobalNameMapEntry), + fallible, + GLOBALNAME_HASHTABLE_INITIAL_LENGTH); + NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_OUT_OF_MEMORY); - PL_DHashTableInit(&mNavigatorNames, &hash_table_ops, - sizeof(GlobalNameMapEntry), - GLOBALNAME_HASHTABLE_INITIAL_LENGTH); + mIsInitialized = PL_DHashTableInit(&mNavigatorNames, &hash_table_ops, + sizeof(GlobalNameMapEntry), + fallible, + GLOBALNAME_HASHTABLE_INITIAL_LENGTH); + if (!mIsInitialized) { + PL_DHashTableFinish(&mGlobalNames); + + return NS_ERROR_OUT_OF_MEMORY; + } RegisterWeakMemoryReporter(this); nsresult rv = NS_OK; rv = RegisterExternalInterfaces(false); NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/base/nsScriptNameSpaceManager.h +++ b/dom/base/nsScriptNameSpaceManager.h @@ -235,11 +235,13 @@ private: nsISupports* aEntry, bool aRemove); nsGlobalNameStruct* LookupNameInternal(const nsAString& aName, const char16_t **aClassName = nullptr); PLDHashTable mGlobalNames; PLDHashTable mNavigatorNames; + + bool mIsInitialized; }; #endif /* nsScriptNameSpaceManager_h__ */
--- a/dom/base/nsWrapperCache.cpp +++ b/dom/base/nsWrapperCache.cpp @@ -1,17 +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 "nsWrapperCacheInlines.h" -#include "jsproxy.h" +#include "js/Proxy.h" #include "mozilla/dom/DOMJSProxyHandler.h" #include "mozilla/HoldDropJSObjects.h" #include "nsCycleCollectionTraversalCallback.h" #include "nsCycleCollector.h" using namespace mozilla; using namespace mozilla::dom;
--- a/dom/base/test/bug403852_fileOpener.js +++ b/dom/base/test/bug403852_fileOpener.js @@ -6,10 +6,12 @@ let testFile = Cc["@mozilla.org/file/dir .QueryInterface(Ci.nsIProperties) .get("ProfD", Ci.nsIFile); testFile.append("prefs.js"); addMessageListener("file.open", function () { sendAsyncMessage("file.opened", { file: new File(testFile), mtime: testFile.lastModifiedTime, + fileWithDate: new File(testFile, { lastModified: 123 }), + fileDate: 123, }); });
--- a/dom/base/test/test_bug403852.html +++ b/dom/base/test/test_bug403852.html @@ -33,28 +33,33 @@ function onOpened(message) { // Make sure the file is accessible with indexed notation var domFile = fileList.files[0]; is(domFile.name, "prefs.js", "fileName should be prefs.js"); ok("lastModifiedDate" in domFile, "lastModifiedDate must be present"); var d = new Date(message.mtime); - todo(d.getTime() == domFile.lastModifiedDate.getTime(), "lastModifiedDate should be the same (FIXME: bug 1121722)"); + is(d.getTime(), domFile.lastModifiedDate.getTime(), "lastModifiedDate should be the same"); var x = new Date(); // In our implementation of File object, lastModifiedDate is unknown only for new objects. // Using canvas or input[type=file] elements, we 'often' have a valid lastModifiedDate values. // For canvas we use memory files and the lastModifiedDate is now(). var f = new File([new Blob(['test'], {type: 'text/plain'})], "test-name"); var y = f.lastModifiedDate; var z = new Date(); ok((x.getTime() <= y.getTime()) && (y.getTime() <= z.getTime()), "lastModifiedDate of file which does not have last modified date should be current time"); + + + var d = new Date(message.fileDate); + is(d.getTime(), message.fileWithDate.lastModifiedDate.getTime(), "lastModifiedDate should be the same when lastModified is set: " + message.fileWithDate.lastModified); + script.destroy(); SimpleTest.finish(); } </script> </pre> </body> </html>
--- a/dom/bindings/DOMJSProxyHandler.h +++ b/dom/bindings/DOMJSProxyHandler.h @@ -5,17 +5,17 @@ #ifndef mozilla_dom_DOMJSProxyHandler_h #define mozilla_dom_DOMJSProxyHandler_h #include "mozilla/Attributes.h" #include "mozilla/Likely.h" #include "jsapi.h" -#include "jsproxy.h" +#include "js/Proxy.h" #include "nsString.h" #define DOM_PROXY_OBJECT_SLOT js::PROXY_PRIVATE_SLOT namespace mozilla { namespace dom { enum {
--- a/dom/events/TextComposition.cpp +++ b/dom/events/TextComposition.cpp @@ -219,16 +219,27 @@ TextComposition::DispatchCompositionEven // composition string empty or didn't have clause information), we don't // need to dispatch redundant DOM text event. if (dispatchDOMTextEvent && aCompositionEvent->message != NS_COMPOSITION_CHANGE && !mIsComposing && mLastData == aCompositionEvent->mData) { dispatchEvent = dispatchDOMTextEvent = false; } + // widget may dispatch redundant NS_COMPOSITION_CHANGE event + // which modifies neither composition string, clauses nor caret + // position. In such case, we shouldn't dispatch DOM events. + if (dispatchDOMTextEvent && + aCompositionEvent->message == NS_COMPOSITION_CHANGE && + mLastData == aCompositionEvent->mData && + mRanges && aCompositionEvent->mRanges && + mRanges->Equals(*aCompositionEvent->mRanges)) { + dispatchEvent = dispatchDOMTextEvent = false; + } + if (dispatchDOMTextEvent) { if (!MaybeDispatchCompositionUpdate(aCompositionEvent)) { return; } } if (dispatchEvent) { // If the composition event should cause a DOM text event, we should
--- a/dom/geolocation/nsGeolocationSettings.cpp +++ b/dom/geolocation/nsGeolocationSettings.cpp @@ -219,28 +219,36 @@ nsGeolocationSettings::HandleGeolocation } mAlaEnabled = aVal.toBoolean(); } void nsGeolocationSettings::HandleGeolocationPerOriginSettingsChange(const JS::Value& aVal) { + MOZ_ASSERT(NS_IsMainThread()); + if (!aVal.isObject()) { return; } // clear the hash table mPerOriginSettings.Clear(); - // enumerate the array - AutoJSAPI jsapi; - jsapi.Init(); - JSContext* cx = jsapi.cx(); - JS::Rooted<JSObject*> obj(cx, &aVal.toObject()); + // root the object and get the global + JS::Rooted<JSObject*> obj(nsContentUtils::RootingCx(), &aVal.toObject()); + MOZ_ASSERT(obj); + nsIGlobalObject* global = xpc::NativeGlobal(obj); + NS_ENSURE_TRUE_VOID(global && global->GetGlobalJSObject()); + + // because the spec requires calling getters when enumerating the key of a + // dictionary + AutoEntryScript aes(global); + aes.TakeOwnershipOfErrorReporting(); + JSContext *cx = aes.cx(); JS::AutoIdArray ids(cx, JS_Enumerate(cx, obj)); // if we get no ids then the exception list is empty and we can return here. if (!ids) return; // go through all of the objects in the exceptions dictionary for (size_t i = 0; i < ids.length(); i++) {
--- a/dom/html/HTMLShadowElement.cpp +++ b/dom/html/HTMLShadowElement.cpp @@ -141,16 +141,18 @@ HTMLShadowElement::BindToTree(nsIDocumen containingShadow->SetInsertionPointChanged(); } if (mIsInsertionPoint && containingShadow) { // Propagate BindToTree calls to projected shadow root children. ShadowRoot* projectedShadow = containingShadow->GetOlderShadowRoot(); if (projectedShadow) { + projectedShadow->SetIsComposedDocParticipant(IsInComposedDoc()); + for (nsIContent* child = projectedShadow->GetFirstChild(); child; child = child->GetNextSibling()) { rv = child->BindToTree(nullptr, projectedShadow, projectedShadow->GetBindingParent(), aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); } } @@ -168,16 +170,18 @@ HTMLShadowElement::UnbindFromTree(bool a // Propagate UnbindFromTree call to previous projected shadow // root children. ShadowRoot* projectedShadow = oldContainingShadow->GetOlderShadowRoot(); if (projectedShadow) { for (nsIContent* child = projectedShadow->GetFirstChild(); child; child = child->GetNextSibling()) { child->UnbindFromTree(true, false); } + + projectedShadow->SetIsComposedDocParticipant(false); } } nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); if (oldContainingShadow && !GetContainingShadow() && mIsInsertionPoint) { nsTArray<HTMLShadowElement*>& shadowDescendants = oldContainingShadow->ShadowDescendants();
--- a/dom/html/HTMLStyleElement.cpp +++ b/dom/html/HTMLStyleElement.cpp @@ -152,25 +152,24 @@ HTMLStyleElement::BindToTree(nsIDocument return rv; } void HTMLStyleElement::UnbindFromTree(bool aDeep, bool aNullParent) { nsCOMPtr<nsIDocument> oldDoc = GetUncomposedDoc(); - nsCOMPtr<nsIDocument> oldComposedDoc = GetComposedDoc(); ShadowRoot* oldShadow = GetContainingShadow(); nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); - if (GetContainingShadow() && !oldComposedDoc) { - // The style is in a shadow tree and was already not - // in the composed document. Thus the sheet does not - // need to be updated. + if (oldShadow && GetContainingShadow()) { + // The style is in a shadow tree and is still in the + // shadow tree. Thus the sheets in the shadow DOM + // do not need to be updated. return; } UpdateStyleSheetInternal(oldDoc, oldShadow); } nsresult HTMLStyleElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
--- a/dom/html/nsBrowserElement.cpp +++ b/dom/html/nsBrowserElement.cpp @@ -14,101 +14,22 @@ #include "mozilla/dom/ToJSValue.h" #include "nsComponentManagerUtils.h" #include "nsContentUtils.h" #include "nsFrameLoader.h" #include "nsIDOMDOMRequest.h" #include "nsIDOMElement.h" #include "nsINode.h" -#include "nsIObserver.h" -#include "nsIObserverService.h" #include "nsIPrincipal.h" -#include "nsWeakReference.h" using namespace mozilla::dom; namespace mozilla { -static const char kRemoteBrowserPending[] = "remote-browser-pending"; -static const char kInprocessBrowserShown[] = "inprocess-browser-shown"; - -class nsBrowserElement::BrowserShownObserver : public nsIObserver - , public nsSupportsWeakReference -{ -public: - explicit BrowserShownObserver(nsBrowserElement* aBrowserElement); - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER - void AddObserver(); - void RemoveObserver(); -private: - virtual ~BrowserShownObserver(); - - // Weak reference to the browser element. nsBrowserElement has a - // reference to us. nsBrowserElement's destructor is responsible to - // null out this weak reference via RemoveObserver() - nsBrowserElement* mBrowserElement; -}; - -NS_IMPL_ISUPPORTS(nsBrowserElement::BrowserShownObserver, nsIObserver, nsISupportsWeakReference) - -nsBrowserElement::BrowserShownObserver::BrowserShownObserver(nsBrowserElement* aBrowserElement) - : mBrowserElement(aBrowserElement) -{ -} - -nsBrowserElement::BrowserShownObserver::~BrowserShownObserver() -{ - RemoveObserver(); -} - -NS_IMETHODIMP -nsBrowserElement::BrowserShownObserver::Observe(nsISupports* aSubject, - const char* aTopic, - const char16_t* aData) -{ - NS_ENSURE_TRUE(mBrowserElement, NS_OK); - - if (!strcmp(aTopic, kRemoteBrowserPending) || - !strcmp(aTopic, kInprocessBrowserShown)) { - nsCOMPtr<nsIFrameLoader> frameLoader = do_QueryInterface(aSubject); - nsCOMPtr<nsIFrameLoader> myFrameLoader = mBrowserElement->GetFrameLoader(); - // The browser element API needs the frameloader to - // initialize. We still use the observer to get notified when the - // frameloader is created. So we check if the frameloader created - // is ours, then initialize the browser element API. - if (frameLoader && frameLoader == myFrameLoader) { - mBrowserElement->InitBrowserElementAPI(); - } - } - return NS_OK; -} - -void -nsBrowserElement::BrowserShownObserver::AddObserver() -{ - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - if (obs) { - obs->AddObserver(this, kRemoteBrowserPending, true); - obs->AddObserver(this, kInprocessBrowserShown, true); - } -} - -void -nsBrowserElement::BrowserShownObserver::RemoveObserver() -{ - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - if (obs) { - obs->RemoveObserver(this, kRemoteBrowserPending); - obs->RemoveObserver(this, kInprocessBrowserShown); - } - mBrowserElement = nullptr; -} - bool nsBrowserElement::IsBrowserElementOrThrow(ErrorResult& aRv) { if (mBrowserElementAPI) { return true; } aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); return false; @@ -140,28 +61,16 @@ nsBrowserElement::InitBrowserElementAPI( } mBrowserElementAPI = do_CreateInstance("@mozilla.org/dom/browser-element-api;1"); if (mBrowserElementAPI) { mBrowserElementAPI->SetFrameLoader(frameLoader); } } -nsBrowserElement::nsBrowserElement() - : mOwnerIsWidget(false) -{ - mObserver = new BrowserShownObserver(this); - mObserver->AddObserver(); -} - -nsBrowserElement::~nsBrowserElement() -{ - mObserver->RemoveObserver(); -} - void nsBrowserElement::SetVisible(bool aVisible, ErrorResult& aRv) { NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv)); nsresult rv = mBrowserElementAPI->SetVisible(aVisible); if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/html/nsBrowserElement.h +++ b/dom/html/nsBrowserElement.h @@ -8,17 +8,16 @@ #define nsBrowserElement_h #include "mozilla/dom/BindingDeclarations.h" #include "nsCOMPtr.h" #include "nsIBrowserElementAPI.h" class nsFrameLoader; -class nsIObserver; namespace mozilla { namespace dom { struct BrowserElementDownloadOptions; class BrowserElementNextPaintEventCallback; class DOMRequest; } // namespace dom @@ -26,18 +25,18 @@ class DOMRequest; class ErrorResult; /** * A helper class for browser-element frames */ class nsBrowserElement { public: - nsBrowserElement(); - virtual ~nsBrowserElement(); + nsBrowserElement() : mOwnerIsWidget(false) {} + virtual ~nsBrowserElement() {} void SetVisible(bool aVisible, ErrorResult& aRv); already_AddRefed<dom::DOMRequest> GetVisible(ErrorResult& aRv); void SetActive(bool aActive, ErrorResult& aRv); bool GetActive(ErrorResult& aRv); void SendMouseEvent(const nsAString& aType, uint32_t aX, @@ -89,24 +88,20 @@ public: already_AddRefed<dom::DOMRequest> SetInputMethodActive(bool isActive, ErrorResult& aRv); void SetNFCFocus(bool isFocus, ErrorResult& aRv); protected: NS_IMETHOD_(already_AddRefed<nsFrameLoader>) GetFrameLoader() = 0; + void InitBrowserElementAPI(); nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI; private: - void InitBrowserElementAPI(); bool IsBrowserElementOrThrow(ErrorResult& aRv); bool IsNotWidgetOrThrow(ErrorResult& aRv); bool mOwnerIsWidget; - - class BrowserShownObserver; - friend class BrowserShownObserver; - nsRefPtr<BrowserShownObserver> mObserver; }; } // namespace mozilla #endif // nsBrowserElement_h
--- a/dom/html/nsGenericHTMLFrameElement.cpp +++ b/dom/html/nsGenericHTMLFrameElement.cpp @@ -494,8 +494,16 @@ nsGenericHTMLFrameElement::DisallowCreat NS_IMETHODIMP nsGenericHTMLFrameElement::AllowCreateFrameLoader() { MOZ_ASSERT(!mFrameLoader); MOZ_ASSERT(mFrameLoaderCreationDisallowed); mFrameLoaderCreationDisallowed = false; return NS_OK; } + +NS_IMETHODIMP +nsGenericHTMLFrameElement::InitializeBrowserAPI() +{ + MOZ_ASSERT(mFrameLoader); + InitBrowserElementAPI(); + return NS_OK; +}
--- a/dom/interfaces/html/nsIMozBrowserFrame.idl +++ b/dom/interfaces/html/nsIMozBrowserFrame.idl @@ -80,9 +80,15 @@ interface nsIMozBrowserFrame : nsIDOMMoz /** * Create a remote (i.e., out-of-process) frame loader attached to the given * tab parent. * * It is an error to call this method if we already have a frame loader. */ void createRemoteFrameLoader(in nsITabParent aTabParent); + + /** + * Initialize the API, and add frame message listener to listen to API + * invocations. + */ + [noscript] void initializeBrowserAPI(); };
--- a/dom/ipc/ProcessPriorityManager.cpp +++ b/dom/ipc/ProcessPriorityManager.cpp @@ -466,16 +466,23 @@ ProcessPriorityManagerImpl::Observe( return NS_OK; } already_AddRefed<ParticularProcessPriorityManager> ProcessPriorityManagerImpl::GetParticularProcessPriorityManager( ContentParent* aContentParent) { +#ifdef MOZ_NUWA_PROCESS + // Do not attempt to change the priority of the Nuwa process + if (aContentParent->IsNuwaProcess()) { + return nullptr; + } +#endif + nsRefPtr<ParticularProcessPriorityManager> pppm; uint64_t cpId = aContentParent->ChildID(); mParticularManagers.Get(cpId, &pppm); if (!pppm) { pppm = new ParticularProcessPriorityManager(aContentParent); pppm->Init(); mParticularManagers.Put(cpId, pppm); @@ -489,17 +496,19 @@ ProcessPriorityManagerImpl::GetParticula void ProcessPriorityManagerImpl::SetProcessPriority(ContentParent* aContentParent, ProcessPriority aPriority, uint32_t aBackgroundLRU) { MOZ_ASSERT(aContentParent); nsRefPtr<ParticularProcessPriorityManager> pppm = GetParticularProcessPriorityManager(aContentParent); - pppm->SetPriorityNow(aPriority, aBackgroundLRU); + if (pppm) { + pppm->SetPriorityNow(aPriority, aBackgroundLRU); + } } void ProcessPriorityManagerImpl::ObserveContentParentCreated( nsISupports* aContentParent) { // Do nothing; it's sufficient to get the PPPM. But assign to nsRefPtr so we // don't leak the already_AddRefed object. @@ -527,28 +536,27 @@ ProcessPriorityManagerImpl::ObserveConte NS_ENSURE_TRUE_VOID(props); uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN; props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID); NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN); nsRefPtr<ParticularProcessPriorityManager> pppm; mParticularManagers.Get(childID, &pppm); - MOZ_ASSERT(pppm); if (pppm) { pppm->ShutDown(); - } - mParticularManagers.Remove(childID); + mParticularManagers.Remove(childID); - if (mHighPriorityChildIDs.Contains(childID)) { - mHighPriorityChildIDs.RemoveEntry(childID); + if (mHighPriorityChildIDs.Contains(childID)) { + mHighPriorityChildIDs.RemoveEntry(childID); - // We just lost a high-priority process; reset everyone's CPU priorities. - ResetAllCPUPriorities(); + // We just lost a high-priority process; reset everyone's CPU priorities. + ResetAllCPUPriorities(); + } } } void ProcessPriorityManagerImpl::ResetAllCPUPriorities( void ) { nsTArray<nsRefPtr<ParticularProcessPriorityManager> > pppms; mParticularManagers.EnumerateRead( @@ -801,32 +809,35 @@ ParticularProcessPriorityManager::OnAudi } void ParticularProcessPriorityManager::OnRemoteBrowserFrameShown(nsISupports* aSubject) { nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject); NS_ENSURE_TRUE_VOID(fl); - // Ignore notifications that aren't from a BrowserOrApp - bool isBrowserOrApp; - fl->GetOwnerIsBrowserOrAppFrame(&isBrowserOrApp); - if (!isBrowserOrApp) { - return; - } - TabParent* tp = TabParent::GetFrom(fl); NS_ENSURE_TRUE_VOID(tp); MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); if (tp->Manager() != mContentParent) { return; } - ResetPriority(); + // Ignore notifications that aren't from a BrowserOrApp + bool isBrowserOrApp; + fl->GetOwnerIsBrowserOrAppFrame(&isBrowserOrApp); + if (isBrowserOrApp) { + ResetPriority(); + } + + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + if (os) { + os->RemoveObserver(this, "remote-browser-shown"); + } } void ParticularProcessPriorityManager::OnTabParentDestroyed(nsISupports* aSubject) { nsCOMPtr<nsITabParent> tp = do_QueryInterface(aSubject); NS_ENSURE_TRUE_VOID(tp); @@ -1034,23 +1045,16 @@ ParticularProcessPriorityManager::SetPri SetPriorityNow(aPriority, ComputeCPUPriority(aPriority), aBackgroundLRU); } void ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority, ProcessCPUPriority aCPUPriority, uint32_t aBackgroundLRU) { -#ifdef MOZ_NUWA_PROCESS - // Do not attempt to change the priority of the Nuwa process - if (mContentParent->IsNuwaProcess()) { - return; - } -#endif - if (aPriority == PROCESS_PRIORITY_UNKNOWN) { MOZ_ASSERT(false); return; } if (aBackgroundLRU > 0 && aPriority == PROCESS_PRIORITY_BACKGROUND && mPriority == PROCESS_PRIORITY_BACKGROUND) {
--- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -642,21 +642,32 @@ TabParent::RecvCreateWindow(PBrowserPare NS_ConvertUTF16toUTF8(aName).get(), NS_ConvertUTF16toUTF8(aFeatures).get(), aCalledFromJS, false, false, this, nullptr, getter_AddRefs(window)); NS_ENSURE_SUCCESS(rv, false); nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(window); NS_ENSURE_TRUE(pwindow, false); - nsRefPtr<nsIDocShell> newDocShell = pwindow->GetDocShell(); - NS_ENSURE_TRUE(newDocShell, false); - - nsCOMPtr<nsITabParent> newRemoteTab = newDocShell->GetOpenedRemote(); - NS_ENSURE_TRUE(newRemoteTab, false); + nsCOMPtr<nsIDocShell> windowDocShell = pwindow->GetDocShell(); + NS_ENSURE_TRUE(windowDocShell, false); + + nsCOMPtr<nsIDocShellTreeOwner> treeOwner; + windowDocShell->GetTreeOwner(getter_AddRefs(treeOwner)); + + nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(treeOwner); + NS_ENSURE_TRUE(xulWin, false); + + nsCOMPtr<nsIXULBrowserWindow> xulBrowserWin; + xulWin->GetXULBrowserWindow(getter_AddRefs(xulBrowserWin)); + NS_ENSURE_TRUE(xulBrowserWin, false); + + nsCOMPtr<nsITabParent> newRemoteTab; + rv = xulBrowserWin->ForceInitialBrowserRemote(getter_AddRefs(newRemoteTab)); + NS_ENSURE_SUCCESS(rv, false); MOZ_ASSERT(TabParent::GetFrom(newRemoteTab) == newTab); aFrameScripts->SwapElements(newTab->mDelayedFrameScripts); return true; } TabParent* TabParent::sNextTabParent;
--- a/dom/media/MediaDecoderReader.cpp +++ b/dom/media/MediaDecoderReader.cpp @@ -309,16 +309,15 @@ MediaDecoderReader::Shutdown() // out mTaskQueue, since otherwise any remaining tasks could crash when they // invoke GetTaskQueue()->IsCurrentThreadIn(). if (mTaskQueue && !mTaskQueueIsBorrowed) { // If we own our task queue, shutdown ends when the task queue is done. p = mTaskQueue->BeginShutdown(); } else { // If we don't own our task queue, we resolve immediately (though // asynchronously). - p = new ShutdownPromise(__func__); - p->Resolve(true, __func__); + p = ShutdownPromise::CreateAndResolve(true, __func__); } return p; } } // namespace mozilla
--- a/dom/media/MediaPromise.h +++ b/dom/media/MediaPromise.h @@ -57,38 +57,52 @@ template<typename T> class MediaPromiseH template<typename ResolveValueT, typename RejectValueT, bool IsExclusive> class MediaPromise { public: typedef ResolveValueT ResolveValueType; typedef RejectValueT RejectValueType; NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPromise) + +protected: + // MediaPromise is the public type, and never constructed directly. Construct + // a MediaPromise::Private, defined below. explicit MediaPromise(const char* aCreationSite) : mCreationSite(aCreationSite) , mMutex("MediaPromise Mutex") , mHaveConsumer(false) { PROMISE_LOG("%s creating MediaPromise (%p)", mCreationSite, this); } +public: + // MediaPromise::Private allows us to separate the public interface (upon which + // consumers of the promise may invoke methods like Then()) from the private + // interface (upon which the creator of the promise may invoke Resolve() or + // Reject()). APIs should create and store a MediaPromise::Private (usually + // via a MediaPromiseHolder), and return a MediaPromise to consumers. + // + // NB: We can include the definition of this class inline once B2G ICS is gone. + class Private; + static nsRefPtr<MediaPromise> CreateAndResolve(ResolveValueType aResolveValue, const char* aResolveSite) { - nsRefPtr<MediaPromise> p = new MediaPromise(aResolveSite); + nsRefPtr<typename MediaPromise::Private> p = new MediaPromise::Private(aResolveSite); p->Resolve(aResolveValue, aResolveSite); - return p; + return Move(p); } static nsRefPtr<MediaPromise> CreateAndReject(RejectValueType aRejectValue, const char* aRejectSite) { - nsRefPtr<MediaPromise> p = new MediaPromise(aRejectSite); + nsRefPtr<typename MediaPromise::Private> p = new MediaPromise::Private(aRejectSite); p->Reject(aRejectValue, aRejectSite); - return p; + return Move(p); } class Consumer { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Consumer) void Disconnect() @@ -323,66 +337,48 @@ public: void Then(TargetType* aResponseTarget, const char* aCallSite, ThisType* aThisVal, ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod) { nsRefPtr<Consumer> c = RefableThen(aResponseTarget, aCallSite, aThisVal, aResolveMethod, aRejectMethod); return; } - void ChainTo(already_AddRefed<MediaPromise> aChainedPromise, const char* aCallSite) + void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite) { MutexAutoLock lock(mMutex); MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveConsumer); mHaveConsumer = true; - nsRefPtr<MediaPromise> chainedPromise = aChainedPromise; + nsRefPtr<Private> chainedPromise = aChainedPromise; PROMISE_LOG("%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]", aCallSite, this, chainedPromise.get(), (int) IsPending()); if (!IsPending()) { ForwardTo(chainedPromise); } else { mChainedPromises.AppendElement(chainedPromise); } } - void Resolve(ResolveValueType aResolveValue, const char* aResolveSite) - { - MutexAutoLock lock(mMutex); - MOZ_ASSERT(IsPending()); - PROMISE_LOG("%s resolving MediaPromise (%p created at %s)", aResolveSite, this, mCreationSite); - mResolveValue.emplace(aResolveValue); - DispatchAll(); - } - - void Reject(RejectValueType aRejectValue, const char* aRejectSite) - { - MutexAutoLock lock(mMutex); - MOZ_ASSERT(IsPending()); - PROMISE_LOG("%s rejecting MediaPromise (%p created at %s)", aRejectSite, this, mCreationSite); - mRejectValue.emplace(aRejectValue); - DispatchAll(); - } - protected: bool IsPending() { return mResolveValue.isNothing() && mRejectValue.isNothing(); } void DispatchAll() { mMutex.AssertCurrentThreadOwns(); for (size_t i = 0; i < mThenValues.Length(); ++i) { mThenValues[i]->Dispatch(this); } mThenValues.Clear(); for (size_t i = 0; i < mChainedPromises.Length(); ++i) { ForwardTo(mChainedPromises[i]); } mChainedPromises.Clear(); } - void ForwardTo(MediaPromise* aOther) + void ForwardTo(Private* aOther) { MOZ_ASSERT(!IsPending()); if (mResolveValue.isSome()) { aOther->Resolve(mResolveValue.ref(), "<chained promise>"); } else { aOther->Reject(mRejectValue.ref(), "<chained promise>"); } } @@ -395,20 +391,46 @@ protected: MOZ_ASSERT(mChainedPromises.IsEmpty()); }; const char* mCreationSite; // For logging Mutex mMutex; Maybe<ResolveValueType> mResolveValue; Maybe<RejectValueType> mRejectValue; nsTArray<nsRefPtr<ThenValueBase>> mThenValues; - nsTArray<nsRefPtr<MediaPromise>> mChainedPromises; + nsTArray<nsRefPtr<Private>> mChainedPromises; bool mHaveConsumer; }; +template<typename ResolveValueT, typename RejectValueT, bool IsExclusive> +class MediaPromise<ResolveValueT, RejectValueT, IsExclusive>::Private + : public MediaPromise<ResolveValueT, RejectValueT, IsExclusive> +{ +public: + explicit Private(const char* aCreationSite) : MediaPromise(aCreationSite) {} + + void Resolve(ResolveValueT aResolveValue, const char* aResolveSite) + { + MutexAutoLock lock(mMutex); + MOZ_ASSERT(IsPending()); + PROMISE_LOG("%s resolving MediaPromise (%p created at %s)", aResolveSite, this, mCreationSite); + mResolveValue.emplace(aResolveValue); + DispatchAll(); + } + + void Reject(RejectValueT aRejectValue, const char* aRejectSite) + { + MutexAutoLock lock(mMutex); + MOZ_ASSERT(IsPending()); + PROMISE_LOG("%s rejecting MediaPromise (%p created at %s)", aRejectSite, this, mCreationSite); + mRejectValue.emplace(aRejectValue); + DispatchAll(); + } +}; + /* * Class to encapsulate a promise for a particular role. Use this as the member * variable for a class whose method returns a promise. */ template<typename PromiseType> class MediaPromiseHolder { public: @@ -417,40 +439,40 @@ public: ~MediaPromiseHolder() { MOZ_ASSERT(!mPromise); } already_AddRefed<PromiseType> Ensure(const char* aMethodName) { if (mMonitor) { mMonitor->AssertCurrentThreadOwns(); } if (!mPromise) { - mPromise = new PromiseType(aMethodName); + mPromise = new (typename PromiseType::Private)(aMethodName); } - nsRefPtr<PromiseType> p = mPromise; + nsRefPtr<PromiseType> p = mPromise.get(); return p.forget(); } // Provide a Monitor that should always be held when accessing this instance. void SetMonitor(Monitor* aMonitor) { mMonitor = aMonitor; } bool IsEmpty() { if (mMonitor) { mMonitor->AssertCurrentThreadOwns(); } return !mPromise; } - already_AddRefed<PromiseType> Steal() + already_AddRefed<typename PromiseType::Private> Steal() { if (mMonitor) { mMonitor->AssertCurrentThreadOwns(); } - nsRefPtr<PromiseType> p = mPromise; + nsRefPtr<typename PromiseType::Private> p = mPromise; mPromise = nullptr; return p.forget(); } void Resolve(typename PromiseType::ResolveValueType aResolveValue, const char* aMethodName) { if (mMonitor) { @@ -485,17 +507,17 @@ public: { if (!IsEmpty()) { Reject(aRejectValue, aMethodName); } } private: Monitor* mMonitor; - nsRefPtr<PromiseType> mPromise; + nsRefPtr<typename PromiseType::Private> mPromise; }; /* * Class to encapsulate a MediaPromise::Consumer reference. Use this as the member * variable for a class waiting on a media promise. */ template<typename PromiseType> class MediaPromiseConsumerHolder @@ -585,41 +607,41 @@ protected: Arg1Type mArg1; Arg2Type mArg2; }; template<typename PromiseType> class ProxyRunnable : public nsRunnable { public: - ProxyRunnable(PromiseType* aProxyPromise, MethodCallBase<PromiseType>* aMethodCall) + ProxyRunnable(typename PromiseType::Private* aProxyPromise, MethodCallBase<PromiseType>* aMethodCall) : mProxyPromise(aProxyPromise), mMethodCall(aMethodCall) {} NS_IMETHODIMP Run() { nsRefPtr<PromiseType> p = mMethodCall->Invoke(); mMethodCall = nullptr; p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>"); return NS_OK; } private: - nsRefPtr<PromiseType> mProxyPromise; + nsRefPtr<typename PromiseType::Private> mProxyPromise; nsAutoPtr<MethodCallBase<PromiseType>> mMethodCall; }; template<typename PromiseType, typename TargetType> static nsRefPtr<PromiseType> ProxyInternal(TargetType* aTarget, MethodCallBase<PromiseType>* aMethodCall, const char* aCallerName) { - nsRefPtr<PromiseType> p = new PromiseType(aCallerName); + nsRefPtr<typename PromiseType::Private> p = new (typename PromiseType::Private)(aCallerName); nsRefPtr<ProxyRunnable<PromiseType>> r = new ProxyRunnable<PromiseType>(p, aMethodCall); nsresult rv = detail::DispatchMediaPromiseRunnable(aTarget, r); MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); - return p; + return Move(p); } } // namespace detail template<typename PromiseType, typename TargetType, typename ThisType> static nsRefPtr<PromiseType> ProxyMediaCall(TargetType* aTarget, ThisType* aThisVal, const char* aCallerName, nsRefPtr<PromiseType>(ThisType::*aMethod)())
--- a/dom/media/eme/MediaKeys.cpp +++ b/dom/media/eme/MediaKeys.cpp @@ -77,16 +77,17 @@ CopySessions(const nsAString& aKey, } static PLDHashOperator CloseSessions(const nsAString& aKey, nsRefPtr<MediaKeySession>& aSession, void* aClosure) { aSession->OnClosed(); + ((MediaKeys*)aClosure)->Release(); return PL_DHASH_NEXT; } void MediaKeys::Terminated() { KeySessionHashMap keySessions; // Remove entries during iteration will screw it. Make a copy first. @@ -106,17 +107,19 @@ MediaKeys::Terminated() void MediaKeys::Shutdown() { if (mProxy) { mProxy->Shutdown(); mProxy = nullptr; } - mPromises.Enumerate(&RejectPromises, nullptr); + nsRefPtr<MediaKeys> kungFuDeathGrip = this; + + mPromises.Enumerate(&RejectPromises, this); mPromises.Clear(); } nsPIDOMWindow* MediaKeys::GetParentObject() const { return mParent; } @@ -164,26 +167,32 @@ MediaKeys::MakePromise(ErrorResult& aRv) } PromiseId MediaKeys::StorePromise(Promise* aPromise) { static uint32_t sEMEPromiseCount = 1; MOZ_ASSERT(aPromise); uint32_t id = sEMEPromiseCount++; + + // Keep MediaKeys alive for the lifetime of its promises. Any still-pending + // promises are rejected in Shutdown(). + AddRef(); + mPromises.Put(id, aPromise); return id; } already_AddRefed<Promise> MediaKeys::RetrievePromise(PromiseId aId) { MOZ_ASSERT(mPromises.Contains(aId)); nsRefPtr<Promise> promise; mPromises.Remove(aId, getter_AddRefs(promise)); + Release(); return promise.forget(); } void MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode) { nsRefPtr<Promise> promise(RetrievePromise(aId)); if (!promise) {
--- a/dom/media/fmp4/MP4Reader.cpp +++ b/dom/media/fmp4/MP4Reader.cpp @@ -551,20 +551,17 @@ nsRefPtr<MediaDecoderReader::VideoDataPr MP4Reader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) { MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn()); VLOG("RequestVideoData skip=%d time=%lld", aSkipToNextKeyframe, aTimeThreshold); if (mShutdown) { NS_WARNING("RequestVideoData on shutdown MP4Reader!"); - MonitorAutoLock lock(mVideo.mMonitor); - nsRefPtr<VideoDataPromise> p = mVideo.mPromise.Ensure(__func__); - p->Reject(CANCELED, __func__); - return p; + return VideoDataPromise::CreateAndReject(CANCELED, __func__); } MOZ_ASSERT(HasVideo() && mPlatform && mVideo.mDecoder); bool eos = false; if (ShouldSkip(aSkipToNextKeyframe, aTimeThreshold)) { uint32_t parsed = 0; eos = !SkipVideoDemuxToNextKeyFrame(aTimeThreshold, parsed); @@ -585,24 +582,24 @@ MP4Reader::RequestVideoData(bool aSkipTo return p; } nsRefPtr<MediaDecoderReader::AudioDataPromise> MP4Reader::RequestAudioData() { MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn()); VLOG("RequestAudioData"); + if (mShutdown) { + NS_WARNING("RequestAudioData on shutdown MP4Reader!"); + return AudioDataPromise::CreateAndReject(CANCELED, __func__); + } + MonitorAutoLock lock(mAudio.mMonitor); nsRefPtr<AudioDataPromise> p = mAudio.mPromise.Ensure(__func__); - if (!mShutdown) { - ScheduleUpdate(kAudio); - } else { - NS_WARNING("RequestAudioData on shutdown MP4Reader!"); - p->Reject(CANCELED, __func__); - } + ScheduleUpdate(kAudio); return p; } void MP4Reader::ScheduleUpdate(TrackType aTrack) { auto& decoder = GetDecoderData(aTrack); decoder.mMonitor.AssertCurrentThreadOwns();
--- a/dom/media/gmp-plugin/gmp-test-decryptor.cpp +++ b/dom/media/gmp-plugin/gmp-test-decryptor.cpp @@ -266,27 +266,30 @@ private: static const string OpenAgainRecordId = "open-again-record-id"; class OpenedSecondTimeContinuation : public OpenContinuation { public: explicit OpenedSecondTimeContinuation(GMPRecord* aRecord, TestManager* aTestManager, const string& aTestID) - : mRecord(aRecord), mTestmanager(aTestManager), mTestID(aTestID) {} + : mRecord(aRecord), mTestmanager(aTestManager), mTestID(aTestID) { + MOZ_ASSERT(aRecord); + } virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) MOZ_OVERRIDE { if (GMP_SUCCEEDED(aStatus)) { FakeDecryptor::Message("FAIL OpenSecondTimeContinuation should not be able to re-open record."); } - + if (aRecord) { + aRecord->Close(); + } // Succeeded, open should have failed. mTestmanager->EndTest(mTestID); mRecord->Close(); - delete this; } private: GMPRecord* mRecord; TestManager* const mTestmanager; const string mTestID; }; @@ -296,22 +299,24 @@ public: TestManager* aTestManager, const string& aTestID) : mID(aID), mTestmanager(aTestManager), mTestID(aTestID) {} virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) MOZ_OVERRIDE { if (GMP_FAILED(aStatus)) { FakeDecryptor::Message("FAIL OpenAgainContinuation to open record initially."); mTestmanager->EndTest(mTestID); + if (aRecord) { + aRecord->Close(); + } return; } auto cont = new OpenedSecondTimeContinuation(aRecord, mTestmanager, mTestID); GMPOpenRecord(mID, cont); - delete this; } private: const string mID; TestManager* const mTestmanager; const string mTestID; };
--- a/dom/media/gmp-plugin/gmp-test-storage.cpp +++ b/dom/media/gmp-plugin/gmp-test-storage.cpp @@ -166,54 +166,68 @@ GMPErr GMPRunOnMainThread(GMPTask* aTask) { MOZ_ASSERT(g_platform_api); return g_platform_api->runonmainthread(aTask); } class OpenRecordClient : public GMPRecordClient { public: - GMPErr Init(GMPRecord* aRecord, - OpenContinuation* aContinuation) { - mRecord = aRecord; - mContinuation = aContinuation; - return mRecord->Open(); + /* + * This function will take the memory ownership of the parameters and + * delete them when done. + */ + static void Open(const std::string& aRecordName, + OpenContinuation* aContinuation) { + MOZ_ASSERT(aContinuation); + (new OpenRecordClient(aContinuation))->Do(aRecordName); } virtual void OpenComplete(GMPErr aStatus) MOZ_OVERRIDE { - mContinuation->OpenComplete(aStatus, mRecord); - delete this; + Done(aStatus); } virtual void ReadComplete(GMPErr aStatus, const uint8_t* aData, - uint32_t aDataSize) MOZ_OVERRIDE { } + uint32_t aDataSize) MOZ_OVERRIDE { + MOZ_CRASH("Should not reach here."); + } - virtual void WriteComplete(GMPErr aStatus) MOZ_OVERRIDE { } + virtual void WriteComplete(GMPErr aStatus) MOZ_OVERRIDE { + MOZ_CRASH("Should not reach here."); + } private: + explicit OpenRecordClient(OpenContinuation* aContinuation) + : mRecord(nullptr), mContinuation(aContinuation) {} + + void Do(const std::string& aName) { + auto err = GMPOpenRecord(aName.c_str(), aName.size(), &mRecord, this); + if (GMP_FAILED(err) || + GMP_FAILED(err = mRecord->Open())) { + Done(err); + } + } + + void Done(GMPErr err) { + // mContinuation is responsible for closing mRecord. + mContinuation->OpenComplete(err, mRecord); + delete mContinuation; + delete this; + } + GMPRecord* mRecord; OpenContinuation* mContinuation; }; -GMPErr +void GMPOpenRecord(const std::string& aRecordName, OpenContinuation* aContinuation) { - MOZ_ASSERT(aContinuation); - GMPRecord* record; - OpenRecordClient* client = new OpenRecordClient(); - auto err = GMPOpenRecord(aRecordName.c_str(), - aRecordName.size(), - &record, - client); - if (GMP_FAILED(err)) { - return err; - } - return client->Init(record, aContinuation); + OpenRecordClient::Open(aRecordName, aContinuation); } GMPErr GMPEnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc, void* aUserArg) { return g_platform_api->getrecordenumerator(aRecvIteratorFunc, aUserArg); }
--- a/dom/media/gmp-plugin/gmp-test-storage.h +++ b/dom/media/gmp-plugin/gmp-test-storage.h @@ -47,17 +47,17 @@ GMPErr GMPRunOnMainThread(GMPTask* aTask); class OpenContinuation { public: virtual ~OpenContinuation() {} virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) = 0; }; -GMPErr +void GMPOpenRecord(const std::string& aRecordName, OpenContinuation* aContinuation); GMPErr GMPEnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc, void* aUserArg); #endif // TEST_GMP_STORAGE_H__
--- a/dom/media/gmp/GMPDecryptorParent.cpp +++ b/dom/media/gmp/GMPDecryptorParent.cpp @@ -264,21 +264,19 @@ GMPDecryptorParent::RecvSessionError(con return true; } bool GMPDecryptorParent::RecvKeyStatusChanged(const nsCString& aSessionId, InfallibleTArray<uint8_t>&& aKeyId, const GMPMediaKeyStatus& aStatus) { - if (!mIsOpen) { - NS_WARNING("Trying to use a dead GMP decrypter!"); - return false; + if (mIsOpen) { + mCallback->KeyStatusChanged(aSessionId, aKeyId, aStatus); } - mCallback->KeyStatusChanged(aSessionId, aKeyId, aStatus); return true; } bool GMPDecryptorParent::RecvSetCaps(const uint64_t& aCaps) { if (!mIsOpen) { NS_WARNING("Trying to use a dead GMP decrypter!");
--- a/dom/media/gmp/GMPStorageChild.cpp +++ b/dom/media/gmp/GMPStorageChild.cpp @@ -104,16 +104,21 @@ GMPStorageChild::CreateRecord(const nsCS MonitorAutoLock lock(mMonitor); if (mShutdown) { NS_WARNING("GMPStorage used after it's been shutdown!"); return GMPClosedErr; } MOZ_ASSERT(aRecordName.Length() && aOutRecord); + + if (HasRecord(aRecordName)) { + return GMPRecordInUse; + } + nsRefPtr<GMPRecordImpl> record(new GMPRecordImpl(this, aRecordName, aClient)); mRecords.Put(aRecordName, record); // Addrefs // The GMPRecord holds a self reference until the GMP calls Close() on // it. This means the object is always valid (even if neutered) while // the GMP expects it to be. record.forget(aOutRecord);
--- a/dom/media/mediasource/TrackBuffer.cpp +++ b/dom/media/mediasource/TrackBuffer.cpp @@ -185,86 +185,88 @@ TrackBuffer::AppendData(LargeDataBuffer* int64_t start = 0, end = 0; bool gotMedia = mParser->ParseStartAndEndTimestamps(aData, start, end); bool gotInit = mParser->HasCompleteInitData(); if (newInitData) { if (!gotInit) { // We need a new decoder, but we can't initialize it yet. nsRefPtr<SourceBufferDecoder> decoder = - NewDecoder(aTimestampOffset - mAdjustedTimestamp); + NewDecoder(aTimestampOffset); // The new decoder is stored in mDecoders/mCurrentDecoder, so we // don't need to do anything with 'decoder'. It's only a placeholder. if (!decoder) { mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__); return p; } } else { - if (!decoders.NewDecoder(aTimestampOffset - mAdjustedTimestamp)) { + if (!decoders.NewDecoder(aTimestampOffset)) { mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__); return p; } } } else if (!hadCompleteInitData && gotInit) { MOZ_ASSERT(mCurrentDecoder); // Queue pending decoder for initialization now that we have a full // init segment. decoders.AppendElement(mCurrentDecoder); } if (gotMedia) { - if (mLastEndTimestamp && + if (mParser->IsMediaSegmentPresent(aData) && mLastEndTimestamp && (!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) || mLastTimestampOffset != aTimestampOffset || mDecoderPerSegment || (mCurrentDecoder && mCurrentDecoder->WasTrimmed()))) { MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]", this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end); if (!newInitData) { // This data is earlier in the timeline than data we have already // processed or not continuous, so we must create a new decoder // to handle the decoding. if (!hadCompleteInitData || - !decoders.NewDecoder(aTimestampOffset - mAdjustedTimestamp)) { + !decoders.NewDecoder(aTimestampOffset)) { mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__); return p; } MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this); AppendDataToCurrentResource(oldInit, 0); } mLastStartTimestamp = start; } else { MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]", this, mLastStartTimestamp, mLastEndTimestamp ? mLastEndTimestamp.value() : 0, start, end); } mLastEndTimestamp.reset(); mLastEndTimestamp.emplace(end); } - if (gotMedia && start > 0 && - (start < FUZZ_TIMESTAMP_OFFSET || start < mAdjustedTimestamp)) { + if (gotMedia && start != mAdjustedTimestamp && + ((start < 0 && -start < FUZZ_TIMESTAMP_OFFSET && start < mAdjustedTimestamp) || + (start > 0 && (start < FUZZ_TIMESTAMP_OFFSET || start < mAdjustedTimestamp)))) { AdjustDecodersTimestampOffset(mAdjustedTimestamp - start); mAdjustedTimestamp = start; } if (!AppendDataToCurrentResource(aData, end - start)) { mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__); return p; } if (decoders.Length()) { // We're going to have to wait for the decoder to initialize, the promise // will be resolved once initialization completes. return p; - } else if (gotMedia) { - // Tell our reader that we have more data to ensure that playback starts if - // required when data is appended. - mParentDecoder->GetReader()->MaybeNotifyHaveData(); } + + // Tell our reader that we have more data to ensure that playback starts if + // required when data is appended. + mParentDecoder->GetReader()->MaybeNotifyHaveData(); + mInitializationPromise.Resolve(gotMedia, __func__); return p; } bool TrackBuffer::AppendDataToCurrentResource(LargeDataBuffer* aData, uint32_t aDuration) { MOZ_ASSERT(NS_IsMainThread()); @@ -450,17 +452,18 @@ TrackBuffer::Buffered(dom::TimeRanges* a already_AddRefed<SourceBufferDecoder> TrackBuffer::NewDecoder(int64_t aTimestampOffset) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mParentDecoder); DiscardCurrentDecoder(); - nsRefPtr<SourceBufferDecoder> decoder = mParentDecoder->CreateSubDecoder(mType, aTimestampOffset); + nsRefPtr<SourceBufferDecoder> decoder = + mParentDecoder->CreateSubDecoder(mType, aTimestampOffset - mAdjustedTimestamp); if (!decoder) { return nullptr; } ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor()); mCurrentDecoder = decoder; mDecoders.AppendElement(decoder); mLastStartTimestamp = 0;
--- a/dom/plugins/base/nsJSNPRuntime.cpp +++ b/dom/plugins/base/nsJSNPRuntime.cpp @@ -1867,17 +1867,17 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS if (!sNPObjWrappers.IsInitialized()) { // No hash yet (or any more), initialize it. if (!CreateNPObjWrapperTable()) { return nullptr; } } NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *> - (PL_DHashTableAdd(&sNPObjWrappers, npobj, fallible)); + (PL_DHashTableAdd(&sNPObjWrappers, npobj)); if (!entry) { // Out of memory JS_ReportOutOfMemory(cx); return nullptr; } @@ -2030,17 +2030,17 @@ static NPP LookupNPP(NPObject *npobj) { if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) { nsJSObjWrapper* o = static_cast<nsJSObjWrapper*>(npobj); return o->mNpp; } NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *> - (PL_DHashTableAdd(&sNPObjWrappers, npobj, fallible)); + (PL_DHashTableAdd(&sNPObjWrappers, npobj)); if (!entry) { return nullptr; } NS_ASSERTION(entry->mNpp, "Live NPObject entry w/o an NPP!"); return entry->mNpp;
--- a/dom/tests/mochitest/webcomponents/mochitest.ini +++ b/dom/tests/mochitest/webcomponents/mochitest.ini @@ -5,16 +5,17 @@ support-files = [test_bug900724.html] [test_bug1017896.html] [test_content_element.html] [test_custom_element_adopt_callbacks.html] [test_custom_element_callback_innerhtml.html] [test_custom_element_clone_callbacks.html] [test_custom_element_clone_callbacks_extended.html] [test_custom_element_import_node_created_callback.html] +[test_custom_element_in_shadow.html] [test_nested_content_element.html] [test_dest_insertion_points.html] [test_dest_insertion_points_shadow.html] [test_fallback_dest_insertion_points.html] [test_detached_style.html] [test_dynamic_content_element_matching.html] [test_document_register.html] [test_document_register_base_queue.html]
new file mode 100644 --- /dev/null +++ b/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html @@ -0,0 +1,132 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1087460 +--> +<head> + <title>Test for custom element callbacks in shadow DOM.</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1087460">Bug 1087460</a> +<div id="container"></div> + +<script> + +// Test callback for custom element when used after registration. + +var createdCallbackCount = 0; +var attachedCallbackCount = 0; +var detachedCallbackCount = 0; +var attributeChangedCallbackCount = 0; + +var p1 = Object.create(HTMLElement.prototype); + +p1.createdCallback = function() { + createdCallbackCount++; +}; + +p1.attachedCallback = function() { + attachedCallbackCount++; +}; + +p1.detachedCallback = function() { + detachedCallbackCount++; +}; + +p1.attributeChangedCallback = function(name, oldValue, newValue) { + attributeChangedCallbackCount++; +}; + +document.registerElement("x-foo", { prototype: p1 }); + +var container = document.getElementById("container"); +var shadow = container.createShadowRoot(); + +is(createdCallbackCount, 0, "createdCallback should not be called more than once in this test."); +var customElem = document.createElement("x-foo"); +is(createdCallbackCount, 1, "createdCallback should be called after creating custom element."); + +is(attributeChangedCallbackCount, 0, "attributeChangedCallback should not be called after just creating an element."); +customElem.setAttribute("data-foo", "bar"); +is(attributeChangedCallbackCount, 1, "attributeChangedCallback should be called after setting an attribute."); + +is(attachedCallbackCount, 0, "attachedCallback should not be called on an element that is not in a document/composed document."); +shadow.appendChild(customElem); +is(attachedCallbackCount, 1, "attachedCallback should be called after attaching custom element to the composed document."); + +is(detachedCallbackCount, 0, "detachedCallback should not be called without detaching custom element."); +shadow.removeChild(customElem); +is(detachedCallbackCount, 1, "detachedCallback should be called after detaching custom element from the composed document."); + +// Test callback for custom element already in the composed doc when created. + +createdCallbackCount = 0; +attachedCallbackCount = 0; +detachedCallbackCount = 0; +attributeChangedCallbackCount = 0; + +shadow.innerHTML = "<x-foo></x-foo>"; +is(createdCallbackCount, 1, "createdCallback should be called after creating a custom element."); +is(attachedCallbackCount, 1, "attachedCallback should be called after creating an element in the composed document."); + +shadow.innerHTML = ""; +is(detachedCallbackCount, 1, "detachedCallback should be called after detaching custom element from the composed document."); + +// Test callback for custom element in shadow DOM when host attached/detached to/from document. + +createdCallbackCount = 0; +attachedCallbackCount = 0; +detachedCallbackCount = 0; +attributeChangedCallbackCount = 0; + +var host = document.createElement("div"); +shadow = host.createShadowRoot(); +customElem = document.createElement("x-foo"); + +is(attachedCallbackCount, 0, "attachedCallback should not be called on newly created element."); +shadow.appendChild(customElem); +is(attachedCallbackCount, 0, "attachedCallback should not be called on attaching to a tree that is not in the composed document."); + +is(attachedCallbackCount, 0, "detachedCallback should not be called."); +shadow.removeChild(customElem); +is(detachedCallbackCount, 0, "detachedCallback should not be called when detaching from a tree that is not in the composed document."); + +shadow.appendChild(customElem); +is(attachedCallbackCount, 0, "attachedCallback should still not be called after reattaching to a shadow tree that is not in the composed document."); + +container.appendChild(host); +is(attachedCallbackCount, 1, "attachedCallback should be called after host is inserted into document."); + +container.removeChild(host); +is(detachedCallbackCount, 1, "detachedCallback should be called after host is removed from document."); + +// Test callback for custom element for upgraded element. + +createdCallbackCount = 0; +attachedCallbackCount = 0; +detachedCallbackCount = 0; +attributeChangedCallbackCount = 0; + +shadow = container.shadowRoot; +shadow.innerHTML = "<x-bar></x-bar>"; + +var p2 = Object.create(HTMLElement.prototype); + +p2.createdCallback = function() { + createdCallbackCount++; +}; + +p2.attachedCallback = function() { + attachedCallbackCount++; +}; + +document.registerElement("x-bar", { prototype: p2 }); +is(createdCallbackCount, 1, "createdCallback should be called after registering element."); +is(attachedCallbackCount, 1, "attachedCallback should be called after upgrading element in composed document."); + +</script> + +</body> +</html>
--- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -1991,17 +1991,17 @@ ServiceWorkerManager::CreateServiceWorke // Alternatively we could persist the original load group values and use // them here. rv = NS_NewLoadGroup(getter_AddRefs(info.mLoadGroup), info.mPrincipal); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsRefPtr<ServiceWorker> serviceWorker; - RuntimeService* rs = RuntimeService::GetService(); + RuntimeService* rs = RuntimeService::GetOrCreateService(); if (!rs) { return NS_ERROR_FAILURE; } AutoJSAPI jsapi; jsapi.Init(); rv = rs->CreateServiceWorkerFromLoadInfo(jsapi.cx(), &info, NS_ConvertUTF8toUTF16(aScriptSpec),
--- a/dom/workers/test/test_worker_interfaces.js +++ b/dom/workers/test/test_worker_interfaces.js @@ -30,17 +30,17 @@ var ecmaGlobals = "Float32Array", "Float64Array", "Function", "Infinity", "Int16Array", "Int32Array", "Int8Array", "InternalError", - {name: "Intl", desktop: true}, + {name: "Intl", b2g: false, android: false}, "Iterator", "JSON", "Map", "Math", "NaN", "Number", "Object", "Proxy", @@ -162,31 +162,32 @@ var interfaceNamesInGlobalScope = // IMPORTANT: Do not change this list without review from a DOM peer! "WorkerLocation", // IMPORTANT: Do not change this list without review from a DOM peer! "WorkerNavigator", // IMPORTANT: Do not change this list without review from a DOM peer! ]; // IMPORTANT: Do not change the list above without review from a DOM peer! -function createInterfaceMap(prefMap, permissionMap, version, userAgent) { +function createInterfaceMap(prefMap, permissionMap, version, userAgent, isB2G) { var isNightly = version.endsWith("a1"); var isRelease = !version.contains("a"); var isDesktop = !/Mobile|Tablet/.test(userAgent); - var isB2G = !isDesktop && !userAgent.contains("Android"); + var isAndroid = !!navigator.userAgent.contains("Android"); var interfaceMap = {}; function addInterfaces(interfaces) { for (var entry of interfaces) { if (typeof(entry) === "string") { interfaceMap[entry] = true; } else if ((entry.nightly === !isNightly) || (entry.desktop === !isDesktop) || + (entry.android === !isAndroid) || (entry.b2g === !isB2G) || (entry.release === !isRelease) || (entry.pref && !prefMap[entry.pref]) || (entry.permission && !permissionMap[entry.permission])) { interfaceMap[entry.name] = false; } else { interfaceMap[entry.name] = true; } @@ -194,18 +195,18 @@ function createInterfaceMap(prefMap, per } addInterfaces(ecmaGlobals); addInterfaces(interfaceNamesInGlobalScope); return interfaceMap; } -function runTest(prefMap, permissionMap, version, userAgent) { - var interfaceMap = createInterfaceMap(prefMap, permissionMap, version, userAgent); +function runTest(prefMap, permissionMap, version, userAgent, isB2G) { + var interfaceMap = createInterfaceMap(prefMap, permissionMap, version, userAgent, isB2G); for (var name of Object.getOwnPropertyNames(self)) { // An interface name should start with an upper case character. if (!/^[A-Z]/.test(name)) { continue; } ok(interfaceMap[name], "If this is failing: DANGER, are you sure you want to expose the new interface " + name + " to all webpages as a property on the worker? Do not make a change to this file without a " + @@ -247,14 +248,16 @@ function appendPermissions(permissions, var permissions = []; appendPermissions(permissions, ecmaGlobals); appendPermissions(permissions, interfaceNamesInGlobalScope); workerTestGetPrefs(prefs, function(prefMap) { workerTestGetPermissions(permissions, function(permissionMap) { workerTestGetVersion(function(version) { workerTestGetUserAgent(function(userAgent) { - runTest(prefMap, permissionMap, version, userAgent); - workerTestDone(); + workerTestGetIsB2G(function(isB2G) { + runTest(prefMap, permissionMap, version, userAgent, isB2G); + workerTestDone(); + }); }); }); }); });
--- a/dom/workers/test/worker_driver.js +++ b/dom/workers/test/worker_driver.js @@ -71,16 +71,21 @@ function workerTestExec(script) { type: 'returnUserAgent', result: navigator.userAgent }); } else if (event.data.type == 'getOSCPU') { worker.postMessage({ type: 'returnOSCPU', result: navigator.oscpu }); + } else if (event.data.type == 'getIsB2G') { + worker.postMessage({ + type: 'returnIsB2G', + result: SpecialPowers.isB2G + }); } } worker.onerror = function(event) { ok(false, 'Worker had an error: ' + event.data); SimpleTest.finish(); };
--- a/dom/workers/test/worker_wrapper.js +++ b/dom/workers/test/worker_wrapper.js @@ -94,16 +94,29 @@ function workerTestGetOSCPU(cb) { removeEventListener('message', workerTestGetOSCPUCB); cb(e.data.result); }); postMessage({ type: 'getOSCPU' }); } +function workerTestGetIsB2G(cb) { + addEventListener('message', function workerTestGetIsB2GCB(e) { + if (e.data.type !== 'returnIsB2G') { + return; + } + removeEventListener('message', workerTestGetIsB2GCB); + cb(e.data.result); + }); + postMessage({ + type: 'getIsB2G' + }); +} + addEventListener('message', function workerWrapperOnMessage(e) { removeEventListener('message', workerWrapperOnMessage); var data = e.data; try { importScripts(data.script); } catch(e) { postMessage({ type: 'status',
--- a/dom/xul/XULDocument.cpp +++ b/dom/xul/XULDocument.cpp @@ -211,18 +211,17 @@ XULDocument::~XULDocument() // decls never got resolved. mForwardReferences.Clear(); // Likewise for any references we have to IDs where we might // look for persisted data: mPersistenceIds.Clear(); // Destroy our broadcaster map. if (mBroadcasterMap) { - PL_DHashTableFinish(mBroadcasterMap); - delete mBroadcasterMap; + PL_DHashTableDestroy(mBroadcasterMap); } delete mTemplateBuilderTable; Preferences::UnregisterCallback(XULDocument::DirectionChanged, "intl.uidirection.", this); if (mOffThreadCompileStringBuf) { @@ -764,27 +763,32 @@ XULDocument::AddBroadcastListenerFor(Ele PL_DHashVoidPtrKeyStub, PL_DHashMatchEntryStub, PL_DHashMoveEntryStub, ClearBroadcasterMapEntry, nullptr }; if (! mBroadcasterMap) { - mBroadcasterMap = new PLDHashTable(); - PL_DHashTableInit(mBroadcasterMap, &gOps, sizeof(BroadcasterMapEntry)); + mBroadcasterMap = PL_NewDHashTable(&gOps, sizeof(BroadcasterMapEntry)); + + if (! mBroadcasterMap) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } } BroadcasterMapEntry* entry = static_cast<BroadcasterMapEntry*> (PL_DHashTableSearch(mBroadcasterMap, &aBroadcaster)); if (!entry) { - entry = static_cast<BroadcasterMapEntry*> - (PL_DHashTableAdd(mBroadcasterMap, &aBroadcaster, fallible)); + entry = + static_cast<BroadcasterMapEntry*> + (PL_DHashTableAdd(mBroadcasterMap, &aBroadcaster)); if (! entry) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } entry->mBroadcaster = &aBroadcaster;
--- a/dom/xul/templates/nsContentSupportMap.h +++ b/dom/xul/templates/nsContentSupportMap.h @@ -23,18 +23,17 @@ class nsContentSupportMap { public: nsContentSupportMap() { Init(); } ~nsContentSupportMap() { Finish(); } nsresult Put(nsIContent* aElement, nsTemplateMatch* aMatch) { if (!mMap.IsInitialized()) return NS_ERROR_NOT_INITIALIZED; - PLDHashEntryHdr* hdr = - PL_DHashTableAdd(&mMap, aElement, mozilla::fallible); + PLDHashEntryHdr* hdr = PL_DHashTableAdd(&mMap, aElement); if (!hdr) return NS_ERROR_OUT_OF_MEMORY; Entry* entry = static_cast<Entry*>(hdr); NS_ASSERTION(entry->mMatch == nullptr, "over-writing entry"); entry->mContent = aElement; entry->mMatch = aMatch; return NS_OK;
--- a/dom/xul/templates/nsTemplateMap.h +++ b/dom/xul/templates/nsTemplateMap.h @@ -32,18 +32,17 @@ public: ~nsTemplateMap() { Finish(); } void Put(nsIContent* aContent, nsIContent* aTemplate) { NS_ASSERTION(!PL_DHashTableSearch(&mTable, aContent), "aContent already in map"); - Entry* entry = static_cast<Entry*> - (PL_DHashTableAdd(&mTable, aContent, fallible)); + Entry* entry = static_cast<Entry*>(PL_DHashTableAdd(&mTable, aContent)); if (entry) { entry->mContent = aContent; entry->mTemplate = aTemplate; } } void
--- a/embedding/components/commandhandler/nsCommandParams.cpp +++ b/embedding/components/commandhandler/nsCommandParams.cpp @@ -225,18 +225,17 @@ nsCommandParams::GetOrMakeEntry(const ch { HashEntry *foundEntry = (HashEntry *)PL_DHashTableSearch(&mValuesHash, (void *)aName); if (foundEntry) { // reuse existing entry foundEntry->Reset(entryType); return foundEntry; } - foundEntry = static_cast<HashEntry*> - (PL_DHashTableAdd(&mValuesHash, (void *)aName, fallible)); + foundEntry = (HashEntry *)PL_DHashTableAdd(&mValuesHash, (void *)aName); if (!foundEntry) { return nullptr; } // Use placement new. Our ctor does not clobber keyHash, which is important. new (foundEntry) HashEntry(entryType, aName); return foundEntry; }
--- a/gfx/layers/TiledLayerBuffer.h +++ b/gfx/layers/TiledLayerBuffer.h @@ -7,16 +7,17 @@ // Debug defines //#define GFX_TILEDLAYER_DEBUG_OVERLAY //#define GFX_TILEDLAYER_PREF_WARNINGS #include <stdint.h> // for uint16_t, uint32_t #include <sys/types.h> // for int32_t #include "gfxPlatform.h" // for GetTileWidth/GetTileHeight +#include "mozilla/gfx/Logging.h" // for gfxCriticalError #include "nsDebug.h" // for NS_ASSERTION #include "nsPoint.h" // for nsIntPoint #include "nsRect.h" // for nsIntRect #include "nsRegion.h" // for nsIntRegion #include "nsTArray.h" // for nsTArray #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 #include <ui/Fence.h> @@ -335,31 +336,33 @@ TiledLayerBuffer<Derived, Tile>::Dump(st } y += h; } x += w; } } template<typename Derived, typename Tile> void -TiledLayerBuffer<Derived, Tile>::Update(const nsIntRegion& aNewValidRegion, +TiledLayerBuffer<Derived, Tile>::Update(const nsIntRegion& newValidRegion, const nsIntRegion& aPaintRegion) { gfx::IntSize scaledTileSize = GetScaledTileSize(); nsTArray<Tile> newRetainedTiles; nsTArray<Tile>& oldRetainedTiles = mRetainedTiles; const nsIntRect oldBound = mValidRegion.GetBounds(); - const nsIntRect newBound = aNewValidRegion.GetBounds(); + const nsIntRect newBound = newValidRegion.GetBounds(); const nsIntPoint oldBufferOrigin(RoundDownToTileEdge(oldBound.x, scaledTileSize.width), RoundDownToTileEdge(oldBound.y, scaledTileSize.height)); const nsIntPoint newBufferOrigin(RoundDownToTileEdge(newBound.x, scaledTileSize.width), RoundDownToTileEdge(newBound.y, scaledTileSize.height)); + + // This is the reason we break the style guide with newValidRegion instead + // of aNewValidRegion - so that the names match better and code easier to read const nsIntRegion& oldValidRegion = mValidRegion; - const nsIntRegion& newValidRegion = aNewValidRegion; const int oldRetainedHeight = mRetainedHeight; // Pass 1: Recycle valid content from the old buffer // Recycle tiles from the old buffer that contain valid regions. // Insert placeholders tiles if we have no valid area for that tile // which we will allocate in pass 2. // TODO: Add a tile pool to reduce new allocation int tileX = 0; @@ -441,22 +444,33 @@ TiledLayerBuffer<Derived, Tile>::Update( if (oldTileCount >= tilesMissing) { oldRetainedTiles[i] = AsDerived().GetPlaceholderTile(); AsDerived().ReleaseTile(oldTile); } else { oldTileCount ++; } } - MOZ_ASSERT(aNewValidRegion.Contains(aPaintRegion), "Painting a region outside the visible region"); + if (!newValidRegion.Contains(aPaintRegion)) { + gfxCriticalError() << "Painting outside visible:" + << " paint " << aPaintRegion.ToString().get() + << " old valid " << oldValidRegion.ToString().get() + << " new valid " << newValidRegion.ToString().get(); + } #ifdef DEBUG nsIntRegion oldAndPainted(oldValidRegion); oldAndPainted.Or(oldAndPainted, aPaintRegion); + if (!oldAndPainted.Contains(newValidRegion)) { + gfxCriticalError() << "Not fully painted:" + << " paint " << aPaintRegion.ToString().get() + << " old valid " << oldValidRegion.ToString().get() + << " old painted " << oldAndPainted.ToString().get() + << " new valid " << newValidRegion.ToString().get(); + } #endif - MOZ_ASSERT(oldAndPainted.Contains(newValidRegion), "newValidRegion has not been fully painted"); nsIntRegion regionToPaint(aPaintRegion); // Pass 2: Validate // We know at this point that any tile in the new buffer that had valid content // from the previous buffer is placed correctly in the new buffer. // We know that any tile in the old buffer that isn't a place holder is // of no use and can be recycled. @@ -548,16 +562,16 @@ TiledLayerBuffer<Derived, Tile>::Update( AsDerived().PostValidate(aPaintRegion); for (unsigned int i = 0; i < mRetainedTiles.Length(); ++i) { AsDerived().UnlockTile(mRetainedTiles[i]); } // At this point, oldTileCount should be zero MOZ_ASSERT(oldTileCount == 0, "Failed to release old tiles"); - mValidRegion = aNewValidRegion; + mValidRegion = newValidRegion; mPaintedRegion.Or(mPaintedRegion, aPaintRegion); } } // layers } // mozilla #endif // GFX_TILEDLAYERBUFFER_H
--- a/gfx/thebes/gfxFT2FontList.cpp +++ b/gfx/thebes/gfxFT2FontList.cpp @@ -683,18 +683,19 @@ public: } uint32_t timestamp = strtoul(beginning, nullptr, 10); beginning = end + 1; if (!(end = strchr(beginning, ';'))) { break; } uint32_t filesize = strtoul(beginning, nullptr, 10); - FNCMapEntry* mapEntry = static_cast<FNCMapEntry*> - (PL_DHashTableAdd(&mMap, filename.get(), fallible)); + FNCMapEntry* mapEntry = + static_cast<FNCMapEntry*> + (PL_DHashTableAdd(&mMap, filename.get())); if (mapEntry) { mapEntry->mFilename.Assign(filename); mapEntry->mTimestamp = timestamp; mapEntry->mFilesize = filesize; mapEntry->mFaces.Assign(faceList); // entries from the startupcache are marked "non-existing" // until we have confirmed that the file still exists mapEntry->mFileExists = false; @@ -731,18 +732,19 @@ public: virtual void CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList, uint32_t aTimestamp, uint32_t aFilesize) { if (!mMap.IsInitialized()) { return; } - FNCMapEntry* entry = static_cast<FNCMapEntry*> - (PL_DHashTableAdd(&mMap, aFileName.get(), fallible)); + FNCMapEntry* entry = + static_cast<FNCMapEntry*> + (PL_DHashTableAdd(&mMap, aFileName.get())); if (entry) { entry->mFilename.Assign(aFileName); entry->mTimestamp = aTimestamp; entry->mFilesize = aFilesize; entry->mFaces.Assign(aFaceList); entry->mFileExists = true; } mWriteNeeded = true;
--- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -1179,16 +1179,17 @@ RasterImage::OnImageDataComplete(nsIRequ void RasterImage::NotifyForDecodeOnDrawOnly() { if (!NS_IsMainThread()) { nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethod(this, &RasterImage::NotifyForDecodeOnDrawOnly); NS_DispatchToMainThread(runnable); + return; } NotifyProgress(FLAG_DECODE_STARTED | FLAG_ONLOAD_BLOCKED); } nsresult RasterImage::OnImageDataAvailable(nsIRequest*, nsISupports*,
--- a/image/src/SurfaceCache.cpp +++ b/image/src/SurfaceCache.cpp @@ -6,17 +6,17 @@ /** * SurfaceCache is a service for caching temporary surfaces in imagelib. */ #include "SurfaceCache.h" #include <algorithm> #include "mozilla/Assertions.h" -#include "mozilla/Attributes.h" // for MOZ_THIS_IN_INITIALIZER_LIST +#include "mozilla/Attributes.h" #include "mozilla/DebugOnly.h" #include "mozilla/Likely.h" #include "mozilla/Move.h" #include "mozilla/Mutex.h" #include "mozilla/RefPtr.h" #include "mozilla/StaticPtr.h" #include "nsIMemoryReporter.h" #include "gfx2DGlue.h"
--- a/js/ipc/JavaScriptParent.cpp +++ b/js/ipc/JavaScriptParent.cpp @@ -4,18 +4,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 "JavaScriptParent.h" #include "mozilla/dom/ContentParent.h" #include "nsJSUtils.h" #include "jsfriendapi.h" -#include "jsproxy.h" #include "jswrapper.h" +#include "js/Proxy.h" #include "HeapAPI.h" #include "xpcprivate.h" #include "mozilla/Casting.h" using namespace js; using namespace JS; using namespace mozilla; using namespace mozilla::jsipc;
--- a/js/ipc/WrapperOwner.h +++ b/js/ipc/WrapperOwner.h @@ -7,33 +7,33 @@ #ifndef mozilla_jsipc_WrapperOwner_h__ #define mozilla_jsipc_WrapperOwner_h__ #include "JavaScriptShared.h" #include "mozilla/ipc/ProtocolUtils.h" #include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "js/Class.h" -#include "jsproxy.h" +#include "js/Proxy.h" namespace mozilla { namespace jsipc { class WrapperOwner : public virtual JavaScriptShared { public: typedef mozilla::ipc::IProtocolManager< mozilla::ipc::IProtocol>::ActorDestroyReason ActorDestroyReason; explicit WrapperOwner(JSRuntime *rt); bool init(); // Standard internal methods. - // (The traps should be in the same order like js/src/jsproxy.h) + // (The traps should be in the same order like js/Proxy.h) bool getOwnPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::MutableHandle<JSPropertyDescriptor> desc); bool defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::MutableHandle<JSPropertyDescriptor> desc); bool ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props); bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp); bool preventExtensions(JSContext *cx, JS::HandleObject proxy, bool *succeeded); bool isExtensible(JSContext *cx, JS::HandleObject proxy, bool *extensible);
--- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -54,16 +54,17 @@ namespace JS { D(DEBUG_GC) \ D(COMPARTMENT_REVIVED) \ D(RESET) \ D(OUT_OF_NURSERY) \ D(EVICT_NURSERY) \ D(FULL_STORE_BUFFER) \ D(SHARED_MEMORY_LIMIT) \ D(PERIODIC_FULL_GC) \ + D(INCREMENTAL_TOO_SLOW) \ \ /* These are reserved for future use. */ \ D(RESERVED0) \ D(RESERVED1) \ D(RESERVED2) \ D(RESERVED3) \ D(RESERVED4) \ D(RESERVED5) \ @@ -73,17 +74,16 @@ namespace JS { D(RESERVED9) \ D(RESERVED10) \ D(RESERVED11) \ D(RESERVED12) \ D(RESERVED13) \ D(RESERVED14) \ D(RESERVED15) \ D(RESERVED16) \ - D(RESERVED17) \ \ /* Reasons from Firefox */ \ D(DOM_WINDOW_UTILS) \ D(COMPONENT_UTILS) \ D(MEM_PRESSURE) \ D(CC_WAITING) \ D(CC_FORCED) \ D(LOAD_END) \
rename from js/src/jsproxy.h rename to js/public/Proxy.h --- a/js/src/jsproxy.h +++ b/js/public/Proxy.h @@ -1,16 +1,16 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef jsproxy_h -#define jsproxy_h +#ifndef js_Proxy_h +#define js_Proxy_h #include "mozilla/Maybe.h" #include "jsfriendapi.h" #include "js/CallNonGenericMethod.h" #include "js/Class.h" @@ -674,9 +674,9 @@ inline void assertEnteredPolicy(JSContex {} #endif } /* namespace js */ extern JS_FRIEND_API(JSObject *) js_InitProxyClass(JSContext *cx, JS::HandleObject obj); -#endif /* jsproxy_h */ +#endif /* js_Proxy_h */
--- a/js/public/Utility.h +++ b/js/public/Utility.h @@ -175,356 +175,50 @@ static inline char* js_strdup(const char * operations on the FreeOp provided to the finalizer: * * FreeOp::{free_,delete_} * * The advantage of these operations is that the memory is batched and freed * on another thread. */ -#define JS_NEW_BODY(allocator, t, parms) \ - void *memory = allocator(sizeof(t)); \ - return memory ? new(memory) t parms : nullptr; -#define JS_MAKE_BODY(newname, T, parms) \ - T *ptr = newname<T> parms; \ - return mozilla::UniquePtr<T, JS::DeletePolicy<T>>(ptr); - /* - * Given a class which should provide 'new' methods, add - * JS_DECLARE_NEW_METHODS (see JSContext for a usage example). This - * adds news with up to 12 parameters. Add more versions of new below if - * you need more than 12 parameters. + * Given a class which should provide a 'new' method, add + * JS_DECLARE_NEW_METHODS (see js::MallocProvider for an example). * * Note: Do not add a ; at the end of a use of JS_DECLARE_NEW_METHODS, * or the build will break. */ #define JS_DECLARE_NEW_METHODS(NEWNAME, ALLOCATOR, QUALIFIERS)\ - template <class T>\ - QUALIFIERS T *NEWNAME() MOZ_HEAP_ALLOCATOR {\ - JS_NEW_BODY(ALLOCATOR, T, ())\ - }\ -\ - template <class T, class P1>\ - QUALIFIERS T *NEWNAME(P1 &&p1) MOZ_HEAP_ALLOCATOR {\ - JS_NEW_BODY(ALLOCATOR, T,\ - (mozilla::Forward<P1>(p1)))\ - }\ -\ - template <class T, class P1, class P2>\ - QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2) MOZ_HEAP_ALLOCATOR {\ - JS_NEW_BODY(ALLOCATOR, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2)))\ - }\ -\ - template <class T, class P1, class P2, class P3>\ - QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3) MOZ_HEAP_ALLOCATOR {\ - JS_NEW_BODY(ALLOCATOR, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4>\ - QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4) MOZ_HEAP_ALLOCATOR {\ - JS_NEW_BODY(ALLOCATOR, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5>\ - QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5) MOZ_HEAP_ALLOCATOR {\ - JS_NEW_BODY(ALLOCATOR, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5, class P6>\ - QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6) MOZ_HEAP_ALLOCATOR {\ - JS_NEW_BODY(ALLOCATOR, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5),\ - mozilla::Forward<P6>(p6)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7>\ - QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7) MOZ_HEAP_ALLOCATOR {\ - JS_NEW_BODY(ALLOCATOR, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5),\ - mozilla::Forward<P6>(p6),\ - mozilla::Forward<P7>(p7)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8>\ - QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8) MOZ_HEAP_ALLOCATOR {\ - JS_NEW_BODY(ALLOCATOR, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5),\ - mozilla::Forward<P6>(p6),\ - mozilla::Forward<P7>(p7),\ - mozilla::Forward<P8>(p8)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9>\ - QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9) MOZ_HEAP_ALLOCATOR {\ - JS_NEW_BODY(ALLOCATOR, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5),\ - mozilla::Forward<P6>(p6),\ - mozilla::Forward<P7>(p7),\ - mozilla::Forward<P8>(p8),\ - mozilla::Forward<P9>(p9)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10>\ - QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10) MOZ_HEAP_ALLOCATOR {\ - JS_NEW_BODY(ALLOCATOR, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5),\ - mozilla::Forward<P6>(p6),\ - mozilla::Forward<P7>(p7),\ - mozilla::Forward<P8>(p8),\ - mozilla::Forward<P9>(p9),\ - mozilla::Forward<P10>(p10)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11>\ - QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11) MOZ_HEAP_ALLOCATOR {\ - JS_NEW_BODY(ALLOCATOR, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5),\ - mozilla::Forward<P6>(p6),\ - mozilla::Forward<P7>(p7),\ - mozilla::Forward<P8>(p8),\ - mozilla::Forward<P9>(p9),\ - mozilla::Forward<P10>(p10),\ - mozilla::Forward<P11>(p11)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11, class P12>\ - QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11, P12 &&p12) MOZ_HEAP_ALLOCATOR {\ - JS_NEW_BODY(ALLOCATOR, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5),\ - mozilla::Forward<P6>(p6),\ - mozilla::Forward<P7>(p7),\ - mozilla::Forward<P8>(p8),\ - mozilla::Forward<P9>(p9),\ - mozilla::Forward<P10>(p10),\ - mozilla::Forward<P11>(p11),\ - mozilla::Forward<P12>(p12)))\ - }\ + template <class T, typename... Args> \ + QUALIFIERS T * \ + NEWNAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \ + void *memory = ALLOCATOR(sizeof(T)); \ + return memory \ + ? new(memory) T(mozilla::Forward<Args>(args)...) \ + : nullptr; \ + } /* * Given a class which should provide 'make' methods, add - * JS_DECLARE_MAKE_METHODS (see JSContext for a usage example). This method - * is functionally the same as JS_DECLARE_NEW_METHODS: it just declares methods - * that return mozilla::UniquePtr instances that will singly-manage ownership - * of the created object. This adds makes with up to 12 parameters. Add more - * versions below if you need more than 12 parameters. + * JS_DECLARE_MAKE_METHODS (see js::MallocProvider for an example). This + * method is functionally the same as JS_DECLARE_NEW_METHODS: it just declares + * methods that return mozilla::UniquePtr instances that will singly-manage + * ownership of the created object. * * Note: Do not add a ; at the end of a use of JS_DECLARE_MAKE_METHODS, * or the build will break. */ #define JS_DECLARE_MAKE_METHODS(MAKENAME, NEWNAME, QUALIFIERS)\ - template <class T> \ - QUALIFIERS \ - mozilla::UniquePtr<T, JS::DeletePolicy<T>> \ - MAKENAME() MOZ_HEAP_ALLOCATOR {\ - JS_MAKE_BODY(NEWNAME, T, ())\ - }\ -\ - template <class T, class P1> \ - QUALIFIERS \ - mozilla::UniquePtr<T, JS::DeletePolicy<T>> \ - MAKENAME(P1 &&p1) MOZ_HEAP_ALLOCATOR {\ - JS_MAKE_BODY(NEWNAME, T,\ - (mozilla::Forward<P1>(p1)))\ - }\ -\ - template <class T, class P1, class P2> \ - QUALIFIERS \ -mozilla::UniquePtr<T, JS::DeletePolicy<T>> \ - MAKENAME(P1 &&p1, P2 &&p2) MOZ_HEAP_ALLOCATOR {\ - JS_MAKE_BODY(NEWNAME, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2)))\ - }\ -\ - template <class T, class P1, class P2, class P3> \ - QUALIFIERS \ - mozilla::UniquePtr<T, JS::DeletePolicy<T>> \ - MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3) MOZ_HEAP_ALLOCATOR {\ - JS_MAKE_BODY(NEWNAME, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4> \ - QUALIFIERS \ - mozilla::UniquePtr<T, JS::DeletePolicy<T>> \ - MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4) MOZ_HEAP_ALLOCATOR {\ - JS_MAKE_BODY(NEWNAME, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5> \ - QUALIFIERS \ - mozilla::UniquePtr<T, JS::DeletePolicy<T>> \ - MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5) MOZ_HEAP_ALLOCATOR {\ - JS_MAKE_BODY(NEWNAME, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5, class P6> \ - QUALIFIERS \ - mozilla::UniquePtr<T, JS::DeletePolicy<T>> \ - MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6) MOZ_HEAP_ALLOCATOR {\ - JS_MAKE_BODY(NEWNAME, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5),\ - mozilla::Forward<P6>(p6)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7> \ - QUALIFIERS \ - mozilla::UniquePtr<T, JS::DeletePolicy<T>> \ - MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7) MOZ_HEAP_ALLOCATOR {\ - JS_MAKE_BODY(NEWNAME, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5),\ - mozilla::Forward<P6>(p6),\ - mozilla::Forward<P7>(p7)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8> \ - QUALIFIERS \ - mozilla::UniquePtr<T, JS::DeletePolicy<T>> \ - MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8) MOZ_HEAP_ALLOCATOR {\ - JS_MAKE_BODY(NEWNAME, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5),\ - mozilla::Forward<P6>(p6),\ - mozilla::Forward<P7>(p7),\ - mozilla::Forward<P8>(p8)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9> \ - QUALIFIERS \ - mozilla::UniquePtr<T, JS::DeletePolicy<T>> \ - MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9) MOZ_HEAP_ALLOCATOR {\ - JS_MAKE_BODY(NEWNAME, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5),\ - mozilla::Forward<P6>(p6),\ - mozilla::Forward<P7>(p7),\ - mozilla::Forward<P8>(p8),\ - mozilla::Forward<P9>(p9)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10> \ - QUALIFIERS \ - mozilla::UniquePtr<T, JS::DeletePolicy<T>> \ - MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10) MOZ_HEAP_ALLOCATOR {\ - JS_MAKE_BODY(NEWNAME, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5),\ - mozilla::Forward<P6>(p6),\ - mozilla::Forward<P7>(p7),\ - mozilla::Forward<P8>(p8),\ - mozilla::Forward<P9>(p9),\ - mozilla::Forward<P10>(p10)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11> \ - QUALIFIERS \ - mozilla::UniquePtr<T, JS::DeletePolicy<T>> \ - MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11) MOZ_HEAP_ALLOCATOR {\ - JS_MAKE_BODY(NEWNAME, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5),\ - mozilla::Forward<P6>(p6),\ - mozilla::Forward<P7>(p7),\ - mozilla::Forward<P8>(p8),\ - mozilla::Forward<P9>(p9),\ - mozilla::Forward<P10>(p10),\ - mozilla::Forward<P11>(p11)))\ - }\ -\ - template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11, class P12> \ - QUALIFIERS \ - mozilla::UniquePtr<T, JS::DeletePolicy<T>> \ - MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11, P12 &&p12) MOZ_HEAP_ALLOCATOR {\ - JS_MAKE_BODY(NEWNAME, T,\ - (mozilla::Forward<P1>(p1),\ - mozilla::Forward<P2>(p2),\ - mozilla::Forward<P3>(p3),\ - mozilla::Forward<P4>(p4),\ - mozilla::Forward<P5>(p5),\ - mozilla::Forward<P6>(p6),\ - mozilla::Forward<P7>(p7),\ - mozilla::Forward<P8>(p8),\ - mozilla::Forward<P9>(p9),\ - mozilla::Forward<P10>(p10),\ - mozilla::Forward<P11>(p11),\ - mozilla::Forward<P12>(p12)))\ - }\ + template <class T, typename... Args> \ + QUALIFIERS mozilla::UniquePtr<T, JS::DeletePolicy<T>> \ + MAKENAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \ + T *ptr = NEWNAME<T>(mozilla::Forward<Args>(args)...); \ + return mozilla::UniquePtr<T, JS::DeletePolicy<T>>(ptr); \ + } JS_DECLARE_NEW_METHODS(js_new, js_malloc, static MOZ_ALWAYS_INLINE) template <class T> static MOZ_ALWAYS_INLINE void js_delete(T *p) { if (p) {
--- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -178,16 +178,124 @@ CaseExpr(ParseNode *pn) static inline ParseNode * CaseBody(ParseNode *pn) { MOZ_ASSERT(pn->isKind(PNK_CASE) || pn->isKind(PNK_DEFAULT)); return BinaryRight(pn); } +static inline ParseNode * +BinaryOpLeft(ParseNode *pn) +{ + MOZ_ASSERT(pn->isBinaryOperation()); + MOZ_ASSERT(pn->isArity(PN_LIST)); + MOZ_ASSERT(pn->pn_count == 2); + return ListHead(pn); +} + +static inline ParseNode * +BinaryOpRight(ParseNode *pn) +{ + MOZ_ASSERT(pn->isBinaryOperation()); + MOZ_ASSERT(pn->isArity(PN_LIST)); + MOZ_ASSERT(pn->pn_count == 2); + return NextNode(ListHead(pn)); +} + +static inline ParseNode * +BitwiseLeft(ParseNode *pn) +{ + return BinaryOpLeft(pn); +} + +static inline ParseNode * +BitwiseRight(ParseNode *pn) +{ + return BinaryOpRight(pn); +} + +static inline ParseNode * +MultiplyLeft(ParseNode *pn) +{ + MOZ_ASSERT(pn->isKind(PNK_STAR)); + return BinaryOpLeft(pn); +} + +static inline ParseNode * +MultiplyRight(ParseNode *pn) +{ + MOZ_ASSERT(pn->isKind(PNK_STAR)); + return BinaryOpRight(pn); +} + +static inline ParseNode * +AddSubLeft(ParseNode *pn) +{ + MOZ_ASSERT(pn->isKind(PNK_ADD) || pn->isKind(PNK_SUB)); + return BinaryOpLeft(pn); +} + +static inline ParseNode * +AddSubRight(ParseNode *pn) +{ + MOZ_ASSERT(pn->isKind(PNK_ADD) || pn->isKind(PNK_SUB)); + return BinaryOpRight(pn); +} + +static inline ParseNode * +DivOrModLeft(ParseNode *pn) +{ + MOZ_ASSERT(pn->isKind(PNK_DIV) || pn->isKind(PNK_MOD)); + return BinaryOpLeft(pn); +} + +static inline ParseNode * +DivOrModRight(ParseNode *pn) +{ + MOZ_ASSERT(pn->isKind(PNK_DIV) || pn->isKind(PNK_MOD)); + return BinaryOpRight(pn); +} + +static inline ParseNode * +ComparisonLeft(ParseNode *pn) +{ + return BinaryOpLeft(pn); +} + +static inline ParseNode * +ComparisonRight(ParseNode *pn) +{ + return BinaryOpRight(pn); +} + +static inline ParseNode * +AndOrLeft(ParseNode *pn) +{ + return BinaryOpLeft(pn); +} + +static inline ParseNode * +AndOrRight(ParseNode *pn) +{ + return BinaryOpRight(pn); +} + +static inline ParseNode * +RelationalLeft(ParseNode *pn) +{ + return BinaryOpLeft(pn); +} + +static inline ParseNode * +RelationalRight(ParseNode *pn) +{ + return BinaryOpRight(pn); +} + static inline bool IsExpressionStatement(ParseNode *pn) { return pn->isKind(PNK_SEMI); } static inline ParseNode * ExpressionStatementExpr(ParseNode *pn) @@ -3707,23 +3815,23 @@ CheckGlobalVariableInitConstant(ModuleCo } static bool CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *coercion, ParseNode **coercedExpr = nullptr) { switch (coercionNode->getKind()) { case PNK_BITOR: { - ParseNode *rhs = BinaryRight(coercionNode); + ParseNode *rhs = BitwiseRight(coercionNode); uint32_t i; if (!IsLiteralInt(m, rhs, &i) || i != 0) return m.fail(rhs, "must use |0 for argument/return coercion"); *coercion = AsmJS_ToInt32; if (coercedExpr) - *coercedExpr = BinaryLeft(coercionNode); + *coercedExpr = BitwiseLeft(coercionNode); return true; } case PNK_POS: { *coercion = AsmJS_ToNumber; if (coercedExpr) *coercedExpr = UnaryKid(coercionNode); return true; } @@ -4324,18 +4432,20 @@ IsLiteralOrConstInt(FunctionCompiler &f, return IsLiteralInt(lit, u32); } static bool FoldMaskedArrayIndex(FunctionCompiler &f, ParseNode **indexExpr, int32_t *mask, NeedsBoundsCheck *needsBoundsCheck) { - ParseNode *indexNode = BinaryLeft(*indexExpr); - ParseNode *maskNode = BinaryRight(*indexExpr); + MOZ_ASSERT((*indexExpr)->isKind(PNK_BITAND)); + + ParseNode *indexNode = BitwiseLeft(*indexExpr); + ParseNode *maskNode = BitwiseRight(*indexExpr); uint32_t mask2; if (IsLiteralOrConstInt(f, maskNode, &mask2)) { // Flag the access to skip the bounds check if the mask ensures that an 'out of // bounds' access can not occur based on the current heap length constraint. if (mask2 == 0 || CountLeadingZeroes32(f.m().minHeapLength() - 1) <= CountLeadingZeroes32(mask2)) { *needsBoundsCheck = NO_BOUNDS_CHECK; @@ -4383,26 +4493,27 @@ CheckArrayAccess(FunctionCompiler &f, Pa // Mask off the low bits to account for the clearing effect of a right shift // followed by the left shift implicit in the array access. E.g., H32[i>>2] // loses the low two bits. int32_t mask = ~(TypedArrayElemSize(*viewType) - 1); MDefinition *pointerDef; if (indexExpr->isKind(PNK_RSH)) { - ParseNode *shiftNode = BinaryRight(indexExpr); - ParseNode *pointerNode = BinaryLeft(indexExpr); + ParseNode *shiftAmountNode = BitwiseRight(indexExpr); uint32_t shift; - if (!IsLiteralInt(f.m(), shiftNode, &shift)) - return f.failf(shiftNode, "shift amount must be constant"); + if (!IsLiteralInt(f.m(), shiftAmountNode, &shift)) + return f.failf(shiftAmountNode, "shift amount must be constant"); unsigned requiredShift = TypedArrayShift(*viewType); if (shift != requiredShift) - return f.failf(shiftNode, "shift amount must be %u", requiredShift); + return f.failf(shiftAmountNode, "shift amount must be %u", requiredShift); + + ParseNode *pointerNode = BitwiseLeft(indexExpr); if (pointerNode->isKind(PNK_BITAND)) FoldMaskedArrayIndex(f, &pointerNode, &mask, needsBoundsCheck); f.enterHeapExpression(); Type pointerType; if (!CheckExpr(f, pointerNode, &pointerDef, &pointerType)) @@ -4591,16 +4702,17 @@ CheckAssignName(FunctionCompiler &f, Par *type = rhsType; return true; } static bool CheckAssign(FunctionCompiler &f, ParseNode *assign, MDefinition **def, Type *type) { MOZ_ASSERT(assign->isKind(PNK_ASSIGN)); + ParseNode *lhs = BinaryLeft(assign); ParseNode *rhs = BinaryRight(assign); if (lhs->getKind() == PNK_ELEM) return CheckStoreArray(f, lhs, rhs, def, type); if (lhs->getKind() == PNK_NAME) return CheckAssignName(f, lhs, rhs, def, type); @@ -5089,18 +5201,18 @@ CheckFuncPtrCall(FunctionCompiler &f, Pa if (const ModuleCompiler::Global *existing = f.lookupGlobal(name)) { if (existing->which() != ModuleCompiler::Global::FuncPtrTable) return f.failName(tableNode, "'%s' is not the name of a function-pointer array", name); } if (!indexExpr->isKind(PNK_BITAND)) return f.fail(indexExpr, "function-pointer table index expression needs & mask"); - ParseNode *indexNode = BinaryLeft(indexExpr); - ParseNode *maskNode = BinaryRight(indexExpr); + ParseNode *indexNode = BitwiseLeft(indexExpr); + ParseNode *maskNode = BitwiseRight(indexExpr); uint32_t mask; if (!IsLiteralInt(f.m(), maskNode, &mask) || mask == UINT32_MAX || !IsPowerOfTwo(mask + 1)) return f.fail(maskNode, "function-pointer table index mask value must be a power of two minus 1"); MDefinition *indexDef; Type indexType; if (!CheckExpr(f, indexNode, &indexDef, &indexType)) @@ -5735,32 +5847,22 @@ static bool CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global, MDefinition **def, Type *type) { MOZ_ASSERT(global->isSimdOperation()); AsmJSSimdType opType = global->simdOperationType(); switch (global->simdOperation()) { - case AsmJSSimdOperation_add: - return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Add, def, type); - case AsmJSSimdOperation_sub: - return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Sub, def, type); - case AsmJSSimdOperation_mul: - return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Mul, def, type); - case AsmJSSimdOperation_div: - return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Div, def, type); - case AsmJSSimdOperation_max: - return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Max, def, type); - case AsmJSSimdOperation_min: - return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Min, def, type); - case AsmJSSimdOperation_maxNum: - return CheckSimdBinary(f, call, opType, MSimdBinaryArith::MaxNum, def, type); - case AsmJSSimdOperation_minNum: - return CheckSimdBinary(f, call, opType, MSimdBinaryArith::MinNum, def, type); +#define OP_CHECK_CASE_LIST_(OP) \ + case AsmJSSimdOperation_##OP: \ + return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Op_##OP, def, type); + ARITH_COMMONX4_SIMD_OP(OP_CHECK_CASE_LIST_) + ARITH_FLOAT32X4_SIMD_OP(OP_CHECK_CASE_LIST_) +#undef OP_CHECK_CASE_LIST_ case AsmJSSimdOperation_lessThan: return CheckSimdBinary(f, call, opType, MSimdBinaryComp::lessThan, def, type); case AsmJSSimdOperation_lessThanOrEqual: return CheckSimdBinary(f, call, opType, MSimdBinaryComp::lessThanOrEqual, def, type); case AsmJSSimdOperation_equal: return CheckSimdBinary(f, call, opType, MSimdBinaryComp::equal, def, type); case AsmJSSimdOperation_notEqual: @@ -6282,18 +6384,18 @@ IsValidIntMultiplyConstant(ModuleCompile MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal"); } static bool CheckMultiply(FunctionCompiler &f, ParseNode *star, MDefinition **def, Type *type) { MOZ_ASSERT(star->isKind(PNK_STAR)); - ParseNode *lhs = BinaryLeft(star); - ParseNode *rhs = BinaryRight(star); + ParseNode *lhs = MultiplyLeft(star); + ParseNode *rhs = MultiplyRight(star); MDefinition *lhsDef; Type lhsType; if (!CheckExpr(f, lhs, &lhsDef, &lhsType)) return false; MDefinition *rhsDef; Type rhsType; @@ -6325,18 +6427,18 @@ CheckMultiply(FunctionCompiler &f, Parse static bool CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type, unsigned *numAddOrSubOut = nullptr) { JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed()); MOZ_ASSERT(expr->isKind(PNK_ADD) || expr->isKind(PNK_SUB)); - ParseNode *lhs = BinaryLeft(expr); - ParseNode *rhs = BinaryRight(expr); + ParseNode *lhs = AddSubLeft(expr); + ParseNode *rhs = AddSubRight(expr); MDefinition *lhsDef, *rhsDef; Type lhsType, rhsType; unsigned lhsNumAddOrSub, rhsNumAddOrSub; if (lhs->isKind(PNK_ADD) || lhs->isKind(PNK_SUB)) { if (!CheckAddOrSub(f, lhs, &lhsDef, &lhsType, &lhsNumAddOrSub)) return false; @@ -6387,18 +6489,19 @@ CheckAddOrSub(FunctionCompiler &f, Parse *numAddOrSubOut = numAddOrSub; return true; } static bool CheckDivOrMod(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type) { MOZ_ASSERT(expr->isKind(PNK_DIV) || expr->isKind(PNK_MOD)); - ParseNode *lhs = BinaryLeft(expr); - ParseNode *rhs = BinaryRight(expr); + + ParseNode *lhs = DivOrModLeft(expr); + ParseNode *rhs = DivOrModRight(expr); MDefinition *lhsDef, *rhsDef; Type lhsType, rhsType; if (!CheckExpr(f, lhs, &lhsDef, &lhsType)) return false; if (!CheckExpr(f, rhs, &rhsDef, &rhsType)) return false; @@ -6441,18 +6544,19 @@ CheckDivOrMod(FunctionCompiler &f, Parse "%s and %s are given", lhsType.toChars(), rhsType.toChars()); } static bool CheckComparison(FunctionCompiler &f, ParseNode *comp, MDefinition **def, Type *type) { MOZ_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) || comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE)); - ParseNode *lhs = BinaryLeft(comp); - ParseNode *rhs = BinaryRight(comp); + + ParseNode *lhs = ComparisonLeft(comp); + ParseNode *rhs = ComparisonRight(comp); MDefinition *lhsDef, *rhsDef; Type lhsType, rhsType; if (!CheckExpr(f, lhs, &lhsDef, &lhsType)) return false; if (!CheckExpr(f, rhs, &rhsDef, &rhsType)) return false; @@ -6479,18 +6583,18 @@ CheckComparison(FunctionCompiler &f, Par return f.failf(comp, "arguments to a comparison must both be signed, unsigned, floats or doubles; " "%s and %s are given", lhsType.toChars(), rhsType.toChars()); } static bool CheckBitwise(FunctionCompiler &f, ParseNode *bitwise, MDefinition **def, Type *type) { - ParseNode *lhs = BinaryLeft(bitwise); - ParseNode *rhs = BinaryRight(bitwise); + ParseNode *lhs = BitwiseLeft(bitwise); + ParseNode *rhs = BitwiseRight(bitwise); int32_t identityElement; bool onlyOnRight; switch (bitwise->getKind()) { case PNK_BITOR: identityElement = 0; onlyOnRight = false; *type = Type::Signed; break; case PNK_BITAND: identityElement = -1; onlyOnRight = false; *type = Type::Signed; break; case PNK_BITXOR: identityElement = 0; onlyOnRight = false; *type = Type::Signed; break; case PNK_LSH: identityElement = 0; onlyOnRight = true; *type = Type::Signed; break; @@ -7203,58 +7307,58 @@ CheckByteLengthCall(ModuleCompiler &m, P return true; } static bool CheckHeapLengthCondition(ModuleCompiler &m, ParseNode *cond, PropertyName *newBufferName, uint32_t *mask, uint32_t *minLength, uint32_t *maxLength) { - if (!cond->isKind(PNK_OR) || !BinaryLeft(cond)->isKind(PNK_OR)) + if (!cond->isKind(PNK_OR) || !AndOrLeft(cond)->isKind(PNK_OR)) return m.fail(cond, "expecting byteLength & K || byteLength <= L || byteLength > M"); - ParseNode *cond1 = BinaryLeft(BinaryLeft(cond)); - ParseNode *cond2 = BinaryRight(BinaryLeft(cond)); - ParseNode *cond3 = BinaryRight(cond); + ParseNode *cond1 = AndOrLeft(AndOrLeft(cond)); + ParseNode *cond2 = AndOrRight(AndOrLeft(cond)); + ParseNode *cond3 = AndOrRight(cond); if (!cond1->isKind(PNK_BITAND)) return m.fail(cond1, "expecting byteLength & K"); - if (!CheckByteLengthCall(m, BinaryLeft(cond1), newBufferName)) - return false; - - ParseNode *maskNode = BinaryRight(cond1); + if (!CheckByteLengthCall(m, BitwiseLeft(cond1), newBufferName)) + return false; + + ParseNode *maskNode = BitwiseRight(cond1); if (!IsLiteralInt(m, maskNode, mask)) return m.fail(maskNode, "expecting integer literal mask"); if ((*mask & 0xffffff) != 0xffffff) return m.fail(maskNode, "mask value must have the bits 0xffffff set"); if (!cond2->isKind(PNK_LE)) return m.fail(cond2, "expecting byteLength <= L"); - if (!CheckByteLengthCall(m, BinaryLeft(cond2), newBufferName)) - return false; - - ParseNode *minLengthNode = BinaryRight(cond2); + if (!CheckByteLengthCall(m, RelationalLeft(cond2), newBufferName)) + return false; + + ParseNode *minLengthNode = RelationalRight(cond2); uint32_t minLengthExclusive; if (!IsLiteralInt(m, minLengthNode, &minLengthExclusive)) return m.fail(minLengthNode, "expecting integer literal"); if (minLengthExclusive < 0xffffff) return m.fail(minLengthNode, "literal must be >= 0xffffff"); // Add one to convert from exclusive (the branch rejects if ==) to inclusive. *minLength = minLengthExclusive + 1; if (!cond3->isKind(PNK_GT)) return m.fail(cond3, "expecting byteLength > M"); - if (!CheckByteLengthCall(m, BinaryLeft(cond3), newBufferName)) - return false; - - ParseNode *maxLengthNode = BinaryRight(cond3); + if (!CheckByteLengthCall(m, RelationalLeft(cond3), newBufferName)) + return false; + + ParseNode *maxLengthNode = RelationalRight(cond3); if (!IsLiteralInt(m, maxLengthNode, maxLength)) return m.fail(maxLengthNode, "expecting integer literal"); if (*maxLength > 0x80000000) return m.fail(maxLengthNode, "literal must be <= 0x80000000"); if (*maxLength < *minLength) return m.fail(maxLengthNode, "maximum length must be greater or equal to minimum length");
--- a/js/src/builtin/SIMD.h +++ b/js/src/builtin/SIMD.h @@ -185,41 +185,47 @@ INT32X4_SHUFFLE_FUNCTION_LIST(V) #define FOREACH_INT32X4_SIMD_OP(_) \ _(fromFloat32x4) \ _(fromFloat32x4Bits) \ _(shiftLeftByScalar) \ _(shiftRightArithmeticByScalar) \ _(shiftRightLogicalByScalar) +#define ARITH_FLOAT32X4_SIMD_OP(_) \ + _(div) \ + _(max) \ + _(min) \ + _(maxNum) \ + _(minNum) #define FOREACH_FLOAT32X4_SIMD_OP(_) \ + ARITH_FLOAT32X4_SIMD_OP(_) \ _(abs) \ _(sqrt) \ _(reciprocal) \ _(reciprocalSqrt) \ _(fromInt32x4) \ - _(fromInt32x4Bits) \ - _(div) \ - _(max) \ - _(min) \ - _(maxNum) \ - _(minNum) -#define FOREACH_COMMONX4_SIMD_OP(_) \ + _(fromInt32x4Bits) +#define ARITH_COMMONX4_SIMD_OP(_) \ _(add) \ _(sub) \ - _(mul) \ + _(mul) +#define BITWISE_COMMONX4_SIMD_OP(_) \ + _(and) \ + _(or) \ + _(xor) +#define FOREACH_COMMONX4_SIMD_OP(_) \ + ARITH_COMMONX4_SIMD_OP(_) \ + BITWISE_COMMONX4_SIMD_OP(_) \ _(lessThan) \ _(lessThanOrEqual) \ _(equal) \ _(notEqual) \ _(greaterThan) \ _(greaterThanOrEqual) \ - _(and) \ - _(or) \ - _(xor) \ _(bitselect) \ _(select) \ _(swizzle) \ _(shuffle) \ _(splat) \ _(withX) \ _(withY) \ _(withZ) \
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2034,25 +2034,20 @@ CheckSideEffects(ExclusiveContext *cx, B if (!CheckSideEffects(cx, bce, pn->pn_right, answer)) return false; if (!*answer && (!pn->isOp(JSOP_NOP) || !pn2->isConst())) *answer = true; } return true; } - if (pn->isOp(JSOP_OR) || pn->isOp(JSOP_AND) || pn->isOp(JSOP_STRICTEQ) || - pn->isOp(JSOP_STRICTNE)) { - /* - * ||, &&, ===, and !== do not convert their operands via - * toString or valueOf method calls. - */ - return CheckSideEffects(cx, bce, pn->pn_left, answer) && - CheckSideEffects(cx, bce, pn->pn_right, answer); - } + MOZ_ASSERT(!pn->isOp(JSOP_OR), "|| produces a list now"); + MOZ_ASSERT(!pn->isOp(JSOP_AND), "&& produces a list now"); + MOZ_ASSERT(!pn->isOp(JSOP_STRICTEQ), "=== produces a list now"); + MOZ_ASSERT(!pn->isOp(JSOP_STRICTNE), "!== produces a list now"); /* * We can't easily prove that neither operand ever denotes an * object with a toString or valueOf method. */ *answer = true; return true; @@ -4570,17 +4565,20 @@ EmitTry(ExclusiveContext *cx, BytecodeEm // Emit jump over catch and/or finally. ptrdiff_t catchJump = -1; if (EmitBackPatchOp(cx, bce, &catchJump) < 0) return false; ptrdiff_t tryEnd = bce->offset(); // If this try has a catch block, emit it. - if (ParseNode *pn2 = pn->pn_kid2) { + ParseNode *catchList = pn->pn_kid2; + if (catchList) { + MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST)); + // The emitted code for a catch block looks like: // // [pushblockscope] only if any local aliased // exception // if there is a catchguard: // dup // setlocal 0; pop assign or possibly destructure exception // if there is a catchguard: @@ -4596,17 +4594,17 @@ EmitTry(ExclusiveContext *cx, BytecodeEm // [popblockscope] only if any local aliased // goto <end of catch blocks> non-local; finally applies // // If there's no catch block without a catchguard, the last <next catch // block> points to rethrow code. This code will [gosub] to the finally // code if appropriate, and is also used for the catch-all trynote for // capturing exceptions thrown from catch{} blocks. // - for (ParseNode *pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + for (ParseNode *pn3 = catchList->pn_head; pn3; pn3 = pn3->pn_next) { MOZ_ASSERT(bce->stackDepth == depth); // Emit the lexical scope and catch body. MOZ_ASSERT(pn3->isKind(PNK_LEXICALSCOPE)); if (!EmitTree(cx, bce, pn3)) return false; // gosub <finally>, if required. @@ -4671,17 +4669,17 @@ EmitTry(ExclusiveContext *cx, BytecodeEm return false; // Fix up the end-of-try/catch jumps to come here. if (!BackPatch(cx, bce, catchJump, bce->code().end(), JSOP_GOTO)) return false; // Add the try note last, to let post-order give us the right ordering // (first to last for a given nesting level, inner to outer by level). - if (pn->pn_kid2 && !bce->tryNoteList.append(JSTRY_CATCH, depth, tryStart, tryEnd)) + if (catchList && !bce->tryNoteList.append(JSTRY_CATCH, depth, tryStart, tryEnd)) return false; // If we've got a finally, mark try+catch region with additional // trynote to catch exceptions (re)thrown from a catch block or // for the try{}finally{} case. if (pn->pn_kid3 && !bce->tryNoteList.append(JSTRY_FINALLY, depth, tryStart, finallyStart)) return false; @@ -6323,46 +6321,28 @@ EmitCallOrNew(ExclusiveContext *cx, Byte return false; } return true; } static bool EmitLogical(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) { + MOZ_ASSERT(pn->isArity(PN_LIST)); + /* * JSOP_OR converts the operand on the stack to boolean, leaves the original * value on the stack and jumps if true; otherwise it falls into the next * bytecode, which pops the left operand and then evaluates the right operand. * The jump goes around the right operand evaluation. * * JSOP_AND converts the operand on the stack to boolean and jumps if false; * otherwise it falls into the right operand's bytecode. */ - if (pn->isArity(PN_BINARY)) { - if (!EmitTree(cx, bce, pn->pn_left)) - return false; - ptrdiff_t top = EmitJump(cx, bce, JSOP_BACKPATCH, 0); - if (top < 0) - return false; - if (Emit1(cx, bce, JSOP_POP) < 0) - return false; - if (!EmitTree(cx, bce, pn->pn_right)) - return false; - ptrdiff_t off = bce->offset(); - jsbytecode *pc = bce->code(top); - SET_JUMP_OFFSET(pc, off - top); - *pc = pn->getOp(); - return true; - } - - MOZ_ASSERT(pn->isArity(PN_LIST)); - MOZ_ASSERT(pn->pn_head->pn_next->pn_next); - /* Left-associative operator chain: avoid too much recursion. */ ParseNode *pn2 = pn->pn_head; if (!EmitTree(cx, bce, pn2)) return false; ptrdiff_t top = EmitJump(cx, bce, JSOP_BACKPATCH, 0); if (top < 0) return false; if (Emit1(cx, bce, JSOP_POP) < 0) @@ -7130,39 +7110,31 @@ frontend::EmitTree(ExclusiveContext *cx, case PNK_GE: case PNK_IN: case PNK_INSTANCEOF: case PNK_LSH: case PNK_RSH: case PNK_URSH: case PNK_STAR: case PNK_DIV: - case PNK_MOD: - if (pn->isArity(PN_LIST)) { - /* Left-associative operator chain: avoid too much recursion. */ - ParseNode *pn2 = pn->pn_head; - if (!EmitTree(cx, bce, pn2)) - return false; - JSOp op = pn->getOp(); - while ((pn2 = pn2->pn_next) != nullptr) { - if (!EmitTree(cx, bce, pn2)) - return false; - if (Emit1(cx, bce, op) < 0) - return false; - } - } else { - /* Binary operators that evaluate both operands unconditionally. */ - if (!EmitTree(cx, bce, pn->pn_left)) - return false; - if (!EmitTree(cx, bce, pn->pn_right)) - return false; - if (Emit1(cx, bce, pn->getOp()) < 0) + case PNK_MOD: { + MOZ_ASSERT(pn->isArity(PN_LIST)); + /* Left-associative operator chain: avoid too much recursion. */ + ParseNode *subexpr = pn->pn_head; + if (!EmitTree(cx, bce, subexpr)) + return false; + JSOp op = pn->getOp(); + while ((subexpr = subexpr->pn_next) != nullptr) { + if (!EmitTree(cx, bce, subexpr)) + return false; + if (Emit1(cx, bce, op) < 0) return false; } break; + } case PNK_THROW: case PNK_TYPEOF: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: case PNK_POS: case PNK_NEG: @@ -7194,22 +7166,24 @@ frontend::EmitTree(ExclusiveContext *cx, case PNK_GENEXP: ok = EmitCallOrNew(cx, bce, pn); break; case PNK_LEXICALSCOPE: ok = EmitLexicalScope(cx, bce, pn); break; - case PNK_LET: + case PNK_LETBLOCK: + case PNK_LETEXPR: + ok = EmitLet(cx, bce, pn); + break; + case PNK_CONST: - MOZ_ASSERT_IF(pn->isKind(PNK_CONST), !pn->isArity(PN_BINARY)); - ok = pn->isArity(PN_BINARY) - ? EmitLet(cx, bce, pn) - : EmitVariables(cx, bce, pn, InitializeVars); + case PNK_LET: + ok = EmitVariables(cx, bce, pn, InitializeVars); break; case PNK_IMPORT: case PNK_EXPORT: case PNK_EXPORT_FROM: // TODO: Implement emitter support for modules bce->reportError(nullptr, JSMSG_MODULES_NOT_IMPLEMENTED); return false;
--- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -24,77 +24,403 @@ using mozilla::IsNaN; using mozilla::IsNegative; using mozilla::NegativeInfinity; using mozilla::PositiveInfinity; using JS::GenericNaN; using JS::ToInt32; using JS::ToUint32; static bool -ContainsVarOrConst(ExclusiveContext *cx, ParseNode *pn, ParseNode **resultp) +ContainsHoistedDeclaration(ExclusiveContext *cx, ParseNode *node, bool *result); + +static bool +ListContainsHoistedDeclaration(ExclusiveContext *cx, ListNode *list, bool *result) +{ + for (ParseNode *node = list->pn_head; node; node = node->pn_next) { + if (!ContainsHoistedDeclaration(cx, node, result)) + return false; + if (*result) + return true; + } + + *result = false; + return true; +} + +// Determines whether the given ParseNode contains any declarations whose +// visibility will extend outside the node itself -- that is, whether the +// ParseNode contains any var statements. +// +// THIS IS NOT A GENERAL-PURPOSE FUNCTION. It is only written to work in the +// specific context of deciding that |node|, as one arm of a PNK_IF controlled +// by a constant condition, contains a declaration that forbids |node| being +// completely eliminated as dead. +static bool +ContainsHoistedDeclaration(ExclusiveContext *cx, ParseNode *node, bool *result) { JS_CHECK_RECURSION(cx, return false); - if (!pn) { - *resultp = nullptr; + // With a better-typed AST, we would have distinct parse node classes for + // expressions and for statements and would characterize expressions with + // ExpressionKind and statements with StatementKind. Perhaps someday. In + // the meantime we must characterize every ParseNodeKind, even the + // expression/sub-expression ones that, if we handle all statement kinds + // correctly, we'll never see. + switch (node->getKind()) { + // Base case. + case PNK_VAR: + *result = true; + return true; + + // Non-global lexical declarations are block-scoped (ergo not hoistable). + // (Global lexical declarations, in addition to being irrelevant here as + // ContainsHoistedDeclaration is only used on the arms of an |if| + // statement, are handled by PNK_GLOBALCONST and PNK_VAR.) + case PNK_LET: + case PNK_CONST: + MOZ_ASSERT(node->isArity(PN_LIST)); + *result = false; + return true; + + // ContainsHoistedDeclaration is only called on nested nodes, so any + // instance of this can't be function statements at body level. In + // SpiderMonkey, a binding induced by a function statement is added when + // the function statement is evaluated. Thus any declaration introduced + // by a function statement, as observed by this function, isn't a hoisted + // declaration. + case PNK_FUNCTION: + MOZ_ASSERT(node->isArity(PN_CODE)); + *result = false; return true; - } - if (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)) { - *resultp = pn; + + // Statements with no sub-components at all. + case PNK_NOP: // induced by function f() {} function f() {} + case PNK_DEBUGGER: + MOZ_ASSERT(node->isArity(PN_NULLARY)); + *result = false; + return true; + + // Statements containing only an expression have no declarations. + case PNK_SEMI: + case PNK_THROW: + MOZ_ASSERT(node->isArity(PN_UNARY)); + *result = false; + return true; + + case PNK_RETURN: + // These two aren't statements in the spec, but we sometimes insert them + // in statement lists anyway. + case PNK_YIELD_STAR: + case PNK_YIELD: + MOZ_ASSERT(node->isArity(PN_BINARY)); + *result = false; + return true; + + // Other statements with no sub-statement components. + case PNK_BREAK: + case PNK_CONTINUE: + case PNK_IMPORT: + case PNK_IMPORT_SPEC_LIST: + case PNK_IMPORT_SPEC: + case PNK_EXPORT_FROM: + case PNK_EXPORT_SPEC_LIST: + case PNK_EXPORT_SPEC: + case PNK_EXPORT: + case PNK_EXPORT_BATCH_SPEC: + *result = false; return true; - } - switch (pn->getArity()) { - case PN_LIST: - for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (!ContainsVarOrConst(cx, pn2, resultp)) - return false; - if (*resultp) - return true; - } - break; + + // Statements possibly containing hoistable declarations only in the left + // half, in ParseNode terms -- the loop body in AST terms. + case PNK_DOWHILE: + return ContainsHoistedDeclaration(cx, node->pn_left, result); + + // Statements possibly containing hoistable declarations only in the + // right half, in ParseNode terms -- the loop body or nested statement + // (usually a block statement), in AST terms. + case PNK_WHILE: + case PNK_WITH: + return ContainsHoistedDeclaration(cx, node->pn_right, result); + + case PNK_LABEL: + return ContainsHoistedDeclaration(cx, node->pn_expr, result); + + // Statements with more complicated structures. + + // if-statement nodes may have hoisted declarations in their consequent + // and alternative components. + case PNK_IF: { + MOZ_ASSERT(node->isArity(PN_TERNARY)); + + ParseNode *consequent = node->pn_kid2; + if (!ContainsHoistedDeclaration(cx, consequent, result)) + return false; + if (*result) + return true; + + if (ParseNode *alternative = node->pn_kid3) + return ContainsHoistedDeclaration(cx, alternative, result); + + *result = false; + return true; + } + + // Legacy array and generator comprehensions use PNK_IF to represent + // conditions specified in the comprehension tail: for example, + // [x for (x in obj) if (x)]. The consequent of such PNK_IF nodes is + // either PNK_YIELD in a PNK_SEMI statement (generator comprehensions) or + // PNK_ARRAYPUSH (array comprehensions) . The first case is consistent + // with normal if-statement structure with consequent/alternative as + // statements. The second case is abnormal and requires that we not + // banish PNK_ARRAYPUSH to the unreachable list, handling it explicitly. + // + // We could require that this one weird PNK_ARRAYPUSH case be packaged in + // a PNK_SEMI, for consistency. That requires careful bytecode emitter + // adjustment that seems unwarranted for a deprecated feature. + case PNK_ARRAYPUSH: + *result = false; + return true; - case PN_TERNARY: - if (!ContainsVarOrConst(cx, pn->pn_kid1, resultp)) + // try-statements have statements to execute, and one or both of a + // catch-list and a finally-block. + case PNK_TRY: { + MOZ_ASSERT(node->isArity(PN_TERNARY)); + MOZ_ASSERT(node->pn_kid2 || node->pn_kid3, + "must have either catch(es) or finally"); + + ParseNode *tryBlock = node->pn_kid1; + if (!ContainsHoistedDeclaration(cx, tryBlock, result)) return false; - if (*resultp) + if (*result) return true; - if (!ContainsVarOrConst(cx, pn->pn_kid2, resultp)) - return false; - if (*resultp) - return true; - return ContainsVarOrConst(cx, pn->pn_kid3, resultp); + + if (ParseNode *catchList = node->pn_kid2) { + for (ParseNode *lexicalScope = catchList->pn_head; + lexicalScope; + lexicalScope = lexicalScope->pn_next) + { + MOZ_ASSERT(lexicalScope->isKind(PNK_LEXICALSCOPE)); + + ParseNode *catchNode = lexicalScope->pn_expr; + MOZ_ASSERT(catchNode->isKind(PNK_CATCH)); + + ParseNode *catchStatements = catchNode->pn_kid3; + if (!ContainsHoistedDeclaration(cx, catchStatements, result)) + return false; + if (*result) + return true; + } + } + + if (ParseNode *finallyBlock = node->pn_kid3) + return ContainsHoistedDeclaration(cx, finallyBlock, result); + + *result = false; + return true; + } + + // A switch node's left half is an expression; only its right half (a + // list of cases/defaults, or a block node) could contain hoisted + // declarations. + case PNK_SWITCH: + MOZ_ASSERT(node->isArity(PN_BINARY)); + return ContainsHoistedDeclaration(cx, node->pn_right, result); + + // A case/default node's right half is its statements. A default node's + // left half is null; a case node's left half is its expression. + case PNK_DEFAULT: + MOZ_ASSERT(!node->pn_left); + case PNK_CASE: + MOZ_ASSERT(node->isArity(PN_BINARY)); + return ContainsHoistedDeclaration(cx, node->pn_right, result); - case PN_BINARY: - case PN_BINARY_OBJ: - // Limit recursion if pn is a binary expression, which can't contain a - // var statement. - if (!pn->isOp(JSOP_NOP)) { - *resultp = nullptr; - return true; + // PNK_SEQ has two purposes. + // + // The first is to prepend destructuring operations to the body of a + // deprecated function expression closure: irrelevant here, as this + // function doesn't recur into PNK_FUNCTION, and this method's sole + // caller acts upon statements nested in if-statements not found in + // destructuring operations. + // + // The second is to provide a place for a hoisted declaration to go, in + // the bizarre for-in/of loops that have as target a declaration with an + // assignment, e.g. |for (var i = 0 in expr)|. This case is sadly still + // relevant, so we can't banish this ParseNodeKind to the unreachable + // list and must check every list member. + case PNK_SEQ: + return ListContainsHoistedDeclaration(cx, &node->as<ListNode>(), result); + + case PNK_FOR: { + MOZ_ASSERT(node->isArity(PN_BINARY)); + + ParseNode *loopHead = node->pn_left; + MOZ_ASSERT(loopHead->isKind(PNK_FORHEAD) || + loopHead->isKind(PNK_FORIN) || + loopHead->isKind(PNK_FOROF)); + + if (loopHead->isKind(PNK_FORHEAD)) { + // for (init?; cond?; update?), with only init possibly containing + // a hoisted declaration. (Note: a lexical-declaration |init| is + // (at present) hoisted in SpiderMonkey parlance -- but such + // hoisting doesn't extend outside of this statement, so it is not + // hoisting in the sense meant by ContainsHoistedDeclaration.) + MOZ_ASSERT(loopHead->isArity(PN_TERNARY)); + + ParseNode *init = loopHead->pn_kid1; + if (init && init->isKind(PNK_VAR)) { + *result = true; + return true; + } + } else { + MOZ_ASSERT(loopHead->isKind(PNK_FORIN) || loopHead->isKind(PNK_FOROF)); + + // for each? (target in ...), where only target may introduce + // hoisted declarations. + // + // -- or -- + // + // for (target of ...), where only target may introduce hoisted + // declarations. + // + // Either way, if |target| contains a declaration, it's either + // |loopHead|'s first kid, *or* that declaration was hoisted to + // become a child of an ancestral PNK_SEQ node. The former case we + // detect here. The latter case is handled by this method + // recurring on PNK_SEQ, above. + MOZ_ASSERT(loopHead->isArity(PN_TERNARY)); + + ParseNode *decl = loopHead->pn_kid1; + if (decl && decl->isKind(PNK_VAR)) { + *result = true; + return true; + } } - if (!ContainsVarOrConst(cx, pn->pn_left, resultp)) - return false; - if (*resultp) - return true; - return ContainsVarOrConst(cx, pn->pn_right, resultp); + + ParseNode *loopBody = node->pn_right; + return ContainsHoistedDeclaration(cx, loopBody, result); + } + + case PNK_LETBLOCK: { + MOZ_ASSERT(node->isArity(PN_BINARY)); + MOZ_ASSERT(node->pn_left->isKind(PNK_LET)); + MOZ_ASSERT(node->pn_right->isKind(PNK_LEXICALSCOPE)); + return ContainsHoistedDeclaration(cx, node->pn_right, result); + } + + case PNK_LEXICALSCOPE: { + MOZ_ASSERT(node->isArity(PN_NAME)); + ParseNode *expr = node->pn_expr; + + if (expr->isKind(PNK_FOR)) + return ContainsHoistedDeclaration(cx, expr, result); + + MOZ_ASSERT(expr->isKind(PNK_STATEMENTLIST)); + return ListContainsHoistedDeclaration(cx, &node->pn_expr->as<ListNode>(), result); + } + + // List nodes with all non-null children. + case PNK_STATEMENTLIST: + return ListContainsHoistedDeclaration(cx, &node->as<ListNode>(), result); - case PN_UNARY: - if (!pn->isOp(JSOP_NOP)) { - *resultp = nullptr; - return true; - } - return ContainsVarOrConst(cx, pn->pn_kid, resultp); + // Grammar sub-components that should never be reached directly by this + // method, because some parent component should have asserted itself. + case PNK_COMPUTED_NAME: + case PNK_SPREAD: + case PNK_MUTATEPROTO: + case PNK_COLON: + case PNK_SHORTHAND: + case PNK_CONDITIONAL: + case PNK_TYPEOF: + case PNK_VOID: + case PNK_NOT: + case PNK_BITNOT: + case PNK_DELETE: + case PNK_POS: + case PNK_NEG: + case PNK_PREINCREMENT: + case PNK_POSTINCREMENT: + case PNK_PREDECREMENT: + case PNK_POSTDECREMENT: + case PNK_OR: + case PNK_AND: + case PNK_BITOR: + case PNK_BITXOR: + case PNK_BITAND: + case PNK_STRICTEQ: + case PNK_EQ: + case PNK_STRICTNE: + case PNK_NE: + case PNK_LT: + case PNK_LE: + case PNK_GT: + case PNK_GE: + case PNK_INSTANCEOF: + case PNK_IN: + case PNK_LSH: + case PNK_RSH: + case PNK_URSH: + case PNK_ADD: + case PNK_SUB: + case PNK_STAR: + case PNK_DIV: + case PNK_MOD: + case PNK_ASSIGN: + case PNK_ADDASSIGN: + case PNK_SUBASSIGN: + case PNK_BITORASSIGN: + case PNK_BITXORASSIGN: + case PNK_BITANDASSIGN: + case PNK_LSHASSIGN: + case PNK_RSHASSIGN: + case PNK_URSHASSIGN: + case PNK_MULASSIGN: + case PNK_DIVASSIGN: + case PNK_MODASSIGN: + case PNK_COMMA: + case PNK_ARRAY: + case PNK_OBJECT: + case PNK_DOT: + case PNK_ELEM: + case PNK_CALL: + case PNK_NAME: + case PNK_TEMPLATE_STRING: + case PNK_TEMPLATE_STRING_LIST: + case PNK_TAGGED_TEMPLATE: + case PNK_CALLSITEOBJ: + case PNK_STRING: + case PNK_REGEXP: + case PNK_TRUE: + case PNK_FALSE: + case PNK_NULL: + case PNK_THIS: + case PNK_LETEXPR: + case PNK_ELISION: + case PNK_NUMBER: + case PNK_NEW: + case PNK_GENERATOR: + case PNK_GENEXP: + case PNK_ARRAYCOMP: + case PNK_ARGSBODY: + case PNK_CATCHLIST: + case PNK_CATCH: + case PNK_FORIN: + case PNK_FOROF: + case PNK_FORHEAD: + MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on " + "some parent node without recurring to test this node"); - case PN_NAME: - return ContainsVarOrConst(cx, pn->maybeExpr(), resultp); + case PNK_GLOBALCONST: + MOZ_CRASH("ContainsHoistedDeclaration is only called on nested nodes where " + "globalconst nodes should never have been generated"); - default:; + case PNK_LIMIT: // invalid sentinel value + MOZ_CRASH("unexpected PNK_LIMIT in node"); } - *resultp = nullptr; - return true; + + MOZ_CRASH("invalid node kind"); } /* * Fold from one constant type to another. * XXX handles only strings and numbers for now */ static bool FoldType(ExclusiveContext *cx, ParseNode *pn, ParseNodeKind kind) @@ -419,33 +745,37 @@ Fold(ExclusiveContext *cx, ParseNode **p case PN_NULLARY: break; } // The immediate child of a PNK_DELETE node should not be replaced // with node indicating a different syntactic form; |delete x| is not // the same as |delete (true && x)|. See bug 888002. // - // pn is the immediate child in question. Its descendents were already + // pn is the immediate child in question. Its descendants were already // constant-folded above, so we're done. if (sc == SyntacticContext::Delete) return true; switch (pn->getKind()) { case PNK_IF: { - ParseNode *decl; - if (!ContainsVarOrConst(cx, pn2, &decl)) - return false; - if (decl) - break; - if (!ContainsVarOrConst(cx, pn3, &decl)) - return false; - if (decl) - break; + bool result; + if (ParseNode *consequent = pn2) { + if (!ContainsHoistedDeclaration(cx, consequent, &result)) + return false; + if (result) + break; + } + if (ParseNode *alternative = pn3) { + if (!ContainsHoistedDeclaration(cx, alternative, &result)) + return false; + if (result) + break; + } } /* FALL THROUGH */ case PNK_CONDITIONAL: /* Reduce 'if (C) T; else E' into T for true C, E for false. */ switch (pn1->getKind()) { case PNK_NUMBER: if (pn1->pn_dval == 0 || IsNaN(pn1->pn_dval)) @@ -490,102 +820,57 @@ Fold(ExclusiveContext *cx, ParseNode **p } if (pn3 && pn3 != pn2) handler.freeTree(pn3); break; case PNK_OR: case PNK_AND: if (sc == SyntacticContext::Condition) { - if (pn->isArity(PN_LIST)) { - ParseNode **listp = &pn->pn_head; - MOZ_ASSERT(*listp == pn1); - uint32_t orig = pn->pn_count; - do { - Truthiness t = Boolish(pn1); - if (t == Unknown) { - listp = &pn1->pn_next; - continue; + ParseNode **listp = &pn->pn_head; + MOZ_ASSERT(*listp == pn1); + uint32_t orig = pn->pn_count; + do { + Truthiness t = Boolish(pn1); + if (t == Unknown) { + listp = &pn1->pn_next; + continue; + } + if ((t == Truthy) == pn->isKind(PNK_OR)) { + for (pn2 = pn1->pn_next; pn2; pn2 = pn3) { + pn3 = pn2->pn_next; + handler.freeTree(pn2); + --pn->pn_count; } - if ((t == Truthy) == pn->isKind(PNK_OR)) { - for (pn2 = pn1->pn_next; pn2; pn2 = pn3) { - pn3 = pn2->pn_next; - handler.freeTree(pn2); - --pn->pn_count; - } - pn1->pn_next = nullptr; - break; - } - MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND)); - if (pn->pn_count == 1) - break; - *listp = pn1->pn_next; - handler.freeTree(pn1); - --pn->pn_count; - } while ((pn1 = *listp) != nullptr); + pn1->pn_next = nullptr; + break; + } + MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND)); + if (pn->pn_count == 1) + break; + *listp = pn1->pn_next; + handler.freeTree(pn1); + --pn->pn_count; + } while ((pn1 = *listp) != nullptr); - // We may have to change arity from LIST to BINARY. - pn1 = pn->pn_head; - if (pn->pn_count == 2) { - pn2 = pn1->pn_next; - pn1->pn_next = nullptr; - MOZ_ASSERT(!pn2->pn_next); - pn->setArity(PN_BINARY); - pn->pn_left = pn1; - pn->pn_right = pn2; - } else if (pn->pn_count == 1) { - ReplaceNode(pnp, pn1); - pn = pn1; - } else if (orig != pn->pn_count) { - // Adjust list tail. - pn2 = pn1->pn_next; - for (; pn1; pn2 = pn1, pn1 = pn1->pn_next) - ; - pn->pn_tail = &pn2->pn_next; - } - } else { - Truthiness t = Boolish(pn1); - if (t != Unknown) { - if ((t == Truthy) == pn->isKind(PNK_OR)) { - handler.freeTree(pn2); - ReplaceNode(pnp, pn1); - pn = pn1; - } else { - MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND)); - handler.freeTree(pn1); - ReplaceNode(pnp, pn2); - pn = pn2; - } - } + // We may have to replace a one-element list with its element. + pn1 = pn->pn_head; + if (pn->pn_count == 1) { + ReplaceNode(pnp, pn1); + pn = pn1; + } else if (orig != pn->pn_count) { + // Adjust list tail. + pn2 = pn1->pn_next; + for (; pn1; pn2 = pn1, pn1 = pn1->pn_next) + continue; + pn->pn_tail = &pn2->pn_next; } } break; - case PNK_SUBASSIGN: - case PNK_BITORASSIGN: - case PNK_BITXORASSIGN: - case PNK_BITANDASSIGN: - case PNK_LSHASSIGN: - case PNK_RSHASSIGN: - case PNK_URSHASSIGN: - case PNK_MULASSIGN: - case PNK_DIVASSIGN: - case PNK_MODASSIGN: - /* - * Compound operators such as *= should be subject to folding, in case - * the left-hand side is constant, and so that the decompiler produces - * the same string that you get from decompiling a script or function - * compiled from that same string. += is special and so must be - * handled below. - */ - goto do_binary_op; - - case PNK_ADDASSIGN: - MOZ_ASSERT(pn->isOp(JSOP_ADD)); - /* FALL THROUGH */ case PNK_ADD: if (pn->isArity(PN_LIST)) { bool folded = false; pn2 = pn1->pn_next; if (pn1->isKind(PNK_NUMBER)) { // Fold addition of numeric literals: (1 + 2 + x === 3 + x). // Note that we can only do this the front of the list: @@ -694,58 +979,52 @@ Fold(ExclusiveContext *cx, ParseNode **p pn->setOp(JSOP_STRING); pn->setArity(PN_NULLARY); handler.freeTree(pn1); handler.freeTree(pn2); break; } /* Can't concatenate string literals, let's try numbers. */ - goto do_binary_op; + if (!FoldType(cx, pn1, PNK_NUMBER) || !FoldType(cx, pn2, PNK_NUMBER)) + return false; + if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) { + if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn)) + return false; + } + break; case PNK_SUB: case PNK_STAR: case PNK_LSH: case PNK_RSH: case PNK_URSH: case PNK_DIV: case PNK_MOD: - do_binary_op: - if (pn->isArity(PN_LIST)) { - MOZ_ASSERT(pn->pn_count > 2); - for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { - if (!FoldType(cx, pn2, PNK_NUMBER)) - return false; - } - for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { - /* XXX fold only if all operands convert to number */ - if (!pn2->isKind(PNK_NUMBER)) - break; - } - if (!pn2) { - JSOp op = pn->getOp(); + MOZ_ASSERT(pn->getArity() == PN_LIST); + MOZ_ASSERT(pn->pn_count >= 2); + for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { + if (!FoldType(cx, pn2, PNK_NUMBER)) + return false; + } + for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { + /* XXX fold only if all operands convert to number */ + if (!pn2->isKind(PNK_NUMBER)) + break; + } + if (!pn2) { + JSOp op = pn->getOp(); - pn2 = pn1->pn_next; + pn2 = pn1->pn_next; + pn3 = pn2->pn_next; + if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn)) + return false; + while ((pn2 = pn3) != nullptr) { pn3 = pn2->pn_next; - if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn)) - return false; - while ((pn2 = pn3) != nullptr) { - pn3 = pn2->pn_next; - if (!FoldBinaryNumeric(cx, op, pn, pn2, pn)) - return false; - } - } - } else { - MOZ_ASSERT(pn->isArity(PN_BINARY)); - if (!FoldType(cx, pn1, PNK_NUMBER) || - !FoldType(cx, pn2, PNK_NUMBER)) { - return false; - } - if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) { - if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn)) + if (!FoldBinaryNumeric(cx, op, pn, pn2, pn)) return false; } } break; case PNK_TYPEOF: case PNK_VOID: case PNK_NOT:
--- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -22,17 +22,16 @@ class Parser; class SyntaxParseHandler; // Parse handler used when generating a full parse tree for all code which the // parser encounters. class FullParseHandler { ParseNodeAllocator allocator; TokenStream &tokenStream; - bool foldConstants; ParseNode *allocParseNode(size_t size) { MOZ_ASSERT(size == sizeof(ParseNode)); return static_cast<ParseNode *>(allocator.allocNode()); } ParseNode *cloneNode(const ParseNode &other) { ParseNode *node = allocParseNode(sizeof(ParseNode)); @@ -66,21 +65,20 @@ class FullParseHandler /* new_ methods for creating parse nodes. These report OOM on context. */ JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline) typedef ParseNode *Node; typedef Definition *DefinitionNode; FullParseHandler(ExclusiveContext *cx, LifoAlloc &alloc, - TokenStream &tokenStream, bool foldConstants, - Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction) + TokenStream &tokenStream, Parser<SyntaxParseHandler> *syntaxParser, + LazyScript *lazyOuterFunction) : allocator(cx, alloc), tokenStream(tokenStream), - foldConstants(foldConstants), lazyOuterFunction_(lazyOuterFunction), lazyInnerFunctionIndex(0), syntaxParser(syntaxParser) {} static ParseNode *null() { return nullptr; } ParseNode *freeTree(ParseNode *pn) { return allocator.freeTree(pn); } @@ -212,20 +210,20 @@ class FullParseHandler JSOp op = JSOP_NOP) { return new_<BinaryNode>(kind, op, left->pn_pos, left, (ParseNode *) nullptr); } ParseNode *newBinary(ParseNodeKind kind, ParseNode *left, ParseNode *right, JSOp op = JSOP_NOP) { TokenPos pos(left->pn_pos.begin, right->pn_pos.end); return new_<BinaryNode>(kind, op, pos, left, right); } - ParseNode *newBinaryOrAppend(ParseNodeKind kind, ParseNode *left, ParseNode *right, - ParseContext<FullParseHandler> *pc, JSOp op = JSOP_NOP) + ParseNode *appendOrCreateList(ParseNodeKind kind, ParseNode *left, ParseNode *right, + ParseContext<FullParseHandler> *pc, JSOp op = JSOP_NOP) { - return ParseNode::newBinaryOrAppend(kind, op, left, right, this, pc, foldConstants); + return ParseNode::appendOrCreateList(kind, op, left, right, this, pc); } ParseNode *newTernary(ParseNodeKind kind, ParseNode *first, ParseNode *second, ParseNode *third, JSOp op = JSOP_NOP) { return new_<TernaryNode>(kind, op, first, second, third); } @@ -535,20 +533,36 @@ class FullParseHandler ParseNode *newLexicalScope(ObjectBox *blockBox) { return new_<LexicalScopeNode>(blockBox, pos()); } void setLexicalScopeBody(ParseNode *block, ParseNode *body) { block->pn_expr = body; } + ParseNode *newLetExpression(ParseNode *vars, ParseNode *block, const TokenPos &pos) { + ParseNode *letExpr = newBinary(PNK_LETEXPR, vars, block); + if (!letExpr) + return nullptr; + letExpr->pn_pos = pos; + return letExpr; + } + + ParseNode *newLetBlock(ParseNode *vars, ParseNode *block, const TokenPos &pos) { + ParseNode *letBlock = newBinary(PNK_LETBLOCK, vars, block); + if (!letBlock) + return nullptr; + letBlock->pn_pos = pos; + return letBlock; + } + ParseNode *newAssignment(ParseNodeKind kind, ParseNode *lhs, ParseNode *rhs, ParseContext<FullParseHandler> *pc, JSOp op) { - return newBinaryOrAppend(kind, lhs, rhs, pc, op); + return newBinary(kind, lhs, rhs, op); } bool isUnparenthesizedYieldExpression(ParseNode *node) { return node->isKind(PNK_YIELD) && !node->isInParens(); } bool isUnparenthesizedCommaExpression(ParseNode *node) { return node->isKind(PNK_COMMA) && !node->isInParens(); @@ -595,16 +609,19 @@ class FullParseHandler return new_<ListNode>(kind, op, pos()); } /* New list with one initial child node. kid must be non-null. */ ParseNode *newList(ParseNodeKind kind, ParseNode *kid, JSOp op = JSOP_NOP) { return new_<ListNode>(kind, op, kid); } + ParseNode *newCatchList() { + return new_<ListNode>(PNK_CATCHLIST, JSOP_NOP, pos()); + } ParseNode *newCommaExpressionList(ParseNode *kid) { return newList(PNK_COMMA, kid, JSOP_NOP); } void addList(ParseNode *list, ParseNode *kid) { list->append(kid); }
--- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -240,66 +240,48 @@ ParseNodeAllocator::allocNode() void *p = alloc.alloc(sizeof (ParseNode)); if (!p) js_ReportOutOfMemory(cx); return p; } ParseNode * -ParseNode::append(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right, - FullParseHandler *handler) +ParseNode::appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right, + FullParseHandler *handler, ParseContext<FullParseHandler> *pc) { - if (!left || !right) - return nullptr; - - MOZ_ASSERT(left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)); + // The asm.js specification is written in ECMAScript grammar terms that + // specify *only* a binary tree. It's a royal pain to implement the asm.js + // spec to act upon n-ary lists as created below. So for asm.js, form a + // binary tree of lists exactly as ECMAScript would by skipping the + // following optimization. + if (!pc->useAsmOrInsideUseAsm()) { + // Left-associative trees of a given operator (e.g. |a + b + c|) are + // binary trees in the spec: (+ (+ a b) c) in Lisp terms. Recursively + // processing such a tree, exactly implemented that way, would blow the + // the stack. We use a list node that uses O(1) stack to represent + // such operations: (+ a b c). + if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)) { + ListNode *list = &left->as<ListNode>(); - ListNode *list; - if (left->pn_arity == PN_LIST) { - list = &left->as<ListNode>(); - } else { - ParseNode *pn1 = left->pn_left, *pn2 = left->pn_right; - list = handler->new_<ListNode>(kind, op, pn1); - if (!list) - return nullptr; - list->append(pn2); + list->append(right); + list->pn_pos.end = right->pn_pos.end; + + return list; + } } + ParseNode *list = handler->new_<ListNode>(kind, op, left); + if (!list) + return nullptr; + list->append(right); - list->pn_pos.end = right->pn_pos.end; - return list; } -ParseNode * -ParseNode::newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right, - FullParseHandler *handler, ParseContext<FullParseHandler> *pc, - bool foldConstants) -{ - if (!left || !right) - return nullptr; - - /* - * Ensure that the parse tree is faithful to the source when "use asm" (for - * the purpose of type checking). - */ - if (pc->useAsmOrInsideUseAsm()) - return handler->new_<BinaryNode>(kind, op, left, right); - - /* - * Flatten a left-associative (left-heavy) tree of a given operator into - * a list to reduce js::FoldConstants and js::frontend::EmitTree recursion. - */ - if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)) - return append(kind, op, left, right, handler); - - return handler->new_<BinaryNode>(kind, op, left, right); -} - const char * Definition::kindString(Kind kind) { static const char * const table[] = { "", js_var_str, js_const_str, js_const_str, js_let_str, "argument", js_function_str, "unknown" }; MOZ_ASSERT(unsigned(kind) <= unsigned(ARG));
--- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -98,17 +98,16 @@ class UpvarCookie F(CALLSITEOBJ) \ F(REGEXP) \ F(TRUE) \ F(FALSE) \ F(NULL) \ F(THIS) \ F(FUNCTION) \ F(IF) \ - F(ELSE) \ F(SWITCH) \ F(CASE) \ F(DEFAULT) \ F(WHILE) \ F(DOWHILE) \ F(FOR) \ F(BREAK) \ F(CONTINUE) \ @@ -117,27 +116,28 @@ class UpvarCookie F(GLOBALCONST) \ F(WITH) \ F(RETURN) \ F(NEW) \ F(DELETE) \ F(TRY) \ F(CATCH) \ F(CATCHLIST) \ - F(FINALLY) \ F(THROW) \ F(DEBUGGER) \ F(GENERATOR) \ F(YIELD) \ F(YIELD_STAR) \ F(GENEXP) \ F(ARRAYCOMP) \ F(ARRAYPUSH) \ F(LEXICALSCOPE) \ F(LET) \ + F(LETBLOCK) \ + F(LETEXPR) \ F(IMPORT) \ F(IMPORT_SPEC_LIST) \ F(IMPORT_SPEC) \ F(EXPORT) \ F(EXPORT_FROM) \ F(EXPORT_SPEC_LIST) \ F(EXPORT_SPEC) \ F(EXPORT_BATCH_SPEC) \ @@ -282,20 +282,21 @@ enum ParseNodeKind * to left of 'of'; if pn_kid1, then this * is a clone of pn_kid1->pn_head * pn_kid3: expr to right of 'of' * PNK_FORHEAD ternary pn_kid1: init expr before first ';' or nullptr * pn_kid2: cond expr before second ';' or nullptr * pn_kid3: update expr after second ';' or nullptr * PNK_THROW unary pn_op: JSOP_THROW, pn_kid: exception * PNK_TRY ternary pn_kid1: try block - * pn_kid2: null or PNK_CATCHLIST list of - * PNK_LEXICALSCOPE nodes, each with pn_expr pointing - * to a PNK_CATCH node + * pn_kid2: null or PNK_CATCHLIST list * pn_kid3: null or finally block + * PNK_CATCHLIST list pn_head: list of PNK_LEXICALSCOPE nodes, one per + * catch-block, each with pn_expr pointing + * to a PNK_CATCH node * PNK_CATCH ternary pn_kid1: PNK_NAME, PNK_ARRAY, or PNK_OBJECT catch var node * (PNK_ARRAY or PNK_OBJECT if destructuring) * pn_kid2: null or the catch guard expression * pn_kid3: catch block statements * PNK_BREAK name pn_atom: label or null * PNK_CONTINUE name pn_atom: label or null * PNK_WITH binary-obj pn_left: head expr; pn_right: body; pn_binary_obj: StaticWithObject * PNK_VAR, list pn_head: list of PNK_NAME or PNK_ASSIGN nodes @@ -504,16 +505,21 @@ class ParseNode bool isArity(ParseNodeArity a) const { return getArity() == a; } void setArity(ParseNodeArity a) { pn_arity = a; } bool isAssignment() const { ParseNodeKind kind = getKind(); return PNK_ASSIGNMENT_START <= kind && kind <= PNK_ASSIGNMENT_LAST; } + bool isBinaryOperation() const { + ParseNodeKind kind = getKind(); + return PNK_BINOP_FIRST <= kind && kind <= PNK_BINOP_LAST; + } + /* Boolean attributes. */ bool isInParens() const { return pn_parens; } bool isLikelyIIFE() const { return isInParens(); } void setInParens(bool enabled) { pn_parens = enabled; } bool isUsed() const { return pn_used; } void setUsed(bool enabled) { pn_used = enabled; } bool isDefn() const { return pn_defn; } void setDefn(bool enabled) { pn_defn = enabled; } @@ -635,31 +641,22 @@ class ParseNode pn_parens = false; MOZ_ASSERT(!pn_used); MOZ_ASSERT(!pn_defn); pn_next = pn_link = nullptr; } public: /* - * Append right to left, forming a list node. |left| must have the given - * kind and op, and op must be left-associative. + * If |left| is a list of the given kind/left-associative op, append + * |right| to it and return |left|. Otherwise return a [left, right] list. */ static ParseNode * - append(ParseNodeKind tt, JSOp op, ParseNode *left, ParseNode *right, FullParseHandler *handler); - - /* - * Either append right to left, if left meets the conditions necessary to - * append (see append), or form a binary node whose children are right and - * left. - */ - static ParseNode * - newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right, - FullParseHandler *handler, ParseContext<FullParseHandler> *pc, - bool foldConstants); + appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right, + FullParseHandler *handler, ParseContext<FullParseHandler> *pc); inline PropertyName *name() const; inline JSAtom *atom() const; /* * The pn_expr and lexdef members are arms of an unsafe union. Unless you * know exactly what you're doing, use only the following methods to access * them. For less overhead and assertions for protection, use pn->expr()
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -511,17 +511,17 @@ Parser<ParseHandler>::Parser(ExclusiveCo abortedSyntaxParse(false), isUnexpectedEOF_(false), sawDeprecatedForEach(false), sawDeprecatedDestructuringForIn(false), sawDeprecatedLegacyGenerator(false), sawDeprecatedExpressionClosure(false), sawDeprecatedLetBlock(false), sawDeprecatedLetExpression(false), - handler(cx, *alloc, tokenStream, foldConstants, syntaxParser, lazyOuterFunction) + handler(cx, *alloc, tokenStream, syntaxParser, lazyOuterFunction) { { AutoLockForExclusiveAccess lock(cx); cx->perThreadData->addActiveCompilation(); } // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings // which are not generated if functions are parsed lazily. Note that the @@ -1225,30 +1225,20 @@ struct BindData this->op = op; this->isConst = op == JSOP_DEFCONST; this->binder = Parser<ParseHandler>::bindVarOrGlobalConst; } }; template <typename ParseHandler> JSFunction * -Parser<ParseHandler>::newFunction(GenericParseContext *pc, HandleAtom atom, - FunctionSyntaxKind kind, JSObject *proto) +Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind, JSObject *proto) { MOZ_ASSERT_IF(kind == Statement, atom != nullptr); - /* - * Find the global compilation context in order to pre-set the newborn - * function's parent slot to pc->sc->as<GlobalObject>()->scopeChain. If the - * global context is a compile-and-go one, we leave the pre-set parent - * intact; otherwise we clear parent and proto. - */ - while (pc->parent) - pc = pc->parent; - RootedFunction fun(context); JSFunction::Flags flags = (kind == Expression) ? JSFunction::INTERPRETED_LAMBDA : (kind == Arrow) ? JSFunction::INTERPRETED_LAMBDA_ARROW : JSFunction::INTERPRETED; gc::AllocKind allocKind = JSFunction::FinalizeKind; if (kind == Arrow) @@ -2184,17 +2174,17 @@ Parser<ParseHandler>::functionDef(Handle // If we are off the main thread, the generator meta-objects have // already been created by js::StartOffThreadParseScript, so cx will not // be necessary. JSContext *cx = context->maybeJSContext(); proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global()); if (!proto) return null(); } - RootedFunction fun(context, newFunction(pc, funName, kind, proto)); + RootedFunction fun(context, newFunction(funName, kind, proto)); if (!fun) return null(); // Speculatively parse using the directives of the parent parsing context. // If a directive is encountered (e.g., "use strict") that changes how the // function should have been parsed, we backup and reparse with the new set // of directives. Directives directives(pc); @@ -3627,17 +3617,17 @@ Parser<SyntaxParseHandler>::pushLetScope /* * Parse a let block statement or let expression (determined by 'letContext'). * In both cases, bindings are not hoisted to the top of the enclosing block * and thus must be carefully injected between variables() and the let body. */ template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::letBlock(LetContext letContext) +Parser<ParseHandler>::deprecatedLetBlockOrExpression(LetContext letContext) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LET)); RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context)); if (!blockObj) return null(); uint32_t begin = pos().begin; @@ -3650,21 +3640,16 @@ Parser<ParseHandler>::letBlock(LetContex MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET); StmtInfoPC stmtInfo(context); Node block = pushLetScope(blockObj, &stmtInfo); if (!block) return null(); - Node pnlet = handler.newBinary(PNK_LET, vars, block); - if (!pnlet) - return null(); - handler.setBeginPosition(pnlet, begin); - bool needExprStmt = false; if (letContext == LetStatement) { bool matched; if (!tokenStream.matchToken(&matched, TOK_LC, TokenStream::Operand)) return null(); if (!matched) { /* * Strict mode eliminates a grammar ambiguity with unparenthesized @@ -3677,26 +3662,26 @@ Parser<ParseHandler>::letBlock(LetContex * // Does this parse as * // (let (loc = "inner") id)(loc) // "outer" * // or as * // let (loc = "inner") (id(loc)) // "inner" * let (loc = "inner") id(loc); * * See bug 569464. */ - if (!report(ParseStrictError, pc->sc->strict, pnlet, - JSMSG_STRICT_CODE_LET_EXPR_STMT)) + if (!reportWithOffset(ParseStrictError, pc->sc->strict, begin, + JSMSG_STRICT_CODE_LET_EXPR_STMT)) { return null(); } /* * If this is really an expression in let statement guise, then we - * need to wrap the PNK_LET node in a PNK_SEMI node so that we pop - * the return value of the expression. + * need to wrap the PNK_LETEXPR node in a PNK_SEMI node so that we + * pop the return value of the expression. */ needExprStmt = true; letContext = LetExpression; } } Node expr; if (letContext == LetStatement) { @@ -3716,24 +3701,32 @@ Parser<ParseHandler>::letBlock(LetContex sawDeprecatedLetExpression = true; if (!report(ParseWarning, pc->sc->strict, expr, JSMSG_DEPRECATED_LET_EXPRESSION)) return null(); } handler.setLexicalScopeBody(block, expr); PopStatementPC(tokenStream, pc); - handler.setEndPosition(pnlet, pos().end); - - if (needExprStmt) { - if (!MatchOrInsertSemicolon(tokenStream)) - return null(); - return handler.newExprStatement(pnlet, pos().end); - } - return pnlet; + TokenPos letPos(begin, pos().end); + + if (letContext == LetExpression) { + if (needExprStmt) { + if (!MatchOrInsertSemicolon(tokenStream)) + return null(); + } + + Node letExpr = handler.newLetExpression(vars, block, letPos); + if (!letExpr) + return null(); + + return needExprStmt ? handler.newExprStatement(letExpr, pos().end) : letExpr; + } + + return handler.newLetBlock(vars, block, letPos); } template <typename ParseHandler> static bool PushBlocklikeStatement(TokenStream &ts, StmtInfoPC *stmt, StmtType type, ParseContext<ParseHandler> *pc) { PushStatementPC(pc, stmt, type); @@ -3883,17 +3876,17 @@ Parser<ParseHandler>::variables(ParseNod Node init = assignExpr(); if (!init) return null(); if (!bindBeforeInitializer && !checkDestructuring(&data, pn2)) return null(); - pn2 = handler.newBinaryOrAppend(PNK_ASSIGN, pn2, init, pc); + pn2 = handler.newBinary(PNK_ASSIGN, pn2, init); if (!pn2) return null(); handler.addList(pn, pn2); break; } if (tt != TOK_NAME) { if (tt == TOK_YIELD) { @@ -4102,37 +4095,54 @@ SyntaxParseHandler::Node Parser<SyntaxParseHandler>::lexicalDeclaration(bool) { JS_ALWAYS_FALSE(abortIfSyntaxParser()); return SyntaxParseHandler::NodeFailure; } template <> ParseNode * -Parser<FullParseHandler>::letStatement() +Parser<FullParseHandler>::letDeclarationOrBlock() { handler.disableSyntaxParser(); /* Check for a let statement or let expression. */ - ParseNode *pn; TokenKind tt; if (!tokenStream.peekToken(&tt)) return null(); if (tt == TOK_LP) { - pn = letBlock(LetStatement); - MOZ_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI)); - } else { - pn = lexicalDeclaration(/* isConst = */ false); - } - return pn; + ParseNode *node = deprecatedLetBlockOrExpression(LetStatement); + if (!node) + return nullptr; + + if (node->isKind(PNK_LETBLOCK)) { + MOZ_ASSERT(node->isArity(PN_BINARY)); + } else { + MOZ_ASSERT(node->isKind(PNK_SEMI)); + MOZ_ASSERT(node->pn_kid->isKind(PNK_LETEXPR)); + MOZ_ASSERT(node->pn_kid->isArity(PN_BINARY)); + } + + return node; + } + + ParseNode *decl = lexicalDeclaration(/* isConst = */ false); + if (!decl) + return nullptr; + + // let-declarations at global scope are currently treated as plain old var. + // See bug 589199. + MOZ_ASSERT(decl->isKind(PNK_LET) || decl->isKind(PNK_VAR)); + MOZ_ASSERT(decl->isArity(PN_LIST)); + return decl; } template <> SyntaxParseHandler::Node -Parser<SyntaxParseHandler>::letStatement() +Parser<SyntaxParseHandler>::letDeclarationOrBlock() { JS_ALWAYS_FALSE(abortIfSyntaxParser()); return SyntaxParseHandler::NodeFailure; } template<typename ParseHandler> typename ParseHandler::Node Parser<ParseHandler>::importDeclaration() @@ -4660,17 +4670,17 @@ Parser<FullParseHandler>::forStatement() pn1 = variables(PNK_VAR); } else if (tt == TOK_LET || tt == TOK_CONST) { handler.disableSyntaxParser(); bool constDecl = tt == TOK_CONST; tokenStream.consumeKnownToken(tt); if (!tokenStream.peekToken(&tt)) return null(); if (tt == TOK_LP) { - pn1 = letBlock(LetExpression); + pn1 = deprecatedLetBlockOrExpression(LetExpression); } else { isForDecl = true; blockObj = StaticBlockObject::create(context); if (!blockObj) return null(); pn1 = variables(constDecl ? PNK_CONST : PNK_LET, nullptr, blockObj, DontHoistVars); } @@ -4933,21 +4943,17 @@ Parser<FullParseHandler>::forStatement() return null(); pnseq->pn_pos = forLoop->pn_pos; pnseq->append(forLoop); return pnseq; } if (forLetImpliedBlock) { forLetImpliedBlock->pn_expr = forLoop; forLetImpliedBlock->pn_pos = forLoop->pn_pos; - ParseNode *let = handler.newBinary(PNK_LET, forLetDecl, forLetImpliedBlock); - if (!let) - return null(); - let->pn_pos = forLoop->pn_pos; - return let; + return handler.newLetBlock(forLetDecl, forLetImpliedBlock, forLoop->pn_pos); } return forLoop; } template <> SyntaxParseHandler::Node Parser<SyntaxParseHandler>::forStatement() { @@ -5634,17 +5640,17 @@ Parser<ParseHandler>::tryStatement() PopStatementPC(tokenStream, pc); bool hasUnconditionalCatch = false; Node catchList = null(); TokenKind tt; if (!tokenStream.getToken(&tt)) return null(); if (tt == TOK_CATCH) { - catchList = handler.newList(PNK_CATCH); + catchList = handler.newCatchList(); if (!catchList) return null(); do { Node pnblock; BindData<ParseHandler> data(context); /* Check for another catch after unconditional catch. */ @@ -5817,17 +5823,17 @@ Parser<ParseHandler>::statement(bool can handler.setListFlag(pn, PNX_POPVAR); if (!MatchOrInsertSemicolon(tokenStream)) return null(); return pn; } case TOK_LET: - return letStatement(); + return letDeclarationOrBlock(); case TOK_IMPORT: return importDeclaration(); case TOK_EXPORT: return exportDeclaration(); case TOK_SEMI: return handler.newEmptyStatement(pos()); case TOK_IF: return ifStatement(); @@ -6072,17 +6078,17 @@ Parser<ParseHandler>::orExpr1(InvokedPre // The >= in this condition works because all the operators in question // are left-associative; if any were not, the case where two operators // have equal precedence would need to be handled specially, and the // stack would need to be a Vector. while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) { depth--; ParseNodeKind combiningPnk = kindStack[depth]; JSOp combiningOp = BinaryOpParseNodeKindToJSOp(combiningPnk); - pn = handler.newBinaryOrAppend(combiningPnk, nodeStack[depth], pn, pc, combiningOp); + pn = handler.appendOrCreateList(combiningPnk, nodeStack[depth], pn, pc, combiningOp); if (!pn) return pn; } if (pnk == PNK_LIMIT) break; nodeStack[depth] = pn; @@ -7057,17 +7063,17 @@ Parser<ParseHandler>::generatorComprehen RootedObject proto(context); if (comprehensionKind == StarGenerator) { JSContext *cx = context->maybeJSContext(); proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global()); if (!proto) return null(); } - RootedFunction fun(context, newFunction(outerpc, /* atom = */ NullPtr(), Expression, proto)); + RootedFunction fun(context, newFunction(/* atom = */ NullPtr(), Expression, proto)); if (!fun) return null(); // Create box for fun->object early to root it. Directives directives(/* strict = */ outerpc->sc->strict); FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives, comprehensionKind); if (!genFunbox) return null(); @@ -7906,16 +7912,18 @@ Parser<ParseHandler>::objectLiteral() JSOp op = JSOP_INITPROP; Node propname; switch (ltok) { case TOK_NUMBER: atom = DoubleToAtom(context, tokenStream.currentToken().number()); if (!atom) return null(); propname = newNumber(tokenStream.currentToken()); + if (!propname) + return null(); break; case TOK_LB: { propname = computedPropertyName(literal); if (!propname) return null(); break; } @@ -8061,16 +8069,18 @@ Parser<ParseHandler>::objectLiteral() if (!tokenStream.checkForKeyword(atom, nullptr)) return null(); PropertyName *name = handler.isName(propname); MOZ_ASSERT(atom); propname = newName(name); if (!propname) return null(); Node ident = identifierName(); + if (!ident) + return null(); if (!handler.addPropertyDefinition(literal, propname, ident, true)) return null(); } else if (tt == TOK_LP) { tokenStream.ungetToken(); if (!methodDefinition(literal, propname, Normal, Method, isGenerator ? StarGenerator : NotGenerator, op)) { return null(); } @@ -8134,17 +8144,17 @@ Parser<ParseHandler>::primaryExpr(TokenK case TOK_LB: return arrayInitializer(); case TOK_LC: return objectLiteral(); case TOK_LET: - return letBlock(LetExpression); + return deprecatedLetBlockOrExpression(LetExpression); case TOK_LP: { TokenKind next; if (!tokenStream.peekToken(&next, TokenStream::Operand)) return null(); if (next != TOK_RP) return parenExprOrGeneratorComprehension();
--- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -438,21 +438,20 @@ class Parser : private JS::AutoGCRooter, * Allocate a new parsed object or function container from * cx->tempLifoAlloc. */ ObjectBox *newObjectBox(NativeObject *obj); FunctionBox *newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *pc, Directives directives, GeneratorKind generatorKind); /* - * Create a new function object given parse context (pc) and a name (which - * is optional if this is a function expression). + * Create a new function object given a name (which is optional if this is + * a function expression). */ - JSFunction *newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind, - JSObject *proto = nullptr); + JSFunction *newFunction(HandleAtom atom, FunctionSyntaxKind kind, JSObject *proto = nullptr); void trace(JSTracer *trc); bool hadAbortedSyntaxParse() { return abortedSyntaxParse; } void clearAbortedSyntaxParse() { abortedSyntaxParse = false; @@ -553,17 +552,17 @@ class Parser : private JS::AutoGCRooter, Node returnStatement(); Node withStatement(); Node labeledStatement(); Node throwStatement(); Node tryStatement(); Node debuggerStatement(); Node lexicalDeclaration(bool isConst); - Node letStatement(); + Node letDeclarationOrBlock(); Node importDeclaration(); Node exportDeclaration(); Node expressionStatement(InvokedPrediction invoked = PredictUninvoked); Node variables(ParseNodeKind kind, bool *psimple = nullptr, StaticBlockObject *blockObj = nullptr, VarContext varContext = HoistVars); Node expr(InvokedPrediction invoked = PredictUninvoked); Node assignExpr(InvokedPrediction invoked = PredictUninvoked); @@ -610,17 +609,17 @@ class Parser : private JS::AutoGCRooter, Node comprehensionTail(GeneratorKind comprehensionKind); Node comprehensionIf(GeneratorKind comprehensionKind); Node comprehensionFor(GeneratorKind comprehensionKind); Node comprehension(GeneratorKind comprehensionKind); Node arrayComprehension(uint32_t begin); Node generatorComprehension(uint32_t begin); bool argumentList(Node listNode, bool *isSpread); - Node letBlock(LetContext letContext); + Node deprecatedLetBlockOrExpression(LetContext letContext); Node destructuringExpr(BindData<ParseHandler> *data, TokenKind tt); Node destructuringExprWithoutYield(BindData<ParseHandler> *data, TokenKind tt, unsigned msg); Node identifierName(); bool matchLabel(MutableHandle<PropertyName*> label); bool allowsForEachIn() {
--- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -92,18 +92,18 @@ class SyntaxParseHandler node == NodeUnparenthesizedCommaExpr || node == NodeUnparenthesizedYieldExpr || node == NodeUnparenthesizedAssignment; } public: SyntaxParseHandler(ExclusiveContext *cx, LifoAlloc &alloc, - TokenStream &tokenStream, bool foldConstants, - Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction) + TokenStream &tokenStream, Parser<SyntaxParseHandler> *syntaxParser, + LazyScript *lazyOuterFunction) : lastAtom(nullptr), tokenStream(tokenStream) {} static Node null() { return NodeFailure; } void trace(JSTracer *trc) {} @@ -158,18 +158,18 @@ class SyntaxParseHandler return NodeGeneric; } Node newBinary(ParseNodeKind kind, JSOp op = JSOP_NOP) { return NodeGeneric; } Node newBinary(ParseNodeKind kind, Node left, JSOp op = JSOP_NOP) { return NodeGeneric; } Node newBinary(ParseNodeKind kind, Node left, Node right, JSOp op = JSOP_NOP) { return NodeGeneric; } - Node newBinaryOrAppend(ParseNodeKind kind, Node left, Node right, - ParseContext<SyntaxParseHandler> *pc, JSOp op = JSOP_NOP) { + Node appendOrCreateList(ParseNodeKind kind, Node left, Node right, + ParseContext<SyntaxParseHandler> *pc, JSOp op = JSOP_NOP) { return NodeGeneric; } Node newTernary(ParseNodeKind kind, Node first, Node second, Node third, JSOp op = JSOP_NOP) { return NodeGeneric; } // Expressions @@ -241,16 +241,24 @@ class SyntaxParseHandler Node newForHead(ParseNodeKind kind, Node decls, Node lhs, Node rhs, const TokenPos &pos) { return NodeGeneric; } Node newLexicalScope(ObjectBox *blockbox) { return NodeGeneric; } void setLexicalScopeBody(Node block, Node body) {} + Node newLetExpression(Node vars, Node block, const TokenPos &pos) { + return NodeGeneric; + } + + Node newLetBlock(Node vars, Node block, const TokenPos &pos) { + return NodeGeneric; + } + bool finishInitializerAssignment(Node pn, Node init, JSOp op) { return true; } void setBeginPosition(Node pn, Node oth) {} void setBeginPosition(Node pn, uint32_t begin) {} void setEndPosition(Node pn, Node oth) {} void setEndPosition(Node pn, uint32_t end) {} @@ -262,30 +270,34 @@ class SyntaxParseHandler Node newList(ParseNodeKind kind, JSOp op = JSOP_NOP) { return NodeGeneric; } Node newList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) { return NodeGeneric; } + Node newCatchList() { + return newList(PNK_CATCHLIST, JSOP_NOP); + } + Node newCommaExpressionList(Node kid) { return NodeUnparenthesizedCommaExpr; } void addList(Node list, Node kid) { MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedCommaExpr); } Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs, ParseContext<SyntaxParseHandler> *pc, JSOp op) { if (kind == PNK_ASSIGN) return NodeUnparenthesizedAssignment; - return newBinaryOrAppend(kind, lhs, rhs, pc, op); + return newBinary(kind, lhs, rhs, op); } bool isUnparenthesizedYieldExpression(Node node) { return node == NodeUnparenthesizedYieldExpr; } bool isUnparenthesizedCommaExpression(Node node) { return node == NodeUnparenthesizedCommaExpr;
--- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -463,16 +463,23 @@ class GCSchedulingTunables * cusp, sometimes it will trigger a non-incremental GC moments before * returning to the event loop, where it could have done an incremental * GC. Thus, we recently added an incremental version of the above with a * substantially lower threshold, so that we have a soft limit here. If * IGC can collect faster than the allocator generates garbage, even if * the allocator does not return to the event loop frequently, we should * not have to fall back to a non-incremental GC. * + * INCREMENTAL_TOO_SLOW + * -------------------- + * Do a full, non-incremental GC if we overflow ALLOC_TRIGGER during an + * incremental GC. When in the middle of an incremental GC, we suppress + * our other triggers, so we need a way to backstop the IGC if the + * mutator allocates faster than the IGC can clean things up. + * * TOO_MUCH_MALLOC * --------------- * Performs a GC before size[allocated] - size[retained] gets too large * for non-incremental sweeping to be fast in the case that we have * significantly more malloc allocation than GC allocation. This is meant * to complement MAYBEGC triggers. We track this by counting malloced * bytes; the counter gets reset at every GC since we do not always have a * size at the time we call free. Because of this, the malloc heuristic @@ -840,16 +847,17 @@ class GCRuntime friend class ArenaLists; Chunk *pickChunk(const AutoLockGC &lock, AutoMaybeStartBackgroundAllocation &maybeStartBGAlloc); ArenaHeader *allocateArena(Chunk *chunk, Zone *zone, AllocKind kind, const AutoLockGC &lock); inline void arenaAllocatedDuringGC(JS::Zone *zone, ArenaHeader *arena); template <AllowGC allowGC> static void *refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind); + static void *tryRefillFreeListFromMainThread(JSContext *cx, AllocKind thingKind); static void *refillFreeListOffMainThread(ExclusiveContext *cx, AllocKind thingKind); /* * Return the list of chunks that can be released outside the GC lock. * Must be called either during the GC or with the GC lock taken. */ ChunkPool expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC &lock); void freeEmptyChunks(JSRuntime *rt, const AutoLockGC &lock);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1127303.js @@ -0,0 +1,9 @@ + +function test1() {} +function test() { test1.call(this); } +var length = 30 * 1024 - 1; +var obj = new test(); +for(var i = 0 ; i < length ; i++) { + obj.next = new (function ( ) { } ) (); + obj = obj.next; +}
--- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -9162,23 +9162,28 @@ GetTemplateObjectForNative(JSContext *cx RootedObject proto(cx, args[0].toObjectOrNull()); res.set(ObjectCreateImpl(cx, proto, TenuredObject)); if (!res) return false; return true; } if (JitSupportsSimd()) { - if (native == js::simd_int32x4_add || native == js::simd_int32x4_and) { +#define ADD_INT32X4_SIMD_OP_NAME_(OP) || native == js::simd_int32x4_##OP + if (false + ARITH_COMMONX4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_) + BITWISE_COMMONX4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_)) + { Rooted<TypeDescr *> descr(cx, &Int32x4::GetTypeDescr(*cx->global())); res.set(TypedObject::createZeroed(cx, descr, 0, gc::TenuredHeap)); if (!res) return false; return true; - } + } +#undef ADD_INT32X4_SIMD_OP_NAME_ } return true; } static bool GetTemplateObjectForClassHook(JSContext *cx, JSNative hook, CallArgs &args, MutableHandleObject templateObject)
--- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -3,30 +3,30 @@ * 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 "jit/IonCaches.h" #include "mozilla/TemplateLib.h" -#include "jsproxy.h" #include "jstypes.h" #include "builtin/TypedObject.h" #include "jit/BaselineIC.h" #include "jit/Ion.h" #include "jit/JitcodeMap.h" #include "jit/JitSpewer.h" #include "jit/Linker.h" #include "jit/Lowering.h" #ifdef JS_ION_PERF # include "jit/PerfSpewer.h" #endif #include "jit/VMFunctions.h" +#include "js/Proxy.h" #include "vm/Shape.h" #include "jit/JitFrames-inl.h" #include "vm/Interpreter-inl.h" #include "vm/Shape-inl.h" using namespace js; using namespace js::jit;
--- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -252,20 +252,27 @@ IonBuilder::inlineNativeCall(CallInfo &c if (native == testingFunc_assertFloat32) return inlineAssertFloat32(callInfo); // Bound function if (native == js::CallOrConstructBoundFunction) return inlineBoundFunction(callInfo, target); // Simd functions - if (native == js::simd_int32x4_add) - return inlineSimdInt32x4BinaryArith(callInfo, native, MSimdBinaryArith::Add); - if (native == js::simd_int32x4_and) - return inlineSimdInt32x4BinaryBitwise(callInfo, native, MSimdBinaryBitwise::and_); +#define INLINE_INT32X4_SIMD_ARITH_(OP) \ + if (native == js::simd_int32x4_##OP) \ + return inlineSimdInt32x4BinaryArith(callInfo, native, MSimdBinaryArith::Op_##OP); + ARITH_COMMONX4_SIMD_OP(INLINE_INT32X4_SIMD_ARITH_) +#undef INLINE_INT32X4_SIMD_ARITH_ + +#define INLINE_INT32X4_SIMD_BITWISE_(OP) \ + if (native == js::simd_int32x4_##OP) \ + return inlineSimdInt32x4BinaryBitwise(callInfo, native, MSimdBinaryBitwise::OP##_); + BITWISE_COMMONX4_SIMD_OP(INLINE_INT32X4_SIMD_BITWISE_) +#undef INLINE_INT32X4_SIMD_BITWISE_ return InliningStatus_NotInlined; } IonBuilder::InliningStatus IonBuilder::inlineNativeGetter(CallInfo &callInfo, JSFunction *target) { MOZ_ASSERT(target->isNative());
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -10,16 +10,17 @@ */ #ifndef jit_MIR_h #define jit_MIR_h #include "mozilla/Array.h" #include "mozilla/DebugOnly.h" +#include "builtin/SIMD.h" #include "jit/AtomicOp.h" #include "jit/FixedList.h" #include "jit/InlineList.h" #include "jit/JitAllocPolicy.h" #include "jit/MacroAssembler.h" #include "jit/MOpcodes.h" #include "jit/TypedObjectPrediction.h" #include "jit/TypePolicy.h" @@ -1953,51 +1954,43 @@ class MSimdBinaryComp }; class MSimdBinaryArith : public MBinaryInstruction, public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1> >::Data { public: enum Operation { - Add, - Sub, - Mul, - Div, - Min, - Max, - MinNum, - MaxNum +#define OP_LIST_(OP) Op_##OP, + ARITH_COMMONX4_SIMD_OP(OP_LIST_) + ARITH_FLOAT32X4_SIMD_OP(OP_LIST_) +#undef OP_LIST_ }; static const char* OperationName(Operation op) { switch (op) { - case Add: return "Add"; - case Sub: return "Sub"; - case Mul: return "Mul"; - case Div: return "Div"; - case Min: return "Min"; - case Max: return "Max"; - case MinNum: return "MinNum"; - case MaxNum: return "MaxNum"; +#define OP_CASE_LIST_(OP) case Op_##OP: return #OP; + ARITH_COMMONX4_SIMD_OP(OP_CASE_LIST_) + ARITH_FLOAT32X4_SIMD_OP(OP_CASE_LIST_) +#undef OP_CASE_LIST_ } MOZ_CRASH("unexpected operation"); } private: Operation operation_; MSimdBinaryArith(MDefinition *left, MDefinition *right, Operation op, MIRType type) : MBinaryInstruction(left, right), operation_(op) { - MOZ_ASSERT_IF(type == MIRType_Int32x4, op == Add || op == Sub || op == Mul); + MOZ_ASSERT_IF(type == MIRType_Int32x4, op == Op_add || op == Op_sub || op == Op_mul); MOZ_ASSERT(IsSimdType(type)); setResultType(type); setMovable(); - if (op == Add || op == Mul || op == Min || op == Max) + if (op == Op_add || op == Op_mul || op == Op_min || op == Op_max) setCommutative(); } public: INSTRUCTION_HEADER(SimdBinaryArith) static MSimdBinaryArith *New(TempAllocator &alloc, MDefinition *left, MDefinition *right, Operation op, MIRType t) {
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp @@ -2652,23 +2652,23 @@ void CodeGeneratorX86Shared::visitSimdBinaryArithIx4(LSimdBinaryArithIx4 *ins) { FloatRegister lhs = ToFloatRegister(ins->lhs()); Operand rhs = ToOperand(ins->rhs()); FloatRegister output = ToFloatRegister(ins->output()); MSimdBinaryArith::Operation op = ins->operation(); switch (op) { - case MSimdBinaryArith::Add: + case MSimdBinaryArith::Op_add: masm.vpaddd(rhs, lhs, output); return; - case MSimdBinaryArith::Sub: + case MSimdBinaryArith::Op_sub: masm.vpsubd(rhs, lhs, output); return; - case MSimdBinaryArith::Mul: { + case MSimdBinaryArith::Op_mul: { if (AssemblerX86Shared::HasSSE41()) { masm.vpmulld(rhs, lhs, output); return; } masm.loadAlignedInt32x4(rhs, ScratchSimdReg); masm.vpmuludq(lhs, ScratchSimdReg, ScratchSimdReg); // ScratchSimdReg contains (Rx, _, Rz, _) where R is the resulting vector. @@ -2679,76 +2679,76 @@ CodeGeneratorX86Shared::visitSimdBinaryA masm.vpmuludq(temp, lhs, lhs); // lhs contains (Ry, _, Rw, _) where R is the resulting vector. masm.vshufps(MacroAssembler::ComputeShuffleMask(LaneX, LaneZ, LaneX, LaneZ), ScratchSimdReg, lhs, lhs); // lhs contains (Ry, Rw, Rx, Rz) masm.vshufps(MacroAssembler::ComputeShuffleMask(LaneZ, LaneX, LaneW, LaneY), lhs, lhs, lhs); return; } - case MSimdBinaryArith::Div: + case MSimdBinaryArith::Op_div: // x86 doesn't have SIMD i32 div. break; - case MSimdBinaryArith::Max: + case MSimdBinaryArith::Op_max: // we can do max with a single instruction only if we have SSE4.1 // using the PMAXSD instruction. break; - case MSimdBinaryArith::Min: + case MSimdBinaryArith::Op_min: // we can do max with a single instruction only if we have SSE4.1 // using the PMINSD instruction. break; - case MSimdBinaryArith::MinNum: - case MSimdBinaryArith::MaxNum: + case MSimdBinaryArith::Op_minNum: + case MSimdBinaryArith::Op_maxNum: break; } MOZ_CRASH("unexpected SIMD op"); } void CodeGeneratorX86Shared::visitSimdBinaryArithFx4(LSimdBinaryArithFx4 *ins) { FloatRegister lhs = ToFloatRegister(ins->lhs()); Operand rhs = ToOperand(ins->rhs()); FloatRegister output = ToFloatRegister(ins->output()); MSimdBinaryArith::Operation op = ins->operation(); switch (op) { - case MSimdBinaryArith::Add: + case MSimdBinaryArith::Op_add: masm.vaddps(rhs, lhs, output); return; - case MSimdBinaryArith::Sub: + case MSimdBinaryArith::Op_sub: masm.vsubps(rhs, lhs, output); return; - case MSimdBinaryArith::Mul: + case MSimdBinaryArith::Op_mul: masm.vmulps(rhs, lhs, output); return; - case MSimdBinaryArith::Div: + case MSimdBinaryArith::Op_div: masm.vdivps(rhs, lhs, output); return; - case MSimdBinaryArith::Max: { + case MSimdBinaryArith::Op_max: { FloatRegister lhsCopy = masm.reusedInputFloat32x4(lhs, ScratchSimdReg); masm.vcmpunordps(rhs, lhsCopy, ScratchSimdReg); FloatRegister tmp = ToFloatRegister(ins->temp()); FloatRegister rhsCopy = masm.reusedInputAlignedFloat32x4(rhs, tmp); masm.vmaxps(Operand(lhs), rhsCopy, tmp); masm.vmaxps(rhs, lhs, output); masm.vandps(tmp, output, output); masm.vorps(ScratchSimdReg, output, output); // or in the all-ones NaNs return; } - case MSimdBinaryArith::Min: { + case MSimdBinaryArith::Op_min: { FloatRegister rhsCopy = masm.reusedInputAlignedFloat32x4(rhs, ScratchSimdReg); masm.vminps(Operand(lhs), rhsCopy, ScratchSimdReg); masm.vminps(rhs, lhs, output); masm.vorps(ScratchSimdReg, output, output); // NaN or'd with arbitrary bits is NaN return; } - case MSimdBinaryArith::MinNum: { + case MSimdBinaryArith::Op_minNum: { FloatRegister tmp = ToFloatRegister(ins->temp()); masm.loadConstantInt32x4(SimdConstant::SplatX4(int32_t(0x80000000)), tmp); FloatRegister mask = ScratchSimdReg; FloatRegister tmpCopy = masm.reusedInputFloat32x4(tmp, ScratchSimdReg); masm.vpcmpeqd(Operand(lhs), tmpCopy, mask); masm.vandps(tmp, mask, mask); @@ -2768,17 +2768,17 @@ CodeGeneratorX86Shared::visitSimdBinaryA if (lhs != output) masm.moveFloat32x4(lhs, output); masm.vandps(Operand(mask), output, output); masm.vandnps(Operand(tmp), mask, mask); masm.vorps(Operand(mask), output, output); } return; } - case MSimdBinaryArith::MaxNum: { + case MSimdBinaryArith::Op_maxNum: { FloatRegister mask = ScratchSimdReg; masm.loadConstantInt32x4(SimdConstant::SplatX4(0), mask); masm.vpcmpeqd(Operand(lhs), mask, mask); FloatRegister tmp = ToFloatRegister(ins->temp()); masm.loadConstantInt32x4(SimdConstant::SplatX4(int32_t(0x80000000)), tmp); masm.vandps(tmp, mask, mask);
--- a/js/src/jit/shared/Lowering-x86-shared.cpp +++ b/js/src/jit/shared/Lowering-x86-shared.cpp @@ -634,29 +634,29 @@ LIRGeneratorX86Shared::visitSimdBinaryAr MDefinition *lhs = ins->lhs(); MDefinition *rhs = ins->rhs(); if (ins->isCommutative()) ReorderCommutative(&lhs, &rhs, ins); if (ins->type() == MIRType_Int32x4) { LSimdBinaryArithIx4 *lir = new(alloc()) LSimdBinaryArithIx4(); - bool needsTemp = ins->operation() == MSimdBinaryArith::Mul && !MacroAssembler::HasSSE41(); + bool needsTemp = ins->operation() == MSimdBinaryArith::Op_mul && !MacroAssembler::HasSSE41(); lir->setTemp(0, needsTemp ? temp(LDefinition::INT32X4) : LDefinition::BogusTemp()); lowerForFPU(lir, ins, lhs, rhs); return; } MOZ_ASSERT(ins->type() == MIRType_Float32x4, "unknown simd type on binary arith operation"); LSimdBinaryArithFx4 *lir = new(alloc()) LSimdBinaryArithFx4(); - bool needsTemp = ins->operation() == MSimdBinaryArith::Max || - ins->operation() == MSimdBinaryArith::MinNum || - ins->operation() == MSimdBinaryArith::MaxNum; + bool needsTemp = ins->operation() == MSimdBinaryArith::Op_max || + ins->operation() == MSimdBinaryArith::Op_minNum || + ins->operation() == MSimdBinaryArith::Op_maxNum; lir->setTemp(0, needsTemp ? temp(LDefinition::FLOAT32X4) : LDefinition::BogusTemp()); lowerForFPU(lir, ins, lhs, rhs); } void LIRGeneratorX86Shared::visitSimdSelect(MSimdSelect *ins) {
--- a/js/src/jsapi-tests/testSetPropertyIgnoringNamedGetter.cpp +++ b/js/src/jsapi-tests/testSetPropertyIgnoringNamedGetter.cpp @@ -1,14 +1,15 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: */ #include "jsfriendapi.h" -#include "jsproxy.h" + +#include "js/Proxy.h" #include "jsapi-tests/tests.h" using namespace js; using namespace JS; class CustomProxyHandler : public DirectProxyHandler { public:
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -30,17 +30,16 @@ #include "jsgc.h" #include "jsiter.h" #include "jslock.h" #include "jsmath.h" #include "jsnum.h" #include "jsobj.h" #include "json.h" #include "jsprf.h" -#include "jsproxy.h" #include "jsscript.h" #include "jsstr.h" #include "jstypes.h" #include "jsutil.h" #include "jswatchpoint.h" #include "jsweakmap.h" #include "jswrapper.h" @@ -57,16 +56,17 @@ #endif #include "frontend/BytecodeCompiler.h" #include "frontend/FullParseHandler.h" // for JS_BufferIsCompileableUnit #include "frontend/Parser.h" // for JS_BufferIsCompileableUnit #include "gc/Marking.h" #include "jit/JitCommon.h" #include "js/CharacterEncoding.h" #include "js/Conversions.h" +#include "js/Proxy.h" #include "js/SliceBudget.h" #include "js/StructuredClone.h" #if ENABLE_INTL_API #include "unicode/uclean.h" #include "unicode/utypes.h" #endif // ENABLE_INTL_API #include "vm/DateObject.h" #include "vm/Debugger.h"
--- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -8,22 +8,22 @@ #include "mozilla/DebugOnly.h" #include "mozilla/MemoryReporting.h" #include "jscntxt.h" #include "jsfriendapi.h" #include "jsgc.h" #include "jsiter.h" -#include "jsproxy.h" #include "jswatchpoint.h" #include "jswrapper.h" #include "gc/Marking.h" #include "jit/JitCompartment.h" +#include "js/Proxy.h" #include "js/RootingAPI.h" #include "proxy/DeadObjectProxy.h" #include "vm/Debugger.h" #include "vm/StopIterationObject.h" #include "vm/WrapperObject.h" #include "jsatominlines.h" #include "jsfuninlines.h"
--- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -10,23 +10,23 @@ #include <stdint.h> #include "jscntxt.h" #include "jscompartment.h" #include "jsgc.h" #include "jsobj.h" #include "jsprf.h" -#include "jsproxy.h" #include "jswatchpoint.h" #include "jsweakmap.h" #include "jswrapper.h" #include "prmjtime.h" #include "builtin/TestingFunctions.h" +#include "js/Proxy.h" #include "proxy/DeadObjectProxy.h" #include "vm/ArgumentsObject.h" #include "vm/WrapperObject.h" #include "jsobjinlines.h" #include "jsscriptinlines.h" #include "vm/NativeObject-inl.h"
--- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -16,30 +16,30 @@ #include <string.h> #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jscntxt.h" #include "jsobj.h" -#include "jsproxy.h" #include "jsscript.h" #include "jsstr.h" #include "jstypes.h" #include "jswrapper.h" #include "builtin/Eval.h" #include "builtin/Object.h" #include "frontend/BytecodeCompiler.h" #include "frontend/TokenStream.h" #include "gc/Marking.h" #include "jit/Ion.h" #include "jit/JitFrameIterator.h" #include "js/CallNonGenericMethod.h" +#include "js/Proxy.h" #include "vm/GlobalObject.h" #include "vm/Interpreter.h" #include "vm/Shape.h" #include "vm/StringBuffer.h" #include "vm/WrapperObject.h" #include "vm/Xdr.h" #include "jsscriptinlines.h"
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2917,89 +2917,73 @@ void ArenaLists::queueForegroundThingsForSweep(FreeOp *fop) { gcShapeArenasToUpdate = arenaListsToSweep[FINALIZE_SHAPE]; gcAccessorShapeArenasToUpdate = arenaListsToSweep[FINALIZE_ACCESSOR_SHAPE]; gcObjectGroupArenasToUpdate = arenaListsToSweep[FINALIZE_OBJECT_GROUP]; gcScriptArenasToUpdate = arenaListsToSweep[FINALIZE_SCRIPT]; } -static void * -RunLastDitchGC(JSContext *cx, JS::Zone *zone, AllocKind thingKind) -{ - PrepareZoneForGC(zone); - - JSRuntime *rt = cx->runtime(); - - /* The last ditch GC preserves all atoms. */ - AutoKeepAtoms keepAtoms(cx->perThreadData); - rt->gc.gc(GC_NORMAL, JS::gcreason::LAST_DITCH); - - /* - * The JSGC_END callback can legitimately allocate new GC - * things and populate the free list. If that happens, just - * return that list head. - */ - size_t thingSize = Arena::thingSize(thingKind); - return zone->arenas.allocateFromFreeList(thingKind, thingSize); +/* static */ void * +GCRuntime::tryRefillFreeListFromMainThread(JSContext *cx, AllocKind thingKind) +{ + ArenaLists *arenas = cx->arenas(); + Zone *zone = cx->zone(); + + AutoMaybeStartBackgroundAllocation maybeStartBGAlloc; + + void *thing = arenas->allocateFromArena(zone, thingKind, maybeStartBGAlloc); + if (MOZ_LIKELY(thing)) + return thing; + + // Even if allocateFromArena failed due to OOM, a background + // finalization or allocation task may be running freeing more memory + // or adding more available memory to our free pool; wait for them to + // finish, then try to allocate again in case they made more memory + // available. + cx->runtime()->gc.waitBackgroundSweepOrAllocEnd(); + + thing = arenas->allocateFromArena(zone, thingKind, maybeStartBGAlloc); + if (thing) + return thing; + + return nullptr; } template <AllowGC allowGC> /* static */ void * GCRuntime::refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind) { JSRuntime *rt = cx->runtime(); MOZ_ASSERT(!rt->isHeapBusy(), "allocating while under GC"); MOZ_ASSERT_IF(allowGC, !rt->currentThreadHasExclusiveAccess()); - ArenaLists *arenas = cx->arenas(); - Zone *zone = cx->zone(); - - // If we have grown past our GC heap threshold while in the middle of an - // incremental GC, we're growing faster than we're GCing, so stop the world - // and do a full, non-incremental GC right now, if possible. - const bool mustCollectNow = allowGC && rt->gc.isIncrementalGCInProgress() && - zone->usage.gcBytes() > zone->threshold.gcTriggerBytes(); - - bool outOfMemory = false; // Set true if we fail to allocate. - bool ranGC = false; // Once we've GC'd and still cannot allocate, report. - do { - if (MOZ_UNLIKELY(mustCollectNow || outOfMemory)) { - // If we are doing a fallible allocation, percolate up the OOM - // instead of reporting it. - if (!allowGC) { - MOZ_ASSERT(!mustCollectNow); - return nullptr; - } - - if (void *thing = RunLastDitchGC(cx, zone, thingKind)) - return thing; - ranGC = true; - } - - AutoMaybeStartBackgroundAllocation maybeStartBGAlloc; - void *thing = arenas->allocateFromArena(zone, thingKind, maybeStartBGAlloc); - if (MOZ_LIKELY(thing)) - return thing; - - // Even if allocateFromArena failed due to OOM, a background - // finalization or allocation task may be running freeing more memory - // or adding more available memory to our free pool; wait for them to - // finish, then try to allocate again in case they made more memory - // available. - rt->gc.waitBackgroundSweepOrAllocEnd(); - - thing = arenas->allocateFromArena(zone, thingKind, maybeStartBGAlloc); - if (MOZ_LIKELY(thing)) - return thing; - - // Retry after a last-ditch GC, unless we've already tried that. - outOfMemory = true; - } while (!ranGC); - + // Try to allocate; synchronize with background GC threads if necessary. + void *thing = tryRefillFreeListFromMainThread(cx, thingKind); + if (MOZ_LIKELY(thing)) + return thing; + + // Perform a last-ditch GC to hopefully free up some memory. + { + // If we are doing a fallible allocation, percolate up the OOM + // instead of reporting it. + if (!allowGC) + return nullptr; + + JS::PrepareZoneForGC(cx->zone()); + AutoKeepAtoms keepAtoms(cx->perThreadData); + rt->gc.gc(GC_NORMAL, JS::gcreason::LAST_DITCH); + } + + // Retry the allocation after the last-ditch GC. + thing = tryRefillFreeListFromMainThread(cx, thingKind); + if (thing) + return thing; + + // We are really just totally out of memory. MOZ_ASSERT(allowGC, "A fallible allocation must not report OOM on failure."); js_ReportOutOfMemory(cx); return nullptr; } /* static */ void * GCRuntime::refillFreeListOffMainThread(ExclusiveContext *cx, AllocKind thingKind) {
--- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -432,30 +432,40 @@ CheckAllocatorState(ExclusiveContext *cx #endif // Crash if we perform a GC action when it is not safe. if (allowGC && !rt->mainThread.suppressGC) JS::AutoAssertOnGC::VerifyIsSafeToGC(rt); // For testing out of memory conditions if (!PossiblyFail()) { - js_ReportOutOfMemory(cx->asJSContext()); + js_ReportOutOfMemory(ncx); return false; } if (allowGC) { #ifdef JS_GC_ZEAL if (rt->gc.needZealousGC()) rt->gc.runDebugGC(); #endif - if (rt->hasPendingInterrupt()) { // Invoking the interrupt callback can fail and we can't usefully // handle that here. Just check in case we need to collect instead. - rt->gc.gcIfRequested(); + rt->gc.gcIfRequested(ncx); + } + + // If we have grown past our GC heap threshold while in the middle of + // an incremental GC, we're growing faster than we're GCing, so stop + // the world and do a full, non-incremental GC right now, if possible. + if (rt->gc.isIncrementalGCInProgress() && + ncx->zone()->usage.gcBytes() > ncx->zone()->threshold.gcTriggerBytes()) + { + PrepareZoneForGC(ncx->zone()); + AutoKeepAtoms keepAtoms(cx->perThreadData); + rt->gc.gc(GC_NORMAL, JS::gcreason::INCREMENTAL_TOO_SLOW); } } return true; } template <typename T> static inline void
--- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -14,23 +14,23 @@ #include "mozilla/PodOperations.h" #include "jsarray.h" #include "jsatom.h" #include "jscntxt.h" #include "jsgc.h" #include "jsobj.h" #include "jsopcode.h" -#include "jsproxy.h" #include "jsscript.h" #include "jstypes.h" #include "jsutil.h" #include "ds/Sort.h" #include "gc/Marking.h" +#include "js/Proxy.h" #include "vm/GeneratorObject.h" #include "vm/GlobalObject.h" #include "vm/Interpreter.h" #include "vm/Shape.h" #include "vm/StopIterationObject.h" #include "vm/TypedArrayCommon.h" #include "jsscriptinlines.h"
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -23,32 +23,32 @@ #include "jscntxt.h" #include "jsfriendapi.h" #include "jsfun.h" #include "jsgc.h" #include "jsiter.h" #include "jsnum.h" #include "jsopcode.h" #include "jsprf.h" -#include "jsproxy.h" #include "jsscript.h" #include "jsstr.h" #include "jstypes.h" #include "jsutil.h" #include "jswatchpoint.h" #include "jswrapper.h" #include "asmjs/AsmJSModule.h" #include "builtin/Eval.h" #include "builtin/Object.h" #include "builtin/SymbolObject.h" #include "frontend/BytecodeCompiler.h" #include "gc/Marking.h" #include "jit/BaselineJIT.h" #include "js/MemoryMetrics.h" +#include "js/Proxy.h" #include "vm/ArgumentsObject.h" #include "vm/Interpreter.h" #include "vm/ProxyObject.h" #include "vm/RegExpStaticsObject.h" #include "vm/Shape.h" #include "vm/TypedArrayCommon.h" #include "jsatominlines.h"
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -725,17 +725,17 @@ namespace js { * is actually quite good on this topic, though you may have to read it a few * times. See ES6 draft rev 29 (6 Dec 2014) 6.1.7.2 and 6.1.7.3. * * When 'obj' is an ordinary object, these functions have boring standard * behavior as specified by ES6 draft rev 29 section 9.1; see the section about * internal methods in vm/NativeObject.h. * * Proxies override the behavior of internal methods. So when 'obj' is a proxy, - * any one of the functions below could do just about anything. See jsproxy.h. + * any one of the functions below could do just about anything. See js/Proxy.h. */ /* * ES6 [[GetPrototypeOf]]. Get obj's prototype, storing it in protop. * * If obj is definitely not a proxy, the infallible obj->getProto() can be used * instead. See the comment on JSObject::getTaggedProto(). */
--- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -2297,21 +2297,21 @@ ASTSerializer::tryStatement(ParseNode *p RootedValue body(cx); if (!statement(pn->pn_kid1, &body)) return false; NodeVector guarded(cx); RootedValue unguarded(cx, NullValue()); - if (pn->pn_kid2) { - if (!guarded.reserve(pn->pn_kid2->pn_count)) + if (ParseNode *catchList = pn->pn_kid2) { + if (!guarded.reserve(catchList->pn_count)) return false; - for (ParseNode *next = pn->pn_kid2->pn_head; next; next = next->pn_next) { + for (ParseNode *next = catchList->pn_head; next; next = next->pn_next) { RootedValue clause(cx); bool isGuarded; if (!catchClause(next->pn_expr, &isGuarded, &clause)) return false; if (isGuarded) guarded.infallibleAppend(clause); else unguarded = clause; @@ -2362,21 +2362,22 @@ ASTSerializer::statement(ParseNode *pn, { JS_CHECK_RECURSION(cx, return false); switch (pn->getKind()) { case PNK_FUNCTION: case PNK_VAR: case PNK_GLOBALCONST: return declaration(pn, dst); + case PNK_LETBLOCK: + return let(pn, false, dst); + case PNK_LET: case PNK_CONST: - return pn->isArity(PN_BINARY) - ? let(pn, false, dst) - : declaration(pn, dst); + return declaration(pn, dst); case PNK_IMPORT: return importDeclaration(pn, dst); case PNK_EXPORT: case PNK_EXPORT_FROM: return exportDeclaration(pn, dst); @@ -2750,28 +2751,17 @@ ASTSerializer::expression(ParseNode *pn, return expression(pn->pn_kid1, &test) && expression(pn->pn_kid2, &cons) && expression(pn->pn_kid3, &alt) && builder.conditionalExpression(test, cons, alt, &pn->pn_pos, dst); } case PNK_OR: case PNK_AND: - { - if (pn->isArity(PN_BINARY)) { - MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); - MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); - - RootedValue left(cx), right(cx); - return expression(pn->pn_left, &left) && - expression(pn->pn_right, &right) && - builder.logicalExpression(pn->isKind(PNK_OR), left, right, &pn->pn_pos, dst); - } return leftAssociate(pn, dst); - } case PNK_PREINCREMENT: case PNK_PREDECREMENT: { MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos)); bool inc = pn->isKind(PNK_PREINCREMENT); RootedValue expr(cx); @@ -2831,28 +2821,16 @@ ASTSerializer::expression(ParseNode *pn, case PNK_STAR: case PNK_DIV: case PNK_MOD: case PNK_BITOR: case PNK_BITXOR: case PNK_BITAND: case PNK_IN: case PNK_INSTANCEOF: - if (pn->isArity(PN_BINARY)) { - MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); - MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); - - BinaryOperator op = binop(pn->getKind(), pn->getOp()); - LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT); - - RootedValue left(cx), right(cx); - return expression(pn->pn_left, &left) && - expression(pn->pn_right, &right) && - builder.binaryExpression(op, left, right, &pn->pn_pos, dst); - } return leftAssociate(pn, dst); case PNK_DELETE: case PNK_TYPEOF: case PNK_VOID: case PNK_NOT: case PNK_BITNOT: case PNK_POS: @@ -3062,17 +3040,17 @@ ASTSerializer::expression(ParseNode *pn, case PNK_ARRAYCOMP: MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_head->pn_pos)); /* NB: it's no longer the case that pn_count could be 2. */ LOCAL_ASSERT(pn->pn_count == 1); return comprehension(pn->pn_head, dst); - case PNK_LET: + case PNK_LETEXPR: return let(pn, true, dst); default: LOCAL_NOT_REACHED("unexpected expression type"); } } bool
--- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -4,17 +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/. */ #ifndef jswrapper_h #define jswrapper_h #include "mozilla/Attributes.h" -#include "jsproxy.h" +#include "js/Proxy.h" namespace js { class DummyFrameGuard; /* * Helper for Wrapper::New default options. *
--- a/js/src/moz.build +++ b/js/src/moz.build @@ -46,17 +46,16 @@ EXPORTS += [ 'jsalloc.h', 'jsapi.h', 'jsbytecode.h', 'jsclist.h', 'jscpucfg.h', 'jsfriendapi.h', 'jsprf.h', 'jsprototypes.h', - 'jsproxy.h', 'jspubtd.h', 'jstypes.h', 'jsversion.h', 'jswrapper.h', 'perf/jsperf.h', ] # If you add a header here, add it to js/src/jsapi-tests/testIntTypesABI.cpp so @@ -74,16 +73,17 @@ EXPORTS.js += [ '../public/HashTable.h', '../public/HeapAPI.h', '../public/Id.h', '../public/LegacyIntTypes.h', '../public/MemoryMetrics.h', '../public/Principals.h', '../public/ProfilingFrameIterator.h', '../public/ProfilingStack.h', + '../public/Proxy.h', '../public/RequiredDefines.h', '../public/RootingAPI.h', '../public/SliceBudget.h', '../public/StructuredClone.h', '../public/TracingAPI.h', '../public/TrackedOptimizationInfo.h', '../public/TypeDecls.h', '../public/UbiNode.h',
--- a/js/src/proxy/BaseProxyHandler.cpp +++ b/js/src/proxy/BaseProxyHandler.cpp @@ -1,16 +1,15 @@ /* -*- 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 "jsproxy.h" - +#include "js/Proxy.h" #include "vm/ProxyObject.h" #include "jscntxtinlines.h" #include "jsobjinlines.h" using namespace js; bool
--- a/js/src/proxy/DeadObjectProxy.h +++ b/js/src/proxy/DeadObjectProxy.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 proxy_DeadObjectProxy_h #define proxy_DeadObjectProxy_h -#include "jsproxy.h" +#include "js/Proxy.h" namespace js { class DeadObjectProxy : public BaseProxyHandler { public: explicit MOZ_CONSTEXPR DeadObjectProxy() : BaseProxyHandler(&family)
--- a/js/src/proxy/DirectProxyHandler.cpp +++ b/js/src/proxy/DirectProxyHandler.cpp @@ -1,17 +1,17 @@ /* -*- 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 "jsproxy.h" #include "jswrapper.h" // UncheckedUnwrap +#include "js/Proxy.h" #include "vm/ProxyObject.h" #include "jsobjinlines.h" using namespace js; bool DirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
--- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -1,21 +1,22 @@ /* -*- 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 "js/Proxy.h" + #include <string.h> #include "jsapi.h" #include "jscntxt.h" #include "jsfun.h" #include "jsgc.h" -#include "jsproxy.h" #include "jswrapper.h" #include "gc/Marking.h" #include "proxy/DeadObjectProxy.h" #include "proxy/ScriptedDirectProxyHandler.h" #include "proxy/ScriptedIndirectProxyHandler.h" #include "vm/WrapperObject.h" @@ -109,53 +110,30 @@ Proxy::getPropertyDescriptor(JSContext * if (!handler->getOwnPropertyDescriptor(cx, proxy, id, desc)) return false; if (desc.object()) return true; INVOKE_ON_PROTOTYPE(cx, handler, proxy, GetPropertyDescriptor(cx, proto, id, desc)); } bool -Proxy::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue vp) -{ - JS_CHECK_RECURSION(cx, return false); - - Rooted<PropertyDescriptor> desc(cx); - if (!Proxy::getPropertyDescriptor(cx, proxy, id, &desc)) - return false; - return NewPropertyDescriptorObject(cx, desc, vp); -} - -bool Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle<PropertyDescriptor> desc) { JS_CHECK_RECURSION(cx, return false); const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler(); desc.object().set(nullptr); // default result if we refuse to perform this action AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true); if (!policy.allowed()) return policy.returnValue(); return handler->getOwnPropertyDescriptor(cx, proxy, id, desc); } bool -Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandleValue vp) -{ - JS_CHECK_RECURSION(cx, return false); - - Rooted<PropertyDescriptor> desc(cx); - if (!Proxy::getOwnPropertyDescriptor(cx, proxy, id, &desc)) - return false; - return NewPropertyDescriptorObject(cx, desc, vp); -} - -bool Proxy::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle<PropertyDescriptor> desc) { JS_CHECK_RECURSION(cx, return false); const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler(); AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true); if (!policy.allowed()) return policy.returnValue();
--- a/js/src/proxy/Proxy.h +++ b/js/src/proxy/Proxy.h @@ -23,18 +23,16 @@ class RegExpGuard; * 945826 comment 0. */ class Proxy { public: /* Standard internal methods. */ static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle<JSPropertyDescriptor> desc); - static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandleValue vp); static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle<JSPropertyDescriptor> desc); static bool ownPropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props); static bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); static bool enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp); static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible); static bool preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded); static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop); @@ -46,18 +44,16 @@ class Proxy static bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict, MutableHandleValue vp); static bool call(JSContext *cx, HandleObject proxy, const CallArgs &args); static bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args); /* SpiderMonkey extensions. */ static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle<JSPropertyDescriptor> desc); - static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandleValue vp); static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); static bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props); static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args); static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp); static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx); static const char *className(JSContext *cx, HandleObject proxy); static JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
--- a/js/src/proxy/ScriptedDirectProxyHandler.h +++ b/js/src/proxy/ScriptedDirectProxyHandler.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 proxy_ScriptedDirectProxyHandler_h #define proxy_ScriptedDirectProxyHandler_h -#include "jsproxy.h" +#include "js/Proxy.h" namespace js { /* Derived class for all scripted direct proxy handlers. */ class ScriptedDirectProxyHandler : public DirectProxyHandler { public: MOZ_CONSTEXPR ScriptedDirectProxyHandler() : DirectProxyHandler(&family)
--- a/js/src/proxy/ScriptedIndirectProxyHandler.h +++ b/js/src/proxy/ScriptedIndirectProxyHandler.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 proxy_ScriptedIndirectProxyHandler_h #define proxy_ScriptedIndirectProxyHandler_h -#include "jsproxy.h" +#include "js/Proxy.h" namespace js { /* Derived class for all scripted indirect proxy handlers. */ class ScriptedIndirectProxyHandler : public BaseProxyHandler { public: MOZ_CONSTEXPR ScriptedIndirectProxyHandler()
--- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -231,21 +231,25 @@ class ObjectGroup : public gc::TenuredCe if (addendumKind() == Addendum_UnboxedLayout) return reinterpret_cast<UnboxedLayout *>(addendum_); return nullptr; } TypeNewScript *anyNewScript(); void detachNewScript(bool writeBarrier); + ObjectGroupFlags flagsDontCheckGeneration() { + return flags_; + } + public: ObjectGroupFlags flags() { maybeSweep(nullptr); - return flags_; + return flagsDontCheckGeneration(); } void addFlags(ObjectGroupFlags flags) { maybeSweep(nullptr); flags_ |= flags; } void clearFlags(ObjectGroupFlags flags) { @@ -373,27 +377,39 @@ class ObjectGroup : public gc::TenuredCe public: inline ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags); inline bool hasAnyFlags(ObjectGroupFlags flags) { MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); return !!(this->flags() & flags); } + bool hasAllFlags(ObjectGroupFlags flags) { MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); return (this->flags() & flags) == flags; } + bool hasAllFlagsDontCheckGeneration(ObjectGroupFlags flags) { + MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); + return (this->flagsDontCheckGeneration() & flags) == flags; + } + bool unknownProperties() { MOZ_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES, hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK)); return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES); } + bool unknownPropertiesDontCheckGeneration() { + MOZ_ASSERT_IF(flagsDontCheckGeneration() & OBJECT_FLAG_UNKNOWN_PROPERTIES, + hasAllFlagsDontCheckGeneration(OBJECT_FLAG_DYNAMIC_MASK)); + return !!(flagsDontCheckGeneration() & OBJECT_FLAG_UNKNOWN_PROPERTIES); + } + bool shouldPreTenure() { return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties(); } gc::InitialHeap initialHeap(CompilerConstraintList *constraints); bool canPreTenure() { return !unknownProperties();
--- a/js/src/vm/ProxyObject.h +++ b/js/src/vm/ProxyObject.h @@ -2,18 +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 vm_ProxyObject_h #define vm_ProxyObject_h -#include "jsproxy.h" - +#include "js/Proxy.h" #include "vm/NativeObject.h" namespace js { // This is the base class for the various kinds of proxy objects. It's never // instantiated. class ProxyObject : public JSObject {
--- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -15,30 +15,30 @@ #include "mozilla/Scoped.h" #include "mozilla/ThreadLocal.h" #include "mozilla/UniquePtr.h" #include <setjmp.h> #include "jsatom.h" #include "jsclist.h" -#ifdef DEBUG -# include "jsproxy.h" -#endif #include "jsscript.h" #ifdef XP_MACOSX # include "asmjs/AsmJSSignalHandlers.h" #endif #include "ds/FixedSizeHash.h" #include "frontend/ParseMaps.h" #include "gc/GCRuntime.h" #include "gc/Tracer.h" #include "irregexp/RegExpStack.h" #include "js/HashTable.h" +#ifdef DEBUG +# include "js/Proxy.h" // For AutoEnterPolicy +#endif #include "js/Vector.h" #include "vm/CommonPropertyNames.h" #include "vm/DateTime.h" #include "vm/MallocProvider.h" #include "vm/SPSProfiler.h" #include "vm/Stack.h" #include "vm/Symbol.h"
--- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -3704,17 +3704,17 @@ ConstraintTypeSet::sweep(Zone *zone, Aut *pentry = key; } else { oom.setOOM(); flags |= TYPE_FLAG_ANYOBJECT; clearObjects(); objectCount = 0; break; } - } else if (key->isGroup() && key->group()->unknownProperties()) { + } else if (key->isGroup() && key->group()->unknownPropertiesDontCheckGeneration()) { // Object sets containing objects with unknown properties might // not be complete. Mark the type set as unknown, which it will // be treated as during Ion compilation. flags |= TYPE_FLAG_ANYOBJECT; clearObjects(); objectCount = 0; break; } @@ -3722,17 +3722,17 @@ ConstraintTypeSet::sweep(Zone *zone, Aut setBaseObjectCount(objectCount); } else if (objectCount == 1) { ObjectKey *key = (ObjectKey *) objectSet; if (!IsAboutToBeFinalized(&key)) { objectSet = reinterpret_cast<ObjectKey **>(key); } else { // As above, mark type sets containing objects with unknown // properties as unknown. - if (key->isGroup() && key->group()->unknownProperties()) + if (key->isGroup() && key->group()->unknownPropertiesDontCheckGeneration()) flags |= TYPE_FLAG_ANYOBJECT; objectSet = nullptr; setBaseObjectCount(0); } } /* * Type constraints only hold weak references. Copy constraints referring
--- a/js/xpconnect/src/ExportHelpers.cpp +++ b/js/xpconnect/src/ExportHelpers.cpp @@ -3,19 +3,19 @@ /* 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 "xpcprivate.h" #include "WrapperFactory.h" #include "AccessCheck.h" #include "jsfriendapi.h" -#include "jsproxy.h" #include "jswrapper.h" #include "js/StructuredClone.h" +#include "js/Proxy.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/BlobBinding.h" #include "mozilla/dom/File.h" #include "nsGlobalWindow.h" #include "nsJSUtils.h" #include "nsIDOMFileList.h" using namespace mozilla;
--- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -5,17 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * The Components.Sandbox object. */ #include "AccessCheck.h" #include "jsfriendapi.h" -#include "jsproxy.h" +#include "js/Proxy.h" #include "js/StructuredClone.h" #include "nsContentUtils.h" #include "nsGlobalWindow.h" #include "nsIScriptContext.h" #include "nsIScriptObjectPrincipal.h" #include "nsIScriptSecurityManager.h" #include "nsIURI.h" #include "nsJSUtils.h"
--- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1531,18 +1531,19 @@ DetachedWrappedNativeProtoShutdownMarker void XPCJSRuntime::DestroyJSContextStack() { delete mJSContextStack; mJSContextStack = nullptr; } void XPCJSRuntime::SystemIsBeingShutDown() { - mDetachedWrappedNativeProtoMap-> - Enumerate(DetachedWrappedNativeProtoShutdownMarker, nullptr); + if (mDetachedWrappedNativeProtoMap) + mDetachedWrappedNativeProtoMap-> + Enumerate(DetachedWrappedNativeProtoShutdownMarker, nullptr); } #define JS_OPTIONS_DOT_STR "javascript.options." static void ReloadPrefsCallback(const char *pref, void *data) { XPCJSRuntime *runtime = reinterpret_cast<XPCJSRuntime *>(data); @@ -1614,43 +1615,61 @@ XPCJSRuntime::~XPCJSRuntime() if (mCallContext) mCallContext->SystemIsBeingShutDown(); auto rtPrivate = static_cast<PerThreadAtomCache*>(JS_GetRuntimePrivate(Runtime())); delete rtPrivate; JS_SetRuntimePrivate(Runtime(), nullptr); // clean up and destroy maps... - mWrappedJSMap->ShutdownMarker(); - delete mWrappedJSMap; - mWrappedJSMap = nullptr; - - delete mWrappedJSClassMap; - mWrappedJSClassMap = nullptr; - - delete mIID2NativeInterfaceMap; - mIID2NativeInterfaceMap = nullptr; - - delete mClassInfo2NativeSetMap; - mClassInfo2NativeSetMap = nullptr; - - delete mNativeSetMap; - mNativeSetMap = nullptr; - - delete mThisTranslatorMap; - mThisTranslatorMap = nullptr; - - delete mNativeScriptableSharedMap; - mNativeScriptableSharedMap = nullptr; - - delete mDyingWrappedNativeProtoMap; - mDyingWrappedNativeProtoMap = nullptr; - - delete mDetachedWrappedNativeProtoMap; - mDetachedWrappedNativeProtoMap = nullptr; + if (mWrappedJSMap) { + mWrappedJSMap->ShutdownMarker(); + delete mWrappedJSMap; + mWrappedJSMap = nullptr; + } + + if (mWrappedJSClassMap) { + delete mWrappedJSClassMap; + mWrappedJSClassMap = nullptr; + } + + if (mIID2NativeInterfaceMap) { + delete mIID2NativeInterfaceMap; + mIID2NativeInterfaceMap = nullptr; + } + + if (mClassInfo2NativeSetMap) { + delete mClassInfo2NativeSetMap; + mClassInfo2NativeSetMap = nullptr; + } + + if (mNativeSetMap) { + delete mNativeSetMap; + mNativeSetMap = nullptr; + } + + if (mThisTranslatorMap) { + delete mThisTranslatorMap; + mThisTranslatorMap = nullptr; + } + + if (mNativeScriptableSharedMap) { + delete mNativeScriptableSharedMap; + mNativeScriptableSharedMap = nullptr; + } + + if (mDyingWrappedNativeProtoMap) { + delete mDyingWrappedNativeProtoMap; + mDyingWrappedNativeProtoMap = nullptr; + } + + if (mDetachedWrappedNativeProtoMap) { + delete mDetachedWrappedNativeProtoMap; + mDetachedWrappedNativeProtoMap = nullptr; + } #ifdef MOZ_ENABLE_PROFILER_SPS // Tell the profiler that the runtime is gone if (PseudoStack *stack = mozilla_get_pseudo_stack()) stack->sampleRuntime(nullptr); #endif Preferences::UnregisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this); @@ -3490,48 +3509,52 @@ XPCJSRuntime::DebugDump(int16_t depth) while (JS_ContextIterator(Runtime(), &iter)) { XPCContext *xpc = XPCContext::GetXPCContext(iter); XPC_LOG_INDENT(); xpc->DebugDump(depth); XPC_LOG_OUTDENT(); } XPC_LOG_ALWAYS(("mWrappedJSClassMap @ %x with %d wrapperclasses(s)", \ - mWrappedJSClassMap, mWrappedJSClassMap->Count())); + mWrappedJSClassMap, mWrappedJSClassMap ? \ + mWrappedJSClassMap->Count() : 0)); // iterate wrappersclasses... - if (depth && mWrappedJSClassMap->Count()) { + if (depth && mWrappedJSClassMap && mWrappedJSClassMap->Count()) { XPC_LOG_INDENT(); mWrappedJSClassMap->Enumerate(WrappedJSClassMapDumpEnumerator, &depth); XPC_LOG_OUTDENT(); } XPC_LOG_ALWAYS(("mWrappedJSMap @ %x with %d wrappers(s)", \ - mWrappedJSMap, mWrappedJSMap->Count())); + mWrappedJSMap, mWrappedJSMap ? \ + mWrappedJSMap->Count() : 0)); // iterate wrappers... - if (depth && mWrappedJSMap->Count()) { + if (depth && mWrappedJSMap && mWrappedJSMap->Count()) { XPC_LOG_INDENT(); mWrappedJSMap->Dump(depth); XPC_LOG_OUTDENT(); } XPC_LOG_ALWAYS(("mIID2NativeInterfaceMap @ %x with %d interface(s)", \ - mIID2NativeInterfaceMap, - mIID2NativeInterfaceMap->Count())); + mIID2NativeInterfaceMap, mIID2NativeInterfaceMap ? \ + mIID2NativeInterfaceMap->Count() : 0)); XPC_LOG_ALWAYS(("mClassInfo2NativeSetMap @ %x with %d sets(s)", \ - mClassInfo2NativeSetMap, \ - mClassInfo2NativeSetMap->Count())); + mClassInfo2NativeSetMap, mClassInfo2NativeSetMap ? \ + mClassInfo2NativeSetMap->Count() : 0)); XPC_LOG_ALWAYS(("mThisTranslatorMap @ %x with %d translator(s)", \ - mThisTranslatorMap, mThisTranslatorMap->Count())); + mThisTranslatorMap, mThisTranslatorMap ? \ + mThisTranslatorMap->Count() : 0)); XPC_LOG_ALWAYS(("mNativeSetMap @ %x with %d sets(s)", \ - mNativeSetMap, mNativeSetMap->Count())); + mNativeSetMap, mNativeSetMap ? \ + mNativeSetMap->Count() : 0)); // iterate sets... - if (depth && mNativeSetMap->Count()) { + if (depth && mNativeSetMap && mNativeSetMap->Count()) { XPC_LOG_INDENT(); mNativeSetMap->Enumerate(NativeSetDumpEnumerator, &depth); XPC_LOG_OUTDENT(); } XPC_LOG_OUTDENT(); #endif }
--- a/js/xpconnect/src/XPCMaps.cpp +++ b/js/xpconnect/src/XPCMaps.cpp @@ -164,37 +164,44 @@ JSObject2WrappedJSMap::SizeOfWrappedJS(m /***************************************************************************/ // implement Native2WrappedNativeMap... // static Native2WrappedNativeMap* Native2WrappedNativeMap::newMap(int length) { - return new Native2WrappedNativeMap(length); + Native2WrappedNativeMap* map = new Native2WrappedNativeMap(length); + if (map && map->mTable) + return map; + // Allocation of the map or the creation of its hash table has + // failed. This will cause a nullptr deref later when we attempt + // to use the map, so we abort immediately to provide a more + // useful crash stack. + NS_RUNTIMEABORT("Ran out of memory."); + return nullptr; } Native2WrappedNativeMap::Native2WrappedNativeMap(int length) { - mTable = new PLDHashTable(); - PL_DHashTableInit(mTable, PL_DHashGetStubOps(), sizeof(Entry), length); + mTable = PL_NewDHashTable(PL_DHashGetStubOps(), sizeof(Entry), length); } Native2WrappedNativeMap::~Native2WrappedNativeMap() { - PL_DHashTableFinish(mTable); - delete mTable; + if (mTable) + PL_DHashTableDestroy(mTable); } size_t Native2WrappedNativeMap::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) { size_t n = 0; n += mallocSizeOf(this); - n += PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf); + n += mTable ? PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf) : 0; return n; } /* static */ size_t Native2WrappedNativeMap::SizeOfEntryExcludingThis(PLDHashEntryHdr *hdr, mozilla::MallocSizeOf mallocSizeOf, void *) { return mallocSizeOf(((Native2WrappedNativeMap::Entry*)hdr)->value); @@ -210,29 +217,32 @@ const struct PLDHashTableOps IID2Wrapped PL_DHashMoveEntryStub, PL_DHashClearEntryStub }; // static IID2WrappedJSClassMap* IID2WrappedJSClassMap::newMap(int length) { - return new IID2WrappedJSClassMap(length); + IID2WrappedJSClassMap* map = new IID2WrappedJSClassMap(length); + if (map && map->mTable) + return map; + delete map; + return nullptr; } IID2WrappedJSClassMap::IID2WrappedJSClassMap(int length) { - mTable = new PLDHashTable(); - PL_DHashTableInit(mTable, &Entry::sOps, sizeof(Entry), length); + mTable = PL_NewDHashTable(&Entry::sOps, sizeof(Entry), length); } IID2WrappedJSClassMap::~IID2WrappedJSClassMap() { - PL_DHashTableFinish(mTable); - delete mTable; + if (mTable) + PL_DHashTableDestroy(mTable); } /***************************************************************************/ // implement IID2NativeInterfaceMap... const struct PLDHashTableOps IID2NativeInterfaceMap::Entry::sOps = { @@ -241,37 +251,40 @@ const struct PLDHashTableOps IID2NativeI PL_DHashMoveEntryStub, PL_DHashClearEntryStub }; // static IID2NativeInterfaceMap* IID2NativeInterfaceMap::newMap(int length) { - return new IID2NativeInterfaceMap(length); + IID2NativeInterfaceMap* map = new IID2NativeInterfaceMap(length); + if (map && map->mTable) + return map; + delete map; + return nullptr; } IID2NativeInterfaceMap::IID2NativeInterfaceMap(int length) { - mTable = new PLDHashTable(); - PL_DHashTableInit(mTable, &Entry::sOps, sizeof(Entry), length); + mTable = PL_NewDHashTable(&Entry::sOps, sizeof(Entry), length); } IID2NativeInterfaceMap::~IID2NativeInterfaceMap() { - PL_DHashTableFinish(mTable); - delete mTable; + if (mTable) + PL_DHashTableDestroy(mTable); } size_t IID2NativeInterfaceMap::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) { size_t n = 0; n += mallocSizeOf(this); - n += PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf); + n += mTable ? PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf) : 0; return n; } /* static */ size_t IID2NativeInterfaceMap::SizeOfEntryExcludingThis(PLDHashEntryHdr *hdr, mozilla::MallocSizeOf mallocSizeOf, void *) { XPCNativeInterface *iface = ((IID2NativeInterfaceMap::Entry*)hdr)->value; @@ -280,69 +293,79 @@ IID2NativeInterfaceMap::SizeOfEntryExclu /***************************************************************************/ // implement ClassInfo2NativeSetMap... // static ClassInfo2NativeSetMap* ClassInfo2NativeSetMap::newMap(int length) { - return new ClassInfo2NativeSetMap(length); + ClassInfo2NativeSetMap* map = new ClassInfo2NativeSetMap(length); + if (map && map->mTable) + return map; + delete map; + return nullptr; } ClassInfo2NativeSetMap::ClassInfo2NativeSetMap(int length) { - mTable = new PLDHashTable(); - PL_DHashTableInit(mTable, PL_DHashGetStubOps(), sizeof(Entry), length); + mTable = PL_NewDHashTable(PL_DHashGetStubOps(), sizeof(Entry), length); } ClassInfo2NativeSetMap::~ClassInfo2NativeSetMap() { - PL_DHashTableFinish(mTable); - delete mTable; + if (mTable) + PL_DHashTableDestroy(mTable); } size_t ClassInfo2NativeSetMap::ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) { size_t n = 0; n += mallocSizeOf(this); // The second arg is nullptr because this is a "shallow" measurement of the map. - n += PL_DHashTableSizeOfIncludingThis(mTable, nullptr, mallocSizeOf); + n += mTable ? PL_DHashTableSizeOfIncludingThis(mTable, nullptr, mallocSizeOf) : 0; return n; } /***************************************************************************/ // implement ClassInfo2WrappedNativeProtoMap... // static ClassInfo2WrappedNativeProtoMap* ClassInfo2WrappedNativeProtoMap::newMap(int length) { - return new ClassInfo2WrappedNativeProtoMap(length); + ClassInfo2WrappedNativeProtoMap* map = new ClassInfo2WrappedNativeProtoMap(length); + if (map && map->mTable) + return map; + // Allocation of the map or the creation of its hash table has + // failed. This will cause a nullptr deref later when we attempt + // to use the map, so we abort immediately to provide a more + // useful crash stack. + NS_RUNTIMEABORT("Ran out of memory."); + return nullptr; } ClassInfo2WrappedNativeProtoMap::ClassInfo2WrappedNativeProtoMap(int length) { - mTable = new PLDHashTable(); - PL_DHashTableInit(mTable, PL_DHashGetStubOps(), sizeof(Entry), length); + mTable = PL_NewDHashTable(PL_DHashGetStubOps(), sizeof(Entry), length); } ClassInfo2WrappedNativeProtoMap::~ClassInfo2WrappedNativeProtoMap() { - PL_DHashTableFinish(mTable); - delete mTable; + if (mTable) + PL_DHashTableDestroy(mTable); } size_t ClassInfo2WrappedNativeProtoMap::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) { size_t n = 0; n += mallocSizeOf(this); - n += PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf); + n += mTable ? PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf) : 0; return n; } /* static */ size_t ClassInfo2WrappedNativeProtoMap::SizeOfEntryExcludingThis(PLDHashEntryHdr *hdr, mozilla::MallocSizeOf mallocSizeOf, void *) { return mallocSizeOf(((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value); @@ -430,37 +453,40 @@ const struct PLDHashTableOps NativeSetMa PL_DHashMoveEntryStub, PL_DHashClearEntryStub }; // static NativeSetMap* NativeSetMap::newMap(int length) { - return new NativeSetMap(length); + NativeSetMap* map = new NativeSetMap(length); + if (map && map->mTable) + return map; + delete map; + return nullptr; } NativeSetMap::NativeSetMap(int length) { - mTable = new PLDHashTable(); - PL_DHashTableInit(mTable, &Entry::sOps, sizeof(Entry), length); + mTable = PL_NewDHashTable(&Entry::sOps, sizeof(Entry), length); } NativeSetMap::~NativeSetMap() { - PL_DHashTableFinish(mTable); - delete mTable; + if (mTable) + PL_DHashTableDestroy(mTable); } size_t NativeSetMap::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) { size_t n = 0; n += mallocSizeOf(this); - n += PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf); + n += mTable ? PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf) : 0; return n; } /* static */ size_t NativeSetMap::SizeOfEntryExcludingThis(PLDHashEntryHdr *hdr, mozilla::MallocSizeOf mallocSizeOf, void *) { XPCNativeSet *set = ((NativeSetMap::Entry*)hdr)->key_value; return set->SizeOfIncludingThis(mallocSizeOf); @@ -491,29 +517,32 @@ const struct PLDHashTableOps IID2ThisTra PL_DHashMoveEntryStub, Clear }; // static IID2ThisTranslatorMap* IID2ThisTranslatorMap::newMap(int length) { - return new IID2ThisTranslatorMap(length); + IID2ThisTranslatorMap* map = new IID2ThisTranslatorMap(length); + if (map && map->mTable) + return map; + delete map; + return nullptr; } IID2ThisTranslatorMap::IID2ThisTranslatorMap(int length) { - mTable = new PLDHashTable(); - PL_DHashTableInit(mTable, &Entry::sOps, sizeof(Entry), length); + mTable = PL_NewDHashTable(&Entry::sOps, sizeof(Entry), length); } IID2ThisTranslatorMap::~IID2ThisTranslatorMap() { - PL_DHashTableFinish(mTable); - delete mTable; + if (mTable) + PL_DHashTableDestroy(mTable); } /***************************************************************************/ PLDHashNumber XPCNativeScriptableSharedMap::Entry::Hash(PLDHashTable *table, const void *key) { PLDHashNumber h; @@ -564,42 +593,45 @@ const struct PLDHashTableOps XPCNativeSc PL_DHashMoveEntryStub, PL_DHashClearEntryStub }; // static XPCNativeScriptableSharedMap* XPCNativeScriptableSharedMap::newMap(int length) { - return new XPCNativeScriptableSharedMap(length); + XPCNativeScriptableSharedMap* map = + new XPCNativeScriptableSharedMap(length); + if (map && map->mTable) + return map; + delete map; + return nullptr; } XPCNativeScriptableSharedMap::XPCNativeScriptableSharedMap(int length) { - mTable = new PLDHashTable(); - PL_DHashTableInit(mTable, &Entry::sOps, sizeof(Entry), length); + mTable = PL_NewDHashTable(&Entry::sOps, sizeof(Entry), length); } XPCNativeScriptableSharedMap::~XPCNativeScriptableSharedMap() { - PL_DHashTableFinish(mTable); - delete mTable; + if (mTable) + PL_DHashTableDestroy(mTable); } bool XPCNativeScriptableSharedMap::GetNewOrUsed(uint32_t flags, char* name, XPCNativeScriptableInfo* si) { NS_PRECONDITION(name,"bad param"); NS_PRECONDITION(si,"bad param"); XPCNativeScriptableShared key(flags, name); - Entry* entry = static_cast<Entry*> - (PL_DHashTableAdd(mTable, &key, fallible)); + Entry* entry = (Entry*) PL_DHashTableAdd(mTable, &key); if (!entry) return false; XPCNativeScriptableShared* shared = entry->key; if (!shared) { entry->key = shared = new XPCNativeScriptableShared(flags, key.TransferNameOwnership()); @@ -613,25 +645,28 @@ XPCNativeScriptableSharedMap::GetNewOrUs /***************************************************************************/ // implement XPCWrappedNativeProtoMap... // static XPCWrappedNativeProtoMap* XPCWrappedNativeProtoMap::newMap(int length) { - return new XPCWrappedNativeProtoMap(length); + XPCWrappedNativeProtoMap* map = new XPCWrappedNativeProtoMap(length); + if (map && map->mTable) + return map; + delete map; + return nullptr; } XPCWrappedNativeProtoMap::XPCWrappedNativeProtoMap(int length) { - mTable = new PLDHashTable(); - PL_DHashTableInit(mTable, PL_DHashGetStubOps(), sizeof(PLDHashEntryStub), - length); + mTable = PL_NewDHashTable(PL_DHashGetStubOps(), + sizeof(PLDHashEntryStub), length); } XPCWrappedNativeProtoMap::~XPCWrappedNativeProtoMap() { - PL_DHashTableFinish(mTable); - delete mTable; + if (mTable) + PL_DHashTableDestroy(mTable); } /***************************************************************************/
--- a/js/xpconnect/src/XPCMaps.h +++ b/js/xpconnect/src/XPCMaps.h @@ -27,17 +27,17 @@ class JSObject2WrappedJSMap { typedef js::HashMap<JSObject*, nsXPCWrappedJS*, js::PointerHasher<JSObject*, 3>, js::SystemAllocPolicy> Map; public: static JSObject2WrappedJSMap* newMap(int length) { JSObject2WrappedJSMap* map = new JSObject2WrappedJSMap(); - if (map->mTable.init(length)) + if (map && map->mTable.init(length)) return map; delete map; return nullptr; } inline nsXPCWrappedJS* Find(JSObject* Obj) { NS_PRECONDITION(Obj,"bad param"); Map::Ptr p = mTable.lookup(Obj); @@ -115,18 +115,17 @@ public: return entry ? entry->value : nullptr; } inline XPCWrappedNative* Add(XPCWrappedNative* wrapper) { NS_PRECONDITION(wrapper,"bad param"); nsISupports* obj = wrapper->GetIdentityObject(); MOZ_ASSERT(!Find(obj), "wrapper already in new scope!"); - Entry* entry = static_cast<Entry*> - (PL_DHashTableAdd(mTable, obj, mozilla::fallible)); + Entry* entry = (Entry*) PL_DHashTableAdd(mTable, obj); if (!entry) return nullptr; if (entry->key) return entry->value; entry->key = obj; entry->value = wrapper; return wrapper; } @@ -181,18 +180,17 @@ public: Entry* entry = (Entry*) PL_DHashTableSearch(mTable, &iid); return entry ? entry->value : nullptr; } inline nsXPCWrappedJSClass* Add(nsXPCWrappedJSClass* clazz) { NS_PRECONDITION(clazz,"bad param"); const nsIID* iid = &clazz->GetIID(); - Entry* entry = static_cast<Entry*> - (PL_DHashTableAdd(mTable, iid, mozilla::fallible)); + Entry* entry = (Entry*) PL_DHashTableAdd(mTable, iid); if (!entry) return nullptr; if (entry->key) return entry->value; entry->key = iid; entry->value = clazz; return clazz; } @@ -235,18 +233,17 @@ public: Entry* entry = (Entry*) PL_DHashTableSearch(mTable, &iid); return entry ? entry->value : nullptr; } inline XPCNativeInterface* Add(XPCNativeInterface* iface) { NS_PRECONDITION(iface,"bad param"); const nsIID* iid = iface->GetIID(); - Entry* entry = static_cast<Entry*> - (PL_DHashTableAdd(mTable, iid, mozilla::fallible)); + Entry* entry = (Entry*) PL_DHashTableAdd(mTable, iid); if (!entry) return nullptr; if (entry->key) return entry->value; entry->key = iid; entry->value = iface; return iface; } @@ -291,18 +288,17 @@ public: { Entry* entry = (Entry*) PL_DHashTableSearch(mTable, info); return entry ? entry->value : nullptr; } inline XPCNativeSet* Add(nsIClassInfo* info, XPCNativeSet* set) { NS_PRECONDITION(info,"bad param"); - Entry* entry = static_cast<Entry*> - (PL_DHashTableAdd(mTable, info, mozilla::fallible)); + Entry* entry = (Entry*) PL_DHashTableAdd(mTable, info); if (!entry) return nullptr; if (entry->key) return entry->value; entry->key = info; entry->value = set; return set; } @@ -348,18 +344,17 @@ public: { Entry* entry = (Entry*) PL_DHashTableSearch(mTable, info); return entry ? entry->value : nullptr; } inline XPCWrappedNativeProto* Add(nsIClassInfo* info, XPCWrappedNativeProto* proto) { NS_PRECONDITION(info,"bad param"); - Entry* entry = static_cast<Entry*> - (PL_DHashTableAdd(mTable, info, mozilla::fallible)); + Entry* entry = (Entry*) PL_DHashTableAdd(mTable, info); if (!entry) return nullptr; if (entry->key) return entry->value; entry->key = info; entry->value = proto; return proto; } @@ -411,18 +406,17 @@ public: Entry* entry = (Entry*) PL_DHashTableSearch(mTable, key); return entry ? entry->key_value : nullptr; } inline XPCNativeSet* Add(const XPCNativeSetKey* key, XPCNativeSet* set) { NS_PRECONDITION(key,"bad param"); NS_PRECONDITION(set,"bad param"); - Entry* entry = static_cast<Entry*> - (PL_DHashTableAdd(mTable, key, mozilla::fallible)); + Entry* entry = (Entry*) PL_DHashTableAdd(mTable, key); if (!entry) return nullptr; if (entry->key_value) return entry->key_value; entry->key_value = set; return set; } @@ -484,18 +478,18 @@ public: { Entry* entry = (Entry*) PL_DHashTableSearch(mTable, &iid); return entry ? entry->value : nullptr; } inline nsIXPCFunctionThisTranslator* Add(REFNSIID iid, nsIXPCFunctionThisTranslator* obj) { - Entry* entry = static_cast<Entry*> - (PL_DHashTableAdd(mTable, &iid, mozilla::fallible)); + + Entry* entry = (Entry*) PL_DHashTableAdd(mTable, &iid); if (!entry) return nullptr; entry->value = obj; entry->key = iid; return obj; } inline void Remove(REFNSIID iid) @@ -556,18 +550,18 @@ private: class XPCWrappedNativeProtoMap { public: static XPCWrappedNativeProtoMap* newMap(int length); inline XPCWrappedNativeProto* Add(XPCWrappedNativeProto* proto) { NS_PRECONDITION(proto,"bad param"); - PLDHashEntryStub* entry = static_cast<PLDHashEntryStub*> - (PL_DHashTableAdd(mTable, proto, mozilla::fallible)); + PLDHashEntryStub* entry = (PLDHashEntryStub*) + PL_DHashTableAdd(mTable, proto); if (!entry) return nullptr; if (entry->key) return (XPCWrappedNativeProto*) entry->key; entry->key = proto; return proto; }
--- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -3,19 +3,19 @@ /* 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 xpcpublic_h #define xpcpublic_h #include "jsapi.h" -#include "jsproxy.h" #include "js/HeapAPI.h" #include "js/GCAPI.h" +#include "js/Proxy.h" #include "nsISupports.h" #include "nsIURI.h" #include "nsIPrincipal.h" #include "nsIGlobalObject.h" #include "nsPIDOMWindow.h" #include "nsWrapperCache.h" #include "nsStringGlue.h"
--- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -4110,17 +4110,20 @@ FrameLayerBuilder::BuildContainerLayerFo LayerManagerData* data = static_cast<LayerManagerData*> (aManager->GetUserData(&gLayerManagerUserData)); nsIntRect pixBounds; nscoord appUnitsPerDevPixel; bool flattenToSingleLayer = false; if ((aContainerFrame->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) && - mRetainingManager && mRetainingManager->ShouldAvoidComponentAlphaLayers()) { + mRetainingManager && + mRetainingManager->ShouldAvoidComponentAlphaLayers() && + !gfxPrefs::AsyncPanZoomEnabled()) + { flattenToSingleLayer = true; } uint32_t flags; while (true) { ContainerState state(aBuilder, aManager, aManager->GetLayerBuilder(), aContainerFrame, aContainerItem, bounds, containerLayer, scaleParameters, flattenToSingleLayer); @@ -4133,17 +4136,19 @@ FrameLayerBuilder::BuildContainerLayerFo pixBounds = state.ScaleToOutsidePixels(bounds, false); appUnitsPerDevPixel = state.GetAppUnitsPerDevPixel(); state.Finish(&flags, data, pixBounds, aChildren, hasComponentAlphaChildren); if (hasComponentAlphaChildren && mRetainingManager && mRetainingManager->ShouldAvoidComponentAlphaLayers() && containerLayer->HasMultipleChildren() && - !flattenToSingleLayer) { + !flattenToSingleLayer && + !gfxPrefs::AsyncPanZoomEnabled()) + { // Since we don't want any component alpha layers on BasicLayers, we repeat // the layer building process with this explicitely forced off. // We restore the previous FrameLayerBuilder state since the first set // of layer building will have changed it. flattenToSingleLayer = true; data->mDisplayItems.EnumerateEntries(RestoreDisplayItemData, &mContainerLayerGeneration); mPaintedLayerItems.EnumerateEntries(RestorePaintedLayerItemEntries,
--- a/layout/base/SelectionCarets.cpp +++ b/layout/base/SelectionCarets.cpp @@ -1121,16 +1121,18 @@ SelectionCarets::DispatchSelectionStateC void SelectionCarets::NotifyBlur(bool aIsLeavingDocument) { SELECTIONCARETS_LOG("Send out the blur event"); SetVisibility(false); if (aIsLeavingDocument) { CancelLongTapDetector(); } + CancelScrollEndDetector(); + mInAsyncPanZoomGesture = false; DispatchSelectionStateChangedEvent(nullptr, SelectionState::Blur); } nsresult SelectionCarets::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection* aSel, int16_t aReason) { @@ -1173,39 +1175,49 @@ DispatchScrollViewChangeEvent(nsIPresShe event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true; doc->DispatchEvent(event, &ret); } } void SelectionCarets::AsyncPanZoomStarted(const mozilla::CSSIntPoint aScrollPos) { - mInAsyncPanZoomGesture = true; - SetVisibility(false); + if (mVisible) { + mInAsyncPanZoomGesture = true; + SetVisibility(false); - SELECTIONCARETS_LOG("Dispatch scroll started with position x=%d, y=%d", - aScrollPos.x, aScrollPos.y); - DispatchScrollViewChangeEvent(mPresShell, dom::ScrollState::Started, aScrollPos); + SELECTIONCARETS_LOG("Dispatch scroll started with position x=%d, y=%d", + aScrollPos.x, aScrollPos.y); + DispatchScrollViewChangeEvent(mPresShell, dom::ScrollState::Started, aScrollPos); + } else { + nsRefPtr<dom::Selection> selection = GetSelection(); + if (selection && selection->RangeCount() && selection->IsCollapsed()) { + mInAsyncPanZoomGesture = true; + DispatchScrollViewChangeEvent(mPresShell, dom::ScrollState::Started, aScrollPos); + } + } } void SelectionCarets::AsyncPanZoomStopped(const mozilla::CSSIntPoint aScrollPos) { - mInAsyncPanZoomGesture = false; - SELECTIONCARETS_LOG("Update selection carets after APZ is stopped!"); - UpdateSelectionCarets(); + if (mInAsyncPanZoomGesture) { + mInAsyncPanZoomGesture = false; + SELECTIONCARETS_LOG("Update selection carets after APZ is stopped!"); + UpdateSelectionCarets(); - // SelectionStateChangedEvent should be dispatched before ScrollViewChangeEvent. - DispatchSelectionStateChangedEvent(GetSelection(), - SelectionState::Updateposition); + // SelectionStateChangedEvent should be dispatched before ScrollViewChangeEvent. + DispatchSelectionStateChangedEvent(GetSelection(), + SelectionState::Updateposition); - SELECTIONCARETS_LOG("Dispatch scroll stopped with position x=%d, y=%d", - aScrollPos.x, aScrollPos.y); + SELECTIONCARETS_LOG("Dispatch scroll stopped with position x=%d, y=%d", + aScrollPos.x, aScrollPos.y); - DispatchScrollViewChangeEvent(mPresShell, dom::ScrollState::Stopped, aScrollPos); + DispatchScrollViewChangeEvent(mPresShell, dom::ScrollState::Stopped, aScrollPos); + } } void SelectionCarets::ScrollPositionChanged() { if (mVisible) { if (!mAsyncPanZoomEnabled) { SetVisibility(false); @@ -1292,16 +1304,28 @@ SelectionCarets::LaunchScrollEndDetector SELECTIONCARETS_LOG("Will fire scroll end after %d ms", kScrollEndTimerDelay); mScrollEndDetectorTimer->InitWithFuncCallback(FireScrollEnd, this, kScrollEndTimerDelay, nsITimer::TYPE_ONE_SHOT); } +void +SelectionCarets::CancelScrollEndDetector() +{ + if (!mScrollEndDetectorTimer) { + return; + } + + SELECTIONCARETS_LOG("Cancel scroll end detector!"); + mScrollEndDetectorTimer->Cancel(); +} + + /* static */void SelectionCarets::FireScrollEnd(nsITimer* aTimer, void* aSelectionCarets) { nsRefPtr<SelectionCarets> self = static_cast<SelectionCarets*>(aSelectionCarets); NS_PRECONDITION(aTimer == self->mScrollEndDetectorTimer, "Unexpected timer"); SELECTIONCARETS_LOG_STATIC("Update selection carets!");
--- a/layout/base/SelectionCarets.h +++ b/layout/base/SelectionCarets.h @@ -213,16 +213,17 @@ private: /** * Detecting long tap using timer */ void LaunchLongTapDetector(); void CancelLongTapDetector(); static void FireLongTap(nsITimer* aTimer, void* aSelectionCarets); void LaunchScrollEndDetector(); + void CancelScrollEndDetector(); static void FireScrollEnd(nsITimer* aTimer, void* aSelectionCarets); nsIPresShell* mPresShell; WeakPtr<nsDocShell> mDocShell; // This timer is used for detecting long tap fire. If content process // has APZC, we'll use APZC for long tap detecting. Otherwise, we use this // timer to detect long tap.
--- a/layout/base/nsFrameManager.cpp +++ b/layout/base/nsFrameManager.cpp @@ -170,19 +170,18 @@ nsFrameManager::RegisterPlaceholderFrame { NS_PRECONDITION(aPlaceholderFrame, "null param unexpected"); NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(), "unexpected frame type"); if (!mPlaceholderMap.IsInitialized()) { PL_DHashTableInit(&mPlaceholderMap, &PlaceholderMapOps, sizeof(PlaceholderMapEntry)); } - PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*> - (PL_DHashTableAdd(&mPlaceholderMap, - aPlaceholderFrame->GetOutOfFlowFrame(), fallible)); + PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*>(PL_DHashTableAdd(&mPlaceholderMap, + aPlaceholderFrame->GetOutOfFlowFrame())); if (!entry) return NS_ERROR_OUT_OF_MEMORY; NS_ASSERTION(!entry->placeholderFrame, "Registering a placeholder for a frame that already has a placeholder!"); entry->placeholderFrame = aPlaceholderFrame; return NS_OK; }
--- a/layout/generic/nsLineLayout.h +++ b/layout/generic/nsLineLayout.h @@ -302,16 +302,21 @@ public: // Retrieve last set optional break position. When this returns null, no // optional break has been recorded (which means that the line can't break yet). nsIFrame* GetLastOptionalBreakPosition(int32_t* aOffset, gfxBreakPriority* aPriority) { *aOffset = mLastOptionalBreakFrameOffset; *aPriority = mLastOptionalBreakPriority; return mLastOptionalBreakFrame; } + // Whether any optional break position has been recorded. + bool HasOptionalBreakPosition() const + { + return mLastOptionalBreakFrame != nullptr; + } /** * Check whether frames overflowed the available width and CanPlaceFrame * requested backing up to a saved break position. */ bool NeedsBackup() { return mNeedBackup; } // Line layout may place too much content on a line, overflowing its available
--- a/layout/generic/nsRubyBaseContainerFrame.cpp +++ b/layout/generic/nsRubyBaseContainerFrame.cpp @@ -9,16 +9,17 @@ #include "nsRubyBaseContainerFrame.h" #include "nsContentUtils.h" #include "nsLineLayout.h" #include "nsPresContext.h" #include "nsStyleContext.h" #include "nsStyleStructInlines.h" #include "WritingModes.h" #include "RubyUtils.h" +#include "nsTextFrame.h" #include "mozilla/Maybe.h" #include "mozilla/DebugOnly.h" using namespace mozilla; //---------------------------------------------------------------------- // Frame class boilerplate @@ -305,34 +306,23 @@ nsRubyBaseContainerFrame::ComputeSize(ns /* virtual */ nscoord nsRubyBaseContainerFrame::GetLogicalBaseline(WritingMode aWritingMode) const { return mBaseline; } struct nsRubyBaseContainerFrame::ReflowState { + bool mAllowInitialLineBreak; bool mAllowLineBreak; const TextContainerArray& mTextContainers; const nsHTMLReflowState& mBaseReflowState; const nsTArray<UniquePtr<nsHTMLReflowState>>& mTextReflowStates; }; -// Check whether the given extra isize can fit in the line in base level. -static bool -ShouldBreakBefore(const nsHTMLReflowState& aReflowState, nscoord aExtraISize) -{ - nsLineLayout* lineLayout = aReflowState.mLineLayout; - int32_t offset; - gfxBreakPriority priority; - nscoord icoord = lineLayout->GetCurrentICoord(); - return icoord + aExtraISize > aReflowState.AvailableISize() && - lineLayout->GetLastOptionalBreakPosition(&offset, &priority); -} - /* virtual */ void nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsRubyBaseContainerFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); @@ -413,66 +403,50 @@ nsRubyBaseContainerFrame::Reflow(nsPresC // Allow line break between ruby bases when white-space allows, // we are not inside a nested ruby, and there is no span. bool allowLineBreak = !inNestedRuby && StyleText()->WhiteSpaceCanWrap(this); bool allowInitialLineBreak = allowLineBreak; if (!GetPrevInFlow()) { allowInitialLineBreak = !inNestedRuby && parent->StyleText()->WhiteSpaceCanWrap(parent); } - if (allowInitialLineBreak && aReflowState.mLineLayout->LineIsBreakable() && - aReflowState.mLineLayout->NotifyOptionalBreakPosition( - this, 0, 0 <= aReflowState.AvailableISize(), - gfxBreakPriority::eNormalBreak)) { - aStatus = NS_INLINE_LINE_BREAK_BEFORE(); + if (!aReflowState.mLineLayout->LineIsBreakable()) { + allowInitialLineBreak = false; } nscoord isize = 0; - if (aStatus == NS_FRAME_COMPLETE) { - // Reflow columns excluding any span - ReflowState reflowState = { - allowLineBreak && !hasSpan, textContainers, aReflowState, reflowStates - }; - isize = ReflowColumns(reflowState, aStatus); - } + // Reflow columns excluding any span + ReflowState reflowState = { + allowInitialLineBreak, allowLineBreak && !hasSpan, + textContainers, aReflowState, reflowStates + }; + isize = ReflowColumns(reflowState, aStatus); DebugOnly<nscoord> lineSpanSize = aReflowState.mLineLayout->EndSpan(this); aDesiredSize.ISize(lineWM) = isize; // When there are no frames inside the ruby base container, EndSpan // will return 0. However, in this case, the actual width of the // container could be non-zero because of non-empty ruby annotations. - MOZ_ASSERT(NS_INLINE_IS_BREAK_BEFORE(aStatus) || + MOZ_ASSERT(NS_INLINE_IS_BREAK(aStatus) || isize == lineSpanSize || mFrames.IsEmpty()); // If there exists any span, the columns must either be completely // reflowed, or be not reflowed at all. MOZ_ASSERT(NS_INLINE_IS_BREAK_BEFORE(aStatus) || NS_FRAME_IS_COMPLETE(aStatus) || !hasSpan); if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) && NS_FRAME_IS_COMPLETE(aStatus) && hasSpan) { // Reflow spans ReflowState reflowState = { - false, textContainers, aReflowState, reflowStates + false, false, textContainers, aReflowState, reflowStates }; nscoord spanISize = ReflowSpans(reflowState); - nscoord deltaISize = spanISize - isize; - if (deltaISize > 0) { - if (allowLineBreak && ShouldBreakBefore(aReflowState, deltaISize)) { - aStatus = NS_INLINE_LINE_BREAK_BEFORE(); - } else { - isize = spanISize; - } - } - // When there are spans, ReflowColumns and ReflowOneColumn won't - // record any optional break position. We have to record one - // at the end of this segment. - if (!NS_INLINE_IS_BREAK(aStatus) && allowLineBreak && - aReflowState.mLineLayout->NotifyOptionalBreakPosition( - this, INT32_MAX, isize <= aReflowState.AvailableISize(), - gfxBreakPriority::eNormalBreak)) { - aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus); + isize = std::max(isize, spanISize); + if (isize > aReflowState.AvailableISize() && + aReflowState.mLineLayout->HasOptionalBreakPosition()) { + aStatus = NS_INLINE_LINE_BREAK_BEFORE(); } } for (uint32_t i = 0; i < rtcCount; i++) { // It happens before the ruby text container is reflowed, and that // when it is reflowed, it will just use this size. nsRubyTextContainerFrame* textContainer = textContainers[i]; nsLineLayout* lineLayout = lineLayouts[i].get(); @@ -622,25 +596,75 @@ nsRubyBaseContainerFrame::ReflowColumns( // not need to push anything. MOZ_ASSERT(e.AtEnd()); aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus); } return icoord; } +static gfxBreakPriority +LineBreakBefore(const nsHTMLReflowState& aReflowState, nsRubyBaseFrame* aFrame) +{ + for (nsIFrame* child = aFrame; child; + child = child->GetFirstPrincipalChild()) { + if (!child->CanContinueTextRun()) { + // It is not an inline element. We can break before it. + return gfxBreakPriority::eNormalBreak; + } + if (child->GetType() != nsGkAtoms::textFrame) { + continue; + } + + auto textFrame = static_cast<nsTextFrame*>(child); + gfxSkipCharsIterator iter = + textFrame->EnsureTextRun(nsTextFrame::eInflated, + aReflowState.rendContext->ThebesContext(), + aReflowState.mLineLayout->LineContainerFrame(), + aReflowState.mLineLayout->GetLine()); + iter.SetOriginalOffset(textFrame->GetContentOffset()); + uint32_t pos = iter.GetSkippedOffset(); + gfxTextRun* textRun = textFrame->GetTextRun(nsTextFrame::eInflated); + // Return whether we can break before the first character. + if (textRun->CanBreakLineBefore(pos)) { + return gfxBreakPriority::eNormalBreak; + } + // Check whether we can wrap word here. + const nsStyleText* textStyle = textFrame->StyleText(); + if (textStyle->WordCanWrap(textFrame) && textRun->IsClusterStart(pos)) { + return gfxBreakPriority::eWordWrapBreak; + } + // We cannot break before. + return gfxBreakPriority::eNoBreak; + } + // Neither block, nor text frame is found as a leaf. We won't break + // before this base frame. It is the behavior of empty spans. + return gfxBreakPriority::eNoBreak; +} + nscoord nsRubyBaseContainerFrame::ReflowOneColumn(const ReflowState& aReflowState, uint32_t aColumnIndex, const RubyColumn& aColumn, nsReflowStatus& aStatus) { const nsHTMLReflowState& baseReflowState = aReflowState.mBaseReflowState; const auto& textReflowStates = aReflowState.mTextReflowStates; + if (aColumn.mBaseFrame) { + int32_t pos = baseReflowState.mLineLayout-> + GetForcedBreakPosition(aColumn.mBaseFrame); + MOZ_ASSERT(pos == -1 || pos == 0, + "It should either break before, or not break at all."); + if (pos >= 0) { + aStatus = NS_INLINE_LINE_BREAK_BEFORE(); + return 0; + } + } + const uint32_t rtcCount = aReflowState.mTextContainers.Length(); MOZ_ASSERT(aColumn.mTextFrames.Length() == rtcCount); MOZ_ASSERT(textReflowStates.Length() == rtcCount); nscoord istart = baseReflowState.mLineLayout->GetCurrentICoord(); nscoord columnISize = 0; nsAutoString baseText; if (aColumn.mBaseFrame) { @@ -677,43 +701,65 @@ nsRubyBaseContainerFrame::ReflowOneColum nscoord textIStart = lineLayout->GetCurrentICoord(); lineLayout->ReflowFrame(textFrame, reflowStatus, nullptr, pushedFrame); MOZ_ASSERT(!NS_INLINE_IS_BREAK(reflowStatus) && !pushedFrame, "Any line break inside ruby box should has been suppressed"); nscoord textISize = lineLayout->GetCurrentICoord() - textIStart; columnISize = std::max(columnISize, textISize); } } - if (aReflowState.mAllowLineBreak && - ShouldBreakBefore(baseReflowState, columnISize)) { - // Since ruby text container uses an independent line layout, it - // may successfully place a frame because the line is empty while - // the line of base container is not. - aStatus = NS_INLINE_LINE_BREAK_BEFORE(); - return 0; - } // Reflow the base frame if (aColumn.mBaseFrame) { RubyUtils::ClearReservedISize(aColumn.mBaseFrame); bool pushedFrame; nsReflowStatus reflowStatus; nsLineLayout* lineLayout = baseReflowState.mLineLayout; nscoord baseIStart = lineLayout->GetCurrentICoord(); lineLayout->ReflowFrame(aColumn.mBaseFrame, reflowStatus, nullptr, pushedFrame); MOZ_ASSERT(!NS_INLINE_IS_BREAK(reflowStatus) && !pushedFrame, "Any line break inside ruby box should has been suppressed"); nscoord baseISize = lineLayout->GetCurrentICoord() - baseIStart; columnISize = std::max(columnISize, baseISize); + + bool allowBreakBefore = aColumnIndex ? + aReflowState.mAllowLineBreak : aReflowState.mAllowInitialLineBreak; + if (allowBreakBefore) { + bool shouldBreakBefore = false; + gfxBreakPriority breakPriority = + LineBreakBefore(baseReflowState, aColumn.mBaseFrame); + if (breakPriority != gfxBreakPriority::eNoBreak) { + int32_t offset; + gfxBreakPriority lastBreakPriority; + baseReflowState.mLineLayout-> + GetLastOptionalBreakPosition(&offset, &lastBreakPriority); + shouldBreakBefore = breakPriority >= lastBreakPriority; + } + if (shouldBreakBefore) { + bool fits = istart <= baseReflowState.AvailableISize(); + DebugOnly<bool> breakBefore = + baseReflowState.mLineLayout->NotifyOptionalBreakPosition( + aColumn.mBaseFrame, 0, fits, breakPriority); + MOZ_ASSERT(!breakBefore, "The break notified here should have " + "triggered at the start of this method."); + } + } + } + + nscoord icoord = istart + columnISize; + // If we can break here, do it now. + if (icoord > baseReflowState.AvailableISize() && + baseReflowState.mLineLayout->HasOptionalBreakPosition()) { + aStatus = NS_INLINE_LINE_BREAK_BEFORE(); + return 0; } // Align all the line layout to the new coordinate. - nscoord icoord = istart + columnISize; nscoord deltaISize = icoord - baseReflowState.mLineLayout->GetCurrentICoord(); if (deltaISize > 0) { baseReflowState.mLineLayout->AdvanceICoord(deltaISize); if (aColumn.mBaseFrame) { RubyUtils::SetReservedISize(aColumn.mBaseFrame, deltaISize); } } for (uint32_t i = 0; i < rtcCount; i++) { @@ -729,23 +775,16 @@ nsRubyBaseContainerFrame::ReflowOneColum RubyUtils::SetReservedISize(textFrame, deltaISize); } } if (aColumn.mBaseFrame && textFrame) { lineLayout->AttachLastFrameToBaseLineLayout(); } } - if (aReflowState.mAllowLineBreak && - baseReflowState.mLineLayout->NotifyOptionalBreakPosition( - this, aColumnIndex + 1, icoord <= baseReflowState.AvailableISize(), - gfxBreakPriority::eNormalBreak)) { - aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus); - } - return columnISize; } nsRubyBaseContainerFrame::PullFrameState::PullFrameState( nsRubyBaseContainerFrame* aBaseContainer, const TextContainerArray& aTextContainers) : mBase(aBaseContainer) , mTextContainers(aTextContainers)
--- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -1144,40 +1144,38 @@ CanTextCrossFrameBoundary(nsIFrame* aFra result.mFrameToScan = aFrame->GetFirstPrincipalChild(); result.mOverflowFrameToScan = aFrame->GetFirstChild(nsIFrame::kOverflowList); NS_WARN_IF_FALSE(!result.mOverflowFrameToScan, "Scanning overflow inline frames is something we should avoid"); result.mScanSiblings = true; result.mTextRunCanCrossFrameBoundary = true; result.mLineBreakerCanCrossFrameBoundary = true; - } else if (aFrame->GetType() == nsGkAtoms::rubyTextFrame || - aFrame->GetType() == nsGkAtoms::rubyTextContainerFrame) { - result.mFrameToScan = aFrame->GetFirstPrincipalChild(); - result.mOverflowFrameToScan = - aFrame->GetFirstChild(nsIFrame::kOverflowList); - NS_WARN_IF_FALSE(!result.mOverflowFrameToScan, - "Scanning overflow inline frames is something we should avoid"); - result.mScanSiblings = true; - result.mTextRunCanCrossFrameBoundary = false; - result.mLineBreakerCanCrossFrameBoundary = false; } else { + MOZ_ASSERT(aType != nsGkAtoms::rubyTextContainerFrame, + "Shouldn't call this method for ruby text container"); result.mFrameToScan = nullptr; result.mOverflowFrameToScan = nullptr; result.mTextRunCanCrossFrameBoundary = false; result.mLineBreakerCanCrossFrameBoundary = false; } } return result; } BuildTextRunsScanner::FindBoundaryResult BuildTextRunsScanner::FindBoundaries(nsIFrame* aFrame, FindBoundaryState* aState) { nsIAtom* frameType = aFrame->GetType(); + if (frameType == nsGkAtoms::rubyTextContainerFrame) { + // Don't stop a text run for ruby text container. We want ruby text + // containers to be skipped, but continue the text run across them. + return FB_CONTINUE; + } + nsTextFrame* textFrame = frameType == nsGkAtoms::textFrame ? static_cast<nsTextFrame*>(aFrame) : nullptr; if (textFrame) { if (aState->mLastTextFrame && textFrame != aState->mLastTextFrame->GetNextInFlow() && !ContinueTextRunAcrossFrames(aState->mLastTextFrame, textFrame)) { aState->mSeenTextRunBoundaryOnThisLine = true; if (aState->mSeenSpaceForLineBreakingOnThisLine) @@ -1658,36 +1656,41 @@ BuildTextRunsScanner::ContinueTextRunAcr sc1->StyleFont()->mLanguage == sc2->StyleFont()->mLanguage && textStyle1->mTextTransform == textStyle2->mTextTransform && nsLayoutUtils::GetTextRunFlagsForStyle(sc1, fontStyle1, textStyle1, letterSpacing1) == nsLayoutUtils::GetTextRunFlagsForStyle(sc2, fontStyle2, textStyle2, letterSpacing2); } void BuildTextRunsScanner::ScanFrame(nsIFrame* aFrame) { + nsIAtom* frameType = aFrame->GetType(); + if (frameType == nsGkAtoms::rubyTextContainerFrame) { + // Don't include any ruby text container into the text run. + return; + } + // First check if we can extend the current mapped frame block. This is common. if (mMappedFlows.Length() > 0) { MappedFlow* mappedFlow = &mMappedFlows[mMappedFlows.Length() - 1]; if (mappedFlow->mEndFrame == aFrame && (aFrame->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION)) { - NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame, + NS_ASSERTION(frameType == nsGkAtoms::textFrame, "Flow-sibling of a text frame is not a text frame?"); // Don't do this optimization if mLastFrame has a terminal newline... // it's quite likely preformatted and we might want to end the textrun here. // This is almost always true: if (mLastFrame->StyleContext() == aFrame->StyleContext() && !HasTerminalNewline(mLastFrame)) { AccumulateRunInfo(static_cast<nsTextFrame*>(aFrame)); return; } } } - nsIAtom* frameType = aFrame->GetType(); // Now see if we can add a new set of frames to the current textrun if (frameType == nsGkAtoms::textFrame) { nsTextFrame* frame = static_cast<nsTextFrame*>(aFrame); if (mLastFrame) { if (!ContinueTextRunAcrossFrames(mLastFrame, frame)) { FlushFrames(false, false); } else {
new file mode 100644 --- /dev/null +++ b/layout/reftests/css-ruby/line-breaking-1-ref.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="UTF-8"> + <title>Bug 1089431 - Meet the specification for line breaking between ruby bases</title> + <link rel="stylesheet" href="common.css"> + <style> + body { line-height: 3; } + span { white-space: nowrap; } + </style> +</head> +<body> + <div style="width: .5em; border: 1px solid silver;"> + <span>「</span><span>な</span><span>に</span><span>、</span><span>誰</span><span>?</span><span>」</span><span>「</span><span>私</span><span>です</span><span>」</span> + </div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/css-ruby/line-breaking-1.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="UTF-8"> + <title>Bug 1089431 - Meet the specification for line breaking between ruby bases</title> + <link rel="stylesheet" href="common.css"> + <style> + body { line-height: 3; } + </style> +</head> +<body> + <div style="width: .5em; border: 1px solid silver;"> + <ruby> + <rb>「<rb>な<rb>に<rb>、<rb>誰<rb>?<rb>」<rb>「<rb>私<rb>です<rb>」</rb> + <!-- Check if ruby text containers are skipped from the text run --> + <rtc><rt> + </ruby> + </div> +</body> +</html>
--- a/layout/reftests/css-ruby/reftest.list +++ b/layout/reftests/css-ruby/reftest.list @@ -24,16 +24,17 @@ fails == autohiding-3.html autohiding-3- == inlinize-blocks-3.html inlinize-blocks-3-ref.html == inlinize-blocks-4.html inlinize-blocks-4-ref.html == inlinize-blocks-5.html inlinize-blocks-5-ref.html == intra-level-whitespace-1.html intra-level-whitespace-1-ref.html == intra-level-whitespace-2.html intra-level-whitespace-2-ref.html == intra-level-whitespace-3.html intra-level-whitespace-3-ref.html == justification-1.html justification-1-ref.html == justification-2.html justification-2-ref.html +== line-breaking-1.html line-breaking-1-ref.html == line-height-1.html line-height-1-ref.html == line-height-2.html line-height-2-ref.html == line-height-3.html line-height-3-ref.html load nested-ruby-1.html == no-transform.html no-transform-ref.html fails-if(cocoaWidget) == relative-positioning-1.html relative-positioning-1-ref.html # bug 1120280 == relative-positioning-2.html relative-positioning-2-ref.html == ruby-position-horizontal.html ruby-position-horizontal-ref.html
--- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -568,29 +568,29 @@ RuleHash::~RuleHash() } } void RuleHash::AppendRuleToTable(PLDHashTable* aTable, const void* aKey, const RuleSelectorPair& aRuleInfo) { // Get a new or existing entry. RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*> - (PL_DHashTableAdd(aTable, aKey, fallible)); + (PL_DHashTableAdd(aTable, aKey)); if (!entry) return; entry->mRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode)); } static void AppendRuleToTagTable(PLDHashTable* aTable, nsIAtom* aKey, const RuleValue& aRuleInfo) { // Get a new or exisiting entry RuleHashTagTableEntry *entry = static_cast<RuleHashTagTableEntry*> - (PL_DHashTableAdd(aTable, aKey, fallible)); + (PL_DHashTableAdd(aTable, aKey)); if (!entry) return; entry->mRules.AppendElement(aRuleInfo); } void RuleHash::AppendUniversalRule(const RuleSelectorPair& aRuleInfo) { @@ -1036,17 +1036,17 @@ RuleCascadeData::SizeOfIncludingThis(Mal return n; } nsTArray<nsCSSSelector*>* RuleCascadeData::AttributeListFor(nsIAtom* aAttribute) { AtomSelectorEntry *entry = static_cast<AtomSelectorEntry*> - (PL_DHashTableAdd(&mAttributeSelectors, aAttribute, fallible)); + (PL_DHashTableAdd(&mAttributeSelectors, aAttribute)); if (!entry) return nullptr; return &entry->mSelectors; } // ------------------------------- // CSS Style rule processor implementation // @@ -3129,33 +3129,34 @@ AddSelector(RuleCascadeData* aCascade, nsCSSRuleProcessor::StateSelector(dependentStates, aSelectorInTopLevel)); } // Build mIDSelectors if (negation == aSelectorInTopLevel) { for (nsAtomList* curID = negation->mIDList; curID; curID = curID->mNext) { - AtomSelectorEntry *entry = static_cast<AtomSelectorEntry*> - (PL_DHashTableAdd(&aCascade->mIdSelectors, curID->mAtom, fallible)); + AtomSelectorEntry *entry = + static_cast<AtomSelectorEntry*>(PL_DHashTableAdd(&aCascade->mIdSelectors, + curID->mAtom)); if (entry) { entry->mSelectors.AppendElement(aSelectorInTopLevel); } } } else if (negation->mIDList) { aCascade->mPossiblyNegatedIDSelectors.AppendElement(aSelectorInTopLevel); } // Build mClassSelectors if (negation == aSelectorInTopLevel) { for (nsAtomList* curClass = negation->mClassList; curClass; curClass = curClass->mNext) { - AtomSelectorEntry *entry = static_cast<AtomSelectorEntry*> - (PL_DHashTableAdd(&aCascade->mClassSelectors, curClass->mAtom, - fallible)); + AtomSelectorEntry *entry = + static_cast<AtomSelectorEntry*>(PL_DHashTableAdd(&aCascade->mClassSelectors, + curClass->mAtom)); if (entry) { entry->mSelectors.AppendElement(aSelectorInTopLevel); } } } else if (negation->mClassList) { aCascade->mPossiblyNegatedClassSelectors.AppendElement(aSelectorInTopLevel); } @@ -3404,18 +3405,17 @@ CascadeRuleEnumFunc(css::Rule* aRule, vo if (css::Rule::STYLE_RULE == type) { css::StyleRule* styleRule = static_cast<css::StyleRule*>(aRule); for (nsCSSSelectorList *sel = styleRule->Selector(); sel; sel = sel->mNext) { int32_t weight = sel->mWeight; RuleByWeightEntry *entry = static_cast<RuleByWeightEntry*>( - PL_DHashTableAdd(&data->mRulesByWeight, NS_INT32_TO_PTR(weight), - fallible)); + PL_DHashTableAdd(&data->mRulesByWeight, NS_INT32_TO_PTR(weight))); if (!entry) return false; entry->data.mWeight = weight; // entry->data.mRuleSelectorPairs should be linked in forward order; // entry->data.mTail is the slot to write to. PerWeightDataListItem *newItem = new (data->mArena) PerWeightDataListItem(styleRule, sel->mSelectors); if (newItem) {
--- a/layout/style/nsHTMLStyleSheet.cpp +++ b/layout/style/nsHTMLStyleSheet.cpp @@ -478,19 +478,18 @@ nsHTMLStyleSheet::SetVisitedLinkColor(ns already_AddRefed<nsMappedAttributes> nsHTMLStyleSheet::UniqueMappedAttributes(nsMappedAttributes* aMapped) { if (!mMappedAttrTable.IsInitialized()) { PL_DHashTableInit(&mMappedAttrTable, &MappedAttrTable_Ops, sizeof(MappedAttrTableEntry)); } - MappedAttrTableEntry *entry = - static_cast<MappedAttrTableEntry*> - (PL_DHashTableAdd(&mMappedAttrTable, aMapped, fallible)); + MappedAttrTableEntry *entry = static_cast<MappedAttrTableEntry*> + (PL_DHashTableAdd(&mMappedAttrTable, aMapped)); if (!entry) return nullptr; if (!entry->mAttributes) { // We added a new entry to the hashtable, so we have a new unique set. entry->mAttributes = aMapped; } nsRefPtr<nsMappedAttributes> ret = entry->mAttributes; return ret.forget(); @@ -514,17 +513,17 @@ nsHTMLStyleSheet::DropMappedAttributes(n nsIStyleRule* nsHTMLStyleSheet::LangRuleFor(const nsString& aLanguage) { if (!mLangRuleTable.IsInitialized()) { PL_DHashTableInit(&mLangRuleTable, &LangRuleTable_Ops, sizeof(LangRuleTableEntry)); } LangRuleTableEntry *entry = static_cast<LangRuleTableEntry*> - (PL_DHashTableAdd(&mLangRuleTable, &aLanguage, fallible)); + (PL_DHashTableAdd(&mLangRuleTable, &aLanguage)); if (!entry) { NS_ASSERTION(false, "out of memory"); return nullptr; } return entry->mRule; } static size_t
--- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -1415,18 +1415,17 @@ nsRuleNode::DestroyInternal(nsRuleNode * destroyQueueTail = &destroyQueue; } if (ChildrenAreHashed()) { PLDHashTable *children = ChildrenHash(); PL_DHashTableEnumerate(children, EnqueueRuleNodeChildren, &destroyQueueTail); *destroyQueueTail = nullptr; // ensure null-termination - PL_DHashTableFinish(children); - delete children; + PL_DHashTableDestroy(children); } else if (HaveChildren()) { *destroyQueueTail = ChildrenList(); do { destroyQueueTail = &(*destroyQueueTail)->mNextSibling; } while (*destroyQueueTail); } mChildren.asVoid = nullptr; @@ -1529,17 +1528,17 @@ nsRuleNode::Transition(nsIStyleRule* aRu if (curr) next = curr; else if (numKids >= kMaxChildrenInList) ConvertChildrenToHash(numKids); } if (ChildrenAreHashed()) { ChildrenHashEntry *entry = static_cast<ChildrenHashEntry*> - (PL_DHashTableAdd(ChildrenHash(), &key, fallible)); + (PL_DHashTableAdd(ChildrenHash(), &key)); if (!entry) { NS_WARNING("out of memory"); return this; } if (entry->mRuleNode) next = entry->mRuleNode; else { next = entry->mRuleNode = new (mPresContext) @@ -1599,23 +1598,25 @@ void nsRuleNode::SetUsedDirectly() } } void nsRuleNode::ConvertChildrenToHash(int32_t aNumKids) { NS_ASSERTION(!ChildrenAreHashed() && HaveChildren(), "must have a non-empty list of children"); - PLDHashTable *