author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Wed, 12 Jul 2017 11:07:09 +0200 | |
changeset 368392 | 09a4282d1172ac255038e7ccacfd772140b219e2 |
parent 368322 | 2ea915a5c474da35b3ab6cf828d6816d53e1347f (current diff) |
parent 368391 | 218d346298695f533ef9056e9d7cf03e1372d1e3 (diff) |
child 368393 | 25da67af538c066eac3facc1390ca7b48fe906b6 |
child 368442 | c59cb9abed3885d87660332e94e43b477ac5fd0d |
child 368619 | 235ab635d17254e70b629bfe106334442a9a728f |
push id | 32162 |
push user | cbook@mozilla.com |
push date | Wed, 12 Jul 2017 09:07:26 +0000 |
treeherder | mozilla-central@09a4282d1172 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 56.0a1 |
first release with | nightly linux32
09a4282d1172
/
56.0a1
/
20170712100301
/
files
nightly linux64
09a4282d1172
/
56.0a1
/
20170712100301
/
files
nightly mac
09a4282d1172
/
56.0a1
/
20170712100330
/
files
nightly win32
09a4282d1172
/
56.0a1
/
20170712030204
/
files
nightly win64
09a4282d1172
/
56.0a1
/
20170712030204
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
56.0a1
/
20170712100301
/
pushlog to previous
nightly linux64
56.0a1
/
20170712100301
/
pushlog to previous
nightly mac
56.0a1
/
20170712100330
/
pushlog to previous
nightly win32
56.0a1
/
20170712030204
/
pushlog to previous
nightly win64
56.0a1
/
20170712030204
/
pushlog to previous
|
browser/app/profile/firefox.js | file | annotate | diff | comparison | revisions | |
browser/base/content/test/performance/browser_startup.js | file | annotate | diff | comparison | revisions | |
dom/base/nsContentUtils.cpp | file | annotate | diff | comparison | revisions | |
dom/security/test/sri/iframe_style_crossdomain_legacy.html | file | annotate | diff | comparison | revisions | |
dom/security/test/sri/test_style_crossdomain_legacy.html | file | annotate | diff | comparison | revisions | |
dom/xbl/test/test_bug379959_legacy.html | file | annotate | diff | comparison | revisions | |
taskcluster/taskgraph/transforms/tests.py | file | annotate | diff | comparison | revisions |
--- a/browser/app/nsBrowserApp.cpp +++ b/browser/app/nsBrowserApp.cpp @@ -211,17 +211,16 @@ static int do_main(int argc, char* argv[ // no -app flag so we use the compiled-in app data config.appData = &sAppData; config.appDataPath = kDesktopFolder; } #if defined(XP_WIN) && defined(MOZ_SANDBOX) sandbox::BrokerServices* brokerServices = sandboxing::GetInitializedBrokerServices(); - sandboxing::NetworkDriveCheck(); sandboxing::PermissionsService* permissionsService = sandboxing::GetPermissionsService(); #if defined(MOZ_CONTENT_SANDBOX) if (!brokerServices) { Output("Couldn't initialize the broker services.\n"); return 255; } #endif
--- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -602,16 +602,19 @@ support-files = [browser_e10s_chrome_process.js] # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. [browser_e10s_javascript.js] # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. [browser_blockHPKP.js] tags = psm # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. [browser_windowactivation.js] +support-files = + file_window_activation.html + file_window_activation2.html # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. [browser_contextmenu_childprocess.js] # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. [browser_bug963945.js] # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. [browser_domFullscreen_fullscreenMode.js] tags = fullscreen # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
--- a/browser/base/content/test/general/browser_windowactivation.js +++ b/browser/base/content/test/general/browser_windowactivation.js @@ -1,15 +1,16 @@ /* * This test checks that window activation state is set properly with multiple tabs. */ /* eslint-env mozilla/frame-script */ -var testPage = "data:text/html;charset=utf-8,<body><style>:-moz-window-inactive { background-color: red; }</style><div id='area'></div></body>"; +const testPage = getRootDirectory(gTestPath) + "file_window_activation.html"; +const testPage2 = getRootDirectory(gTestPath) + "file_window_activation2.html"; var colorChangeNotifications = 0; var otherWindow; var browser1, browser2; add_task(async function reallyRunTests() { @@ -102,17 +103,17 @@ add_task(async function reallyRunTests() }); function sendGetBackgroundRequest(ifChanged) { browser1.messageManager.sendAsyncMessage("Test:GetBackgroundColor", { ifChanged }); browser2.messageManager.sendAsyncMessage("Test:GetBackgroundColor", { ifChanged }); } function runOtherWindowTests() { - otherWindow = window.open("data:text/html;charset=utf-8,<body>Hi</body>", "", "chrome"); + otherWindow = window.open(testPage2, "", "chrome"); waitForFocus(function() { sendGetBackgroundRequest(true); }, otherWindow); } function childFunction() { let oldColor = null;
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/general/file_window_activation.html @@ -0,0 +1,4 @@ +<body> +<style>:-moz-window-inactive { background-color: red; }</style> +<div id='area'></div> +</body>
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/general/file_window_activation2.html @@ -0,0 +1,1 @@ +<body>Hi</body>
--- a/browser/base/content/test/performance/browser_startup.js +++ b/browser/base/content/test/performance/browser_startup.js @@ -66,16 +66,17 @@ const startupPhases = { "UnifiedComplete.js", "nsPlacesExpiration.js", "nsSearchService.js", ]), modules: new Set([ "chrome://webcompat-reporter/content/TabListener.jsm", "resource:///modules/AboutNewTab.jsm", "resource:///modules/DirectoryLinksProvider.jsm", + "resource:///modules/RecentWindow.jsm", "resource://gre/modules/BookmarkHTMLUtils.jsm", "resource://gre/modules/Bookmarks.jsm", "resource://gre/modules/ContextualIdentityService.jsm", "resource://gre/modules/NewTabUtils.jsm", "resource://gre/modules/PageThumbs.jsm", "resource://gre/modules/PlacesSyncUtils.jsm", "resource://gre/modules/Promise.jsm", "resource://gre/modules/Sqlite.jsm",
--- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -3,17 +3,19 @@ * 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/. */ // Services = object with smart getters for common XPCOM services Components.utils.import("resource://gre/modules/AppConstants.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); -Components.utils.import("resource:///modules/RecentWindow.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", + "resource:///modules/RecentWindow.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ShellService", "resource:///modules/ShellService.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService", "resource://gre/modules/ContextualIdentityService.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
--- a/browser/components/search/content/search.xml +++ b/browser/components/search/content/search.xml @@ -1583,16 +1583,31 @@ // header is a xul:deck so collapsed doesn't work on it, see bug 589569. this.header.hidden = this.buttons.collapsed = !oneOffCount; if (!oneOffCount) return; let panelWidth = parseInt(this.popup.clientWidth); + + // There's one weird thing to guard against: when layout pixels + // aren't an integral multiple of device pixels, the last button + // of each row sometimes gets pushed to the next row, depending on the + // panel and button widths. + // This is likely because the clientWidth getter rounds the value, but + // the panel's border width is not an integer. + // As a workaround, decrement the width if the scale is not an integer. + let scale = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .screenPixelsPerCSSPixel; + if (Math.floor(scale) != scale) { + --panelWidth; + } + // The + 1 is because the last button doesn't have a right border. let enginesPerRow = Math.floor((panelWidth + 1) / this.buttonWidth); let buttonWidth = Math.floor(panelWidth / enginesPerRow); // There will be an emtpy area of: // panelWidth - enginesPerRow * buttonWidth px // at the end of each row. // If the <description> tag with the list of search engines doesn't have @@ -1654,30 +1669,17 @@ if (this.compact) { this.settingsButtonCompact.setAttribute("width", buttonWidth); if (rowCount == 1 && hasDummyItems) { // When there's only one row, make the compact settings button // hug the right edge of the panel. It may not due to the panel's // width not being an integral multiple of the button width. (See // the "There will be an emtpy area" comment above.) Increase the // width of the last dummy item by the remainder. - // - // There's one weird thing to guard against: when layout pixels - // aren't an integral multiple of device pixels, the settings - // button sometimes gets pushed to a new row, depending on the - // panel and button widths. It's as if `remainder` is somehow - // too big, even though it's an integer. To work around that, - // decrement the remainder if the scale is not an integer. - let scale = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .screenPixelsPerCSSPixel; let remainder = panelWidth - (enginesPerRow * buttonWidth); - if (Math.floor(scale) != scale) { - remainder--; - } let width = remainder + buttonWidth; let lastDummyItem = this.settingsButtonCompact.previousSibling; lastDummyItem.setAttribute("width", width); } } ]]></body> </method>
--- a/browser/components/shell/nsGNOMEShellService.cpp +++ b/browser/components/shell/nsGNOMEShellService.cpp @@ -27,16 +27,17 @@ #include "nsIImageLoadingContent.h" #include "imgIRequest.h" #include "imgIContainer.h" #include "mozilla/Sprintf.h" #if defined(MOZ_WIDGET_GTK) #include "nsIImageToPixbuf.h" #endif #include "nsXULAppAPI.h" +#include "gfxPlatform.h" #include <glib.h> #include <glib-object.h> #include <gtk/gtk.h> #include <gdk/gdk.h> #include <gdk-pixbuf/gdk-pixbuf.h> #include <limits.h> #include <stdlib.h> @@ -81,16 +82,20 @@ static const MimeTypeAssociation appType #define kDesktopDrawBGGSKey "draw-background" #define kDesktopColorGSKey "primary-color" nsresult nsGNOMEShellService::Init() { nsresult rv; + if (gfxPlatform::IsHeadless()) { + return NS_ERROR_NOT_AVAILABLE; + } + // GConf, GSettings or GIO _must_ be available, or we do not allow // CreateInstance to succeed. nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID); nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID); nsCOMPtr<nsIGSettingsService> gsettings = do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
--- a/browser/modules/BrowserUITelemetry.jsm +++ b/browser/modules/BrowserUITelemetry.jsm @@ -300,27 +300,24 @@ this.BrowserUITelemetry = { _firstWindowMeasurements: null, _gatherFirstWindowMeasurements() { // We'll gather measurements as soon as the session has restored. // We do this here instead of waiting for UITelemetry to ask for // our measurements because at that point all browser windows have // probably been closed, since the vast majority of saved-session // pings are gathered during shutdown. - let win = RecentWindow.getMostRecentBrowserWindow({ - private: false, - allowPopups: false, - }); - Services.search.init(rv => { - // If there are no such windows (or we've just about found one - // but it's closed already), we're out of luck. :( - let hasWindow = win && !win.closed; - this._firstWindowMeasurements = hasWindow ? this._getWindowMeasurements(win, rv) - : {}; + let win = RecentWindow.getMostRecentBrowserWindow({ + private: false, + allowPopups: false, + }); + // If there are no such windows, we're out of luck. :( + this._firstWindowMeasurements = win ? this._getWindowMeasurements(win, rv) + : {}; }); }, _registerWindow(aWindow) { aWindow.addEventListener("unload", this); let document = aWindow.document; for (let areaID of CustomizableUI.areas) {
--- a/browser/modules/ExtensionsUI.jsm +++ b/browser/modules/ExtensionsUI.jsm @@ -42,17 +42,17 @@ this.ExtensionsUI = { async init() { this.histogram = Services.telemetry.getHistogramById("EXTENSION_INSTALL_PROMPT_RESULT"); Services.obs.addObserver(this, "webextension-permission-prompt"); Services.obs.addObserver(this, "webextension-update-permissions"); Services.obs.addObserver(this, "webextension-install-notify"); Services.obs.addObserver(this, "webextension-optional-permission-prompt"); - await RecentWindow.getMostRecentBrowserWindow().delayedStartupPromise; + await Services.wm.getMostRecentWindow("navigator:browser").delayedStartupPromise; this._checkForSideloaded(); }, async _checkForSideloaded() { let sideloaded = await AddonManagerPrivate.getNewSideloads(); if (!sideloaded.length) {
--- a/docshell/base/moz.build +++ b/docshell/base/moz.build @@ -110,16 +110,17 @@ include('/ipc/chromium/chromium-config.m FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ '/docshell/shistory', '/dom/base', '/layout/base', '/layout/generic', '/layout/style', '/layout/xul', + '/netwerk/base', '/netwerk/protocol/viewsource', '/toolkit/components/browser', '/tools/profiler', ] if CONFIG['MOZ_TOOLKIT_SEARCH']: DEFINES['MOZ_TOOLKIT_SEARCH'] = True
--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -200,16 +200,17 @@ #include "nsICommandManager.h" #include "nsIDOMNode.h" #include "nsIClassOfService.h" #include "nsIDocShellTreeOwner.h" #include "nsIHttpChannel.h" #include "nsIIDNService.h" #include "nsIInputStreamChannel.h" #include "nsINestedURI.h" +#include "nsIOService.h" #include "nsISHContainer.h" #include "nsISHistory.h" #include "nsISecureBrowserUI.h" #include "nsISocketProvider.h" #include "nsIStringBundle.h" #include "nsIURIFixup.h" #include "nsIURILoader.h" #include "nsIURL.h" @@ -10957,21 +10958,27 @@ nsDocShell::DoURILoad(nsIURI* aURI, // the triggeringPrincipal for TYPE_DOCUMENT loads. MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal"); bool isSandBoxed = mSandboxFlags & SANDBOXED_ORIGIN; // only inherit if we have a aPrincipalToInherit bool inherit = false; if (aPrincipalToInherit) { + bool isData; + bool isURIUniqueOrigin = nsIOService::IsDataURIUniqueOpaqueOrigin() && + NS_SUCCEEDED(aURI->SchemeIs("data", &isData)) && + isData; + // If aURI is data: URI and is treated as a unique opaque origin, we don't + // want to inherit principal. inherit = nsContentUtils::ChannelShouldInheritPrincipal( aPrincipalToInherit, aURI, true, // aInheritForAboutBlank - isSrcdoc); + isSrcdoc) && !isURIUniqueOrigin ; } nsLoadFlags loadFlags = mDefaultLoadFlags; nsSecurityFlags securityFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL; if (aFirstParty) { // tag first party URL loads
--- a/docshell/test/bug404548-subframe.html +++ b/docshell/test/bug404548-subframe.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> -<body onload="setTimeout(function() { window.location = "data:text/html,<body onload='window.opener.finishTest()'>" }, 10)"> -<iframe src="data:text/html,<body onpagehide='var p = window.parent.opener; var e = window.frameElement; e.parentNode.removeChild(e); if (e.parentNode == null && e.contentWindow == null) { p.firstRemoved = true; }'>"> +<body onload="setTimeout(function() { window.location = 'bug404548-subframe_window.html'; }, 10)"> +<iframe srcdoc="<body onpagehide='var p = window.parent.opener; var e = window.frameElement; e.parentNode.removeChild(e); if (e.parentNode == null && e.contentWindow == null) { p.firstRemoved = true; }'>"> </iframe> -<iframe src="data:text/html,<body onpagehide='window.parent.opener.secondHidden = true;'>"> +<iframe srcdoc="<body onpagehide='window.parent.opener.secondHidden = true;'>"> </iframe>
new file mode 100644 --- /dev/null +++ b/docshell/test/bug404548-subframe_window.html @@ -0,0 +1,1 @@ +<body onload='window.opener.finishTest()'>
new file mode 100644 --- /dev/null +++ b/docshell/test/file_bfcache_plus_hash_1.html @@ -0,0 +1,1 @@ +<html><body onload="opener.childLoad(1)" onpageshow="opener.childPageshow(1)">Popup 1</body></html>
new file mode 100644 --- /dev/null +++ b/docshell/test/file_bfcache_plus_hash_2.html @@ -0,0 +1,1 @@ +<html><body onload="opener.childLoad(2)">Popup 2</body></html>
new file mode 100644 --- /dev/null +++ b/docshell/test/file_bug1121701_1.html @@ -0,0 +1,1 @@ +<script>window.onpageshow = function(e) { opener.child1PageShow(e); } </script>
new file mode 100644 --- /dev/null +++ b/docshell/test/file_bug1121701_2.html @@ -0,0 +1,1 @@ +<script>window.onpageshow = function(e) { opener.child2PageShow(e); } </script>
new file mode 100644 --- /dev/null +++ b/docshell/test/file_bug1186774.html @@ -0,0 +1,1 @@ +<div style='height: 9000px;'></div>
new file mode 100644 --- /dev/null +++ b/docshell/test/file_bug369814.html @@ -0,0 +1,1 @@ +<script>window.onunload = function() { frameElement.unloading(); }</script>
new file mode 100644 --- /dev/null +++ b/docshell/test/file_bug660404-1.html @@ -0,0 +1,1 @@ +<script>window.onload = function() { opener.continueTest(); }</script>
--- a/docshell/test/historyframes.html +++ b/docshell/test/historyframes.html @@ -4,17 +4,17 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=602256 --> <head> <title>Test for Bug 602256</title> </head> <body onload="SimpleTest.executeSoon(run_test)"> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602256">Mozilla Bug 602256</a> <div id="content"> - <iframe id="iframe" src="data:text/html,<p%20id='text'>Start</p>"></iframe> + <iframe id="iframe" src="start_historyframe.html"></iframe> </div> <pre id="test"> <script type="application/javascript"> /** Test for Bug 602256 **/ var testWin = window.opener ? window.opener : window.parent; @@ -45,19 +45,20 @@ function loadContent(aURL, aCallback) { function getURL() { return gFrame.contentDocument.documentURI; } function getContent() { return gFrame.contentDocument.getElementById("text").textContent; } -var START = "data:text/html,<p%20id='text'>Start</p>"; -var URL1 = "data:text/html,<p%20id='text'>Test1</p>"; -var URL2 = "data:text/html,<p%20id='text'>Test2</p>"; +var BASE_URI = "http://mochi.test:8888/tests/docshell/test/"; +var START = BASE_URI + "start_historyframe.html"; +var URL1 = BASE_URI + "url1_historyframe.html"; +var URL2 = BASE_URI + "url2_historyframe.html"; function run_test() { window.location.hash = "START"; gFrame = document.getElementById("iframe"); test_basic_inner_navigation(); }
new file mode 100644 --- /dev/null +++ b/docshell/test/iframesandbox/file_child_navigation_by_location.html @@ -0,0 +1,1 @@ +<script>function onNav() { parent.parent.postMessage('childIframe', '*'); } window.onload = onNav; window.onhashchange = onNav;</script>
--- a/docshell/test/iframesandbox/mochitest.ini +++ b/docshell/test/iframesandbox/mochitest.ini @@ -1,10 +1,11 @@ [DEFAULT] support-files = + file_child_navigation_by_location.html file_marquee_event_handlers.html file_other_auxiliary_navigation_by_location.html file_our_auxiliary_navigation_by_location.html file_parent_navigation_by_location.html file_sibling_navigation_by_location.html file_top_navigation_by_location.html file_top_navigation_by_location_exotic.html
--- a/docshell/test/iframesandbox/test_child_navigation_by_location.html +++ b/docshell/test/iframesandbox/test_child_navigation_by_location.html @@ -8,18 +8,17 @@ html5 sandboxed iframe should not be abl <meta charset="utf-8"> <title>Test for Bug 785310 - iframe sandbox child navigation by location tests</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <script> SimpleTest.waitForExplicitFinish(); - var testHtml = "<script>function onNav() { parent.parent.postMessage('childIframe', '*'); } window.onload = onNav; window.onhashchange = onNav;<\/script>"; - var testDataUri = "data:text/html," + testHtml; + var testDataUri = "file_child_navigation_by_location.html"; function runScriptNavigationTest(testCase) { window.onmessage = function(event) { if (event.data != 'childIframe') { ok(false, "event.data: got '" + event.data + "', expected 'childIframe'"); } ok(!testCase.shouldBeBlocked, testCase.desc, "child navigation was NOT blocked"); runNextTest(); @@ -81,12 +80,12 @@ html5 sandboxed iframe should not be abl </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a> <p id="display"></p> <div id="content"> Tests for Bug 785310 </div> -<iframe name="parentIframe" sandbox="allow-scripts allow-same-origin" src="data:text/html,<iframe name='sameOriginChildIframe'></iframe><iframe name='crossOriginChildIframe' sandbox='allow-scripts'></iframe>"</iframe> +<iframe name="parentIframe" sandbox="allow-scripts allow-same-origin" srcdoc="<iframe name='sameOriginChildIframe'></iframe><iframe name='crossOriginChildIframe' sandbox='allow-scripts'></iframe>"</iframe> </body> </html>
--- a/docshell/test/mochitest.ini +++ b/docshell/test/mochitest.ini @@ -1,46 +1,57 @@ [DEFAULT] support-files = bug123696-subframe.html bug369814.jar bug369814.zip bug404548-subframe.html + bug404548-subframe_window.html bug413310-post.sjs bug413310-subframe.html bug529119-window.html bug570341_recordevents.html bug668513_redirect.html bug668513_redirect.html^headers^ bug691547_frame.html dummy_page.html file_anchor_scroll_after_document_open.html + file_bfcache_plus_hash_1.html + file_bfcache_plus_hash_2.html + file_bug369814.html file_bug385434_1.html file_bug385434_2.html file_bug385434_3.html file_bug475636.sjs file_bug509055.html file_bug540462.html file_bug580069_1.html file_bug580069_2.sjs file_bug590573_1.html file_bug590573_2.html file_bug634834.html file_bug640387.html file_bug653741.html file_bug660404 file_bug660404^headers^ + file_bug660404-1.html file_bug662170.html file_bug669671.sjs file_bug680257.html file_bug703855.html file_bug728939.html + file_bug1121701_1.html + file_bug1121701_2.html + file_bug1186774.html file_bug1151421.html file_pushState_after_document_open.html historyframes.html + start_historyframe.html + url1_historyframe.html + url2_historyframe.html [test_anchor_scroll_after_document_open.html] [test_bfcache_plus_hash.html] [test_bug123696.html] [test_bug369814.html] [test_bug384014.html] [test_bug385434.html] [test_bug387979.html]
new file mode 100644 --- /dev/null +++ b/docshell/test/navigation/bluebox_bug430723.html @@ -0,0 +1,6 @@ +<html><head> +<script> window.addEventListener("pageshow", function(){opener.nextTest();}, false); </script> +</head><body> +<div style="position:absolute; left:0px; top:0px; width:50%; height:150%; background-color:blue"> +<p>This is a very tall blue box.</p> +</div></body></html>
--- a/docshell/test/navigation/mochitest.ini +++ b/docshell/test/navigation/mochitest.ini @@ -1,14 +1,16 @@ [DEFAULT] support-files = NavigationUtils.js blank.html file_bug386782_contenteditable.html file_bug386782_designmode.html + redbox_bug430723.html + bluebox_bug430723.html file_bug462076_1.html file_bug462076_2.html file_bug462076_3.html file_bug508537_1.html file_bug534178.html file_document_write_1.html file_fragment_handling_during_load.html file_nested_frames.html
new file mode 100644 --- /dev/null +++ b/docshell/test/navigation/redbox_bug430723.html @@ -0,0 +1,6 @@ +<html><head> +<script> window.addEventListener("pageshow", function(){opener.nextTest();}, false); </script> +</head><body> +<div style="position:absolute; left:0px; top:0px; width:50%; height:150%; background-color:red"> +<p>This is a very tall red box.</p> +</div></body></html>
--- a/docshell/test/navigation/test_bug430624.html +++ b/docshell/test/navigation/test_bug430624.html @@ -20,17 +20,17 @@ https://bugzilla.mozilla.org/show_bug.cg </div> <pre id="test"> <script class="testbody" type="text/javascript"> /** Test for Bug 430624 **/ function onLoad() { window.frames[0].frameElement.onload = onReload; - window.frames[0].location = window.frames[0].location; + window.frames[0].frameElement.srcdoc = window.frames[0].frameElement.srcdoc; } function onReload() { var iframe = window.frames[0].frameElement; SimpleTest.waitForFocus(doTest, iframe.contentWindow); iframe.contentDocument.body.focus(); } @@ -44,13 +44,13 @@ function doTest() { SimpleTest.finish(); } SimpleTest.waitForExplicitFinish(); </script> </pre> -<iframe onload="onLoad()" src="data:text/html;charset=utf-8,<body contenteditable>contentEditable</body>"></iframe> +<iframe onload="onLoad()" srcdoc="<body contenteditable>contentEditable</body>"></iframe> </body> </html>
--- a/docshell/test/navigation/test_bug430723.html +++ b/docshell/test/navigation/test_bug430723.html @@ -16,31 +16,19 @@ https://bugzilla.mozilla.org/show_bug.cg </div> <pre id="test"> <script class="testbody" type="text/javascript"> //<![CDATA[ /** Test for Bug 430723 **/ -var gTallRedBoxURI = "data:text/html;charset=utf-8;base64,PGh0bWw%2BPGhlYWQ%2BPHNjcmlwdD53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigncGFnZXNob3cnLCBmdW5jdGlvbigpe29wZW5lci5uZXh0VGVzdCgpO30sIGZhbHNlKTs8L3NjcmlwdD48L2hlYWQ%2BPGJvZHk%2BPGRpdiBzdHlsZT0icG9zaXRpb246YWJzb2x1dGU7IGxlZnQ6MHB4OyB0b3A6MHB4OyB3aWR0aDo1MCU7IGhlaWdodDoxNTAlOyBiYWNrZ3JvdW5kLWNvbG9yOnJlZCI%2BPHA%2BVGhpcyBpcyBhIHZlcnkgdGFsbCByZWQgYm94LjwvcD48L2Rpdj48L2JvZHk%2BPC9odG1sPg%3D%3D"; -// <html><head> -// < script > window.addEventListener("pageshow", function(){opener.nextTest();}, false); < /script > -// </head><body> -// <div style="position:absolute; left:0px; top:0px; width:50%; height:150%; background-color:red"> -// <p>This is a very tall red box.</p> -// </div></body></html> - -var gTallBlueBoxURI = "data:text/html;charset=utf-8;base64,PGh0bWw%2BPGhlYWQ%2BPHNjcmlwdD53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigncGFnZXNob3cnLCBmdW5jdGlvbigpe29wZW5lci5uZXh0VGVzdCgpO30sIGZhbHNlKTs8L3NjcmlwdD48L2hlYWQ%2BPGJvZHk%2BPGRpdiBzdHlsZT0icG9zaXRpb246YWJzb2x1dGU7IGxlZnQ6MHB4OyB0b3A6MHB4OyB3aWR0aDo1MCU7IGhlaWdodDoxNTAlOyBiYWNrZ3JvdW5kLWNvbG9yOmJsdWUiPjxwPlRoaXMgaXMgYSB2ZXJ5IHRhbGwgYmx1ZSBib3guPC9wPjwvZGl2PjwvYm9keT48L2h0bWw%2B"; -// <html><head> -// < script > window.addEventListener("pageshow", function(){opener.nextTest();}, false); < /script > -// </head><body> -// <div style="position:absolute; left:0px; top:0px; width:50%; height:150%; background-color:blue"> -// <p>This is a very tall blue box.</p> -// </div></body></html> +var BASE_URI = "http://mochi.test:8888/tests/docshell/test/navigation/"; +var gTallRedBoxURI = BASE_URI + "redbox_bug430723.html"; +var gTallBlueBoxURI = BASE_URI + "bluebox_bug430723.html"; window.onload = runTest; var testWindow; var testNum = 0; var smoothScrollPref = "general.smoothScroll"; function runTest() {
new file mode 100644 --- /dev/null +++ b/docshell/test/start_historyframe.html @@ -0,0 +1,1 @@ +<p id='text'>Start</p>
--- a/docshell/test/test_bfcache_plus_hash.html +++ b/docshell/test/test_bfcache_plus_hash.html @@ -77,27 +77,23 @@ function waitForLoad(n) { } function waitForShow(n) { debug('Waiting for show ' + n); expectedPageshowNum = n; } function* test() { - var popup = window.open('data:text/html,' + - '<html><body onload="opener.childLoad(1)" ' + - 'onpageshow="opener.childPageshow(1)">' + - 'Popup 1' + - '</body></html>'); + var popup = window.open('file_bfcache_plus_hash_1.html'); waitForLoad(1); yield undefined; popup.history.pushState('', '', ''); - popup.location = 'data:text/html,<html><body onload="opener.childLoad(2)">Popup 2</body></html>'; + popup.location = 'file_bfcache_plus_hash_2.html'; waitForLoad(2); yield undefined; // Now go back 2. The first page should be retrieved from bfcache. popup.history.go(-2); waitForShow(1); yield undefined;
--- a/docshell/test/test_bug1121701.html +++ b/docshell/test/test_bug1121701.html @@ -7,18 +7,18 @@ https://bugzilla.mozilla.org/show_bug.cg <meta charset="utf-8"> <title>Test for Bug 1121701</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <script type="application/javascript"> /** Test for Bug 1121701 **/ - var testUrl1 = "data:text/html,<script>window.onpageshow = function(e) { opener.child1PageShow(e); } <" + "/script>"; - var testUrl2 = "data:text/html,<script>window.onpageshow = function(e) { opener.child2PageShow(e); } <" + "/script>"; + var testUrl1 = "file_bug1121701_1.html"; + var testUrl2 = "file_bug1121701_2.html"; var testWin; var page1LoadCount = 0; function child1PageShow(e) { ++page1LoadCount; if (page1LoadCount == 1) { SimpleTest.executeSoon(function() { is(e.persisted, false, "Initial page load shouldn't be persisted.");
--- a/docshell/test/test_bug1186774.html +++ b/docshell/test/test_bug1186774.html @@ -10,17 +10,17 @@ https://bugzilla.mozilla.org/show_bug.cg <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <script type="application/javascript"> /** Test for Bug 1186774 **/ var child; function runTest() { - child = window.open("data:text/html,<div style='height: 9000px;'></div>", "", "width=100,height=100"); + child = window.open("file_bug1186774.html", "", "width=100,height=100"); child.onload = function() { setTimeout(function() { child.scrollTo(0, 0); child.history.pushState({}, "initial"); child.scrollTo(0, 3000); child.history.pushState({}, "scrolled"); child.scrollTo(0, 6000); child.history.back();
--- a/docshell/test/test_bug369814.html +++ b/docshell/test/test_bug369814.html @@ -86,17 +86,17 @@ function loadErrorTest(test) } catch (e) { errorPage = true; } ok(errorPage, gCurrentTest["name"] + ": should block a suspicious JAR load."); finishTest(); }, 0); } - var unloadDetector = "data:text/html,<script>window.onunload = function() { frameElement.unloading(); }</" + "script>"; + var unloadDetector = "file_bug369814.html"; gTestFrame.src = unloadDetector; } function iframeTest(test) { gTestFrame.src = test['url']; loadEvent(gTestFrame, function() { finishTest(); });
--- a/docshell/test/test_bug660404.html +++ b/docshell/test/test_bug660404.html @@ -35,14 +35,14 @@ function continueTest() { function finishTest() { is(w.document.documentElement.textContent, "opener.finishTest();"); is(w.document.documentElement.innerHTML, "<head><script>opener.finishTest();</"+"script></head>"); w.close(); SimpleTest.finish(); } // Have to open a new window, since there's no bfcache in subframes -w = window.open("data:text/html,<script>window.onload = function() { opener.continueTest(); }</"+"script>"); +w = window.open("file_bug660404-1.html"); </script> </pre> </body> </html>
new file mode 100644 --- /dev/null +++ b/docshell/test/url1_historyframe.html @@ -0,0 +1,1 @@ +<p id='text'>Test1</p>
new file mode 100644 --- /dev/null +++ b/docshell/test/url2_historyframe.html @@ -0,0 +1,1 @@ +<p id='text'>Test2</p>
--- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -944,16 +944,18 @@ nsContentUtils::InitializeEventTable() { sAtomEventTable = new nsDataHashtable<nsISupportsHashKey, EventNameMapping>( ArrayLength(eventArray)); sStringEventTable = new nsDataHashtable<nsStringHashKey, EventNameMapping>( ArrayLength(eventArray)); sUserDefinedEvents = new nsCOMArray<nsIAtom>(64); // Subtract one from the length because of the trailing null for (uint32_t i = 0; i < ArrayLength(eventArray) - 1; ++i) { + MOZ_ASSERT(!sAtomEventTable->Lookup(eventArray[i].mAtom), + "Double-defining event name; fix your EventNameList.h"); sAtomEventTable->Put(eventArray[i].mAtom, eventArray[i]); if (ShouldAddEventToStringEventTable(eventArray[i])) { sStringEventTable->Put( Substring(nsDependentAtomString(eventArray[i].mAtom), 2), eventArray[i]); } }
--- a/dom/base/nsWindowMemoryReporter.cpp +++ b/dom/base/nsWindowMemoryReporter.cpp @@ -29,17 +29,18 @@ StaticRefPtr<nsWindowMemoryReporter> sWi * Don't trigger a ghost window check when a DOM window is detached if we've * run it this recently. */ const int32_t kTimeBetweenChecks = 45; /* seconds */ nsWindowMemoryReporter::nsWindowMemoryReporter() : mLastCheckForGhostWindows(TimeStamp::NowLoRes()), mCycleCollectorIsRunning(false), - mCheckTimerWaitingForCCEnd(false) + mCheckTimerWaitingForCCEnd(false), + mGhostWindowCount(0) { } nsWindowMemoryReporter::~nsWindowMemoryReporter() { KillCheckTimer(); } @@ -114,18 +115,17 @@ nsWindowMemoryReporter::Init() os->AddObserver(sWindowReporter, "after-minimize-memory-usage", /* weakRef = */ true); os->AddObserver(sWindowReporter, "cycle-collector-begin", /* weakRef = */ true); os->AddObserver(sWindowReporter, "cycle-collector-end", /* weakRef = */ true); } - RegisterStrongMemoryReporter(new GhostWindowsReporter()); - RegisterGhostWindowsDistinguishedAmount(GhostWindowsReporter::DistinguishedAmount); + RegisterGhostWindowsDistinguishedAmount(GhostWindowsDistinguishedAmount); } /* static */ nsWindowMemoryReporter* nsWindowMemoryReporter::Get() { return sWindowReporter; } @@ -486,16 +486,27 @@ nsWindowMemoryReporter::CollectReports(n path, nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT, /* amount = */ 1, /* description = */ NS_LITERAL_CSTRING("A ghost window."), aData); } + MOZ_COLLECT_REPORT( + "ghost-windows", KIND_OTHER, UNITS_COUNT, ghostWindows.Count(), +"The number of ghost windows present (the number of nodes underneath " +"explicit/window-objects/top(none)/ghost, modulo race conditions). A ghost " +"window is not shown in any tab, does not share a domain with any non-detached " +"windows, and has met these criteria for at least " +"memory.ghost_window_timeout_seconds, or has survived a round of " +"about:memory's minimize memory usage button.\n\n" +"Ghost windows can happen legitimately, but they are often indicative of " +"leaks in the browser or add-ons."); + WindowPaths windowPaths; WindowPaths topWindowPaths; // Collect window memory usage. nsWindowSizes windowTotalSizes(nullptr); nsCOMPtr<amIAddonManager> addonManager; if (XRE_IsParentProcess()) { // Only try to access the service from the main process. @@ -729,39 +740,46 @@ nsWindowMemoryReporter::CheckForGhostWin NS_WARNING("GetWindowsTable returned null"); return; } mLastCheckForGhostWindows = TimeStamp::NowLoRes(); KillCheckTimer(); nsTHashtable<nsCStringHashKey> nonDetachedWindowDomains; + nsDataHashtable<nsISupportsHashKey, nsCString> domainMap; // Populate nonDetachedWindowDomains. for (auto iter = windowsById->Iter(); !iter.Done(); iter.Next()) { // Null outer window implies null top, but calling GetTop() when there's no // outer window causes us to spew debug warnings. nsGlobalWindow* window = iter.UserData(); if (!window->GetOuterWindow() || !window->GetTopInternal()) { // This window is detached, so we don't care about its domain. continue; } nsCOMPtr<nsIURI> uri = GetWindowURI(window); nsAutoCString domain; if (uri) { - tldService->GetBaseDomain(uri, 0, domain); + domain = domainMap.LookupForAdd(uri).OrInsert([&]() { + nsCString d; + tldService->GetBaseDomain(uri, 0, d); + return d; + }); } + nonDetachedWindowDomains.PutEntry(domain); } // Update mDetachedWindows and write the ghost window IDs into aOutGhostIDs, // if it's not null. uint32_t ghostTimeout = GetGhostTimeout(); TimeStamp now = mLastCheckForGhostWindows; + mGhostWindowCount = 0; for (auto iter = mDetachedWindows.Iter(); !iter.Done(); iter.Next()) { nsWeakPtr weakKey = do_QueryInterface(iter.Key()); nsCOMPtr<mozIDOMWindow> iwindow = do_QueryReferent(weakKey); if (!iwindow) { // The window object has been destroyed. Stop tracking its weak ref in // our hashtable. iter.Remove(); continue; @@ -804,31 +822,27 @@ nsWindowMemoryReporter::CheckForGhostWin if (timeStamp.IsNull()) { // This may become a ghost window later; start its clock. timeStamp = now; } else if ((now - timeStamp).ToSeconds() > ghostTimeout) { // This definitely is a ghost window, so add it to aOutGhostIDs, if // that is not null. if (aOutGhostIDs && window) { aOutGhostIDs->PutEntry(window->WindowID()); + mGhostWindowCount++; } } } } } -NS_IMPL_ISUPPORTS(nsWindowMemoryReporter::GhostWindowsReporter, - nsIMemoryReporter) - /* static */ int64_t -nsWindowMemoryReporter::GhostWindowsReporter::DistinguishedAmount() +nsWindowMemoryReporter::GhostWindowsDistinguishedAmount() { - nsTHashtable<nsUint64HashKey> ghostWindows; - sWindowReporter->CheckForGhostWindows(&ghostWindows); - return ghostWindows.Count(); + return sWindowReporter->mGhostWindowCount; } void nsWindowMemoryReporter::KillCheckTimer() { if (mCheckTimer) { mCheckTimer->Cancel(); mCheckTimer = nullptr;
--- a/dom/base/nsWindowMemoryReporter.h +++ b/dom/base/nsWindowMemoryReporter.h @@ -157,50 +157,21 @@ public: * to become ghost windows in the first place. */ static void UnlinkGhostWindows(); #endif static nsWindowMemoryReporter* Get(); void ObserveDOMWindowDetached(nsGlobalWindow* aWindow); + static int64_t GhostWindowsDistinguishedAmount(); + private: ~nsWindowMemoryReporter(); - /** - * nsGhostWindowReporter generates the "ghost-windows" report, which counts - * the number of ghost windows present. - */ - class GhostWindowsReporter final : public nsIMemoryReporter - { - ~GhostWindowsReporter() {} - public: - NS_DECL_ISUPPORTS - - static int64_t DistinguishedAmount(); - - NS_IMETHOD - CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, - bool aAnonymize) override - { - MOZ_COLLECT_REPORT( - "ghost-windows", KIND_OTHER, UNITS_COUNT, DistinguishedAmount(), -"The number of ghost windows present (the number of nodes underneath " -"explicit/window-objects/top(none)/ghost, modulo race conditions). A ghost " -"window is not shown in any tab, does not share a domain with any non-detached " -"windows, and has met these criteria for at least " -"memory.ghost_window_timeout_seconds, or has survived a round of " -"about:memory's minimize memory usage button.\n\n" -"Ghost windows can happen legitimately, but they are often indicative of " -"leaks in the browser or add-ons."); - - return NS_OK; - } - }; - // Protect ctor, use Init() instead. nsWindowMemoryReporter(); /** * Get the number of seconds for which a window must satisfy ghost criteria * (1) and (2) before we deem that it satisfies criterion (3). */ uint32_t GetGhostTimeout(); @@ -253,12 +224,14 @@ private: */ mozilla::TimeStamp mLastCheckForGhostWindows; nsCOMPtr<nsITimer> mCheckTimer; bool mCycleCollectorIsRunning; bool mCheckTimerWaitingForCCEnd; + + int64_t mGhostWindowCount; }; #endif // nsWindowMemoryReporter_h__
--- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -632,16 +632,17 @@ skip-if = toolkit == 'android' #bug 9041 [test_copypaste.xhtml] subsuite = clipboard skip-if = toolkit == 'android' #bug 904183 [test_createHTMLDocument.html] [test_declare_stylesheet_obsolete.html] [test_dialogArguments.html] tags = openwindow skip-if = toolkit == 'android' || e10s # showmodaldialog +[test_data_uri.html] [test_document.all_iteration.html] [test_document.all_unqualified.html] [test_document_constructor.html] [test_document_importNode_document.html] [test_document_register.html] [test_domcursor.html] [test_domparser_null_char.html] [test_domparsing.html]
new file mode 100644 --- /dev/null +++ b/dom/base/test/test_data_uri.html @@ -0,0 +1,102 @@ +<html> +<head> + <title>Tests for Data URI</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + +<script> +SpecialPowers.setBoolPref("security.data_uri.unique_opaque_origin", true); +SimpleTest.registerCleanupFunction(() => { + SpecialPowers.clearUserPref("security.data_uri.unique_opaque_origin"); +}); + +SimpleTest.waitForExplicitFinish(); + +function imgListener(img) { + return new Promise((resolve, reject) => { + img.addEventListener("load", () => resolve()); + img.addEventListener("error", () => reject()); + }); +} + +function runTests() +{ + var iframe = document.getElementById("iframe"); + iframe.src="data:text/html,hello"; + iframe.onload = function() { + ok(SpecialPowers.wrap(iframe).contentDocument.nodePrincipal.isNullPrincipal, + "iframe should have NullPrincipal."); + } + var iframe1 = document.getElementById("iframe1"); + iframe1.src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%18%00%00%00%18%02%03%00%00%00%9D%19%D5k%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%00%0CPLTE%FF%FF%FF%FF%FF%FF%F7%DC%13%00%00%00%03%80%01X%00%00%00%01tRNS%08N%3DPT%00%00%00%01bKGD%00%88%05%1DH%00%00%00%09pHYs%00%00%0B%11%00%00%0B%11%01%7Fd_%91%00%00%00%07tIME%07%D2%05%0C%14%0C%0D%D8%3F%1FQ%00%00%00%5CIDATx%9C%7D%8E%CB%09%C0%20%10D%07r%B7%20%2F%E9wV0%15h%EA%D9%12D4%BB%C1x%CC%5C%1E%0C%CC%07%C0%9C0%9Dd7()%C0A%D3%8D%E0%B8%10%1DiCHM%D0%AC%D2d%C3M%F1%B4%E7%FF%10%0BY%AC%25%93%CD%CBF%B5%B2%C0%3Alh%CD%AE%13%DF%A5%F7%E0%03byW%09A%B4%F3%E2%00%00%00%00IEND%AEB%60%82"; + iframe1.onload = function() { + ok(SpecialPowers.wrap(iframe1).contentDocument.nodePrincipal.isNullPrincipal, + "iframe1 should have NullPrincipal."); + } + + var canvas = document.getElementById('canvas'); + var ctx = canvas.getContext('2d'); + ctx.fillRect(0, 0, canvas.height, canvas.width); + ctx.fillStyle = '#000'; + var data = canvas.toDataURL('image/png'); + var img = new Image(); + img.src = data; + imgListener(img).then(() => { + dump("img onload\n"); + ctx.drawImage(img, 0, 0); + return new Promise((resolve, reject) => { + try { + ctx.getImageData(0, 0, 1, 1); + ok(true, "data:image should be same origin."); + resolve(); + } catch (e) { + ok(false, "data:image is cross-origin."); + reject(); + }}); + }).then(() => { + ctx.clearRect(0, 0, canvas.height, canvas.width); + ctx.drawImage(document.getElementById('img'), 0, 0); + return new Promise((resolve, reject) => { + try { + canvas.toDataURL(); + ok(true, "data:image should be same origin."); + resolve(); + } catch (e) { + ok(false, "data:image is cross-origin."); + reject(); + }}); + }).then(() => { + var win = window.open("data:text/html,<script>parent.opener.postMessage('ok', '*');<\/script>"); + return new Promise(resolve => { + window.onmessage = function (evt) { + is(evt.origin, "null", "The origin of data:text/html should be null."); + win.close(); + resolve(); + }}); + }).then(() => { + document.fonts.add(new FontFace("test", "url(data:font/opentype;base64,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AC4AIABDAG8AcAB5AHIAaQBnAGgAdAAgAGkAbgBmAG8AIABwAGUAbgBkAGkAbgBnAC4AbABvAGEAZAB0AGUAcwB0AFIAZQBnAHUAbABhAHIAdwBlAGIAZgBvAG4AdABsAG8AYQBkAHQAZQBzAHQAIABSAGUAZwB1AGwAYQByAFYAZQByAHMAaQBvAG4AIAAwADAAMQAuADAAMAAxAEIAYQBzAGUAZAAgAG8AbgAgAEwAZQBhAGcAdQBlACAARwBvAHQAaABpAGMAIABiAHkAIABMAGUAYQBnAHUAZQAgAG8AZgAgAE0AbwB2AGUAYQBiAGwAZQAgAFQAeQBwAGUALAAgAG0AbwBkAGkAZgBpAGUAZAAgAGIAeQAgAEoAbwBoAG4AIABEAGEAZwBnAGUAdAB0AGgAdAB0AHAAOgAvAC8AdwB3AHcALgB0AGgAZQBsAGUAYQBnAHUAZQBvAGYAbQBvAHYAZQBhAGIAbABlAHQAeQBwAGUALgBjAG8AbQAvAGwAZQBhAGcAdQBlAC0AZwBvAHQAaABpAGMAQQBsAGwAIABmAG8AbgB0AHMAIABmAHIAbwBtACAAVABoAGUAIABMAGUAYQBnAHUAZQAgAG8AZgAgAE0AbwB2AGUAYQBiAGwAZQAgAFQAeQBwAGUAIABhAHIAZQAgAHMAdQBiAGoAZQBjAHQAIAB0AG8AIAB0AGgAZQAgAE8AcABlAG4AIABGAG8AbgB0ACAATABpAGMAZQBuAHMAZQBoAHQAdABwADoALwAvAHMAYwByAGkAcAB0AHMALgBzAGkAbAAuAG8AcgBnAC8ATwBGAEwAAgAAAAAAAP9nAGYAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAQACAAMABAAFAAYABwAIAAkACgALAAwADQAOAA8AEAARABIAEwAUABUAFgAXABgAGQAaABsAHAAdAB4AHwAgACEAIgAjACQAJQAmACcAKAApACoAKwAsAC0ALgAvADAAMQAyADMANAA1ADYANwA4ADkAOgA7ADwAPQA+AD8AQABBAEIAQwBEAEUARgBHAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAWwBcAF0AXgBfAGAAYQECAKMAhACFAJYAjgCLAQMAigDaAI0AwwDeAKIArQDJAMcArgBiAGMAkABkAMsAZQDIAMoAzwDMAM0AzgDpAGYA0wDQANEArwBnAJEA1gDUANUAaADrAO0AiQBqAGkAawBtAGwAbgCgAG8AcQBwAHIAcwB1AHQAdgB3AOoAeAB6AHkAewB9AHwAoQB/AH4AgACBAOwA7gC6ANcA4gDjALAAsQDkAOUAuwDmAOcA2ADhANsA3ADdAOAA2QDfAQQBBQEGAQcBCAEJAQoBCwEMAQ0BDgEPARABEQCyALMAtgC3AMQAtAC1AMUAhwCrARIBEwEUAIwA7wEVARYBFwd1bmkwMEEwB3VuaTAwQUQHdW5pMjAwMAd1bmkyMDAxB3VuaTIwMDIHdW5pMjAwMwd1bmkyMDA0B3VuaTIwMDUHdW5pMjAwNgd1bmkyMDA3B3VuaTIwMDgHdW5pMjAwOQd1bmkyMDBBB3VuaTIwMTAHdW5pMjAxMQpmaWd1cmVkYXNoB3VuaTIwMkYHdW5pMjA1RgRFdXJvB3VuaUUwMDAHdW5pRkIwMQd1bmlGQjAyAAAAAQAB//8ADwAAAAEAAAAAyYlvMQAAAADGp3m3AAAAAMrhKHcAAQAAAAwAAAAWAAAAAgABAAEA3wABAAQAAAABAAAAAAABAAAACgAuADwAAkRGTFQADmxhdG4AGAAEAAAAAP//AAAABAAAAAD//wABAAAAAVRTVDEACAAAAAEAAAABAAQAAQAAAAEACAACAA4ABABEADMANgBWAAEABAAkACkALAAvAAA=)")); + return document.fonts.ready; + }).then(() => { + is(document.fonts.size, 1, "should load data:font"); + SimpleTest.finish(); + }).catch((e) => { + ok(false, "throwing " + e); + SimpleTest.finish(); + }); +} +</script> + +<link rel="stylesheet" href="data:text/css,.green-text{color:rgb(0, 255, 0)}" + onload="ok(true, 'data:text/css should be same origin.');" + onerror="ok(false, 'data:text/css should be same origin');"> + +<body onload="runTests()"> +<img style="width: 100px; height: 100px;" + src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%18%00%00%00%18%02%03%00%00%00%9D%19%D5k%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%00%0CPLTE%FF%FF%FF%FF%FF%FF%F7%DC%13%00%00%00%03%80%01X%00%00%00%01tRNS%08N%3DPT%00%00%00%01bKGD%00%88%05%1DH%00%00%00%09pHYs%00%00%0B%11%00%00%0B%11%01%7Fd_%91%00%00%00%07tIME%07%D2%05%0C%14%0C%0D%D8%3F%1FQ%00%00%00%5CIDATx%9C%7D%8E%CB%09%C0%20%10D%07r%B7%20%2F%E9wV0%15h%EA%D9%12D4%BB%C1x%CC%5C%1E%0C%CC%07%C0%9C0%9Dd7()%C0A%D3%8D%E0%B8%10%1DiCHM%D0%AC%D2d%C3M%F1%B4%E7%FF%10%0BY%AC%25%93%CD%CBF%B5%B2%C0%3Alh%CD%AE%13%DF%A5%F7%E0%03byW%09A%B4%F3%E2%00%00%00%00IEND%AEB%60%82" + id="img"> +<iframe id="iframe"></iframe> +<iframe id="iframe1" ></iframe> +<canvas id="canvas" class="output" width="100" height="50"> +</body> +</html>
--- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -177,17 +177,17 @@ EVENT(auxclick, EventNameType_All, eMouseEventClass) EVENT(click, eMouseClick, EventNameType_All, eMouseEventClass) EVENT(close, eClose, - EventNameType_HTML, + EventNameType_HTMLXUL, eBasicEventClass) EVENT(contextmenu, eContextMenu, EventNameType_HTMLXUL, eMouseEventClass) NON_IDL_EVENT(mouselongtap, eMouseLongTap, EventNameType_HTMLXUL, @@ -779,20 +779,16 @@ NON_IDL_EVENT(compositionupdate, NON_IDL_EVENT(compositionend, eCompositionEnd, EventNameType_XUL, eCompositionEventClass) NON_IDL_EVENT(command, eXULCommand, EventNameType_XUL, eInputEventClass) -NON_IDL_EVENT(close, - eWindowClose, - EventNameType_XUL, - eBasicEventClass) NON_IDL_EVENT(popupshowing, eXULPopupShowing, EventNameType_XUL, eBasicEventClass) NON_IDL_EVENT(popupshown, eXULPopupShown, EventNameType_XUL, eBasicEventClass)
--- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -7411,266 +7411,17 @@ HTMLInputElement::GetValidationMessage(n { aRv = GetValidationMessage(aValidationMessage); } nsresult HTMLInputElement::GetValidationMessage(nsAString& aValidationMessage, ValidityStateType aType) { - nsresult rv = NS_OK; - - switch (aType) - { - case VALIDITY_STATE_TOO_LONG: - { - nsXPIDLString message; - int32_t maxLength = MaxLength(); - int32_t textLength = InputTextLength(CallerType::System); - nsAutoString strMaxLength; - nsAutoString strTextLength; - - strMaxLength.AppendInt(maxLength); - strTextLength.AppendInt(textLength); - - const char16_t* params[] = { strMaxLength.get(), strTextLength.get() }; - rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, - "FormValidationTextTooLong", - params, message); - aValidationMessage = message; - break; - } - case VALIDITY_STATE_TOO_SHORT: - { - nsXPIDLString message; - int32_t minLength = MinLength(); - int32_t textLength = InputTextLength(CallerType::System); - nsAutoString strMinLength; - nsAutoString strTextLength; - - strMinLength.AppendInt(minLength); - strTextLength.AppendInt(textLength); - - const char16_t* params[] = { strMinLength.get(), strTextLength.get() }; - rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, - "FormValidationTextTooShort", - params, message); - aValidationMessage = message; - break; - } - case VALIDITY_STATE_VALUE_MISSING: - { - nsXPIDLString message; - nsAutoCString key; - switch (mType) - { - case NS_FORM_INPUT_FILE: - key.AssignLiteral("FormValidationFileMissing"); - break; - case NS_FORM_INPUT_CHECKBOX: - key.AssignLiteral("FormValidationCheckboxMissing"); - break; - case NS_FORM_INPUT_RADIO: - key.AssignLiteral("FormValidationRadioMissing"); - break; - case NS_FORM_INPUT_NUMBER: - key.AssignLiteral("FormValidationBadInputNumber"); - break; - default: - key.AssignLiteral("FormValidationValueMissing"); - } - rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, - key.get(), message); - aValidationMessage = message; - break; - } - case VALIDITY_STATE_TYPE_MISMATCH: - { - nsXPIDLString message; - nsAutoCString key; - if (mType == NS_FORM_INPUT_EMAIL) { - key.AssignLiteral("FormValidationInvalidEmail"); - } else if (mType == NS_FORM_INPUT_URL) { - key.AssignLiteral("FormValidationInvalidURL"); - } else { - return NS_ERROR_UNEXPECTED; - } - rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, - key.get(), message); - aValidationMessage = message; - break; - } - case VALIDITY_STATE_PATTERN_MISMATCH: - { - nsXPIDLString message; - nsAutoString title; - GetAttr(kNameSpaceID_None, nsGkAtoms::title, title); - if (title.IsEmpty()) { - rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, - "FormValidationPatternMismatch", - message); - } else { - if (title.Length() > nsIConstraintValidation::sContentSpecifiedMaxLengthMessage) { - title.Truncate(nsIConstraintValidation::sContentSpecifiedMaxLengthMessage); - } - const char16_t* params[] = { title.get() }; - rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, - "FormValidationPatternMismatchWithTitle", - params, message); - } - aValidationMessage = message; - break; - } - case VALIDITY_STATE_RANGE_OVERFLOW: - { - static const char kNumberOverTemplate[] = "FormValidationNumberRangeOverflow"; - static const char kDateOverTemplate[] = "FormValidationDateRangeOverflow"; - static const char kTimeOverTemplate[] = "FormValidationTimeRangeOverflow"; - - const char* msgTemplate; - nsXPIDLString message; - - nsAutoString maxStr; - if (mType == NS_FORM_INPUT_NUMBER || - mType == NS_FORM_INPUT_RANGE) { - msgTemplate = kNumberOverTemplate; - - //We want to show the value as parsed when it's a number - Decimal maximum = GetMaximum(); - MOZ_ASSERT(!maximum.isNaN()); - - char buf[32]; - DebugOnly<bool> ok = maximum.toString(buf, ArrayLength(buf)); - maxStr.AssignASCII(buf); - MOZ_ASSERT(ok, "buf not big enough"); - } else if (IsDateTimeInputType(mType)) { - msgTemplate = mType == NS_FORM_INPUT_TIME ? kTimeOverTemplate : kDateOverTemplate; - GetAttr(kNameSpaceID_None, nsGkAtoms::max, maxStr); - } else { - msgTemplate = kNumberOverTemplate; - NS_NOTREACHED("Unexpected input type"); - } - - const char16_t* params[] = { maxStr.get() }; - rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, - msgTemplate, - params, message); - aValidationMessage = message; - break; - } - case VALIDITY_STATE_RANGE_UNDERFLOW: - { - static const char kNumberUnderTemplate[] = "FormValidationNumberRangeUnderflow"; - static const char kDateUnderTemplate[] = "FormValidationDateRangeUnderflow"; - static const char kTimeUnderTemplate[] = "FormValidationTimeRangeUnderflow"; - - const char* msgTemplate; - nsXPIDLString message; - - nsAutoString minStr; - if (mType == NS_FORM_INPUT_NUMBER || - mType == NS_FORM_INPUT_RANGE) { - msgTemplate = kNumberUnderTemplate; - - Decimal minimum = GetMinimum(); - MOZ_ASSERT(!minimum.isNaN()); - - char buf[32]; - DebugOnly<bool> ok = minimum.toString(buf, ArrayLength(buf)); - minStr.AssignASCII(buf); - MOZ_ASSERT(ok, "buf not big enough"); - } else if (IsDateTimeInputType(mType)) { - msgTemplate = mType == NS_FORM_INPUT_TIME ? kTimeUnderTemplate : kDateUnderTemplate; - GetAttr(kNameSpaceID_None, nsGkAtoms::min, minStr); - } else { - msgTemplate = kNumberUnderTemplate; - NS_NOTREACHED("Unexpected input type"); - } - - const char16_t* params[] = { minStr.get() }; - rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, - msgTemplate, - params, message); - aValidationMessage = message; - break; - } - case VALIDITY_STATE_STEP_MISMATCH: - { - nsXPIDLString message; - - Decimal value = GetValueAsDecimal(); - MOZ_ASSERT(!value.isNaN()); - - Decimal step = GetStep(); - MOZ_ASSERT(step != kStepAny && step > Decimal(0)); - - Decimal stepBase = GetStepBase(); - - Decimal valueLow = value - NS_floorModulo(value - stepBase, step); - Decimal valueHigh = value + step - NS_floorModulo(value - stepBase, step); - - Decimal maximum = GetMaximum(); - - if (maximum.isNaN() || valueHigh <= maximum) { - nsAutoString valueLowStr, valueHighStr; - mInputType->ConvertNumberToString(valueLow, valueLowStr); - mInputType->ConvertNumberToString(valueHigh, valueHighStr); - - if (valueLowStr.Equals(valueHighStr)) { - const char16_t* params[] = { valueLowStr.get() }; - rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, - "FormValidationStepMismatchOneValue", - params, message); - } else { - const char16_t* params[] = { valueLowStr.get(), valueHighStr.get() }; - rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, - "FormValidationStepMismatch", - params, message); - } - } else { - nsAutoString valueLowStr; - mInputType->ConvertNumberToString(valueLow, valueLowStr); - - const char16_t* params[] = { valueLowStr.get() }; - rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, - "FormValidationStepMismatchOneValue", - params, message); - } - - aValidationMessage = message; - break; - } - case VALIDITY_STATE_BAD_INPUT: - { - nsXPIDLString message; - nsAutoCString key; - if (mType == NS_FORM_INPUT_NUMBER) { - key.AssignLiteral("FormValidationBadInputNumber"); - } else if (mType == NS_FORM_INPUT_EMAIL) { - key.AssignLiteral("FormValidationInvalidEmail"); - } else if (mType == NS_FORM_INPUT_DATE && IsInputDateTimeEnabled()) { - // For input date and time, only date may have an invalid value, as - // we forbid or autocorrect values that are not in the valid range for - // time. For example, in 12hr format, if user enters '2' in the hour - // field, it will be treated as '02' and automatically advance to the - // next field. - key.AssignLiteral("FormValidationInvalidDate"); - } else { - return NS_ERROR_UNEXPECTED; - } - rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, - key.get(), message); - aValidationMessage = message; - break; - } - default: - rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType); - } - - return rv; + return mInputType->GetValidationMessage(aValidationMessage, aType); } NS_IMETHODIMP_(bool) HTMLInputElement::IsSingleLineTextControl() const { return IsSingleLineTextControl(false); }
--- a/dom/html/input/CheckableInputTypes.cpp +++ b/dom/html/input/CheckableInputTypes.cpp @@ -3,21 +3,41 @@ /* 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 "CheckableInputTypes.h" #include "mozilla/dom/HTMLInputElement.h" +/* input type=checkbox */ + bool CheckboxInputType::IsValueMissing() const { if (!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { return false; } if (!IsMutable()) { return false; } return !mInputElement->Checked(); } + +nsresult +CheckboxInputType::GetValueMissingMessage(nsXPIDLString& aMessage) +{ + return nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationCheckboxMissing", + aMessage); +} + +/* input type=radio */ + +nsresult +RadioInputType::GetValueMissingMessage(nsXPIDLString& aMessage) +{ + return nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationRadioMissing", + aMessage); +}
--- a/dom/html/input/CheckableInputTypes.h +++ b/dom/html/input/CheckableInputTypes.h @@ -27,31 +27,35 @@ public: static InputType* Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) { return new (aMemory) CheckboxInputType(aInputElement); } bool IsValueMissing() const override; + nsresult GetValueMissingMessage(nsXPIDLString& aMessage) override; + private: explicit CheckboxInputType(mozilla::dom::HTMLInputElement* aInputElement) : CheckableInputTypeBase(aInputElement) {} }; // input type=radio class RadioInputType : public CheckableInputTypeBase { public: static InputType* Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) { return new (aMemory) RadioInputType(aInputElement); } + nsresult GetValueMissingMessage(nsXPIDLString& aMessage) override; + private: explicit RadioInputType(mozilla::dom::HTMLInputElement* aInputElement) : CheckableInputTypeBase(aInputElement) {} }; #endif /* CheckableInputTypes_h__ */
--- a/dom/html/input/DateTimeInputTypes.cpp +++ b/dom/html/input/DateTimeInputTypes.cpp @@ -11,16 +11,31 @@ #include "nsDateTimeControlFrame.h" const double DateTimeInputTypeBase::kMinimumYear = 1; const double DateTimeInputTypeBase::kMaximumYear = 275760; const double DateTimeInputTypeBase::kMaximumMonthInMaximumYear = 9; const double DateTimeInputTypeBase::kMaximumWeekInMaximumYear = 37; const double DateTimeInputTypeBase::kMsPerDay = 24 * 60 * 60 * 1000; +/* static */ bool +DateTimeInputTypeBase::IsInputDateTimeEnabled() +{ + static bool sDateTimeEnabled = false; + static bool sDateTimePrefCached = false; + if (!sDateTimePrefCached) { + sDateTimePrefCached = true; + mozilla::Preferences::AddBoolVarCache(&sDateTimeEnabled, + "dom.forms.datetime", + false); + } + + return sDateTimeEnabled; +} + bool DateTimeInputTypeBase::IsMutable() const { return !mInputElement->IsDisabled() && !mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly); } bool @@ -98,16 +113,38 @@ DateTimeInputTypeBase::HasBadInput() con if (!frame) { return false; } return frame->HasBadInput();; } nsresult +DateTimeInputTypeBase::GetRangeOverflowMessage(nsXPIDLString& aMessage) +{ + nsAutoString maxStr; + mInputElement->GetAttr(kNameSpaceID_None, nsGkAtoms::max, maxStr); + + const char16_t* params[] = { maxStr.get() }; + return nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationDateTimeRangeOverflow", params, aMessage); +} + +nsresult +DateTimeInputTypeBase::GetRangeUnderflowMessage(nsXPIDLString& aMessage) +{ + nsAutoString minStr; + mInputElement->GetAttr(kNameSpaceID_None, nsGkAtoms::min, minStr); + + const char16_t* params[] = { minStr.get() }; + return nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationDateTimeRangeUnderflow", params, aMessage); +} + +nsresult DateTimeInputTypeBase::MinMaxStepAttrChanged() { nsDateTimeControlFrame* frame = do_QueryFrame(GetPrimaryFrame()); if (frame) { frame->OnMinMaxStepAttrChanged(); } return NS_OK; @@ -133,16 +170,27 @@ DateTimeInputTypeBase::GetTimeFromMs(dou *aHours = value; return true; } // input type=date +nsresult +DateInputType::GetBadInputMessage(nsXPIDLString& aMessage) +{ + if (!IsInputDateTimeEnabled()) { + return NS_ERROR_UNEXPECTED; + } + + return nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationInvalidDate", aMessage); +} + bool DateInputType::ConvertStringToNumber(nsAString& aValue, mozilla::Decimal& aResultValue) const { uint32_t year, month, day; if (!ParseDate(aValue, &year, &month, &day)) { return false; }
--- a/dom/html/input/DateTimeInputTypes.h +++ b/dom/html/input/DateTimeInputTypes.h @@ -15,23 +15,32 @@ public: ~DateTimeInputTypeBase() override {} bool IsValueMissing() const override; bool IsRangeOverflow() const override; bool IsRangeUnderflow() const override; bool HasStepMismatch(bool aUseZeroIfValueNaN) const override; bool HasBadInput() const override; + nsresult GetRangeOverflowMessage(nsXPIDLString& aMessage) override; + nsresult GetRangeUnderflowMessage(nsXPIDLString& aMessage) override; + nsresult MinMaxStepAttrChanged() override; protected: explicit DateTimeInputTypeBase(mozilla::dom::HTMLInputElement* aInputElement) : InputType(aInputElement) {} + /** + * Checks preference "dom.forms.datetime" to determine if input date and time + * should be supported. + */ + static bool IsInputDateTimeEnabled(); + bool IsMutable() const override; /** * This method converts aValue (milliseconds within a day) to hours, minutes, * seconds and milliseconds. */ bool GetTimeFromMs(double aValue, uint16_t* aHours, uint16_t* aMinutes, uint16_t* aSeconds, uint16_t* aMilliseconds) const; @@ -53,16 +62,22 @@ class DateInputType : public DateTimeInp { public: static InputType* Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) { return new (aMemory) DateInputType(aInputElement); } + // Currently, for input date and time, only date can have an invalid value, as + // we forbid or autocorrect values that are not in the valid range for time. + // For example, in 12hr format, if user enters '2' in the hour field, it will + // be treated as '02' and automatically advance to the next field. + nsresult GetBadInputMessage(nsXPIDLString& aMessage) override; + bool ConvertStringToNumber(nsAString& aValue, mozilla::Decimal& aResultValue) const override; bool ConvertNumberToString(mozilla::Decimal aValue, nsAString& aResultString) const override; private: explicit DateInputType(mozilla::dom::HTMLInputElement* aInputElement) : DateTimeInputTypeBase(aInputElement)
--- a/dom/html/input/FileInputType.cpp +++ b/dom/html/input/FileInputType.cpp @@ -16,8 +16,16 @@ FileInputType::IsValueMissing() const } if (!IsMutable()) { return false; } return mInputElement->GetFilesOrDirectoriesInternal().IsEmpty(); } + +nsresult +FileInputType::GetValueMissingMessage(nsXPIDLString& aMessage) +{ + return nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationFileMissing", + aMessage); +}
--- a/dom/html/input/FileInputType.h +++ b/dom/html/input/FileInputType.h @@ -16,15 +16,17 @@ public: static InputType* Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) { return new (aMemory) FileInputType(aInputElement); } bool IsValueMissing() const override; + nsresult GetValueMissingMessage(nsXPIDLString& aMessage) override; + private: explicit FileInputType(mozilla::dom::HTMLInputElement* aInputElement) : InputType(aInputElement) {} }; #endif /* FileInputType_h__ */
--- a/dom/html/input/InputType.cpp +++ b/dom/html/input/InputType.cpp @@ -11,16 +11,18 @@ #include "CheckableInputTypes.h" #include "ColorInputType.h" #include "DateTimeInputTypes.h" #include "FileInputType.h" #include "HiddenInputType.h" #include "NumericInputTypes.h" #include "SingleLineTextInputTypes.h" +#include "nsContentUtils.h" + const mozilla::Decimal InputType::kStepAny = mozilla::Decimal(0); /* static */ mozilla::UniquePtr<InputType, DoNotDelete> InputType::Create(mozilla::dom::HTMLInputElement* aInputElement, uint8_t aType, void* aMemory) { mozilla::UniquePtr<InputType, DoNotDelete> inputType; switch(aType) { @@ -196,16 +198,226 @@ InputType::HasStepMismatch(bool aUseZero bool InputType::HasBadInput() const { return false; } nsresult +InputType::GetValidationMessage(nsAString& aValidationMessage, + nsIConstraintValidation::ValidityStateType aType) +{ + nsresult rv = NS_OK; + + switch (aType) + { + case nsIConstraintValidation::VALIDITY_STATE_TOO_LONG: + { + nsXPIDLString message; + int32_t maxLength = mInputElement->MaxLength(); + int32_t textLength = + mInputElement->InputTextLength(mozilla::dom::CallerType::System); + nsAutoString strMaxLength; + nsAutoString strTextLength; + + strMaxLength.AppendInt(maxLength); + strTextLength.AppendInt(textLength); + + const char16_t* params[] = { strMaxLength.get(), strTextLength.get() }; + rv = nsContentUtils::FormatLocalizedString( + nsContentUtils::eDOM_PROPERTIES, "FormValidationTextTooLong", + params, message); + aValidationMessage = message; + break; + } + case nsIConstraintValidation::VALIDITY_STATE_TOO_SHORT: + { + nsXPIDLString message; + int32_t minLength = mInputElement->MinLength(); + int32_t textLength = + mInputElement->InputTextLength(mozilla::dom::CallerType::System); + nsAutoString strMinLength; + nsAutoString strTextLength; + + strMinLength.AppendInt(minLength); + strTextLength.AppendInt(textLength); + + const char16_t* params[] = { strMinLength.get(), strTextLength.get() }; + rv = nsContentUtils::FormatLocalizedString( + nsContentUtils::eDOM_PROPERTIES, "FormValidationTextTooShort", + params, message); + + aValidationMessage = message; + break; + } + case nsIConstraintValidation::VALIDITY_STATE_VALUE_MISSING: + { + nsXPIDLString message; + rv = GetValueMissingMessage(message); + if (NS_FAILED(rv)) { + return rv; + } + + aValidationMessage = message; + break; + } + case nsIConstraintValidation::VALIDITY_STATE_TYPE_MISMATCH: + { + nsXPIDLString message; + rv = GetTypeMismatchMessage(message); + if (NS_FAILED(rv)) { + return rv; + } + + aValidationMessage = message; + break; + } + case nsIConstraintValidation::VALIDITY_STATE_PATTERN_MISMATCH: + { + nsXPIDLString message; + nsAutoString title; + mInputElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, title); + if (title.IsEmpty()) { + rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationPatternMismatch", message); + } else { + if (title.Length() > + nsIConstraintValidation::sContentSpecifiedMaxLengthMessage) { + title.Truncate( + nsIConstraintValidation::sContentSpecifiedMaxLengthMessage); + } + const char16_t* params[] = { title.get() }; + rv = nsContentUtils::FormatLocalizedString( + nsContentUtils::eDOM_PROPERTIES, + "FormValidationPatternMismatchWithTitle", params, message); + } + aValidationMessage = message; + break; + } + case nsIConstraintValidation::VALIDITY_STATE_RANGE_OVERFLOW: + { + nsXPIDLString message; + rv = GetRangeOverflowMessage(message); + if (NS_FAILED(rv)) { + return rv; + } + + aValidationMessage = message; + break; + } + case nsIConstraintValidation::VALIDITY_STATE_RANGE_UNDERFLOW: + { + nsXPIDLString message; + rv = GetRangeUnderflowMessage(message); + if (NS_FAILED(rv)) { + return rv; + } + + aValidationMessage = message; + break; + } + case nsIConstraintValidation::VALIDITY_STATE_STEP_MISMATCH: + { + nsXPIDLString message; + + mozilla::Decimal value = mInputElement->GetValueAsDecimal(); + MOZ_ASSERT(!value.isNaN()); + + mozilla::Decimal step = mInputElement->GetStep(); + MOZ_ASSERT(step != kStepAny && step > mozilla::Decimal(0)); + + mozilla::Decimal stepBase = mInputElement->GetStepBase(); + + mozilla::Decimal valueLow = + value - NS_floorModulo(value - stepBase, step); + mozilla::Decimal valueHigh = + value + step - NS_floorModulo(value - stepBase, step); + + mozilla::Decimal maximum = mInputElement->GetMaximum(); + + if (maximum.isNaN() || valueHigh <= maximum) { + nsAutoString valueLowStr, valueHighStr; + ConvertNumberToString(valueLow, valueLowStr); + ConvertNumberToString(valueHigh, valueHighStr); + + if (valueLowStr.Equals(valueHighStr)) { + const char16_t* params[] = { valueLowStr.get() }; + rv = nsContentUtils::FormatLocalizedString( + nsContentUtils::eDOM_PROPERTIES, + "FormValidationStepMismatchOneValue", params, message); + } else { + const char16_t* params[] = { valueLowStr.get(), valueHighStr.get() }; + rv = nsContentUtils::FormatLocalizedString( + nsContentUtils::eDOM_PROPERTIES, + "FormValidationStepMismatch", params, message); + } + } else { + nsAutoString valueLowStr; + ConvertNumberToString(valueLow, valueLowStr); + + const char16_t* params[] = { valueLowStr.get() }; + rv = nsContentUtils::FormatLocalizedString( + nsContentUtils::eDOM_PROPERTIES, + "FormValidationStepMismatchOneValue", params, message); + } + + aValidationMessage = message; + break; + } + case nsIConstraintValidation::VALIDITY_STATE_BAD_INPUT: + { + nsXPIDLString message; + rv = GetBadInputMessage(message); + if (NS_FAILED(rv)) { + return rv; + } + + aValidationMessage = message; + break; + } + default: + return NS_ERROR_UNEXPECTED; + } + + return rv; +} + +nsresult +InputType::GetValueMissingMessage(nsXPIDLString& aMessage) +{ + return nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationValueMissing", aMessage); +} + +nsresult +InputType::GetTypeMismatchMessage(nsXPIDLString& aMessage) +{ + return NS_ERROR_UNEXPECTED; +} + +nsresult +InputType::GetRangeOverflowMessage(nsXPIDLString& aMessage) +{ + return NS_ERROR_UNEXPECTED; +} + +nsresult +InputType::GetRangeUnderflowMessage(nsXPIDLString& aMessage) +{ + return NS_ERROR_UNEXPECTED; +} + +nsresult +InputType::GetBadInputMessage(nsXPIDLString& aMessage) +{ + return NS_ERROR_UNEXPECTED; +} + +nsresult InputType::MinMaxStepAttrChanged() { return NS_OK; } bool InputType::ConvertStringToNumber(nsAString& aValue, mozilla::Decimal& aResultValue) const
--- a/dom/html/input/InputType.h +++ b/dom/html/input/InputType.h @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef InputType_h__ #define InputType_h__ #include <stdint.h> #include "mozilla/Decimal.h" #include "mozilla/UniquePtr.h" +#include "nsIConstraintValidation.h" #include "nsString.h" #include "nsError.h" // This must come outside of any namespace, or else it won't overload with the // double based version in nsMathUtils.h inline mozilla::Decimal NS_floorModulo(mozilla::Decimal x, mozilla::Decimal y) { @@ -55,16 +56,24 @@ public: virtual bool IsValueMissing() const; virtual bool HasTypeMismatch() const; virtual bool HasPatternMismatch() const; virtual bool IsRangeOverflow() const; virtual bool IsRangeUnderflow() const; virtual bool HasStepMismatch(bool aUseZeroIfValueNaN) const; virtual bool HasBadInput() const; + nsresult GetValidationMessage(nsAString& aValidationMessage, + nsIConstraintValidation::ValidityStateType aType); + virtual nsresult GetValueMissingMessage(nsXPIDLString& aMessage); + virtual nsresult GetTypeMismatchMessage(nsXPIDLString& aMessage); + virtual nsresult GetRangeOverflowMessage(nsXPIDLString& aMessage); + virtual nsresult GetRangeUnderflowMessage(nsXPIDLString& aMessage); + virtual nsresult GetBadInputMessage(nsXPIDLString& aMessage); + virtual nsresult MinMaxStepAttrChanged(); /** * Convert a string to a Decimal number in a type specific way, * http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#concept-input-value-string-number * ie parse a date string to a timestamp if type=date, * or parse a number string to its value if type=number. * @param aValue the string to be parsed.
--- a/dom/html/input/NumericInputTypes.cpp +++ b/dom/html/input/NumericInputTypes.cpp @@ -6,23 +6,16 @@ #include "NumericInputTypes.h" #include "mozilla/dom/HTMLInputElement.h" #include "nsNumberControlFrame.h" #include "nsTextEditorState.h" bool -NumberInputType::IsMutable() const -{ - return !mInputElement->IsDisabled() && - !mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly); -} - -bool NumericInputTypeBase::IsRangeOverflow() const { mozilla::Decimal maximum = mInputElement->GetMaximum(); if (maximum.isNaN()) { return false; } mozilla::Decimal value = mInputElement->GetValueAsDecimal(); @@ -66,16 +59,53 @@ NumericInputTypeBase::HasStepMismatch(bo if (step == kStepAny) { return false; } // Value has to be an integral multiple of step. return NS_floorModulo(value - GetStepBase(), step) != mozilla::Decimal(0); } +nsresult +NumericInputTypeBase::GetRangeOverflowMessage(nsXPIDLString& aMessage) +{ + // We want to show the value as parsed when it's a number + mozilla::Decimal maximum = mInputElement->GetMaximum(); + MOZ_ASSERT(!maximum.isNaN()); + + nsAutoString maxStr; + char buf[32]; + mozilla::DebugOnly<bool> ok = maximum.toString(buf, + mozilla::ArrayLength(buf)); + maxStr.AssignASCII(buf); + MOZ_ASSERT(ok, "buf not big enough"); + + const char16_t* params[] = { maxStr.get() }; + return nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationNumberRangeOverflow", params, aMessage); +} + +nsresult +NumericInputTypeBase::GetRangeUnderflowMessage(nsXPIDLString& aMessage) +{ + mozilla::Decimal minimum = mInputElement->GetMinimum(); + MOZ_ASSERT(!minimum.isNaN()); + + nsAutoString minStr; + char buf[32]; + mozilla::DebugOnly<bool> ok = minimum.toString(buf, + mozilla::ArrayLength(buf)); + minStr.AssignASCII(buf); + MOZ_ASSERT(ok, "buf not big enough"); + + const char16_t* params[] = { minStr.get() }; + return nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationNumberRangeUnderflow", params, aMessage); +} + bool NumericInputTypeBase::ConvertStringToNumber(nsAString& aValue, mozilla::Decimal& aResultValue) const { aResultValue = mozilla::dom::HTMLInputElement::StringToDecimal(aValue); if (!aResultValue.isFinite()) { return false; } @@ -131,16 +161,37 @@ NumberInputType::HasBadInput() const if (numberControlFrame && !numberControlFrame->AnonTextControlIsEmpty()) { // The input the user entered failed to parse as a number. return true; } return false; } +nsresult +NumberInputType::GetValueMissingMessage(nsXPIDLString& aMessage) +{ + return nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationBadInputNumber", aMessage); +} + +nsresult +NumberInputType::GetBadInputMessage(nsXPIDLString& aMessage) +{ + return nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationBadInputNumber", aMessage); +} + +bool +NumberInputType::IsMutable() const +{ + return !mInputElement->IsDisabled() && + !mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly); +} + /* input type=range */ nsresult RangeInputType::MinMaxStepAttrChanged() { // The value may need to change when @min/max/step changes since the value may // have been invalid and can now change to a valid value, or vice versa. For // example, consider: <input type=range value=-1 max=1 step=3>. The valid // range is 0 to 1 while the nearest valid steps are -1 and 2 (the max value
--- a/dom/html/input/NumericInputTypes.h +++ b/dom/html/input/NumericInputTypes.h @@ -13,16 +13,19 @@ class NumericInputTypeBase : public ::In { public: ~NumericInputTypeBase() override {} bool IsRangeOverflow() const override; bool IsRangeUnderflow() const override; bool HasStepMismatch(bool aUseZeroIfValueNaN) const override; + nsresult GetRangeOverflowMessage(nsXPIDLString& aMessage) override; + nsresult GetRangeUnderflowMessage(nsXPIDLString& aMessage) override; + bool ConvertStringToNumber(nsAString& aValue, mozilla::Decimal& aResultValue) const override; bool ConvertNumberToString(mozilla::Decimal aValue, nsAString& aResultString) const override; protected: explicit NumericInputTypeBase(mozilla::dom::HTMLInputElement* aInputElement) : InputType(aInputElement) @@ -37,22 +40,26 @@ public: Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) { return new (aMemory) NumberInputType(aInputElement); } bool IsValueMissing() const override; bool HasBadInput() const override; + nsresult GetValueMissingMessage(nsXPIDLString& aMessage) override; + nsresult GetBadInputMessage(nsXPIDLString& aMessage) override; + +protected: + bool IsMutable() const override; + private: explicit NumberInputType(mozilla::dom::HTMLInputElement* aInputElement) : NumericInputTypeBase(aInputElement) {} - - bool IsMutable() const override; }; // input type=range class RangeInputType : public NumericInputTypeBase { public: static InputType* Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory)
--- a/dom/html/input/SingleLineTextInputTypes.cpp +++ b/dom/html/input/SingleLineTextInputTypes.cpp @@ -115,16 +115,24 @@ URLInputType::HasTypeMismatch() const nsCOMPtr<nsIIOService> ioService = do_GetIOService(); nsCOMPtr<nsIURI> uri; return !NS_SUCCEEDED(ioService->NewURI(NS_ConvertUTF16toUTF8(value), nullptr, nullptr, getter_AddRefs(uri))); } +nsresult +URLInputType::GetTypeMismatchMessage(nsXPIDLString& aMessage) +{ + return nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationInvalidURL", + aMessage); +} + /* input type=email */ bool EmailInputType::HasTypeMismatch() const { nsAutoString value; GetNonFileValueInternal(value); @@ -151,16 +159,32 @@ EmailInputType::HasBadInput() const while (tokenizer.hasMoreTokens()) { if (!PunycodeEncodeEmailAddress(tokenizer.nextToken(), unused, &unused2)) { return true; } } return false; } +nsresult +EmailInputType::GetTypeMismatchMessage(nsXPIDLString& aMessage) +{ + return nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationInvalidEmail", + aMessage); +} + +nsresult +EmailInputType::GetBadInputMessage(nsXPIDLString& aMessage) +{ + return nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationInvalidEmail", + aMessage); +} + /* static */ bool EmailInputType::IsValidEmailAddressList(const nsAString& aValue) { HTMLSplitOnSpacesTokenizer tokenizer(aValue, ','); while (tokenizer.hasMoreTokens()) { if (!IsValidEmailAddress(tokenizer.nextToken())) { return false;
--- a/dom/html/input/SingleLineTextInputTypes.h +++ b/dom/html/input/SingleLineTextInputTypes.h @@ -83,16 +83,18 @@ public: static InputType* Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) { return new (aMemory) URLInputType(aInputElement); } bool HasTypeMismatch() const override; + nsresult GetTypeMismatchMessage(nsXPIDLString& aMessage) override; + private: explicit URLInputType(mozilla::dom::HTMLInputElement* aInputElement) : SingleLineTextInputTypeBase(aInputElement) {} }; // input type=email class EmailInputType : public SingleLineTextInputTypeBase @@ -102,16 +104,19 @@ public: Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) { return new (aMemory) EmailInputType(aInputElement); } bool HasTypeMismatch() const override; bool HasBadInput() const override; + nsresult GetTypeMismatchMessage(nsXPIDLString& aMessage) override; + nsresult GetBadInputMessage(nsXPIDLString& aMessage) override; + private: explicit EmailInputType(mozilla::dom::HTMLInputElement* aInputElement) : SingleLineTextInputTypeBase(aInputElement) {} /** * This helper method returns true if aValue is a valid email address. * This is following the HTML5 specification:
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -598,16 +598,18 @@ static const char* sObserverTopics[] = { // ContentParent then takes this process back within GetNewOrUsedBrowserProcess. /*static*/ already_AddRefed<ContentParent> ContentParent::PreallocateProcess() { RefPtr<ContentParent> process = new ContentParent(/* aOpener = */ nullptr, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)); + PreallocatedProcessManager::AddBlocker(process); + if (!process->LaunchSubprocess(PROCESS_PRIORITY_PREALLOC)) { return nullptr; } process->Init(); return process.forget(); } @@ -875,16 +877,19 @@ ContentParent::GetNewOrUsedBrowserProces p->mActivateTS = TimeStamp::Now(); return p.forget(); } } // Create a new process from scratch. RefPtr<ContentParent> p = new ContentParent(aOpener, aRemoteType); + // Until the new process is ready let's not allow to start up any preallocated processes. + PreallocatedProcessManager::AddBlocker(p); + if (!p->LaunchSubprocess(aPriority)) { return nullptr; } p->Init(); contentParents.AppendElement(p); p->mActivateTS = TimeStamp::Now(); @@ -2721,20 +2726,19 @@ ContentParent::RecvGetShowPasswordSettin #endif return IPC_OK(); } mozilla::ipc::IPCResult ContentParent::RecvFirstIdle() { // When the ContentChild goes idle, it sends us a FirstIdle message - // which we use as a good time to prelaunch another process. If we - // prelaunch any sooner than this, then we'll be competing with the - // child process and slowing it down. - PreallocatedProcessManager::AllocateAfterDelay(); + // which we use as a good time to signal the PreallocatedProcessManager + // that it can start allocating processes from now on. + PreallocatedProcessManager::RemoveBlocker(this); return IPC_OK(); } // We want ContentParent to show up in CC logs for debugging purposes, but we // don't actually cycle collect it. NS_IMPL_CYCLE_COLLECTION_0(ContentParent) NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentParent)
--- a/dom/ipc/PreallocatedProcessManager.cpp +++ b/dom/ipc/PreallocatedProcessManager.cpp @@ -34,65 +34,61 @@ class PreallocatedProcessManagerImpl fin { public: static PreallocatedProcessManagerImpl* Singleton(); NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER // See comments on PreallocatedProcessManager for these methods. - void AllocateAfterDelay(); - void AllocateOnIdle(); - void AllocateNow(); + void AddBlocker(ContentParent* aParent); + void RemoveBlocker(ContentParent* aParent); already_AddRefed<ContentParent> Take(); bool Provide(ContentParent* aParent); private: static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton; PreallocatedProcessManagerImpl(); ~PreallocatedProcessManagerImpl() {} DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManagerImpl); void Init(); + bool CanAllocate(); + void AllocateAfterDelay(); + void AllocateOnIdle(); + void AllocateNow(); + void RereadPrefs(); void Enable(); void Disable(); void CloseProcess(); void ObserveProcessShutdown(nsISupports* aSubject); bool mEnabled; bool mShutdown; RefPtr<ContentParent> mPreallocatedProcess; + nsTHashtable<nsUint64HashKey> mBlockers; }; /* static */ StaticRefPtr<PreallocatedProcessManagerImpl> PreallocatedProcessManagerImpl::sSingleton; /* static */ PreallocatedProcessManagerImpl* PreallocatedProcessManagerImpl::Singleton() { MOZ_ASSERT(NS_IsMainThread()); if (!sSingleton) { sSingleton = new PreallocatedProcessManagerImpl(); sSingleton->Init(); ClearOnShutdown(&sSingleton); } - // First time when we init sSingleton, the pref database might not be in a - // reliable state (we are too early), so despite dom.ipc.processPrelaunch.enabled - // is set to true Preferences::GetBool will return false (it cannot find the pref). - // Since Init() above will be called only once, and the pref will not be changed, - // the manger will stay disabled. To prevent that let's re-read the pref each time - // someone accessing the manager singleton. This is a hack but this is not a hot code - // so it should be fine. - sSingleton->RereadPrefs(); - return sSingleton; } NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver) PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl() : mEnabled(false) , mShutdown(false) @@ -163,16 +159,21 @@ PreallocatedProcessManagerImpl::RereadPr already_AddRefed<ContentParent> PreallocatedProcessManagerImpl::Take() { if (!mEnabled || mShutdown) { return nullptr; } + if (mPreallocatedProcess) { + // The preallocated process is taken. Let's try to start up a new one soon. + AllocateOnIdle(); + } + return mPreallocatedProcess.forget(); } bool PreallocatedProcessManagerImpl::Provide(ContentParent* aParent) { if (mEnabled && !mShutdown && !mPreallocatedProcess) { mPreallocatedProcess = aParent; @@ -191,51 +192,80 @@ PreallocatedProcessManagerImpl::Enable() return; } mEnabled = true; AllocateAfterDelay(); } void +PreallocatedProcessManagerImpl::AddBlocker(ContentParent* aParent) +{ + uint64_t childID = aParent->ChildID(); + MOZ_ASSERT(!mBlockers.Contains(childID)); + mBlockers.PutEntry(childID); +} + +void +PreallocatedProcessManagerImpl::RemoveBlocker(ContentParent* aParent) +{ + uint64_t childID = aParent->ChildID(); + MOZ_ASSERT(mBlockers.Contains(childID)); + mBlockers.RemoveEntry(childID); + if (!mPreallocatedProcess && mBlockers.IsEmpty()) { + AllocateAfterDelay(); + } +} + +bool +PreallocatedProcessManagerImpl::CanAllocate() +{ + return mEnabled && + mBlockers.IsEmpty() && + !mPreallocatedProcess && + !mShutdown && + !ContentParent::IsMaxProcessCountReached(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)); +} + +void PreallocatedProcessManagerImpl::AllocateAfterDelay() { - if (!mEnabled || mPreallocatedProcess || mShutdown) { + if (!mEnabled) { return; } - // Originally AllocateOnIdle() was post here, but since the gecko parent - // message loop in practice never goes idle, that didn't work out well. - // Let's just launch the process after the delay. NS_DelayedDispatchToCurrentThread( - NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateNow", + NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateOnIdle", this, - &PreallocatedProcessManagerImpl::AllocateNow), + &PreallocatedProcessManagerImpl::AllocateOnIdle), Preferences::GetUint("dom.ipc.processPrelaunch.delayMs", DEFAULT_ALLOCATE_DELAY)); } void PreallocatedProcessManagerImpl::AllocateOnIdle() { - if (!mEnabled || mPreallocatedProcess || mShutdown) { + if (!mEnabled) { return; } NS_IdleDispatchToCurrentThread( NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateNow", this, &PreallocatedProcessManagerImpl::AllocateNow)); } void PreallocatedProcessManagerImpl::AllocateNow() { - if (!mEnabled || mPreallocatedProcess || mShutdown || - ContentParent::IsMaxProcessCountReached(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) { + if (!CanAllocate()) { + if (mEnabled && !mShutdown && !mPreallocatedProcess && !mBlockers.IsEmpty()) { + // If it's too early to allocate a process let's retry later. + AllocateAfterDelay(); + } return; } mPreallocatedProcess = ContentParent::PreallocateProcess(); } void PreallocatedProcessManagerImpl::Disable() @@ -255,53 +285,45 @@ PreallocatedProcessManagerImpl::ClosePro mPreallocatedProcess->ShutDownProcess(ContentParent::SEND_SHUTDOWN_MESSAGE); mPreallocatedProcess = nullptr; } } void PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject) { - if (!mPreallocatedProcess) { - return; - } - nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject); 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); - if (childID == mPreallocatedProcess->ChildID()) { + if (mPreallocatedProcess && childID == mPreallocatedProcess->ChildID()) { mPreallocatedProcess = nullptr; } + + mBlockers.RemoveEntry(childID); } inline PreallocatedProcessManagerImpl* GetPPMImpl() { return PreallocatedProcessManagerImpl::Singleton(); } /* static */ void -PreallocatedProcessManager::AllocateAfterDelay() +PreallocatedProcessManager::AddBlocker(ContentParent* aParent) { - GetPPMImpl()->AllocateAfterDelay(); + GetPPMImpl()->AddBlocker(aParent); } /* static */ void -PreallocatedProcessManager::AllocateOnIdle() +PreallocatedProcessManager::RemoveBlocker(ContentParent* aParent) { - GetPPMImpl()->AllocateOnIdle(); -} - -/* static */ void -PreallocatedProcessManager::AllocateNow() -{ - GetPPMImpl()->AllocateNow(); + GetPPMImpl()->RemoveBlocker(aParent); } /* static */ already_AddRefed<ContentParent> PreallocatedProcessManager::Take() { return GetPPMImpl()->Take(); }
--- a/dom/ipc/PreallocatedProcessManager.h +++ b/dom/ipc/PreallocatedProcessManager.h @@ -28,41 +28,24 @@ class ContentParent; * We don't expect this pref to flip between true and false in production, but * flipping the pref is important for tests. */ class PreallocatedProcessManager final { typedef mozilla::dom::ContentParent ContentParent; public: - /** - * Create a process after a delay. We wait for a period of time (specified - * by the dom.ipc.processPrelaunch.delayMs pref), then wait for this process - * to go idle, then allocate the new process. - * - * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already - * have a preallocated process, this function does nothing. - */ - static void AllocateAfterDelay(); /** - * Create a process once this process goes idle. - * - * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already - * have a preallocated process, this function does nothing. + * Before first paint we don't want to allocate any processes in the background. + * To avoid that, the PreallocatedProcessManager won't start up any processes while + * there is a blocker active. */ - static void AllocateOnIdle(); - - /** - * Create a process right now. - * - * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already - * have a preallocated process, this function does nothing. - */ - static void AllocateNow(); + static void AddBlocker(ContentParent* aParent); + static void RemoveBlocker(ContentParent* aParent); /** * Take the preallocated process, if we have one. If we don't have one, this * returns null. * * If you call Take() twice in a row, the second call is guaranteed to return * null. *
--- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -34,26 +34,22 @@ FormValidationSelectMissing=Please selec FormValidationInvalidEmail=Please enter an email address. FormValidationInvalidURL=Please enter a URL. FormValidationInvalidDate =Please enter a valid date. FormValidationPatternMismatch=Please match the requested format. # LOCALIZATION NOTE (FormValidationPatternMismatchWithTitle): %S is the (possibly truncated) title attribute value. FormValidationPatternMismatchWithTitle=Please match the requested format: %S. # LOCALIZATION NOTE (FormValidationNumberRangeOverflow): %S is a number. FormValidationNumberRangeOverflow=Please select a value that is no more than %S. -# LOCALIZATION NOTE (FormValidationDateRangeOverflow): %S is a date. -FormValidationDateRangeOverflow=Please select a value that is no later than %S. -# LOCALIZATION NOTE (FormValidationTimeRangeOverflow): %S is a time. -FormValidationTimeRangeOverflow=Please select a value that is no later than %S. +# LOCALIZATION NOTE (FormValidationDateRangeOverflow): %S is a date or time. +FormValidationDateTimeRangeOverflow=Please select a value that is no later than %S. # LOCALIZATION NOTE (FormValidationNumberRangeUnderflow): %S is a number. FormValidationNumberRangeUnderflow=Please select a value that is no less than %S. -# LOCALIZATION NOTE (FormValidationDateRangeUnderflow): %S is a date. -FormValidationDateRangeUnderflow=Please select a value that is no earlier than %S. -# LOCALIZATION NOTE (FormValidationTimeRangeUnderflow): %S is a time. -FormValidationTimeRangeUnderflow=Please select a value that is no earlier than %S. +# LOCALIZATION NOTE (FormValidationDateRangeUnderflow): %S is a date or time. +FormValidationDateTimeRangeUnderflow=Please select a value that is no earlier than %S. # LOCALIZATION NOTE (FormValidationStepMismatch): both %S can be a number, a date or a time. FormValidationStepMismatch=Please select a valid value. The two nearest valid values are %S and %S. # LOCALIZATION NOTE (FormValidationStepMismatchOneValue): %S can be a number, a date or a time. This is called instead of FormValidationStepMismatch when the second value is the same as the first. FormValidationStepMismatchOneValue=Please select a valid value. The nearest valid value is %S. FormValidationBadInputNumber=Please enter a number. GetAttributeNodeWarning=Use of getAttributeNode() is deprecated. Use getAttribute() instead. SetAttributeNodeWarning=Use of setAttributeNode() is deprecated. Use setAttribute() instead. GetAttributeNodeNSWarning=Use of getAttributeNodeNS() is deprecated. Use getAttributeNS() instead.
--- a/dom/media/MediaRecorder.cpp +++ b/dom/media/MediaRecorder.cpp @@ -454,16 +454,24 @@ public: void NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack) override { LOG(LogLevel::Warning, ("Session.NotifyTrackAdded %p Raising error due to track set change", this)); DoSessionEndTask(NS_ERROR_ABORT); } void NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack) override { + // Handle possible early removal of direct video listener + if (mEncoder) { + RefPtr<VideoStreamTrack> videoTrack = aTrack->AsVideoStreamTrack(); + if (videoTrack) { + videoTrack->RemoveDirectListener(mEncoder->GetVideoSink()); + } + } + RefPtr<MediaInputPort> foundInputPort; for (RefPtr<MediaInputPort> inputPort : mInputPorts) { if (aTrack->IsForwardedThrough(inputPort)) { foundInputPort = inputPort; break; } }
--- a/dom/plugins/base/nsPluginTags.cpp +++ b/dom/plugins/base/nsPluginTags.cpp @@ -6,17 +6,16 @@ #include "nsPluginTags.h" #include "prlink.h" #include "plstr.h" #include "nsIPluginInstanceOwner.h" #include "nsPluginsDir.h" #include "nsPluginHost.h" #include "nsIBlocklistService.h" -#include "nsIPlatformCharset.h" #include "nsPluginLogging.h" #include "nsNPAPIPlugin.h" #include "nsCharSeparatedTokenizer.h" #include "mozilla/Preferences.h" #include "mozilla/Unused.h" #include "nsNetUtil.h" #include <cctype> #include "mozilla/Encoding.h"
--- a/dom/security/test/sri/iframe_style_crossdomain.html +++ b/dom/security/test/sri/iframe_style_crossdomain.html @@ -40,21 +40,21 @@ function good_incorrectHashBlocked() { ok(true, "A non-CORS cross-domain stylesheet with incorrect hash was correctly blocked."); } function bad_incorrectHashLoaded() { ok(false, "We should load non-CORS cross-domain stylesheets with incorrect hashes!"); } - function good_correctDataBlocked() { - ok(true, "We should block CORS cross-domain stylesheets in data: URI!"); + function bad_correctDataBlocked() { + ok(false, "We should not block non-CORS cross-domain stylesheets in data: URI!"); } - function bad_correctDataLoaded() { - ok(false, "A CORS cross-domain stylesheet with data: URI was correctly loaded."); + function good_correctDataLoaded() { + ok(true, "A non-CORS cross-domain stylesheet with data: URI was correctly loaded."); } function bad_correctDataCORSBlocked() { ok(false, "We should not block CORS stylesheets in data: URI!"); } function good_correctDataCORSLoaded() { ok(true, "A CORS stylesheet with data: URI was correctly loaded."); } @@ -80,28 +80,21 @@ onload="bad_correctHashLoaded()"> <!-- invalid non-CORS sha256 hash --> <link rel="stylesheet" href="style_301.css?again" integrity="sha256-bogus" onerror="good_incorrectHashBlocked()" onload="bad_incorrectHashLoaded()"> - <!-- valid CORS sha256 hash in a data: URL, but didn't specify crossorigin --> + <!-- valid non-CORS sha256 hash in a data: URL --> <link rel="stylesheet" href="data:text/css,.green-text{color:rgb(0, 255, 0)}" integrity="sha256-EhVtGGyovvffvYdhyqJxUJ/ekam7zlxxo46iM13cwP0=" - onerror="good_correctDataBlocked()" - onload="bad_correctDataLoaded()"> - - <!-- valid CORS sha256 hash in a data: URL --> - <link rel="stylesheet" href="data:text/css,.green-text{color:rgb(0, 255, 0)}" - integrity="sha256-EhVtGGyovvffvYdhyqJxUJ/ekam7zlxxo46iM13cwP0=" - crossorigin="anonymous" - onerror="bad_correctDataCORSBlocked()" - onload="good_correctDataCORSLoaded()"> + onerror="bad_correctDataBlocked()" + onload="good_correctDataLoaded()"> <!-- valid CORS sha256 hash in a data: URL --> <link rel="stylesheet" href="data:text/css,.blue-text{color:rgb(0, 0, 255)}" crossorigin="anonymous" integrity="sha256-m0Fs2hNSyPOn1030Dp+c8pJFHNmwpeTbB+8J/DcqLss=" onerror="bad_correctDataCORSBlocked()" onload="good_correctDataCORSLoaded()">
deleted file mode 100644 --- a/dom/security/test/sri/iframe_style_crossdomain_legacy.html +++ /dev/null @@ -1,117 +0,0 @@ -<!DOCTYPE HTML> -<!-- Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ --> -<html> -<head> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript"> - function check_styles() { - var redText = document.getElementById('red-text'); - var greenText = document.getElementById('green-text'); - var blueText = document.getElementById('blue-text'); - var redTextColor = window.getComputedStyle(redText).getPropertyValue('color'); - var greenTextColor = window.getComputedStyle(greenText).getPropertyValue('color'); - var blueTextColor = window.getComputedStyle(blueText).getPropertyValue('color'); - ok(redTextColor == 'rgb(255, 0, 0)', "The first part should be red."); - ok(greenTextColor == 'rgb(0, 255, 0)', "The second part should be green."); - ok(blueTextColor == 'rgb(0, 0, 255)', "The third part should be blue."); - } - - SimpleTest.waitForExplicitFinish(); - window.onload = function() { - check_styles(); - SimpleTest.finish(); - } - </script> - <script> - function good_correctHashCORSLoaded() { - ok(true, "A CORS cross-domain stylesheet with correct hash was correctly loaded."); - } - function bad_correctHashCORSBlocked() { - ok(false, "We should load CORS cross-domain stylesheets with hashes that match!"); - } - function good_correctHashBlocked() { - ok(true, "A non-CORS cross-domain stylesheet with correct hash was correctly blocked."); - } - function bad_correctHashLoaded() { - ok(false, "We should block non-CORS cross-domain stylesheets with hashes that match!"); - } - - function good_incorrectHashBlocked() { - ok(true, "A non-CORS cross-domain stylesheet with incorrect hash was correctly blocked."); - } - function bad_incorrectHashLoaded() { - ok(false, "We should load non-CORS cross-domain stylesheets with incorrect hashes!"); - } - - function bad_correctDataBlocked() { - ok(false, "We should not block non-CORS cross-domain stylesheets in data: URI!"); - } - function good_correctDataLoaded() { - ok(true, "A non-CORS cross-domain stylesheet with data: URI was correctly loaded."); - } - function bad_correctDataCORSBlocked() { - ok(false, "We should not block CORS stylesheets in data: URI!"); - } - function good_correctDataCORSLoaded() { - ok(true, "A CORS stylesheet with data: URI was correctly loaded."); - } - - function good_correctHashOpaqueBlocked() { - ok(true, "A non-CORS(Opaque) cross-domain stylesheet with correct hash was correctly blocked."); - } - function bad_correctHashOpaqueLoaded() { - ok(false, "We should not load non-CORS(Opaque) cross-domain stylesheets with correct hashes!"); - } - </script> - - <!-- valid CORS sha256 hash --> - <link rel="stylesheet" href="http://example.com/tests/dom/security/test/sri/style1.css" - crossorigin="anonymous" - integrity="sha256-qs8lnkunWoVldk5d5E+652yth4VTSHohlBKQvvgGwa8=" - onerror="bad_correctHashCORSBlocked()" - onload="good_correctHashCORSLoaded()"> - - <!-- valid non-CORS sha256 hash --> - <link rel="stylesheet" href="style_301.css" - integrity="sha256-qs8lnkunWoVldk5d5E+652yth4VTSHohlBKQvvgGwa8=" - onerror="good_correctHashBlocked()" - onload="bad_correctHashLoaded()"> - - <!-- invalid non-CORS sha256 hash --> - <link rel="stylesheet" href="style_301.css?again" - integrity="sha256-bogus" - onerror="good_incorrectHashBlocked()" - onload="bad_incorrectHashLoaded()"> - - <!-- valid non-CORS sha256 hash in a data: URL --> - <link rel="stylesheet" href="data:text/css,.green-text{color:rgb(0, 255, 0)}" - integrity="sha256-EhVtGGyovvffvYdhyqJxUJ/ekam7zlxxo46iM13cwP0=" - onerror="bad_correctDataBlocked()" - onload="good_correctDataLoaded()"> - - <!-- valid CORS sha256 hash in a data: URL --> - <link rel="stylesheet" href="data:text/css,.blue-text{color:rgb(0, 0, 255)}" - crossorigin="anonymous" - integrity="sha256-m0Fs2hNSyPOn1030Dp+c8pJFHNmwpeTbB+8J/DcqLss=" - onerror="bad_correctDataCORSBlocked()" - onload="good_correctDataCORSLoaded()"> - - <!-- valid non-CORS sha256 hash --> - <link rel="stylesheet" href="http://example.com/tests/dom/security/test/sri/style1.css" - integrity="sha256-qs8lnkunWoVldk5d5E+652yth4VTSHohlBKQvvgGwa8=" - onerror="good_correctHashOpaqueBlocked()" - onload="bad_correctHashOpaqueLoaded()"> -</head> -<body> -<p><span id="red-text">This should be red</span> but - <span id="green-text" class="green-text">this should be green</span> and - <span id="blue-text" class="blue-text">this should be blue</span></p> -<p id="display"></p> -<div id="content" style="display: none"> -</div> -<pre id="test"> -</pre> -</body> -</html>
--- a/dom/security/test/sri/mochitest.ini +++ b/dom/security/test/sri/mochitest.ini @@ -5,17 +5,16 @@ support-files = iframe_csp_directive_style_imports.html^headers^ iframe_require-sri-for_main.html iframe_require-sri-for_main.html^headers^ iframe_require-sri-for_no_csp.html iframe_script_crossdomain.html iframe_script_sameorigin.html iframe_sri_disabled.html iframe_style_crossdomain.html - iframe_style_crossdomain_legacy.html iframe_style_sameorigin.html rsf_csp_worker.js rsf_csp_worker.js^headers^ rsf_imported.js rsf_worker.js script_crossdomain1.js script_crossdomain1.js^headers^ script_crossdomain2.js @@ -46,15 +45,14 @@ support-files = style_301.css^headers^ style_importing.css style_imported.css [test_script_sameorigin.html] [test_script_crossdomain.html] [test_sri_disabled.html] [test_style_crossdomain.html] -[test_style_crossdomain_legacy.html] [test_style_sameorigin.html] [test_require-sri-for_csp_directive.html] [test_require-sri-for_csp_directive_disabled.html] [test_bug_1271796.html] [test_csp_directive_style_imports.html] [test_bug_1364262.html]
--- a/dom/security/test/sri/test_style_crossdomain.html +++ b/dom/security/test/sri/test_style_crossdomain.html @@ -2,18 +2,16 @@ <!-- Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ --> <html> <head> <meta charset="utf-8"> <title>Cross-domain stylesheet tests for Bug 1196740</title> <script> SpecialPowers.setBoolPref("security.sri.enable", true); - // TODO: Bug 1324406: Treat 'data:' documents as unique, opaque origins - SpecialPowers.setBoolPref("security.data_uri.unique_opaque_origin", true); </script> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1196740">Mozilla Bug 1196740</a> <div> <iframe src="iframe_style_crossdomain.html" height="100%" width="90%" frameborder="0"></iframe> </div> </body>
deleted file mode 100644 --- a/dom/security/test/sri/test_style_crossdomain_legacy.html +++ /dev/null @@ -1,20 +0,0 @@ -<!DOCTYPE HTML> -<!-- Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ --> -<html> -<head> - <meta charset="utf-8"> - <title>Cross-domain stylesheet tests for Bug 1196740</title> - <script> - SpecialPowers.setBoolPref("security.sri.enable", true); - // TODO: Bug 1324406: Treat 'data:' documents as unique, opaque origins - SpecialPowers.setBoolPref("security.data_uri.unique_opaque_origin", false); - </script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1196740">Mozilla Bug 1196740</a> -<div> - <iframe src="iframe_style_crossdomain_legacy.html" height="100%" width="90%" frameborder="0"></iframe> -</div> -</body> -</html>
--- a/dom/webauthn/U2FHIDTokenManager.cpp +++ b/dom/webauthn/U2FHIDTokenManager.cpp @@ -31,23 +31,22 @@ U2FHIDTokenManager::~U2FHIDTokenManager( // Bytes Value // 1 0x05 // 65 public key // 1 key handle length // * key handle // ASN.1 attestation certificate // * attestation signature // -RefPtr<ResultPromise> +RefPtr<U2FRegisterPromise> U2FHIDTokenManager::Register(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors, const nsTArray<uint8_t>& aApplication, - const nsTArray<uint8_t>& aChallenge, - /* out */ nsTArray<uint8_t>& aRegistration) + const nsTArray<uint8_t>& aChallenge) { - return ResultPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__); + return U2FRegisterPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__); } // A U2F Sign operation creates a signature over the "param" arguments (plus // some other stuff) using the private key indicated in the key handle argument. // // The format of the signed data is as follows: // // 32 Application parameter @@ -56,24 +55,22 @@ U2FHIDTokenManager::Register(const nsTAr // 32 Challenge parameter // // The format of the signature data is as follows: // // 1 User presence // 4 Counter // * Signature // -RefPtr<ResultPromise> +RefPtr<U2FSignPromise> U2FHIDTokenManager::Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors, const nsTArray<uint8_t>& aApplication, - const nsTArray<uint8_t>& aChallenge, - /* out */ nsTArray<uint8_t>& aKeyHandle, - /* out */ nsTArray<uint8_t>& aSignature) + const nsTArray<uint8_t>& aChallenge) { - return ResultPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__); + return U2FSignPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__); } void U2FHIDTokenManager::Cancel() { } }
--- a/dom/webauthn/U2FHIDTokenManager.h +++ b/dom/webauthn/U2FHIDTokenManager.h @@ -17,28 +17,25 @@ namespace mozilla { namespace dom { class U2FHIDTokenManager final : public U2FTokenTransport { public: explicit U2FHIDTokenManager(); - virtual RefPtr<ResultPromise> + virtual RefPtr<U2FRegisterPromise> Register(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors, const nsTArray<uint8_t>& aApplication, - const nsTArray<uint8_t>& aChallenge, - /* out */ nsTArray<uint8_t>& aRegistration) override; + const nsTArray<uint8_t>& aChallenge) override; - virtual RefPtr<ResultPromise> + virtual RefPtr<U2FSignPromise> Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors, const nsTArray<uint8_t>& aApplication, - const nsTArray<uint8_t>& aChallenge, - /* out */ nsTArray<uint8_t>& aKeyHandle, - /* out */ nsTArray<uint8_t>& aSignature) override; + const nsTArray<uint8_t>& aChallenge) override; void Cancel() override; private: ~U2FHIDTokenManager(); }; } // namespace dom
--- a/dom/webauthn/U2FSoftTokenManager.cpp +++ b/dom/webauthn/U2FSoftTokenManager.cpp @@ -628,86 +628,85 @@ U2FSoftTokenManager::IsRegistered(const // Bytes Value // 1 0x05 // 65 public key // 1 key handle length // * key handle // ASN.1 attestation certificate // * attestation signature // -RefPtr<ResultPromise> +RefPtr<U2FRegisterPromise> U2FSoftTokenManager::Register(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors, const nsTArray<uint8_t>& aApplication, - const nsTArray<uint8_t>& aChallenge, - /* out */ nsTArray<uint8_t>& aRegistration) + const nsTArray<uint8_t>& aChallenge) { nsNSSShutDownPreventionLock locker; if (NS_WARN_IF(isAlreadyShutDown())) { - return ResultPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__); + return U2FRegisterPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__); } if (!mInitialized) { nsresult rv = Init(); if (NS_WARN_IF(NS_FAILED(rv))) { - return ResultPromise::CreateAndReject(rv, __func__); + return U2FRegisterPromise::CreateAndReject(rv, __func__); } } // Optional exclusion list. for (auto desc: aDescriptors) { bool isRegistered = false; nsresult rv = IsRegistered(desc.id(), aApplication, isRegistered); if (NS_FAILED(rv)) { - return ResultPromise::CreateAndReject(rv, __func__); + return U2FRegisterPromise::CreateAndReject(rv, __func__); } if (isRegistered) { - return ResultPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__); + return U2FRegisterPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__); } } // We should already have a wrapping key MOZ_ASSERT(mWrappingKey); UniquePK11SlotInfo slot(PK11_GetInternalSlot()); MOZ_ASSERT(slot.get()); // Construct a one-time-use Attestation Certificate UniqueSECKEYPrivateKey attestPrivKey; UniqueCERTCertificate attestCert; nsresult rv = GetAttestationCertificate(slot, attestPrivKey, attestCert, locker); if (NS_WARN_IF(NS_FAILED(rv))) { - return ResultPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } MOZ_ASSERT(attestCert); MOZ_ASSERT(attestPrivKey); // Generate a new keypair; the private will be wrapped into a Key Handle UniqueSECKEYPrivateKey privKey; UniqueSECKEYPublicKey pubKey; rv = GenEcKeypair(slot, privKey, pubKey, locker); if (NS_WARN_IF(NS_FAILED(rv))) { - return ResultPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } // The key handle will be the result of keywrap(privKey, key=mWrappingKey) UniqueSECItem keyHandleItem = KeyHandleFromPrivateKey(slot, mWrappingKey, const_cast<uint8_t*>(aApplication.Elements()), aApplication.Length(), privKey, locker); if (NS_WARN_IF(!keyHandleItem.get())) { - return ResultPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } // Sign the challenge using the Attestation privkey (from attestCert) mozilla::dom::CryptoBuffer signedDataBuf; if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + aApplication.Length() + aChallenge.Length() + keyHandleItem->len + kPublicKeyLen, mozilla::fallible))) { - return ResultPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__); + return U2FRegisterPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__); } // // It's OK to ignore the return values here because we're writing into // // pre-allocated space signedDataBuf.AppendElement(0x00, mozilla::fallible); signedDataBuf.AppendElements(aApplication, mozilla::fallible); signedDataBuf.AppendElements(aChallenge, mozilla::fallible); signedDataBuf.AppendSECItem(keyHandleItem.get()); @@ -715,35 +714,35 @@ U2FSoftTokenManager::Register(const nsTA ScopedAutoSECItem signatureItem; SECStatus srv = SEC_SignData(&signatureItem, signedDataBuf.Elements(), signedDataBuf.Length(), attestPrivKey.get(), SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); if (NS_WARN_IF(srv != SECSuccess)) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Signature failure: %d", PORT_GetError())); - return ResultPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } // Serialize the registration data mozilla::dom::CryptoBuffer registrationBuf; if (NS_WARN_IF(!registrationBuf.SetCapacity(1 + kPublicKeyLen + 1 + keyHandleItem->len + attestCert.get()->derCert.len + signatureItem.len, mozilla::fallible))) { - return ResultPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__); + return U2FRegisterPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__); } registrationBuf.AppendElement(0x05, mozilla::fallible); registrationBuf.AppendSECItem(pubKey->u.ec.publicValue); registrationBuf.AppendElement(keyHandleItem->len, mozilla::fallible); registrationBuf.AppendSECItem(keyHandleItem.get()); registrationBuf.AppendSECItem(attestCert.get()->derCert); registrationBuf.AppendSECItem(signatureItem); - aRegistration = registrationBuf; - return ResultPromise::CreateAndResolve(NS_OK, __func__); + U2FRegisterResult result((nsTArray<uint8_t>(registrationBuf))); + return U2FRegisterPromise::CreateAndResolve(Move(result), __func__); } // A U2F Sign operation creates a signature over the "param" arguments (plus // some other stuff) using the private key indicated in the key handle argument. // // The format of the signed data is as follows: // // 32 Application parameter @@ -752,65 +751,64 @@ U2FSoftTokenManager::Register(const nsTA // 32 Challenge parameter // // The format of the signature data is as follows: // // 1 User presence // 4 Counter // * Signature // -RefPtr<ResultPromise> +RefPtr<U2FSignPromise> U2FSoftTokenManager::Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors, const nsTArray<uint8_t>& aApplication, - const nsTArray<uint8_t>& aChallenge, - nsTArray<uint8_t>& aKeyHandle, - nsTArray<uint8_t>& aSignature) + const nsTArray<uint8_t>& aChallenge) { nsNSSShutDownPreventionLock locker; if (NS_WARN_IF(isAlreadyShutDown())) { - return ResultPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__); + return U2FSignPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__); } + nsTArray<uint8_t> keyHandle; for (auto desc: aDescriptors) { bool isRegistered = false; nsresult rv = IsRegistered(desc.id(), aApplication, isRegistered); if (NS_SUCCEEDED(rv) && isRegistered) { - aKeyHandle.Assign(desc.id()); + keyHandle.Assign(desc.id()); break; } } // Fail if we didn't recognize a key id. - if (aKeyHandle.IsEmpty()) { - return ResultPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__); + if (keyHandle.IsEmpty()) { + return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__); } MOZ_ASSERT(mWrappingKey); UniquePK11SlotInfo slot(PK11_GetInternalSlot()); MOZ_ASSERT(slot.get()); if (NS_WARN_IF((aChallenge.Length() != kParamLen) || (aApplication.Length() != kParamLen))) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Parameter lengths are wrong! challenge=%d app=%d expected=%d", (uint32_t)aChallenge.Length(), (uint32_t)aApplication.Length(), kParamLen)); - return ResultPromise::CreateAndReject(NS_ERROR_ILLEGAL_VALUE, __func__); + return U2FSignPromise::CreateAndReject(NS_ERROR_ILLEGAL_VALUE, __func__); } // Decode the key handle UniqueSECKEYPrivateKey privKey = PrivateKeyFromKeyHandle(slot, mWrappingKey, - const_cast<uint8_t*>(aKeyHandle.Elements()), - aKeyHandle.Length(), + const_cast<uint8_t*>(keyHandle.Elements()), + keyHandle.Length(), const_cast<uint8_t*>(aApplication.Elements()), aApplication.Length(), locker); if (NS_WARN_IF(!privKey.get())) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Couldn't get the priv key!")); - return ResultPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + return U2FSignPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } // Increment the counter and turn it into a SECItem mCounter += 1; ScopedAutoSECItem counterItem(4); counterItem.data[0] = (mCounter >> 24) & 0xFF; counterItem.data[1] = (mCounter >> 16) & 0xFF; counterItem.data[2] = (mCounter >> 8) & 0xFF; @@ -821,65 +819,65 @@ U2FSoftTokenManager::Sign(const nsTArray [counter] () { MOZ_ASSERT(NS_IsMainThread()); Preferences::SetUint(PREF_U2F_NSSTOKEN_COUNTER, counter); })); // Compute the signature mozilla::dom::CryptoBuffer signedDataBuf; if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + 4 + (2 * kParamLen), mozilla::fallible))) { - return ResultPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__); + return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__); } // It's OK to ignore the return values here because we're writing into // pre-allocated space signedDataBuf.AppendElements(aApplication.Elements(), aApplication.Length(), mozilla::fallible); signedDataBuf.AppendElement(0x01, mozilla::fallible); signedDataBuf.AppendSECItem(counterItem); signedDataBuf.AppendElements(aChallenge.Elements(), aChallenge.Length(), mozilla::fallible); if (MOZ_LOG_TEST(gNSSTokenLog, LogLevel::Debug)) { nsAutoCString base64; nsresult rv = Base64URLEncode(signedDataBuf.Length(), signedDataBuf.Elements(), Base64URLEncodePaddingPolicy::Omit, base64); if (NS_WARN_IF(NS_FAILED(rv))) { - return ResultPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + return U2FSignPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("U2F Token signing bytes (base64): %s", base64.get())); } ScopedAutoSECItem signatureItem; SECStatus srv = SEC_SignData(&signatureItem, signedDataBuf.Elements(), signedDataBuf.Length(), privKey.get(), SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); if (NS_WARN_IF(srv != SECSuccess)) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Signature failure: %d", PORT_GetError())); - return ResultPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + return U2FSignPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } // Assemble the signature data into a buffer for return mozilla::dom::CryptoBuffer signatureBuf; if (NS_WARN_IF(!signatureBuf.SetCapacity(1 + counterItem.len + signatureItem.len, mozilla::fallible))) { - return ResultPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__); + return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__); } // It's OK to ignore the return values here because we're writing into // pre-allocated space signatureBuf.AppendElement(0x01, mozilla::fallible); signatureBuf.AppendSECItem(counterItem); signatureBuf.AppendSECItem(signatureItem); - aSignature = signatureBuf; - return ResultPromise::CreateAndResolve(NS_OK, __func__); + U2FSignResult result(Move(keyHandle), nsTArray<uint8_t>(signatureBuf)); + return U2FSignPromise::CreateAndResolve(Move(result), __func__); } void U2FSoftTokenManager::Cancel() { // This implementation is sync, requests can't be aborted. }
--- a/dom/webauthn/U2FSoftTokenManager.h +++ b/dom/webauthn/U2FSoftTokenManager.h @@ -20,28 +20,25 @@ namespace mozilla { namespace dom { class U2FSoftTokenManager final : public U2FTokenTransport, public nsNSSShutDownObject { public: explicit U2FSoftTokenManager(uint32_t aCounter); - virtual RefPtr<ResultPromise> + virtual RefPtr<U2FRegisterPromise> Register(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors, const nsTArray<uint8_t>& aApplication, - const nsTArray<uint8_t>& aChallenge, - /* out */ nsTArray<uint8_t>& aRegistration) override; + const nsTArray<uint8_t>& aChallenge) override; - virtual RefPtr<ResultPromise> + virtual RefPtr<U2FSignPromise> Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors, const nsTArray<uint8_t>& aApplication, - const nsTArray<uint8_t>& aChallenge, - /* out */ nsTArray<uint8_t>& aKeyHandle, - /* out */ nsTArray<uint8_t>& aSignature) override; + const nsTArray<uint8_t>& aChallenge) override; virtual void Cancel() override; // For nsNSSShutDownObject virtual void virtualDestroyNSSReference() override; void destructorSafeDestroyNSSReference(); private:
--- a/dom/webauthn/U2FTokenManager.cpp +++ b/dom/webauthn/U2FTokenManager.cpp @@ -186,18 +186,19 @@ U2FTokenManager::MaybeClearTransaction(W } void U2FTokenManager::ClearTransaction() { mTransactionParent = nullptr; // Drop managers at the end of all transactions mTokenManagerImpl = nullptr; - // Drop promise. - mResultPromise = nullptr; + // Drop promises. + mRegisterPromise = nullptr; + mSignPromise = nullptr; // Increase in case we're called by the WebAuthnTransactionParent. mTransactionId++; } RefPtr<U2FTokenTransport> U2FTokenManager::GetTokenManagerImpl() { if (mTokenManagerImpl) { @@ -246,44 +247,44 @@ U2FTokenManager::Register(WebAuthnTransa // UnknownError and terminate the operation. if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) || (aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) { AbortTransaction(NS_ERROR_DOM_UNKNOWN_ERR); return; } - nsTArray<uint8_t> reg; - mResultPromise = mTokenManagerImpl->Register(aTransactionInfo.Descriptors(), - aTransactionInfo.RpIdHash(), - aTransactionInfo.ClientDataHash(), - reg); + mRegisterPromise = mTokenManagerImpl->Register(aTransactionInfo.Descriptors(), + aTransactionInfo.RpIdHash(), + aTransactionInfo.ClientDataHash()); - mResultPromise->Then(GetCurrentThreadSerialEventTarget(), __func__, - [tid, reg](nsresult rv) { - MOZ_ASSERT(NS_SUCCEEDED(rv)); - U2FTokenManager* mgr = U2FTokenManager::Get(); - mgr->MaybeConfirmRegister(tid, reg); - }, - [tid](nsresult rv) { - MOZ_ASSERT(NS_FAILED(rv)); - U2FTokenManager* mgr = U2FTokenManager::Get(); - mgr->MaybeAbortTransaction(tid, rv); - }); + mRegisterPromise->Then(GetCurrentThreadSerialEventTarget(), __func__, + [tid](U2FRegisterResult&& aResult) { + U2FTokenManager* mgr = U2FTokenManager::Get(); + mgr->MaybeConfirmRegister(tid, aResult); + }, + [tid](nsresult rv) { + MOZ_ASSERT(NS_FAILED(rv)); + U2FTokenManager* mgr = U2FTokenManager::Get(); + mgr->MaybeAbortTransaction(tid, rv); + }); } void U2FTokenManager::MaybeConfirmRegister(uint64_t aTransactionId, - const nsTArray<uint8_t>& aRegister) + U2FRegisterResult& aResult) { if (mTransactionId != aTransactionId) { return; } - Unused << mTransactionParent->SendConfirmRegister(aRegister); + nsTArray<uint8_t> registration; + aResult.ConsumeRegistration(registration); + + Unused << mTransactionParent->SendConfirmRegister(registration); ClearTransaction(); } void U2FTokenManager::Sign(WebAuthnTransactionParent* aTransactionParent, const WebAuthnTransactionInfo& aTransactionInfo) { MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthSign")); @@ -299,47 +300,46 @@ U2FTokenManager::Sign(WebAuthnTransactio } if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) || (aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) { AbortTransaction(NS_ERROR_DOM_UNKNOWN_ERR); return; } - nsTArray<uint8_t> id; - nsTArray<uint8_t> sig; - mResultPromise = mTokenManagerImpl->Sign(aTransactionInfo.Descriptors(), - aTransactionInfo.RpIdHash(), - aTransactionInfo.ClientDataHash(), - id, - sig); + mSignPromise = mTokenManagerImpl->Sign(aTransactionInfo.Descriptors(), + aTransactionInfo.RpIdHash(), + aTransactionInfo.ClientDataHash()); - mResultPromise->Then(GetCurrentThreadSerialEventTarget(), __func__, - [tid, id, sig](nsresult rv) { - MOZ_ASSERT(NS_SUCCEEDED(rv)); - U2FTokenManager* mgr = U2FTokenManager::Get(); - mgr->MaybeConfirmSign(tid, id, sig); - }, - [tid](nsresult rv) { - MOZ_ASSERT(NS_FAILED(rv)); - U2FTokenManager* mgr = U2FTokenManager::Get(); - mgr->MaybeAbortTransaction(tid, rv); - }); + mSignPromise->Then(GetCurrentThreadSerialEventTarget(), __func__, + [tid](U2FSignResult&& aResult) { + U2FTokenManager* mgr = U2FTokenManager::Get(); + mgr->MaybeConfirmSign(tid, aResult); + }, + [tid](nsresult rv) { + MOZ_ASSERT(NS_FAILED(rv)); + U2FTokenManager* mgr = U2FTokenManager::Get(); + mgr->MaybeAbortTransaction(tid, rv); + }); } void U2FTokenManager::MaybeConfirmSign(uint64_t aTransactionId, - const nsTArray<uint8_t>& aKeyHandle, - const nsTArray<uint8_t>& aSignature) + U2FSignResult& aResult) { if (mTransactionId != aTransactionId) { return; } - Unused << mTransactionParent->SendConfirmSign(aKeyHandle, aSignature); + nsTArray<uint8_t> keyHandle; + aResult.ConsumeKeyHandle(keyHandle); + nsTArray<uint8_t> signature; + aResult.ConsumeSignature(signature); + + Unused << mTransactionParent->SendConfirmSign(keyHandle, signature); ClearTransaction(); } void U2FTokenManager::Cancel(WebAuthnTransactionParent* aParent) { if (mTransactionParent == aParent) { mTokenManagerImpl->Cancel();
--- a/dom/webauthn/U2FTokenManager.h +++ b/dom/webauthn/U2FTokenManager.h @@ -47,26 +47,25 @@ private: U2FTokenManager(); ~U2FTokenManager(); RefPtr<U2FTokenTransport> GetTokenManagerImpl(); void AbortTransaction(const nsresult& aError); void ClearTransaction(); void MaybeAbortTransaction(uint64_t aTransactionId, const nsresult& aError); void MaybeConfirmRegister(uint64_t aTransactionId, - const nsTArray<uint8_t>& aRegister); - void MaybeConfirmSign(uint64_t aTransactionId, - const nsTArray<uint8_t>& aKeyHandle, - const nsTArray<uint8_t>& aSignature); + U2FRegisterResult& aResult); + void MaybeConfirmSign(uint64_t aTransactionId, U2FSignResult& aResult); // Using a raw pointer here, as the lifetime of the IPC object is managed by // the PBackground protocol code. This means we cannot be left holding an // invalid IPC protocol object after the transaction is finished. WebAuthnTransactionParent* mTransactionParent; RefPtr<U2FTokenTransport> mTokenManagerImpl; - RefPtr<ResultPromise> mResultPromise; + RefPtr<U2FRegisterPromise> mRegisterPromise; + RefPtr<U2FSignPromise> mSignPromise; // Guards the asynchronous promise resolution of token manager impls. // We don't need to protect this with a lock as it will only be modified // and checked on the PBackground thread in the parent process. uint64_t mTransactionId; }; } // namespace dom } // namespace mozilla
--- a/dom/webauthn/U2FTokenTransport.h +++ b/dom/webauthn/U2FTokenTransport.h @@ -13,36 +13,69 @@ * Abstract class representing a transport manager for U2F Keys (software, * bluetooth, usb, etc.). Hides the implementation details for specific key * transport types. */ namespace mozilla { namespace dom { -typedef MozPromise<nsresult, nsresult, false> ResultPromise; +class U2FRegisterResult { +public: + explicit U2FRegisterResult(nsTArray<uint8_t>&& aRegistration) + : mRegistration(aRegistration) + { } + + void ConsumeRegistration(nsTArray<uint8_t>& aBuffer) { + aBuffer = Move(mRegistration); + } + +private: + nsTArray<uint8_t> mRegistration; +}; + +class U2FSignResult { +public: + explicit U2FSignResult(nsTArray<uint8_t>&& aKeyHandle, + nsTArray<uint8_t>&& aSignature) + : mKeyHandle(aKeyHandle) + , mSignature(aSignature) + { } + + void ConsumeKeyHandle(nsTArray<uint8_t>& aBuffer) { + aBuffer = Move(mKeyHandle); + } + + void ConsumeSignature(nsTArray<uint8_t>& aBuffer) { + aBuffer = Move(mSignature); + } + +private: + nsTArray<uint8_t> mKeyHandle; + nsTArray<uint8_t> mSignature; +}; + +typedef MozPromise<U2FRegisterResult, nsresult, true> U2FRegisterPromise; +typedef MozPromise<U2FSignResult, nsresult, true> U2FSignPromise; class U2FTokenTransport { public: NS_INLINE_DECL_REFCOUNTING(U2FTokenTransport); U2FTokenTransport() {} - virtual RefPtr<ResultPromise> + virtual RefPtr<U2FRegisterPromise> Register(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors, const nsTArray<uint8_t>& aApplication, - const nsTArray<uint8_t>& aChallenge, - /* out */ nsTArray<uint8_t>& aRegistration) = 0; + const nsTArray<uint8_t>& aChallenge) = 0; - virtual RefPtr<ResultPromise> + virtual RefPtr<U2FSignPromise> Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors, const nsTArray<uint8_t>& aApplication, - const nsTArray<uint8_t>& aChallenge, - /* out */ nsTArray<uint8_t>& aKeyHandle, - /* out */ nsTArray<uint8_t>& aSignature) = 0; + const nsTArray<uint8_t>& aChallenge) = 0; virtual void Cancel() = 0; protected: virtual ~U2FTokenTransport() = default; }; } // namespace dom
--- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -149,17 +149,16 @@ partial interface Document { //(HTML only)boolean queryCommandSupported(DOMString commandId); //(HTML only)DOMString queryCommandValue(DOMString commandId); //(Not implemented)readonly attribute HTMLCollection commands; // special event handler IDL attributes that only apply to Document objects [LenientThis] attribute EventHandler onreadystatechange; // Gecko extensions? - attribute EventHandler onwheel; attribute EventHandler onbeforescriptexecute; attribute EventHandler onafterscriptexecute; [Pref="dom.select_events.enabled"] attribute EventHandler onselectionchange; /** * True if this document is synthetic : stand alone image, video, audio file,
--- a/dom/webidl/Element.webidl +++ b/dom/webidl/Element.webidl @@ -85,20 +85,16 @@ interface Element : Node { * * @note The font size inflation ratio that is returned is actually the * font size inflation data for the element's _primary frame_, not the * element itself, but for most purposes, this should be sufficient. */ [ChromeOnly] readonly attribute float fontSizeInflation; - // Mozilla specific stuff - [Pure] - attribute EventHandler onwheel; - // Selectors API /** * Returns whether this element would be selected by the given selector * string. * * See <http://dev.w3.org/2006/webapi/selectors-api2/#matchesselector> */ [Throws, Pure, BinaryName="matches"]
--- a/dom/webidl/EventHandler.webidl +++ b/dom/webidl/EventHandler.webidl @@ -63,17 +63,17 @@ interface GlobalEventHandlers { attribute EventHandler onloadstart; attribute EventHandler onmousedown; [LenientThis] attribute EventHandler onmouseenter; [LenientThis] attribute EventHandler onmouseleave; attribute EventHandler onmousemove; attribute EventHandler onmouseout; attribute EventHandler onmouseover; attribute EventHandler onmouseup; - //(Not implemented)attribute EventHandler onmousewheel; + attribute EventHandler onwheel; attribute EventHandler onpause; attribute EventHandler onplay; attribute EventHandler onplaying; attribute EventHandler onprogress; attribute EventHandler onratechange; attribute EventHandler onreset; attribute EventHandler onresize; attribute EventHandler onscroll;
--- a/dom/webidl/Window.webidl +++ b/dom/webidl/Window.webidl @@ -319,19 +319,16 @@ partial interface Window { /** * This property exists because static attributes don't yet work for * JS-implemented WebIDL (see bugs 1058606 and 863952). With this hack, we * can use `MozSelfSupport.something(...)`, which will continue to work * after we ditch this property and switch to static attributes. See */ [ChromeOnly, Throws] readonly attribute MozSelfSupport MozSelfSupport; - [Pure] - attribute EventHandler onwheel; - attribute EventHandler ondevicemotion; attribute EventHandler ondeviceorientation; attribute EventHandler onabsolutedeviceorientation; attribute EventHandler ondeviceproximity; attribute EventHandler onuserproximity; attribute EventHandler ondevicelight; #ifdef MOZ_B2G
--- a/dom/xbl/test/mochitest.ini +++ b/dom/xbl/test/mochitest.ini @@ -19,17 +19,16 @@ support-files = file_bug950909.html [test_bug310107.html] [test_bug366770.html] [test_bug371724.xhtml] [test_bug372769.html] [test_bug378866.xhtml] [test_bug379959.html] -[test_bug379959_legacy.html] [test_bug389322.xhtml] [test_bug397934.html] [test_bug400705.xhtml] [test_bug401907.xhtml] [test_bug403162.xhtml] [test_bug468210.xhtml] [test_bug481558.html] [test_bug526178.xhtml]
--- a/dom/xbl/test/test_bug379959.html +++ b/dom/xbl/test/test_bug379959.html @@ -1,20 +1,16 @@ <!DOCTYPE HTML> <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=379959 --> <head> <title>Test for Bug 379959</title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <script> - // TODO: Bug 1324406: Treat 'data:' documents as unique, opaque origins - SpecialPowers.setBoolPref("security.data_uri.unique_opaque_origin", true); - </script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body onload="runTest();"> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=379959">Mozilla Bug 379959</a> <p id="display"> Note: In order to re-run this test correctly you need to shift-reload rather than simply reload. If you just reload we will restore the previous url in the iframe which will result in an extra unexpected @@ -32,17 +28,17 @@ SimpleTest.waitForExplicitFinish(); var seenData = false; var seenSameOrigin = false; var seenCrossOrign = false; function receiveMessage(e) { is(e.origin, "http://mochi.test:8888", "wrong sender!"); if (e.data.test === "dataIsAllowed") { - is(e.data.result, 0, "data-url load should have blocked"); + is(e.data.result, 1, "data-url load should have succeeded"); seenData = true; } else if (e.data.test === "sameOriginIsAllowed") { is(e.data.result, 1, "same site load should have succeeded"); seenSameOrigin = true; } else if (e.data.test === "crossOriginIsBlocked") { is(e.data.result, 0, "cross site load should have failed");
deleted file mode 100644 --- a/dom/xbl/test/test_bug379959_legacy.html +++ /dev/null @@ -1,73 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=379959 ---> -<head> - <title>Test for Bug 379959</title> - <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <script> - // TODO: Bug 1324406: Treat 'data:' documents as unique, opaque origins - SpecialPowers.setBoolPref("security.data_uri.unique_opaque_origin", false); - </script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> -</head> -<body onload="runTest();"> - <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=379959">Mozilla Bug 379959</a> - <p id="display"> - Note: In order to re-run this test correctly you need to shift-reload - rather than simply reload. If you just reload we will restore the - previous url in the iframe which will result in an extra unexpected - message. - </p> - <div id="content" style="display: none"></div> - <iframe id="dataFrame"></iframe> - <iframe id="originFrame"></iframe> - - <pre id="test"> - <script class="testbody" type="application/javascript"> - -SimpleTest.waitForExplicitFinish(); - -var seenData = false; -var seenSameOrigin = false; -var seenCrossOrign = false; - -function receiveMessage(e) { - is(e.origin, "http://mochi.test:8888", "wrong sender!"); - - if (e.data.test === "dataIsAllowed") { - is(e.data.result, 1, "data-url load should have succeeded"); - seenData = true; - } - else if (e.data.test === "sameOriginIsAllowed") { - is(e.data.result, 1, "same site load should have succeeded"); - seenSameOrigin = true; - } - else if (e.data.test === "crossOriginIsBlocked") { - is(e.data.result, 0, "cross site load should have failed"); - seenCrossOrign = true; - } - else { - ok (false, "unrecognized test"); - } - - if (seenData && seenSameOrigin && seenCrossOrign) { - window.removeEventListener("message", receiveMessage); - SimpleTest.finish(); - } -} - -window.addEventListener("message", receiveMessage); - -function runTest() { - // make sure data: is allowed - document.getElementById('dataFrame').src = "file_bug379959_data.html"; - // make sure same-origin is allowed but cross site is blocked - document.getElementById('originFrame').src = "file_bug379959_cross.html"; -} - - </script> - </pre> -</body> -</html>
--- a/editor/libeditor/crashtests/430624-1.html +++ b/editor/libeditor/crashtests/430624-1.html @@ -6,9 +6,9 @@ function crash() { window.frames[0].location = 'data:text/html;charset=utf-8,2nd%20page'; } </script> </head> <body onload="crash()"> <!-- iframe contents: <html><body onload="document.body.setAttribute('spellcheck', true);"></body></html> --> <iframe src="data:text/html;charset=utf-8;base64,PGh0bWw%2BPGJvZHkgb25sb2FkPSJkb2N1bWVudC5ib2R5LnNldEF0dHJpYnV0ZSgnc3BlbGxjaGVjaycsIHRydWUpOyI%2BPC9ib2R5PjwvaHRtbD4%3D"></iframe> </body> -</html> \ No newline at end of file +</html>
--- a/gfx/layers/client/ClientPaintedLayer.cpp +++ b/gfx/layers/client/ClientPaintedLayer.cpp @@ -258,19 +258,21 @@ ClientPaintedLayer::CapturePaintedConten // DrawTargetCapture requires a reference DT // That is used when some API requires a snapshot. // TODO: Fixup so DrawTargetCapture lazily creates a reference DT RefPtr<DrawTarget> refDT = Factory::CreateDrawTarget(gfxPlatform::GetPlatform()->GetDefaultContentBackend(), imageSize, gfx::SurfaceFormat::B8G8R8A8); + // We don't clear the rect here like WRPaintedBlobLayers do + // because ContentClient already clears the surface for us during BeginPaint. RefPtr<DrawTargetCapture> captureDT = refDT->CreateCaptureDT(imageSize); - captureDT->ClearRect(Rect(0, 0, imageSize.width, imageSize.height)); captureDT->SetTransform(Matrix().PreTranslate(-bounds.x, -bounds.y)); + RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(captureDT); MOZ_ASSERT(ctx); // already checked the target above ClientManager()->GetPaintedLayerCallback()(this, ctx, visibleRegion.ToUnknownRegion(), visibleRegion.ToUnknownRegion(), DrawRegionClip::DRAW,
--- a/gfx/layers/mlgpu/BufferCache.cpp +++ b/gfx/layers/mlgpu/BufferCache.cpp @@ -1,127 +1,101 @@ /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * 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 "BufferCache.h" #include "MLGDevice.h" +#include "ShaderDefinitionsMLGPU.h" #include "mozilla/MathAlgorithms.h" namespace mozilla { namespace layers { +using namespace mlg; + BufferCache::BufferCache(MLGDevice* aDevice) - : mDevice(aDevice) + : mDevice(aDevice), + mFirstSizeClass(CeilingLog2(kConstantBufferElementSize)), + mFrameNumber(0), + mNextSizeClassToShrink(0) { + // Create a cache of buffers for each size class, where each size class is a + // power of 2 between the minimum and maximum size of a constant buffer. + size_t maxBindSize = mDevice->GetMaxConstantBufferBindSize(); + MOZ_ASSERT(IsPowerOfTwo(maxBindSize)); + + size_t lastSizeClass = CeilingLog2(maxBindSize); + MOZ_ASSERT(lastSizeClass >= mFirstSizeClass); + + mCaches.resize(lastSizeClass - mFirstSizeClass + 1); } BufferCache::~BufferCache() { } RefPtr<MLGBuffer> BufferCache::GetOrCreateBuffer(size_t aBytes) { - // Try to take a buffer from the expired frame. If none exists, make a new one. - RefPtr<MLGBuffer> buffer = mExpired.Take(aBytes); - if (!buffer) { - // Round up to the nearest size class, but not over 1024 bytes. - size_t roundedUp = std::max(std::min(RoundUpPow2(aBytes), size_t(1024)), aBytes); - buffer = mDevice->CreateBuffer(MLGBufferType::Constant, roundedUp, MLGUsage::Dynamic, nullptr); - if (!buffer) { - return nullptr; - } + size_t sizeClass = CeilingLog2(aBytes); + size_t sizeClassIndex = sizeClass - mFirstSizeClass; + if (sizeClassIndex >= mCaches.size()) { + return mDevice->CreateBuffer(MLGBufferType::Constant, aBytes, MLGUsage::Dynamic, nullptr); } - MOZ_ASSERT(buffer->GetSize() >= aBytes); + CachePool& pool = mCaches[sizeClassIndex]; - // Assign this buffer to the current frame, so it becomes available again once - // this frame expires. - mCurrent.Put(buffer); + // See if we've cached a buffer that wasn't used in the past 2 frames. A buffer + // used this frame could have already been mapped and written to, and a buffer + // used the previous frame might still be in-use by the GPU. While the latter + // case is okay, it causes aliasing in the driver. Since content is double + // buffered we do not let the compositor get more than 1 frames ahead, and a + // count of 2 frames should ensure the buffer is unused. + if (!pool.empty() && mFrameNumber >= pool.front().mLastUsedFrame + 2) { + RefPtr<MLGBuffer> buffer = pool.front().mBuffer; + pool.pop_front(); + pool.push_back(CacheEntry(mFrameNumber, buffer)); + MOZ_RELEASE_ASSERT(buffer->GetSize() >= aBytes); + return buffer; + } + + // Allocate a new buffer and cache it. + size_t bytes = (size_t(1) << sizeClass); + MOZ_ASSERT(bytes >= aBytes); + + RefPtr<MLGBuffer> buffer = + mDevice->CreateBuffer(MLGBufferType::Constant, bytes, MLGUsage::Dynamic, nullptr); + if (!buffer) { + return nullptr; + } + + pool.push_back(CacheEntry(mFrameNumber, buffer)); return buffer; } void BufferCache::EndFrame() { - BufferPool empty; - mExpired = Move(mPrevious); - mPrevious = Move(mCurrent); - mCurrent = Move(empty); -} - -RefPtr<MLGBuffer> -BufferPool::Take(size_t aBytes) -{ - MOZ_ASSERT(aBytes >= 16); - - // We need to bump the request up to the nearest size class. For example, - // a request of 24 bytes must allocate from the 32 byte pool. - SizeClass sc = GetSizeClassFromHighBit(CeilingLog2(aBytes)); - if (sc == SizeClass::Huge) { - return TakeHugeBuffer(aBytes); - } - - if (mClasses[sc].IsEmpty()) { - return nullptr; - } - - RefPtr<MLGBuffer> buffer = mClasses[sc].LastElement(); - mClasses[sc].RemoveElementAt(mClasses[sc].Length() - 1); - return buffer.forget(); -} - -void -BufferPool::Put(MLGBuffer* aBuffer) -{ - MOZ_ASSERT(aBuffer->GetSize() >= 16); + // Consider a buffer dead after ~5 seconds assuming 60 fps. + static size_t kMaxUnusedFrameCount = 60 * 5; - // When returning buffers, we bump them into a lower size class. For example - // a 24 byte buffer cannot be re-used for a 32-byte allocation, so it goes - // into the 16-byte class. - SizeClass sc = GetSizeClassFromHighBit(FloorLog2(aBuffer->GetSize())); - if (sc == SizeClass::Huge) { - mHugeBuffers.push_back(aBuffer); - } else { - mClasses[sc].AppendElement(aBuffer); + // At the end of each frame we pick one size class and see if it has any + // buffers that haven't been used for many frames. If so we clear them. + // The next frame we'll search the next size class. (This is just to spread + // work over more than one frame.) + CachePool& pool = mCaches[mNextSizeClassToShrink]; + while (!pool.empty()) { + // Since the deque is sorted oldest-to-newest, front-to-back, we can stop + // searching as soon as a buffer is active. + if (mFrameNumber - pool.front().mLastUsedFrame < kMaxUnusedFrameCount) { + break; + } + pool.pop_front(); } -} - -RefPtr<MLGBuffer> -BufferPool::TakeHugeBuffer(size_t aBytes) -{ - static const size_t kMaxSearches = 3; - size_t numSearches = std::min(kMaxSearches, mHugeBuffers.size()); - - for (size_t i = 0; i < numSearches; i++) { - RefPtr<MLGBuffer> buffer = mHugeBuffers.front(); - mHugeBuffers.pop_front(); + mNextSizeClassToShrink = (mNextSizeClassToShrink + 1) % mCaches.size(); - // Don't pick buffers that are massively overallocated. - if (buffer->GetSize() >= aBytes && buffer->GetSize() <= aBytes * 2) { - return buffer.forget(); - } - - // Return the buffer to the list. - mHugeBuffers.push_back(buffer); - } - - return nullptr; -} - -/* static */ BufferPool::SizeClass -BufferPool::GetSizeClassFromHighBit(size_t aBit) -{ - // If the size is smaller than our smallest size class (which should - // never happen), or bigger than our largest size class, we dump it - // in the catch-all "huge" list. - static const size_t kBitForFirstClass = 4; - static const size_t kBitForLastClass = kBitForFirstClass + size_t(SizeClass::Huge); - if (aBit < kBitForFirstClass || aBit >= kBitForLastClass) { - return SizeClass::Huge; - } - return SizeClass(aBit - kBitForFirstClass); + mFrameNumber++; } } // namespace layers } // namespace mozilla
--- a/gfx/layers/mlgpu/BufferCache.h +++ b/gfx/layers/mlgpu/BufferCache.h @@ -2,98 +2,86 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_gfx_layers_mlgpu_BufferCache_h #define mozilla_gfx_layers_mlgpu_BufferCache_h #include "mozilla/EnumeratedArray.h" -#include "nsTArray.h" #include <deque> +#include <vector> namespace mozilla { namespace layers { class MLGBuffer; class MLGDevice; -// This file defines a buffer caching mechanism for systems where constant -// buffer offset binding is not allowed. On those systems we must allocate -// new buffers each frame, and this cache allows us to re-use them. - -// Track buffers based on their size class, for small buffers. -class BufferPool -{ -public: - // Remove a buffer from the pool holding at least |aBytes|. - RefPtr<MLGBuffer> Take(size_t aBytes); - - // Put a buffer into the pool holding at least |aBytes|. - void Put(MLGBuffer* aBuffer); - - BufferPool& operator =(BufferPool&& aOther) { - mClasses = Move(aOther.mClasses); - mHugeBuffers = Move(aOther.mHugeBuffers); - return *this; - } - -private: - // Try to see if we can quickly re-use any buffer that didn't fit into a - // pre-existing size class. - RefPtr<MLGBuffer> TakeHugeBuffer(size_t aBytes); - - enum class SizeClass { - One, // 16+ bytes (one constant) - Two, // 32+ bytes (two constants) - Four, // 64+ bytes (four constants) - Eight, // 128+ bytes (eight constants) - Medium, // 256+ bytes (16 constants) - Large, // 512+ bytes (32 constants) - Huge // 1024+ bytes (64+ constants) - }; - static SizeClass GetSizeClassFromHighBit(size_t bit); - -private: - typedef nsTArray<RefPtr<MLGBuffer>> BufferList; - EnumeratedArray<SizeClass, SizeClass::Huge, BufferList> mClasses; - std::deque<RefPtr<MLGBuffer>> mHugeBuffers; -}; - -// Cache buffer pools based on how long ago they were last used. +// Cache MLGBuffers based on how long ago they were last used. class BufferCache { public: explicit BufferCache(MLGDevice* aDevice); ~BufferCache(); // Get a buffer that has at least |aBytes|, or create a new one // if none can be re-used. RefPtr<MLGBuffer> GetOrCreateBuffer(size_t aBytes); - // Rotate buffers after a frame has been completed. + // Age out old buffers after a frame has been completed. void EndFrame(); private: // Not RefPtr since this would create a cycle. MLGDevice* mDevice; - // We keep three active buffer pools: - // The "expired" pool, which was used two frames ago. - // The "previous" pool, which is being used by the previous frame. - // The "current" pool, which is being used for the current frame. + // The first size class is Log2(N), where 16 is the minimum size of a + // constant buffer (currently 16 bytes). + size_t mFirstSizeClass; + + // Each size class is a power of 2. Each pool of buffers is represented as a + // deque, with the least-recently-used (i.e., oldest) buffers at the front, + // and most-recently-used (i.e., newest) buffers at the back. To re-use a + // buffer it is popped off the front and re-added to the back. // - // We always allocate from the expired pool into the current pool. - // After a frame is completed, the current is moved into the previous, - // and the previous is moved into the expired. The expired buffers - // are deleted if still alive. - // - // Since Layers does not allow us to composite more than one frame - // ahead, this system ensures the expired buffers are always free. - BufferPool mExpired; - BufferPool mPrevious; - BufferPool mCurrent; + // This is not always efficient use of storage: if a single frame allocates + // 300 buffers of the same size, we may keep recycling through all those + // buffers for a long time, as long as at least one gets used per frame. + // But since buffers use tiny amounts of memory, and they are only mapped + // while drawing, it shouldn't be a big deal. + struct CacheEntry { + CacheEntry() : mLastUsedFrame(0) + {} + CacheEntry(const CacheEntry& aEntry) + : mLastUsedFrame(aEntry.mLastUsedFrame), + mBuffer(aEntry.mBuffer) + {} + CacheEntry(CacheEntry&& aEntry) + : mLastUsedFrame(aEntry.mLastUsedFrame), + mBuffer(Move(aEntry.mBuffer)) + {} + CacheEntry(size_t aLastUsedFrame, MLGBuffer* aBuffer) + : mLastUsedFrame(aLastUsedFrame), + mBuffer(aBuffer) + {} + + uint64_t mLastUsedFrame; + RefPtr<MLGBuffer> mBuffer; + }; + typedef std::deque<CacheEntry> CachePool; + + // We track how many frames have occurred to determine the age of cache entries. + uint64_t mFrameNumber; + + // To avoid doing too much work in one frame, we only shrink one size class + // per frame. + uint64_t mNextSizeClassToShrink; + + // There is one pool of buffers for each power of 2 allocation size. The + // maximum buffer size is at most 64KB on Direct3D 11. + std::vector<CachePool> mCaches; }; } // namespace layers } // namespace mozilla #endif // mozilla_gfx_layers_mlgpu_BufferCache_h
--- a/gfx/layers/mlgpu/MLGDevice.h +++ b/gfx/layers/mlgpu/MLGDevice.h @@ -404,16 +404,18 @@ public: // a given run of bytes. This is only supported on Windows 8+ for Direct3D 11. bool CanUseConstantBufferOffsetBinding() const { return mCanUseConstantBufferOffsetBinding; } // Return the maximum number of elements that can be bound to a constant // buffer. This is different than the maximum size of a buffer (there is // no such limit on Direct3D 11.1). + // + // The return value must be a power of two. size_t GetMaxConstantBufferBindSize() const { return mMaxConstantBufferBindSize; } protected: virtual ~MLGDevice(); virtual void SetPrimitiveTopology(MLGPrimitiveTopology aTopology) = 0;
--- a/gfx/thebes/gfxBlur.cpp +++ b/gfx/thebes/gfxBlur.cpp @@ -23,19 +23,16 @@ using namespace mozilla::gfx; gfxAlphaBoxBlur::gfxAlphaBoxBlur() : mData(nullptr), mAccelerated(false) { } gfxAlphaBoxBlur::~gfxAlphaBoxBlur() { - if (mData) { - free(mData); - } } already_AddRefed<gfxContext> gfxAlphaBoxBlur::Init(gfxContext* aDestinationCtx, const gfxRect& aRect, const IntSize& aSpreadRadius, const IntSize& aBlurRadius, const gfxRect* aDirtyRect, @@ -86,16 +83,17 @@ gfxAlphaBoxBlur::InitDrawTarget(const Dr mAccelerated = true; mDrawTarget = aReferenceDT->CreateShadowDrawTarget(mBlur.GetSize(), SurfaceFormat::A8, AlphaBoxBlur::CalculateBlurSigma(aBlurRadius.width)); } else { // Make an alpha-only surface to draw on. We will play with the data after // everything is drawn to create a blur effect. + // This will be freed when the DrawTarget dies mData = static_cast<uint8_t*>(calloc(1, blurDataSize)); if (!mData) { return nullptr; } mDrawTarget = Factory::DoesBackendSupportDataDrawtarget(backend) ? Factory::CreateDrawTargetForData(backend, mData, @@ -104,18 +102,29 @@ gfxAlphaBoxBlur::InitDrawTarget(const Dr SurfaceFormat::A8) : gfxPlatform::CreateDrawTargetForData(mData, mBlur.GetSize(), mBlur.GetStride(), SurfaceFormat::A8); } if (!mDrawTarget || !mDrawTarget->IsValid()) { + if (mData) { + free(mData); + } + return nullptr; } + + if (mData) { + mDrawTarget->AddUserData(reinterpret_cast<UserDataKey*>(mDrawTarget.get()), + mData, + free); + } + mDrawTarget->SetTransform(Matrix::Translation(-mBlur.GetRect().TopLeft())); return do_AddRef(mDrawTarget); } already_AddRefed<SourceSurface> gfxAlphaBoxBlur::DoBlur(const Color* aShadowColor, IntPoint* aOutTopLeft) { if (mData) {
--- a/js/ipc/WrapperOwner.cpp +++ b/js/ipc/WrapperOwner.cpp @@ -124,53 +124,52 @@ class CPOWProxyHandler : public BaseProx virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const override; virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const override; virtual bool getBuiltinClass(JSContext* cx, HandleObject obj, js::ESClass* cls) const override; virtual bool isArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer) const override; virtual const char* className(JSContext* cx, HandleObject proxy) const override; - virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, - MutableHandle<RegExpShared*> shared) const override; + virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override; virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override; virtual void objectMoved(JSObject* proxy, const JSObject* old) const override; virtual bool isCallable(JSObject* obj) const override; virtual bool isConstructor(JSObject* obj) const override; virtual bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const override; virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary, MutableHandleObject protop) const override; static const char family; static const CPOWProxyHandler singleton; }; const char CPOWProxyHandler::family = 0; const CPOWProxyHandler CPOWProxyHandler::singleton; -#define FORWARD(call, args) \ +#define FORWARD(call, args, failRetVal) \ AUTO_PROFILER_LABEL(__func__, JS); \ WrapperOwner* owner = OwnerOf(proxy); \ if (!owner->active()) { \ JS_ReportErrorASCII(cx, "cannot use a CPOW whose process is gone"); \ - return false; \ + return failRetVal; \ } \ if (!owner->allowMessage(cx)) { \ - return false; \ + return failRetVal; \ } \ { \ CPOWTimer timer(cx); \ return owner->call args; \ } bool CPOWProxyHandler::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, MutableHandle<PropertyDescriptor> desc) const { - FORWARD(getPropertyDescriptor, (cx, proxy, id, desc)); + FORWARD(getPropertyDescriptor, (cx, proxy, id, desc), false); } bool WrapperOwner::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, MutableHandle<PropertyDescriptor> desc) { ObjectId objId = idOf(proxy); @@ -190,17 +189,17 @@ WrapperOwner::getPropertyDescriptor(JSCo return toDescriptor(cx, result, desc); } bool CPOWProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, MutableHandle<PropertyDescriptor> desc) const { - FORWARD(getOwnPropertyDescriptor, (cx, proxy, id, desc)); + FORWARD(getOwnPropertyDescriptor, (cx, proxy, id, desc), false); } bool WrapperOwner::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, MutableHandle<PropertyDescriptor> desc) { ObjectId objId = idOf(proxy); @@ -221,17 +220,17 @@ WrapperOwner::getOwnPropertyDescriptor(J return toDescriptor(cx, result, desc); } bool CPOWProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id, Handle<PropertyDescriptor> desc, ObjectOpResult& result) const { - FORWARD(defineProperty, (cx, proxy, id, desc, result)); + FORWARD(defineProperty, (cx, proxy, id, desc, result), false); } bool WrapperOwner::defineProperty(JSContext* cx, HandleObject proxy, HandleId id, Handle<PropertyDescriptor> desc, ObjectOpResult& result) { ObjectId objId = idOf(proxy); @@ -252,30 +251,30 @@ WrapperOwner::defineProperty(JSContext* return ok(cx, status, result); } bool CPOWProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const { - FORWARD(ownPropertyKeys, (cx, proxy, props)); + FORWARD(ownPropertyKeys, (cx, proxy, props), false); } bool WrapperOwner::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) { return getPropertyKeys(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props); } bool CPOWProxyHandler::delete_(JSContext* cx, HandleObject proxy, HandleId id, ObjectOpResult& result) const { - FORWARD(delete_, (cx, proxy, id, result)); + FORWARD(delete_, (cx, proxy, id, result), false); } bool WrapperOwner::delete_(JSContext* cx, HandleObject proxy, HandleId id, ObjectOpResult& result) { ObjectId objId = idOf(proxy); JSIDVariant idVar; @@ -298,17 +297,17 @@ CPOWProxyHandler::enumerate(JSContext* c // call the base hook, that will use our implementation of getOwnEnumerablePropertyKeys // and follow the proto chain. return BaseProxyHandler::enumerate(cx, proxy); } bool CPOWProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const { - FORWARD(has, (cx, proxy, id, bp)); + FORWARD(has, (cx, proxy, id, bp), false); } bool WrapperOwner::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) { ObjectId objId = idOf(proxy); JSIDVariant idVar; @@ -322,17 +321,17 @@ WrapperOwner::has(JSContext* cx, HandleO LOG_STACK(); return ok(cx, status); } bool CPOWProxyHandler::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const { - FORWARD(hasOwn, (cx, proxy, id, bp)); + FORWARD(hasOwn, (cx, proxy, id, bp), false); } bool WrapperOwner::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) { ObjectId objId = idOf(proxy); JSIDVariant idVar; @@ -347,30 +346,30 @@ WrapperOwner::hasOwn(JSContext* cx, Hand return !!ok(cx, status); } bool CPOWProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id, MutableHandleValue vp) const { - FORWARD(get, (cx, proxy, receiver, id, vp)); + FORWARD(get, (cx, proxy, receiver, id, vp), false); } static bool CPOWDOMQI(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); if (!args.thisv().isObject() || !IsCPOW(&args.thisv().toObject())) { JS_ReportErrorASCII(cx, "bad this object passed to special QI"); return false; } RootedObject proxy(cx, &args.thisv().toObject()); - FORWARD(DOMQI, (cx, proxy, args)); + FORWARD(DOMQI, (cx, proxy, args), false); } static bool CPOWToString(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); RootedObject callee(cx, &args.callee()); RootedValue cpowValue(cx); @@ -378,17 +377,17 @@ CPOWToString(JSContext* cx, unsigned arg return false; if (!cpowValue.isObject() || !IsCPOW(&cpowValue.toObject())) { JS_ReportErrorASCII(cx, "CPOWToString called on an incompatible object"); return false; } RootedObject proxy(cx, &cpowValue.toObject()); - FORWARD(toString, (cx, proxy, args)); + FORWARD(toString, (cx, proxy, args), false); } bool WrapperOwner::toString(JSContext* cx, HandleObject cpow, JS::CallArgs& args) { // Ask the other side to call its toString method. Update the callee so that // it points to the CPOW and not to the synthesized CPOWToString function. args.setCallee(ObjectValue(*cpow)); @@ -525,17 +524,17 @@ WrapperOwner::get(JSContext* cx, HandleO return true; } bool CPOWProxyHandler::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v, JS::HandleValue receiver, JS::ObjectOpResult& result) const { - FORWARD(set, (cx, proxy, id, v, receiver, result)); + FORWARD(set, (cx, proxy, id, v, receiver, result), false); } bool WrapperOwner::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v, JS::HandleValue receiver, JS::ObjectOpResult& result) { ObjectId objId = idOf(proxy); @@ -559,29 +558,29 @@ WrapperOwner::set(JSContext* cx, JS::Han return ok(cx, status, result); } bool CPOWProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const { - FORWARD(getOwnEnumerablePropertyKeys, (cx, proxy, props)); + FORWARD(getOwnEnumerablePropertyKeys, (cx, proxy, props), false); } bool WrapperOwner::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) { return getPropertyKeys(cx, proxy, JSITER_OWNONLY, props); } bool CPOWProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result) const { - FORWARD(preventExtensions, (cx, proxy, result)); + FORWARD(preventExtensions, (cx, proxy, result), false); } bool WrapperOwner::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result) { ObjectId objId = idOf(proxy); ReturnStatus status; @@ -591,17 +590,17 @@ WrapperOwner::preventExtensions(JSContex LOG_STACK(); return ok(cx, status, result); } bool CPOWProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const { - FORWARD(isExtensible, (cx, proxy, extensible)); + FORWARD(isExtensible, (cx, proxy, extensible), false); } bool WrapperOwner::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) { ObjectId objId = idOf(proxy); ReturnStatus status; @@ -611,23 +610,23 @@ WrapperOwner::isExtensible(JSContext* cx LOG_STACK(); return ok(cx, status); } bool CPOWProxyHandler::call(JSContext* cx, HandleObject proxy, const CallArgs& args) const { - FORWARD(callOrConstruct, (cx, proxy, args, false)); + FORWARD(callOrConstruct, (cx, proxy, args, false), false); } bool CPOWProxyHandler::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const { - FORWARD(callOrConstruct, (cx, proxy, args, true)); + FORWARD(callOrConstruct, (cx, proxy, args, true), false); } bool WrapperOwner::callOrConstruct(JSContext* cx, HandleObject proxy, const CallArgs& args, bool construct) { ObjectId objId = idOf(proxy); @@ -700,17 +699,17 @@ WrapperOwner::callOrConstruct(JSContext* return false; return true; } bool CPOWProxyHandler::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const { - FORWARD(hasInstance, (cx, proxy, v, bp)); + FORWARD(hasInstance, (cx, proxy, v, bp), false); } bool WrapperOwner::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) { ObjectId objId = idOf(proxy); JSVariant vVar; @@ -724,17 +723,17 @@ WrapperOwner::hasInstance(JSContext* cx, LOG_STACK(); return ok(cx, status); } bool CPOWProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const { - FORWARD(getBuiltinClass, (cx, proxy, cls)); + FORWARD(getBuiltinClass, (cx, proxy, cls), false); } bool WrapperOwner::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) { ObjectId objId = idOf(proxy); uint32_t classValue = uint32_t(ESClass::Other); @@ -747,17 +746,17 @@ WrapperOwner::getBuiltinClass(JSContext* return ok(cx, status); } bool CPOWProxyHandler::isArray(JSContext* cx, HandleObject proxy, IsArrayAnswer* answer) const { - FORWARD(isArray, (cx, proxy, answer)); + FORWARD(isArray, (cx, proxy, answer), false); } bool WrapperOwner::isArray(JSContext* cx, HandleObject proxy, IsArrayAnswer* answer) { ObjectId objId = idOf(proxy); uint32_t ans; @@ -798,17 +797,17 @@ WrapperOwner::className(JSContext* cx, H } return data->className.get(); } bool CPOWProxyHandler::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const { - FORWARD(getPrototype, (cx, proxy, objp)); + FORWARD(getPrototype, (cx, proxy, objp), false); } bool WrapperOwner::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject objp) { ObjectId objId = idOf(proxy); ObjectOrNullVariant val; @@ -825,17 +824,17 @@ WrapperOwner::getPrototype(JSContext* cx return true; } bool CPOWProxyHandler::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary, MutableHandleObject objp) const { - FORWARD(getPrototypeIfOrdinary, (cx, proxy, isOrdinary, objp)); + FORWARD(getPrototypeIfOrdinary, (cx, proxy, isOrdinary, objp), false); } bool WrapperOwner::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary, MutableHandleObject objp) { ObjectId objId = idOf(proxy); @@ -849,45 +848,45 @@ WrapperOwner::getPrototypeIfOrdinary(JSC if (!ok(cx, status)) return false; objp.set(fromObjectOrNullVariant(cx, val)); return true; } -bool -CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy, - MutableHandle<RegExpShared*> shared) const +RegExpShared* +CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy) const { - FORWARD(regexp_toShared, (cx, proxy, shared)); + FORWARD(regexp_toShared, (cx, proxy), nullptr); } -bool -WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandle<RegExpShared*> shared) +RegExpShared* +WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy) { ObjectId objId = idOf(proxy); ReturnStatus status; nsString source; unsigned flags = 0; - if (!SendRegExpToShared(objId, &status, &source, &flags)) - return ipcfail(cx); - + if (!SendRegExpToShared(objId, &status, &source, &flags)) { + MOZ_ALWAYS_FALSE(ipcfail(cx)); + return nullptr; + } LOG_STACK(); if (!ok(cx, status)) - return false; + return nullptr; RootedObject regexp(cx); regexp = JS_NewUCRegExpObject(cx, source.get(), source.Length(), flags); if (!regexp) - return false; + return nullptr; - return js::RegExpToSharedNonInline(cx, regexp, shared); + return js::RegExpToSharedNonInline(cx, regexp); } void CPOWProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const { AuxCPOWData* aux = AuxCPOWDataOf(proxy); OwnerOf(proxy)->drop(proxy); @@ -1010,17 +1009,17 @@ InstanceOf(JSObject* proxy, const nsID* return NS_ERROR_UNEXPECTED; return parent->instanceOf(proxy, id, bp); } bool DOMInstanceOf(JSContext* cx, JSObject* proxyArg, int prototypeID, int depth, bool* bp) { RootedObject proxy(cx, proxyArg); - FORWARD(domInstanceOf, (cx, proxy, prototypeID, depth, bp)); + FORWARD(domInstanceOf, (cx, proxy, prototypeID, depth, bp), false); } } /* namespace jsipc */ } /* namespace mozilla */ nsresult WrapperOwner::instanceOf(JSObject* obj, const nsID* id, bool* bp) {
--- a/js/ipc/WrapperOwner.h +++ b/js/ipc/WrapperOwner.h @@ -55,18 +55,17 @@ class WrapperOwner : public virtual Java bool hasInstance(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool* bp); bool getBuiltinClass(JSContext* cx, JS::HandleObject proxy, js::ESClass* cls); bool isArray(JSContext* cx, JS::HandleObject proxy, JS::IsArrayAnswer* answer); const char* className(JSContext* cx, JS::HandleObject proxy); bool getPrototype(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleObject protop); bool getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject proxy, bool* isOrdinary, JS::MutableHandleObject protop); - bool regexp_toShared(JSContext* cx, JS::HandleObject proxy, - js::MutableHandle<js::RegExpShared*> shared); + js::RegExpShared* regexp_toShared(JSContext* cx, JS::HandleObject proxy); nsresult instanceOf(JSObject* obj, const nsID* id, bool* bp); bool toString(JSContext* cx, JS::HandleObject callee, JS::CallArgs& args); bool DOMQI(JSContext* cx, JS::HandleObject callee, JS::CallArgs& args); /* * Check that |obj| is a DOM wrapper whose prototype chain contains
--- a/js/public/CharacterEncoding.h +++ b/js/public/CharacterEncoding.h @@ -227,43 +227,43 @@ LossyTwoByteCharsToNewLatin1CharsZ(JSCon const mozilla::Range<const char16_t> tbchars(begin, length); return JS::LossyTwoByteCharsToNewLatin1CharsZ(cx, tbchars); } template <typename CharT> extern UTF8CharsZ CharsToNewUTF8CharsZ(JSContext* maybeCx, const mozilla::Range<CharT> chars); -uint32_t +JS_PUBLIC_API(uint32_t) Utf8ToOneUcs4Char(const uint8_t* utf8Buffer, int utf8Length); /* * Inflate bytes in UTF-8 encoding to char16_t. * - On error, returns an empty TwoByteCharsZ. * - On success, returns a malloc'd TwoByteCharsZ, and updates |outlen| to hold * its length; the length value excludes the trailing null. */ -extern TwoByteCharsZ +extern JS_PUBLIC_API(TwoByteCharsZ) UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen); /* * Like UTF8CharsToNewTwoByteCharsZ, but for ConstUTF8CharsZ. */ -extern TwoByteCharsZ +extern JS_PUBLIC_API(TwoByteCharsZ) UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8, size_t* outlen); /* * The same as UTF8CharsToNewTwoByteCharsZ(), except that any malformed UTF-8 characters * will be replaced by \uFFFD. No exception will be thrown for malformed UTF-8 * input. */ -extern TwoByteCharsZ +extern JS_PUBLIC_API(TwoByteCharsZ) LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen); -extern TwoByteCharsZ +extern JS_PUBLIC_API(TwoByteCharsZ) LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8, size_t* outlen); /* * Returns the length of the char buffer required to encode |s| as UTF8. * Does not include the null-terminator. */ JS_PUBLIC_API(size_t) GetDeflatedUTF8StringLength(JSFlatString* s); @@ -303,32 +303,32 @@ JS_PUBLIC_API(SmallestEncoding) FindSmallestEncoding(UTF8Chars utf8); /* * Return a null-terminated Latin-1 string copied from the input string, * storing its length (excluding null terminator) in |*outlen|. Fail and * report an error if the string contains non-Latin-1 codepoints. Returns * Latin1CharsZ() on failure. */ -extern Latin1CharsZ +extern JS_PUBLIC_API(Latin1CharsZ) UTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen); /* * Return a null-terminated Latin-1 string copied from the input string, * storing its length (excluding null terminator) in |*outlen|. Non-Latin-1 * codepoints are replaced by '?'. Returns Latin1CharsZ() on failure. */ -extern Latin1CharsZ +extern JS_PUBLIC_API(Latin1CharsZ) LossyUTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen); /* * Returns true if all characters in the given null-terminated string are * ASCII, i.e. < 0x80, false otherwise. */ -extern bool +extern JS_PUBLIC_API(bool) StringIsASCII(const char* s); } // namespace JS inline void JS_free(JS::Latin1CharsZ& ptr) { js_free((void*)ptr.get()); } inline void JS_free(JS::UTF8CharsZ& ptr) { js_free((void*)ptr.get()); } #endif /* js_CharacterEncoding_h */
--- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -51,17 +51,17 @@ typedef enum JSGCInvocationKind { namespace JS { #define GCREASONS(D) \ /* Reasons internal to the JS engine */ \ D(API) \ D(EAGER_ALLOC_TRIGGER) \ D(DESTROY_RUNTIME) \ - D(UNUSED0) \ + D(ROOTS_REMOVED) \ D(LAST_DITCH) \ D(TOO_MUCH_MALLOC) \ D(ALLOC_TRIGGER) \ D(DEBUG_GC) \ D(COMPARTMENT_REVIVED) \ D(RESET) \ D(OUT_OF_NURSERY) \ D(EVICT_NURSERY) \ @@ -697,21 +697,19 @@ static MOZ_ALWAYS_INLINE void ExposeScriptToActiveJS(JSScript* script) { MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&script)); js::gc::ExposeGCThingToActiveJS(GCCellPtr(script)); } /* * Internal to Firefox. - * - * Note: this is not related to the PokeGC in nsJSEnvironment. */ extern JS_FRIEND_API(void) -PokeGC(JSContext* cx); +NotifyGCRootsRemoved(JSContext* cx); /* * Internal to Firefox. */ extern JS_FRIEND_API(void) NotifyDidPaint(JSContext* cx); } /* namespace JS */
--- a/js/public/HeapAPI.h +++ b/js/public/HeapAPI.h @@ -405,16 +405,19 @@ static MOZ_ALWAYS_INLINE Zone* GetStringZone(JSString* str) { return js::gc::detail::GetGCThingZone(uintptr_t(str)); } extern JS_PUBLIC_API(Zone*) GetObjectZone(JSObject* obj); +extern JS_PUBLIC_API(Zone*) +GetValueZone(const Value& value); + static MOZ_ALWAYS_INLINE bool GCThingIsMarkedGray(GCCellPtr thing) { if (thing.mayBeOwnedByOtherRuntime()) return false; return js::gc::detail::CellIsMarkedGrayIfKnown(thing.asCell()); }
--- a/js/public/Id.h +++ b/js/public/Id.h @@ -175,16 +175,23 @@ struct GCPolicy<jsid> } // namespace JS namespace js { template <> struct BarrierMethods<jsid> { + static gc::Cell* asGCThingOrNull(jsid id) { + if (JSID_IS_STRING(id)) + return reinterpret_cast<gc::Cell*>(JSID_TO_STRING(id)); + if (JSID_IS_SYMBOL(id)) + return reinterpret_cast<gc::Cell*>(JSID_TO_SYMBOL(id)); + return nullptr; + } static void postBarrier(jsid* idp, jsid prev, jsid next) {} static void exposeToJS(jsid id) { if (JSID_IS_GCTHING(id)) js::gc::ExposeGCThingToActiveJS(JSID_TO_GCTHING(id)); } }; // If the jsid is a GC pointer type, convert to that type and call |f| with
--- a/js/public/Proxy.h +++ b/js/public/Proxy.h @@ -326,18 +326,17 @@ class JS_FRIEND_API(BaseProxyHandler) virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl, const CallArgs& args) const; virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const; virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const; virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const; virtual const char* className(JSContext* cx, HandleObject proxy) const; virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const; - virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, - MutableHandle<js::RegExpShared*> shared) const; + virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const; virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const; virtual void trace(JSTracer* trc, JSObject* proxy) const; virtual void finalize(JSFreeOp* fop, JSObject* proxy) const; virtual void objectMoved(JSObject* proxy, const JSObject* old) const; // Allow proxies, wrappers in particular, to specify callability at runtime. // Note: These do not take const JSObject*, but they do in spirit. // We are not prepared to do this, as there's little const correctness
--- a/js/public/TracingAPI.h +++ b/js/public/TracingAPI.h @@ -332,38 +332,59 @@ class MOZ_RAII AutoTracingDetails JS::CallbackTracer* JSTracer::asCallbackTracer() { MOZ_ASSERT(isCallbackTracer()); return static_cast<JS::CallbackTracer*>(this); } +namespace js { +namespace gc { +template <typename T> +JS_PUBLIC_API(void) TraceExternalEdge(JSTracer* trc, T* thingp, const char* name); +} // namespace gc +} // namespace js + namespace JS { // The JS::TraceEdge family of functions traces the given GC thing reference. // This performs the tracing action configured on the given JSTracer: typically // calling the JSTracer::callback or marking the thing as live. // // The argument to JS::TraceEdge is an in-out param: when the function returns, // the garbage collector might have moved the GC thing. In this case, the // reference passed to JS::TraceEdge will be updated to the thing's new // location. Callers of this method are responsible for updating any state that // is dependent on the object's address. For example, if the object's address // is used as a key in a hashtable, then the object must be removed and // re-inserted with the correct hash. // // Note that while |edgep| must never be null, it is fine for |*edgep| to be // nullptr. + template <typename T> -extern JS_PUBLIC_API(void) -TraceEdge(JSTracer* trc, JS::Heap<T>* edgep, const char* name); +inline void +TraceEdge(JSTracer* trc, JS::Heap<T>* thingp, const char* name) +{ + MOZ_ASSERT(thingp); + if (*thingp) + js::gc::TraceExternalEdge(trc, thingp->unsafeGet(), name); +} -extern JS_PUBLIC_API(void) -TraceEdge(JSTracer* trc, JS::TenuredHeap<JSObject*>* edgep, const char* name); +template <typename T> +inline void +TraceEdge(JSTracer* trc, JS::TenuredHeap<T>* thingp, const char* name) +{ + MOZ_ASSERT(thingp); + if (T ptr = thingp->unbarrieredGetPtr()) { + js::gc::TraceExternalEdge(trc, &ptr, name); + thingp->setPtr(ptr); + } +} // Edges that are always traced as part of root marking do not require // incremental barriers. This function allows for marking non-barriered // pointers, but asserts that this happens during root marking. // // Note that while |edgep| must never be null, it is fine for |*edgep| to be // nullptr. template <typename T>
--- a/js/public/Utility.h +++ b/js/public/Utility.h @@ -73,17 +73,17 @@ enum ThreadType { /* * Getter/Setter functions to encapsulate mozilla::ThreadLocal, * implementation is in jsutil.cpp. */ # if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) extern bool InitThreadType(void); extern void SetThreadType(ThreadType); -extern uint32_t GetThreadType(void); +extern JS_FRIEND_API(uint32_t) GetThreadType(void); # else inline bool InitThreadType(void) { return true; } inline void SetThreadType(ThreadType t) {}; inline uint32_t GetThreadType(void) { return 0; } # endif } /* namespace oom */ } /* namespace js */
--- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -141,18 +141,18 @@ ExecuteRegExpImpl(JSContext* cx, RegExpS } /* Legacy ExecuteRegExp behavior is baked into the JSAPI. */ bool js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*> reobj, HandleLinearString input, size_t* lastIndex, bool test, MutableHandleValue rval) { - RootedRegExpShared shared(cx); - if (!RegExpObject::getShared(cx, reobj, &shared)) + RootedRegExpShared shared(cx, RegExpObject::getShared(cx, reobj)); + if (!shared) return false; ScopedMatchPairs matches(&cx->tempLifoAlloc()); RegExpRunStatus status = ExecuteRegExpImpl(cx, res, &shared, input, *lastIndex, &matches, nullptr); if (status == RegExpRunStatus_Error) return false; @@ -229,18 +229,18 @@ RegExpInitializeIgnoringLastIndex(JSCont /* Step 5. */ if (!ParseRegExpFlags(cx, flagStr, &flags)) return false; } if (sharedUse == UseRegExpShared) { /* Steps 7-8. */ - RootedRegExpShared re(cx); - if (!cx->zone()->regExps.get(cx, pattern, flags, &re)) + RegExpShared* re = cx->zone()->regExps.get(cx, pattern, flags); + if (!re) return false; /* Steps 9-12. */ obj->initIgnoringLastIndex(pattern, flags); obj->setShared(*re); } else { /* Steps 7-8. */ @@ -335,22 +335,22 @@ regexp_compile_impl(JSContext* cx, const // don't assume |patternObj.is<RegExpObject>()|. For the same reason, // don't reuse the RegExpShared below. RootedObject patternObj(cx, &patternValue.toObject()); RootedAtom sourceAtom(cx); RegExpFlag flags; { // Step 3b. - RootedRegExpShared g(cx); - if (!RegExpToShared(cx, patternObj, &g)) + RegExpShared* shared = RegExpToShared(cx, patternObj); + if (!shared) return false; - sourceAtom = g->getSource(); - flags = g->getFlags(); + sourceAtom = shared->getSource(); + flags = shared->getFlags(); } // Step 5, minus lastIndex zeroing. regexp->initIgnoringLastIndex(sourceAtom, flags); } else { // Step 4. RootedValue P(cx, patternValue); RootedValue F(cx, args.get(1)); @@ -430,24 +430,24 @@ js::regexp_construct(JSContext* cx, unsi // don't assume |patternObj.is<RegExpObject>()|. For the same reason, // don't reuse the RegExpShared below. RootedObject patternObj(cx, &patternValue.toObject()); RootedAtom sourceAtom(cx); RegExpFlag flags; { // Step 4.a. - RootedRegExpShared g(cx); - if (!RegExpToShared(cx, patternObj, &g)) + RegExpShared* shared = RegExpToShared(cx, patternObj); + if (!shared) return false; - sourceAtom = g->getSource(); + sourceAtom = shared->getSource(); // Step 4.b. // Get original flags in all cases, to compare with passed flags. - flags = g->getFlags(); + flags = shared->getFlags(); } // Step 7. RootedObject proto(cx); if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) return false; Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, GenericObject, proto)); @@ -894,18 +894,18 @@ ExecuteRegExp(JSContext* cx, HandleObjec * WARNING: Despite the presence of spec step comment numbers, this * algorithm isn't consistent with any ES6 version, draft or * otherwise. YOU HAVE BEEN WARNED. */ /* Steps 1-2 performed by the caller. */ Rooted<RegExpObject*> reobj(cx, ®exp->as<RegExpObject>()); - RootedRegExpShared re(cx); - if (!RegExpObject::getShared(cx, reobj, &re)) + RootedRegExpShared re(cx, RegExpObject::getShared(cx, reobj)); + if (!re) return RegExpRunStatus_Error; RegExpStatics* res; if (staticsUpdate == UpdateRegExpStatics) { res = GlobalObject::getRegExpStatics(cx, cx->global()); if (!res) return RegExpRunStatus_Error; } else {
--- a/js/src/devtools/automation/cgc-jittest-timeouts.txt +++ b/js/src/devtools/automation/cgc-jittest-timeouts.txt @@ -1,9 +1,10 @@ SIMD/nursery-overflow.js +asm.js/testBug1117235.js asm.js/testParallelCompile.js auto-regress/bug653395.js auto-regress/bug654392.js auto-regress/bug675251.js auto-regress/bug729797.js baseline/bug847446.js baseline/bug852175.js basic/bug632964-regexp.js
--- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -228,16 +228,18 @@ var ignoreFunctions = { "iJIT_IsProfilingActive" : true, "iJIT_NotifyEvent": true, // The big hammers. "PR_GetCurrentThread" : true, "calloc" : true, "uint8 nsContentUtils::IsExpandedPrincipal(nsIPrincipal*)" : true, + + "void mozilla::AutoProfilerLabel::~AutoProfilerLabel(int32)" : true, }; function extraGCFunctions() { return ["ffi_call"]; } function isProtobuf(name) {
--- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -711,17 +711,17 @@ class GCRuntime void triggerFullGCForAtoms() { MOZ_ASSERT(fullGCForAtomsRequested_); fullGCForAtomsRequested_ = false; MOZ_RELEASE_ASSERT(triggerGC(JS::gcreason::ALLOC_TRIGGER)); } void runDebugGC(); - inline void poke(); + void notifyRootsRemoved(); enum TraceOrMarkRuntime { TraceRuntime, MarkRuntime }; void traceRuntime(JSTracer* trc, AutoLockForExclusiveAccess& lock); void traceRuntimeForMinorGC(JSTracer* trc, AutoLockForExclusiveAccess& lock); @@ -1298,33 +1298,34 @@ class GCRuntime */ ActiveThreadData<bool> incrementalAllowed; /* * Whether compacting GC can is enabled globally. */ ActiveThreadData<bool> compactingEnabled; - ActiveThreadData<bool> poked; + ActiveThreadData<bool> rootsRemoved; /* * These options control the zealousness of the GC. At every allocation, * nextScheduled is decremented. When it reaches zero we do a full GC. * * At this point, if zeal_ is one of the types that trigger periodic * collection, then nextScheduled is reset to the value of zealFrequency. * Otherwise, no additional GCs take place. * * You can control these values in several ways: * - Set the JS_GC_ZEAL environment variable * - Call gczeal() or schedulegc() from inside shell-executed JS code * (see the help for details) * * If gcZeal_ == 1 then we perform GCs in select places (during MaybeGC and - * whenever a GC poke happens). This option is mainly useful to embedders. + * whenever we are notified that GC roots have been removed). This option is + * mainly useful to embedders. * * We use zeal_ == 4 to enable write barrier verification. See the comment * in jsgc.cpp for more information about this. * * zeal_ values from 8 to 10 periodically run different types of * incremental GC. * * zeal_ value 14 performs periodic shrinking collections.
--- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -450,31 +450,20 @@ void js::TraceNullableEdge(JSTracer* trc, ReadBarriered<T>* thingp, const char* name) { if (InternalBarrierMethods<T>::isMarkable(thingp->unbarrieredGet())) DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name); } template <typename T> JS_PUBLIC_API(void) -JS::TraceEdge(JSTracer* trc, JS::Heap<T>* thingp, const char* name) +js::gc::TraceExternalEdge(JSTracer* trc, T* thingp, const char* name) { - MOZ_ASSERT(thingp); - if (InternalBarrierMethods<T>::isMarkable(*thingp->unsafeGet())) - DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name); -} - -JS_PUBLIC_API(void) -JS::TraceEdge(JSTracer* trc, JS::TenuredHeap<JSObject*>* thingp, const char* name) -{ - MOZ_ASSERT(thingp); - if (JSObject* ptr = thingp->unbarrieredGetPtr()) { - DispatchToTracer(trc, &ptr, name); - thingp->setPtr(ptr); - } + MOZ_ASSERT(InternalBarrierMethods<T>::isMarkable(*thingp)); + DispatchToTracer(trc, ConvertToBase(thingp), name); } template <typename T> void js::TraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name) { DispatchToTracer(trc, ConvertToBase(thingp), name); } @@ -578,20 +567,20 @@ js::TraceRootRange(JSTracer* trc, size_t template void js::TraceNullableRoot<type>(JSTracer*, type*, const char*); \ template void js::TraceNullableRoot<type>(JSTracer*, ReadBarriered<type>*, const char*); \ template void js::TraceRange<type>(JSTracer*, size_t, WriteBarrieredBase<type>*, const char*); \ template void js::TraceRootRange<type>(JSTracer*, size_t, type*, const char*); FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS) #undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS #define INSTANTIATE_PUBLIC_TRACE_FUNCTIONS(type) \ - template JS_PUBLIC_API(void) JS::TraceEdge<type>(JSTracer*, JS::Heap<type>*, const char*); \ template JS_PUBLIC_API(void) JS::UnsafeTraceRoot<type>(JSTracer*, type*, const char*); \ template JS_PUBLIC_API(void) js::UnsafeTraceManuallyBarrieredEdge<type>(JSTracer*, type*, \ - const char*); + const char*); \ + template JS_PUBLIC_API(void) js::gc::TraceExternalEdge<type>(JSTracer*, type*, const char*); FOR_EACH_PUBLIC_GC_POINTER_TYPE(INSTANTIATE_PUBLIC_TRACE_FUNCTIONS) FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_PUBLIC_TRACE_FUNCTIONS) #undef INSTANTIATE_PUBLIC_TRACE_FUNCTIONS template <typename T> void js::TraceManuallyBarrieredCrossCompartmentEdge(JSTracer* trc, JSObject* src, T* dst, const char* name)
--- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -437,22 +437,31 @@ js::gc::GCRuntime::finishRoots() // Restore the wrapper tracing so that we leak instead of leaving dangling // pointers. grayRootTracer = prior; #endif // DEBUG } // Append traced things to a buffer on the zone for use later in the GC. // See the comment in GCRuntime.h above grayBufferState for details. -class BufferGrayRootsTracer : public JS::CallbackTracer +class BufferGrayRootsTracer final : public JS::CallbackTracer { // Set to false if we OOM while buffering gray roots. bool bufferingGrayRootsFailed; - void onChild(const JS::GCCellPtr& thing) override; + void onObjectEdge(JSObject** objp) override { bufferRoot(*objp); } + void onStringEdge(JSString** stringp) override { bufferRoot(*stringp); } + void onScriptEdge(JSScript** scriptp) override { bufferRoot(*scriptp); } + void onSymbolEdge(JS::Symbol** symbolp) override { bufferRoot(*symbolp); } + + void onChild(const JS::GCCellPtr& thing) override { + MOZ_CRASH("Unexpected gray root kind"); + } + + template <typename T> inline void bufferRoot(T* thing); public: explicit BufferGrayRootsTracer(JSRuntime* rt) : JS::CallbackTracer(rt), bufferingGrayRootsFailed(false) {} bool failed() const { return bufferingGrayRootsFailed; } @@ -489,42 +498,36 @@ js::gc::GCRuntime::bufferGrayRoots() if (grayBufferer.failed()) { grayBufferState = GrayBufferState::Failed; resetBufferedGrayRoots(); } else { grayBufferState = GrayBufferState::Okay; } } -struct SetMaybeAliveFunctor { - template <typename T> void operator()(T* t) { SetMaybeAliveFlag(t); } -}; - -void -BufferGrayRootsTracer::onChild(const JS::GCCellPtr& thing) +template <typename T> +inline void +BufferGrayRootsTracer::bufferRoot(T* thing) { MOZ_ASSERT(JS::CurrentThreadIsHeapBusy()); - MOZ_RELEASE_ASSERT(thing); + MOZ_ASSERT(thing); // Check if |thing| is corrupt by calling a method that touches the heap. - MOZ_RELEASE_ASSERT(thing.asCell()->getTraceKind() <= JS::TraceKind::Null); + MOZ_ASSERT(thing->getTraceKind() <= JS::TraceKind::Null); - if (bufferingGrayRootsFailed) - return; - - gc::TenuredCell* tenured = gc::TenuredCell::fromPointer(thing.asCell()); + TenuredCell* tenured = &thing->asTenured(); // This is run from a helper thread while the mutator is paused so we have // to use *FromAnyThread methods here. Zone* zone = tenured->zoneFromAnyThread(); if (zone->isCollectingFromAnyThread()) { // See the comment on SetMaybeAliveFlag to see why we only do this for // objects and scripts. We rely on gray root buffering for this to work, // but we only need to worry about uncollected dead compartments during // incremental GCs (when we do gray root buffering). - DispatchTyped(SetMaybeAliveFunctor(), thing); + SetMaybeAliveFlag(thing); if (!zone->gcGrayRoots().append(tenured)) bufferingGrayRootsFailed = true; } } void GCRuntime::markBufferedGrayRoots(JS::Zone* zone)
--- a/js/src/jit/BaselineCacheIRCompiler.cpp +++ b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -1911,16 +1911,17 @@ BaselineCacheIRCompiler::init(CacheKind AllocatableGeneralRegisterSet available(ICStubCompiler::availableGeneralRegs(numInputsInRegs)); switch (kind) { case CacheKind::GetProp: case CacheKind::TypeOf: MOZ_ASSERT(numInputs == 1); allocator.initInputLocation(0, R0); break; + case CacheKind::Compare: case CacheKind::GetElem: case CacheKind::GetPropSuper: case CacheKind::SetProp: case CacheKind::In: case CacheKind::HasOwn: MOZ_ASSERT(numInputs == 2); allocator.initInputLocation(0, R0); allocator.initInputLocation(1, R1); @@ -1981,16 +1982,17 @@ jit::AttachBaselineCacheIRStub(JSContext // unlimited number of stubs. MOZ_ASSERT(stub->numOptimizedStubs() < MaxOptimizedCacheIRStubs); enum class CacheIRStubKind { Regular, Monitored, Updated }; uint32_t stubDataOffset; CacheIRStubKind stubKind; switch (kind) { + case CacheKind::Compare: case CacheKind::In: case CacheKind::HasOwn: case CacheKind::BindName: case CacheKind::TypeOf: stubDataOffset = sizeof(ICCacheIR_Regular); stubKind = CacheIRStubKind::Regular; break; case CacheKind::GetProp:
--- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -3691,8 +3691,88 @@ CallIRGenerator::tryAttachStub() if (strategy == OptStrategy::StringSplit) { return tryAttachStringSplit(); } MOZ_ASSERT(strategy == OptStrategy::None); return false; } + +CompareIRGenerator::CompareIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, + ICState::Mode mode, JSOp op, + HandleValue lhsVal, HandleValue rhsVal) + : IRGenerator(cx, script, pc, CacheKind::Compare, mode), + op_(op), lhsVal_(lhsVal), rhsVal_(rhsVal) +{ } + +bool +CompareIRGenerator::tryAttachString(ValOperandId lhsId, ValOperandId rhsId) +{ + MOZ_ASSERT(IsEqualityOp(op_)); + + if (!lhsVal_.isString() || !rhsVal_.isString()) + return false; + + StringOperandId lhsStrId = writer.guardIsString(lhsId); + StringOperandId rhsStrId = writer.guardIsString(rhsId); + writer.compareStringResult(op_, lhsStrId, rhsStrId); + writer.returnFromIC(); + + trackAttached("String"); + return true; +} + +bool +CompareIRGenerator::tryAttachStub() +{ + MOZ_ASSERT(cacheKind_ == CacheKind::Compare); + MOZ_ASSERT(IsEqualityOp(op_) || + op_ == JSOP_LE || op_ == JSOP_LT || + op_ == JSOP_GE || op_ == JSOP_GT); + + AutoAssertNoPendingException aanpe(cx_); + + ValOperandId lhsId(writer.setInputOperandId(0)); + ValOperandId rhsId(writer.setInputOperandId(1)); + + if (IsEqualityOp(op_)) { + if (tryAttachString(lhsId, rhsId)) + return true; + + trackNotAttached(); + return false; + } + + trackNotAttached(); + return false; +} + +void +CompareIRGenerator::trackAttached(const char* name) +{ +#ifdef JS_CACHEIR_SPEW + CacheIRSpewer& sp = CacheIRSpewer::singleton(); + if (sp.enabled()) { + LockGuard<Mutex> guard(sp.lock()); + sp.beginCache(guard, *this); + sp.valueProperty(guard, "lhs", lhsVal_); + sp.valueProperty(guard, "rhs", rhsVal_); + sp.attached(guard, name); + sp.endCache(guard); + } +#endif +} + +void +CompareIRGenerator::trackNotAttached() +{ +#ifdef JS_CACHEIR_SPEW + CacheIRSpewer& sp = CacheIRSpewer::singleton(); + if (sp.enabled()) { + LockGuard<Mutex> guard(sp.lock()); + sp.beginCache(guard, *this); + sp.valueProperty(guard, "lhs", lhsVal_); + sp.valueProperty(guard, "rhs", rhsVal_); + sp.endCache(guard); + } +#endif +}
--- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -140,16 +140,17 @@ class TypedOperandId : public OperandId _(GetPropSuper) \ _(GetElemSuper) \ _(SetProp) \ _(SetElem) \ _(BindName) \ _(In) \ _(HasOwn) \ _(TypeOf) \ + _(Compare) \ _(Call) enum class CacheKind : uint8_t { #define DEFINE_KIND(kind) kind, CACHE_IR_KINDS(DEFINE_KIND) #undef DEFINE_KIND }; @@ -251,16 +252,19 @@ extern const char* CacheKindNames[]; _(CallProxyGetByValueResult) \ _(CallProxyHasOwnResult) \ _(LoadUndefinedResult) \ _(LoadBooleanResult) \ _(LoadStringResult) \ _(LoadTypeOfObjectResult) \ \ _(CallStringSplitResult) \ + \ + _(CompareStringResult) \ + \ _(CallPrintString) \ _(Breakpoint) \ \ _(TypeMonitorResult) \ _(ReturnFromIC) \ _(WrapResult) enum class CacheOp { @@ -949,16 +953,22 @@ class MOZ_RAII CacheIRWriter : public JS void callStringSplitResult(StringOperandId str, StringOperandId sep, ObjectGroup* group) { writeOp(CacheOp::CallStringSplitResult); writeOperandId(str); writeOperandId(sep); addStubField(uintptr_t(group), StubField::Type::ObjectGroup); } + void compareStringResult(uint32_t op, StringOperandId lhs, StringOperandId rhs) { + writeOpWithOperandId(CacheOp::CompareStringResult, lhs); + writeOperandId(rhs); + buffer_.writeByte(uint32_t(op)); + } + void callPrintString(const char* str) { writeOp(CacheOp::CallPrintString); writePointer(const_cast<char*>(str)); } void breakpoint() { writeOp(CacheOp::Breakpoint); } @@ -1006,16 +1016,17 @@ class MOZ_RAII CacheIRReader uint32_t stubOffset() { return buffer_.readByte() * sizeof(uintptr_t); } GuardClassKind guardClassKind() { return GuardClassKind(buffer_.readByte()); } JSValueType valueType() { return JSValueType(buffer_.readByte()); } TypedThingLayout typedThingLayout() { return TypedThingLayout(buffer_.readByte()); } Scalar::Type scalarType() { return Scalar::Type(buffer_.readByte()); } uint32_t typeDescrKey() { return buffer_.readByte(); } JSWhyMagic whyMagic() { return JSWhyMagic(buffer_.readByte()); } + JSOp jsop() { return JSOp(buffer_.readByte()); } int32_t int32Immediate() { return buffer_.readSigned(); } uint32_t uint32Immediate() { return buffer_.readUnsigned(); } void* pointer() { return buffer_.readRawPointer(); } ReferenceTypeDescr::Type referenceTypeDescrType() { return ReferenceTypeDescr::Type(buffer_.readByte()); } @@ -1409,12 +1420,30 @@ class MOZ_RAII CallIRGenerator : public CallIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode mode, uint32_t argc, HandleValue callee, HandleValue thisval, HandleValueArray args); OptStrategy getOptStrategy(bool* optimizeAfterCall = nullptr); bool tryAttachStub(); }; +class MOZ_RAII CompareIRGenerator : public IRGenerator +{ + JSOp op_; + HandleValue lhsVal_; + HandleValue rhsVal_; + + bool tryAttachString(ValOperandId lhsId, ValOperandId rhsId); + + void trackAttached(const char* name); + void trackNotAttached(); + + public: + CompareIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode mode, + JSOp op, HandleValue lhsVal, HandleValue rhsVal); + + bool tryAttachStub(); +}; + } // namespace jit } // namespace js #endif /* jit_CacheIR_h */
--- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -2222,16 +2222,36 @@ CacheIRCompiler::emitLoadTypeOfObjectRes masm.tagValue(JSVAL_TYPE_STRING, scratch, output.valueReg()); } masm.bind(&done); return true; } bool +CacheIRCompiler::emitCompareStringResult() +{ + AutoOutputRegister output(*this); + + Register left = allocator.useRegister(masm, reader.stringOperandId()); + Register right = allocator.useRegister(masm, reader.stringOperandId()); + JSOp op = reader.jsop(); + + AutoScratchRegisterMaybeOutput scratch(allocator, masm, output); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + masm.compareStrings(op, left, right, scratch, failure->label()); + masm.tagValue(JSVAL_TYPE_BOOLEAN, scratch, output.valueReg()); + return true; +} + +bool CacheIRCompiler::emitCallPrintString() { const char* str = reinterpret_cast<char*>(reader.pointer()); masm.printf(str); return true; } bool
--- a/js/src/jit/CacheIRCompiler.h +++ b/js/src/jit/CacheIRCompiler.h @@ -50,16 +50,17 @@ namespace jit { _(LoadDenseElementResult) \ _(LoadDenseElementHoleResult) \ _(LoadDenseElementExistsResult) \ _(LoadDenseElementHoleExistsResult) \ _(LoadUnboxedArrayElementResult) \ _(LoadTypedElementResult) \ _(LoadObjectResult) \ _(LoadTypeOfObjectResult) \ + _(CompareStringResult) \ _(CallPrintString) \ _(Breakpoint) \ _(MegamorphicLoadSlotByValueResult) \ _(MegamorphicHasOwnResult) \ _(WrapResult) // Represents a Value on the Baseline frame's expression stack. Slot 0 is the // value on top of the stack (the most recently pushed value), slot 1 is the
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -281,16 +281,17 @@ CodeGenerator::visitOutOfLineICFallback( StoreRegisterTo(hasOwnIC->output()).generate(this); restoreLiveIgnore(lir, StoreRegisterTo(hasOwnIC->output()).clobbered()); masm.jump(ool->rejoin()); return; } case CacheKind::Call: + case CacheKind::Compare: case CacheKind::TypeOf: case CacheKind::GetPropSuper: case CacheKind::GetElemSuper: MOZ_CRASH("Unsupported IC"); } MOZ_CRASH(); }
--- a/js/src/jit/IonCacheIRCompiler.cpp +++ b/js/src/jit/IonCacheIRCompiler.cpp @@ -465,16 +465,17 @@ IonCacheIRCompiler::init() outputUnchecked_.emplace(TypedOrValueRegister(MIRType::Boolean, AnyRegister(output))); MOZ_ASSERT(numInputs == 2); allocator.initInputLocation(0, ic->id()); allocator.initInputLocation(1, ic->value()); break; } case CacheKind::Call: + case CacheKind::Compare: case CacheKind::TypeOf: case CacheKind::GetPropSuper: case CacheKind::GetElemSuper: MOZ_CRASH("Unsupported IC"); } if (liveRegs_) liveFloatRegs_ = LiveFloatRegisterSet(liveRegs_->fpus());
--- a/js/src/jit/IonIC.cpp +++ b/js/src/jit/IonIC.cpp @@ -48,16 +48,17 @@ IonIC::scratchRegisterForEntryJump() return asGetNameIC()->temp(); case CacheKind::BindName: return asBindNameIC()->temp(); case CacheKind::In: return asInIC()->temp(); case CacheKind::HasOwn: return asHasOwnIC()->output(); case CacheKind::Call: + case CacheKind::Compare: case CacheKind::TypeOf: case CacheKind::GetPropSuper: case CacheKind::GetElemSuper: MOZ_CRASH("Unsupported IC"); } MOZ_CRASH("Invalid kind"); }
--- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -1400,17 +1400,17 @@ DoCompareFallback(JSContext* cx, void* p // Don't pass lhs/rhs directly, we need the original values when // generating stubs. RootedValue lhsCopy(cx, lhs); RootedValue rhsCopy(cx, rhs); // Perform the compare operation. bool out; - switch(op) { + switch (op) { case JSOP_LT: if (!LessThan(cx, &lhsCopy, &rhsCopy, &out)) return false; break; case JSOP_LE: if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, &out)) return false; break; @@ -1451,16 +1451,29 @@ DoCompareFallback(JSContext* cx, void* p // Check to see if a new stub should be generated. if (stub->numOptimizedStubs() >= ICCompare_Fallback::MAX_OPTIMIZED_STUBS) { // TODO: Discard all stubs in this IC and replace with inert megamorphic stub. // But for now we just bail. return true; } + if (engine == ICStubEngine::Baseline) { + RootedScript script(cx, info.outerScript(cx)); + CompareIRGenerator gen(cx, script, pc, stub->state().mode(), op, lhs, rhs); + bool attached = false; + if (gen.tryAttachStub()) { + ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), + engine, script, stub, &attached); + if (newStub) + JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub"); + return true; + } + } + // Try to generate new stubs. if (lhs.isInt32() && rhs.isInt32()) { JitSpew(JitSpew_BaselineIC, " Generating %s(Int32, Int32) stub", CodeName[op]); ICCompare_Int32::Compiler compiler(cx, op, engine); ICStub* int32Stub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx))); if (!int32Stub) return false;
--- a/js/src/jsalloc.h +++ b/js/src/jsalloc.h @@ -12,24 +12,25 @@ */ #ifndef jsalloc_h #define jsalloc_h #include "js/TypeDecls.h" #include "js/Utility.h" +extern JS_PUBLIC_API(void) JS_ReportOutOfMemory(JSContext* cx); + namespace js { enum class AllocFunction { Malloc, Calloc, Realloc }; - /* Policy for using system memory functions and doing no error reporting. */ class SystemAllocPolicy { public: template <typename T> T* maybe_pod_malloc(size_t numElems) { return js_pod_malloc<T>(numElems); } template <typename T> T* maybe_pod_calloc(size_t numElems) { return js_pod_calloc<T>(numElems); } template <typename T> T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) { return js_pod_realloc<T>(p, oldSize, newSize);
--- a/js/src/jsapi-tests/testGCHooks.cpp +++ b/js/src/jsapi-tests/testGCHooks.cpp @@ -1,40 +1,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 "mozilla/TimeStamp.h" +#include "mozilla/ArrayUtils.h" #include "mozilla/UniquePtr.h" #include "js/GCAPI.h" #include "jsapi-tests/tests.h" static unsigned gSliceCallbackCount = 0; static void NonIncrementalGCSliceCallback(JSContext* cx, JS::GCProgress progress, const JS::GCDescription& desc) { using namespace JS; static GCProgress expect[] = { GC_CYCLE_BEGIN, GC_SLICE_BEGIN, GC_SLICE_END, GC_CYCLE_END }; + MOZ_RELEASE_ASSERT(gSliceCallbackCount < mozilla::ArrayLength(expect)); MOZ_RELEASE_ASSERT(progress == expect[gSliceCallbackCount++]); MOZ_RELEASE_ASSERT(desc.isZone_ == false); MOZ_RELEASE_ASSERT(desc.invocationKind_ == GC_NORMAL); MOZ_RELEASE_ASSERT(desc.reason_ == JS::gcreason::API); if (progress == GC_CYCLE_END) { mozilla::UniquePtr<char16_t> summary(desc.formatSummaryMessage(cx)); mozilla::UniquePtr<char16_t> message(desc.formatSliceMessage(cx)); mozilla::UniquePtr<char16_t> json(desc.formatJSON(cx, 0)); } } BEGIN_TEST(testGCSliceCallback) { + gSliceCallbackCount = 0; JS::SetGCSliceCallback(cx, NonIncrementalGCSliceCallback); JS_GC(cx); JS::SetGCSliceCallback(cx, nullptr); CHECK(gSliceCallbackCount == 4); return true; } END_TEST(testGCSliceCallback) + +static void +RootsRemovedGCSliceCallback(JSContext* cx, JS::GCProgress progress, const JS::GCDescription& desc) +{ + using namespace JS; + using namespace JS::gcreason; + + static GCProgress expectProgress[] = { + GC_CYCLE_BEGIN, GC_SLICE_BEGIN, GC_SLICE_END, GC_SLICE_BEGIN, GC_SLICE_END, GC_CYCLE_END, + GC_CYCLE_BEGIN, GC_SLICE_BEGIN, GC_SLICE_END, GC_CYCLE_END + }; + + static Reason expectReasons[] = { + DEBUG_GC, DEBUG_GC, DEBUG_GC, DEBUG_GC, DEBUG_GC, DEBUG_GC, + ROOTS_REMOVED, ROOTS_REMOVED, ROOTS_REMOVED, ROOTS_REMOVED + }; + + static_assert(mozilla::ArrayLength(expectProgress) == mozilla::ArrayLength(expectReasons), + "expectProgress and expectReasons arrays should be the same length"); + + MOZ_RELEASE_ASSERT(gSliceCallbackCount < mozilla::ArrayLength(expectProgress)); + MOZ_RELEASE_ASSERT(progress == expectProgress[gSliceCallbackCount]); + MOZ_RELEASE_ASSERT(desc.isZone_ == false); + MOZ_RELEASE_ASSERT(desc.invocationKind_ == GC_SHRINK); + MOZ_RELEASE_ASSERT(desc.reason_ == expectReasons[gSliceCallbackCount]); + gSliceCallbackCount++; +} + +BEGIN_TEST(testGCRootsRemoved) +{ + JS_SetGCParameter(cx, JSGC_MODE, JSGC_MODE_INCREMENTAL); + + gSliceCallbackCount = 0; + JS::SetGCSliceCallback(cx, RootsRemovedGCSliceCallback); + + JS::RootedObject obj(cx, JS_NewPlainObject(cx)); + CHECK(obj); + + JS::PrepareForFullGC(cx); + js::SliceBudget budget(js::WorkBudget(1)); + cx->runtime()->gc.startDebugGC(GC_SHRINK, budget); + CHECK(JS::IsIncrementalGCInProgress(cx)); + + // Trigger another GC after the current one in shrinking / shutdown GCs. + cx->runtime()->gc.notifyRootsRemoved(); + + JS::FinishIncrementalGC(cx, JS::gcreason::DEBUG_GC); + CHECK(!JS::IsIncrementalGCInProgress(cx)); + + JS::SetGCSliceCallback(cx, nullptr); + + JS_SetGCParameter(cx, JSGC_MODE, JSGC_MODE_GLOBAL); + + return true; +} +END_TEST(testGCRootsRemoved) +
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -6267,30 +6267,30 @@ JS_ObjectIsRegExp(JSContext* cx, HandleO } JS_PUBLIC_API(unsigned) JS_GetRegExpFlags(JSContext* cx, HandleObject obj) { AssertHeapIsIdle(); CHECK_REQUEST(cx); - RootedRegExpShared shared(cx); - if (!RegExpToShared(cx, obj, &shared)) + RegExpShared* shared = RegExpToShared(cx, obj); + if (!shared) return false; return shared->getFlags(); } JS_PUBLIC_API(JSString*) JS_GetRegExpSource(JSContext* cx, HandleObject obj) { AssertHeapIsIdle(); CHECK_REQUEST(cx); - RootedRegExpShared shared(cx); - if (!RegExpToShared(cx, obj, &shared)) + RegExpShared* shared = RegExpToShared(cx, obj); + if (!shared) return nullptr; return shared->getSource(); } /************************************************************************/ JS_PUBLIC_API(bool) JS_SetDefaultLocale(JSContext* cx, const char* locale)
--- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -1201,19 +1201,19 @@ js::SetActivityCallback(JSContext* cx, A JS_FRIEND_API(void) JS::NotifyDidPaint(JSContext* cx) { cx->runtime()->gc.notifyDidPaint(); } JS_FRIEND_API(void) -JS::PokeGC(JSContext* cx) +JS::NotifyGCRootsRemoved(JSContext* cx) { - cx->runtime()->gc.poke(); + cx->runtime()->gc.notifyRootsRemoved(); } JS_FRIEND_API(JSCompartment*) js::GetAnyCompartmentInZone(JS::Zone* zone) { CompartmentsInZoneIter comp(zone); MOZ_ASSERT(!comp.done()); return comp.get();
--- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1176,19 +1176,18 @@ CastToJSFreeOp(FreeOp* fop) extern JS_FRIEND_API(JSFlatString*) GetErrorTypeName(JSContext* cx, int16_t exnType); #ifdef JS_DEBUG extern JS_FRIEND_API(unsigned) GetEnterCompartmentDepth(JSContext* cx); #endif -extern JS_FRIEND_API(bool) -RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp, - JS::MutableHandle<RegExpShared*> shared); +extern JS_FRIEND_API(RegExpShared*) +RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp); /* Implemented in CrossCompartmentWrapper.cpp. */ typedef enum NukeReferencesToWindow { NukeWindowReferences, DontNukeWindowReferences } NukeReferencesToWindow; typedef enum NukeReferencesFromTarget {
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -923,17 +923,17 @@ GCRuntime::GCRuntime(JSRuntime* rt) : relocatedArenasToRelease(nullptr), #ifdef JS_GC_ZEAL markingValidator(nullptr), #endif interFrameGC(false), defaultTimeBudget_((int64_t) SliceBudget::UnlimitedTimeBudget), incrementalAllowed(true), compactingEnabled(true), - poked(false), + rootsRemoved(false), #ifdef JS_GC_ZEAL zealModeBits(0), zealFrequency(0), nextScheduled(0), deterministicOnly(false), incrementalLimit(0), #endif fullCompartmentChecks(false), @@ -964,17 +964,17 @@ GCRuntime::getZealBits(uint32_t* zealBit const char* gc::ZealModeHelpText = " Specifies how zealous the garbage collector should be. Some of these modes can\n" " be set simultaneously, by passing multiple level options, e.g. \"2;4\" will activate\n" " both modes 2 and 4. Modes can be specified by name or number.\n" " \n" " Values:\n" " 0: (None) Normal amount of collection (resets all modes)\n" - " 1: (Poke) Collect when roots are added or removed\n" + " 1: (RootsChange) Collect when roots are added or removed\n" " 2: (Alloc) Collect when every N allocations (default: 100)\n" " 3: (FrameGC) Collect when the window paints (browser only)\n" " 4: (VerifierPre) Verify pre write barriers between instructions\n" " 5: (FrameVerifierPre) Verify pre write barriers between paints\n" " 6: (StackRooting) Verify stack rooting\n" " 7: (GenerationalGC) Collect the nursery every N nursery allocations\n" " 8: (IncrementalRootsThenFinish) Incremental GC in two slices: 1) mark roots 2) finish collection\n" " 9: (IncrementalMarkAllThenFinish) Incremental GC in two slices: 1) mark all 2) new marking and finish\n" @@ -1628,17 +1628,17 @@ GCRuntime::addRoot(Value* vp, const char return rootsHash.ref().put(vp, name); } void GCRuntime::removeRoot(Value* vp) { rootsHash.ref().remove(vp); - poke(); + notifyRootsRemoved(); } extern JS_FRIEND_API(bool) js::AddRawValueRoot(JSContext* cx, Value* vp, const char* name) { MOZ_ASSERT(vp); MOZ_ASSERT(name); bool ok = cx->runtime()->gc.addRoot(vp, name); @@ -3103,17 +3103,17 @@ GCRuntime::triggerZoneGC(Zone* zone, JS: } void GCRuntime::maybeGC(Zone* zone) { MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); #ifdef JS_GC_ZEAL - if (hasZealMode(ZealMode::Alloc) || hasZealMode(ZealMode::Poke)) { + if (hasZealMode(ZealMode::Alloc) || hasZealMode(ZealMode::RootsChange)) { JS::PrepareForFullGC(rt->activeContextFromOwnThread()); gc(GC_NORMAL, JS::gcreason::DEBUG_GC); return; } #endif if (gcIfRequested()) return; @@ -6456,16 +6456,17 @@ GCRuntime::incrementalCollectSlice(Slice } switch (incrementalState) { case State::NotActive: initialReason = reason; cleanUpEverything = ShouldCleanUpEverything(reason, invocationKind); isCompacting = shouldCompact(); lastMarkSlice = false; + rootsRemoved = false; incrementalState = State::MarkRoots; MOZ_FALLTHROUGH; case State::MarkRoots: if (!beginMarkPhase(reason, lock)) { incrementalState = State::NotActive; @@ -6941,18 +6942,19 @@ GCRuntime::checkIfGCAllowedInCurrentStat return true; } bool GCRuntime::shouldRepeatForDeadZone(JS::gcreason::Reason reason) { MOZ_ASSERT_IF(reason == JS::gcreason::COMPARTMENT_REVIVED, !isIncremental); - - if (!isIncremental || isIncrementalGCInProgress()) + MOZ_ASSERT(!isIncrementalGCInProgress()); + + if (!isIncremental) return false; for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { if (c->scheduledForDestruction) return true; } return false; @@ -6968,47 +6970,48 @@ GCRuntime::collect(bool nonincrementalBy if (!checkIfGCAllowedInCurrentState(reason)) return; AutoTraceLog logGC(TraceLoggerForCurrentThread(), TraceLogger_GC); AutoStopVerifyingBarriers av(rt, IsShutdownGC(reason)); AutoEnqueuePendingParseTasksAfterGC aept(*this); AutoScheduleZonesForGC asz(rt); - bool repeat = false; + bool repeat; do { - poked = false; bool wasReset = gcCycle(nonincrementalByAPI, budget, reason) == IncrementalResult::Reset; if (reason == JS::gcreason::ABORT_GC) { MOZ_ASSERT(!isIncrementalGCInProgress()); break; } - bool repeatForDeadZone = false; - if (poked && cleanUpEverything) { - /* Need to re-schedule all zones for GC. */ - JS::PrepareForFullGC(rt->activeContextFromOwnThread()); - } else if (shouldRepeatForDeadZone(reason) && !wasReset) { - /* - * This code makes an extra effort to collect compartments that we - * thought were dead at the start of the GC. See the large comment - * in beginMarkPhase. - */ - repeatForDeadZone = true; - reason = JS::gcreason::COMPARTMENT_REVIVED; - } - /* - * If we reset an existing GC, we need to start a new one. Also, we - * repeat GCs that happen during shutdown (the gcShouldCleanUpEverything - * case) until we can be sure that no additional garbage is created - * (which typically happens if roots are dropped during finalizers). + * Sometimes when we finish a GC we need to immediately start a new one. + * This happens in the following cases: + * - when we reset the current GC + * - when finalizers drop roots during shutdown (the cleanUpEverything + * case) + * - when zones that we thought were dead at the start of GC are + * not collected (see the large comment in beginMarkPhase) */ - repeat = (poked && cleanUpEverything) || wasReset || repeatForDeadZone; + repeat = false; + if (!isIncrementalGCInProgress()) { + if (wasReset) { + repeat = true; + } else if (rootsRemoved && cleanUpEverything) { + /* Need to re-schedule all zones for GC. */ + JS::PrepareForFullGC(rt->activeContextFromOwnThread()); + repeat = true; + reason = JS::gcreason::ROOTS_REMOVED; + } else if (shouldRepeatForDeadZone(reason)) { + repeat = true; + reason = JS::gcreason::COMPARTMENT_REVIVED; + } + } } while (repeat); if (reason == JS::gcreason::COMPARTMENT_REVIVED) maybeDoCycleCollection(); #ifdef JS_GC_ZEAL if (rt->hasZealMode(ZealMode::CheckHeapAfterGC)) { gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::TRACE_HEAP); @@ -7565,16 +7568,28 @@ GCRuntime::runDebugGC() void GCRuntime::setFullCompartmentChecks(bool enabled) { MOZ_ASSERT(!JS::CurrentThreadIsHeapMajorCollecting()); fullCompartmentChecks = enabled; } +void +GCRuntime::notifyRootsRemoved() +{ + rootsRemoved = true; + +#ifdef JS_GC_ZEAL + /* Schedule a GC to happen "soon". */ + if (hasZealMode(ZealMode::RootsChange)) + nextScheduled = 1; +#endif +} + #ifdef JS_GC_ZEAL bool GCRuntime::selectForMarking(JSObject* object) { MOZ_ASSERT(!JS::CurrentThreadIsHeapMajorCollecting()); return selectedForMarking.ref().append(object); }
--- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1161,17 +1161,17 @@ inline void CheckGCThingAfterMovingGC(T* template <typename T> inline void CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t); inline void CheckValueAfterMovingGC(const JS::Value& value); #endif // JSGC_HASH_TABLE_CHECKS #define JS_FOR_EACH_ZEAL_MODE(D) \ - D(Poke, 1) \ + D(RootsChange, 1) \ D(Alloc, 2) \ D(FrameGC, 3) \ D(VerifierPre, 4) \ D(FrameVerifierPre, 5) \ D(StackRooting, 6) \ D(GenerationalGC, 7) \ D(IncrementalRootsThenFinish, 8) \ D(IncrementalMarkAllThenFinish, 9) \
--- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -36,28 +36,16 @@ GetGCObjectKind(const Class* clasp) MOZ_ASSERT(!clasp->isProxy(), "Proxies should use GetProxyGCObjectKind"); uint32_t nslots = JSCLASS_RESERVED_SLOTS(clasp); if (clasp->flags & JSCLASS_HAS_PRIVATE) nslots++; return GetGCObjectKind(nslots); } -inline void -GCRuntime::poke() -{ - poked = true; - -#ifdef JS_GC_ZEAL - /* Schedule a GC to happen "soon" after a GC poke. */ - if (hasZealMode(ZealMode::Poke)) - nextScheduled = 1; -#endif -} - class ArenaIter { Arena* arena; Arena* unsweptArena; Arena* sweptArena; mozilla::DebugOnly<bool> initialized; public:
--- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -111,18 +111,17 @@ class JS_FRIEND_API(Wrapper) : public Ba virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const override; virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const override; virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const override; virtual const char* className(JSContext* cx, HandleObject proxy) const override; virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override; - virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, - MutableHandle<RegExpShared*> shared) const override; + virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override; virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override; virtual bool isCallable(JSObject* obj) const override; virtual bool isConstructor(JSObject* obj) const override; virtual JSObject* weakmapKeyDelegate(JSObject* proxy) const override; public: using BaseProxyHandler::Action; @@ -206,18 +205,17 @@ class JS_FRIEND_API(CrossCompartmentWrap AutoIdVector& props) const override; virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl, const CallArgs& args) const override; virtual bool hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v, bool* bp) const override; virtual const char* className(JSContext* cx, HandleObject proxy) const override; virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper, unsigned indent) const override; - virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, - MutableHandle<RegExpShared*> shared) const override; + virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override; virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override; // Allocate CrossCompartmentWrappers in the nursery. virtual bool canNurseryAllocate() const override { return true; } static const CrossCompartmentWrapper singleton; static const CrossCompartmentWrapper singletonWithPrototype; }; @@ -304,18 +302,17 @@ class JS_FRIEND_API(SecurityWrapper) : p virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, ObjectOpResult& result) const override; virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const override; virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl, const CallArgs& args) const override; virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper, ESClass* cls) const override; virtual bool isArray(JSContext* cx, HandleObject wrapper, JS::IsArrayAnswer* answer) const override; - virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, - MutableHandle<RegExpShared*> shared) const override; + virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override; virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override; // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded // against. virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable) const override; virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const override;
--- a/js/src/proxy/BaseProxyHandler.cpp +++ b/js/src/proxy/BaseProxyHandler.cpp @@ -320,19 +320,18 @@ BaseProxyHandler::fun_toString(JSContext { if (proxy->isCallable()) return JS_NewStringCopyZ(cx, "function () {\n [native code]\n}"); RootedValue v(cx, ObjectValue(*proxy)); ReportIsNotFunction(cx, v); return nullptr; } -bool -BaseProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy, - MutableHandleRegExpShared shared) const +RegExpShared* +BaseProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy) const { MOZ_CRASH("This should have been a wrapped regexp"); } bool BaseProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const { vp.setUndefined();
--- a/js/src/proxy/CrossCompartmentWrapper.cpp +++ b/js/src/proxy/CrossCompartmentWrapper.cpp @@ -458,31 +458,31 @@ CrossCompartmentWrapper::fun_toString(JS if (!str) return nullptr; } if (!cx->compartment()->wrap(cx, &str)) return nullptr; return str; } -bool -CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper, - MutableHandleRegExpShared shared) const +RegExpShared* +CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper) const { RootedRegExpShared re(cx); { AutoCompartment call(cx, wrappedObject(wrapper)); - if (!Wrapper::regexp_toShared(cx, wrapper, &re)) - return false; + re = Wrapper::regexp_toShared(cx, wrapper); + if (!re) + return nullptr; } // Get an equivalent RegExpShared associated with the current compartment. RootedAtom source(cx, re->getSource()); cx->markAtom(source); - return cx->zone()->regExps.get(cx, source, re->getFlags(), shared); + return cx->zone()->regExps.get(cx, source, re->getFlags()); } bool CrossCompartmentWrapper::boxedValue_unbox(JSContext* cx, HandleObject wrapper, MutableHandleValue vp) const { PIERCE(cx, wrapper, NOTHING, Wrapper::boxedValue_unbox(cx, wrapper, vp),
--- a/js/src/proxy/DeadObjectProxy.cpp +++ b/js/src/proxy/DeadObjectProxy.cpp @@ -155,22 +155,21 @@ template <DeadProxyIsCallableIsConstruct JSString* DeadObjectProxy<CC>::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const { ReportDead(cx); return nullptr; } template <DeadProxyIsCallableIsConstructorOption CC> -bool -DeadObjectProxy<CC>::regexp_toShared(JSContext* cx, HandleObject proxy, - MutableHandle<RegExpShared*> shared) const +RegExpShared* +DeadObjectProxy<CC>::regexp_toShared(JSContext* cx, HandleObject proxy) const { ReportDead(cx); - return false; + return nullptr; } template <> const char DeadObjectProxy<DeadProxyNotCallableNotConstructor>::family = 0; template <> const char DeadObjectProxy<DeadProxyNotCallableIsConstructor>::family = 0; template <> const char DeadObjectProxy<DeadProxyIsCallableNotConstructor>::family = 0;
--- a/js/src/proxy/DeadObjectProxy.h +++ b/js/src/proxy/DeadObjectProxy.h @@ -55,18 +55,17 @@ class DeadObjectProxy : public BaseProxy virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl, const CallArgs& args) const override; virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const override; virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const override; virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const override; virtual const char* className(JSContext* cx, HandleObject proxy) const override; virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override; - virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, - MutableHandle<RegExpShared*> shared) const override; + virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override; virtual bool isCallable(JSObject* obj) const override { return CC == DeadProxyIsCallableIsConstructor || CC == DeadProxyIsCallableNotConstructor; } virtual bool isConstructor(JSObject* obj) const override { return CC == DeadProxyIsCallableIsConstructor || CC == DeadProxyNotCallableIsConstructor; }
--- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -570,22 +570,22 @@ Proxy::fun_toString(JSContext* cx, Handl AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET, /* mayThrow = */ false); // Do the safe thing if the policy rejects. if (!policy.allowed()) return handler->BaseProxyHandler::fun_toString(cx, proxy, indent); return handler->fun_toString(cx, proxy, indent); } -bool -Proxy::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandleRegExpShared shared) +RegExpShared* +Proxy::regexp_toShared(JSContext* cx, HandleObject proxy) { if (!CheckRecursionLimit(cx)) - return false; - return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy, shared); + return nullptr; + return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy); } bool Proxy::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) { if (!CheckRecursionLimit(cx)) return false; return proxy->as<ProxyObject>().handler()->boxedValue_unbox(cx, proxy, vp);
--- a/js/src/proxy/Proxy.h +++ b/js/src/proxy/Proxy.h @@ -55,18 +55,17 @@ class Proxy AutoIdVector& props); static bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl, const CallArgs& args); static bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp); static bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls); static bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer); static const char* className(JSContext* cx, HandleObject proxy); static JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent); - static bool regexp_toShared(JSContext* cx, HandleObject proxy, - MutableHandle<RegExpShared*> shared); + static RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy); static bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp); static bool watch(JSContext* cx, HandleObject proxy, HandleId id, HandleObject callable); static bool unwatch(JSContext* cx, HandleObject proxy, HandleId id); static bool getElements(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end, ElementAdder* adder);
--- a/js/src/proxy/ScriptedProxyHandler.cpp +++ b/js/src/proxy/ScriptedProxyHandler.cpp @@ -1262,22 +1262,20 @@ ScriptedProxyHandler::className(JSContex JSString* ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, js_Function_str, js_toString_str, "object"); return nullptr; } -bool -ScriptedProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy, - MutableHandleRegExpShared shared) const +RegExpShared* +ScriptedProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy) const { MOZ_CRASH("Should not end up in ScriptedProxyHandler::regexp_toShared"); - return false; } bool ScriptedProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const { MOZ_CRASH("Should not end up in ScriptedProxyHandler::boxedValue_unbox"); return false;
--- a/js/src/proxy/ScriptedProxyHandler.h +++ b/js/src/proxy/ScriptedProxyHandler.h @@ -64,18 +64,17 @@ class ScriptedProxyHandler : public Base virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const override; virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const override; virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const override; virtual const char* className(JSContext* cx, HandleObject proxy) const override; virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override; - virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, - MutableHandle<RegExpShared*> shared) const override; + virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override; virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override; virtual bool isCallable(JSObject* obj) const override; virtual bool isConstructor(JSObject* obj) const override; virtual bool isScripted() const override { return true; }
--- a/js/src/proxy/SecurityWrapper.cpp +++ b/js/src/proxy/SecurityWrapper.cpp @@ -82,21 +82,20 @@ bool SecurityWrapper<Base>::isArray(JSContext* cx, HandleObject obj, JS::IsArrayAnswer* answer) const { // This should ReportAccessDenied(cx), but bug 849730 disagrees. :-( *answer = JS::IsArrayAnswer::NotArray; return true; } template <class Base> -bool -SecurityWrapper<Base>::regexp_toShared(JSContext* cx, HandleObject obj, - MutableHandle<RegExpShared*> shared) const +RegExpShared* +SecurityWrapper<Base>::regexp_toShared(JSContext* cx, HandleObject obj) const { - return Base::regexp_toShared(cx, obj, shared); + return Base::regexp_toShared(cx, obj); } template <class Base> bool SecurityWrapper<Base>::boxedValue_unbox(JSContext* cx, HandleObject obj, MutableHandleValue vp) const { vp.setUndefined(); return true;
--- a/js/src/proxy/Wrapper.cpp +++ b/js/src/proxy/Wrapper.cpp @@ -266,21 +266,21 @@ Wrapper::className(JSContext* cx, Handle JSString* Wrapper::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const { assertEnteredPolicy(cx, proxy, JSID_VOID, GET); RootedObject target(cx, proxy->as<ProxyObject>().target()); return fun_toStringHelper(cx, target, indent); } -bool -Wrapper::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandleRegExpShared shared) const +RegExpShared* +Wrapper::regexp_toShared(JSContext* cx, HandleObject proxy) const { RootedObject target(cx, proxy->as<ProxyObject>().target()); - return RegExpToShared(cx, target, shared); + return RegExpToShared(cx, target); } bool Wrapper::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const { RootedObject target(cx, proxy->as<ProxyObject>().target()); return Unbox(cx, target, vp); }
--- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -2860,18 +2860,16 @@ js::NativeDeleteProperty(JSContext* cx, // Step 4. if (!prop) { // If no property call the class's delProperty hook, passing succeeded // as the result parameter. This always succeeds when there is no hook. return CallJSDeletePropertyOp(cx, obj->getClass()->getDelProperty(), obj, id, result); } - cx->runtime()->gc.poke(); - // Step 6. Non-configurable property. if (GetPropertyAttributes(obj, prop) & JSPROP_PERMANENT) return result.failCantDelete(); if (!CallJSDeletePropertyOp(cx, obj->getClass()->getDelProperty(), obj, id, result)) return false; if (!result) return true;
--- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -111,26 +111,23 @@ VectorMatchPairs::allocOrExpandArray(siz pairs_ = &vec_[0]; pairCount_ = pairCount; return true; } /* RegExpObject */ -/* static */ bool -RegExpObject::getShared(JSContext* cx, Handle<RegExpObject*> regexp, - MutableHandleRegExpShared shared) +/* static */ RegExpShared* +RegExpObject::getShared(JSContext* cx, Handle<RegExpObject*> regexp) { - if (regexp->hasShared()) { - shared.set(regexp->sharedRef()); - return true; - } + if (regexp->hasShared()) + return regexp->sharedRef(); - return createShared(cx, regexp, shared); + return createShared(cx, regexp); } /* static */ bool RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlag* mask) { if (native == regexp_global) { *mask = GlobalFlag; return true; @@ -265,27 +262,27 @@ RegExpObject::create(JSContext* cx, Hand if (!regexp) return nullptr; regexp->initAndZeroLastIndex(source, flags, cx); return regexp; } -/* static */ bool -RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp, - MutableHandleRegExpShared shared) +/* static */ RegExpShared* +RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp) { MOZ_ASSERT(!regexp->hasShared()); RootedAtom source(cx, regexp->getSource()); - if (!cx->zone()->regExps.get(cx, source, regexp->getFlags(), shared)) - return false; + RegExpShared* shared = cx->zone()->regExps.get(cx, source, regexp->getFlags()); + if (!shared) + return nullptr; regexp->setShared(*shared); - return true; + return shared; } Shape* RegExpObject::assignInitialShape(JSContext* cx, Handle<RegExpObject*> self) { MOZ_ASSERT(self->empty()); JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0); @@ -883,18 +880,18 @@ RegExpShared::dumpBytecode(JSContext* cx return true; } /* static */ bool RegExpObject::dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp, bool match_only, HandleLinearString input) { - RootedRegExpShared shared(cx); - if (!getShared(cx, regexp, &shared)) + RootedRegExpShared shared(cx, getShared(cx, regexp)); + if (!shared) return false; return RegExpShared::dumpBytecode(cx, &shared, match_only, input); } #endif template <typename CharT> static MOZ_ALWAYS_INLINE bool @@ -1284,49 +1281,45 @@ RegExpCompartment::sweep(JSRuntime* rt) if (optimizableRegExpInstanceShape_ && IsAboutToBeFinalized(&optimizableRegExpInstanceShape_)) { optimizableRegExpInstanceShape_.set(nullptr); } } -bool -RegExpZone::get(JSContext* cx, HandleAtom source, RegExpFlag flags, - MutableHandleRegExpShared result) +RegExpShared* +RegExpZone::get(JSContext* cx, HandleAtom source, RegExpFlag flags) { DependentAddPtr<Set> p(cx, set_, Key(source, flags)); - if (p) { - result.set(*p); - return true; - } + if (p) + return *p; auto shared = Allocate<RegExpShared>(cx); if (!shared) - return false; + return nullptr; new (shared) RegExpShared(source, flags); if (!p.add(cx, set_, Key(source, flags), shared)) { ReportOutOfMemory(cx); - return false; + return nullptr; } - result.set(shared); - return true; + return shared; } -bool -RegExpZone::get(JSContext* cx, HandleAtom atom, JSString* opt, MutableHandleRegExpShared shared) +RegExpShared* +RegExpZone::get(JSContext* cx, HandleAtom atom, JSString* opt) { RegExpFlag flags = RegExpFlag(0); if (opt && !ParseRegExpFlags(cx, opt, &flags)) - return false; + return nullptr; - return get(cx, atom, flags, shared); + return get(cx, atom, flags); } size_t RegExpZone::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { return set_.sizeOfExcludingThis(mallocSizeOf); } @@ -1348,18 +1341,18 @@ js::CloneRegExpObject(JSContext* cx, JSO return nullptr; clone->initPrivate(nullptr); if (!EmptyShape::ensureInitialCustomShape<RegExpObject>(cx, clone)) return nullptr; Rooted<JSAtom*> source(cx, regex->getSource()); - RootedRegExpShared shared(cx); - if (!RegExpObject::getShared(cx, regex, &shared)) + RegExpShared* shared = RegExpObject::getShared(cx, regex); + if (!shared) return nullptr; clone->initAndZeroLastIndex(source, shared->getFlags(), cx); clone->setShared(*shared); return clone; } @@ -1488,20 +1481,20 @@ js::CloneScriptRegExpObject(JSContext* c RootedAtom source(cx, reobj.getSource()); cx->markAtom(source); return RegExpObject::create(cx, source, reobj.getFlags(), nullptr, nullptr, cx->tempLifoAlloc(), TenuredObject); } -JS_FRIEND_API(bool) -js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj, MutableHandleRegExpShared shared) +JS_FRIEND_API(RegExpShared*) +js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj) { - return RegExpToShared(cx, obj, shared); + return RegExpToShared(cx, obj); } JS::ubi::Node::Size JS::ubi::Concrete<RegExpShared>::size(mozilla::MallocSizeOf mallocSizeOf) const { return js::gc::Arena::thingSize(gc::AllocKind::REGEXP_SHARED) + get().sizeOfExcludingThis(mallocSizeOf); }
--- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -138,18 +138,17 @@ class RegExpObject : public NativeObject bool ignoreCase() const { return getFlags() & IgnoreCaseFlag; } bool global() const { return getFlags() & GlobalFlag; } bool multiline() const { return getFlags() & MultilineFlag; } bool sticky() const { return getFlags() & StickyFlag; } bool unicode() const { return getFlags() & UnicodeFlag; } static bool isOriginalFlagGetter(JSNative native, RegExpFlag* mask); - static MOZ_MUST_USE bool getShared(JSContext* cx, Handle<RegExpObject*> regexp, - MutableHandleRegExpShared shared); + static RegExpShared* getShared(JSContext* cx, Handle<RegExpObject*> regexp); bool hasShared() { return !!sharedRef(); } void setShared(RegExpShared& shared) { MOZ_ASSERT(!hasShared()); sharedRef().init(&shared); @@ -175,40 +174,39 @@ class RegExpObject : public NativeObject bool match_only, HandleLinearString input); #endif private: /* * Precondition: the syntax for |source| has already been validated. * Side effect: sets the private field. */ - static MOZ_MUST_USE bool createShared(JSContext* cx, Handle<RegExpObject*> regexp, - MutableHandleRegExpShared shared); + static RegExpShared* createShared(JSContext* cx, Handle<RegExpObject*> regexp); /* Call setShared in preference to setPrivate. */ void setPrivate(void* priv) = delete; }; /* * Parse regexp flags. Report an error and return false if an invalid * sequence of flags is encountered (repeat/invalid flag). * * N.B. flagStr must be rooted. */ bool ParseRegExpFlags(JSContext* cx, JSString* flagStr, RegExpFlag* flagsOut); /* Assuming GetBuiltinClass(obj) is ESClass::RegExp, return a RegExpShared for obj. */ -inline bool -RegExpToShared(JSContext* cx, HandleObject obj, MutableHandleRegExpShared shared) +inline RegExpShared* +RegExpToShared(JSContext* cx, HandleObject obj) { if (obj->is<RegExpObject>()) - return RegExpObject::getShared(cx, obj.as<RegExpObject>(), shared); + return RegExpObject::getShared(cx, obj.as<RegExpObject>()); - return Proxy::regexp_toShared(cx, obj, shared); + return Proxy::regexp_toShared(cx, obj); } template<XDRMode mode> bool XDRScriptRegExpObject(XDRState<mode>* xdr, MutableHandle<RegExpObject*> objp); extern JSObject* CloneScriptRegExpObject(JSContext* cx, RegExpObject& re);
--- a/js/src/vm/RegExpShared.h +++ b/js/src/vm/RegExpShared.h @@ -268,21 +268,20 @@ class RegExpZone ~RegExpZone() { MOZ_ASSERT_IF(set_.initialized(), set_.empty()); } bool init(); bool empty() const { return set_.empty(); } - bool get(JSContext* cx, HandleAtom source, RegExpFlag flags, MutableHandleRegExpShared shared); + RegExpShared* get(JSContext* cx, HandleAtom source, RegExpFlag flags); /* Like 'get', but compile 'maybeOpt' (if non-null). */ - bool get(JSContext* cx, HandleAtom source, JSString* maybeOpt, - MutableHandleRegExpShared shared); + RegExpShared* get(JSContext* cx, HandleAtom source, JSString* maybeOpt); size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf); }; class RegExpCompartment { /* * This is the template object where the result of re.exec() is based on,
--- a/js/src/vm/RegExpStatics.cpp +++ b/js/src/vm/RegExpStatics.cpp @@ -77,19 +77,19 @@ RegExpStatics::executeLazy(JSContext* cx if (!pendingLazyEvaluation) return true; MOZ_ASSERT(lazySource); MOZ_ASSERT(matchesInput); MOZ_ASSERT(lazyIndex != size_t(-1)); /* Retrieve or create the RegExpShared in this zone. */ - RootedRegExpShared shared(cx); RootedAtom source(cx, lazySource); - if (!cx->zone()->regExps.get(cx, source, lazyFlags, &shared)) + RootedRegExpShared shared(cx, cx->zone()->regExps.get(cx, source, lazyFlags)); + if (!shared) return false; /* * It is not necessary to call aboutToWrite(): evaluation of * implicit copies is safe. */ /* Execute the full regular expression. */
--- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -1484,18 +1484,18 @@ JSStructuredCloneWriter::startWrite(Hand if (backref) return true; ESClass cls; if (!GetBuiltinClass(context(), obj, &cls)) return false; if (cls == ESClass::RegExp) { - RootedRegExpShared re(context()); - if (!RegExpToShared(context(), obj, &re)) + RegExpShared* re = RegExpToShared(context(), obj); + if (!re) return false; return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags()) && writeString(SCTAG_STRING, re->getSource()); } else if (cls == ESClass::Date) { RootedValue unboxed(context()); if (!Unbox(context(), obj, &unboxed)) return false; return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(unboxed.toNumber());
--- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -3020,17 +3020,17 @@ XPCRootSetElem::AddToRootSet(XPCRootSetE mNext->mSelfp = &mNext; } *listHead = this; } void XPCRootSetElem::RemoveFromRootSet() { - JS::PokeGC(XPCJSContext::Get()->Context()); + JS::NotifyGCRootsRemoved(XPCJSContext::Get()->Context()); MOZ_ASSERT(mSelfp, "Must be linked"); MOZ_ASSERT(*mSelfp == this, "Link invariant"); *mSelfp = mNext; if (mNext) mNext->mSelfp = mSelfp; #ifdef DEBUG
--- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -1890,16 +1890,19 @@ nsPresContext::SysColorChangedInternal() mPendingSysColorChanged = false; if (sLookAndFeelChanged) { // Don't use the cached values for the system colors LookAndFeel::Refresh(); sLookAndFeelChanged = false; } + // Invalidate cached '-moz-windows-accent-color-applies' media query: + nsCSSRuleProcessor::FreeSystemMetrics(); + // Reset default background and foreground colors for the document since // they may be using system colors GetDocumentColorPreferences(); // The system color values are computed to colors in the style data, // so normal style data comparison is sufficient here. RebuildAllStyleData(nsChangeHint(0), nsRestyleHint(0)); }
--- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -3363,28 +3363,30 @@ ScrollFrameHelper::BuildDisplayList(nsDi !(mIsRoot && mOuter->PresContext()->PresShell()->GetIsViewportOverridden()); // Whether we might want to build a scrollable layer for this scroll frame // at some point in the future. This controls whether we add the information // to the layer tree (a scroll info layer if necessary, and add the right // area to the dispatch to content layer event regions) necessary to activate // a scroll frame so it creates a scrollable layer. bool couldBuildLayer = false; - if (mWillBuildScrollableLayer) { - couldBuildLayer = true; - } else { - couldBuildLayer = - nsLayoutUtils::AsyncPanZoomEnabled(mOuter) && - WantAsyncScroll() && - // If we are using containers for root frames, and we are the root - // scroll frame for the display root, then we don't need a scroll - // info layer. nsDisplayList::PaintForFrame already calls - // ComputeFrameMetrics for us. - (!(gfxPrefs::LayoutUseContainersForRootFrames() && mIsRoot) || - (aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext())); + if (aBuilder->IsPaintingToWindow()) { + if (mWillBuildScrollableLayer) { + couldBuildLayer = true; + } else { + couldBuildLayer = + nsLayoutUtils::AsyncPanZoomEnabled(mOuter) && + WantAsyncScroll() && + // If we are using containers for root frames, and we are the root + // scroll frame for the display root, then we don't need a scroll + // info layer. nsDisplayList::PaintForFrame already calls + // ComputeFrameMetrics for us. + (!(gfxPrefs::LayoutUseContainersForRootFrames() && mIsRoot) || + (aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext())); + } } // Now display the scrollbars and scrollcorner. These parts are drawn // in the border-background layer, on top of our own background and // borders and underneath borders and backgrounds of later elements // in the tree. // Note that this does not apply for overlay scrollbars; those are drawn // in the positioned-elements layer on top of everything else by the call @@ -3596,22 +3598,21 @@ ScrollFrameHelper::BuildDisplayList(nsDi bool ScrollFrameHelper::DecideScrollableLayer(nsDisplayListBuilder* aBuilder, nsRect* aDirtyRect, bool aAllowCreateDisplayPort) { // Save and check if this changes so we can recompute the current agr. bool oldWillBuildScrollableLayer = mWillBuildScrollableLayer; - bool wasUsingDisplayPort = false; - bool usingDisplayPort = false; nsIContent* content = mOuter->GetContent(); + bool wasUsingDisplayPort = nsLayoutUtils::HasDisplayPort(content); + bool usingDisplayPort = wasUsingDisplayPort; + if (aBuilder->IsPaintingToWindow()) { - wasUsingDisplayPort = nsLayoutUtils::HasDisplayPort(content); - if (aAllowCreateDisplayPort) { nsLayoutUtils::MaybeCreateDisplayPort(*aBuilder, mOuter); nsRect displayportBase = *aDirtyRect; nsPresContext* pc = mOuter->PresContext(); if (mIsRoot && (pc->IsRootContentDocument() || !pc->GetParentPresContext())) { displayportBase = nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(mOuter));
--- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -400,17 +400,19 @@ nsSubDocumentFrame::BuildDisplayList(nsD if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) { nsIScrollableFrame* rootScrollableFrame = presShell->GetRootScrollFrameAsScrollable(); MOZ_ASSERT(rootScrollableFrame); // Use a copy, so the dirty rect doesn't get modified to the display port. nsRect copy = dirty; haveDisplayPort = rootScrollableFrame->DecideScrollableLayer(aBuilder, ©, /* aAllowCreateDisplayPort = */ true); - if (!gfxPrefs::LayoutUseContainersForRootFrames()) { + + if (!gfxPrefs::LayoutUseContainersForRootFrames() || + !aBuilder->IsPaintingToWindow()) { haveDisplayPort = false; } ignoreViewportScrolling = presShell->IgnoringViewportScrolling(); if (ignoreViewportScrolling) { savedIgnoreScrollFrame = aBuilder->GetIgnoreScrollFrame(); aBuilder->SetIgnoreScrollFrame(rootScrollFrame); }
--- a/media/webrtc/trunk/webrtc/base/task_queue_libevent.cc +++ b/media/webrtc/trunk/webrtc/base/task_queue_libevent.cc @@ -229,17 +229,17 @@ void TaskQueue::PostTask(std::unique_ptr void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task, uint32_t milliseconds) { if (IsCurrent()) { TimerEvent* timer = new TimerEvent(std::move(task)); EventAssign(&timer->ev, event_base_, -1, 0, &TaskQueue::RunTimer, timer); QueueContext* ctx = static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls())); ctx->pending_timers_.push_back(timer); - timeval tv = {milliseconds / 1000, + timeval tv = {static_cast<time_t>(milliseconds) / 1000, static_cast<suseconds_t>((milliseconds % 1000) * 1000)}; event_add(&timer->ev, &tv); } else { PostTask(std::unique_ptr<QueuedTask>( new SetTimerTask(std::move(task), milliseconds))); } }
--- a/modules/pdfium/moz.build +++ b/modules/pdfium/moz.build @@ -22,17 +22,17 @@ if CONFIG['OS_TARGET'] == 'WINNT': DEFINES['V8_DEPRECATION_WARNINGS'] = True if CONFIG['OS_ARCH'] == 'Linux': if CONFIG['CPU_ARCH'] == 'x86_64': DEFINES['_FX_CPU_'] = '_FX_X64_' elif CONFIG['CPU_ARCH'] == 'x86': DEFINES['_FX_CPU_'] = '_FX_X86_' # pdfium -SOURCES += [ +UNIFIED_SOURCES += [ 'pdfium/fpdfsdk/cba_annotiterator.cpp', 'pdfium/fpdfsdk/cfx_systemhandler.cpp', 'pdfium/fpdfsdk/cpdfsdk_annot.cpp', 'pdfium/fpdfsdk/cpdfsdk_annothandlermgr.cpp', 'pdfium/fpdfsdk/cpdfsdk_annotiteration.cpp', 'pdfium/fpdfsdk/cpdfsdk_baannot.cpp', 'pdfium/fpdfsdk/cpdfsdk_baannothandler.cpp', 'pdfium/fpdfsdk/cpdfsdk_datetime.cpp', @@ -66,37 +66,43 @@ SOURCES += [ if CONFIG['OS_TARGET'] == 'WINNT': OS_LIBS += [ 'advapi32', 'gdi32', 'user32', ] # fdrm +UNIFIED_SOURCES += [ + 'pdfium/core/fdrm/crypto/fx_crypt_aes.cpp', +] SOURCES += [ + # not able to be unified because of the error: + # - 'GET_UINT32': macro redefinition + # - 'PUT_UINT32': macro redefinition + # - 'P': macro redefinition 'pdfium/core/fdrm/crypto/fx_crypt.cpp', - 'pdfium/core/fdrm/crypto/fx_crypt_aes.cpp', 'pdfium/core/fdrm/crypto/fx_crypt_sha.cpp', ] # formfiller -SOURCES += [ +UNIFIED_SOURCES += [ 'pdfium/fpdfsdk/formfiller/cba_fontmap.cpp', 'pdfium/fpdfsdk/formfiller/cffl_checkbox.cpp', 'pdfium/fpdfsdk/formfiller/cffl_combobox.cpp', 'pdfium/fpdfsdk/formfiller/cffl_formfiller.cpp', 'pdfium/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp', 'pdfium/fpdfsdk/formfiller/cffl_listbox.cpp', 'pdfium/fpdfsdk/formfiller/cffl_pushbutton.cpp', 'pdfium/fpdfsdk/formfiller/cffl_radiobutton.cpp', 'pdfium/fpdfsdk/formfiller/cffl_textfield.cpp', ] # fpdfapi -SOURCES += [ +UNIFIED_SOURCES += [ 'pdfium/core/fpdfapi/cmaps/CNS1/Adobe-CNS1-UCS2_5.cpp', 'pdfium/core/fpdfapi/cmaps/CNS1/B5pc-H_0.cpp', 'pdfium/core/fpdfapi/cmaps/CNS1/B5pc-V_0.cpp', 'pdfium/core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp', 'pdfium/core/fpdfapi/cmaps/CNS1/CNS-EUC-H_0.cpp', 'pdfium/core/fpdfapi/cmaps/CNS1/CNS-EUC-V_0.cpp', 'pdfium/core/fpdfapi/cmaps/CNS1/ETen-B5-H_0.cpp', 'pdfium/core/fpdfapi/cmaps/CNS1/ETen-B5-V_0.cpp', @@ -223,36 +229,40 @@ SOURCES += [ 'pdfium/core/fpdfapi/parser/cpdf_stream.cpp', 'pdfium/core/fpdfapi/parser/cpdf_stream_acc.cpp', 'pdfium/core/fpdfapi/parser/cpdf_string.cpp', 'pdfium/core/fpdfapi/parser/cpdf_syntax_parser.cpp', 'pdfium/core/fpdfapi/parser/fpdf_parser_decode.cpp', 'pdfium/core/fpdfapi/parser/fpdf_parser_utility.cpp', 'pdfium/core/fpdfapi/render/cpdf_charposlist.cpp', 'pdfium/core/fpdfapi/render/cpdf_devicebuffer.cpp', - 'pdfium/core/fpdfapi/render/cpdf_dibsource.cpp', 'pdfium/core/fpdfapi/render/cpdf_dibtransferfunc.cpp', 'pdfium/core/fpdfapi/render/cpdf_docrenderdata.cpp', 'pdfium/core/fpdfapi/render/cpdf_imagecacheentry.cpp', 'pdfium/core/fpdfapi/render/cpdf_imageloader.cpp', 'pdfium/core/fpdfapi/render/cpdf_imagerenderer.cpp', 'pdfium/core/fpdfapi/render/cpdf_pagerendercache.cpp', 'pdfium/core/fpdfapi/render/cpdf_progressiverenderer.cpp', 'pdfium/core/fpdfapi/render/cpdf_rendercontext.cpp', 'pdfium/core/fpdfapi/render/cpdf_renderoptions.cpp', 'pdfium/core/fpdfapi/render/cpdf_renderstatus.cpp', 'pdfium/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp', 'pdfium/core/fpdfapi/render/cpdf_textrenderer.cpp', 'pdfium/core/fpdfapi/render/cpdf_transferfunc.cpp', 'pdfium/core/fpdfapi/render/cpdf_type3cache.cpp', 'pdfium/core/fpdfapi/render/cpdf_type3glyphs.cpp', ] +SOURCES += [ + # not able to be unified because of the error: + # - '`anonymous-namespace'::kMaxImageDimension': redefinition; different storage class + 'pdfium/core/fpdfapi/render/cpdf_dibsource.cpp', +] # fpdfdoc -SOURCES += [ +UNIFIED_SOURCES += [ 'pdfium/core/fpdfdoc/cline.cpp', 'pdfium/core/fpdfdoc/clines.cpp', 'pdfium/core/fpdfdoc/cpdf_aaction.cpp', 'pdfium/core/fpdfdoc/cpdf_action.cpp', 'pdfium/core/fpdfdoc/cpdf_actionfields.cpp', 'pdfium/core/fpdfdoc/cpdf_annot.cpp', 'pdfium/core/fpdfdoc/cpdf_annotlist.cpp', 'pdfium/core/fpdfdoc/cpdf_apsettings.cpp', @@ -260,48 +270,51 @@ SOURCES += [ 'pdfium/core/fpdfdoc/cpdf_bookmarktree.cpp', 'pdfium/core/fpdfdoc/cpdf_defaultappearance.cpp', 'pdfium/core/fpdfdoc/cpdf_dest.cpp', 'pdfium/core/fpdfdoc/cpdf_docjsactions.cpp', 'pdfium/core/fpdfdoc/cpdf_filespec.cpp', 'pdfium/core/fpdfdoc/cpdf_formcontrol.cpp', 'pdfium/core/fpdfdoc/cpdf_formfield.cpp', 'pdfium/core/fpdfdoc/cpdf_iconfit.cpp', - 'pdfium/core/fpdfdoc/cpdf_interform.cpp', 'pdfium/core/fpdfdoc/cpdf_link.cpp', 'pdfium/core/fpdfdoc/cpdf_linklist.cpp', 'pdfium/core/fpdfdoc/cpdf_metadata.cpp', - 'pdfium/core/fpdfdoc/cpdf_nametree.cpp', 'pdfium/core/fpdfdoc/cpdf_numbertree.cpp', 'pdfium/core/fpdfdoc/cpdf_occontext.cpp', 'pdfium/core/fpdfdoc/cpdf_pagelabel.cpp', 'pdfium/core/fpdfdoc/cpdf_variabletext.cpp', 'pdfium/core/fpdfdoc/cpdf_viewerpreferences.cpp', 'pdfium/core/fpdfdoc/cpvt_color.cpp', 'pdfium/core/fpdfdoc/cpvt_fontmap.cpp', 'pdfium/core/fpdfdoc/cpvt_generateap.cpp', 'pdfium/core/fpdfdoc/cpvt_sectioninfo.cpp', 'pdfium/core/fpdfdoc/cpvt_wordinfo.cpp', 'pdfium/core/fpdfdoc/csection.cpp', 'pdfium/core/fpdfdoc/ctypeset.cpp', +] +SOURCES += [ + # not able to be unified because of the error: + # - '`anonymous-namespace'::nMaxRecursion': redefinition; different storage class + 'pdfium/core/fpdfdoc/cpdf_interform.cpp', + 'pdfium/core/fpdfdoc/cpdf_nametree.cpp', 'pdfium/core/fpdfdoc/doc_tagged.cpp', ] # fpdftext -SOURCES += [ +UNIFIED_SOURCES += [ 'pdfium/core/fpdftext/cpdf_linkextract.cpp', 'pdfium/core/fpdftext/cpdf_textpage.cpp', 'pdfium/core/fpdftext/cpdf_textpagefind.cpp', 'pdfium/core/fpdftext/unicodenormalizationdata.cpp', ] # fxcodec -SOURCES += [ +UNIFIED_SOURCES += [ 'pdfium/core/fxcodec/codec/fx_codec.cpp', - 'pdfium/core/fxcodec/codec/fx_codec_fax.cpp', 'pdfium/core/fxcodec/codec/fx_codec_flate.cpp', 'pdfium/core/fxcodec/codec/fx_codec_icc.cpp', 'pdfium/core/fxcodec/codec/fx_codec_jbig.cpp', 'pdfium/core/fxcodec/codec/fx_codec_jpeg.cpp', # 'pdfium/core/fxcodec/codec/fx_codec_jpx_opj.cpp', # SkiaPDF doesn't use JPEG 2000 format 'pdfium/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp', 'pdfium/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp', 'pdfium/core/fxcodec/jbig2/JBig2_BitStream.cpp', @@ -316,19 +329,24 @@ SOURCES += [ 'pdfium/core/fxcodec/jbig2/JBig2_Image.cpp', 'pdfium/core/fxcodec/jbig2/JBig2_PatternDict.cpp', 'pdfium/core/fxcodec/jbig2/JBig2_PddProc.cpp', 'pdfium/core/fxcodec/jbig2/JBig2_SddProc.cpp', 'pdfium/core/fxcodec/jbig2/JBig2_Segment.cpp', 'pdfium/core/fxcodec/jbig2/JBig2_SymbolDict.cpp', 'pdfium/core/fxcodec/jbig2/JBig2_TrdProc.cpp', ] +SOURCES += [ + # not able to be unified because of the error: + # - '`anonymous-namespace'::kMaxImageDimension': redefinition; different storage class + 'pdfium/core/fxcodec/codec/fx_codec_fax.cpp', +] # fxcrt -SOURCES += [ +UNIFIED_SOURCES += [ 'pdfium/core/fxcrt/fx_basic_array.cpp', 'pdfium/core/fxcrt/fx_basic_bstring.cpp', 'pdfium/core/fxcrt/fx_basic_buffer.cpp', 'pdfium/core/fxcrt/fx_basic_coords.cpp', 'pdfium/core/fxcrt/fx_basic_gcc.cpp', 'pdfium/core/fxcrt/fx_basic_memmgr.cpp', 'pdfium/core/fxcrt/fx_basic_utf.cpp', 'pdfium/core/fxcrt/fx_basic_util.cpp', @@ -340,24 +358,24 @@ SOURCES += [ 'pdfium/core/fxcrt/fx_xml_composer.cpp', 'pdfium/core/fxcrt/fx_xml_parser.cpp', 'pdfium/core/fxcrt/fxcrt_posix.cpp', 'pdfium/core/fxcrt/fxcrt_stream.cpp', 'pdfium/core/fxcrt/fxcrt_windows.cpp', ] # fxedit -SOURCES += [ +UNIFIED_SOURCES += [ 'pdfium/fpdfsdk/fxedit/fxet_ap.cpp', 'pdfium/fpdfsdk/fxedit/fxet_edit.cpp', 'pdfium/fpdfsdk/fxedit/fxet_list.cpp', ] # fxge -SOURCES += [ +UNIFIED_SOURCES += [ 'pdfium/core/fxge/agg/fx_agg_driver.cpp', 'pdfium/core/fxge/dib/fx_dib_composite.cpp', 'pdfium/core/fxge/dib/fx_dib_convert.cpp', 'pdfium/core/fxge/dib/fx_dib_engine.cpp', 'pdfium/core/fxge/dib/fx_dib_main.cpp', 'pdfium/core/fxge/dib/fx_dib_transform.cpp', 'pdfium/core/fxge/fontdata/chromefontdata/FoxitDingbats.cpp', 'pdfium/core/fxge/fontdata/chromefontdata/FoxitFixed.cpp', @@ -392,116 +410,139 @@ SOURCES += [ 'pdfium/core/fxge/ge/cfx_unicodeencoding.cpp', 'pdfium/core/fxge/ge/cttfontdesc.cpp', 'pdfium/core/fxge/ge/fx_ge_fontmap.cpp', 'pdfium/core/fxge/ge/fx_ge_linux.cpp', 'pdfium/core/fxge/ge/fx_ge_text.cpp', 'pdfium/core/fxge/ifx_renderdevicedriver.cpp', ] if CONFIG['OS_TARGET'] == 'WINNT': - SOURCES += [ + UNIFIED_SOURCES += [ 'pdfium/core/fxge/win32/cfx_psrenderer.cpp', 'pdfium/core/fxge/win32/cpsoutput.cpp', 'pdfium/core/fxge/win32/fx_win32_device.cpp', 'pdfium/core/fxge/win32/fx_win32_dib.cpp', - 'pdfium/core/fxge/win32/fx_win32_dwrite.cpp', - 'pdfium/core/fxge/win32/fx_win32_gdipext.cpp', 'pdfium/core/fxge/win32/fx_win32_print.cpp', ] + SOURCES += [ + # not able to be unified because of the error: + # - 'boolean': redefinition; different basic types + # - 'BOOLEAN': redefinition; different basic types + 'pdfium/core/fxge/win32/fx_win32_dwrite.cpp', + ] + SOURCES += [ + # avoid other files being polluted by WIN32_LEAN_AND_MEAN macro + 'pdfium/core/fxge/win32/fx_win32_gdipext.cpp', + ] if CONFIG['OS_TARGET'] == 'Darwin': - SOURCES += [ + UNIFIED_SOURCES += [ 'pdfium/core/fxge/apple/fx_apple_platform.cpp', 'pdfium/core/fxge/apple/fx_mac_imp.cpp', 'pdfium/core/fxge/apple/fx_quartz_device.cpp', ] if CONFIG['OS_TARGET'] == 'Android': - SOURCES += [ + UNIFIED_SOURCES += [ 'pdfium/core/fxge/android/cfpf_skiadevicemodule.cpp', 'pdfium/core/fxge/android/cfpf_skiafont.cpp', 'pdfium/core/fxge/android/cfpf_skiafontmgr.cpp', 'pdfium/core/fxge/android/cfx_androidfontinfo.cpp', 'pdfium/core/fxge/android/fx_android_imp.cpp', ] # javascript -SOURCES += [ +UNIFIED_SOURCES += [ 'pdfium/fpdfsdk/javascript/JS_Runtime_Stub.cpp', ] # pdfwindow -SOURCES += [ +UNIFIED_SOURCES += [ 'pdfium/fpdfsdk/pdfwindow/cpwl_color.cpp', 'pdfium/fpdfsdk/pdfwindow/PWL_Button.cpp', 'pdfium/fpdfsdk/pdfwindow/PWL_Caret.cpp', 'pdfium/fpdfsdk/pdfwindow/PWL_ComboBox.cpp', 'pdfium/fpdfsdk/pdfwindow/PWL_Edit.cpp', 'pdfium/fpdfsdk/pdfwindow/PWL_EditCtrl.cpp', 'pdfium/fpdfsdk/pdfwindow/PWL_FontMap.cpp', 'pdfium/fpdfsdk/pdfwindow/PWL_Icon.cpp', 'pdfium/fpdfsdk/pdfwindow/PWL_ListBox.cpp', 'pdfium/fpdfsdk/pdfwindow/PWL_ScrollBar.cpp', 'pdfium/fpdfsdk/pdfwindow/PWL_SpecialButton.cpp', 'pdfium/fpdfsdk/pdfwindow/PWL_Utils.cpp', 'pdfium/fpdfsdk/pdfwindow/PWL_Wnd.cpp', ] # third_party:fx_agg -SOURCES += [ +UNIFIED_SOURCES += [ 'pdfium/third_party/agg23/agg_curves.cpp', 'pdfium/third_party/agg23/agg_path_storage.cpp', 'pdfium/third_party/agg23/agg_rasterizer_scanline_aa.cpp', 'pdfium/third_party/agg23/agg_vcgen_dash.cpp', 'pdfium/third_party/agg23/agg_vcgen_stroke.cpp', ] # third_party:bigint -SOURCES += [ - 'pdfium/third_party/bigint/BigInteger.cc', +UNIFIED_SOURCES += [ 'pdfium/third_party/bigint/BigIntegerUtils.cc', + 'pdfium/third_party/bigint/BigUnsignedInABase.cc', +] +SOURCES += [ + # not able to be unified because of the error: + # - 'DTRT_ALIASED': macro redefinition + 'pdfium/third_party/bigint/BigInteger.cc', 'pdfium/third_party/bigint/BigUnsigned.cc', - 'pdfium/third_party/bigint/BigUnsignedInABase.cc', ] # third_party:fx_freetype LOCAL_INCLUDES += [ '/modules/freetype2/include', '/modules/freetype2/src', ] USE_LIBS += [ 'freetype', ] # third_party:fx_lcms2 -SOURCES += [ +UNIFIED_SOURCES += [ 'pdfium/third_party/lcms2-2.6/src/cmscam02.c', 'pdfium/third_party/lcms2-2.6/src/cmscgats.c', 'pdfium/third_party/lcms2-2.6/src/cmscnvrt.c', 'pdfium/third_party/lcms2-2.6/src/cmserr.c', 'pdfium/third_party/lcms2-2.6/src/cmsgamma.c', 'pdfium/third_party/lcms2-2.6/src/cmsgmt.c', 'pdfium/third_party/lcms2-2.6/src/cmshalf.c', 'pdfium/third_party/lcms2-2.6/src/cmsintrp.c', 'pdfium/third_party/lcms2-2.6/src/cmsio0.c', 'pdfium/third_party/lcms2-2.6/src/cmsio1.c', 'pdfium/third_party/lcms2-2.6/src/cmslut.c', 'pdfium/third_party/lcms2-2.6/src/cmsmd5.c', 'pdfium/third_party/lcms2-2.6/src/cmsmtrx.c', - 'pdfium/third_party/lcms2-2.6/src/cmsnamed.c', 'pdfium/third_party/lcms2-2.6/src/cmsopt.c', 'pdfium/third_party/lcms2-2.6/src/cmspack.c', 'pdfium/third_party/lcms2-2.6/src/cmspcs.c', 'pdfium/third_party/lcms2-2.6/src/cmsplugin.c', - 'pdfium/third_party/lcms2-2.6/src/cmsps2.c', 'pdfium/third_party/lcms2-2.6/src/cmssamp.c', 'pdfium/third_party/lcms2-2.6/src/cmssm.c', - 'pdfium/third_party/lcms2-2.6/src/cmstypes.c', 'pdfium/third_party/lcms2-2.6/src/cmsvirt.c', 'pdfium/third_party/lcms2-2.6/src/cmswtpnt.c', 'pdfium/third_party/lcms2-2.6/src/cmsxform.c', ] +SOURCES += [ + # not able to be unified because of the error: + # - function 'cmsUInt32Number mywcslen(const wchar_t *)' already has a body + 'pdfium/third_party/lcms2-2.6/src/cmsnamed.c', + + # not able to be unified because of the error: + # - 'WriteCLUT': redefinition; different basic types + 'pdfium/third_party/lcms2-2.6/src/cmsps2.c', + + # not able to be unified because of the error: + # - function 'cmsUInt32Number mywcslen(const wchar_t *)' already has a body + # - 'WriteCLUT': redefinition; different basic types + 'pdfium/third_party/lcms2-2.6/src/cmstypes.c', +] # third_party:fx_zlib LOCAL_INCLUDES += [ '/modules/zlib/src', ] USE_LIBS += [ 'zlib', ]
--- a/netwerk/base/NetUtil.jsm +++ b/netwerk/base/NetUtil.jsm @@ -20,16 +20,19 @@ const Cc = Components.classes; const Cr = Components.results; const Cu = Components.utils; const PR_UINT32_MAX = 0xffffffff; Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); +const BinaryInputStream = Components.Constructor("@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", "setInputStream"); + //////////////////////////////////////////////////////////////////////////////// //// NetUtil Object this.NetUtil = { /** * Function to perform simple async copying from aSource (an input stream) * to aSink (an output stream). The copy will happen on some background * thread. Both streams will be closed when the copy completes. @@ -443,16 +446,53 @@ this.NetUtil = { catch (e) { // Adjust the stack so it throws at the caller's location. throw new Components.Exception(e.message, e.result, Components.stack.caller, e.data); } }, /** + * Reads aCount bytes from aInputStream into a string. + * + * @param {nsIInputStream} aInputStream + * The input stream to read from. + * @param {integer} [aCount = aInputStream.available()] + * The number of bytes to read from the stream. + * + * @return the bytes from the input stream in string form. + * + * @throws NS_ERROR_INVALID_ARG if aInputStream is not an nsIInputStream. + * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from aInputStream would + * block the calling thread (non-blocking mode only). + * @throws NS_ERROR_FAILURE if there are not enough bytes available to read + * aCount amount of data. + */ + readInputStream(aInputStream, aCount) + { + if (!(aInputStream instanceof Ci.nsIInputStream)) { + let exception = new Components.Exception( + "First argument should be an nsIInputStream", + Cr.NS_ERROR_INVALID_ARG, + Components.stack.caller + ); + throw exception; + } + + if (!aCount) { + aCount = aInputStream.available(); + } + + let stream = new BinaryInputStream(aInputStream); + let result = new ArrayBuffer(aCount); + stream.readArrayBuffer(result.byteLength, result); + return result; + }, + + /** * Returns a reference to nsIIOService. * * @return a reference to nsIIOService. */ get ioService() { delete this.ioService; return this.ioService = Cc["@mozilla.org/network/io-service;1"].
--- a/netwerk/protocol/data/nsDataHandler.cpp +++ b/netwerk/protocol/data/nsDataHandler.cpp @@ -47,25 +47,19 @@ NS_IMETHODIMP nsDataHandler::GetDefaultPort(int32_t *result) { // no ports for data protocol *result = -1; return NS_OK; } NS_IMETHODIMP nsDataHandler::GetProtocolFlags(uint32_t *result) { - *result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | - URI_NON_PERSISTABLE | URI_IS_LOCAL_RESOURCE | + *result = URI_NORELATIVE | URI_NOAUTH | URI_INHERITS_SECURITY_CONTEXT | + URI_LOADABLE_BY_ANYONE | URI_NON_PERSISTABLE | URI_IS_LOCAL_RESOURCE | URI_SYNC_LOAD_IS_OK; - - // Until Bug 1324406 and all it's dependencies are fixed - // data: URIs inherit the security context. - if (!nsIOService::IsDataURIUniqueOpaqueOrigin()) { - *result |= URI_INHERITS_SECURITY_CONTEXT; - } return NS_OK; } NS_IMETHODIMP nsDataHandler::NewURI(const nsACString &aSpec, const char *aCharset, // ignore charset info nsIURI *aBaseURI, nsIURI **result) {
--- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -480,16 +480,24 @@ nsHttpChannel::Connect() if (!mFallbackChannel && !mFallbackKey.IsEmpty()) { return AsyncCall(&nsHttpChannel::HandleAsyncFallback); } return NS_ERROR_DOCUMENT_NOT_CACHED; } // otherwise, let's just proceed without using the cache. } + // When racing, if OnCacheEntryAvailable is called before AsyncOpenURI + // returns, then we may not have started reading from the cache. + // If the content is valid, we should attempt to do so, as technically the + // cache has won the race. + if (sRCWNEnabled && mCachedContentIsValid && mNetworkTriggered) { + Unused << ReadFromCache(true); + } + return TriggerNetwork(0); } nsresult nsHttpChannel::TryHSTSPriming() { bool isHttpScheme; nsresult rv = mURI->SchemeIs("http", &isHttpScheme);
--- a/netwerk/streamconv/converters/nsFTPDirListingConv.cpp +++ b/netwerk/streamconv/converters/nsFTPDirListingConv.cpp @@ -284,16 +284,23 @@ nsFTPDirListingConv::DigestBufferLines(c aString.Append(' '); } else aString.AppendLiteral("0 "); // MODIFIED DATE char buffer[256] = ""; + + // ParseFTPList can return time structure with invalid values. + // PR_NormalizeTime will set all values into valid limits. + result.fe_time.tm_params.tp_gmt_offset = 0; + result.fe_time.tm_params.tp_dst_offset = 0; + PR_NormalizeTime(&result.fe_time, PR_GMTParameters); + // Note: The below is the RFC822/1123 format, as required by // the application/http-index-format specs // viewers of such a format can then reformat this into the // current locale (or anything else they choose) PR_FormatTimeUSEnglish(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S", &result.fe_time ); nsAutoCString escaped;
--- a/netwerk/test/unit/test_bug365133.js +++ b/netwerk/test/unit/test_bug365133.js @@ -1,66 +1,66 @@ const URL = "ftp://localhost/bug365133/"; const tests = [ [ /* Unix style listing, space at the end of filename */ "drwxrwxr-x 2 500 500 4096 Jan 01 2000 a \r\n" , "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"a%20\" 0 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 DIRECTORY \n" + "201: \"a%20\" 0 Sat%2C%2001%20Jan%202000%2000%3A00%3A00 DIRECTORY \n" ], [ /* Unix style listing, space at the end of link name */ "lrwxrwxrwx 1 500 500 2 Jan 01 2000 l -> a \r\n" , "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"l%20\" 2 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 SYMBOLIC-LINK \n" + "201: \"l%20\" 2 Sat%2C%2001%20Jan%202000%2000%3A00%3A00 SYMBOLIC-LINK \n" ], [ /* Unix style listing, regular file with " -> " in name */ "-rw-rw-r-- 1 500 500 0 Jan 01 2000 arrow -> in name \r\n" , "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"arrow%20-%3E%20in%20name%20\" 0 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n" + "201: \"arrow%20-%3E%20in%20name%20\" 0 Sat%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n" ], [ /* Unix style listing, tab at the end of filename */ "drwxrwxrwx 2 500 500 4096 Jan 01 2000 t \r\n" , "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"t%09\" 0 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 DIRECTORY \n" + "201: \"t%09\" 0 Sat%2C%2001%20Jan%202000%2000%3A00%3A00 DIRECTORY \n" ], [ /* Unix style listing, multiple " -> " in filename */ "lrwxrwxrwx 1 500 500 26 Jan 01 2000 symlink with arrow -> in name -> file with arrow -> in name\r\n" , "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"symlink%20with%20arrow%20-%3E%20in%20name\" 26 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 SYMBOLIC-LINK \n" + "201: \"symlink%20with%20arrow%20-%3E%20in%20name\" 26 Sat%2C%2001%20Jan%202000%2000%3A00%3A00 SYMBOLIC-LINK \n" ], [ /* Unix style listing, multiple " -> " in filename, incorrect filesize */ "lrwxrwxrwx 1 500 500 0 Jan 01 2000 symlink with arrow -> in name -> file with arrow -> in name\r\n" , "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"symlink%20with%20arrow%20-%3E%20in%20name%20-%3E%20file%20with%20arrow\" 0 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 SYMBOLIC-LINK \n" + "201: \"symlink%20with%20arrow%20-%3E%20in%20name%20-%3E%20file%20with%20arrow\" 0 Sat%2C%2001%20Jan%202000%2000%3A00%3A00 SYMBOLIC-LINK \n" ], [ /* DOS style listing, space at the end of filename, year 1999 */ "01-01-99 01:00AM 1024 file \r\n" , "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"file%20\" 1024 Sun%2C%2001%20Jan%201999%2001%3A00%3A00 FILE \n" + "201: \"file%20\" 1024 Fri%2C%2001%20Jan%201999%2001%3A00%3A00 FILE \n" ], [ /* DOS style listing, tab at the end of filename, year 2000 */ "01-01-00 01:00AM 1024 file \r\n" , "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"file%09\" 1024 Sun%2C%2001%20Jan%202000%2001%3A00%3A00 FILE \n" + "201: \"file%09\" 1024 Sat%2C%2001%20Jan%202000%2001%3A00%3A00 FILE \n" ] ] function checkData(request, data, ctx) { do_check_eq(tests[0][1], data); tests.shift(); do_execute_soon(next_test); }
--- a/netwerk/test/unit/test_bug484684.js +++ b/netwerk/test/unit/test_bug484684.js @@ -2,70 +2,70 @@ const URL = "ftp://localhost/bug464884/" const tests = [ // standard ls unix format ["-rw-rw-r-- 1 500 500 0 Jan 01 2000 file1\r\n" + "-rw-rw-r-- 1 500 500 0 Jan 01 2000 file2\r\n", "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"file1\" 0 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n" + - "201: \"%20file2\" 0 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n"], + "201: \"file1\" 0 Sat%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n" + + "201: \"%20file2\" 0 Sat%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n"], // old Hellsoft unix format ["-[RWCEMFA] supervisor 214059 Jan 01 2000 file1\r\n" + "-[RWCEMFA] supervisor 214059 Jan 01 2000 file2\r\n", "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"file1\" 214059 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n" + - "201: \"file2\" 214059 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n"], + "201: \"file1\" 214059 Sat%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n" + + "201: \"file2\" 214059 Sat%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n"], // new Hellsoft unix format ["- [RWCEAFMS] jrd 192 Jan 01 2000 file1\r\n"+ "- [RWCEAFMS] jrd 192 Jan 01 2000 file2\r\n", "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"file1\" 192 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n" + - "201: \"%20file2\" 192 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n"], + "201: \"file1\" 192 Sat%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n" + + "201: \"%20file2\" 192 Sat%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n"], // DOS format with correct offsets ["01-01-00 01:00AM <DIR> dir1\r\n" + "01-01-00 01:00AM <JUNCTION> junction1 -> foo1\r\n" + "01-01-00 01:00AM 95077 file1\r\n" + "01-01-00 01:00AM <DIR> dir2\r\n" + "01-01-00 01:00AM <JUNCTION> junction2 -> foo2\r\n" + "01-01-00 01:00AM 95077 file2\r\n", "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"dir1\" 0 Sun%2C%2001%20Jan%202000%2001%3A00%3A00 DIRECTORY \n" + - "201: \"junction1\" Sun%2C%2001%20Jan%202000%2001%3A00%3A00 SYMBOLIC-LINK \n" + - "201: \"file1\" 95077 Sun%2C%2001%20Jan%202000%2001%3A00%3A00 FILE \n" + - "201: \"%20dir2\" 0 Sun%2C%2001%20Jan%202000%2001%3A00%3A00 DIRECTORY \n" + - "201: \"%20junction2\" Sun%2C%2001%20Jan%202000%2001%3A00%3A00 SYMBOLIC-LINK \n" + - "201: \"%20file2\" 95077 Sun%2C%2001%20Jan%202000%2001%3A00%3A00 FILE \n"], + "201: \"dir1\" 0 Sat%2C%2001%20Jan%202000%2001%3A00%3A00 DIRECTORY \n" + + "201: \"junction1\" Sat%2C%2001%20Jan%202000%2001%3A00%3A00 SYMBOLIC-LINK \n" + + "201: \"file1\" 95077 Sat%2C%2001%20Jan%202000%2001%3A00%3A00 FILE \n" + + "201: \"%20dir2\" 0 Sat%2C%2001%20Jan%202000%2001%3A00%3A00 DIRECTORY \n" + + "201: \"%20junction2\" Sat%2C%2001%20Jan%202000%2001%3A00%3A00 SYMBOLIC-LINK \n" + + "201: \"%20file2\" 95077 Sat%2C%2001%20Jan%202000%2001%3A00%3A00 FILE \n"], // DOS format with wrong offsets ["01-01-00 01:00AM <DIR> dir1\r\n" + "01-01-00 01:00AM <DIR> dir2\r\n" + "01-01-00 01:00AM <DIR> dir3\r\n" + "01-01-00 01:00AM <JUNCTION> junction1 -> foo1\r\n" + "01-01-00 01:00AM <JUNCTION> junction2 -> foo2\r\n" + "01-01-00 01:00AM <JUNCTION> junction3 -> foo3\r\n" + "01-01-00 01:00AM 95077 file1\r\n" + "01-01-00 01:00AM 95077 file2\r\n", "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"dir1\" 0 Sun%2C%2001%20Jan%202000%2001%3A00%3A00 DIRECTORY \n" + - "201: \"dir2\" 0 Sun%2C%2001%20Jan%202000%2001%3A00%3A00 DIRECTORY \n" + - "201: \"dir3\" 0 Sun%2C%2001%20Jan%202000%2001%3A00%3A00 DIRECTORY \n" + - "201: \"junction1\" Sun%2C%2001%20Jan%202000%2001%3A00%3A00 SYMBOLIC-LINK \n" + - "201: \"junction2\" Sun%2C%2001%20Jan%202000%2001%3A00%3A00 SYMBOLIC-LINK \n" + - "201: \"junction3\" Sun%2C%2001%20Jan%202000%2001%3A00%3A00 SYMBOLIC-LINK \n" + - "201: \"file1\" 95077 Sun%2C%2001%20Jan%202000%2001%3A00%3A00 FILE \n" + - "201: \"file2\" 95077 Sun%2C%2001%20Jan%202000%2001%3A00%3A00 FILE \n"] + "201: \"dir1\" 0 Sat%2C%2001%20Jan%202000%2001%3A00%3A00 DIRECTORY \n" + + "201: \"dir2\" 0 Sat%2C%2001%20Jan%202000%2001%3A00%3A00 DIRECTORY \n" + + "201: \"dir3\" 0 Sat%2C%2001%20Jan%202000%2001%3A00%3A00 DIRECTORY \n" + + "201: \"junction1\" Sat%2C%2001%20Jan%202000%2001%3A00%3A00 SYMBOLIC-LINK \n" + + "201: \"junction2\" Sat%2C%2001%20Jan%202000%2001%3A00%3A00 SYMBOLIC-LINK \n" + + "201: \"junction3\" Sat%2C%2001%20Jan%202000%2001%3A00%3A00 SYMBOLIC-LINK \n" + + "201: \"file1\" 95077 Sat%2C%2001%20Jan%202000%2001%3A00%3A00 FILE \n" + + "201: \"file2\" 95077 Sat%2C%2001%20Jan%202000%2001%3A00%3A00 FILE \n"] ] function checkData(request, data, ctx) { do_check_eq(tests[0][1], data); tests.shift(); do_execute_soon(next_test); }
--- a/netwerk/test/unit/test_bug515583.js +++ b/netwerk/test/unit/test_bug515583.js @@ -7,18 +7,18 @@ const tests = [ "[RWCEM1]B; 4 4-MAR-1993 18:09:01.12\r\n" + "[RWCEM1];1 4 5-MAR-1993 18:09:01.12\r\n" + "[RWCEM1]; 4 6-MAR-1993 18:09:01.12\r\n" + "[RWCEM1]C;1D 4 7-MAR-1993 18:09:01.12\r\n" + "[RWCEM1]E;1 4 8-MAR-1993 18:09:01.12\r\n" , "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"A\" 2048 Sun%2C%2003%20Mar%201993%2018%3A09%3A01 FILE \n" + - "201: \"E\" 2048 Sun%2C%2008%20Mar%201993%2018%3A09%3A01 FILE \n"] + "201: \"A\" 2048 Wed%2C%2003%20Mar%201993%2018%3A09%3A01 FILE \n" + + "201: \"E\" 2048 Mon%2C%2008%20Mar%201993%2018%3A09%3A01 FILE \n"] , ["\r\r\r\n" , "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n"] ] function checkData(request, data, ctx) {
--- a/netwerk/test/unit/test_bug543805.js +++ b/netwerk/test/unit/test_bug543805.js @@ -1,47 +1,49 @@ const URL = "ftp://localhost/bug543805/"; +var dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; var year = new Date().getFullYear().toString(); +var day = dayNames[new Date(year, 0, 1).getDay()]; const tests = [ // AIX ls format ["-rw-r--r-- 1 0 11 Jan 1 20:19 nodup.file\r\n" + "-rw-r--r-- 1 0 22 Jan 1 20:19 test.blankfile\r\n" + "-rw-r--r-- 1 0 33 Apr 1 2008 test2.blankfile\r\n" + "-rw-r--r-- 1 0 44 Jan 1 20:19 nodup.file\r\n" + "-rw-r--r-- 1 0 55 Jan 1 20:19 test.file\r\n" + "-rw-r--r-- 1 0 66 Apr 1 2008 test2.file\r\n", "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"%20nodup.file\" 11 Sun%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + - "201: \"%20test.blankfile\" 22 Sun%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + - "201: \"%20test2.blankfile\" 33 Sun%2C%2001%20Apr%202008%2000%3A00%3A00 FILE \n" + - "201: \"nodup.file\" 44 Sun%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + - "201: \"test.file\" 55 Sun%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + - "201: \"test2.file\" 66 Sun%2C%2001%20Apr%202008%2000%3A00%3A00 FILE \n"], + "201: \"%20nodup.file\" 11 " + day + "%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + + "201: \"%20test.blankfile\" 22 " + day + "%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + + "201: \"%20test2.blankfile\" 33 Tue%2C%2001%20Apr%202008%2000%3A00%3A00 FILE \n" + + "201: \"nodup.file\" 44 " + day + "%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + + "201: \"test.file\" 55 " + day + "%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + + "201: \"test2.file\" 66 Tue%2C%2001%20Apr%202008%2000%3A00%3A00 FILE \n"], // standard ls format [ "-rw-r--r-- 1 500 500 11 Jan 1 20:19 nodup.file\r\n" + "-rw-r--r-- 1 500 500 22 Jan 1 20:19 test.blankfile\r\n" + "-rw-r--r-- 1 500 500 33 Apr 1 2008 test2.blankfile\r\n" + "-rw-r--r-- 1 500 500 44 Jan 1 20:19 nodup.file\r\n" + "-rw-r--r-- 1 500 500 55 Jan 1 20:19 test.file\r\n" + "-rw-r--r-- 1 500 500 66 Apr 1 2008 test2.file\r\n", "300: " + URL + "\n" + "200: filename content-length last-modified file-type\n" + - "201: \"%20nodup.file\" 11 Sun%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + - "201: \"%20test.blankfile\" 22 Sun%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + - "201: \"%20test2.blankfile\" 33 Sun%2C%2001%20Apr%202008%2000%3A00%3A00 FILE \n" + - "201: \"nodup.file\" 44 Sun%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + - "201: \"test.file\" 55 Sun%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + - "201: \"test2.file\" 66 Sun%2C%2001%20Apr%202008%2000%3A00%3A00 FILE \n"] + "201: \"%20nodup.file\" 11 " + day + "%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + + "201: \"%20test.blankfile\" 22 " + day + "%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + + "201: \"%20test2.blankfile\" 33 Tue%2C%2001%20Apr%202008%2000%3A00%3A00 FILE \n" + + "201: \"nodup.file\" 44 " + day + "%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + + "201: \"test.file\" 55 " + day + "%2C%2001%20Jan%20" + year + "%2020%3A19%3A00 FILE \n" + + "201: \"test2.file\" 66 Tue%2C%2001%20Apr%202008%2000%3A00%3A00 FILE \n"] ] function checkData(request, data, ctx) { do_check_eq(tests[0][1], data); tests.shift(); do_execute_soon(next_test); }
--- a/python/mozbuild/mozbuild/codecoverage/lcov_rewriter.py +++ b/python/mozbuild/mozbuild/codecoverage/lcov_rewriter.py @@ -502,16 +502,17 @@ class UrlFinder(object): for leaf in reversed(mozpath.split(objdir_path)): offset += len(leaf) if objdir_path[:-offset] in self._install_mapping: pattern_prefix, is_pp = self._install_mapping[objdir_path[:-offset]] full_leaf = objdir_path[len(objdir_path) - offset:] src_prefix = ''.join(_prefix(pattern_prefix)) self._install_mapping[objdir_path] = (mozpath.join(src_prefix, full_leaf), is_pp) + break offset += 1 def _install_info(self, objdir_path): if objdir_path not in self._install_mapping: # If our path is missing, some prefix of it may be in the install # mapping mapped to a wildcard. self._find_install_prefix(objdir_path) if objdir_path not in self._install_mapping:
--- a/security/sandbox/chromium/sandbox/win/src/restricted_token.cc +++ b/security/sandbox/chromium/sandbox/win/src/restricted_token.cc @@ -38,19 +38,16 @@ std::unique_ptr<BYTE[]> GetTokenInfo(con *error = ERROR_SUCCESS; return buffer; } } // namespace namespace sandbox { -// We want to use restricting SIDs in the tokens by default. -bool gUseRestricting = true; - RestrictedToken::RestrictedToken() : integrity_level_(INTEGRITY_LEVEL_LAST), init_(false), lockdown_default_dacl_(false) {} RestrictedToken::~RestrictedToken() { } @@ -81,17 +78,17 @@ DWORD RestrictedToken::Init(const HANDLE DWORD RestrictedToken::GetRestrictedToken( base::win::ScopedHandle* token) const { DCHECK(init_); if (!init_) return ERROR_NO_TOKEN; size_t deny_size = sids_for_deny_only_.size(); - size_t restrict_size = gUseRestricting ? sids_to_restrict_.size() : 0; + size_t restrict_size = sids_to_restrict_.size(); size_t privileges_size = privileges_to_disable_.size(); SID_AND_ATTRIBUTES *deny_only_array = NULL; if (deny_size) { deny_only_array = new SID_AND_ATTRIBUTES[deny_size]; for (unsigned int i = 0; i < sids_for_deny_only_.size() ; ++i) { deny_only_array[i].Attributes = SE_GROUP_USE_FOR_DENY_ONLY; @@ -224,41 +221,33 @@ DWORD RestrictedToken::GetRestrictedToke return ERROR_SUCCESS; } DWORD RestrictedToken::AddAllSidsForDenyOnly(std::vector<Sid> *exceptions) { DCHECK(init_); if (!init_) return ERROR_NO_TOKEN; - // If this is normally a token with restricting SIDs, but we're not allowing - // them, then use the sids_to_restrict_ as an exceptions list to give a - // similar effect. - std::vector<Sid>* localExpections = - gUseRestricting || !sids_to_restrict_.size() ? exceptions : &sids_to_restrict_; - DWORD error; std::unique_ptr<BYTE[]> buffer = GetTokenInfo(effective_token_, TokenGroups, &error); if (!buffer) return error; TOKEN_GROUPS* token_groups = reinterpret_cast<TOKEN_GROUPS*>(buffer.get()); - // Build the list of the deny only group SIDs. We want to be able to have - // logon SID as deny only, if we're not allowing restricting SIDs. + // Build the list of the deny only group SIDs for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) { if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0 && - (!gUseRestricting || - (token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == 0)) { + (token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == 0) { bool should_ignore = false; - if (localExpections) { - for (unsigned int j = 0; j < localExpections->size(); ++j) { - if (::EqualSid(const_cast<SID*>((*localExpections)[j].GetPSID()), + if (exceptions) { + for (unsigned int j = 0; j < exceptions->size(); ++j) { + if (::EqualSid(const_cast<SID*>((*exceptions)[j].GetPSID()), token_groups->Groups[i].Sid)) { should_ignore = true; break; } } } if (!should_ignore) { sids_for_deny_only_.push_back(
--- a/security/sandbox/chromium/sandbox/win/src/restricted_token.h +++ b/security/sandbox/chromium/sandbox/win/src/restricted_token.h @@ -20,19 +20,16 @@ #define SE_GROUP_INTEGRITY (0x00000020L) #endif #ifndef SE_GROUP_INTEGRITY_ENABLED #define SE_GROUP_INTEGRITY_ENABLED (0x00000040L) #endif namespace sandbox { -// Whether we are allowing restricting SIDs in the access tokens or not. -extern bool gUseRestricting; - // Handles the creation of a restricted token using the effective token or // any token handle. // Sample usage: // RestrictedToken restricted_token; // DWORD err_code = restricted_token.Init(NULL); // Use the current // // effective token // if (ERROR_SUCCESS != err_code) { // // handle error.
--- a/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc +++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc @@ -17,16 +17,17 @@ #include "sandbox/win/src/sid.h" namespace sandbox { DWORD CreateRestrictedToken(TokenLevel security_level, IntegrityLevel integrity_level, TokenType token_type, bool lockdown_default_dacl, + bool use_restricting_sids, base::win::ScopedHandle* token) { RestrictedToken restricted_token; restricted_token.Init(NULL); // Initialized with the current process token if (lockdown_default_dacl) restricted_token.SetLockdownDefaultDacl(); std::vector<base::string16> privilege_exceptions; std::vector<Sid> sid_exceptions; @@ -40,19 +41,22 @@ DWORD CreateRestrictedToken(TokenLevel s deny_sids = false; remove_privileges = false; break; } case USER_RESTRICTED_SAME_ACCESS: { deny_sids = false; remove_privileges = false; - unsigned err_code = restricted_token.AddRestrictingSidAllSids(); - if (ERROR_SUCCESS != err_code) - return err_code; + if (use_restricting_sids) { + unsigned err_code = restricted_token.AddRestrictingSidAllSids(); + if (ERROR_SUCCESS != err_code) { + return err_code; + } + } break; } case USER_NON_ADMIN: { deny_sids = false; deny_only_sids.push_back(WinBuiltinAdministratorsSid); deny_only_sids.push_back(WinAccountAdministratorSid); deny_only_sids.push_back(WinAccountDomainAdminsSid); @@ -66,54 +70,57 @@ DWORD CreateRestrictedToken(TokenLevel s break; } case USER_INTERACTIVE: { sid_exceptions.push_back(WinBuiltinUsersSid); sid_exceptions.push_back(WinWorldSid); sid_exceptions.push_back(WinInteractiveSid); sid_exceptions.push_back(WinAuthenticatedUserSid); privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); - restricted_token.AddRestrictingSid(WinBuiltinUsersSid); - restricted_token.AddRestrictingSid(WinWorldSid); - restricted_token.AddRestrictingSid(WinRestrictedCodeSid); - restricted_token.AddRestrictingSidCurrentUser(); - restricted_token.AddRestrictingSidLogonSession(); + if (use_restricting_sids) { + restricted_token.AddRestrictingSid(WinBuiltinUsersSid); + restricted_token.AddRestrictingSid(WinWorldSid); + restricted_token.AddRestrictingSid(WinRestrictedCodeSid); + restricted_token.AddRestrictingSidCurrentUser(); + restricted_token.AddRestrictingSidLogonSession(); + } break; } case USER_LIMITED: { sid_exceptions.push_back(WinBuiltinUsersSid); sid_exceptions.push_back(WinWorldSid); sid_exceptions.push_back(WinInteractiveSid); privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); - // This breaks web audio, so we don't want to do this in the restricting - // SIDs (normal) case. See bug 1378061. - if (!gUseRestricting) { - restricted_token.AddUserSidForDenyOnly(); + if (use_restricting_sids) { + restricted_token.AddRestrictingSid(WinBuiltinUsersSid); + restricted_token.AddRestrictingSid(WinWorldSid); + restricted_token.AddRestrictingSid(WinRestrictedCodeSid); + + // This token has to be able to create objects in BNO. + // Unfortunately, on Vista+, it needs the current logon sid + // in the token to achieve this. You should also set the process to be + // low integrity level so it can't access object created by other + // processes. + restricted_token.AddRestrictingSidLogonSession(); } - restricted_token.AddRestrictingSid(WinBuiltinUsersSid); - restricted_token.AddRestrictingSid(WinWorldSid); - restricted_token.AddRestrictingSid(WinRestrictedCodeSid); - - // This token has to be able to create objects in BNO. - // Unfortunately, on Vista+, it needs the current logon sid - // in the token to achieve this. You should also set the process to be - // low integrity level so it can't access object created by other - // processes. - restricted_token.AddRestrictingSidLogonSession(); break; } case USER_RESTRICTED: { privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); restricted_token.AddUserSidForDenyOnly(); - restricted_token.AddRestrictingSid(WinRestrictedCodeSid); + if (use_restricting_sids) { + restricted_token.AddRestrictingSid(WinRestrictedCodeSid); + } break; } case USER_LOCKDOWN: { restricted_token.AddUserSidForDenyOnly(); - restricted_token.AddRestrictingSid(WinNullSid); + if (use_restricting_sids) { + restricted_token.AddRestrictingSid(WinNullSid); + } break; } default: { return ERROR_BAD_ARGUMENTS; } } DWORD err_code = ERROR_SUCCESS;
--- a/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h +++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h @@ -35,16 +35,17 @@ enum TokenType { // running under the token. // If the function succeeds, the return value is ERROR_SUCCESS. If the // function fails, the return value is the win32 error code corresponding to // the error. DWORD CreateRestrictedToken(TokenLevel security_level, IntegrityLevel integrity_level, TokenType token_type, bool lockdown_default_dacl, + bool use_restricting_sids, base::win::ScopedHandle* token); // Sets the integrity label on a object handle. DWORD SetObjectIntegrityLabel(HANDLE handle, SE_OBJECT_TYPE type, const wchar_t* ace_access, const wchar_t* integrity_level_sid); // Sets the integrity level on a token. This is only valid on Vista. It returns
--- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h @@ -95,16 +95,21 @@ class TargetPolicy { virtual ResultCode SetTokenLevel(TokenLevel initial, TokenLevel lockdown) = 0; // Returns the initial token level. virtual TokenLevel GetInitialTokenLevel() const = 0; // Returns the lockdown token level. virtual TokenLevel GetLockdownTokenLevel() const = 0; + // Sets that we should not use restricting SIDs in the access tokens. We need + // to do this in some circumstances even though it weakens the sandbox. + // The default is to use them. + virtual void SetDoNotUseRestrictingSIDs() = 0; + // Sets the security level of the Job Object to which the target process will // belong. This setting is permanent and cannot be changed once the target // process is spawned. The job controls the global security settings which // can not be specified in the token security profile. // job_level: the security level for the job. See the explanation of each // level in the JobLevel definition. // ui_exceptions: specify what specific rights that are disabled in the // chosen job_level that need to be granted. Use this parameter to avoid
--- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc @@ -178,16 +178,20 @@ ResultCode PolicyBase::SetTokenLevel(Tok TokenLevel PolicyBase::GetInitialTokenLevel() const { return initial_level_; } TokenLevel PolicyBase::GetLockdownTokenLevel() const { return lockdown_level_; } +void PolicyBase::SetDoNotUseRestrictingSIDs() { + use_restricting_sids_ = false; +} + ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32_t ui_exceptions) { if (memory_limit_ && job_level == JOB_NONE) { return SBOX_ERROR_BAD_PARAMS; } job_level_ = job_level; ui_exceptions_ = ui_exceptions; return SBOX_ALL_OK; } @@ -432,17 +436,18 @@ ResultCode PolicyBase::MakeJobObject(bas ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial, base::win::ScopedHandle* lockdown, base::win::ScopedHandle* lowbox) { // Create the 'naked' token. This will be the permanent token associated // with the process and therefore with any thread that is not impersonating. DWORD result = CreateRestrictedToken(lockdown_level_, integrity_level_, PRIMARY, - lockdown_default_dacl_, lockdown); + lockdown_default_dacl_, use_restricting_sids_, + lockdown); if (ERROR_SUCCESS != result) return SBOX_ERROR_GENERIC; // If we're launching on the alternate desktop we need to make sure the // integrity label on the object is no higher than the sandboxed process's // integrity level. So, we lower the label on the desktop process if it's // not already low enough for our process. if (alternate_desktop_handle_ && use_alternate_desktop_ && @@ -488,17 +493,18 @@ ResultCode PolicyBase::MakeTokens(base:: lowbox->Set(token_lowbox); } // Create the 'better' token. We use this token as the one that the main // thread uses when booting up the process. It should contain most of // what we need (before reaching main( )) result = CreateRestrictedToken(initial_level_, integrity_level_, IMPERSONATION, - lockdown_default_dacl_, initial); + lockdown_default_dacl_, use_restricting_sids_, + initial); if (ERROR_SUCCESS != result) return SBOX_ERROR_GENERIC; return SBOX_ALL_OK; } PSID PolicyBase::GetLowBoxSid() const { return lowbox_sid_;
--- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h @@ -37,16 +37,17 @@ class PolicyBase final : public TargetPo PolicyBase(); // TargetPolicy: void AddRef() override; void Release() override; ResultCode SetTokenLevel(TokenLevel initial, TokenLevel lockdown) override; TokenLevel GetInitialTokenLevel() const override; TokenLevel GetLockdownTokenLevel() const override; + void SetDoNotUseRestrictingSIDs() final; ResultCode SetJobLevel(JobLevel job_level, uint32_t ui_exceptions) override; JobLevel GetJobLevel() const override; ResultCode SetJobMemoryLimit(size_t memory_limit) override; ResultCode SetAlternateDesktop(bool alternate_winstation) override; base::string16 GetAlternateDesktop() const override; ResultCode CreateAlternateDesktop(bool alternate_winstation) override; void DestroyAlternateDesktop() override; ResultCode SetIntegrityLevel(IntegrityLevel integrity_level) override; @@ -122,16 +123,17 @@ class PolicyBase final : public TargetPo // The policy takes ownership of them. typedef std::list<TargetProcess*> TargetSet; TargetSet targets_; // Standard object-lifetime reference counter. volatile LONG ref_count; // The user-defined global policy settings. TokenLevel lockdown_level_; TokenLevel initial_level_; + bool use_restricting_sids_ = true; JobLevel job_level_; uint32_t ui_exceptions_; size_t memory_limit_; bool use_alternate_desktop_; bool use_alternate_winstation_; // Helps the file system policy initialization. bool file_system_init_; bool relaxed_interceptions_;
--- a/security/sandbox/win/SandboxInitialization.cpp +++ b/security/sandbox/win/SandboxInitialization.cpp @@ -1,17 +1,16 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=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 "SandboxInitialization.h" -#include "sandbox/win/src/restricted_token.h" #include "sandbox/win/src/sandbox_factory.h" #include "mozilla/sandboxing/permissionsService.h" namespace mozilla { namespace sandboxing { static sandbox::TargetServices* InitializeTargetServices() @@ -74,31 +73,15 @@ sandbox::BrokerServices* GetInitializedBrokerServices() { static sandbox::BrokerServices* sInitializedBrokerServices = InitializeBrokerServices(); return sInitializedBrokerServices; } -void -NetworkDriveCheck() -{ - wchar_t exePath[MAX_PATH]; - if (!::GetModuleFileNameW(nullptr, exePath, MAX_PATH)) { - return; - } - - wchar_t volPath[MAX_PATH]; - if (!::GetVolumePathNameW(exePath, volPath, MAX_PATH)) { - return; - } - - sandbox::gUseRestricting = (::GetDriveTypeW(volPath) != DRIVE_REMOTE); -} - PermissionsService* GetPermissionsService() { return PermissionsService::GetInstance(); } } // sandboxing } // mozilla
--- a/security/sandbox/win/SandboxInitialization.h +++ b/security/sandbox/win/SandboxInitialization.h @@ -38,22 +38,14 @@ void LowerSandbox(); /** * Initializes (if required) and returns the Chromium sandbox BrokerServices. * * @return the BrokerServices or null if the creation or initialization failed. */ sandbox::BrokerServices* GetInitializedBrokerServices(); -/** - * Checks to see if we are running from a network drive and sets a flag in - * sandbox code to disable the use of restricting SIDs. - * Using restricting SIDs blocks access to network drives and prevents DLL - * loading during initial sandboxed child process start-up. - */ -void NetworkDriveCheck(); - PermissionsService* GetPermissionsService(); } // sandboxing } // mozilla #endif // mozilla_sandboxing_SandboxInitialization_h
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp +++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp @@ -1,38 +1,47 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=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 "sandboxBroker.h" +#include <string> + #include "base/win/windows_version.h" #include "mozilla/Assertions.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/Logging.h" #include "mozilla/NSPRLogModulesParser.h" #include "mozilla/UniquePtr.h" #include "mozilla/Telemetry.h" #include "nsAppDirectoryServiceDefs.h" #include "nsCOMPtr.h" #include "nsDirectoryServiceDefs.h" #include "nsIFile.h" #include "nsIProperties.h" #include "nsServiceManagerUtils.h" #include "nsString.h" #include "sandbox/win/src/sandbox.h" #include "sandbox/win/src/security_level.h" +#include "WinUtils.h" namespace mozilla { sandbox::BrokerServices *SandboxBroker::sBrokerService = nullptr; +// This is set to true in Initialize when our exe file name has a drive type of +// DRIVE_REMOTE, so that we can tailor the sandbox policy as some settings break +// fundamental things when running from a network drive. We default to false in +// case those checks fail as that gives us the strongest policy. +bool SandboxBroker::sRunningFromNetworkDrive = false; + // Cached special directories used for adding policy rules. static UniquePtr<nsString> sBinDir; static UniquePtr<nsString> sProfileDir; static UniquePtr<nsString> sContentTempDir; static UniquePtr<nsString> sRoamingAppDataDir; static UniquePtr<nsString> sLocalAppDataDir; static LazyLogModule sSandboxBrokerLog("SandboxBroker"); @@ -40,16 +49,33 @@ static LazyLogModule sSandboxBrokerLog(" #define LOG_E(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Error, (__VA_ARGS__)) #define LOG_W(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Warning, (__VA_ARGS__)) /* static */ void SandboxBroker::Initialize(sandbox::BrokerServices* aBrokerServices) { sBrokerService = aBrokerServices; + + wchar_t exePath[MAX_PATH]; + if (!::GetModuleFileNameW(nullptr, exePath, MAX_PATH)) { + return; + } + + std::wstring exeString(exePath); + if (!widget::WinUtils::ResolveJunctionPointsAndSymLinks(exeString)) { + return; + } + + wchar_t volPath[MAX_PATH]; + if (!::GetVolumePathNameW(exeString.c_str(), volPath, MAX_PATH)) { + return; + } + + sRunningFromNetworkDrive = (::GetDriveTypeW(volPath) == DRIVE_REMOTE); } static void CacheDirAndAutoClear(nsIProperties* aDirSvc, const char* aDirKey, UniquePtr<nsString>* cacheVar) { nsCOMPtr<nsIFile> dirToCache; nsresult rv = @@ -92,16 +118,19 @@ SandboxBroker::CacheRulesDirectories() CacheDirAndAutoClear(dirSvc, NS_WIN_APPDATA_DIR, &sRoamingAppDataDir); CacheDirAndAutoClear(dirSvc, NS_WIN_LOCAL_APPDATA_DIR, &sLocalAppDataDir); } SandboxBroker::SandboxBroker() { if (sBrokerService) { mPolicy = sBrokerService->CreatePolicy(); + if (sRunningFromNetworkDrive) { + mPolicy->SetDoNotUseRestrictingSIDs(); + } } else { mPolicy = nullptr; } } bool SandboxBroker::LaunchApp(const wchar_t *aPath, const wchar_t *aArguments,
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.h +++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.h @@ -60,14 +60,15 @@ public: // processes can be added as handle duplication targets. bool AddTargetPeer(HANDLE aPeerProcess); // Set up dummy interceptions via the broker, so we can log calls. void ApplyLoggingPolicy(); private: static sandbox::BrokerServices *sBrokerService; + static bool sRunningFromNetworkDrive; sandbox::TargetPolicy *mPolicy; }; } // mozilla #endif
--- a/taskcluster/taskgraph/transforms/tests.py +++ b/taskcluster/taskgraph/transforms/tests.py @@ -418,18 +418,18 @@ def set_target(config, tests): @transforms.add def set_treeherder_machine_platform(config, tests): """Set the appropriate task.extra.treeherder.machine.platform""" translation = { # Linux64 build platforms for asan and pgo are specified differently to # treeherder. 'linux64-asan/opt': 'linux64/asan', 'linux64-pgo/opt': 'linux64/pgo', - 'macosx64/debug': 'osx-cross/debug', - 'macosx64/opt': 'osx-cross/opt', + 'macosx64/debug': 'osx-10-10/debug', + 'macosx64/opt': 'osx-10-10/opt', 'win64-asan/opt': 'windows10-64/asan', # The build names for Android platforms have partially evolved over the # years and need to be translated. 'android-api-15/debug': 'android-4-3-armv7-api15/debug', 'android-api-15/opt': 'android-4-3-armv7-api15/opt', 'android-x86/opt': 'android-4-2-x86/opt', 'android-api-15-gradle/opt': 'android-api-15-gradle/opt', }
--- a/testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini +++ b/testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini @@ -4,17 +4,17 @@ skip-if = manage_instance == false || ap [test_geckoinstance.py] [test_data_driven.py] [test_session.py] [test_capabilities.py] [test_accessibility.py] [test_expectedfail.py] expected = fail [test_click.py] -skip-if = debug && e10s # Bug 1360446 +skip-if = e10s # Bug 1360446 [test_click_chrome.py] skip-if = appname == 'fennec' [test_checkbox.py] [test_checkbox_chrome.py] skip-if = appname == 'fennec' [test_elementsize.py] [test_elementsize_chrome.py] skip-if = appname == 'fennec'
--- a/testing/talos/talos/tests/perf-reftest-singletons/bloom_basic_singleton.manifest +++ b/testing/talos/talos/tests/perf-reftest-singletons/bloom_basic_singleton.manifest @@ -1,1 +1,1 @@ -% http://localhost/tests/perf-reftest/bloom-basic.html +% http://localhost/tests/perf-reftest-singletons/bloom-basic.html
--- a/toolkit/components/downloads/ApplicationReputation.cpp +++ b/toolkit/components/downloads/ApplicationReputation.cpp @@ -46,16 +46,17 @@ #include "nsNetCID.h" #include "nsReadableUtils.h" #include "nsServiceManagerUtils.h" #include "nsString.h" #include "nsTArray.h" #include "nsThreadUtils.h" #include "nsIContentPolicy.h" +#include "nsICryptoHash.h" #include "nsILoadInfo.h" #include "nsContentUtils.h" #include "nsWeakReference.h" #include "nsIRedirectHistoryEntry.h" using mozilla::ArrayLength; using mozilla::BasePrincipal; using mozilla::OriginAttributes; @@ -180,18 +181,21 @@ private: // Wrapper function for nsIStreamListener.onStopRequest to make it easy to // guarantee calling the callback nsresult OnStopRequestInternal(nsIRequest *aRequest, nsISupports *aContext, nsresult aResult, bool* aShouldBlock, uint32_t* aVerdict); + // Return the hex-encoded hash of the whole URI. + nsresult GetSpecHash(nsACString& aSpec, nsACString& hexEncodedHash); + // Strip url parameters, fragments, and user@pass fields from the URI spec - // using nsIURL. If aURI is not an nsIURL, returns the original nsIURI.spec. + // using nsIURL. Hash data URIs and return blob URIs unfiltered. nsresult GetStrippedSpec(nsIURI* aUri, nsACString& spec); // Escape '/' and '%' in certificate attribute values. nsCString EscapeCertificateAttribute(const nsACString& aAttribute); // Escape ':' in fingerprint values. nsCString EscapeFingerprint(const nsACString& aAttribute); @@ -290,18 +294,21 @@ nsresult PendingDBLookup::LookupSpec(const nsACString& aSpec, bool aAllowlistOnly) { LOG(("Checking principal %s [this=%p]", aSpec.Data(), this)); mSpec = aSpec; mAllowlistOnly = aAllowlistOnly; nsresult rv = LookupSpecInternal(aSpec); if (NS_FAILED(rv)) { - LOG(("Error in LookupSpecInternal")); - return mPendingLookup->OnComplete(false, NS_OK); + nsAutoCString errorName; + mozilla::GetErrorName(rv, errorName); + LOG(("Error in LookupSpecInternal() [rv = %s, this = %p]", + errorName.get(), this)); + return mPendingLookup->LookupNext(); // ignore this lookup and move to next } // LookupSpecInternal has called nsIUrlClassifierCallback.lookup, which is // guaranteed to call HandleEvent. return rv; } nsresult PendingDBLookup::LookupSpecInternal(const nsACString& aSpec) @@ -978,40 +985,108 @@ PendingLookup::StartLookup() nsresult rv = DoLookupInternal(); if (NS_FAILED(rv)) { return OnComplete(false, NS_OK); } return rv; } nsresult +PendingLookup::GetSpecHash(nsACString& aSpec, nsACString& hexEncodedHash) +{ + nsresult rv; + + nsCOMPtr<nsICryptoHash> cryptoHash = + do_CreateInstance("@mozilla.org/security/hash;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + rv = cryptoHash->Init(nsICryptoHash::SHA256); + NS_ENSURE_SUCCESS(rv, rv); + + rv = cryptoHash->Update(reinterpret_cast<const uint8_t*>(aSpec.BeginReading()), + aSpec.Length()); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString binaryHash; + rv = cryptoHash->Finish(false, binaryHash); + NS_ENSURE_SUCCESS(rv, rv); + + // This needs to match HexEncode() in Chrome's + // src/base/strings/string_number_conversions.cc + static const char* const hex = "0123456789ABCDEF"; + hexEncodedHash.SetCapacity(2 * binaryHash.Length()); + for (size_t i = 0; i < binaryHash.Length(); ++i) { + auto c = static_cast<const unsigned char>(binaryHash[i]); + hexEncodedHash.Append(hex[(c >> 4) & 0x0F]); + hexEncodedHash.Append(hex[c & 0x0F]); + } + + return NS_OK; +} + +nsresult PendingLookup::GetStrippedSpec(nsIURI* aUri, nsACString& escaped) { + if (NS_WARN_IF(!aUri)) { + return NS_ERROR_INVALID_ARG; + } + + nsresult rv; + rv = aUri->GetScheme(escaped); + NS_ENSURE_SUCCESS(rv, rv); + + if (escaped.EqualsLiteral("blob")) { + aUri->GetSpec(escaped); + LOG(("PendingLookup::GetStrippedSpec(): blob URL left unstripped as '%s' [this = %p]", + PromiseFlatCString(escaped).get(), this)); + return NS_OK; + + } else if (escaped.EqualsLiteral("data")) { + // Replace URI with "data:<everything before comma>,SHA256(<whole URI>)" + aUri->GetSpec(escaped); + int32_t comma = escaped.FindChar(','); + if (comma > -1 && + static_cast<nsCString::size_type>(comma) < escaped.Length() - 1) { + MOZ_ASSERT(comma > 4, "Data URIs start with 'data:'"); + nsAutoCString hexEncodedHash; + rv = GetSpecHash(escaped, hexEncodedHash); + if (NS_SUCCEEDED(rv)) { + escaped.Truncate(comma + 1); + escaped.Append(hexEncodedHash); + } + } + + LOG(("PendingLookup::GetStrippedSpec(): data URL stripped to '%s' [this = %p]", + PromiseFlatCString(escaped).get(), this)); + return NS_OK; + } + // If aURI is not an nsIURL, we do not want to check the lists or send a // remote query. - nsresult rv; nsCOMPtr<nsIURL> url = do_QueryInterface(aUri, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = url->GetScheme(escaped); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_FAILED(rv)) { + LOG(("PendingLookup::GetStrippedSpec(): scheme '%s' is not supported [this = %p]", + PromiseFlatCString(escaped).get(), this)); + return rv; + } nsCString temp; rv = url->GetHostPort(temp); NS_ENSURE_SUCCESS(rv, rv); escaped.Append("://"); escaped.Append(temp); rv = url->GetFilePath(temp); NS_ENSURE_SUCCESS(rv, rv); // nsIUrl.filePath starts with '/' escaped.Append(temp); + LOG(("PendingLookup::GetStrippedSpec(): URL stripped to '%s' [this = %p]", + PromiseFlatCString(escaped).get(), this)); return NS_OK; } nsresult PendingLookup::DoLookupInternal() { // We want to check the target URI, its referrer, and associated redirects // against the local lists.
--- a/toolkit/components/extensions/Extension.jsm +++ b/toolkit/components/extensions/Extension.jsm @@ -378,16 +378,17 @@ this.ExtensionData = class { try { await iter.forEach(entry => { results.push(entry); }); } catch (e) { // Always return a list, even if the directory does not exist (or is // not a directory) for symmetry with the ZipReader behavior. + Cu.reportError(e); } iter.close(); return results; } // FIXME: We need a way to do this without main thread IO. @@ -635,42 +636,55 @@ this.ExtensionData = class { let messages = await this.readJSON(file); return this.localeData.addLocale(locale, messages, this); } catch (e) { this.packagingError(`Loading locale file ${file}: ${e}`); return new Map(); } } + async _promiseLocaleMap() { + let locales = new Map(); + + let entries = await this.readDirectory("_locales"); + for (let file of entries) { + if (file.isDir) { + let locale = this.normalizeLocaleCode(file.name); + locales.set(locale, file.name); + } + } + + return locales; + } + + _setupLocaleData(locales) { + if (this.localeData) { + return this.localeData.locales; + } + + this.localeData = new LocaleData({ + defaultLocale: this.defaultLocale, + locales, + builtinMessages: this.builtinMessages, + }); + + return locales; + } + // Reads the list of locales available in the extension, and returns a // Promise which resolves to a Map upon completion. // Each map key is a Gecko-compatible locale code, and each value is the // "_locales" subdirectory containing that locale: // // Map(gecko-locale-code -> locale-directory-name) promiseLocales() { if (!this._promiseLocales) { this._promiseLocales = (async () => { - let locales = new Map(); - - let entries = await this.readDirectory("_locales"); - for (let file of entries) { - if (file.isDir) { - let locale = this.normalizeLocaleCode(file.name); - locales.set(locale, file.name); - } - } - - this.localeData = new LocaleData({ - defaultLocale: this.defaultLocale, - locales, - builtinMessages: this.builtinMessages, - }); - - return locales; + let locales = this._promiseLocaleMap(); + return this._setupLocaleData(locales); })(); } return this._promiseLocales; } // Reads the locale messages for all locales, and returns a promise which // resolves to a Map of locale messages upon completion. Each key in the map @@ -880,16 +894,23 @@ this.Extension = class extends Extension // Checks that the given URL is a child of our baseURI. isExtensionURL(url) { let uri = Services.io.newURI(url); let common = this.baseURI.getCommonBaseSpec(uri); return common == this.baseURI.spec; } + async promiseLocales(locale) { + let locales = await StartupCache.locales + .get([this.id, "@@all_locales"], () => this._promiseLocaleMap()); + + return this._setupLocaleData(locales); + } + readLocaleFile(locale) { return StartupCache.locales.get([this.id, this.version, locale], () => super.readLocaleFile(locale)) .then(result => { this.localeData.messages.set(locale, result); }); } @@ -1048,16 +1069,20 @@ this.Extension = class extends Extension Services.perms.removeFromPrincipal(principal, "WebExtensions-unlimitedStorage"); Services.perms.removeFromPrincipal(principal, "indexedDB"); Services.perms.removeFromPrincipal(principal, "persistent-storage"); } } startup() { this.startupPromise = this._startup(); + + this._startupComplete = this.startupPromise.catch(() => {}); + OS.File.shutdown.addBlocker("Extension startup", this._startupComplete); + return this.startupPromise; } async _startup() { if (shutdownPromises.has(this.id)) { await shutdownPromises.get(this.id); } @@ -1075,17 +1100,20 @@ this.Extension = class extends Extension // so during upgrades and add-on restarts, startup() gets called // before the last shutdown has completed, and this fails when // there's another active add-on with the same ID. this.policy.active = true; } TelemetryStopwatch.start("WEBEXT_EXTENSION_STARTUP_MS", this); try { - let [, perms] = await Promise.all([this.loadManifest(), ExtensionPermissions.get(this)]); + let [perms] = await Promise.all([ + ExtensionPermissions.get(this), + this.loadManifest(), + ]); if (!this.hasShutdown) { await this.initLocale(); } if (this.errors.length) { return Promise.reject({errors: this.errors}); } @@ -1127,17 +1155,17 @@ this.Extension = class extends Extension Management.emit("startup", this); await this.runManifest(this.manifest); Management.emit("ready", this); this.emit("ready"); TelemetryStopwatch.finish("WEBEXT_EXTENSION_STARTUP_MS", this); } catch (e) { - dump(`Extension error: ${e.message} ${e.filename || e.fileName}:${e.lineNumber} :: ${e.stack || new Error().stack}\n`); + dump(`Extension error: ${e.message || e} ${e.filename || e.fileName}:${e.lineNumber} :: ${e.stack || new Error().stack}\n`); Cu.reportError(e); if (this.policy) { this.policy.active = false; } this.cleanupGeneratedFile();
--- a/toolkit/components/extensions/ExtensionParent.jsm +++ b/toolkit/components/extensions/ExtensionParent.jsm @@ -17,34 +17,41 @@ this.EXPORTED_SYMBOLS = ["ExtensionParen Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "DeferredSave", + "resource://gre/modules/DeferredSave.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils", "resource:///modules/E10SUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", + "resource://gre/modules/FileUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "IndexedDB", "resource://gre/modules/IndexedDB.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel", "resource://gre/modules/MessageChannel.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NativeApp", "resource://gre/modules/NativeMessaging.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Schemas", "resource://gre/modules/Schemas.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "gAddonPolicyService", "@mozilla.org/addons/policy-service;1", "nsIAddonPolicyService"); +XPCOMUtils.defineLazyServiceGetter(this, "aomStartup", + "@mozilla.org/addons/addon-manager-startup;1", + "amIAddonManagerStartup"); Cu.import("resource://gre/modules/ExtensionCommon.jsm"); Cu.import("resource://gre/modules/ExtensionUtils.jsm"); var { BaseContext, CanOfAPIs, SchemaAPIManager, @@ -53,16 +60,17 @@ var { var { DefaultWeakMap, ExtensionError, MessageManagerProxy, defineLazyGetter, promiseDocumentLoaded, promiseEvent, + promiseFileContents, promiseObserved, } = ExtensionUtils; const BASE_SCHEMA = "chrome://extensions/content/schemas/manifest.json"; const CATEGORY_EXTENSION_SCHEMAS = "webextension-schemas"; const CATEGORY_EXTENSION_SCRIPTS = "webextension-scripts"; const XUL_URL = "data:application/vnd.mozilla.xul+xml;charset=utf-8," + encodeURI( @@ -1333,133 +1341,141 @@ let IconDetails = { escapeUrl(url) { return url.replace(/[\\\s"]/g, encodeURIComponent); }, }; let StartupCache = { DB_NAME: "ExtensionStartupCache", - SCHEMA_VERSION: 4, + STORE_NAMES: Object.freeze(["locales", "manifests", "permissions", "schemas"]), + + get file() { + return FileUtils.getFile("ProfLD", ["startupCache", "webext.sc.lz4"]); + }, - STORE_NAMES: Object.freeze(["locales", "manifests", "schemas"]), + get saver() { + if (!this._saver) { + this._saver = new DeferredSave(this.file.path, + () => this.getBlob(), + {delay: 5000}); + } + return this._saver; + }, - dbPromise: null, + async save() { + return this.saver.saveChanges(); + }, + + getBlob() { + return new Uint8Array(aomStartup.encodeBlob(this._data)); + }, - initDB(db) { - for (let name of StartupCache.STORE_NAMES) { - try { - db.deleteObjectStore(name); - } catch (e) { - // Don't worry if the store doesn't already exist. + _data: null, + async _readData() { + let result = new Map(); + try { + let data = await promiseFileContents(this.file); + + result = aomStartup.decodeBlob(data); + } catch (e) { + if (!e.becauseNoSuchFile) { + Cu.reportError(e); } - db.createObjectStore(name, {keyPath: "key"}); } + + this._data = result; + return result; + }, + + get dataPromise() { + if (!this._dataPromise) { + this._dataPromise = this._readData(); + } + return this._dataPromise; }, clearAddonData(id) { - let range = IDBKeyRange.bound([id], [id, "\uFFFF"]); - return Promise.all([ - this.locales.delete(range), - this.manifests.delete(range), + this.locales.delete(id), + this.manifests.delete(id), + this.permissions.delete(id), ]).catch(e => { // Ignore the error. It happens when we try to flush the add-on // data after the AddonManager has flushed the entire startup cache. - this.dbPromise = this.reallyOpen(true).catch(e => {}); }); }, - async reallyOpen(invalidate = false) { - if (this.dbPromise) { - let db = await this.dbPromise; - db.close(); - } - - if (invalidate) { - IndexedDB.deleteDatabase(this.DB_NAME, {storage: "persistent"}); - } - - return IndexedDB.open(this.DB_NAME, - {storage: "persistent", version: this.SCHEMA_VERSION}, - db => this.initDB(db)); - }, - - async open() { - if (!this.dbPromise) { - this.dbPromise = this.reallyOpen(); - } - - return this.dbPromise; - }, - observe(subject, topic, data) { if (topic === "startupcache-invalidate") { - this.dbPromise = this.reallyOpen(true).catch(e => {}); + this._data = new Map(); + this._dataPromise = Promise.resolve(this._data); } }, }; +// void StartupCache.dataPromise; + Services.obs.addObserver(StartupCache, "startupcache-invalidate"); class CacheStore { constructor(storeName) { this.storeName = storeName; } - async get(key, createFunc) { - let db; - let result; - try { - db = await StartupCache.open(); + async getStore(path = null) { + let data = await StartupCache.dataPromise; + + let store = data.get(this.storeName); + if (!store) { + store = new Map(); + data.set(this.storeName, store); + } - result = await db.objectStore(this.storeName) - .get(key); - } catch (e) { - Cu.reportError(e); + let key = path; + if (Array.isArray(path)) { + for (let elem of path.slice(0, -1)) { + let next = store.get(elem); + if (!next) { + next = new Map(); + store.set(elem, next); + } + store = next; + } + key = path[path.length - 1]; + } - return createFunc(key); - } + return [store, key]; + } + + async get(path, createFunc) { + let [store, key] = await this.getStore(path); + + let result = store.get(key); if (result === undefined) { - let value = await createFunc(key); - result = {key, value}; - - try { - db.objectStore(this.storeName, "readwrite") - .put(result); - } catch (e) { - Cu.reportError(e); - } - } - - return result && result.value; - } - - async getAll() { - let result = new Map(); - try { - let db = await StartupCache.open(); - - let results = await db.objectStore(this.storeName) - .getAll(); - for (let {key, value} of results) { - result.set(key, value); - } - } catch (e) { - Cu.reportError(e); + result = await createFunc(path); + store.set(key, result); + StartupCache.save(); } return result; } - async delete(key) { - let db = await StartupCache.open(); + async getAll() { + let [store] = await this.getStore(); + + return new Map(store); + } - return db.objectStore(this.storeName, "readwrite").delete(key); + async delete(path) { + let [store, key] = await this.getStore(path); + + store.delete(key); + StartupCache.save(); } } for (let name of StartupCache.STORE_NAMES) { StartupCache[name] = new CacheStore(name); } var ExtensionParent = {
--- a/toolkit/components/extensions/ExtensionPermissions.jsm +++ b/toolkit/components/extensions/ExtensionPermissions.jsm @@ -1,58 +1,94 @@ "use strict"; const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent", + "resource://gre/modules/ExtensionParent.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "ExtensionUtils", + "resource://gre/modules/ExtensionUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", + "resource://gre/modules/FileUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "JSONFile", "resource://gre/modules/JSONFile.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); +XPCOMUtils.defineLazyGetter(this, "StartupCache", () => ExtensionParent.StartupCache); + this.EXPORTED_SYMBOLS = ["ExtensionPermissions"]; const FILE_NAME = "extension-preferences.json"; let prefs; let _initPromise; + +async function _lazyInit() { + let file = FileUtils.getFile("ProfD", [FILE_NAME]); + + prefs = new JSONFile({path: file.path}); + prefs.data = {}; + + try { + let blob = await ExtensionUtils.promiseFileContents(file); + prefs.data = JSON.parse(new TextDecoder().decode(blob)); + } catch (e) { + if (!e.becauseNoSuchFile) { + Cu.reportError(e); + } + } +} + function lazyInit() { if (!_initPromise) { - prefs = new JSONFile({path: OS.Path.join(OS.Constants.Path.profileDir, FILE_NAME)}); - - _initPromise = prefs.load(); + _initPromise = _lazyInit(); } return _initPromise; } function emptyPermissions() { return {permissions: [], origins: []}; } this.ExtensionPermissions = { - async get(extension) { + async _saveSoon(extension) { + await lazyInit(); + + prefs.data[extension.id] = await this._getCached(extension); + return prefs.saveSoon(); + }, + + async _get(extension) { await lazyInit(); - let perms = emptyPermissions(); - if (prefs.data[extension.id]) { - Object.assign(perms, prefs.data[extension.id]); + let perms = prefs.data[extension.id]; + if (!perms) { + perms = emptyPermissions(); + prefs.data[extension.id] = perms; } + return perms; }, + async _getCached(extension) { + return StartupCache.permissions.get(extension.id, + () => this._get(extension)); + }, + + get(extension) { + return this._getCached(extension); + }, + // Add new permissions for the given extension. `permissions` is // in the format that is passed to browser.permissions.request(). async add(extension, perms) { - await lazyInit(); - - if (!prefs.data[extension.id]) { - prefs.data[extension.id] = emptyPermissions(); - } - let {permissions, origins} = prefs.data[extension.id]; + let {permissions, origins} = await this._getCached(extension); let added = emptyPermissions(); for (let perm of perms.permissions) { if (!permissions.includes(perm)) { added.permissions.push(perm); permissions.push(perm); } @@ -62,30 +98,25 @@ this.ExtensionPermissions = { origin = new MatchPattern(origin, {ignorePath: true}).pattern; if (!origins.includes(origin)) { added.origins.push(origin); origins.push(origin); } } if (added.permissions.length > 0 || added.origins.length > 0) { - prefs.saveSoon(); + this._saveSoon(extension); extension.emit("add-permissions", added); } }, // Revoke permissions from the given extension. `permissions` is // in the format that is passed to browser.permissions.remove(). async remove(extension, perms) { - await lazyInit(); - - if (!prefs.data[extension.id]) { - return; - } - let {permissions, origins} = prefs.data[extension.id]; + let {permissions, origins} = await this._getCached(extension); let removed = emptyPermissions(); for (let perm of perms.permissions) { let i = permissions.indexOf(perm); if (i >= 0) { removed.permissions.push(perm); permissions.splice(i, 1); @@ -98,25 +129,28 @@ this.ExtensionPermissions = { let i = origins.indexOf(origin); if (i >= 0) { removed.origins.push(origin); origins.splice(i, 1); } } if (removed.permissions.length > 0 || removed.origins.length > 0) { - prefs.saveSoon(); + this._saveSoon(extension); extension.emit("remove-permissions", removed); } }, async removeAll(extension) { - await lazyInit(); - delete prefs.data[extension.id]; - prefs.saveSoon(); + let perms = await this._getCached(extension); + + if (perms.permissions.length || perms.origins.length) { + Object.assign(perms, emptyPermissions()); + prefs.saveSoon(); + } }, // This is meant for tests only async _uninit() { if (!_initPromise) { return; }
--- a/toolkit/components/extensions/ExtensionUtils.jsm +++ b/toolkit/components/extensions/ExtensionUtils.jsm @@ -13,33 +13,42 @@ const Cr = Components.results; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ConsoleAPI", "resource://gre/modules/Console.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel", "resource://gre/modules/MessageChannel.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "OS", + "resource://gre/modules/osfile.jsm"); function getConsole() { return new ConsoleAPI({ maxLogLevelPref: "extensions.webextensions.log.level", prefix: "WebExtensions", }); } XPCOMUtils.defineLazyGetter(this, "console", getConsole); +const appinfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime); + let nextId = 0; -XPCOMUtils.defineLazyGetter(this, "uniqueProcessID", () => Services.appinfo.uniqueProcessID); +XPCOMUtils.defineLazyGetter(this, "uniqueProcessID", () => appinfo.uniqueProcessID); function getUniqueId() { return `${nextId++}-${uniqueProcessID}`; } +async function promiseFileContents(file) { + let res = await OS.File.read(file.path); + return res.buffer; +} + /** * An Error subclass for which complete error messages are always passed * to extensions, rather than being interpreted as an unknown error. */ class ExtensionError extends Error {} function filterStack(error) { @@ -628,16 +637,17 @@ this.ExtensionUtils = { getUniqueId, filterStack, getWinUtils, instanceOf, normalizeTime, promiseDocumentLoaded, promiseDocumentReady, promiseEvent, + promiseFileContents, promiseObserved, runSafe, runSafeSync, runSafeSyncWithoutClone, runSafeWithoutClone, withHandlingUserInput, DefaultMap, DefaultWeakMap,
--- a/toolkit/mozapps/extensions/AddonManagerStartup.cpp +++ b/toolkit/mozapps/extensions/AddonManagerStartup.cpp @@ -2,29 +2,33 @@ /* 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 "AddonManagerStartup.h" #include "AddonManagerStartup-inlines.h" #include "jsapi.h" +#include "jsfriendapi.h" #include "js/TracingAPI.h" #include "xpcpublic.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/EndianUtils.h" #include "mozilla/Compression.h" #include "mozilla/Preferences.h" #include "mozilla/ScopeExit.h" #include "mozilla/Services.h" #include "mozilla/Unused.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/ipc/StructuredCloneData.h" #include "nsAppDirectoryServiceDefs.h" #include "nsAppRunner.h" +#include "nsContentUtils.h" #include "nsIAddonInterposition.h" #include "nsXULAppAPI.h" #include <stdlib.h> namespace mozilla { template <> @@ -57,16 +61,17 @@ WrapNSResult(nsresult aRv) } return Ok(); } #define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr)) using Compression::LZ4; +using dom::ipc::StructuredCloneData; #ifdef XP_WIN # define READ_BINARYMODE "rb" #else # define READ_BINARYMODE "r" #endif AddonManagerStartup& @@ -146,54 +151,98 @@ ReadFile(const char* path) size_t rd = fread(result.BeginWriting(), sizeof(char), len, fd); if (rd != len) { result.Truncate(); } return result; } +static const char STRUCTURED_CLONE_MAGIC[] = "mozJSSCLz40v001"; + +template <typename T> +static Result<nsCString, nsresult> +DecodeLZ4(const nsACString& lz4, const T& magicNumber) +{ + constexpr auto HEADER_SIZE = sizeof(magicNumber) + 4; + + // Note: We want to include the null terminator here. + nsDependentCSubstring magic(magicNumber, sizeof(magicNumber)); + + if (lz4.Length() < HEADER_SIZE || StringHead(lz4, magic.Length()) != magic) { + return Err(NS_ERROR_UNEXPECTED); + } + + auto data = lz4.BeginReading() + magic.Length(); + auto size = LittleEndian::readUint32(data); + data += 4; + + nsCString result; + if (!result.SetLength(size, fallible) || + !LZ4::decompress(data, result.BeginWriting(), size)) { + return Err(NS_ERROR_UNEXPECTED); + } + + return result; +} + +// Our zlib headers redefine this to MOZ_Z_compress, which breaks LZ4::compress +#undef compress + +template <typename T> +static Result<nsCString, nsresult> +EncodeLZ4(const nsACString& data, const T& magicNumber) +{ + // Note: We want to include the null terminator here. + nsDependentCSubstring magic(magicNumber, sizeof(magicNumber)); + + nsAutoCString result; + result.Append(magic); + + auto off = result.Length(); + result.SetLength(off + 4); + + LittleEndian::writeUint32(result.BeginWriting() + off, data.Length()); + off += 4; + + auto size = LZ4::maxCompressedSize(data.Length()); + result.SetLength(off + size); + + size = LZ4::compress(data.BeginReading(), data.Length(), + result.BeginWriting() + off); + + result.SetLength(off + size); + return result; +} + +static_assert(sizeof STRUCTURED_CLONE_MAGIC % 8 == 0, + "Magic number should be an array of uint64_t"); + /** * Reads the contents of a LZ4-compressed file, as stored by the OS.File * module, and returns the decompressed contents on success. * * A nonexistent or empty file is treated as success. A corrupt or non-LZ4 * file is treated as failure. */ static Result<nsCString, nsresult> ReadFileLZ4(const char* path) { static const char MAGIC_NUMBER[] = "mozLz40"; - constexpr auto HEADER_SIZE = sizeof(MAGIC_NUMBER) + 4; nsCString result; nsCString lz4 = ReadFile(path); if (lz4.IsEmpty()) { return result; } - // Note: We want to include the null terminator here. - nsDependentCSubstring magic(MAGIC_NUMBER, sizeof(MAGIC_NUMBER)); - - if (lz4.Length() < HEADER_SIZE || StringHead(lz4, magic.Length()) != magic) { - return Err(NS_ERROR_UNEXPECTED); - } - - auto size = LittleEndian::readUint32(lz4.get() + magic.Length()); - - if (!result.SetLength(size, fallible) || - !LZ4::decompress(lz4.get() + HEADER_SIZE, result.BeginWriting(), size)) { - return Err(NS_ERROR_UNEXPECTED); - } - - return result; + return DecodeLZ4(lz4, MAGIC_NUMBER); } - static bool ParseJSON(JSContext* cx, nsACString& jsonData, JS::MutableHandleValue result) { NS_ConvertUTF8toUTF16 str(jsonData); jsonData.Truncate(); return JS_ParseJSON(cx, str.Data(), str.Length(), result); } @@ -583,16 +632,78 @@ AddonManagerStartup::InitializeExtension } } } return NS_OK; } nsresult +AddonManagerStartup::EncodeBlob(JS::HandleValue value, JSContext* cx, JS::MutableHandleValue result) +{ + StructuredCloneData holder; + + ErrorResult rv; + holder.Write(cx, value, rv); + if (rv.Failed()) { + return rv.StealNSResult(); + } + + nsAutoCString scData; + + auto& data = holder.Data(); + auto iter = data.Iter(); + while (!iter.Done()) { + scData.Append(nsDependentCSubstring(iter.Data(), iter.RemainingInSegment())); + iter.Advance(data, iter.RemainingInSegment()); + } + + nsCString lz4; + MOZ_TRY_VAR(lz4, EncodeLZ4(scData, STRUCTURED_CLONE_MAGIC)); + + JS::RootedObject obj(cx); + NS_TRY(nsContentUtils::CreateArrayBuffer(cx, lz4, &obj.get())); + + result.set(JS::ObjectValue(*obj)); + return NS_OK; +} + +nsresult +AddonManagerStartup::DecodeBlob(JS::HandleValue value, JSContext* cx, JS::MutableHandleValue result) +{ + NS_ENSURE_TRUE(value.isObject() && + JS_IsArrayBufferObject(&value.toObject()) && + JS_ArrayBufferHasData(&value.toObject()), + NS_ERROR_INVALID_ARG); + + StructuredCloneData holder; + + nsCString data; + { + JS::AutoCheckCannotGC nogc; + + auto obj = &value.toObject(); + bool isShared; + + nsDependentCSubstring lz4( + reinterpret_cast<char*>(JS_GetArrayBufferData(obj, &isShared, nogc)), + JS_GetArrayBufferByteLength(obj)); + + MOZ_TRY_VAR(data, DecodeLZ4(lz4, STRUCTURED_CLONE_MAGIC)); + } + + bool ok = holder.CopyExternalData(data.get(), data.Length()); + NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); + + ErrorResult rv; + holder.Read(cx, result, rv); + return rv.StealNSResult();; +} + +nsresult AddonManagerStartup::Reset() { MOZ_RELEASE_ASSERT(xpc::IsInAutomation()); mInitialized = false; mExtensionPaths.Clear(); mThemePaths.Clear();
--- a/toolkit/mozapps/extensions/DeferredSave.jsm +++ b/toolkit/mozapps/extensions/DeferredSave.jsm @@ -38,16 +38,21 @@ parentLogger.addAppender(new Log.DumpApp // messages at runtime. // If the "extensions.logging.enabled" preference is // missing or 'false', messages at the WARNING and higher // severity should be logged to the JS console and standard error. // If "extensions.logging.enabled" is set to 'true', messages // at DEBUG and higher should go to JS console and standard error. Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown", + "resource://gre/modules/AsyncShutdown.jsm"); + const PREF_LOGGING_ENABLED = "extensions.logging.enabled"; const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed"; /** * Preference listener which listens for a change in the * "extensions.logging.enabled" preference and changes the logging level of the * parent 'addons' level logger accordingly. */ @@ -78,31 +83,40 @@ PrefObserver.init(); /** * A module to manage deferred, asynchronous writing of data files * to disk. Writing is deferred by waiting for a specified delay after * a request to save the data, before beginning to write. If more than * one save request is received during the delay, all requests are * fulfilled by a single write. * * @constructor - * @param aPath + * @param {string} aPath * String representing the full path of the file where the data * is to be written. - * @param aDataProvider + * @param {function} aDataProvider * Callback function that takes no argument and returns the data to * be written. If aDataProvider returns an ArrayBufferView, the * bytes it contains are written to the file as is. * If aDataProvider returns a String the data are UTF-8 encoded * and then written to the file. - * @param [optional] aDelay + * @param {object | integer} [aOptions] * The delay in milliseconds between the first saveChanges() call * that marks the data as needing to be saved, and when the DeferredSave * begins writing the data to disk. Default 50 milliseconds. + * + * Or, an options object containing: + * - delay: A delay in milliseconds. + * - finalizeAt: An AsyncShutdown blocker during which to + * finalize any pending writes. */ -this.DeferredSave = function(aPath, aDataProvider, aDelay) { +this.DeferredSave = function(aPath, aDataProvider, aOptions = {}) { + if (typeof aOptions == "number") { + aOptions = {delay: aOptions}; + } + // Create a new logger (child of 'DeferredSave' logger) // for use by this particular instance of DeferredSave object let leafName = OS.Path.basename(aPath); let logger_id = DEFERREDSAVE_PARENT_LOGGER_ID + "." + leafName; this.logger = Log.repository.getLogger(logger_id); // @type {Deferred|null}, null when no data needs to be written // @resolves with the result of OS.File.writeAtomic when all writes complete @@ -136,31 +150,40 @@ this.DeferredSave = function(aPath, aDat // The number of times the data became dirty while // another save was in progress this.overlappedSaves = 0; // Error returned by the most recent write (if any) this._lastError = null; - if (aDelay && (aDelay > 0)) - this._delay = aDelay; + if (aOptions.delay && (aOptions.delay > 0)) + this._delay = aOptions.delay; else this._delay = DEFAULT_SAVE_DELAY_MS; + + this._finalizeAt = aOptions.finalizeAt || AsyncShutdown.profileBeforeChange; + this._finalize = this._finalize.bind(this); + this._finalizeAt.addBlocker(`DeferredSave: writing data to ${aPath}`, + this._finalize); } this.DeferredSave.prototype = { get dirty() { return this._pending || this.writeInProgress; }, get lastError() { return this._lastError; }, + get path() { + return this._path; + }, + // Start the pending timer if data is dirty _startTimer() { if (!this._pending) { return; } this.logger.debug("Starting timer"); if (!this._timer) @@ -258,10 +281,15 @@ this.DeferredSave.prototype = { if (this._timer) { this._timer.cancel(); this._timer = null; } this._deferredSave(); } return this._writing; - } + }, + + _finalize() { + return this.flush().catch(Cu.reportError); + }, + };
--- a/toolkit/mozapps/extensions/amIAddonManagerStartup.idl +++ b/toolkit/mozapps/extensions/amIAddonManagerStartup.idl @@ -19,16 +19,22 @@ interface amIAddonManagerStartup : nsISu /** * Initializes the chrome registry for the enabled, non-restartless add-on * in the given state data. */ [implicit_jscontext] void initializeExtensions(in jsval locations); + [implicit_jscontext] + jsval encodeBlob(in jsval value); + + [implicit_jscontext] + jsval decodeBlob(in jsval value); + /** * Resets the internal state of the startup service, and allows * initializeExtensions() to be called again. Does *not* fully unregister * chrome registry locations for previously registered add-ons. * * NOT FOR USE OUTSIDE OF UNIT TESTS. */ void reset();
--- a/widget/cocoa/nsAppShell.mm +++ b/widget/cocoa/nsAppShell.mm @@ -320,17 +320,22 @@ nsAppShell::Init() // running in a Cocoa embedder. See bug 604901. if (!mRunningCocoaEmbedded) { nsToolkit::SwizzleMethods([NSApplication class], @selector(terminate:), @selector(nsAppShell_NSApplication_terminate:)); } gAppShellMethodsSwizzled = true; } - if (nsCocoaFeatures::OnYosemiteOrLater()) { + // The bug that this works around was introduced in OS X 10.10.0 + // and fixed in OS X 10.10.2. Order these version checks so as + // few as possible will actually end up running. + if (nsCocoaFeatures::OSXVersionMinor() == 10 && + nsCocoaFeatures::OSXVersionBugFix() < 2 && + nsCocoaFeatures::OSXVersionMajor() == 10) { // Explicitly turn off CGEvent logging. This works around bug 1092855. // If there are already CGEvents in the log, turning off logging also // causes those events to be written to disk. But at this point no // CGEvents have yet been processed. CGEvents are events (usually // input events) pulled from the WindowServer. An option of 0x80000008 // turns on CGEvent logging. CGSSetDebugOptions(0x80000007); }
--- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -504,17 +504,17 @@ MozCrashWarningReporter(JSContext*, JSEr } CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSContext* aCx) : mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal) , mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal) , mJSRuntime(JS_GetRuntime(aCx)) , mPrevGCSliceCallback(nullptr) , mPrevGCNurseryCollectionCallback(nullptr) - , mJSHolders(256) + , mJSHolderMap(256) , mOutOfMemoryState(OOMState::OK) , mLargeAllocationFailureState(OOMState::OK) { MOZ_COUNT_CTOR(CycleCollectedJSRuntime); MOZ_ASSERT(aCx); MOZ_ASSERT(mJSRuntime); if (!JS_AddExtraGCRootsTracer(aCx, TraceBlackJS, this)) { @@ -582,27 +582,28 @@ CycleCollectedJSRuntime::RemoveContext(C size_t CycleCollectedJSRuntime::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { size_t n = 0; // We're deliberately not measuring anything hanging off the entries in // mJSHolders.