author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Mon, 23 Nov 2015 14:08:50 +0100 | |
changeset 273760 | d3d286102ba7f8801e9dfe12d534f49554ba50c0 |
parent 273759 | 8b1fc0961a076e35646d0472a81feefc0074558c (current diff) |
parent 273679 | e722799fa71f653c31454612564c213d09566219 (diff) |
child 273761 | eff4131a3e4caf33761bd52cb9a5f2dd5601c4f1 |
child 273839 | 8d9c87b4c312bd778eb9c06ddde0249b57393551 |
child 273847 | f73d0397bce2f1c66e12ec8553d29550e5c0d90f |
child 273995 | 5052c7b0634f91e50ee9fbf4d1c741c7eff074ae |
push id | 68366 |
push user | cbook@mozilla.com |
push date | Mon, 23 Nov 2015 13:31:58 +0000 |
treeherder | mozilla-inbound@eff4131a3e4c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 45.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
browser/base/content/browser.js | file | annotate | diff | comparison | revisions | |
dom/devicestorage/ipc/ipc.json | file | annotate | diff | comparison | revisions | |
dom/devicestorage/ipc/mochitest.ini | file | annotate | diff | comparison | revisions | |
dom/devicestorage/ipc/test_ipc.html | file | annotate | diff | comparison | revisions | |
dom/quota/QuotaManager.cpp | file | annotate | diff | comparison | revisions | |
dom/quota/Utilities.h | file | annotate | diff | comparison | revisions | |
dom/quota/nsIQuotaManager.idl | file | annotate | diff | comparison | revisions | |
dom/quota/nsIQuotaRequest.idl | file | annotate | diff | comparison | revisions | |
dom/quota/nsIUsageCallback.idl | file | annotate | diff | comparison | revisions | |
layout/reftests/reftest.list | file | annotate | diff | comparison | revisions |
--- a/b2g/branding/browserhtml/content/jar.mn +++ b/b2g/branding/browserhtml/content/jar.mn @@ -1,10 +1,10 @@ # 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/. chrome.jar: -% content branding %content/branding/ +% content branding %content/branding/ contentaccessible=yes content/branding/about.png (about.png) content/branding/logoWordmark.png (logoWordmark.png) content/branding/logo.png (logo.png) content/branding/favicon32.png (favicon32.png)
--- a/b2g/branding/horizon/content/jar.mn +++ b/b2g/branding/horizon/content/jar.mn @@ -1,10 +1,10 @@ # 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/. chrome.jar: -% content branding %content/branding/ +% content branding %content/branding/ contentaccessible=yes content/branding/about.png (about.png) content/branding/logoWordmark.png (logoWordmark.png) content/branding/logo.png (logo.png) content/branding/favicon32.png (favicon32.png)
--- a/b2g/branding/official/content/jar.mn +++ b/b2g/branding/official/content/jar.mn @@ -1,10 +1,10 @@ # 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/. chrome.jar: -% content branding %content/branding/ +% content branding %content/branding/ contentaccessible=yes content/branding/about.png (about.png) content/branding/logoWordmark.png (logoWordmark.png) content/branding/logo.png (logo.png) content/branding/favicon32.png (favicon32.png)
--- a/b2g/branding/unofficial/content/jar.mn +++ b/b2g/branding/unofficial/content/jar.mn @@ -1,10 +1,10 @@ # 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/. chrome.jar: -% content branding %content/branding/ +% content branding %content/branding/ contentaccessible=yes content/branding/about.png (about.png) content/branding/logoWordmark.png (logoWordmark.png) content/branding/logo.png (logo.png) content/branding/favicon32.png (favicon32.png)
--- a/b2g/chrome/jar.mn +++ b/b2g/chrome/jar.mn @@ -1,16 +1,16 @@ #filter substitution # 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/. chrome.jar: -% content branding %content/branding/ +% content branding %content/branding/ contentaccessible=yes % content b2g %content/ content/arrow.svg (content/arrow.svg) * content/settings.js (content/settings.js) * content/shell.html (content/shell.html) * content/shell.js (content/shell.js) content/shell_remote.html (content/shell_remote.html) content/shell_remote.js (content/shell_remote.js)
--- a/b2g/locales/en-US/chrome/overrides/appstrings.properties +++ b/b2g/locales/en-US/chrome/overrides/appstrings.properties @@ -27,13 +27,14 @@ externalProtocolTitle=External Protocol externalProtocolPrompt=An external application must be launched to handle %1$S: links.\n\n\nRequested link:\n\n%2$S\n\nApplication: %3$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n #LOCALIZATION NOTE (externalProtocolUnknown): The following string is shown if the application name can't be determined externalProtocolUnknown=<Unknown> externalProtocolChkMsg=Remember my choice for all links of this type. externalProtocolLaunchBtn=Launch application malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences. unwantedBlocked=The site at %S has been reported as serving unwanted software and has been blocked based on your security preferences. phishingBlocked=The website at %S has been reported as a web forgery designed to trick users into sharing personal or financial information. +forbiddenBlocked=The site at %S has been blocked by your browser configuration. cspBlocked=This page has a content security policy that prevents it from being loaded in this way. corruptedContentError=The page you are trying to view cannot be shown because an error in the data transmission was detected. remoteXUL=This page uses an unsupported technology that is no longer available by default in Firefox. sslv3Used=Firefox cannot guarantee the safety of your data on %S because it uses SSLv3, a broken security protocol. weakCryptoUsed=The owner of %S has configured their website improperly. To protect your information from being stolen, Firefox has not connected to this website.
--- a/browser/base/content/aboutNetError.xhtml +++ b/browser/base/content/aboutNetError.xhtml @@ -418,16 +418,17 @@ <h1 id="et_proxyResolveFailure">&proxyResolveFailure.title;</h1> <h1 id="et_proxyConnectFailure">&proxyConnectFailure.title;</h1> <h1 id="et_contentEncodingError">&contentEncodingError.title;</h1> <h1 id="et_unsafeContentType">&unsafeContentType.title;</h1> <h1 id="et_nssFailure2">&nssFailure2.title;</h1> <h1 id="et_nssBadCert">&nssBadCert.title;</h1> <h1 id="et_malwareBlocked">&malwareBlocked.title;</h1> <h1 id="et_unwantedBlocked">&unwantedBlocked.title;</h1> + <h1 id="et_forbiddenBlocked">&forbiddenBlocked.title;</h1> <h1 id="et_cspBlocked">&cspBlocked.title;</h1> <h1 id="et_remoteXUL">&remoteXUL.title;</h1> <h1 id="et_corruptedContentError">&corruptedContentError.title;</h1> <h1 id="et_sslv3Used">&sslv3Used.title;</h1> <h1 id="et_weakCryptoUsed">&weakCryptoUsed.title;</h1> </div> <div id="errorDescriptionsContainer"> <div id="ed_generic">&generic.longDesc;</div> @@ -447,16 +448,17 @@ <div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc;</div> <div id="ed_proxyConnectFailure">&proxyConnectFailure.longDesc;</div> <div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div> <div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div> <div id="ed_nssFailure2">&nssFailure2.longDesc2;</div> <div id="ed_nssBadCert">&nssBadCert.longDesc2;</div> <div id="ed_malwareBlocked">&malwareBlocked.longDesc;</div> <div id="ed_unwantedBlocked">&unwantedBlocked.longDesc;</div> + <div id="ed_forbiddenBlocked">&forbiddenBlocked.longDesc;</div> <div id="ed_cspBlocked">&cspBlocked.longDesc;</div> <div id="ed_remoteXUL">&remoteXUL.longDesc;</div> <div id="ed_corruptedContentError">&corruptedContentError.longDesc;</div> <div id="ed_sslv3Used">&sslv3Used.longDesc;</div> <div id="learn_more_ssl3">&sslv3Used.learnMore;</div> <div id="ed_weakCryptoUsed">&weakCryptoUsed.longDesc;</div> <div id="learn_more_weak_crypto">&weakCryptoUsed.learnMore;</div> </div>
--- a/browser/base/content/blockedSite.xhtml +++ b/browser/base/content/blockedSite.xhtml @@ -63,121 +63,86 @@ function getHostString() { try { return document.location.hostname; } catch (e) { return getURL(); } } - + function initPage() { - // Handoff to the appropriate initializer, based on error code + var error = ""; switch (getErrorCode()) { case "malwareBlocked" : - initPage_malware(); + error = "malware"; break; case "phishingBlocked" : - initPage_phishing(); + error = "phishing"; break; case "unwantedBlocked" : - initPage_unwanted(); + error = "unwanted"; break; + case "forbiddenBlocked" : + error = "forbidden"; + break; + default: + return; } - } - - /** - * Initialize custom strings and functionality for blocked malware case - */ - function initPage_malware() - { - // Remove phishing and unwanted strings - var el = document.getElementById("errorTitleText_phishing"); - el.parentNode.removeChild(el); + + var el; + + if (error !== "malware") { + el = document.getElementById("errorTitleText_malware"); + el.parentNode.removeChild(el); + el = document.getElementById("errorShortDescText_malware"); + el.parentNode.removeChild(el); + el = document.getElementById("errorLongDescText_malware"); + el.parentNode.removeChild(el); + } - el = document.getElementById("errorShortDescText_phishing"); - el.parentNode.removeChild(el); - - el = document.getElementById("errorLongDescText_phishing"); - el.parentNode.removeChild(el); + if (error !== "phishing") { + el = document.getElementById("errorTitleText_phishing"); + el.parentNode.removeChild(el); + el = document.getElementById("errorShortDescText_phishing"); + el.parentNode.removeChild(el); + el = document.getElementById("errorLongDescText_phishing"); + el.parentNode.removeChild(el); + } - el = document.getElementById("errorTitleText_unwanted"); - el.parentNode.removeChild(el); + if (error !== "unwanted") { + el = document.getElementById("errorTitleText_unwanted"); + el.parentNode.removeChild(el); + el = document.getElementById("errorShortDescText_unwanted"); + el.parentNode.removeChild(el); + el = document.getElementById("errorLongDescText_unwanted"); + el.parentNode.removeChild(el); + } - el = document.getElementById("errorShortDescText_unwanted"); - el.parentNode.removeChild(el); - - el = document.getElementById("errorLongDescText_unwanted"); - el.parentNode.removeChild(el); + if (error !== "forbidden") { + el = document.getElementById("errorTitleText_forbidden"); + el.parentNode.removeChild(el); + el = document.getElementById("errorShortDescText_forbidden"); + el.parentNode.removeChild(el); + } else { + el = document.getElementById("ignoreWarningButton"); + el.parentNode.removeChild(el); + el = document.getElementById("reportButton"); + el.parentNode.removeChild(el); + } // Set sitename - document.getElementById("malware_sitename").textContent = getHostString(); - document.title = document.getElementById("errorTitleText_malware") + document.getElementById(error + "_sitename").textContent = getHostString(); + document.title = document.getElementById("errorTitleText_" + error) .innerHTML; - } - - /** - * Initialize custom strings and functionality for blocked malware case - */ - function initPage_unwanted() - { - // Remove phishing and malware strings - var el = document.getElementById("errorTitleText_phishing"); - el.parentNode.removeChild(el); - - el = document.getElementById("errorShortDescText_phishing"); - el.parentNode.removeChild(el); - - el = document.getElementById("errorLongDescText_phishing"); - el.parentNode.removeChild(el); - - el = document.getElementById("errorTitleText_malware"); - el.parentNode.removeChild(el); - - el = document.getElementById("errorShortDescText_malware"); - el.parentNode.removeChild(el); - - el = document.getElementById("errorLongDescText_malware"); - el.parentNode.removeChild(el); - // Set sitename - document.getElementById("unwanted_sitename").textContent = getHostString(); - document.title = document.getElementById("errorTitleText_unwanted") - .innerHTML; - } - - /** - * Initialize custom strings and functionality for blocked phishing case - */ - function initPage_phishing() - { - // Remove malware and unwanted strings - var el = document.getElementById("errorTitleText_malware"); - el.parentNode.removeChild(el); - - el = document.getElementById("errorShortDescText_malware"); - el.parentNode.removeChild(el); - - el = document.getElementById("errorLongDescText_malware"); - el.parentNode.removeChild(el); - - el = document.getElementById("errorTitleText_unwanted"); - el.parentNode.removeChild(el); - - el = document.getElementById("errorShortDescText_unwanted"); - el.parentNode.removeChild(el); - - el = document.getElementById("errorLongDescText_unwanted"); - el.parentNode.removeChild(el); - - // Set sitename - document.getElementById("phishing_sitename").textContent = getHostString(); - document.title = document.getElementById("errorTitleText_phishing") - .innerHTML; + // Inform the test harness that we're done loading the page + var event = new CustomEvent("AboutBlockedLoaded"); + document.dispatchEvent(event); } ]]></script> <style type="text/css"> /* Style warning button to look like a small text link in the bottom right. This is preferable to just using a text link since there is already a mechanism in browser.js for trapping oncommand events from unprivileged chrome pages (BrowserOnCommand).*/ #ignoreWarningButton { @@ -208,25 +173,27 @@ <body dir="&locale.dir;"> <div id="errorPageContainer"> <!-- Error Title --> <div id="errorTitle"> <h1 id="errorTitleText_phishing">&safeb.blocked.phishingPage.title;</h1> <h1 id="errorTitleText_malware">&safeb.blocked.malwarePage.title;</h1> <h1 id="errorTitleText_unwanted">&safeb.blocked.unwantedPage.title;</h1> + <h1 id="errorTitleText_forbidden">&safeb.blocked.forbiddenPage.title;</h1> </div> <div id="errorLongContent"> <!-- Short Description --> <div id="errorShortDesc"> <p id="errorShortDescText_phishing">&safeb.blocked.phishingPage.shortDesc;</p> <p id="errorShortDescText_malware">&safeb.blocked.malwarePage.shortDesc;</p> <p id="errorShortDescText_unwanted">&safeb.blocked.unwantedPage.shortDesc;</p> + <p id="errorShortDescText_forbidden">&safeb.blocked.forbiddenPage.shortDesc;</p> </div> <!-- Long Description --> <div id="errorLongDesc"> <p id="errorLongDescText_phishing">&safeb.blocked.phishingPage.longDesc;</p> <p id="errorLongDescText_malware">&safeb.blocked.malwarePage.longDesc;</p> <p id="errorLongDescText_unwanted">&safeb.blocked.unwantedPage.longDesc;</p> </div>
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -2953,44 +2953,55 @@ var BrowserOnClick = { break; } }, onAboutBlocked: function (elementId, reason, isTopFrame, location) { // Depending on what page we are displaying here (malware/phishing/unwanted) // use the right strings and links for each. - let bucketName = "WARNING_PHISHING_PAGE_"; + let bucketName = ""; + let sendTelemetry = false; if (reason === 'malware') { + sendTelemetry = true; bucketName = "WARNING_MALWARE_PAGE_"; + } else if (reason === 'phishing') { + sendTelemetry = true; + bucketName = "WARNING_PHISHING_PAGE_"; } else if (reason === 'unwanted') { + sendTelemetry = true; bucketName = "WARNING_UNWANTED_PAGE_"; } let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI"); let nsISecTel = Ci.nsISecurityUITelemetry; bucketName += isTopFrame ? "TOP_" : "FRAME_"; switch (elementId) { case "getMeOutButton": - secHistogram.add(nsISecTel[bucketName + "GET_ME_OUT_OF_HERE"]); + if (sendTelemetry) { + secHistogram.add(nsISecTel[bucketName + "GET_ME_OUT_OF_HERE"]); + } getMeOutOfHere(); break; case "reportButton": // This is the "Why is this site blocked" button. We redirect // to the generic page describing phishing/malware protection. // We log even if malware/phishing/unwanted info URL couldn't be found: // the measurement is for how many users clicked the WHY BLOCKED button - secHistogram.add(nsISecTel[bucketName + "WHY_BLOCKED"]); - + if (sendTelemetry) { + secHistogram.add(nsISecTel[bucketName + "WHY_BLOCKED"]); + } openHelpLink("phishing-malware", false, "current"); break; case "ignoreWarningButton": - secHistogram.add(nsISecTel[bucketName + "IGNORE_WARNING"]); + if (sendTelemetry) { + secHistogram.add(nsISecTel[bucketName + "IGNORE_WARNING"]); + } this.ignoreWarningButton(reason); break; } }, /** * This functions prevents navigation from happening directly through the <a> * link in about:newtab (which is loaded in the parent and therefore would load @@ -3049,16 +3060,18 @@ var BrowserOnClick = { callback: function() { openUILinkIn(gSafeBrowsing.getReportURL('PhishMistake'), 'tab'); } }; } else if (reason === 'unwanted') { title = gNavigatorBundle.getString("safebrowsing.reportedUnwantedSite"); // There is no button for reporting errors since Google doesn't currently // provide a URL endpoint for these reports. + } else { + return; // no notifications for forbidden sites } let notificationBox = gBrowser.getNotificationBox(); let value = "blocked-badware-page"; let previousNotification = notificationBox.getNotificationWithValue(value); if (previousNotification) { notificationBox.removeNotification(previousNotification);
--- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -455,16 +455,18 @@ var ClickEventHandler = { }, onAboutBlocked: function (targetElement, ownerDoc) { var reason = 'phishing'; if (/e=malwareBlocked/.test(ownerDoc.documentURI)) { reason = 'malware'; } else if (/e=unwantedBlocked/.test(ownerDoc.documentURI)) { reason = 'unwanted'; + } else if (/e=forbiddenBlocked/.test(ownerDoc.documentURI)) { + reason = 'forbidden'; } sendAsyncMessage("Browser:SiteBlockedError", { location: ownerDoc.location.href, reason: reason, elementId: targetElement.getAttribute("id"), isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView) }); },
--- a/browser/base/content/pageinfo/permissions.js +++ b/browser/base/content/pageinfo/permissions.js @@ -1,16 +1,16 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ Components.utils.import("resource:///modules/SitePermissions.jsm"); Components.utils.import("resource://gre/modules/BrowserUtils.jsm"); -const nsIQuotaManager = Components.interfaces.nsIQuotaManager; +const nsIQuotaManagerService = Components.interfaces.nsIQuotaManagerService; var gPermURI; var gUsageRequest; var gPermissions = SitePermissions.listPermissions(); gPermissions.push("plugins"); var permissionObserver = { @@ -181,68 +181,70 @@ function setRadioState(aPartId, aValue) function initIndexedDBRow() { let row = document.getElementById("perm-indexedDB-row"); let extras = document.getElementById("perm-indexedDB-extras"); row.appendChild(extras); - var quotaManager = Components.classes["@mozilla.org/dom/quota/manager;1"] - .getService(nsIQuotaManager); + var quotaManagerService = + Components.classes["@mozilla.org/dom/quota-manager-service;1"] + .getService(nsIQuotaManagerService); let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"] .getService(Components.interfaces.nsIScriptSecurityManager) .createCodebasePrincipal(gPermURI, {}); gUsageRequest = - quotaManager.getUsageForPrincipal(principal, onIndexedDBUsageCallback); + quotaManagerService.getUsageForPrincipal(principal, + onIndexedDBUsageCallback); var status = document.getElementById("indexedDBStatus"); var button = document.getElementById("indexedDBClear"); status.value = ""; status.setAttribute("hidden", "true"); button.setAttribute("hidden", "true"); } function onIndexedDBClear() { let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"] .getService(Components.interfaces.nsIScriptSecurityManager) .createCodebasePrincipal(gPermURI, {}); - Components.classes["@mozilla.org/dom/quota/manager;1"] - .getService(nsIQuotaManager) + Components.classes["@mozilla.org/dom/quota-manager-service;1"] + .getService(nsIQuotaManagerService) .clearStoragesForPrincipal(principal); Components.classes["@mozilla.org/serviceworkers/manager;1"] .getService(Components.interfaces.nsIServiceWorkerManager) .removeAndPropagate(gPermURI.host); SitePermissions.remove(gPermURI, "indexedDB"); initIndexedDBRow(); } -function onIndexedDBUsageCallback(principal, usage, fileUsage) +function onIndexedDBUsageCallback(request) { - let uri = principal.URI; + let uri = request.principal.URI; if (!uri.equals(gPermURI)) { throw new Error("Callback received for bad URI: " + uri); } - if (usage) { + if (request.usage) { if (!("DownloadUtils" in window)) { Components.utils.import("resource://gre/modules/DownloadUtils.jsm"); } var status = document.getElementById("indexedDBStatus"); var button = document.getElementById("indexedDBClear"); status.value = gBundle.getFormattedString("indexedDBUsage", - DownloadUtils.convertByteUnits(usage)); + DownloadUtils.convertByteUnits(request.usage)); status.removeAttribute("hidden"); button.removeAttribute("hidden"); } } function fillInPluginPermissionTemplate(aPluginName, aPermissionString) { let permPluginTemplate = document.getElementById("permPluginTemplate").cloneNode(true); permPluginTemplate.setAttribute("permString", aPermissionString);
--- a/browser/base/content/test/general/browser_aboutHome.js +++ b/browser/base/content/test/general/browser_aboutHome.js @@ -355,25 +355,35 @@ var gTests = [ attributeFilter: ["aria-expanded"], }); yield deferred.promise; // Empty the search input, causing the suggestions to be hidden. EventUtils.synthesizeKey("a", { accelKey: true }); EventUtils.synthesizeKey("VK_DELETE", {}); ok(table.hidden, "Search suggestion table hidden"); + + try { + Services.search.removeEngine(engine); + } catch (ex) { } }); } }, { desc: "Clicking suggestion list while composing", setup: function() {}, run: function() { return Task.spawn(function* () { + // Add a test engine that provides suggestions and switch to it. + let engine = yield promiseNewEngine("searchSuggestionEngine.xml"); + let p = promiseContentSearchChange(engine.name); + Services.search.currentEngine = engine; + yield p; + // Start composition and type "x" let input = gBrowser.contentDocument.getElementById("searchText"); input.focus(); EventUtils.synthesizeComposition({ type: "compositionstart", data: "" }, gBrowser.contentWindow); EventUtils.synthesizeCompositionChange({ composition: { string: "x",
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/general/bug_1182546.xml @@ -0,0 +1,5 @@ +<!DOCTYPE html [ + <!ENTITY % passwordManagerDTD SYSTEM "chrome://passwordmgr/locale/passwordManager.dtd"> + %passwordManagerDTD; +]> +<window>&savedLogins.title;</window>
--- a/browser/base/content/test/general/mochitest.ini +++ b/browser/base/content/test/general/mochitest.ini @@ -20,21 +20,23 @@ support-files = offlineChild2.cacheManifest offlineChild2.cacheManifest^headers^ offlineChild2.html offlineEvent.cacheManifest offlineEvent.cacheManifest^headers^ offlineEvent.html subtst_contextmenu.html video.ogg + bug_1182546.xml [test_bug364677.html] [test_bug395533.html] [test_contextmenu.html] skip-if = toolkit == "gtk2" || toolkit == "gtk3" || (os == 'mac' && os_version != '10.6') # disabled on Linux due to bug 513558, on Mac after 10.6 due to bug 792304 [test_contextmenu_input.html] skip-if = toolkit == "gtk2" || toolkit == "gtk3" || e10s # disabled on Linux due to bug 513558 [test_feed_discovery.html] skip-if = e10s [test_offlineNotification.html] skip-if = buildapp == 'mulet' || e10s # Bug 1066070 - I don't think either popup notifications nor addon install stuff works? [test_offline_gzip.html] skip-if = buildapp == 'mulet' || e10s # Bug 1066070 - I don't think either popup notifications nor addon install stuff works? +[test_bug1182546.html]
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/general/test_bug1182546.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1182546 +--> +<head> + <title>Bug 1182546 - Test block loading DTD from random page</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe id="testframe" src="bug_1182546.xml"></iframe> + +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +addLoadEvent(function() { + // make sure the DTD loader (nsExpatDriver) prevents accessing chrome: from random pages + var childNodes = testframe.contentDocument.documentElement.childNodes; + + // make sure '&savedLogins.title;' from bug_1182546.xml does not translate into 'Saved Logins' + // the URL 'chrome://passwordmgr/locale/passwordManager.dtd' should not be accessible from content + var nodeValue = childNodes[0].nodeValue; + isnot(nodeValue, "Saved Logins", + "expatDriver should prevent accessing &savedLogins.title;"); + ok(nodeValue.startsWith("XML Parsing Error: undefined entity"), + "expatDriver should not allow accessing chrome:"); +}); + +addLoadEvent(SimpleTest.finish); + +</script> +</body> +</html>
--- a/browser/components/migration/tests/unit/test_Chrome_cookies.js +++ b/browser/components/migration/tests/unit/test_Chrome_cookies.js @@ -23,27 +23,28 @@ add_task(function* () { const PROFILE = { id: "Default", name: "Person 1", }; // Migrate unencrypted cookies. yield promiseMigration(migrator, MigrationUtils.resourceTypes.COOKIES, PROFILE); - do_register_cleanup(() => { - ForgetAboutSite.removeDataFromDomain(COOKIE.host); - Assert.equal(Services.cookies.countCookiesFromHost(COOKIE.host), 0, - "There are no cookies after cleanup"); - }); Assert.equal(Services.cookies.countCookiesFromHost(COOKIE.host), 1, "Migrated the expected number of unencrypted cookies"); Assert.equal(Services.cookies.countCookiesFromHost("encryptedcookie.invalid"), 0, "Migrated the expected number of encrypted cookies"); // Now check the cookie details. let enumerator = Services.cookies.getCookiesFromHost(COOKIE.host); Assert.ok(enumerator.hasMoreElements(), "Cookies available"); let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); for (let prop of Object.keys(COOKIE)) { Assert.equal(foundCookie[prop], COOKIE[prop], "Check cookie " + prop); } + + // Cleanup. + ForgetAboutSite.removeDataFromDomain(COOKIE.host); + Assert.equal(Services.cookies.countCookiesFromHost(COOKIE.host), 0, + "There are no cookies after cleanup"); + });
--- a/browser/components/safebrowsing/content/test/browser.ini +++ b/browser/components/safebrowsing/content/test/browser.ini @@ -1,11 +1,12 @@ [DEFAULT] support-files = head.js +[browser_forbidden.js] [browser_bug400731.js] skip-if = e10s [browser_bug415846.js] skip-if = true # Disabled because it seems to now touch network resources # skip-if = os == "mac" # Disabled on Mac because of its bizarre special-and-unique # snowflake of a help menu.
new file mode 100644 --- /dev/null +++ b/browser/components/safebrowsing/content/test/browser_forbidden.js @@ -0,0 +1,40 @@ +/* Ensure that pages in the forbidden list are blocked. */ + +const PREF_FORBIDDEN_ENABLED = "browser.safebrowsing.forbiddenURIs.enabled"; +const BENIGN_PAGE = "http://example.com/"; +const FORBIDDEN_PAGE = "http://www.itisatrap.org/firefox/forbidden.html"; +var tabbrowser = null; + +registerCleanupFunction(function() { + tabbrowser = null; + Services.prefs.clearUserPref(PREF_FORBIDDEN_ENABLED); + while (gBrowser.tabs.length > 1) { + gBrowser.removeCurrentTab(); + } +}); + +function testBenignPage(window) { + info("Non-forbidden content must not be blocked"); + var getmeout_button = window.document.getElementById("getMeOutButton"); + var ignorewarning_button = window.document.getElementById("ignoreWarningButton"); + ok(!getmeout_button, "GetMeOut button not present"); + ok(!ignorewarning_button, "IgnoreWarning button not present"); +} + +function testForbiddenPage(window) { + info("Forbidden content must be blocked"); + ok(true, "about:blocked was shown"); +} + +add_task(function* testNormalBrowsing() { + tabbrowser = gBrowser; + let tab = tabbrowser.selectedTab = tabbrowser.addTab(); + + info("Load a test page that's not forbidden"); + yield promiseTabLoadEvent(tab, BENIGN_PAGE, "load"); + testBenignPage(tab.ownerDocument.defaultView); + + info("Load a test page that is forbidden"); + yield promiseTabLoadEvent(tab, FORBIDDEN_PAGE, "AboutBlockedLoaded"); + testForbiddenPage(tab.ownerDocument.defaultView); +});
--- a/browser/components/safebrowsing/content/test/head.js +++ b/browser/components/safebrowsing/content/test/head.js @@ -1,5 +1,57 @@ -// Force SafeBrowsing to be initialized for the tests +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "Promise", + "resource://gre/modules/Promise.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Task", + "resource://gre/modules/Task.jsm"); + +/** + * Waits for a load (or custom) event to finish in a given tab. If provided + * load an uri into the tab. + * + * @param tab + * The tab to load into. + * @param [optional] url + * The url to load, or the current url. + * @param [optional] event + * The load event type to wait for. Defaults to "load". + * @return {Promise} resolved when the event is handled. + * @resolves to the received event + * @rejects if a valid load event is not received within a meaningful interval + */ +function promiseTabLoadEvent(tab, url, eventType="load") +{ + let deferred = Promise.defer(); + info("Wait tab event: " + eventType); + + function handle(event) { + if (event.originalTarget != tab.linkedBrowser.contentDocument || + event.target.location.href == "about:blank" || + (url && event.target.location.href != url)) { + info("Skipping spurious '" + eventType + "'' event" + + " for " + event.target.location.href); + return; + } + clearTimeout(timeout); + tab.linkedBrowser.removeEventListener(eventType, handle, true); + info("Tab event received: " + eventType); + deferred.resolve(event); + } + + let timeout = setTimeout(() => { + tab.linkedBrowser.removeEventListener(eventType, handle, true); + deferred.reject(new Error("Timed out while waiting for a '" + eventType + "'' event")); + }, 30000); + + tab.linkedBrowser.addEventListener(eventType, handle, true, true); + if (url) { + tab.linkedBrowser.loadURI(url); + } + return deferred.promise; +} + +Services.prefs.setCharPref("urlclassifier.forbiddenTable", "test-forbid-simple"); Services.prefs.setCharPref("urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple"); Services.prefs.setCharPref("urlclassifier.phishTable", "test-phish-simple"); +Services.prefs.setBoolPref("browser.safebrowsing.forbiddenURIs.enabled", true); SafeBrowsing.init(); -
--- a/browser/installer/windows/moz.build +++ b/browser/installer/windows/moz.build @@ -2,10 +2,10 @@ # vim: set filetype=python: # 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/. DEFINES['APP_VERSION'] = CONFIG['FIREFOX_VERSION'] DEFINES['MOZ_APP_NAME'] = CONFIG['MOZ_APP_NAME'] -DEFINES['MOZ_APP_DISPLAYNAME'] = "'%s'" % CONFIG['MOZ_APP_DISPLAYNAME'] +DEFINES['MOZ_APP_DISPLAYNAME'] = CONFIG['MOZ_APP_DISPLAYNAME'] DEFINES['MOZILLA_VERSION'] = CONFIG['MOZILLA_VERSION']
--- a/browser/locales/en-US/chrome/browser/safebrowsing/phishing-afterload-warning-message.dtd +++ b/browser/locales/en-US/chrome/browser/safebrowsing/phishing-afterload-warning-message.dtd @@ -3,21 +3,26 @@ - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <!ENTITY safeb.palm.accept.label "Get me out of here!"> <!ENTITY safeb.palm.decline.label "Ignore this warning"> <!ENTITY safeb.palm.notforgery.label2 "This isn't a web forgery…"> <!ENTITY safeb.palm.reportPage.label "Why was this page blocked?"> <!ENTITY safeb.blocked.malwarePage.title "Reported Attack Page!"> -<!-- Localization note (safeb.blocked.malware.shortDesc) - Please don't translate the contents of the <span id="malware_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) --> +<!-- Localization note (safeb.blocked.malwarePage.shortDesc) - Please don't translate the contents of the <span id="malware_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) --> <!ENTITY safeb.blocked.malwarePage.shortDesc "This web page at <span id='malware_sitename'/> has been reported as an attack page and has been blocked based on your security preferences."> <!ENTITY safeb.blocked.malwarePage.longDesc "<p>Attack pages try to install programs that steal private information, use your computer to attack others, or damage your system.</p><p>Some attack pages intentionally distribute harmful software, but many are compromised without the knowledge or permission of their owners.</p>"> <!ENTITY safeb.blocked.unwantedPage.title "Reported Unwanted Software Page!"> -<!-- Localization note (safeb.blocked.malware.shortDesc) - Please don't translate the contents of the <span id="unwanted_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) --> +<!-- Localization note (safeb.blocked.unwantedPage.shortDesc) - Please don't translate the contents of the <span id="unwanted_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) --> <!ENTITY safeb.blocked.unwantedPage.shortDesc "This web page at <span id='unwanted_sitename'/> has been reported to contain unwanted software and has been blocked based on your security preferences."> <!ENTITY safeb.blocked.unwantedPage.longDesc "<p>Unwanted software pages try to install software that can be deceptive and affect your system in unexpected ways.</p>"> <!ENTITY safeb.blocked.phishingPage.title "Reported Web Forgery!"> -<!-- Localization note (safeb.blocked.phishing.shortDesc) - Please don't translate the contents of the <span id="phishing_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) --> +<!-- Localization note (safeb.blocked.phishingPage.shortDesc) - Please don't translate the contents of the <span id="phishing_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) --> <!ENTITY safeb.blocked.phishingPage.shortDesc "This web page at <span id='phishing_sitename'/> has been reported as a web forgery and has been blocked based on your security preferences."> <!ENTITY safeb.blocked.phishingPage.longDesc "<p>Web forgeries are designed to trick you into revealing personal or financial information by imitating sources you may trust.</p><p>Entering any information on this web page may result in identity theft or other fraud.</p>"> + +<!ENTITY safeb.blocked.forbiddenPage.title "Forbidden Site"> +<!-- Localization note (safeb.blocked.forbiddenPage.shortDesc) - Please don't translate the contents of the <span id="forbidden_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) --> +<!ENTITY safeb.blocked.forbiddenPage.shortDesc "This Web page at <span id='forbidden_sitename'/> has been blocked based on your browser configuration."> +
--- a/browser/locales/en-US/chrome/overrides/appstrings.properties +++ b/browser/locales/en-US/chrome/overrides/appstrings.properties @@ -27,15 +27,16 @@ externalProtocolTitle=External Protocol externalProtocolPrompt=An external application must be launched to handle %1$S: links.\n\n\nRequested link:\n\n%2$S\n\nApplication: %3$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n #LOCALIZATION NOTE (externalProtocolUnknown): The following string is shown if the application name can't be determined externalProtocolUnknown=<Unknown> externalProtocolChkMsg=Remember my choice for all links of this type. externalProtocolLaunchBtn=Launch application malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences. unwantedBlocked=The site at %S has been reported as serving unwanted software and has been blocked based on your security preferences. phishingBlocked=The website at %S has been reported as a web forgery designed to trick users into sharing personal or financial information. +forbiddenBlocked=The site at %S has been blocked by your browser configuration. cspBlocked=This page has a content security policy that prevents it from being loaded in this way. corruptedContentError=The page you are trying to view cannot be shown because an error in the data transmission was detected. remoteXUL=This page uses an unsupported technology that is no longer available by default in Firefox. ## LOCALIZATION NOTE (sslv3Used) - Do not translate "%S". sslv3Used=Firefox cannot guarantee the safety of your data on %S because it uses SSLv3, a broken security protocol. ## LOCALIZATION NOTE (weakCryptoUsed) - Do not translate "%S". weakCryptoUsed=The owner of %S has configured their website improperly. To protect your information from being stolen, Firefox has not connected to this website.
--- a/browser/locales/en-US/chrome/overrides/netError.dtd +++ b/browser/locales/en-US/chrome/overrides/netError.dtd @@ -170,16 +170,20 @@ be temporary, and you can try again late "> <!ENTITY phishingBlocked.title "Suspected Web Forgery!"> <!ENTITY phishingBlocked.longDesc " <p>Entering any personal information on this page may result in identity theft or other fraud.</p> <p>These types of web forgeries are used in scams known as phishing attacks, in which fraudulent web pages and emails are used to imitate sources you may trust.</p> "> +<!ENTITY forbiddenBlocked.title "Forbidden Site"> +<!ENTITY forbiddenBlocked.longDesc "<p>&brandShortName; prevented this page from loading because it is configured to block it.</p> +"> + <!ENTITY cspBlocked.title "Blocked by Content Security Policy"> <!ENTITY cspBlocked.longDesc "<p>&brandShortName; prevented this page from loading in this way because the page has a content security policy that disallows it.</p>"> <!ENTITY corruptedContentError.title "Corrupted Content Error"> <!ENTITY corruptedContentError.longDesc "<p>The page you are trying to view cannot be shown because an error in the data transmission was detected.</p><ul><li>Please contact the website owners to inform them of this problem.</li></ul>"> <!ENTITY securityOverride.linkText "Or you can add an exception…">
--- a/browser/themes/shared/incontent-icons/cert-error.svg +++ b/browser/themes/shared/incontent-icons/cert-error.svg @@ -1,10 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="45" height="45" viewBox="0 0 45 45">
--- a/devtools/shared/heapsnapshot/DominatorTree.cpp +++ b/devtools/shared/heapsnapshot/DominatorTree.cpp @@ -1,22 +1,53 @@ /* -*- Mode: C++; tab-width: 2; 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 "mozilla/devtools/DominatorTree.h" + +#include "js/Debug.h" +#include "mozilla/CycleCollectedJSRuntime.h" #include "mozilla/dom/DominatorTreeBinding.h" namespace mozilla { namespace devtools { +dom::Nullable<uint64_t> +DominatorTree::GetRetainedSize(uint64_t aNodeId, ErrorResult& aRv) +{ + JS::ubi::Node::Id id(aNodeId); + auto node = mHeapSnapshot->getNodeById(id); + if (node.isNothing()) + return dom::Nullable<uint64_t>(); + + auto ccrt = CycleCollectedJSRuntime::Get(); + MOZ_ASSERT(ccrt); + auto rt = ccrt->Runtime(); + MOZ_ASSERT(rt); + auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(rt); + MOZ_ASSERT(mallocSizeOf); + + JS::ubi::Node::Size size = 0; + if (!mDominatorTree.getRetainedSize(*node, mallocSizeOf, size)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return dom::Nullable<uint64_t>(); + } + + MOZ_ASSERT(size != 0, + "The node should not have been unknown since we got it from the heap snapshot."); + return dom::Nullable<uint64_t>(size); +} + + /*** Cycle Collection Boilerplate *****************************************************************/ -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DominatorTree, mParent) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DominatorTree, mParent, mHeapSnapshot) + NS_IMPL_CYCLE_COLLECTING_ADDREF(DominatorTree) NS_IMPL_CYCLE_COLLECTING_RELEASE(DominatorTree) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DominatorTree) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END
--- a/devtools/shared/heapsnapshot/DominatorTree.h +++ b/devtools/shared/heapsnapshot/DominatorTree.h @@ -1,16 +1,17 @@ /* -*- Mode: C++; tab-width: 2; 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/. */ #ifndef mozilla_devtools_DominatorTree__ #define mozilla_devtools_DominatorTree__ +#include "mozilla/devtools/HeapSnapshot.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/ErrorResult.h" #include "mozilla/RefCounted.h" #include "js/UbiNodeDominatorTree.h" #include "nsWrapperCache.h" namespace mozilla { namespace devtools { @@ -20,32 +21,40 @@ class DominatorTree final : public nsISu { protected: nsCOMPtr<nsISupports> mParent; virtual ~DominatorTree() { } private: JS::ubi::DominatorTree mDominatorTree; + RefPtr<HeapSnapshot> mHeapSnapshot; public: - explicit DominatorTree(JS::ubi::DominatorTree&& aDominatorTree, nsISupports* aParent) + explicit DominatorTree(JS::ubi::DominatorTree&& aDominatorTree, HeapSnapshot* aHeapSnapshot, + nsISupports* aParent) : mParent(aParent) , mDominatorTree(Move(aDominatorTree)) + , mHeapSnapshot(aHeapSnapshot) { MOZ_ASSERT(aParent); + MOZ_ASSERT(aHeapSnapshot); }; NS_DECL_CYCLE_COLLECTING_ISUPPORTS; NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DominatorTree); nsISupports* GetParentObject() const { return mParent; } virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + // readonly attribute NodeId root uint64_t Root() const { return mDominatorTree.root().identifier(); } + + // [Throws] NodeSize getRetainedSize(NodeId node) + dom::Nullable<uint64_t> GetRetainedSize(uint64_t aNodeId, ErrorResult& aRv); }; } // namespace devtools } // namespace mozilla #endif // mozilla_devtools_DominatorTree__
--- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp +++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp @@ -6,22 +6,25 @@ #include "HeapSnapshot.h" #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/io/gzip_stream.h> #include <google/protobuf/io/zero_copy_stream_impl_lite.h> #include "js/Debug.h" #include "js/TypeDecls.h" +#include "js/UbiNodeBreadthFirst.h" #include "js/UbiNodeCensus.h" -#include "js/UbiNodeBreadthFirst.h" +#include "js/UbiNodeDominatorTree.h" #include "mozilla/Attributes.h" +#include "mozilla/CycleCollectedJSRuntime.h" #include "mozilla/devtools/AutoMemMap.h" #include "mozilla/devtools/CoreDump.pb.h" #include "mozilla/devtools/DeserializedNode.h" +#include "mozilla/devtools/DominatorTree.h" #include "mozilla/devtools/FileDescriptorOutputStream.h" #include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h" #include "mozilla/devtools/ZeroCopyNSIOutputStream.h" #include "mozilla/dom/ChromeUtils.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/HeapSnapshotBinding.h" #include "mozilla/RangedPtr.h" #include "mozilla/Telemetry.h" @@ -533,17 +536,17 @@ HeapSnapshot::ComputeDominatorTree(Error maybeTree = JS::ubi::DominatorTree::Create(rt, nogc, getRoot()); } if (maybeTree.isNothing()) { rv.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; } - return MakeAndAddRef<DominatorTree>(Move(*maybeTree), mParent); + return MakeAndAddRef<DominatorTree>(Move(*maybeTree), this, mParent); } /*** Saving Heap Snapshots ************************************************************************/ // If we are only taking a snapshot of the heap affected by the given set of // globals, find the set of zones the globals are allocated within. Returns // false on OOM failure.
--- a/devtools/shared/heapsnapshot/HeapSnapshot.h +++ b/devtools/shared/heapsnapshot/HeapSnapshot.h @@ -4,17 +4,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_devtools_HeapSnapshot__ #define mozilla_devtools_HeapSnapshot__ #include "js/HashTable.h" #include "mozilla/ErrorResult.h" #include "mozilla/devtools/DeserializedNode.h" -#include "mozilla/devtools/DominatorTree.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/Nullable.h" #include "mozilla/HashFunctions.h" #include "mozilla/Maybe.h" #include "mozilla/RefCounted.h" #include "mozilla/RefPtr.h" #include "mozilla/TimeStamp.h" #include "mozilla/UniquePtr.h" @@ -25,16 +24,18 @@ #include "nsCycleCollectionParticipant.h" #include "nsISupports.h" #include "nsWrapperCache.h" #include "nsXPCOM.h" namespace mozilla { namespace devtools { +class DominatorTree; + struct NSFreePolicy { void operator()(void* ptr) { NS_Free(ptr); } }; using UniqueTwoByteString = UniquePtr<char16_t[], NSFreePolicy>; using UniqueOneByteString = UniquePtr<char[], NSFreePolicy>; @@ -143,16 +144,23 @@ public: JS::ubi::Node getRoot() { MOZ_ASSERT(nodes.initialized()); auto p = nodes.lookup(rootId); MOZ_ASSERT(p); const DeserializedNode& node = *p; return JS::ubi::Node(const_cast<DeserializedNode*>(&node)); } + Maybe<JS::ubi::Node> getNodeById(JS::ubi::Node::Id nodeId) { + auto p = nodes.lookup(nodeId); + if (!p) + return Nothing(); + return Some(JS::ubi::Node(const_cast<DeserializedNode*>(&*p))); + } + void TakeCensus(JSContext* cx, JS::HandleObject options, JS::MutableHandleValue rval, ErrorResult& rv); already_AddRefed<DominatorTree> ComputeDominatorTree(ErrorResult& rv); dom::Nullable<uint64_t> GetCreationTime() { static const uint64_t maxTime = uint64_t(1) << 53; if (timestamp.isSome() && timestamp.ref() <= maxTime) {
--- a/devtools/shared/heapsnapshot/ZeroCopyNSIOutputStream.cpp +++ b/devtools/shared/heapsnapshot/ZeroCopyNSIOutputStream.cpp @@ -74,18 +74,18 @@ ZeroCopyNSIOutputStream::Next(void** dat *size = BUFFER_SIZE - amountUsed; amountUsed = BUFFER_SIZE; return true; } void ZeroCopyNSIOutputStream::BackUp(int count) { - MOZ_ASSERT(count > 0, - "Must back up a positive number of bytes."); + MOZ_ASSERT(count >= 0, + "Cannot back up a negative amount of bytes."); MOZ_ASSERT(amountUsed == BUFFER_SIZE, "Can only call BackUp directly after calling Next."); MOZ_ASSERT(count <= amountUsed, "Can't back up further than we've given out."); amountUsed -= count; }
new file mode 100644 --- /dev/null +++ b/devtools/shared/heapsnapshot/tests/unit/test_DominatorTree_04.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can get the retained sizes of dominator trees. + +function run_test() { + const dominatorTree = saveHeapSnapshotAndComputeDominatorTree(); + equal(typeof dominatorTree.getRetainedSize, "function", + "getRetainedSize should be a function"); + + const size = dominatorTree.getRetainedSize(dominatorTree.root); + ok(size, "should get a size for the root"); + equal(typeof size, "number", "retained sizes should be a number"); + equal(Math.floor(size), size, "size should be an integer"); + ok(size > 0, "size should be positive"); + ok(size <= Math.pow(2, 64), "size should be less than or equal to 2^64"); + + const bad = dominatorTree.getRetainedSize(1); + equal(bad, null, "null is returned for unknown node ids"); + + do_test_finished(); +}
--- a/devtools/shared/heapsnapshot/tests/unit/xpcshell.ini +++ b/devtools/shared/heapsnapshot/tests/unit/xpcshell.ini @@ -26,16 +26,17 @@ support-files = [test_census-tree-node-04.js] [test_census-tree-node-05.js] [test_census-tree-node-06.js] [test_census-tree-node-07.js] [test_census-tree-node-08.js] [test_DominatorTree_01.js] [test_DominatorTree_02.js] [test_DominatorTree_03.js] +[test_DominatorTree_04.js] [test_HeapAnalyses_getCreationTime_01.js] [test_HeapAnalyses_readHeapSnapshot_01.js] [test_HeapAnalyses_takeCensusDiff_01.js] [test_HeapAnalyses_takeCensusDiff_02.js] [test_HeapAnalyses_takeCensus_01.js] [test_HeapAnalyses_takeCensus_02.js] [test_HeapAnalyses_takeCensus_03.js] [test_HeapAnalyses_takeCensus_04.js]
--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -4904,46 +4904,53 @@ nsDocShell::DisplayLoadError(nsresult aE } } else { error.AssignLiteral("nssFailure2"); } } } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_URI == aError || - NS_ERROR_UNWANTED_URI == aError) { + NS_ERROR_UNWANTED_URI == aError || + NS_ERROR_FORBIDDEN_URI == aError) { nsAutoCString host; aURI->GetHost(host); CopyUTF8toUTF16(host, formatStrs[0]); formatStrCount = 1; // Malware and phishing detectors may want to use an alternate error // page, but if the pref's not set, we'll fall back on the standard page nsAdoptingCString alternateErrorPage = Preferences::GetCString("urlclassifier.alternate_error_page"); if (alternateErrorPage) { errorPage.Assign(alternateErrorPage); } uint32_t bucketId; + bool sendTelemetry = false; if (NS_ERROR_PHISHING_URI == aError) { + sendTelemetry = true; error.AssignLiteral("phishingBlocked"); bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_PHISHING_PAGE_FRAME : nsISecurityUITelemetry::WARNING_PHISHING_PAGE_TOP; } else if (NS_ERROR_MALWARE_URI == aError) { + sendTelemetry = true; error.AssignLiteral("malwareBlocked"); bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_MALWARE_PAGE_FRAME : nsISecurityUITelemetry::WARNING_MALWARE_PAGE_TOP; - } else { + } else if (NS_ERROR_UNWANTED_URI == aError) { + sendTelemetry = true; error.AssignLiteral("unwantedBlocked"); bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_UNWANTED_PAGE_FRAME : nsISecurityUITelemetry::WARNING_UNWANTED_PAGE_TOP; - } - - if (errorPage.EqualsIgnoreCase("blocked")) { + } else if (NS_ERROR_FORBIDDEN_URI == aError) { + error.AssignLiteral("forbiddenBlocked"); + } + + if (sendTelemetry && errorPage.EqualsIgnoreCase("blocked")) { Telemetry::Accumulate(Telemetry::SECURITY_UI, bucketId); } cssClass.AssignLiteral("blacklist"); } else if (NS_ERROR_CONTENT_CRASHED == aError) { errorPage.AssignLiteral("tabcrashed"); error.AssignLiteral("tabcrashed"); @@ -7694,16 +7701,17 @@ nsDocShell::EndPageLoad(nsIWebProgress* aStatus == NS_ERROR_REDIRECT_LOOP || aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE || aStatus == NS_ERROR_NET_INTERRUPT || aStatus == NS_ERROR_NET_RESET || aStatus == NS_ERROR_OFFLINE || aStatus == NS_ERROR_MALWARE_URI || aStatus == NS_ERROR_PHISHING_URI || aStatus == NS_ERROR_UNWANTED_URI || + aStatus == NS_ERROR_FORBIDDEN_URI || aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE || aStatus == NS_ERROR_REMOTE_XUL || aStatus == NS_ERROR_INTERCEPTION_FAILED || NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) { // Errors to be shown for any frame DisplayLoadError(aStatus, url, nullptr, aChannel); } else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) { // Non-caching channels will simply return NS_ERROR_OFFLINE.
--- a/docshell/resources/content/netError.xhtml +++ b/docshell/resources/content/netError.xhtml @@ -290,16 +290,17 @@ <h1 id="et_proxyResolveFailure">&proxyResolveFailure.title;</h1> <h1 id="et_proxyConnectFailure">&proxyConnectFailure.title;</h1> <h1 id="et_contentEncodingError">&contentEncodingError.title;</h1> <h1 id="et_unsafeContentType">&unsafeContentType.title;</h1> <h1 id="et_nssFailure2">&nssFailure2.title;</h1> <h1 id="et_nssBadCert">&nssBadCert.title;</h1> <h1 id="et_malwareBlocked">&malwareBlocked.title;</h1> <h1 id="et_unwantedBlocked">&unwantedBlocked.title;</h1> + <h1 id="et_forbiddenBlocked">&forbiddenBlocked.title;</h1> <h1 id="et_cspBlocked">&cspBlocked.title;</h1> <h1 id="et_remoteXUL">&remoteXUL.title;</h1> <h1 id="et_corruptedContentError">&corruptedContentError.title;</h1> </div> <div id="errorDescriptionsContainer"> <div id="ed_generic">&generic.longDesc;</div> <div id="ed_dnsNotFound">&dnsNotFound.longDesc;</div> <div id="ed_fileNotFound">&fileNotFound.longDesc;</div> @@ -317,16 +318,17 @@ <div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc;</div> <div id="ed_proxyConnectFailure">&proxyConnectFailure.longDesc;</div> <div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div> <div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div> <div id="ed_nssFailure2">&nssFailure2.longDesc2;</div> <div id="ed_nssBadCert">&nssBadCert.longDesc2;</div> <div id="ed_malwareBlocked">&malwareBlocked.longDesc;</div> <div id="ed_unwantedBlocked">&unwantedBlocked.longDesc;</div> + <div id="ed_forbiddenBlocked">&forbiddenBlocked.longDesc;</div> <div id="ed_cspBlocked">&cspBlocked.longDesc;</div> <div id="ed_remoteXUL">&remoteXUL.longDesc;</div> <div id="ed_corruptedContentError">&corruptedContentError.longDesc;</div> </div> </div> <!-- PAGE CONTAINER (for styling purposes only) --> <div id="errorPageContainer">
--- a/dom/archivereader/ArchiveReader.cpp +++ b/dom/archivereader/ArchiveReader.cpp @@ -32,17 +32,17 @@ ArchiveReader::Constructor(const GlobalO if (!window) { aError.Throw(NS_ERROR_UNEXPECTED); return nullptr; } nsAutoCString encoding; if (!EncodingUtils::FindEncodingForLabelNoReplacement(aOptions.mEncoding, encoding)) { - aError.ThrowRangeError<MSG_ENCODING_NOT_SUPPORTED>(&aOptions.mEncoding); + aError.ThrowRangeError<MSG_ENCODING_NOT_SUPPORTED>(aOptions.mEncoding); return nullptr; } RefPtr<ArchiveReader> reader = new ArchiveReader(aBlob, window, encoding); return reader.forget(); }
--- a/dom/asmjscache/AsmJSCache.cpp +++ b/dom/asmjscache/AsmJSCache.cpp @@ -324,45 +324,16 @@ protected: RefPtr<QuotaObject> mQuotaObject; int64_t mFileSize; PRFileDesc* mFileDesc; PRFileMap* mFileMap; void* mMappedMemory; }; -class UnlockDirectoryRunnable final - : public nsRunnable -{ - RefPtr<DirectoryLock> mDirectoryLock; - -public: - explicit - UnlockDirectoryRunnable(already_AddRefed<DirectoryLock> aDirectoryLock) - : mDirectoryLock(Move(aDirectoryLock)) - { } - -private: - ~UnlockDirectoryRunnable() - { - MOZ_ASSERT(!mDirectoryLock); - } - - NS_IMETHOD - Run() override - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mDirectoryLock); - - mDirectoryLock = nullptr; - - return NS_OK; - } -}; - // A runnable that implements a state machine required to open a cache entry. // It executes in the parent for a cache access originating in the child. // This runnable gets registered as an IPDL subprotocol actor so that it // can communicate with the corresponding ChildRunnable. class ParentRunnable final : public FileDescriptorHolder , public quota::OpenDirectoryListener , public PAsmJSCacheEntryParent @@ -498,32 +469,35 @@ private: } void InitPersistenceType(); nsresult InitOnMainThread(); + void + OpenDirectory(); + nsresult ReadMetadata(); nsresult OpenCacheFileForWrite(); nsresult OpenCacheFileForRead(); void FinishOnOwningThread(); void DispatchToIOThread() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); // If shutdown just started, the QuotaManager may have been deleted. QuotaManager* qm = QuotaManager::Get(); if (!qm) { FailOnNonOwningThread(); return; } @@ -588,18 +562,18 @@ private: { AssertIsOnOwningThread(); MOZ_ASSERT(mState == eWaitingToOpenCacheFileForRead); MOZ_ASSERT(mOpenMode == eOpenForRead); // A cache entry has been selected to open. mModuleIndex = aModuleIndex; - mState = eDispatchToMainThread; - NS_DispatchToMainThread(this); + mState = eReadyToOpenCacheFileForRead; + DispatchToIOThread(); return true; } bool RecvCacheMiss() override { AssertIsOnOwningThread(); @@ -625,22 +599,23 @@ private: nsCOMPtr<nsIFile> mMetadataFile; Metadata mMetadata; // State initialized during eWaitingToOpenCacheFileForRead unsigned mModuleIndex; enum State { eInitial, // Just created, waiting to be dispatched to main thread + eWaitingToFinishInit, // Waiting to finish initialization + eWaitingToOpenDirectory, // Waiting to open directory eWaitingToOpenMetadata, // Waiting to be called back from OpenDirectory eReadyToReadMetadata, // Waiting to read the metadata file on the IO thread eFailedToReadMetadata, // Waiting to be dispatched to owning thread after fail eSendingMetadataForRead, // Waiting to send OnOpenMetadataForRead eWaitingToOpenCacheFileForRead, // Waiting to hear back from child - eDispatchToMainThread, // IO thread dispatch allowed from main thread only eReadyToOpenCacheFileForRead, // Waiting to open cache file for read eSendingCacheFile, // Waiting to send OnOpenCacheFile on the owning thread eOpened, // Finished calling OnOpenCacheFile, waiting to be closed eFailing, // Just failed, waiting to be dispatched to the owning thread eFinished, // Terminal state }; State mState; JS::AsmJSCacheResult mResult; @@ -706,31 +681,48 @@ ParentRunnable::InitOnMainThread() nsresult rv; nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(mPrincipalInfo, &rv); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - QuotaManager* qm = QuotaManager::GetOrCreate(); - NS_ENSURE_STATE(qm); - rv = QuotaManager::GetInfoFromPrincipal(principal, &mGroup, &mOrigin, &mIsApp); NS_ENSURE_SUCCESS(rv, rv); InitPersistenceType(); mEnforcingQuota = QuotaManager::IsQuotaEnforced(mPersistence, mOrigin, mIsApp); return NS_OK; } +void +ParentRunnable::OpenDirectory() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == eWaitingToFinishInit || + mState == eWaitingToOpenDirectory); + MOZ_ASSERT(QuotaManager::Get()); + + mState = eWaitingToOpenMetadata; + + // XXX The exclusive lock shouldn't be needed for read operations. + QuotaManager::Get()->OpenDirectory(mPersistence, + mGroup, + mOrigin, + mIsApp, + quota::Client::ASMJS, + /* aExclusive */ true, + this); +} + nsresult ParentRunnable::ReadMetadata() { AssertIsOnIOThread(); MOZ_ASSERT(mState == eReadyToReadMetadata); QuotaManager* qm = QuotaManager::Get(); MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread"); @@ -898,22 +890,17 @@ void ParentRunnable::FinishOnOwningThread() { AssertIsOnOwningThread(); // Per FileDescriptorHolder::Finish()'s comment, call before // releasing the directory lock. FileDescriptorHolder::Finish(); - if (mDirectoryLock) { - RefPtr<UnlockDirectoryRunnable> runnable = - new UnlockDirectoryRunnable(mDirectoryLock.forget()); - - NS_DispatchToMainThread(runnable); - } + mDirectoryLock = nullptr; } NS_IMETHODIMP ParentRunnable::Run() { nsresult rv; // All success/failure paths must eventually call Finish() to avoid leaving @@ -923,30 +910,54 @@ ParentRunnable::Run() MOZ_ASSERT(NS_IsMainThread()); rv = InitOnMainThread(); if (NS_FAILED(rv)) { FailOnNonOwningThread(); return NS_OK; } - mState = eWaitingToOpenMetadata; + mState = eWaitingToFinishInit; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL))); + + return NS_OK; + } + + case eWaitingToFinishInit: { + AssertIsOnOwningThread(); + + if (QuotaManager::IsShuttingDown()) { + Fail(); + return NS_OK; + } - // XXX The exclusive lock shouldn't be needed for read operations. - QuotaManager::Get()->OpenDirectory(mPersistence, - mGroup, - mOrigin, - mIsApp, - quota::Client::ASMJS, - /* aExclusive */ true, - this); + if (QuotaManager::Get()) { + OpenDirectory(); + return NS_OK; + } + + mState = eWaitingToOpenDirectory; + QuotaManager::GetOrCreate(this); return NS_OK; } + case eWaitingToOpenDirectory: { + AssertIsOnOwningThread(); + + if (NS_WARN_IF(!QuotaManager::Get())) { + Fail(); + return NS_OK; + } + + OpenDirectory(); + return NS_OK; + } + case eReadyToReadMetadata: { AssertIsOnIOThread(); rv = ReadMetadata(); if (NS_FAILED(rv)) { mState = eFailedToReadMetadata; MOZ_ALWAYS_TRUE(NS_SUCCEEDED( mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL))); @@ -994,24 +1005,16 @@ ParentRunnable::Run() // Metadata is now open. if (!SendOnOpenMetadataForRead(mMetadata)) { Unused << Send__delete__(this, JS::AsmJSCache_InternalError); } return NS_OK; } - case eDispatchToMainThread: { - MOZ_ASSERT(NS_IsMainThread()); - - mState = eReadyToOpenCacheFileForRead; - DispatchToIOThread(); - return NS_OK; - } - case eReadyToOpenCacheFileForRead: { AssertIsOnIOThread(); MOZ_ASSERT(mOpenMode == eOpenForRead); rv = OpenCacheFileForRead(); if (NS_FAILED(rv)) { FailOnNonOwningThread(); return NS_OK; @@ -1059,34 +1062,34 @@ ParentRunnable::Run() MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Corrupt state"); return NS_OK; } void ParentRunnable::DirectoryLockAcquired(DirectoryLock* aLock) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(mState == eWaitingToOpenMetadata); MOZ_ASSERT(!mDirectoryLock); mDirectoryLock = aLock; mState = eReadyToReadMetadata; DispatchToIOThread(); } void ParentRunnable::DirectoryLockFailed() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(mState == eWaitingToOpenMetadata); MOZ_ASSERT(!mDirectoryLock); - FailOnNonOwningThread(); + Fail(); } NS_IMPL_ISUPPORTS_INHERITED0(ParentRunnable, FileDescriptorHolder) bool FindHashMatch(const Metadata& aMetadata, const ReadParams& aReadParams, unsigned* aModuleIndex) { @@ -1807,17 +1810,21 @@ public: AbortOperations(const nsACString& aOrigin) override { } virtual void AbortOperationsForProcess(ContentParentId aContentParentId) override { } virtual void - PerformIdleMaintenance() override + StartIdleMaintenance() override + { } + + virtual void + StopIdleMaintenance() override { } virtual void ShutdownWorkThreads() override { } private: nsAutoRefCnt mRefCnt;
--- a/dom/base/ScriptSettings.cpp +++ b/dom/base/ScriptSettings.cpp @@ -300,24 +300,23 @@ FindJSContext(nsIGlobalObject* aGlobalOb } return cx; } AutoJSAPI::AutoJSAPI() : mCx(nullptr) , mOwnErrorReporting(false) , mOldAutoJSAPIOwnsErrorReporting(false) + , mIsMainThread(false) // For lack of anything better { } AutoJSAPI::~AutoJSAPI() { if (mOwnErrorReporting) { - MOZ_ASSERT(NS_IsMainThread(), "See corresponding assertion in TakeOwnershipOfErrorReporting()"); - ReportException(); // We need to do this _after_ processing the existing exception, because the // JS engine can throw while doing that, and uses this bit to determine what // to do in that case: squelch the exception if the bit is set, otherwise // call the error reporter. Calling WarningOnlyErrorReporter with a // non-warning will assert, so we need to make sure we do the former. JS::ContextOptionsRef(cx()).setAutoJSAPIOwnsErrorReporting(mOldAutoJSAPIOwnsErrorReporting); @@ -327,17 +326,20 @@ AutoJSAPI::~AutoJSAPI() JS_SetErrorReporter(JS_GetRuntime(cx()), mOldErrorReporter.value()); } } void AutoJSAPI::InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread) { MOZ_ASSERT(aCx); + MOZ_ASSERT(aIsMainThread == NS_IsMainThread()); + mCx = aCx; + mIsMainThread = aIsMainThread; if (aIsMainThread) { // This Rooted<> is necessary only as long as AutoCxPusher::AutoCxPusher // can GC, which is only possible because XPCJSContextStack::Push calls // nsIPrincipal.Equals. Once that is removed, the Rooted<> will no longer // be necessary. JS::Rooted<JSObject*> global(JS_GetRuntime(aCx), aGlobal); mCxPusher.emplace(mCx); mAutoNullableCompartment.emplace(mCx, global); @@ -352,21 +354,22 @@ AutoJSAPI::InitInternal(JSObject* aGloba } } AutoJSAPI::AutoJSAPI(nsIGlobalObject* aGlobalObject, bool aIsMainThread, JSContext* aCx) : mOwnErrorReporting(false) , mOldAutoJSAPIOwnsErrorReporting(false) + , mIsMainThread(aIsMainThread) { MOZ_ASSERT(aGlobalObject); MOZ_ASSERT(aGlobalObject->GetGlobalJSObject(), "Must have a JS global"); MOZ_ASSERT(aCx); - MOZ_ASSERT_IF(aIsMainThread, NS_IsMainThread()); + MOZ_ASSERT(aIsMainThread == NS_IsMainThread()); InitInternal(aGlobalObject->GetGlobalJSObject(), aCx, aIsMainThread); } void AutoJSAPI::Init() { MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once"); @@ -455,35 +458,42 @@ AutoJSAPI::InitWithLegacyErrorReporting( // reports to the JSErrorReporter as soon as they are generated. These go // directly to the console, so we can handle them easily here. // // Eventually, SpiderMonkey will have a special-purpose callback for warnings // only. void WarningOnlyErrorReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aRep) { + MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(JSREPORT_IS_WARNING(aRep->flags)); + RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport(); nsPIDOMWindow* win = xpc::CurrentWindowOrNull(aCx); xpcReport->Init(aRep, aMessage, nsContentUtils::IsCallerChrome(), win ? win->WindowID() : 0); xpcReport->LogToConsole(); } void AutoJSAPI::TakeOwnershipOfErrorReporting() { - MOZ_ASSERT(NS_IsMainThread(), "Can't own error reporting off-main-thread yet"); MOZ_ASSERT(!mOwnErrorReporting); mOwnErrorReporting = true; JSRuntime *rt = JS_GetRuntime(cx()); mOldAutoJSAPIOwnsErrorReporting = JS::ContextOptionsRef(cx()).autoJSAPIOwnsErrorReporting(); JS::ContextOptionsRef(cx()).setAutoJSAPIOwnsErrorReporting(true); - JS_SetErrorReporter(rt, WarningOnlyErrorReporter); + // Workers have their own error reporting mechanism which deals with warnings + // as well, so don't change the worker error reporter for now. Once we switch + // all of workers to TakeOwnershipOfErrorReporting(), we will just make the + // default worker error reporter assert that it only sees warnings. + if (mIsMainThread) { + JS_SetErrorReporter(rt, WarningOnlyErrorReporter); + } } void AutoJSAPI::ReportException() { MOZ_ASSERT(OwnsErrorReporting(), "This is not our exception to report!"); if (!HasException()) { return; @@ -493,38 +503,55 @@ AutoJSAPI::ReportException() // compartment when the destructor is called. However, the JS engine // requires us to be in a compartment when we fetch the pending exception. // In this case, we enter the privileged junk scope and don't dispatch any // error events. JS::Rooted<JSObject*> errorGlobal(cx(), JS::CurrentGlobalOrNull(cx())); if (!errorGlobal) errorGlobal = xpc::PrivilegedJunkScope(); JSAutoCompartment ac(cx(), errorGlobal); - nsCOMPtr<nsPIDOMWindow> win = xpc::WindowGlobalOrNull(errorGlobal); JS::Rooted<JS::Value> exn(cx()); js::ErrorReport jsReport(cx()); if (StealException(&exn) && jsReport.init(cx(), exn)) { - RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport(); - xpcReport->Init(jsReport.report(), jsReport.message(), - nsContentUtils::IsCallerChrome(), - win ? win->WindowID() : 0); - if (win) { - DispatchScriptErrorEvent(win, JS_GetRuntime(cx()), xpcReport, exn); + if (mIsMainThread) { + RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport(); + nsCOMPtr<nsPIDOMWindow> win = xpc::WindowGlobalOrNull(errorGlobal); + xpcReport->Init(jsReport.report(), jsReport.message(), + nsContentUtils::IsCallerChrome(), + win ? win->WindowID() : 0); + if (win) { + DispatchScriptErrorEvent(win, JS_GetRuntime(cx()), xpcReport, exn); + } else { + xpcReport->LogToConsole(); + } } else { - xpcReport->LogToConsole(); + // On a worker, we just use the worker error reporting mechanism and don't + // bother with xpc::ErrorReport. This will ensure that all the right + // events (which are a lot more complicated than in the window case) get + // fired. + workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(worker); + MOZ_ASSERT(worker->GetJSContext() == cx()); + // Before invoking ReportError, put the exception back on the context, + // because it may want to put it in its error events and has no other way + // to get hold of it. After we invoke ReportError, clear the exception on + // cx(), just in case ReportError didn't. + JS_SetPendingException(cx(), exn); + worker->ReportError(cx(), jsReport.message(), jsReport.report()); + ClearException(); } } else { NS_WARNING("OOMed while acquiring uncaught exception from JSAPI"); } } bool AutoJSAPI::StealException(JS::MutableHandle<JS::Value> aVal) { - MOZ_ASSERT(CxPusherIsStackTop()); + MOZ_ASSERT_IF(mIsMainThread, CxPusherIsStackTop()); MOZ_ASSERT(HasException()); MOZ_ASSERT(js::GetContextCompartment(cx())); if (!JS_GetPendingException(cx(), aVal)) { return false; } JS_ClearPendingException(cx()); return true; }
--- a/dom/base/ScriptSettings.h +++ b/dom/base/ScriptSettings.h @@ -257,47 +257,47 @@ public: bool Init(nsGlobalWindow* aWindow); bool Init(nsGlobalWindow* aWindow, JSContext* aCx); bool InitWithLegacyErrorReporting(nsPIDOMWindow* aWindow); bool InitWithLegacyErrorReporting(nsGlobalWindow* aWindow); JSContext* cx() const { MOZ_ASSERT(mCx, "Must call Init before using an AutoJSAPI"); - MOZ_ASSERT_IF(NS_IsMainThread(), CxPusherIsStackTop()); + MOZ_ASSERT_IF(mIsMainThread, CxPusherIsStackTop()); return mCx; } bool CxPusherIsStackTop() const { return mCxPusher->IsStackTop(); } // We're moving towards a world where the AutoJSAPI always handles // exceptions that bubble up from the JS engine. In order to make this // process incremental, we allow consumers to opt-in to the new behavior // while keeping the old behavior as the default. void TakeOwnershipOfErrorReporting(); bool OwnsErrorReporting() { return mOwnErrorReporting; } // If HasException, report it. Otherwise, a no-op. This must be // called only if OwnsErrorReporting(). void ReportException(); bool HasException() const { - MOZ_ASSERT(CxPusherIsStackTop()); + MOZ_ASSERT_IF(NS_IsMainThread(), CxPusherIsStackTop()); return JS_IsExceptionPending(cx()); }; // Transfers ownership of the current exception from the JS engine to the // caller. Callers must ensure that HasException() is true, and that cx() // is in a non-null compartment. // // Note that this fails if and only if we OOM while wrapping the exception // into the current compartment. bool StealException(JS::MutableHandle<JS::Value> aVal); void ClearException() { - MOZ_ASSERT(CxPusherIsStackTop()); + MOZ_ASSERT_IF(NS_IsMainThread(), CxPusherIsStackTop()); JS_ClearPendingException(cx()); } protected: // Protected constructor, allowing subclasses to specify a particular cx to // be used. This constructor initialises the AutoJSAPI, so Init must NOT be // called on subclasses that use this. // If aGlobalObject, its associated JS global or aCx are null this will cause @@ -307,16 +307,18 @@ protected: private: mozilla::Maybe<danger::AutoCxPusher> mCxPusher; mozilla::Maybe<JSAutoNullableCompartment> mAutoNullableCompartment; JSContext *mCx; // Track state between the old and new error reporting modes. bool mOwnErrorReporting; bool mOldAutoJSAPIOwnsErrorReporting; + // Whether we're mainthread or not; set when we're initialized. + bool mIsMainThread; Maybe<JSErrorReporter> mOldErrorReporter; void InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread); AutoJSAPI(const AutoJSAPI&) = delete; AutoJSAPI& operator= (const AutoJSAPI&) = delete; };
--- a/dom/base/URL.cpp +++ b/dom/base/URL.cpp @@ -73,33 +73,33 @@ URL::Constructor(const GlobalObject& aGl /* static */ already_AddRefed<URL> URL::Constructor(nsISupports* aParent, const nsAString& aUrl, const nsAString& aBase, ErrorResult& aRv) { nsCOMPtr<nsIURI> baseUri; nsresult rv = NS_NewURI(getter_AddRefs(baseUri), aBase, nullptr, nullptr, nsContentUtils::GetIOService()); if (NS_WARN_IF(NS_FAILED(rv))) { - aRv.ThrowTypeError<MSG_INVALID_URL>(&aBase); + aRv.ThrowTypeError<MSG_INVALID_URL>(aBase); return nullptr; } return Constructor(aParent, aUrl, baseUri, aRv); } /* static */ already_AddRefed<URL> URL::Constructor(nsISupports* aParent, const nsAString& aUrl, nsIURI* aBase, ErrorResult& aRv) { nsCOMPtr<nsIURI> uri; nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl, nullptr, aBase, nsContentUtils::GetIOService()); if (NS_WARN_IF(NS_FAILED(rv))) { - aRv.ThrowTypeError<MSG_INVALID_URL>(&aUrl); + aRv.ThrowTypeError<MSG_INVALID_URL>(aUrl); return nullptr; } RefPtr<URL> url = new URL(aParent, uri.forget()); return url.forget(); } void @@ -224,18 +224,17 @@ URL::SetHref(const nsAString& aHref, Err if (NS_FAILED(rv)) { aRv.Throw(rv); return; } nsCOMPtr<nsIURI> uri; rv = ioService->NewURI(href, nullptr, nullptr, getter_AddRefs(uri)); if (NS_FAILED(rv)) { - nsAutoString label(aHref); - aRv.ThrowTypeError<MSG_INVALID_URL>(&label); + aRv.ThrowTypeError<MSG_INVALID_URL>(aHref); return; } mURI = uri; UpdateURLSearchParams(); } void
--- a/dom/base/WindowNamedPropertiesHandler.cpp +++ b/dom/base/WindowNamedPropertiesHandler.cpp @@ -153,17 +153,17 @@ bool WindowNamedPropertiesHandler::defineProperty(JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId, JS::Handle<JSPropertyDescriptor> aDesc, JS::ObjectOpResult &result) const { ErrorResult rv; rv.ThrowTypeError<MSG_DEFINEPROPERTY_ON_GSP>(); - rv.ReportErrorWithMessage(aCx); + rv.MaybeSetPendingException(aCx); return false; } bool WindowNamedPropertiesHandler::ownPropNames(JSContext* aCx, JS::Handle<JSObject*> aProxy, unsigned flags, JS::AutoIdVector& aProps) const
--- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -5110,29 +5110,27 @@ nsDocument::DocumentStatesChanged(EventS mGotDocumentState &= ~aStateMask; mDocumentState &= ~aStateMask; NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask)); } void nsDocument::StyleRuleChanged(CSSStyleSheet* aSheet, - css::Rule* aOldStyleRule, - css::Rule* aNewStyleRule) + css::Rule* aStyleRule) { NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged, (this, aSheet, - aOldStyleRule, aNewStyleRule)); + aStyleRule)); if (StyleSheetChangeEventsEnabled()) { DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent, "StyleRuleChanged", mRule, - aNewStyleRule ? aNewStyleRule->GetDOMRule() - : nullptr); + aStyleRule ? aStyleRule->GetDOMRule() : nullptr); } } void nsDocument::StyleRuleAdded(CSSStyleSheet* aSheet, css::Rule* aStyleRule) { NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded,
--- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -879,18 +879,17 @@ public: virtual void ContentStateChanged(nsIContent* aContent, mozilla::EventStates aStateMask) override; virtual void DocumentStatesChanged( mozilla::EventStates aStateMask) override; virtual void StyleRuleChanged(mozilla::CSSStyleSheet* aStyleSheet, - mozilla::css::Rule* aOldStyleRule, - mozilla::css::Rule* aNewStyleRule) override; + mozilla::css::Rule* aStyleRule) override; virtual void StyleRuleAdded(mozilla::CSSStyleSheet* aStyleSheet, mozilla::css::Rule* aStyleRule) override; virtual void StyleRuleRemoved(mozilla::CSSStyleSheet* aStyleSheet, mozilla::css::Rule* aStyleRule) override; virtual void FlushPendingNotifications(mozFlushType aType) override; virtual void FlushExternalResources(mozFlushType aType) override; virtual void SetXMLDeclaration(const char16_t *aVersion,
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -6133,20 +6133,22 @@ nsGlobalWindow::FinishFullscreenChange(b if (!pmService) { return; } ErrorResult rv; mWakeLock = pmService->NewWakeLock(NS_LITERAL_STRING("DOM_Fullscreen"), this, rv); NS_WARN_IF_FALSE(!rv.Failed(), "Failed to lock the wakelock"); + rv.SuppressException(); } else if (mWakeLock && !mFullScreen) { ErrorResult rv; mWakeLock->Unlock(rv); mWakeLock = nullptr; + rv.SuppressException(); } } bool nsGlobalWindow::FullScreen() const { MOZ_ASSERT(IsOuterWindow()); @@ -11759,16 +11761,17 @@ nsGlobalWindow::RunTimeoutHandler(nsTime nsJSUtils::EvaluateString(entryScript.cx(), nsDependentString(script), global, options); } else { // Hold strong ref to ourselves while we call the callback. nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow *>(this)); ErrorResult ignored; JS::Rooted<JS::Value> ignoredVal(CycleCollectedJSRuntime::Get()->Runtime()); callback->Call(me, handler->GetArgs(), &ignoredVal, ignored, reason); + ignored.SuppressException(); } // We ignore any failures from calling EvaluateString() on the context or // Call() on a Function here since we're in a loop // where we're likely to be running timeouts whose OS timers // didn't fire in time and we don't want to not fire those timers // now just because execution of one timer failed. We can't // propagate the error to anyone who cares about it from this
--- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -1126,17 +1126,20 @@ public: mozilla::ErrorResult& aError); void GetContent(JSContext* aCx, JS::MutableHandle<JSObject*> aRetval, mozilla::ErrorResult& aError); already_AddRefed<nsIDOMWindow> GetContent() { MOZ_ASSERT(IsOuterWindow()); mozilla::ErrorResult ignored; - return GetContentInternal(ignored, /* aUnprivilegedCaller = */ false); + nsCOMPtr<nsIDOMWindow> win = + GetContentInternal(ignored, /* aUnprivilegedCaller = */ false); + ignored.SuppressException(); + return win.forget(); } void Get_content(JSContext* aCx, JS::MutableHandle<JSObject*> aRetval, mozilla::ErrorResult& aError) { if (mDoc) { mDoc->WarnOnceAbout(nsIDocument::eWindow_Content);
--- a/dom/base/nsIConsoleReportCollector.h +++ b/dom/base/nsIConsoleReportCollector.h @@ -44,29 +44,29 @@ public: virtual void AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory, nsContentUtils::PropertiesFile aPropertiesFile, const nsACString& aSourceFileURI, uint32_t aLineNumber, uint32_t aColumnNumber, const nsACString& aMessageName, const nsTArray<nsString>& aStringParams) = 0; // A version of AddConsoleReport() that accepts the message parameters - // as variable nsString arguments. Note, the parameters must be exactly - // nsString and not another string class. All other args the same as - // AddConsoleReport(). + // as variable nsString arguments (or really, any sort of const nsAString). + // All other args the same as AddConsoleReport(). template<typename... Params> void AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory, nsContentUtils::PropertiesFile aPropertiesFile, const nsACString& aSourceFileURI, uint32_t aLineNumber, uint32_t aColumnNumber, const nsACString& aMessageName, - Params... aParams) + Params&&... aParams) { nsTArray<nsString> params; - mozilla::dom::StringArrayAppender::Append(params, sizeof...(Params), aParams...); + mozilla::dom::StringArrayAppender::Append(params, sizeof...(Params), + mozilla::Forward<Params>(aParams)...); AddConsoleReport(aErrorFlags, aCategory, aPropertiesFile, aSourceFileURI, aLineNumber, aColumnNumber, aMessageName, params); } // Flush all pending reports to the console. Main thread only. // // aDocument An optional document representing where to flush the // reports. If provided, then the corresponding window's
--- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -150,18 +150,18 @@ template<typename> class Sequence; template<typename, typename> class CallbackObjectHolder; typedef CallbackObjectHolder<NodeFilter, nsIDOMNodeFilter> NodeFilterHolder; } // namespace dom } // namespace mozilla #define NS_IDOCUMENT_IID \ -{ 0xecc9e376, 0x6c31, 0x4f04, \ - { 0xbe, 0xde, 0xd6, 0x27, 0x61, 0xd7, 0x00, 0x84 } } +{ 0x13011a82, 0x46cd, 0x4c33, \ + { 0x9d, 0x4e, 0x31, 0x41, 0xbb, 0x3f, 0x18, 0xe9 } } // Enum for requesting a particular type of document when creating a doc enum DocumentFlavor { DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true DocumentFlavorSVG, // SVGDocument DocumentFlavorPlain, // Just a Document }; @@ -1285,18 +1285,17 @@ public: // Notify that a document state has changed. // This should only be called by callers whose state is also reflected in the // implementation of nsDocument::GetDocumentState. virtual void DocumentStatesChanged(mozilla::EventStates aStateMask) = 0; // Observation hooks for style data to propagate notifications // to document observers virtual void StyleRuleChanged(mozilla::CSSStyleSheet* aStyleSheet, - mozilla::css::Rule* aOldStyleRule, - mozilla::css::Rule* aNewStyleRule) = 0; + mozilla::css::Rule* aStyleRule) = 0; virtual void StyleRuleAdded(mozilla::CSSStyleSheet* aStyleSheet, mozilla::css::Rule* aStyleRule) = 0; virtual void StyleRuleRemoved(mozilla::CSSStyleSheet* aStyleSheet, mozilla::css::Rule* aStyleRule) = 0; /** * Flush notifications for this document and its parent documents * (since those may affect the layout of this one).
--- a/dom/base/nsIDocumentObserver.h +++ b/dom/base/nsIDocumentObserver.h @@ -16,18 +16,18 @@ class nsIDocument; namespace mozilla { class CSSStyleSheet; namespace css { class Rule; } // namespace css } // namespace mozilla #define NS_IDOCUMENT_OBSERVER_IID \ -{ 0x21c8ad67, 0x3a7d, 0x4881, \ - { 0xa5, 0x43, 0xcb, 0xa9, 0xbb, 0xe4, 0x9e, 0x39 } } +{ 0x71041fa3, 0x6dd7, 0x4cde, \ + { 0xbb, 0x76, 0xae, 0xcc, 0x69, 0xe1, 0x75, 0x78 } } typedef uint32_t nsUpdateType; #define UPDATE_CONTENT_MODEL 0x00000001 #define UPDATE_STYLE 0x00000002 #define UPDATE_ALL (UPDATE_CONTENT_MODEL | UPDATE_STYLE) // Document observer interface @@ -138,36 +138,23 @@ public: /** * A StyleRule has just been modified within a style sheet. * This method is called automatically when the rule gets * modified. The style sheet passes this notification to * the document. The notification is passed on to all of * the document observers. * - * Since nsIStyleRule objects are immutable, there is a new object - * replacing the old one. However, the use of this method (rather - * than StyleRuleAdded and StyleRuleRemoved) implies that the new rule - * matches the same elements and has the same priority (weight, - * origin, specificity) as the old one. (However, if it is a CSS - * style rule, there may be a change in whether it has an important - * rule.) - * * @param aDocument The document being observed * @param aStyleSheet the StyleSheet that contians the rule - * @param aOldStyleRule The rule being removed. This rule may not be - * fully valid anymore -- however, it can still - * be used for pointer comparison and - * |QueryInterface|. - * @param aNewStyleRule The rule being added. + * @param aStyleRule The rule being changed. */ virtual void StyleRuleChanged(nsIDocument *aDocument, mozilla::CSSStyleSheet* aStyleSheet, - mozilla::css::Rule* aOldStyleRule, - mozilla::css::Rule* aNewStyleRule) = 0; + mozilla::css::Rule* aStyleRule) = 0; /** * A StyleRule has just been added to a style sheet. * This method is called automatically when the rule gets * added to the sheet. The style sheet passes this * notification to the document. The notification is passed on * to all of the document observers. * @@ -233,18 +220,17 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumen virtual void StyleSheetApplicableStateChanged( \ nsIDocument* aDocument, \ mozilla::CSSStyleSheet* aStyleSheet, \ bool aApplicable) override; #define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULECHANGED \ virtual void StyleRuleChanged(nsIDocument* aDocument, \ mozilla::CSSStyleSheet* aStyleSheet, \ - mozilla::css::Rule* aOldStyleRule, \ - mozilla::css::Rule* aNewStyleRule) override; + mozilla::css::Rule* aStyleRule) override; #define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEADDED \ virtual void StyleRuleAdded(nsIDocument* aDocument, \ mozilla::CSSStyleSheet* aStyleSheet, \ mozilla::css::Rule* aStyleRule) override; #define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEREMOVED \ virtual void StyleRuleRemoved(nsIDocument* aDocument, \ @@ -322,18 +308,17 @@ void _class::StyleSheetApplicableStateChanged(nsIDocument* aDocument, \ mozilla::CSSStyleSheet* aStyleSheet,\ bool aApplicable) \ { \ } \ void \ _class::StyleRuleChanged(nsIDocument* aDocument, \ mozilla::CSSStyleSheet* aStyleSheet, \ - mozilla::css::Rule* aOldStyleRule, \ - mozilla::css::Rule* aNewStyleRule) \ + mozilla::css::Rule* aStyleRule) \ { \ } \ void \ _class::StyleRuleAdded(nsIDocument* aDocument, \ mozilla::CSSStyleSheet* aStyleSheet, \ mozilla::css::Rule* aStyleRule) \ { \ } \
--- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -267,16 +267,18 @@ skip-if = buildapp == 'b2g' # Requires w [test_anonymousContent_style_csp.html] [test_applet_alternate_content.html] [test_appname_override.html] [test_async_setTimeout_stack.html] [test_async_setTimeout_stack_across_globals.html] [test_audioWindowUtils.html] [test_audioNotification.html] skip-if = buildapp == 'mulet' +[test_audioNotificationStream.html] +skip-if = buildapp == 'mulet' [test_audioNotificationStopOnNavigation.html] skip-if = buildapp == 'mulet' [test_audioNotificationWithEarlyPlay.html] skip-if = buildapp == 'mulet' [test_bug1091883.html] [test_bug116083.html] [test_bug793311.html] [test_bug913761.html]
copy from dom/base/test/test_audioNotification.html copy to dom/base/test/test_audioNotificationStream.html --- a/dom/base/test/test_audioNotification.html +++ b/dom/base/test/test_audioNotificationStream.html @@ -22,17 +22,17 @@ var observer = { runTest(); } }; var observerService = SpecialPowers.Cc["@mozilla.org/observer-service;1"] .getService(SpecialPowers.Ci.nsIObserverService); var audio = new Audio(); -audio.src = "audio.ogg"; +audio.srcObject = (new AudioContext()).createMediaStreamDestination().stream; var tests = [ function() { observerService.addObserver(observer, "audio-playback", false); ok(true, "Observer set"); runTest(); },
--- a/dom/bindings/BindingDeclarations.h +++ b/dom/bindings/BindingDeclarations.h @@ -134,33 +134,20 @@ public: } bool WasPassed() const { return mImpl.isSome(); } // Return InternalType here so we can work with it usefully. - InternalType& Construct() - { - mImpl.emplace(); - return *mImpl; - } - - template <class T1> - InternalType& Construct(const T1 &t1) + template<typename... Args> + InternalType& Construct(Args&&... aArgs) { - mImpl.emplace(t1); - return *mImpl; - } - - template <class T1, class T2> - InternalType& Construct(const T1 &t1, const T2 &t2) - { - mImpl.emplace(t1, t2); + mImpl.emplace(Forward<Args>(aArgs)...); return *mImpl; } void Reset() { mImpl.reset(); }
--- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -121,48 +121,16 @@ ThrowInvalidThis(JSContext* aCx, const J const ErrNum aErrorNumber, prototypes::ID aProtoId) { return ThrowInvalidThis(aCx, aArgs, aErrorNumber, NamesOfInterfacesWithProtos(aProtoId)); } bool -ThrowMethodFailed(JSContext* cx, ErrorResult& rv) -{ - if (rv.IsUncatchableException()) { - // Nuke any existing exception on aCx, to make sure we're uncatchable. - JS_ClearPendingException(cx); - // Don't do any reporting. Just return false, to create an - // uncatchable exception. - return false; - } - if (rv.IsJSContextException()) { - // Whatever we need to throw is on the JSContext already. We - // can't assert that there is a pending exception on it, though, - // because in the uncatchable exception case there won't be one. - return false; - } - if (rv.IsErrorWithMessage()) { - rv.ReportErrorWithMessage(cx); - return false; - } - if (rv.IsJSException()) { - rv.ReportJSException(cx); - return false; - } - if (rv.IsDOMException()) { - rv.ReportDOMException(cx); - return false; - } - rv.ReportGenericError(cx); - return false; -} - -bool ThrowNoSetterArg(JSContext* aCx, prototypes::ID aProtoId) { nsPrintfCString errorMessage("%s attribute setter", NamesOfInterfacesWithProtos(aProtoId)); return ThrowErrorMessage(aCx, MSG_MISSING_ARGUMENTS, errorMessage.get()); } } // namespace dom @@ -217,35 +185,36 @@ ErrorResult::DeserializeMessage(const IP mMessage = readMessage.forget(); #ifdef DEBUG mUnionState = HasMessage; #endif // DEBUG return true; } void -ErrorResult::ReportErrorWithMessage(JSContext* aCx) +ErrorResult::SetPendingExceptionWithMessage(JSContext* aCx) { - MOZ_ASSERT(mMessage, "ReportErrorWithMessage() can be called only once"); + MOZ_ASSERT(mMessage, "SetPendingExceptionWithMessage() can be called only once"); MOZ_ASSERT(mUnionState == HasMessage); Message* message = mMessage; MOZ_RELEASE_ASSERT(message->HasCorrectNumberOfArguments()); const uint32_t argCount = message->mArgs.Length(); const char16_t* args[JS::MaxNumErrorArguments + 1]; for (uint32_t i = 0; i < argCount; ++i) { args[i] = message->mArgs.ElementAt(i).get(); } args[argCount] = nullptr; JS_ReportErrorNumberUCArray(aCx, dom::GetErrorMessage, nullptr, static_cast<const unsigned>(message->mErrorNumber), argCount > 0 ? args : nullptr); ClearMessage(); + mResult = NS_OK; } void ErrorResult::ClearMessage() { MOZ_ASSERT(IsErrorWithMessage()); delete mMessage; mMessage = nullptr; @@ -275,50 +244,31 @@ ErrorResult::ThrowJSException(JSContext* mResult = NS_ERROR_DOM_JS_EXCEPTION; #ifdef DEBUG mUnionState = HasJSException; #endif // DEBUG } } void -ErrorResult::ReportJSException(JSContext* cx) +ErrorResult::SetPendingJSException(JSContext* cx) { MOZ_ASSERT(!mMightHaveUnreportedJSException, "Why didn't you tell us you planned to handle JS exceptions?"); MOZ_ASSERT(mUnionState == HasJSException); JS::Rooted<JS::Value> exception(cx, mJSException); if (JS_WrapValue(cx, &exception)) { JS_SetPendingException(cx, exception); } mJSException = exception; // If JS_WrapValue failed, not much we can do about it... No matter // what, go ahead and unroot mJSException. js::RemoveRawValueRoot(cx, &mJSException); - // We no longer have a useful exception but we do want to signal that an error - // occured. - mResult = NS_ERROR_FAILURE; -#ifdef DEBUG - mUnionState = HasNothing; -#endif // DEBUG -} - -void -ErrorResult::StealJSException(JSContext* cx, - JS::MutableHandle<JS::Value> value) -{ - MOZ_ASSERT(!mMightHaveUnreportedJSException, - "Must call WouldReportJSException unconditionally in all codepaths that might call StealJSException"); - MOZ_ASSERT(IsJSException(), "No exception to steal"); - MOZ_ASSERT(mUnionState == HasJSException); - - value.set(mJSException); - js::RemoveRawValueRoot(cx, &mJSException); mResult = NS_OK; #ifdef DEBUG mUnionState = HasNothing; #endif // DEBUG } struct ErrorResult::DOMExceptionInfo { DOMExceptionInfo(nsresult rv, const nsACString& message) @@ -368,24 +318,26 @@ ErrorResult::ThrowDOMException(nsresult mResult = NS_ERROR_DOM_DOMEXCEPTION; mDOMExceptionInfo = new DOMExceptionInfo(rv, message); #ifdef DEBUG mUnionState = HasDOMExceptionInfo; #endif } void -ErrorResult::ReportDOMException(JSContext* cx) +ErrorResult::SetPendingDOMException(JSContext* cx) { - MOZ_ASSERT(mDOMExceptionInfo, "ReportDOMException() can be called only once"); + MOZ_ASSERT(mDOMExceptionInfo, + "SetPendingDOMException() can be called only once"); MOZ_ASSERT(mUnionState == HasDOMExceptionInfo); dom::Throw(cx, mDOMExceptionInfo->mRv, mDOMExceptionInfo->mMessage); ClearDOMExceptionInfo(); + mResult = NS_OK; } void ErrorResult::ClearDOMExceptionInfo() { MOZ_ASSERT(IsDOMException()); MOZ_ASSERT(mUnionState == HasDOMExceptionInfo || !mDOMExceptionInfo); delete mDOMExceptionInfo; @@ -409,22 +361,23 @@ ErrorResult::ClearUnionData() } else if (IsErrorWithMessage()) { ClearMessage(); } else if (IsDOMException()) { ClearDOMExceptionInfo(); } } void -ErrorResult::ReportGenericError(JSContext* cx) +ErrorResult::SetPendingGenericErrorException(JSContext* cx) { MOZ_ASSERT(!IsErrorWithMessage()); MOZ_ASSERT(!IsJSException()); MOZ_ASSERT(!IsDOMException()); dom::Throw(cx, ErrorCode()); + mResult = NS_OK; } ErrorResult& ErrorResult::operator=(ErrorResult&& aRHS) { // Clear out any union members we may have right now, before we // start writing to it. ClearUnionData(); @@ -503,16 +456,49 @@ ErrorResult::SuppressException() { WouldReportJSException(); ClearUnionData(); // We don't use AssignErrorCode, because we want to override existing error // states, which AssignErrorCode is not allowed to do. mResult = NS_OK; } +void +ErrorResult::SetPendingException(JSContext* cx) +{ + if (IsUncatchableException()) { + // Nuke any existing exception on cx, to make sure we're uncatchable. + JS_ClearPendingException(cx); + // Don't do any reporting. Just return, to create an + // uncatchable exception. + mResult = NS_OK; + return; + } + if (IsJSContextException()) { + // Whatever we need to throw is on the JSContext already. We + // can't assert that there is a pending exception on it, though, + // because in the uncatchable exception case there won't be one. + mResult = NS_OK; + return; + } + if (IsErrorWithMessage()) { + SetPendingExceptionWithMessage(cx); + return; + } + if (IsJSException()) { + SetPendingJSException(cx); + return; + } + if (IsDOMException()) { + SetPendingDOMException(cx); + return; + } + SetPendingGenericErrorException(cx); +} + namespace dom { bool DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj, const ConstantSpec* cs) { JS::Rooted<JS::Value> value(cx); for (; cs->name; ++cs) { @@ -2772,20 +2758,20 @@ ConvertExceptionToPromise(JSContext* cx, // ending up in this code, that means the callee threw an uncatchable // exception. Just propagate that out as-is. return false; } JS_ClearPendingException(cx); ErrorResult rv; RefPtr<Promise> promise = Promise::Reject(global, exn, rv); - if (rv.Failed()) { - // We just give up. Make sure to not leak memory on the - // ErrorResult, but then just put the original exception back. - ThrowMethodFailed(cx, rv); + if (rv.MaybeSetPendingException(cx)) { + // We just give up. We put the exception from the ErrorResult on + // the JSContext just to make sure to not leak memory on the + // ErrorResult, but now just put the original exception back. JS_SetPendingException(cx, exn); return false; } return GetOrCreateDOMReflector(cx, promise, rval); } /* static */
--- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -87,19 +87,16 @@ ThrowInvalidThis(JSContext* aCx, const J const ErrNum aErrorNumber, const char* aInterfaceName); bool ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, const ErrNum aErrorNumber, prototypes::ID aProtoId); -bool -ThrowMethodFailed(JSContext* cx, ErrorResult& rv); - // Returns true if the JSClass is used for DOM objects. inline bool IsDOMClass(const JSClass* clasp) { return clasp->flags & JSCLASS_IS_DOMJSCLASS; } inline bool
--- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1658,21 +1658,17 @@ class CGClassConstructor(CGAbstractStati else: ctorName = self.descriptor.interface.identifier.name preamble = fill( """ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); JS::Rooted<JSObject*> obj(cx, &args.callee()); $*{chromeOnlyCheck} - bool mayInvoke = args.isConstructing(); - #ifdef RELEASE_BUILD - mayInvoke = mayInvoke || nsContentUtils::ThreadsafeIsCallerChrome(); - #endif // RELEASE_BUILD - if (!mayInvoke) { + if (!args.isConstructing()) { // XXXbz wish I could get the name from the callee instead of // Adding more relocations return ThrowConstructorWithoutNew(cx, "${ctorName}"); } JS::Rooted<JSObject*> desiredProto(cx); if (!GetDesiredProto(cx, args, &desiredProto)) { return false; } @@ -1723,19 +1719,17 @@ class CGConstructNavigatorObject(CGAbstr GlobalObject global(aCx, aObj); if (global.Failed()) { return nullptr; } ErrorResult rv; JS::Rooted<JS::Value> v(aCx); { // Scope to make sure |result| goes out of scope while |v| is rooted RefPtr<mozilla::dom::${descriptorName}> result = ConstructNavigatorObjectHelper(aCx, global, rv); - rv.WouldReportJSException(); - if (rv.Failed()) { - ThrowMethodFailed(aCx, rv); + if (rv.MaybeSetPendingException(aCx)) { return nullptr; } if (!GetOrCreateDOMReflector(aCx, result, &v)) { //XXX Assertion disabled for now, see bug 991271. MOZ_ASSERT(true || JS_IsExceptionPending(aCx)); return nullptr; } } @@ -5096,18 +5090,17 @@ def getJSToNativeConversionInfo(type, de // least give us the same behavior as if the caller just called // Promise.resolve() themselves. GlobalObject promiseGlobal(cx, JS::CurrentGlobalOrNull(cx)); if (promiseGlobal.Failed()) { $*{exceptionCode} } ErrorResult promiseRv; $${declName} = Promise::Resolve(promiseGlobal, $${val}, promiseRv); - if (promiseRv.Failed()) { - ThrowMethodFailed(cx, promiseRv); + if (promiseRv.MaybeSetPendingException(cx)) { $*{exceptionCode} } } """, exceptionCode=exceptionCode) elif not descriptor.skipGen and not descriptor.interface.isConsequential() and not descriptor.interface.isExternal(): if failureCode is not None: templateBody += str(CastableObjectUnwrapper( @@ -6269,31 +6262,24 @@ def getWrapTemplateForType(type, descrip if type.isEnum(): if type.nullable(): resultLoc = "%s.Value()" % result else: resultLoc = result conversion = fill( """ - { - // Scope for resultStr - MOZ_ASSERT(uint32_t(${result}) < ArrayLength(${strings})); - JSString* resultStr = JS_NewStringCopyN(cx, ${strings}[uint32_t(${result})].value, ${strings}[uint32_t(${result})].length); - if (!resultStr) { - $*{exceptionCode} - } - $*{setResultStr} - } + if (!ToJSValue(cx, ${result}, $${jsvalHandle})) { + $*{exceptionCode} + } + $*{successCode} """, result=resultLoc, - strings=(type.unroll().inner.identifier.name + "Values::" + - ENUM_ENTRY_VARIABLE_NAME), exceptionCode=exceptionCode, - setResultStr=setString("resultStr")) + successCode=successCode) if type.nullable(): conversion = CGIfElseWrapper( "%s.IsNull()" % result, CGGeneric(setNull()), CGGeneric(conversion)).define() return conversion, False @@ -6659,33 +6645,28 @@ def needScopeObject(returnType, argument any(typeNeedsScopeObject(a.type) for a in arguments))) class CGCallGenerator(CGThing): """ A class to generate an actual call to a C++ object. Assumes that the C++ object is stored in a variable whose name is given by the |object| argument. - errorReport should be a CGThing for an error report or None if no - error reporting is needed. + isFallible is a boolean indicating whether the call should be fallible. resultVar: If the returnType is not void, then the result of the call is stored in a C++ variable named by resultVar. The caller is responsible for declaring the result variable. If the caller doesn't care about the result value, resultVar can be omitted. """ - def __init__(self, errorReport, arguments, argsPre, returnType, + def __init__(self, isFallible, arguments, argsPre, returnType, extendedAttributes, descriptorProvider, nativeMethodName, static, object="self", argsPost=[], resultVar=None): CGThing.__init__(self) - assert errorReport is None or isinstance(errorReport, CGThing) - - isFallible = errorReport is not None - result, resultOutParam, resultRooter, resultArgs, resultConversion = \ getRetvalDeclarationForType(returnType, descriptorProvider) args = CGList([CGGeneric(arg) for arg in argsPre], ", ") for a, name in arguments: arg = CGGeneric(name) # Now constify the things that need it @@ -6770,20 +6751,22 @@ class CGCallGenerator(CGThing): assert resultOutParam is None call = CGWrapper(call, pre=resultVar + " = ") call = CGWrapper(call, post=";\n") self.cgRoot.append(call) if isFallible: self.cgRoot.prepend(CGGeneric("ErrorResult rv;\n")) - self.cgRoot.append(CGGeneric("rv.WouldReportJSException();\n")) - self.cgRoot.append(CGGeneric("if (MOZ_UNLIKELY(rv.Failed())) {\n")) - self.cgRoot.append(CGIndenter(errorReport)) - self.cgRoot.append(CGGeneric("}\n")) + self.cgRoot.append(CGGeneric(dedent( + """ + if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx))) { + return false; + } + """))) self.cgRoot.append(CGGeneric("MOZ_ASSERT(!JS_IsExceptionPending(cx));\n")) def define(self): return self.cgRoot.define() def getUnionMemberName(type): @@ -7167,17 +7150,17 @@ class CGPerSignatureCall(CGThing): idlNode.maplikeOrSetlikeOrIterable, idlNode.identifier.name)) else: cgThings.append(CGIterableMethodGenerator(descriptor, idlNode.maplikeOrSetlikeOrIterable, idlNode.identifier.name)) else: cgThings.append(CGCallGenerator( - self.getErrorReport() if self.isFallible() else None, + self.isFallible(), self.getArguments(), argsPre, returnType, self.extendedAttributes, descriptor, nativeMethodName, static, argsPost=argsPost, resultVar=resultVar)) if useCounterName: # Generate a telemetry call for when [UseCounter] is used. code = "SetDocumentAndPageUseCounter(cx, obj, eUseCounter_%s);\n" % useCounterName cgThings.append(CGGeneric(code)) @@ -7274,19 +7257,16 @@ class CGPerSignatureCall(CGThing): // And now make sure args.rval() is in the caller compartment return ${maybeWrap}(cx, args.rval()); """, wrapCode=wrapCode, postSteps=postSteps, maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type)) return wrapCode - def getErrorReport(self): - return CGGeneric('return ThrowMethodFailed(cx, rv);\n') - def define(self): return (self.cgRoot.define() + self.wrap_return_value()) class CGSwitch(CGList): """ A class to generate code for a switch statement. @@ -8245,19 +8225,18 @@ class CGEnumerateHook(CGAbstractBindingM self, descriptor, ENUMERATE_HOOK_NAME, args, getThisObj="", callArgs="") def generate_code(self): return CGGeneric(dedent(""" nsAutoTArray<nsString, 8> names; ErrorResult rv; self->GetOwnPropertyNames(cx, names, rv); - rv.WouldReportJSException(); - if (rv.Failed()) { - return ThrowMethodFailed(cx, rv); + if (rv.MaybeSetPendingException(cx)) { + return false; } bool dummy; for (uint32_t i = 0; i < names.Length(); ++i) { if (!JS_HasUCProperty(cx, obj, names[i].get(), names[i].Length(), &dummy)) { return false; } } return true; @@ -9063,21 +9042,63 @@ def getEnumValueName(value): return "_empty" nativeName = MakeNativeName(value) if nativeName == "EndGuard_": raise SyntaxError('Enum value "' + value + '" cannot be used because it' ' collides with our internal EndGuard_ value. Please' ' rename our internal EndGuard_ to something else') return nativeName +class CGEnumToJSValue(CGAbstractMethod): + def __init__(self, enum): + enumType = enum.identifier.name + self.stringsArray = enumType + "Values::" + ENUM_ENTRY_VARIABLE_NAME + CGAbstractMethod.__init__(self, None, "ToJSValue", "bool", + [Argument("JSContext*", "aCx"), + Argument(enumType, "aArgument"), + Argument("JS::MutableHandle<JS::Value>", + "aValue")]) + + def definition_body(self): + return fill( + """ + MOZ_ASSERT(uint32_t(aArgument) < ArrayLength(${strings})); + JSString* resultStr = + JS_NewStringCopyN(aCx, ${strings}[uint32_t(aArgument)].value, + ${strings}[uint32_t(aArgument)].length); + if (!resultStr) { + return false; + } + aValue.setString(resultStr); + return true; + """, + strings=self.stringsArray) + class CGEnum(CGThing): def __init__(self, enum): CGThing.__init__(self) self.enum = enum + strings = CGNamespace( + self.stringsNamespace(), + CGGeneric(declare=("extern const EnumEntry %s[%d];\n" % + (ENUM_ENTRY_VARIABLE_NAME, self.nEnumStrings())), + define=fill( + """ + extern const EnumEntry ${name}[${count}] = { + $*{entries} + { nullptr, 0 } + }; + """, + name=ENUM_ENTRY_VARIABLE_NAME, + count=self.nEnumStrings(), + entries=''.join('{"%s", %d},\n' % (val, len(val)) + for val in self.enum.values())))) + toJSValue = CGEnumToJSValue(enum) + self.cgThings = CGList([strings, toJSValue], "\n") def stringsNamespace(self): return self.enum.identifier.name + "Values" def nEnumStrings(self): return len(self.enum.values()) + 1 def declare(self): @@ -9088,32 +9109,20 @@ class CGEnum(CGThing): EndGuard_ }; """, name=self.enum.identifier.name, enums=",\n".join(map(getEnumValueName, self.enum.values())) + ",\n") strings = CGNamespace(self.stringsNamespace(), CGGeneric(declare="extern const EnumEntry %s[%d];\n" % (ENUM_ENTRY_VARIABLE_NAME, self.nEnumStrings()))) - return decl + "\n" + strings.declare() + return decl + "\n" + self.cgThings.declare() def define(self): - strings = fill( - """ - extern const EnumEntry ${name}[${count}] = { - $*{entries} - { nullptr, 0 } - }; - """, - name=ENUM_ENTRY_VARIABLE_NAME, - count=self.nEnumStrings(), - entries=''.join('{"%s", %d},\n' % (val, len(val)) - for val in self.enum.values())) - return CGNamespace(self.stringsNamespace(), - CGGeneric(define=indent(strings))).define() + return self.cgThings.define() def deps(self): return self.enum.getDeps() def getUnionAccessorSignatureType(type, descriptorProvider): """ Returns the types that are used in the getter and setter signatures for @@ -10303,19 +10312,18 @@ class CGEnumerateOwnPropertiesViaGetOwnP args, getThisObj="", callArgs="") def generate_code(self): return CGGeneric(dedent(""" nsAutoTArray<nsString, 8> names; ErrorResult rv; self->GetOwnPropertyNames(cx, names, rv); - rv.WouldReportJSException(); - if (rv.Failed()) { - return ThrowMethodFailed(cx, rv); + if (rv.MaybeSetPendingException(cx)) { + return false; } // OK to pass null as "proxy" because it's ignored if // shadowPrototypeProperties is true return AppendNamedPropertyIds(cx, nullptr, names, true, props); """)) class CGPrototypeTraitsClass(CGClass): @@ -12167,21 +12175,19 @@ class CGDictionary(CGThing): body.append(CGGeneric( "%s::operator=(aOther);\n" % self.makeClassName(self.dictionary.parent))) for m, _ in self.memberInfo: memberName = self.makeMemberName(m.identifier.name) if m.canHaveMissingValue(): memberAssign = CGGeneric(fill( """ + ${name}.Reset(); if (aOther.${name}.WasPassed()) { - ${name}.Construct(); - ${name}.Value() = aOther.${name}.Value(); - } else { - ${name}.Reset(); + ${name}.Construct(aOther.${name}.Value()); } """, name=memberName)) else: memberAssign = CGGeneric( "%s = aOther.%s;\n" % (memberName, memberName)) body.append(memberAssign) return ClassMethod( @@ -13009,16 +13015,21 @@ class CGBindingRoot(CGThing): bindingHeaders["mozilla/OwningNonNull.h"] = hasCode bindingHeaders["mozilla/dom/BindingDeclarations.h"] = ( not hasCode and enums) bindingHeaders["WrapperFactory.h"] = descriptors bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries # AutoJSAPI bindingHeaders["xpcpublic.h"] = dictionaries # xpc::UnprivilegedJunkScope + # Ensure we see our enums in the generated .cpp file, for the ToJSValue + # method body. Also ensure that we see jsapi.h. + if enums: + bindingHeaders[CGHeaders.getDeclarationFilename(enums[0])] = True + bindingHeaders["jsapi.h"] = True # For things that have [UseCounter] def descriptorRequiresTelemetry(desc): iface = desc.interface return any(m.getExtendedAttribute("UseCounter") for m in iface.members) bindingHeaders["mozilla/UseCounter.h"] = any( descriptorRequiresTelemetry(d) for d in descriptors)
--- a/dom/bindings/ErrorResult.h +++ b/dom/bindings/ErrorResult.h @@ -1,16 +1,27 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ /** * A struct for tracking exceptions that need to be thrown to JS. + * + * Conceptually, an ErrorResult represents either success or an exception in the + * process of being thrown. This means that a failing ErrorResult _must_ be + * handled in one of the following ways before coming off the stack: + * + * 1) Suppressed via SuppressException(). + * 2) Converted to a pure nsresult return value via StealNSResult(). + * 3) Converted to an actual pending exception on a JSContext via + * MaybeSetPendingException. + * 4) Converted to an exception JS::Value (probably to then reject a Promise + * with) via dom::ToJSValue. */ #ifndef mozilla_ErrorResult_h #define mozilla_ErrorResult_h #include <stdarg.h> #include "js/Value.h" @@ -56,24 +67,24 @@ ThrowErrorMessage(JSContext* aCx, const struct StringArrayAppender { static void Append(nsTArray<nsString>& aArgs, uint16_t aCount) { MOZ_RELEASE_ASSERT(aCount == 0, "Must give at least as many string arguments as are required by the ErrNum."); } template<typename... Ts> - static void Append(nsTArray<nsString>& aArgs, uint16_t aCount, const nsAString* aFirst, Ts... aOtherArgs) + static void Append(nsTArray<nsString>& aArgs, uint16_t aCount, const nsAString& aFirst, Ts&&... aOtherArgs) { if (aCount == 0) { MOZ_ASSERT(false, "There should not be more string arguments provided than are required by the ErrNum."); return; } - aArgs.AppendElement(*aFirst); - Append(aArgs, aCount - 1, aOtherArgs...); + aArgs.AppendElement(aFirst); + Append(aArgs, aCount - 1, Forward<Ts>(aOtherArgs)...); } }; } // namespace dom class ErrorResult { public: ErrorResult() @@ -82,18 +93,19 @@ public: , mMightHaveUnreportedJSException(false) , mUnionState(HasNothing) #endif { } #ifdef DEBUG ~ErrorResult() { - MOZ_ASSERT_IF(IsErrorWithMessage(), !mMessage); - MOZ_ASSERT_IF(IsDOMException(), !mDOMExceptionInfo); + // Consumers should have called one of MaybeSetPendingException + // (possibly via ToJSValue), StealNSResult, and SuppressException + MOZ_ASSERT(!Failed()); MOZ_ASSERT(!mMightHaveUnreportedJSException); MOZ_ASSERT(mUnionState == HasNothing); } #endif // DEBUG ErrorResult(ErrorResult&& aRHS) // Initialize mResult and whatever else we need to default-initialize, so // the ClearUnionData call in our operator= will do the right thing @@ -129,82 +141,110 @@ public: // nsresult that you will then return to a caller. This will // SuppressException(), since there will no longer be a way to report it. nsresult StealNSResult() { nsresult rv = ErrorCode(); SuppressException(); return rv; } - template<dom::ErrNum errorNumber, typename... Ts> - void ThrowTypeError(Ts... messageArgs) + // Use MaybeSetPendingException to convert an ErrorResult to a pending + // exception on the given JSContext. This is the normal "throw an exception" + // codepath. + // + // The return value is false if the ErrorResult represents success, true + // otherwise. This does mean that in JSAPI method implementations you can't + // just use this as |return rv.MaybeSetPendingException(cx)| (though you could + // |return !rv.MaybeSetPendingException(cx)|), but in practice pretty much any + // consumer would want to do some more work on the success codepath. So + // instead the way you use this is: + // + // if (rv.MaybeSetPendingException(cx)) { + // bail out here + // } + // go on to do something useful + // + // The success path is inline, since it should be the common case and we don't + // want to pay the price of a function call in some of the consumers of this + // method in the common case. + // + // Note that a true return value does NOT mean there is now a pending + // exception on aCx, due to uncatchable exceptions. It should still be + // considered equivalent to a JSAPI failure in terms of what callers should do + // after true is returned. + // + // After this call, the ErrorResult will no longer return true from Failed(), + // since the exception will have moved to the JSContext. + bool MaybeSetPendingException(JSContext* cx) { - ThrowErrorWithMessage<errorNumber>(NS_ERROR_TYPE_ERR, messageArgs...); + WouldReportJSException(); + if (!Failed()) { + return false; + } + + SetPendingException(cx); + return true; } template<dom::ErrNum errorNumber, typename... Ts> - void ThrowRangeError(Ts... messageArgs) + void ThrowTypeError(Ts&&... messageArgs) { - ThrowErrorWithMessage<errorNumber>(NS_ERROR_RANGE_ERR, messageArgs...); + ThrowErrorWithMessage<errorNumber>(NS_ERROR_TYPE_ERR, + Forward<Ts>(messageArgs)...); } - void ReportErrorWithMessage(JSContext* cx); + template<dom::ErrNum errorNumber, typename... Ts> + void ThrowRangeError(Ts&&... messageArgs) + { + ThrowErrorWithMessage<errorNumber>(NS_ERROR_RANGE_ERR, + Forward<Ts>(messageArgs)...); + } + bool IsErrorWithMessage() const { return ErrorCode() == NS_ERROR_TYPE_ERR || ErrorCode() == NS_ERROR_RANGE_ERR; } // Facilities for throwing a preexisting JS exception value via this // ErrorResult. The contract is that any code which might end up calling // ThrowJSException() must call MightThrowJSException() even if no exception - // is being thrown. Code that would call ReportJSException or - // StealJSException as needed must first call WouldReportJSException even if + // is being thrown. Code that conditionally calls ToJSValue on this + // ErrorResult only if Failed() must first call WouldReportJSException even if // this ErrorResult has not failed. // // The exn argument to ThrowJSException can be in any compartment. It does // not have to be in the compartment of cx. If someone later uses it, they // will wrap it into whatever compartment they're working in, as needed. void ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn); - void ReportJSException(JSContext* cx); bool IsJSException() const { return ErrorCode() == NS_ERROR_DOM_JS_EXCEPTION; } // Facilities for throwing a DOMException. If an empty message string is // passed to ThrowDOMException, the default message string for the given // nsresult will be used. The passed-in string must be UTF-8. The nsresult // passed in must be one we create DOMExceptions for; otherwise you may get an // XPConnect Exception. void ThrowDOMException(nsresult rv, const nsACString& message = EmptyCString()); - void ReportDOMException(JSContext* cx); bool IsDOMException() const { return ErrorCode() == NS_ERROR_DOM_DOMEXCEPTION; } // Flag on the ErrorResult that whatever needs throwing has been // thrown on the JSContext already and we should not mess with it. void NoteJSContextException() { mResult = NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT; } // Check whether the ErrorResult says to just throw whatever is on // the JSContext already. bool IsJSContextException() { return ErrorCode() == NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT; } - // Report a generic error. This should only be used if we're not - // some more specific exception type. - void ReportGenericError(JSContext* cx); - // Support for uncatchable exceptions. void ThrowUncatchableException() { Throw(NS_ERROR_UNCATCHABLE_EXCEPTION); } bool IsUncatchableException() const { return ErrorCode() == NS_ERROR_UNCATCHABLE_EXCEPTION; } - // StealJSException steals the JS Exception from the object. This method must - // be called only if IsJSException() returns true. This method also resets the - // error code to NS_OK. - void StealJSException(JSContext* cx, JS::MutableHandle<JS::Value> value); - void MOZ_ALWAYS_INLINE MightThrowJSException() { #ifdef DEBUG mMightHaveUnreportedJSException = true; #endif } void MOZ_ALWAYS_INLINE WouldReportJSException() { @@ -259,28 +299,29 @@ private: void SerializeDOMExceptionInfo(IPC::Message* aMsg) const; bool DeserializeDOMExceptionInfo(const IPC::Message* aMsg, void** aIter); // Helper method that creates a new Message for this ErrorResult, // and returns the arguments array from that Message. nsTArray<nsString>& CreateErrorMessageHelper(const dom::ErrNum errorNumber, nsresult errorType); template<dom::ErrNum errorNumber, typename... Ts> - void ThrowErrorWithMessage(nsresult errorType, Ts... messageArgs) + void ThrowErrorWithMessage(nsresult errorType, Ts&&... messageArgs) { #if defined(DEBUG) && (defined(__clang__) || defined(__GNUC__)) static_assert(dom::ErrorFormatNumArgs[errorNumber] == sizeof...(messageArgs), "Pass in the right number of arguments"); #endif ClearUnionData(); nsTArray<nsString>& messageArgsArray = CreateErrorMessageHelper(errorNumber, errorType); uint16_t argCount = dom::GetErrorArgCount(errorNumber); - dom::StringArrayAppender::Append(messageArgsArray, argCount, messageArgs...); + dom::StringArrayAppender::Append(messageArgsArray, argCount, + Forward<Ts>(messageArgs)...); #ifdef DEBUG mUnionState = HasMessage; #endif // DEBUG } void AssignErrorCode(nsresult aRv) { MOZ_ASSERT(aRv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()"); MOZ_ASSERT(aRv != NS_ERROR_RANGE_ERR, "Use ThrowRangeError()"); @@ -301,32 +342,43 @@ private: // ClearUnionData will try to clear the data in our // mMessage/mJSException/mDOMExceptionInfo union. After this the union may be // in an uninitialized state (e.g. mMessage or mDOMExceptionInfo may be // pointing to deleted memory) and the caller must either reinitialize it or // change mResult to something that will not involve us touching the union // anymore. void ClearUnionData(); + // Implementation of MaybeSetPendingException for the case when we're a + // failure result. + void SetPendingException(JSContext* cx); + + // Methods for setting various specific kinds of pending exceptions. + void SetPendingExceptionWithMessage(JSContext* cx); + void SetPendingJSException(JSContext* cx); + void SetPendingDOMException(JSContext* cx); + void SetPendingGenericErrorException(JSContext* cx); + + // Special values of mResult: // NS_ERROR_TYPE_ERR -- ThrowTypeError() called on us. // NS_ERROR_RANGE_ERR -- ThrowRangeError() called on us. // NS_ERROR_DOM_JS_EXCEPTION -- ThrowJSException() called on us. // NS_ERROR_UNCATCHABLE_EXCEPTION -- ThrowUncatchableException called on us. // NS_ERROR_DOM_DOMEXCEPTION -- ThrowDOMException() called on us. nsresult mResult; struct Message; struct DOMExceptionInfo; // mMessage is set by ThrowErrorWithMessage and reported (and deallocated) by - // ReportErrorWithMessage. + // SetPendingExceptionWithMessage. // mJSException is set (and rooted) by ThrowJSException and reported - // (and unrooted) by ReportJSException. + // (and unrooted) by SetPendingJSException. // mDOMExceptionInfo is set by ThrowDOMException and reported - // (and deallocated) by ReportDOMException. + // (and deallocated) by SetPendingDOMException. union { Message* mMessage; // valid when IsErrorWithMessage() JS::Value mJSException; // valid when IsJSException() DOMExceptionInfo* mDOMExceptionInfo; // valid when IsDOMException() }; #ifdef DEBUG // Used to keep track of codepaths that might throw JS exceptions,
--- a/dom/bindings/ToJSValue.cpp +++ b/dom/bindings/ToJSValue.cpp @@ -51,18 +51,18 @@ bool ToJSValue(JSContext* aCx, ErrorResult& aArgument, JS::MutableHandle<JS::Value> aValue) { MOZ_ASSERT(aArgument.Failed()); MOZ_ASSERT(!aArgument.IsUncatchableException(), "Doesn't make sense to convert uncatchable exception to a JS value!"); AutoForceSetExceptionOnContext forceExn(aCx); - DebugOnly<bool> throwResult = ThrowMethodFailed(aCx, aArgument); - MOZ_ASSERT(!throwResult); + DebugOnly<bool> throwResult = aArgument.MaybeSetPendingException(aCx); + MOZ_ASSERT(throwResult); DebugOnly<bool> getPendingResult = JS_GetPendingException(aCx, aValue); MOZ_ASSERT(getPendingResult); JS_ClearPendingException(aCx); return true; } } // namespace dom } // namespace mozilla
--- a/dom/bluetooth/common/BluetoothService.cpp +++ b/dom/bluetooth/common/BluetoothService.cpp @@ -14,16 +14,17 @@ #include "BluetoothServiceChildProcess.h" #include "BluetoothUtils.h" #include "jsapi.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" #include "mozilla/unused.h" +#include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" #include "mozilla/dom/ipc/BlobChild.h" #include "mozilla/dom/ipc/BlobParent.h" #include "nsContentUtils.h" #include "nsIObserverService.h" #include "nsISettingsService.h" #include "nsISystemMessagesInternal.h"
--- a/dom/browser-element/BrowserElementChildPreload.js +++ b/dom/browser-element/BrowserElementChildPreload.js @@ -2032,16 +2032,19 @@ BrowserElementChild.prototype = { sendAsyncMsg('error', { type: 'phishingBlocked' }); return; case Cr.NS_ERROR_MALWARE_URI : sendAsyncMsg('error', { type: 'malwareBlocked' }); return; case Cr.NS_ERROR_UNWANTED_URI : sendAsyncMsg('error', { type: 'unwantedBlocked' }); return; + case Cr.NS_ERROR_FORBIDDEN_URI : + sendAsyncMsg('error', { type: 'forbiddenBlocked' }); + return; case Cr.NS_ERROR_OFFLINE : sendAsyncMsg('error', { type: 'offline' }); return; case Cr.NS_ERROR_MALFORMED_URI : sendAsyncMsg('error', { type: 'malformedURI' }); return; case Cr.NS_ERROR_REDIRECT_LOOP :
--- a/dom/cache/Cache.cpp +++ b/dom/cache/Cache.cpp @@ -41,32 +41,32 @@ IsValidPutRequestURL(const nsAString& aU NS_ConvertUTF16toUTF8 url(aUrl); TypeUtils::ProcessURL(url, &validScheme, nullptr, nullptr, aRv); if (aRv.Failed()) { return false; } if (!validScheme) { - NS_NAMED_LITERAL_STRING(label, "Request"); - aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>(&label, &aUrl); + aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>(NS_LITERAL_STRING("Request"), + aUrl); return false; } return true; } static bool IsValidPutRequestMethod(const Request& aRequest, ErrorResult& aRv) { nsAutoCString method; aRequest.GetMethod(method); if (!method.LowerCaseEqualsLiteral("get")) { NS_ConvertASCIItoUTF16 label(method); - aRv.ThrowTypeError<MSG_INVALID_REQUEST_METHOD>(&label); + aRv.ThrowTypeError<MSG_INVALID_REQUEST_METHOD>(label); return false; } return true; } static bool IsValidPutRequestMethod(const RequestOrUSVString& aRequest, ErrorResult& aRv)
--- a/dom/cache/CacheStorage.cpp +++ b/dom/cache/CacheStorage.cpp @@ -256,18 +256,18 @@ CacheStorage::DefineCaches(JSContext* aC MOZ_ASSERT(principal); ErrorResult rv; RefPtr<CacheStorage> storage = CreateOnMainThread(DEFAULT_NAMESPACE, xpc::NativeGlobal(aGlobal), principal, false, /* private browsing */ true, /* force trusted */ rv); - if (NS_WARN_IF(rv.Failed())) { - return ThrowMethodFailed(aCx, rv); + if (NS_WARN_IF(rv.MaybeSetPendingException(aCx))) { + return false; } JS::Rooted<JS::Value> caches(aCx); js::AssertSameCompartment(aCx, aGlobal); if (NS_WARN_IF(!ToJSValue(aCx, storage, &caches))) { return false; }
--- a/dom/cache/Context.cpp +++ b/dom/cache/Context.cpp @@ -127,33 +127,35 @@ public: MOZ_ASSERT(mInitAction); } nsresult Dispatch() { NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable); MOZ_ASSERT(mState == STATE_INIT); - mState = STATE_OPEN_DIRECTORY; + mState = STATE_GET_INFO; nsresult rv = NS_DispatchToMainThread(this, nsIThread::DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { mState = STATE_COMPLETE; Clear(); } return rv; } void Cancel() { NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable); MOZ_ASSERT(!mCanceled); mCanceled = true; mInitAction->CancelOnInitiatingThread(); } + void OpenDirectory(); + // OpenDirectoryListener methods virtual void DirectoryLockAcquired(DirectoryLock* aLock) override; virtual void DirectoryLockFailed() override; private: @@ -190,16 +192,18 @@ private: MOZ_ASSERT(mState == STATE_COMPLETE); MOZ_ASSERT(!mContext); MOZ_ASSERT(!mInitAction); } enum State { STATE_INIT, + STATE_GET_INFO, + STATE_CREATE_QUOTA_MANAGER, STATE_OPEN_DIRECTORY, STATE_WAIT_FOR_DIRECTORY_LOCK, STATE_ENSURE_ORIGIN_INITIALIZED, STATE_RUN_ON_TARGET, STATE_RUNNING, STATE_COMPLETING, STATE_COMPLETE }; @@ -229,33 +233,54 @@ private: RefPtr<ThreadsafeHandle> mThreadsafeHandle; RefPtr<Manager> mManager; RefPtr<Data> mData; nsCOMPtr<nsIThread> mTarget; RefPtr<Action> mInitAction; nsCOMPtr<nsIThread> mInitiatingThread; nsresult mResult; QuotaInfo mQuotaInfo; - nsMainThreadPtrHandle<DirectoryLock> mDirectoryLock; + RefPtr<DirectoryLock> mDirectoryLock; State mState; Atomic<bool> mCanceled; public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIRUNNABLE }; void +Context::QuotaInitRunnable::OpenDirectory() +{ + NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable); + MOZ_ASSERT(mState == STATE_CREATE_QUOTA_MANAGER || + mState == STATE_OPEN_DIRECTORY); + MOZ_ASSERT(QuotaManager::Get()); + + // QuotaManager::OpenDirectory() will hold a reference to us as + // a listener. We will then get DirectoryLockAcquired() on the owning + // thread when it is safe to access our storage directory. + mState = STATE_WAIT_FOR_DIRECTORY_LOCK; + QuotaManager::Get()->OpenDirectory(PERSISTENCE_TYPE_DEFAULT, + mQuotaInfo.mGroup, + mQuotaInfo.mOrigin, + mQuotaInfo.mIsApp, + quota::Client::DOMCACHE, + /* aExclusive */ false, + this); +} + +void Context::QuotaInitRunnable::DirectoryLockAcquired(DirectoryLock* aLock) { - MOZ_ASSERT(NS_IsMainThread()); + NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable); MOZ_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK); MOZ_ASSERT(!mDirectoryLock); - mDirectoryLock = new nsMainThreadPtrHolder<DirectoryLock>(aLock); + mDirectoryLock = aLock; if (mCanceled) { Complete(NS_ERROR_ABORT); return; } QuotaManager* qm = QuotaManager::Get(); MOZ_ASSERT(qm); @@ -266,17 +291,17 @@ Context::QuotaInitRunnable::DirectoryLoc Complete(rv); return; } } void Context::QuotaInitRunnable::DirectoryLockFailed() { - MOZ_ASSERT(NS_IsMainThread()); + NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable); MOZ_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK); MOZ_ASSERT(!mDirectoryLock); NS_WARNING("Failed to acquire a directory lock!"); Complete(NS_ERROR_FAILURE); } @@ -285,23 +310,33 @@ NS_IMPL_ISUPPORTS(mozilla::dom::cache::C // The QuotaManager init state machine is represented in the following diagram: // // +---------------+ // | Start | Resolve(error) // | (Orig Thread) +---------------------+ // +-------+-------+ | // | | // +----------v-----------+ | -// | OpenDirectory | Resolve(error) | +// | GetInfo | Resolve(error) | // | (Main Thread) +-----------------+ // +----------+-----------+ | // | | // +----------v-----------+ | +// | CreateQuotaManager | Resolve(error) | +// | (Orig Thread) +-----------------+ +// +----------+-----------+ | +// | | +// +----------v-----------+ | +// | OpenDirectory | Resolve(error) | +// | (Orig Thread) +-----------------+ +// +----------+-----------+ | +// | | +// +----------v-----------+ | // | WaitForDirectoryLock | Resolve(error) | -// | (Main Thread) +-----------------+ +// | (Orig Thread) +-----------------+ // +----------+-----------+ | // | | // +----------v------------+ | // |EnsureOriginInitialized| Resolve(error) | // | (Quota IO Thread) +----------------+ // +----------+------------+ | // | | // +----------v------------+ | @@ -325,53 +360,71 @@ Context::QuotaInitRunnable::Run() { // May run on different threads depending on the state. See individual // state cases for thread assertions. RefPtr<SyncResolver> resolver = new SyncResolver(); switch(mState) { // ----------------------------------- - case STATE_OPEN_DIRECTORY: + case STATE_GET_INFO: { MOZ_ASSERT(NS_IsMainThread()); + if (mCanceled) { + resolver->Resolve(NS_ERROR_ABORT); + break; + } + + RefPtr<ManagerId> managerId = mManager->GetManagerId(); + nsCOMPtr<nsIPrincipal> principal = managerId->Principal(); + nsresult rv = QuotaManager::GetInfoFromPrincipal(principal, + &mQuotaInfo.mGroup, + &mQuotaInfo.mOrigin, + &mQuotaInfo.mIsApp); + if (NS_WARN_IF(NS_FAILED(rv))) { + resolver->Resolve(rv); + break; + } + + mState = STATE_CREATE_QUOTA_MANAGER; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL))); + break; + } + // ---------------------------------- + case STATE_CREATE_QUOTA_MANAGER: + { + NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable); + if (mCanceled || QuotaManager::IsShuttingDown()) { resolver->Resolve(NS_ERROR_ABORT); break; } - QuotaManager* qm = QuotaManager::GetOrCreate(); - if (!qm) { + if (QuotaManager::Get()) { + OpenDirectory(); + return NS_OK; + } + + mState = STATE_OPEN_DIRECTORY; + QuotaManager::GetOrCreate(this); + break; + } + // ---------------------------------- + case STATE_OPEN_DIRECTORY: + { + NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable); + + if (NS_WARN_IF(!QuotaManager::Get())) { resolver->Resolve(NS_ERROR_FAILURE); break; } - RefPtr<ManagerId> managerId = mManager->GetManagerId(); - nsCOMPtr<nsIPrincipal> principal = managerId->Principal(); - nsresult rv = qm->GetInfoFromPrincipal(principal, - &mQuotaInfo.mGroup, - &mQuotaInfo.mOrigin, - &mQuotaInfo.mIsApp); - if (NS_WARN_IF(NS_FAILED(rv))) { - resolver->Resolve(rv); - break; - } - - // QuotaManager::OpenDirectory() will hold a reference to us as - // a listener. We will then get DirectoryLockAcquired() on the main - // thread when it is safe to access our storage directory. - mState = STATE_WAIT_FOR_DIRECTORY_LOCK; - qm->OpenDirectory(PERSISTENCE_TYPE_DEFAULT, - mQuotaInfo.mGroup, - mQuotaInfo.mOrigin, - mQuotaInfo.mIsApp, - quota::Client::DOMCACHE, - /* aExclusive */ false, - this); + OpenDirectory(); break; } // ---------------------------------- case STATE_ENSURE_ORIGIN_INITIALIZED: { AssertIsOnIOThread(); if (mCanceled) { @@ -420,17 +473,17 @@ Context::QuotaInitRunnable::Run() break; } // ------------------- case STATE_COMPLETING: { NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable); mInitAction->CompleteOnInitiatingThread(mResult); - mContext->OnQuotaInit(mResult, mQuotaInfo, mDirectoryLock); + mContext->OnQuotaInit(mResult, mQuotaInfo, mDirectoryLock.forget()); mState = STATE_COMPLETE; // Explicitly cleanup here as the destructor could fire on any of // the threads we have bounced through. Clear(); break; } // ----- @@ -982,17 +1035,17 @@ Context::DispatchAction(Action* aAction, // for this invariant violation. MOZ_CRASH("Failed to dispatch ActionRunnable to target thread."); } AddActivity(runnable); } void Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo, - nsMainThreadPtrHandle<DirectoryLock>& aDirectoryLock) + already_AddRefed<DirectoryLock> aDirectoryLock) { NS_ASSERT_OWNINGTHREAD(Context); MOZ_ASSERT(mInitRunnable); mInitRunnable = nullptr; mQuotaInfo = aQuotaInfo;
--- a/dom/cache/Context.h +++ b/dom/cache/Context.h @@ -185,17 +185,18 @@ private: }; Context(Manager* aManager, nsIThread* aTarget, Action* aInitAction); ~Context(); void Init(Context* aOldContext); void Start(); void DispatchAction(Action* aAction, bool aDoomData = false); void OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo, - nsMainThreadPtrHandle<DirectoryLock>& aDirectoryLock); + already_AddRefed<DirectoryLock> aDirectoryLock); + already_AddRefed<ThreadsafeHandle> CreateThreadsafeHandle(); void SetNextContext(Context* aNextContext); void @@ -216,17 +217,17 @@ private: typedef nsTObserverArray<Activity*> ActivityList; ActivityList mActivityList; // The ThreadsafeHandle may have a strong ref back to us. This creates // a ref-cycle that keeps the Context alive. The ref-cycle is broken // when ThreadsafeHandle::AllowToClose() is called. RefPtr<ThreadsafeHandle> mThreadsafeHandle; - nsMainThreadPtrHandle<DirectoryLock> mDirectoryLock; + RefPtr<DirectoryLock> mDirectoryLock; RefPtr<Context> mNextContext; public: NS_INLINE_DECL_REFCOUNTING(cache::Context) }; } // namespace cache } // namespace dom
--- a/dom/cache/Manager.cpp +++ b/dom/cache/Manager.cpp @@ -237,65 +237,71 @@ public: MOZ_ALWAYS_TRUE(sFactory->mManagerList.RemoveElement(aManager)); // clean up the factory singleton if there are no more managers MaybeDestroyInstance(); } static void - StartAbortOnMainThread(const nsACString& aOrigin) + Abort(const nsACString& aOrigin) { - MOZ_ASSERT(NS_IsMainThread()); + mozilla::ipc::AssertIsOnBackgroundThread(); - // Lock for sBackgroundThread. - StaticMutexAutoLock lock(sMutex); - - if (!sBackgroundThread) { + if (!sFactory) { return; } - // Guaranteed to succeed because we should get abort only before the - // background thread is destroyed. - nsCOMPtr<nsIRunnable> runnable = new AbortRunnable(aOrigin); - nsresult rv = sBackgroundThread->Dispatch(runnable, - nsIThread::DISPATCH_NORMAL); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv)); + MOZ_ASSERT(!sFactory->mManagerList.IsEmpty()); + + { + ManagerList::ForwardIterator iter(sFactory->mManagerList); + while (iter.HasMore()) { + RefPtr<Manager> manager = iter.GetNext(); + if (aOrigin.IsVoid() || + manager->mManagerId->QuotaOrigin() == aOrigin) { + manager->Abort(); + } + } + } } static void - StartShutdownAllOnMainThread() + ShutdownAll() { - MOZ_ASSERT(NS_IsMainThread()); + mozilla::ipc::AssertIsOnBackgroundThread(); - // Lock for sFactoryShutdown and sBackgroundThread. - StaticMutexAutoLock lock(sMutex); - - sFactoryShutdown = true; - - if (!sBackgroundThread) { + if (!sFactory) { return; } - // Guaranteed to succeed because we should be shutdown before the - // background thread is destroyed. - nsCOMPtr<nsIRunnable> runnable = new ShutdownAllRunnable(); - nsresult rv = sBackgroundThread->Dispatch(runnable, - nsIThread::DISPATCH_NORMAL); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv)); + MOZ_ASSERT(!sFactory->mManagerList.IsEmpty()); + + { + // Note that we are synchronously calling shutdown code here. If any + // of the shutdown code synchronously decides to delete the Factory + // we need to delay that delete until the end of this method. + AutoRestore<bool> restore(sFactory->mInSyncShutdown); + sFactory->mInSyncShutdown = true; + + ManagerList::ForwardIterator iter(sFactory->mManagerList); + while (iter.HasMore()) { + RefPtr<Manager> manager = iter.GetNext(); + manager->Shutdown(); + } + } + + MaybeDestroyInstance(); } static bool - IsShutdownAllCompleteOnMainThread() + IsShutdownAllComplete() { - MOZ_ASSERT(NS_IsMainThread()); - StaticMutexAutoLock lock(sMutex); - // Infer whether we have shutdown using the sBackgroundThread value. We - // guarantee this is nullptr when sFactory is destroyed. - return sFactoryShutdown && !sBackgroundThread; + mozilla::ipc::AssertIsOnBackgroundThread(); + return !sFactory; } private: Factory() : mInSyncShutdown(false) { MOZ_COUNT_CTOR(cache::Manager::Factory); } @@ -317,22 +323,16 @@ private: // we don't need to lock it here. Just protect sFactoryShutdown and // sBackgroundThread. { StaticMutexAutoLock lock(sMutex); if (sFactoryShutdown) { return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; } - - // Cannot use ClearOnShutdown() because we're on the background thread. - // This is automatically cleared when Factory::Remove() calls - // MaybeDestroyInstance(). - MOZ_ASSERT(!sBackgroundThread); - sBackgroundThread = NS_GetCurrentThread(); } // We cannot use ClearOnShutdown() here because we're not on the main // thread. Instead, we delete sFactory in Factory::Remove() after the // last manager is removed. ShutdownObserver ensures this happens // before shutdown. sFactory = new Factory(); } @@ -354,144 +354,31 @@ private: // If the factory is is still in use then we cannot delete yet. This // could be due to managers still existing or because we are in the // middle of shutting down. We need to be careful not to delete ourself // synchronously during shutdown. if (!sFactory->mManagerList.IsEmpty() || sFactory->mInSyncShutdown) { return; } - // Be clear about what we are locking. sFactory is bg thread only, so - // we don't need to lock it here. Just protect sBackgroundThread. - { - StaticMutexAutoLock lock(sMutex); - MOZ_ASSERT(sBackgroundThread); - sBackgroundThread = nullptr; - } - sFactory = nullptr; } - static void - AbortOnBackgroundThread(const nsACString& aOrigin) - { - mozilla::ipc::AssertIsOnBackgroundThread(); - - // The factory was destroyed between when abort started on main thread and - // when we could start abort on the worker thread. Just declare abort - // complete. - if (!sFactory) { -#ifdef DEBUG - StaticMutexAutoLock lock(sMutex); - MOZ_ASSERT(!sBackgroundThread); -#endif - return; - } - - MOZ_ASSERT(!sFactory->mManagerList.IsEmpty()); - - { - ManagerList::ForwardIterator iter(sFactory->mManagerList); - while (iter.HasMore()) { - RefPtr<Manager> manager = iter.GetNext(); - if (aOrigin.IsVoid() || - manager->mManagerId->QuotaOrigin() == aOrigin) { - manager->Abort(); - } - } - } - } - - static void - ShutdownAllOnBackgroundThread() - { - mozilla::ipc::AssertIsOnBackgroundThread(); - - // The factory shutdown between when shutdown started on main thread and - // when we could start shutdown on the worker thread. Just declare - // shutdown complete. The sFactoryShutdown flag prevents the factory - // from racing to restart here. - if (!sFactory) { -#ifdef DEBUG - StaticMutexAutoLock lock(sMutex); - MOZ_ASSERT(!sBackgroundThread); -#endif - return; - } - - MOZ_ASSERT(!sFactory->mManagerList.IsEmpty()); - - { - // Note that we are synchronously calling shutdown code here. If any - // of the shutdown code synchronously decides to delete the Factory - // we need to delay that delete until the end of this method. - AutoRestore<bool> restore(sFactory->mInSyncShutdown); - sFactory->mInSyncShutdown = true; - - ManagerList::ForwardIterator iter(sFactory->mManagerList); - while (iter.HasMore()) { - RefPtr<Manager> manager = iter.GetNext(); - manager->Shutdown(); - } - } - - MaybeDestroyInstance(); - } - - class AbortRunnable final : public nsRunnable - { - public: - explicit AbortRunnable(const nsACString& aOrigin) - : mOrigin(aOrigin) - { } - - NS_IMETHOD - Run() override - { - mozilla::ipc::AssertIsOnBackgroundThread(); - AbortOnBackgroundThread(mOrigin); - return NS_OK; - } - private: - ~AbortRunnable() { } - - const nsCString mOrigin; - }; - - class ShutdownAllRunnable final : public nsRunnable - { - public: - NS_IMETHOD - Run() override - { - mozilla::ipc::AssertIsOnBackgroundThread(); - ShutdownAllOnBackgroundThread(); - return NS_OK; - } - private: - ~ShutdownAllRunnable() { } - }; - // Singleton created on demand and deleted when last Manager is cleared // in Remove(). // PBackground thread only. static StaticAutoPtr<Factory> sFactory; - // protects following static attributes + // protects following static attribute static StaticMutex sMutex; // Indicate if shutdown has occurred to block re-creation of sFactory. // Must hold sMutex to access. static bool sFactoryShutdown; - // Background thread owning all Manager objects. Only set while sFactory is - // set. - // Must hold sMutex to access. - static StaticRefPtr<nsIThread> sBackgroundThread; - // Weak references as we don't want to keep Manager objects alive forever. // When a Manager is destroyed it calls Factory::Remove() to clear itself. // PBackground thread only. typedef nsTObserverArray<Manager*> ManagerList; ManagerList mManagerList; // This flag is set when we are looping through the list and calling // Shutdown() on each Manager. We need to be careful not to synchronously @@ -503,19 +390,16 @@ private: StaticAutoPtr<Manager::Factory> Manager::Factory::sFactory; // static StaticMutex Manager::Factory::sMutex; // static bool Manager::Factory::sFactoryShutdown = false; -// static -StaticRefPtr<nsIThread> Manager::Factory::sBackgroundThread; - // ---------------------------------------------------------------------------- // Abstract class to help implement the various Actions. The vast majority // of Actions are synchronous and need to report back to a Listener on the // Manager. class Manager::BaseAction : public SyncDBAction { protected: @@ -1526,37 +1410,37 @@ already_AddRefed<Manager> Manager::Get(ManagerId* aManagerId) { mozilla::ipc::AssertIsOnBackgroundThread(); return Factory::Get(aManagerId); } // static void -Manager::ShutdownAllOnMainThread() +Manager::ShutdownAll() { - MOZ_ASSERT(NS_IsMainThread()); + mozilla::ipc::AssertIsOnBackgroundThread(); - Factory::StartShutdownAllOnMainThread(); + Factory::ShutdownAll(); - while (!Factory::IsShutdownAllCompleteOnMainThread()) { + while (!Factory::IsShutdownAllComplete()) { if (!NS_ProcessNextEvent()) { NS_WARNING("Something bad happened!"); break; } } } // static void -Manager::AbortOnMainThread(const nsACString& aOrigin) +Manager::Abort(const nsACString& aOrigin) { - MOZ_ASSERT(NS_IsMainThread()); + mozilla::ipc::AssertIsOnBackgroundThread(); - Factory::StartAbortOnMainThread(aOrigin); + Factory::Abort(aOrigin); } void Manager::RemoveListener(Listener* aListener) { NS_ASSERT_OWNINGTHREAD(Manager); // There may not be a listener here in the case where an actor is killed // before it can perform any actual async requests on Manager.
--- a/dom/cache/Manager.h +++ b/dom/cache/Manager.h @@ -126,21 +126,21 @@ public: { Open, Closing }; static nsresult GetOrCreate(ManagerId* aManagerId, Manager** aManagerOut); static already_AddRefed<Manager> Get(ManagerId* aManagerId); - // Synchronously shutdown from main thread. This spins the event loop. - static void ShutdownAllOnMainThread(); + // Synchronously shutdown. This spins the event loop. + static void ShutdownAll(); // Cancel actions for given origin or all actions if passed string is null. - static void AbortOnMainThread(const nsACString& aOrigin); + static void Abort(const nsACString& aOrigin); // Must be called by Listener objects before they are destroyed. void RemoveListener(Listener* aListener); // Must be called by Context objects before they are destroyed. void RemoveContext(Context* aContext); // Marks the Manager "invalid". Once the Context completes no new operations
--- a/dom/cache/QuotaClient.cpp +++ b/dom/cache/QuotaClient.cpp @@ -5,29 +5,31 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/cache/QuotaClient.h" #include "mozilla/DebugOnly.h" #include "mozilla/dom/cache/Manager.h" #include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/dom/quota/UsageInfo.h" +#include "mozilla/ipc/BackgroundParent.h" #include "nsIFile.h" #include "nsISimpleEnumerator.h" #include "nsThreadUtils.h" namespace { using mozilla::DebugOnly; using mozilla::dom::ContentParentId; using mozilla::dom::cache::Manager; using mozilla::dom::quota::Client; using mozilla::dom::quota::PersistenceType; using mozilla::dom::quota::QuotaManager; using mozilla::dom::quota::UsageInfo; +using mozilla::ipc::AssertIsOnBackgroundThread; static nsresult GetBodyUsage(nsIFile* aDir, UsageInfo* aUsageInfo) { nsCOMPtr<nsISimpleEnumerator> entries; nsresult rv = aDir->GetDirectoryEntries(getter_AddRefs(entries)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -170,19 +172,19 @@ public: { // Nothing to do here as the Context handles cleaning everything up // automatically. } virtual void AbortOperations(const nsACString& aOrigin) override { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); - Manager::AbortOnMainThread(aOrigin); + Manager::Abort(aOrigin); } virtual void AbortOperationsForProcess(ContentParentId aContentParentId) override { // The Cache and Context can be shared by multiple client processes. They // are not exclusively owned by a single process. // @@ -190,44 +192,50 @@ public: // when a particular process goes away. We definitely don't want this // since we are shared. Also, the Cache actor code already properly // handles asynchronous actor destruction when the child process dies. // // Therefore, do nothing here. } virtual void - PerformIdleMaintenance() override + StartIdleMaintenance() override + { } + + virtual void + StopIdleMaintenance() override { } virtual void ShutdownWorkThreads() override { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); // spins the event loop and synchronously shuts down all Managers - Manager::ShutdownAllOnMainThread(); + Manager::ShutdownAll(); } private: ~CacheQuotaClient() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); } NS_INLINE_DECL_REFCOUNTING(CacheQuotaClient, override) }; } // namespace namespace mozilla { namespace dom { namespace cache { already_AddRefed<quota::Client> CreateQuotaClient() { + AssertIsOnBackgroundThread(); + RefPtr<CacheQuotaClient> ref = new CacheQuotaClient(); return ref.forget(); } } // namespace cache } // namespace dom } // namespace mozilla
--- a/dom/cache/TypeUtils.cpp +++ b/dom/cache/TypeUtils.cpp @@ -157,19 +157,19 @@ TypeUtils::ToCacheRequest(CacheRequest& bool schemeValid; ProcessURL(url, &schemeValid, &aOut.urlWithoutQuery(), &aOut.urlQuery(), aRv); if (aRv.Failed()) { return; } if (!schemeValid) { if (aSchemeAction == TypeErrorOnInvalidScheme) { - NS_NAMED_LITERAL_STRING(label, "Request"); NS_ConvertUTF8toUTF16 urlUTF16(url); - aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>(&label, &urlUTF16); + aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>(NS_LITERAL_STRING("Request"), + urlUTF16); return; } } aIn->GetReferrer(aOut.referrer()); RefPtr<InternalHeaders> headers = aIn->Headers(); MOZ_ASSERT(headers);
--- a/dom/cache/test/mochitest/driver.js +++ b/dom/cache/test/mochitest/driver.js @@ -30,17 +30,21 @@ function runTests(testFile, order) { resolve(); }); }); } // adapted from dom/indexedDB/test/helpers.js function clearStorage() { return new Promise(function(resolve, reject) { - SpecialPowers.clearStorageForDoc(SpecialPowers.wrap(document), resolve); + var qms = SpecialPowers.Services.qms; + var principal = SpecialPowers.wrap(document).nodePrincipal; + var request = qms.clearStoragesForPrincipal(principal); + var cb = SpecialPowers.wrapCallback(resolve); + request.callback = cb; }); } function loadScript(script) { return new Promise(function(resolve, reject) { var s = document.createElement("script"); s.src = script; s.onerror = reject;
--- a/dom/cache/test/mochitest/test_cache_orphaned_body.html +++ b/dom/cache/test/mochitest/test_cache_orphaned_body.html @@ -19,29 +19,41 @@ function setupTestIframe() { resolve(); }; document.body.appendChild(iframe); }); } function clearStorage() { return new Promise(function(resolve, reject) { - SpecialPowers.clearStorageForDoc(SpecialPowers.wrap(document), resolve); + var qms = SpecialPowers.Services.qms; + var principal = SpecialPowers.wrap(document).nodePrincipal; + var request = qms.clearStoragesForPrincipal(principal); + var cb = SpecialPowers.wrapCallback(resolve); + request.callback = cb; }); } function storageUsage() { return new Promise(function(resolve, reject) { - SpecialPowers.getStorageUsageForDoc(SpecialPowers.wrap(document), resolve); + var qms = SpecialPowers.Services.qms; + var principal = SpecialPowers.wrap(document).nodePrincipal; + var cb = SpecialPowers.wrapCallback(function(request) { + resolve(request.usage, request.fileUsage); + }); + qms.getUsageForPrincipal(principal, cb); }); } function resetStorage() { return new Promise(function(resolve, reject) { - SpecialPowers.resetStorageForDoc(SpecialPowers.wrap(document), resolve); + var qms = SpecialPowers.Services.qms; + var request = qms.reset(); + var cb = SpecialPowers.wrapCallback(resolve); + request.callback = cb; }); } function gc() { return new Promise(function(resolve, reject) { SpecialPowers.exactGC(window, resolve); }); }
--- a/dom/cache/test/mochitest/test_cache_orphaned_cache.html +++ b/dom/cache/test/mochitest/test_cache_orphaned_cache.html @@ -19,29 +19,41 @@ function setupTestIframe() { resolve(); }; document.body.appendChild(iframe); }); } function clearStorage() { return new Promise(function(resolve, reject) { - SpecialPowers.clearStorageForDoc(SpecialPowers.wrap(document), resolve); + var qms = SpecialPowers.Services.qms; + var principal = SpecialPowers.wrap(document).nodePrincipal; + var request = qms.clearStoragesForPrincipal(principal); + var cb = SpecialPowers.wrapCallback(resolve); + request.callback = cb; }); } function storageUsage() { return new Promise(function(resolve, reject) { - SpecialPowers.getStorageUsageForDoc(SpecialPowers.wrap(document), resolve); + var qms = SpecialPowers.Services.qms; + var principal = SpecialPowers.wrap(document).nodePrincipal; + var cb = SpecialPowers.wrapCallback(function(request) { + resolve(request.usage, request.fileUsage); + }); + qms.getUsageForPrincipal(principal, cb); }); } function resetStorage() { return new Promise(function(resolve, reject) { - SpecialPowers.resetStorageForDoc(SpecialPowers.wrap(document), resolve); + var qms = SpecialPowers.Services.qms; + var request = qms.reset(); + var cb = SpecialPowers.wrapCallback(resolve); + request.callback = cb; }); } function gc() { return new Promise(function(resolve, reject) { SpecialPowers.exactGC(window, resolve); }); }
--- a/dom/cache/test/mochitest/test_cache_restart.html +++ b/dom/cache/test/mochitest/test_cache_restart.html @@ -18,17 +18,20 @@ function setupTestIframe() { resolve(); }; document.body.appendChild(iframe); }); } function resetStorage() { return new Promise(function(resolve, reject) { - SpecialPowers.resetStorageForDoc(SpecialPowers.wrap(document), resolve); + var qms = SpecialPowers.Services.qms; + var request = qms.reset(); + var cb = SpecialPowers.wrapCallback(resolve); + request.callback = cb; }); } SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({ "set": [["dom.caches.enabled", true], ["dom.caches.testing.enabled", true], ["dom.quotaManager.testing", true]],
--- a/dom/cache/test/mochitest/test_cache_shrink.html +++ b/dom/cache/test/mochitest/test_cache_shrink.html @@ -19,29 +19,41 @@ function setupTestIframe() { resolve(); }; document.body.appendChild(iframe); }); } function clearStorage() { return new Promise(function(resolve, reject) { - SpecialPowers.clearStorageForDoc(SpecialPowers.wrap(document), resolve); + var qms = SpecialPowers.Services.qms; + var principal = SpecialPowers.wrap(document).nodePrincipal; + var request = qms.clearStoragesForPrincipal(principal); + var cb = SpecialPowers.wrapCallback(resolve); + request.callback = cb; }); } function storageUsage() { return new Promise(function(resolve, reject) { - SpecialPowers.getStorageUsageForDoc(SpecialPowers.wrap(document), resolve); + var qms = SpecialPowers.Services.qms; + var principal = SpecialPowers.wrap(document).nodePrincipal; + var cb = SpecialPowers.wrapCallback(function(request) { + resolve(request.usage, request.fileUsage); + }); + qms.getUsageForPrincipal(principal, cb); }); } function resetStorage() { return new Promise(function(resolve, reject) { - SpecialPowers.resetStorageForDoc(SpecialPowers.wrap(document), resolve); + var qms = SpecialPowers.Services.qms; + var request = qms.reset(); + var cb = SpecialPowers.wrapCallback(resolve); + request.callback = cb; }); } function gc() { return new Promise(function(resolve, reject) { SpecialPowers.exactGC(window, resolve); }); }
--- a/dom/cache/test/xpcshell/make_profile.js +++ b/dom/cache/test/xpcshell/make_profile.js @@ -87,32 +87,21 @@ function resetQuotaManager() { var prefService = Cc['@mozilla.org/preferences-service;1'] .getService(Ci.nsIPrefService); // enable quota manager testing mode var pref = 'dom.quotaManager.testing'; prefService.getBranch(null).setBoolPref(pref, true); - qm.reset(); + var request = qm.reset(); + request.callback = resolve; // disable quota manager testing mode //prefService.getBranch(null).setBoolPref(pref, false); - - var uri = Cc['@mozilla.org/network/io-service;1'] - .getService(Ci.nsIIOService) - .newURI('http://example.com', null, null); - var principal = Cc['@mozilla.org/scriptsecuritymanager;1'] - .getService(Ci.nsIScriptSecurityManager) - .getSystemPrincipal(); - - // use getUsageForPrincipal() to get a callback when the reset() is done - qm.getUsageForPrincipal(principal, function(principal, usage, fileUsage) { - resolve(usage); - }); }); } function run_test() { do_test_pending(); do_get_profile(); var directoryService = Cc['@mozilla.org/file/directory_service;1']
--- a/dom/camera/DOMCameraControl.cpp +++ b/dom/camera/DOMCameraControl.cpp @@ -1271,18 +1271,16 @@ nsDOMCameraControl::OnGetCameraComplete( // Camera Control event handlers--must only be called from the Main Thread! void nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState aState, nsresult aReason) { DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); MOZ_ASSERT(NS_IsMainThread()); - ErrorResult ignored; - switch (aState) { case CameraControlListener::kHardwareOpen: DOM_CAMERA_LOGI("DOM OnHardwareStateChange: open\n"); MOZ_ASSERT(aReason == NS_OK); if (!mSetInitialConfig) { // The hardware is open, so we can return a camera to JS, even if // the preview hasn't started yet. OnGetCameraComplete(); @@ -1400,17 +1398,16 @@ nsDOMCameraControl::OnPoster(BlobImpl* a void nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState aState, int32_t aArg, int32_t aTrackNum) { // For now, we do nothing with 'aStatus' and 'aTrackNum'. DOM_CAMERA_LOGT("%s:%d : this=%p, state=%u\n", __func__, __LINE__, this, aState); MOZ_ASSERT(NS_IsMainThread()); - ErrorResult ignored; nsString state; switch (aState) { case CameraControlListener::kRecorderStarted: { RefPtr<Promise> promise = mStartRecordingPromise.forget(); if (promise) { promise->MaybeResolve(JS::UndefinedHandleValue);
--- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -3962,16 +3962,17 @@ gfxFontGroup *CanvasRenderingContext2D:: // use lazy initilization for the font group since it's rather expensive if (!CurrentState().fontGroup) { ErrorResult err; NS_NAMED_LITERAL_STRING(kDefaultFontStyle, "10px sans-serif"); static float kDefaultFontSize = 10.0; nsCOMPtr<nsIPresShell> presShell = GetPresShell(); bool fontUpdated = SetFontInternal(kDefaultFontStyle, err); if (err.Failed() || !fontUpdated) { + err.SuppressException(); gfxFontStyle style; style.size = kDefaultFontSize; gfxTextPerfMetrics* tp = nullptr; if (presShell && !presShell->IsDestroying()) { tp = presShell->GetPresContext()->GetTextPerfMetrics(); } int32_t perDevPixel, perCSSPixel; GetAppUnitsValues(&perDevPixel, &perCSSPixel);
--- a/dom/canvas/test/reftest/reftest.list +++ b/dom/canvas/test/reftest/reftest.list @@ -1,10 +1,10 @@ # WebGL Reftests! -default-preferences pref(webgl.force-enabled,true) +default-preferences pref(webgl.force-enabled,true) pref(media.useAudioChannelAPI,true) pref(dom.audiochannel.mutedByDefault,false) # Check that disabling works: == webgl-disable-test.html?nogl wrapper.html?green.png pref(webgl.disabled,true) == webgl-disable-test.html wrapper.html?green.png # Basic WebGL tests: # Do we get pixels to the screen at all? # Neither of these should ever break.
deleted file mode 100644 --- a/dom/devicestorage/ipc/ipc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ -"runtests":{ -}, -"excludetests":{ - "dom/devicestorage/test/test_dirs.html":"excluded", - "dom/devicestorage/test/test_storageAreaListener.html":"excluded" - } -}
deleted file mode 100644 --- a/dom/devicestorage/ipc/mochitest.ini +++ /dev/null @@ -1,8 +0,0 @@ -[DEFAULT] -skip-if = toolkit == 'android' || e10s #bug 781789 & bug 782275 -support-files = - ../test/devicestorage_common.js - ipc.json - -[test_ipc.html] -skip-if = buildapp == 'mulet' || buildapp == 'b2g' # b2g(nested ipc not working) b2g-debug(nested ipc not working) b2g-desktop(nested ipc not working)
deleted file mode 100644 --- a/dom/devicestorage/ipc/test_ipc.html +++ /dev/null @@ -1,173 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <title>Test for OOP DeviceStorage</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> -</head> - <body> - - <script type="application/javascript;version=1.7"> - "use strict"; - - SimpleTest.waitForExplicitFinish(); - SimpleTest.requestFlakyTimeout("untriaged"); - - // The crash observer registration functions are stubbed out here to - // prevent the iframe test runner from breaking later crash-related tests. - function iframeScriptFirst() { - SpecialPowers.prototype.registerProcessCrashObservers = () => {}; - SpecialPowers.prototype.unregisterProcessCrashObservers = () => {}; - - content.wrappedJSObject.RunSet.reloadAndRunAll({ - preventDefault: function() { }, - __exposedProps__: { preventDefault: 'r' } - }); - } - - function iframeScriptSecond() { - let TestRunner = content.wrappedJSObject.TestRunner; - - let oldComplete = TestRunner.onComplete; - - TestRunner.onComplete = function() { - TestRunner.onComplete = oldComplete; - - sendAsyncMessage("test:DeviceStorage:ipcTestComplete", { - result: JSON.stringify(TestRunner._failedTests) - }); - - if (oldComplete) { - oldComplete(); - } - }; - - TestRunner.structuredLogger._dumpMessage = function(msg) { - sendAsyncMessage("test:DeviceStorage:ipcTestMessage", { msg: msg }); - } - } - - let VALID_ACTIONS = ['suite_start', 'suite_end', 'test_start', 'test_end', 'test_status', 'process_output', 'log']; - function validStructuredMessage(message) { - return message.action !== undefined && VALID_ACTIONS.indexOf(message.action) >= 0; - } - function onTestMessage(data) { - let message = SpecialPowers.wrap(data).data.msg; - - if (validStructuredMessage(message)) { - switch (message.action) { - case "test_status": - case "test_end": - let test_tokens = message.test.split("/"); - let test_name = test_tokens[test_tokens.length - 1]; - if (message.subtest) { - test_name += " | " + message.subtest; - } - ok(message.expected === undefined, test_name, message.message); - break; - case "log": - info(message.message); - break; - default: - // nothing - } - } - } - - function onTestComplete() { - let comp = SpecialPowers.wrap(SpecialPowers.Components); - let mm = SpecialPowers.getBrowserFrameMessageManager(iframe); - let spObserver = comp.classes["@mozilla.org/special-powers-observer;1"] - .getService(comp.interfaces.nsIMessageListener); - - mm.removeMessageListener("SPPrefService", spObserver); - mm.removeMessageListener("SPProcessCrashService", spObserver); - mm.removeMessageListener("SPPingService", spObserver); - mm.removeMessageListener("SpecialPowers.Quit", spObserver); - mm.removeMessageListener("SPPermissionManager", spObserver); - - mm.removeMessageListener("test:DeviceStorage:ipcTestMessage", onTestMessage); - mm.removeMessageListener("test:DeviceStorage:ipcTestComplete", onTestComplete); - - SimpleTest.executeSoon(function () { SimpleTest.finish(); }); - } - - function runTests() { - let iframe = document.createElement("iframe"); - SpecialPowers.wrap(iframe).mozbrowser = true; - iframe.id = "iframe"; - iframe.style.width = "100%"; - iframe.style.height = "1000px"; - - function iframeLoadSecond() { - ok(true, "Got second iframe load event."); - iframe.removeEventListener("mozbrowserloadend", iframeLoadSecond); - let mm = SpecialPowers.getBrowserFrameMessageManager(iframe); - mm.loadFrameScript("data:,(" + iframeScriptSecond.toString() + ")();", - false); - } - - function iframeLoadFirst() { - ok(true, "Got first iframe load event."); - iframe.removeEventListener("mozbrowserloadend", iframeLoadFirst); - iframe.addEventListener("mozbrowserloadend", iframeLoadSecond); - - let mm = SpecialPowers.getBrowserFrameMessageManager(iframe); - - let comp = SpecialPowers.wrap(SpecialPowers.Components); - - let spObserver = - comp.classes["@mozilla.org/special-powers-observer;1"] - .getService(comp.interfaces.nsIMessageListener); - - mm.addMessageListener("SPPrefService", spObserver); - mm.addMessageListener("SPProcessCrashService", spObserver); - mm.addMessageListener("SPPingService", spObserver); - mm.addMessageListener("SpecialPowers.Quit", spObserver); - mm.addMessageListener("SPPermissionManager", spObserver); - - mm.addMessageListener("test:DeviceStorage:ipcTestMessage", onTestMessage); - mm.addMessageListener("test:DeviceStorage:ipcTestComplete", onTestComplete); - - let specialPowersBase = "chrome://specialpowers/content/"; - mm.loadFrameScript(specialPowersBase + "MozillaLogger.js", false); - mm.loadFrameScript(specialPowersBase + "specialpowersAPI.js", false); - mm.loadFrameScript(specialPowersBase + "specialpowers.js", false); - - mm.loadFrameScript("data:,(" + iframeScriptFirst.toString() + ")();", false); - } - - iframe.addEventListener("mozbrowserloadend", iframeLoadFirst); - - // Strip this filename and one directory level and then add "/test". - let href = window.location.href; - href = href.substring(0, href.lastIndexOf('/')); - href = href.substring(0, href.lastIndexOf('/')); - let manifest = "tests/dom/devicestorage/ipc/ipc.json"; - iframe.src = href + "/test?consoleLevel=INFO&testManifest=" + manifest; - document.body.appendChild(iframe); - } - - addEventListener("load", function() { - - SpecialPowers.addPermission("browser", true, document); - SpecialPowers.pushPrefEnv({ - "set": [ - - ["device.storage.enabled", true], - ["device.storage.testing", true], - ["device.storage.prompt.testing", true], - - // TODO: remove this as part of bug 820712 - ["network.disable.ipc.security", true], - - ["dom.ipc.browser_frames.oop_by_default", true], - ["dom.mozBrowserFramesEnabled", true], - ["browser.pagethumbnails.capturing_disabled", true] - ] - }, runTests); - }); - - </script> -</body> -</html>
--- a/dom/devicestorage/moz.build +++ b/dom/devicestorage/moz.build @@ -36,15 +36,14 @@ include('/ipc/chromium/chromium-config.m FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ '/dom/base', '/dom/ipc', ] MOCHITEST_MANIFESTS += [ - 'ipc/mochitest.ini', 'test/mochitest.ini', ] MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini'] if CONFIG['GNU_CXX']: CXXFLAGS += ['-Wshadow']
--- a/dom/devicestorage/test/devicestorage_common.js +++ b/dom/devicestorage/test/devicestorage_common.js @@ -1,57 +1,39 @@ /** * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ var oldVal = false; - + Object.defineProperty(Array.prototype, "remove", { enumerable: false, configurable: false, writable: false, value: function(from, to) { // Array Remove - By John Resig (MIT Licensed) var rest = this.slice((to || from) + 1 || this.length); this.length = from < 0 ? this.length + from : from; return this.push.apply(this, rest); } }); -function devicestorage_setup() { - - // ensure that the directory we are writing into is empty - try { - const Cc = SpecialPowers.Cc; - const Ci = SpecialPowers.Ci; - var directoryService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); - var f = directoryService.get("TmpD", Ci.nsIFile); - f.appendRelativePath("device-storage-testing"); - f.remove(true); - } catch(e) {} - +function devicestorage_setup(callback) { SimpleTest.waitForExplicitFinish(); - if (SpecialPowers.isMainProcess()) { - try { - oldVal = SpecialPowers.getBoolPref("device.storage.enabled"); - } catch(e) {} - SpecialPowers.setBoolPref("device.storage.enabled", true); - SpecialPowers.setBoolPref("device.storage.testing", true); - SpecialPowers.setBoolPref("device.storage.prompt.testing", true); - } -} + + let script = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('remove_testing_directory.js')); -function devicestorage_cleanup() { - if (SpecialPowers.isMainProcess()) { - SpecialPowers.setBoolPref("device.storage.enabled", oldVal); - SpecialPowers.setBoolPref("device.storage.testing", false); - SpecialPowers.setBoolPref("device.storage.prompt.testing", false); - } - SimpleTest.finish(); + script.addMessageListener('directory-removed', function listener () { + script.removeMessageListener('directory-removed', listener); + var prefs = [["device.storage.enabled", true], + ["device.storage.testing", true], + ["device.storage.prompt.testing", true]]; + SpecialPowers.pushPrefEnv({"set": prefs}, callback); + }); } function getRandomBuffer() { var size = 1024; var buffer = new ArrayBuffer(size); var view = new Uint8Array(buffer); for (var i = 0; i < size; i++) { view[i] = parseInt(Math.random() * 255); @@ -70,17 +52,17 @@ function randomFilename(l) { var r = Math.floor(set.length * Math.random()); result += set.substring(r, r + 1); } return result; } function reportErrorAndQuit(e) { ok(false, "handleError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function createTestFiles(storage, paths) { function createTestFile(path) { return new Promise(function(resolve, reject) { function addNamed() { var req = storage.addNamed(createRandomBlob("image/png"), path);
--- a/dom/devicestorage/test/mochitest.ini +++ b/dom/devicestorage/test/mochitest.ini @@ -1,19 +1,21 @@ [DEFAULT] -skip-if = e10s || (toolkit == 'android' && processor == 'x86') # e10s: bug 1222522. Android: bug 781789 & bug 782275 +skip-if = (toolkit == 'android' && processor == 'x86') # Android: bug 781789 & bug 782275 support-files = devicestorage_common.js + remove_testing_directory.js [test_823965.html] # [test_add.html] # man, our mime database sucks hard. followup bug # 788273 [test_addCorrectType.html] [test_available.html] [test_basic.html] [test_dirs.html] +skip-if = e10s # Bug 1063569. # [test_diskSpace.html] # Possible race between the time we write a file, and the # time it takes to be reflected by statfs(). Bug # 791287 [test_dotdot.html] [test_enumerate.html] [test_enumerateMultipleContinue.html] [test_enumerateOptions.html] [test_freeSpace.html]
new file mode 100644 --- /dev/null +++ b/dom/devicestorage/test/remove_testing_directory.js @@ -0,0 +1,11 @@ +// ensure that the directory we are writing into is empty +try { + var Cc = Components.classes; + var Ci = Components.interfaces; + var directoryService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); + var f = directoryService.get("TmpD", Ci.nsIFile); + f.appendRelativePath("device-storage-testing"); + f.remove(true); +} catch(e) {} + +sendAsyncMessage('directory-removed', {});
--- a/dom/devicestorage/test/test_823965.html +++ b/dom/devicestorage/test/test_823965.html @@ -17,17 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=823965">Mozilla Bug 823965</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { var gFileName = "devicestorage/" + randomFilename(12) + "/hi.png"; var gData = "My name is Doug Turner (?!?). My IRC nick is DougT. I like Maple cookies." var gDataBlob = new Blob([gData], {type: 'image/png'}); function getSuccess(e) { var storage = navigator.getDeviceStorage("pictures"); ok(navigator.getDeviceStorage, "Should have getDeviceStorage"); @@ -38,30 +38,30 @@ function getSuccess(e) { ok(e.target.result.lastModifiedDate, "File should have a last modified date"); var mreq = storage.enumerate(); mreq.onsuccess = function() { var storage2 = navigator.getDeviceStorage('music'); var dreq = storage2.delete(mreq.result.name); dreq.onerror = function () { ok(true, "The bug has been fixed"); - devicestorage_cleanup(); + SimpleTest.finish(); }; dreq.onsuccess = function () { ok(false, "The bug has been fixed"); - devicestorage_cleanup(); + SimpleTest.finish(); }; }; mreq.onerror = getError; } function getError(e) { ok(false, "getError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function addSuccess(e) { var filename = e.target.result; if (filename[0] == "/") { // We got /storageName/prefix/filename // Remove the storageName (this shows up on FirefoxOS) @@ -80,27 +80,28 @@ function addSuccess(e) { request.onsuccess = getSuccess; request.onerror = getError; ok(true, "addSuccess was called"); } function addError(e) { ok(false, "addError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } ok(navigator.getDeviceStorage, "Should have getDeviceStorage"); var storage = navigator.getDeviceStorage("pictures"); ok(storage, "Should have gotten a storage"); request = storage.addNamed(gDataBlob, gFileName); ok(request, "Should have a non-null request"); request.onsuccess = addSuccess; request.onerror = addError; +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_add.html +++ b/dom/devicestorage/test/test_add.html @@ -13,57 +13,58 @@ https://bugzilla.mozilla.org/show_bug.cg <script type="text/javascript" src="devicestorage_common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=786922">Mozilla Bug 786922</a> <p id="display"></p> <div id="content" style="display: none"> - + </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { function add(storage, mime) { dump("adding: " + mime + "\n"); return navigator.getDeviceStorage(storage).add(createRandomBlob(mime)); } var tests = [ function () { return add("pictures", "image/png")}, function () { return add("videos", "video/webm")}, function () { return add("music", "audio/wav")}, function () { return add("sdcard", "maple/cookies")}, ]; function fail(e) { ok(false, "onerror was called"); - devicestorage_cleanup(); + SimpleTest.finish(); } function next(e) { if (e != undefined) ok(true, "addError was called"); - + var f = tests.pop(); if (f == undefined) { - devicestorage_cleanup(); + SimpleTest.finish(); return; } request = f(); request.onsuccess = next; request.onerror = fail; } next(); +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_addCorrectType.html +++ b/dom/devicestorage/test/test_addCorrectType.html @@ -13,23 +13,23 @@ https://bugzilla.mozilla.org/show_bug.cg <script type="text/javascript" src="devicestorage_common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=786922">Mozilla Bug 786922</a> <p id="display"></p> <div id="content" style="display: none"> - + </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { function addNamed(storage, mime, fileExtension) { dump("adding: " + mime + " " + fileExtension + "\n"); return navigator.getDeviceStorage(storage).addNamed(createRandomBlob(mime), randomFilename(40) + "." + fileExtension); } // These tests must all fail var tests = [ @@ -40,35 +40,36 @@ var tests = [ function () { return addNamed("videos", "kyle/smash", ".ogv")}, function () { return addNamed("videos", "video/ogv", ".poo")}, ]; function fail(e) { ok(false, "addSuccess was called"); ok(e.target.error.name == "TypeMismatchError", "Error must be TypeMismatchError"); - devicestorage_cleanup(); + SimpleTest.finish(); } function next(e) { if (e != undefined) ok(true, "addError was called"); - + var f = tests.pop(); if (f == undefined) { - devicestorage_cleanup(); + SimpleTest.finish(); return; } request = f(); request.onsuccess = fail; request.onerror = next; } next(); +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_available.html +++ b/dom/devicestorage/test/test_available.html @@ -17,32 +17,34 @@ https://bugzilla.mozilla.org/show_bug.cg <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=834595">Mozilla Bug 834595</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { function availableSuccess(e) { isnot(e.target.result, null, "result should not be null"); - devicestorage_cleanup(); + SimpleTest.finish(); } function availableError(e) { ok(false, "availableError was called"); - devicestorage_cleanup(); + SimpleTest.finish(); } var storage = navigator.getDeviceStorage("pictures"); request = storage.available(); ok(request, "Should have a non-null request"); request.onsuccess = availableSuccess; request.onerror = availableError; +}); + </script> </pre> </body> </html>
--- a/dom/devicestorage/test/test_basic.html +++ b/dom/devicestorage/test/test_basic.html @@ -12,53 +12,53 @@ https://bugzilla.mozilla.org/show_bug.cg <script type="text/javascript" src="devicestorage_common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a> <p id="display"></p> <div id="content" style="display: none"> - + </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function() { var gFileName = "devicestorage/" + randomFilename(12) + "/hi.png"; var gData = "My name is Doug Turner. My IRC nick is DougT. I like Maple cookies." var gDataBlob = new Blob([gData], {type: 'image/png'}); var gFileReader = new FileReader(); function getAfterDeleteSuccess(e) { ok(false, "file was deleted not successfully"); - devicestorage_cleanup(); + SimpleTest.finish(); } function getAfterDeleteError(e) { ok(true, "file was deleted successfully"); - devicestorage_cleanup(); + SimpleTest.finish(); } function deleteSuccess(e) { ok(e.target.result == gFileName, "File name should match"); dump(e.target.result + "\n") var storage = navigator.getDeviceStorage("pictures"); request = storage.get(e.target.result); request.onsuccess = getAfterDeleteSuccess; request.onerror = getAfterDeleteError; } function deleteError(e) { ok(false, "deleteError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function getSuccess(e) { var storage = navigator.getDeviceStorage("pictures"); ok(navigator.getDeviceStorage, "Should have getDeviceStorage"); ok(e.target.result.name == gFileName, "File name should match"); ok(e.target.result.size > 0, "File size be greater than zero"); @@ -83,17 +83,17 @@ function readerCallback(e) { is(ab.byteLength, gData.length, "wrong arraybuffer byteLength"); var u8v = new Uint8Array(ab); is(String.fromCharCode.apply(String, u8v), gData, "wrong values"); } function getError(e) { ok(false, "getError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function addSuccess(e) { var filename = e.target.result; if (filename[0] == "/") { // We got /storageName/prefix/filename // Remove the storageName (this shows up on FirefoxOS) @@ -114,27 +114,28 @@ function addSuccess(e) { request.onsuccess = getSuccess; request.onerror = getError; ok(true, "addSuccess was called"); } function addError(e) { ok(false, "addError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } ok(navigator.getDeviceStorage, "Should have getDeviceStorage"); var storage = navigator.getDeviceStorage("pictures"); ok(storage, "Should have gotten a storage"); request = storage.addNamed(gDataBlob, gFileName); ok(request, "Should have a non-null request"); request.onsuccess = addSuccess; request.onerror = addError; +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_diskSpace.html +++ b/dom/devicestorage/test/test_diskSpace.html @@ -12,39 +12,38 @@ https://bugzilla.mozilla.org/show_bug.cg <script type="text/javascript" src="devicestorage_common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a> <p id="display"></p> <div id="content" style="display: none"> - + </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); - +devicestorage_setup(function () { var freeBytes = -1; var stats = 0; function stat(s, file_list_length) { if (freeBytes == -1) { freeBytes = s.target.result.freeBytes; } ok(freeBytes == s.target.result.freeBytes, "Free bytes should be the same"); ok(file_list_length * 1024 == s.target.result.totalBytes, "space taken up by files should match") stats = stats + 1; if (stats == 2) { - devicestorage_cleanup(); + SimpleTest.finish(); } } function addSuccess(e) { added = added - 1; if (added == 0) { request = pictures.stat(); @@ -55,17 +54,17 @@ function addSuccess(e) { request = music.stat(); request.onsuccess = function(s) {stat(s, music_files.length)}; } } function addError(e) { ok(false, "addError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } ok(true, "hi"); var pictures = navigator.getDeviceStorage("pictures"); var picture_files = [ "a.png", "b.png", "c.png", "d.png", "e.png" ]; var videos = navigator.getDeviceStorage("videos"); @@ -89,13 +88,14 @@ for (var i=0; i < video_files.length; i+ } for (var i=0; i < music_files.length; i++) { request = music.addNamed(createRandomBlob('audio/mp3'), music_files[i]); request.onsuccess = addSuccess; request.onerror = addError; } +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_dotdot.html +++ b/dom/devicestorage/test/test_dotdot.html @@ -12,22 +12,22 @@ https://bugzilla.mozilla.org/show_bug.cg <script type="text/javascript" src="devicestorage_common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a> <p id="display"></p> <div id="content" style="display: none"> - + </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { function testingStorage() { return navigator.getDeviceStorage("pictures"); } var tests = [ function () { return testingStorage().addNamed(createRandomBlob('image/png'), gFileName); }, function () { return testingStorage().delete(gFileName); }, @@ -35,39 +35,38 @@ var tests = [ function () { var r = testingStorage().enumerate("../"); return r; } ]; var gFileName = "../owned.png"; function fail(e) { ok(false, "addSuccess was called"); dump(request); - devicestorage_cleanup(); + SimpleTest.finish(); } function next(e) { if (e != undefined) { - ok(true, "addError was called"); + ok(true, "addError was called"); ok(e.target.error.name == "SecurityError", "Error must be SecurityError"); } var f = tests.pop(); if (f == undefined) { - devicestorage_cleanup(); + SimpleTest.finish(); return; } request = f(); request.onsuccess = fail; request.onerror = next; } next(); - +}); </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_enumerate.html +++ b/dom/devicestorage/test/test_enumerate.html @@ -12,32 +12,32 @@ https://bugzilla.mozilla.org/show_bug.cg <script type="text/javascript" src="devicestorage_common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a> <p id="display"></p> <div id="content" style="display: none"> - + </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { function enumerateSuccess(e) { if (e.target.result == null) { ok(files.length == 0, "when the enumeration is done, we shouldn't have any files in this array") dump("We still have length = " + files.length + "\n"); - devicestorage_cleanup(); + SimpleTest.finish(); return; } - + var filename = e.target.result.name; if (filename[0] == "/") { // We got /storageName/prefix/filename // Remove the storageName (this shows up on FirefoxOS) filename = filename.substring(1); // Remove leading slash var slashIndex = filename.indexOf("/"); if (slashIndex >= 0) { filename = filename.substring(slashIndex + 1); // Remove storageName @@ -56,32 +56,32 @@ function enumerateSuccess(e) { var cleanup = storage.delete(e.target.result.name); cleanup.onsuccess = function(e) {} // todo - can i remove this? e.target.continue(); } function handleError(e) { ok(false, "handleError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function addSuccess(e) { addedSoFar = addedSoFar + 1; if (addedSoFar == files.length) { var cursor = storage.enumerate(prefix); cursor.onsuccess = enumerateSuccess; cursor.onerror = handleError; } } function addError(e) { ok(false, "addError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } var storage = navigator.getDeviceStorage("pictures"); ok(navigator.getDeviceStorage, "Should have getDeviceStorage"); var prefix = "devicestorage/" + randomFilename(12) + ".png" var files = [ "a.PNG", "b.pnG", "c.png", "d/a.png", "d/b.png", "d/c.png", "d/d.png", "The/quick/brown/fox/jumps/over/the/lazy/dog.png"] var addedSoFar = 0; @@ -91,13 +91,14 @@ for (var i=0; i<files.length; i++) { request = storage.addNamed(createRandomBlob('image/png'), prefix + '/' + files[i]); ok(request, "Should have a non-null request"); request.onsuccess = addSuccess; request.onerror = addError; } +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_enumerateMultipleContinue.html +++ b/dom/devicestorage/test/test_enumerateMultipleContinue.html @@ -12,39 +12,39 @@ https://bugzilla.mozilla.org/show_bug.cg <script type="text/javascript" src="devicestorage_common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a> <p id="display"></p> <div id="content" style="display: none"> - + </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { function enumerateSuccess(e) { } function enumerateFailure(e) { } var cursor = navigator.getDeviceStorage("pictures").enumerate(); cursor.onsuccess = enumerateSuccess; cursor.onerror = enumerateFailure; try { cursor.continue(); } catch (e) { ok(true, "Calling continue before enumerateSuccess fires should throw"); - devicestorage_cleanup(); + SimpleTest.finish(); } +}); </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_enumerateNoParam.html +++ b/dom/devicestorage/test/test_enumerateNoParam.html @@ -30,17 +30,17 @@ Array.prototype.remove = function(from, }; devicestorage_setup(); function enumerateSuccess(e) { if (e.target.result == null) { ok(files.length == 0, "when the enumeration is done, we shouldn't have any files in this array") - devicestorage_cleanup(); + SimpleTest.finish(); return; } var filename = e.target.result.name; if (filename[0] == "/") { // We got /storageName/prefix/filename // Remove the storageName (this shows up on FirefoxOS) filename = filename.substring(1); // Remove leading slash @@ -62,32 +62,32 @@ function enumerateSuccess(e) { var cleanup = storage.delete(e.target.result.name); cleanup.onsuccess = function(e) {} // todo - can i remove this? e.target.continue(); } function handleError(e) { ok(false, "handleError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function addSuccess(e) { addedSoFar = addedSoFar + 1; if (addedSoFar == files.length) { var cursor = storage.enumerate(); cursor.onsuccess = enumerateSuccess; cursor.onerror = handleError; } } function addError(e) { ok(false, "addError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } var storage = navigator.getDeviceStorage("pictures"); ok(navigator.getDeviceStorage, "Should have getDeviceStorage"); var prefix = "devicestorage/" + randomFilename(12) var files = [ "a.png", "b.png", "c.png" ] var addedSoFar = 0;
--- a/dom/devicestorage/test/test_enumerateOptions.html +++ b/dom/devicestorage/test/test_enumerateOptions.html @@ -13,22 +13,22 @@ https://bugzilla.mozilla.org/show_bug.cg <script type="text/javascript" src="devicestorage_common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a> <p id="display"></p> <div id="content" style="display: none"> - + </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup() +devicestorage_setup(function () { storage = navigator.getDeviceStorage("pictures"); throws = false; try { var cursor = storage.enumerate(); } catch(e) {throws = true} @@ -65,17 +65,16 @@ var cursor = storage.enumerate({}, "stri ok(throws, "enumerate object then a string"); throws = false; try { var cursor = storage.enumerate({"path": "a", "since": new Date(0) }); } catch(e) {throws = true} ok(!throws, "enumerate object parameter with path"); - +SimpleTest.finish() +}); -devicestorage_cleanup() </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_freeSpace.html +++ b/dom/devicestorage/test/test_freeSpace.html @@ -17,45 +17,46 @@ https://bugzilla.mozilla.org/show_bug.cg <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { function freeSpaceSuccess(e) { ok(e.target.result > 0, "free bytes should exist and be greater than zero"); - devicestorage_cleanup(); + SimpleTest.finish(); } function freeSpaceError(e) { ok(false, "freeSpaceError was called"); - devicestorage_cleanup(); + SimpleTest.finish(); } var storage = navigator.getDeviceStorage("pictures"); function addError(e) { ok(false, "addError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function addSuccess(e) { request = storage.freeSpace(); ok(request, "Should have a non-null request"); request.onsuccess = freeSpaceSuccess; request.onerror = freeSpaceError; } var prefix = "devicestorage/" + randomFilename(12); request = storage.addNamed(createRandomBlob('image/png'), prefix + "/a/b.png"); request.onsuccess = addSuccess; request.onerror = addError; +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_fs_appendFile.html +++ b/dom/devicestorage/test/test_fs_appendFile.html @@ -21,38 +21,38 @@ https://bugzilla.mozilla.org/show_bug.cg </div> <pre id="test"> <script class="testbody" type="text/javascript"> var file = new Blob(["This is a text file."], {type: "text/plain"}); var appendFile = new Blob([" Another text file."], {type: "text/plain"}); -devicestorage_setup(); +devicestorage_setup(function () { function deleteSuccess(e) { - devicestorage_cleanup(); + SimpleTest.finish(); } function deleteError(e) { ok(false, "deleteError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function appendSuccess(e) { ok(true, "appendSuccess was called."); request = gStorage.delete(e.target.result) request.onsuccess = deleteSuccess; request.onerror = deleteError; } function appendError(e) { ok(false, "appendError was called."); - devicestorage_cleanup(); + SimpleTest.finish(); } function addSuccess(e) { ok(true, "addSuccess was called"); request = gStorage.appendNamed(appendFile, e.target.result); ok(request, "Should have a non-null request"); @@ -78,13 +78,14 @@ function runtest() { addFile(); } var gStorage = navigator.getDeviceStorage("sdcard"); ok(navigator.getDeviceStorage, "Should have getDeviceStorage"); ok(gStorage, "Should get storage from sdcard"); runtest(); +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_fs_basic.html +++ b/dom/devicestorage/test/test_fs_basic.html @@ -16,17 +16,17 @@ https://bugzilla.mozilla.org/show_bug.cg <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=910412">Mozilla Bug 910412</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { var gFileName = randomFilename(12); // The root directory object. var gRoot; function getRootSuccess(r) { ok(r && r.name === storage.storageName, "Failed to get the root directory."); @@ -41,30 +41,32 @@ function createDirectorySuccess(d) { ok(d.name === gFileName, "Failed to create directory: name mismatch."); // Get the new created directory from the root. gRoot.get(gFileName).then(getSuccess, cbError); } function getSuccess(d) { ok(d.name === gFileName, "Should get directory - " + gFileName + "."); - devicestorage_cleanup(); + SimpleTest.finish(); } function cbError(e) { ok(false, "Should not arrive here! Error: " + e.name); - devicestorage_cleanup(); + SimpleTest.finish(); } ok(navigator.getDeviceStorage, "Should have getDeviceStorage"); var storage = navigator.getDeviceStorage("pictures"); ok(storage, "Should have gotten a storage"); var promise = storage.getRoot(); ok(promise, "Should have a non-null promise"); promise.then(getRootSuccess, cbError); + +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_fs_createDirectory.html +++ b/dom/devicestorage/test/test_fs_createDirectory.html @@ -16,17 +16,17 @@ https://bugzilla.mozilla.org/show_bug.cg <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=910412">Mozilla Bug 910412</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { // The root directory object. var gRoot; var gTestCount = 0; var gPath = ''; var gName = ''; function testCreateDirectory(rootDir, path) { @@ -54,52 +54,54 @@ function getSuccess(d) { gName = randomFilename(12); gPath = gPath + '/' + gName; testCreateDirectory(d, gName); break; case 2: // Create directory with an existing path. gRoot.createDirectory(gPath).then(function(what) { ok(false, "Should not overwrite an existing directory."); - devicestorage_cleanup(); + SimpleTest.finish(); }, function(e) { ok(true, "Creating directory should fail if it already exists."); // Create a directory whose intermediate directory doesn't exit. gName = randomFilename(12); gPath = 'sub1/sub2/' + gName; testCreateDirectory(gRoot, gPath); }); break; default: // Create the parent directory. d.createDirectory('..').then(function(what) { ok(false, "Should not overwrite an existing directory."); - devicestorage_cleanup(); + SimpleTest.finish(); }, function(e) { ok(true, "Accessing parent directory with '..' is not allowed."); - devicestorage_cleanup(); + SimpleTest.finish(); }); break; } gTestCount++; } function cbError(e) { ok(false, e.name + " error should not arrive here!"); - devicestorage_cleanup(); + SimpleTest.finish(); } ok(navigator.getDeviceStorage, "Should have getDeviceStorage."); var storage = navigator.getDeviceStorage("pictures"); ok(storage, "Should have gotten a storage."); var promise = storage.getRoot(); ok(promise, "Should have a non-null promise for getRoot."); gName = storage.storageName; promise.then(getSuccess, cbError); + +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_fs_createFile.html +++ b/dom/devicestorage/test/test_fs_createFile.html @@ -16,17 +16,17 @@ https://bugzilla.mozilla.org/show_bug.cg <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=910412">Mozilla Bug 910412</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script class="testbody" type="application/javascript;version=1.7"> -devicestorage_setup(); +devicestorage_setup(function () { let gTestCount = 0; let gFileReader = new FileReader(); let gRoot; function str2array(str) { let strlen = str.length; let buf = new ArrayBuffer(strlen); @@ -79,17 +79,17 @@ let gTestCases = [ data: null, shouldPass: false, mode: "fail" } ]; function next() { if (gTestCount >= gTestCases.length) { - devicestorage_cleanup(); + SimpleTest.finish(); return; } let c = gTestCases[gTestCount++]; gRoot.createFile("text.png", { data: c.data, ifExists: c.mode }).then(function(file) { is(c.shouldPass, true, "[" + gTestCount + "] Success callback was called for createFile."); @@ -117,16 +117,17 @@ ok(storage, "Should have gotten a storag // Get the root directory storage.getRoot().then(function(dir) { ok(dir, "Should have gotten the root directory."); gRoot = dir; next(); }, function(e) { ok(false, e.name + " error should not arrive here!"); - devicestorage_cleanup(); + SimpleTest.finish(); +}); + }); </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_fs_get.html +++ b/dom/devicestorage/test/test_fs_get.html @@ -16,17 +16,18 @@ https://bugzilla.mozilla.org/show_bug.cg <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=910412">Mozilla Bug 910412</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { + SimpleTest.requestCompleteLog(); // The root directory object. var gRoot = null; var gSub1 = null; var gSub2 = null; var gTestCount = 0; var gPath = "/"; @@ -72,17 +73,17 @@ function getSuccess(r) { break; case 5: // Get sub1 from sub1/sub2 with "..". gPath = "sub1/sub2/.."; testGetFailure(gSub2, ".."); break; default: ok(false, "Should not arrive at getSuccess!"); - devicestorage_cleanup(); + SimpleTest.finish(); break; } gTestCount++; } function getFailure(e) { ok(true, "[" + gTestCount +"] Should not get the file - " + gPath + ". Error: " + e.name); switch (gTestCount) { @@ -132,34 +133,34 @@ function getFailure(e) { testGetFailure(gSub2, ""); break; case 16: // Test special path "//". gPath = "sub1//sub2"; testGetFailure(gRoot, "sub1//sub2"); break; case 17: - devicestorage_cleanup(); + SimpleTest.finish(); break; default: ok(false, "Should not arrive here!"); - devicestorage_cleanup(); + SimpleTest.finish(); break; } gTestCount++; } function cbError(e) { ok(false, "Should not arrive at cbError! Error: " + e.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function cbSuccess(e) { ok(false, "Should not arrive at cbSuccess!"); - devicestorage_cleanup(); + SimpleTest.finish(); } ok(navigator.getDeviceStorage, "Should have getDeviceStorage."); var gStorage = navigator.getDeviceStorage("pictures"); ok(gStorage, "Should have gotten a storage."); function createTestFile(path, callback) { @@ -168,17 +169,17 @@ function createTestFile(path, callback) req.onsuccess = function() { ok(true, path + " was created."); callback(); }; req.onerror = function(e) { ok(false, "Failed to create " + path + ": " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); }; } // Bug 980136. Check if the file exists before we create. var req = gStorage.get(path); req.onsuccess = function() { ok(true, path + " exists. Do not need to create."); @@ -192,13 +193,14 @@ function createTestFile(path, callback) } createTestFile("sub1/sub2/test.png", function() { var promise = gStorage.getRoot(); ok(promise, "Should have a non-null promise for getRoot."); promise.then(getSuccess, cbError); }); +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_fs_getFilesAndDirectories.html +++ b/dom/devicestorage/test/test_fs_getFilesAndDirectories.html @@ -16,17 +16,17 @@ https://bugzilla.mozilla.org/show_bug.cg <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=XXX">Mozilla Bug XXX</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { SimpleTest.requestCompleteLog(); // The root directory object. var gRoot = null; function checkContents1(contents) { var expected = { "sub2": "/sub", @@ -57,22 +57,22 @@ function checkContents1(contents) { ok(false, "Expected '" + missing.name + "' in /sub"); } sub2.getFilesAndDirectories().then(checkContents2, handleError); } function checkContents2(contents) { is(contents[0].name, "c.png", "'sub2' should contain 'c.png'"); - devicestorage_cleanup(); + SimpleTest.finish(); } function handleError(e) { ok(false, "Should not arrive at handleError! Error: " + e.name); - devicestorage_cleanup(); + SimpleTest.finish(); } var gStorage = navigator.getDeviceStorage("pictures"); ok(gStorage, "Should have gotten a storage."); function runTests() { gStorage.getRoot().then(function(rootDir) { @@ -82,16 +82,17 @@ function runTests() { return subDir.getFilesAndDirectories(); }).then(checkContents1).catch(handleError); } createTestFiles(gStorage, ["sub/a.png", "sub/b.png", "sub/sub2/c.png", "sub/sub3/d.png"]).then(function() { runTests(); }, function() { ok(false, "Failed to created test files."); - devicestorage_cleanup(); + SimpleTest.finish(); +}); + }); </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_fs_remove.html +++ b/dom/devicestorage/test/test_fs_remove.html @@ -16,17 +16,17 @@ https://bugzilla.mozilla.org/show_bug.cg <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=934368">Mozilla Bug 934368</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script class="testbody" type="application/javascript;version=1.7"> -devicestorage_setup(); +devicestorage_setup(function () { let gStorage = null; let gTestCount = 0; let gFileMap = {}; let gRemoveDeep = true; let gTestCases = [ // Remove a non-existent file should return false. @@ -88,17 +88,17 @@ let gTestCases = [ } ]; function runNextTests() { gTestCount = 0; function runTests() { function cbError(e) { ok(false, "Should not arrive at cbError! Error: " + e.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function cbSuccess(r) { ok(r, "Should get the file - " + this); gFileMap[this] = r; } // Get directory and file objects. @@ -111,25 +111,25 @@ function runNextTests() { ["sub1", "sub1/sub2", "sub1/sub2/a.png", "sub/b.png"].forEach(function(path) { arr.push(root.get(path).then(cbSuccess.bind(path), cbError)); }); Promise.all(arr).then(function() { testNextRemove(); }, function() { ok(false, "Failed to get test files."); - devicestorage_cleanup(); + SimpleTest.finish(); }); }, cbError); }; createTestFiles(gStorage, ["sub1/sub2/a.png", "sub/b.png"]).then(function() { runTests(); }, function() { ok(false, "Failed to created test files."); - devicestorage_cleanup(); + SimpleTest.finish(); }); } function testNextRemove() { if (gTestCount < gTestCases.length) { let data = gTestCases[gTestCount++]; let dir = gFileMap[data.dir]; let path = data.path || gFileMap[data.target]; @@ -151,25 +151,26 @@ function testNextRemove() { if (gRemoveDeep) { // Test "remove" after "removeDeep". gRemoveDeep = false; runNextTests(); return; } - devicestorage_cleanup(); + SimpleTest.finish(); } ok(navigator.getDeviceStorage, "Should have getDeviceStorage."); gStorage = navigator.getDeviceStorage("pictures"); ok(gStorage, "Should have gotten a storage."); // Test "removeDeep" first. gRemoveDeep = true; runNextTests(); +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_lastModificationFilter.html +++ b/dom/devicestorage/test/test_lastModificationFilter.html @@ -12,23 +12,24 @@ https://bugzilla.mozilla.org/show_bug.cg <script type="text/javascript" src="devicestorage_common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a> <p id="display"></p> <div id="content" style="display: none"> - + </div> <pre id="test"> <script class="testbody" type="text/javascript"> SimpleTest.requestFlakyTimeout("untriaged"); -devicestorage_setup(); +devicestorage_setup(function () { + // We put the old files in 2 levels deep. When you add a file to a directory // it will modify the parents last modification time, but not the parents // parents. So we want to make sure that even though x's timestamp is earlier // than the since parameter, we still pick up the later files. var oldFiles = ["x/y/aa.png", "x/y/ab.png", "x/y/ac.png"]; var timeFile = "t.png"; var newFiles = ["x/y/ad.png", "x/y/ae.png", "x/y/af.png", // new files in old dir "z/bd.png", "z/be.png", "z/bf.png"]; // new files in new dir @@ -39,17 +40,17 @@ var i; var timestamp; var lastFileAddedTimestamp; function verifyAndDelete(prefix, files, e) { if (e.target.result == null) { ok(files.length == 0, "when the enumeration is done, we shouldn't have any files in this array") dump("We still have length = " + files.length + "\n"); dump(files + "\n"); - devicestorage_cleanup(); + SimpleTest.finish(); return; } var filename = e.target.result.name; if (filename[0] == "/") { // We got /storageName/prefix/filename // Remove the storageName (this shows up on FirefoxOS) filename = filename.substring(1); // Remove leading slash @@ -81,22 +82,22 @@ function addFile(filename, callback) { // that we just added var getReq = storage.get(prefix + '/' + filename); getReq.onsuccess = function(e) { lastFileAddedTimestamp = e.target.result.lastModifiedDate; callback(); } getReq.onerror = function(e) { ok(false, "getError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); }; } addReq.onerror = function(e) { ok(false, "addError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } } function addFileArray(fileArray, callback) { var i = 0; function addNextFile() { i = i + 1; if (i == fileArray.length) { @@ -110,31 +111,31 @@ function addFileArray(fileArray, callbac function delFile(filename, callback) { var req = storage.delete(prefix + '/' + filename); req.onsuccess = function(e) { callback(); }; req.onerror = function(e) { ok(false, "delError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); }; } function afterNewFiles() { var cursor = storage.enumerate(prefix, {"since": timestamp}); cursor.onsuccess = function(e) { verifyAndDelete(prefix, newFiles, e); if (e.target.result) { e.target.continue(); } }; cursor.onerror = function (e) { ok(false, "handleError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); }; } function waitForTimestampChange() { // We've added a new file. See if the timestamp differs from // oldFileAddedTimestamp, and if it's the same wait for a bit // and try again. if (lastFileAddedTimestamp.valueOf() === timestamp.valueOf()) { @@ -162,13 +163,14 @@ function afterOldFiles() { } function addOldFiles() { addFileArray(oldFiles, afterOldFiles); } addOldFiles(); +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_overrideDir.html +++ b/dom/devicestorage/test/test_overrideDir.html @@ -12,36 +12,36 @@ https://bugzilla.mozilla.org/show_bug.cg <script type="text/javascript" src="devicestorage_common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a> <p id="display"></p> <div id="content" style="display: none"> - + </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { var gFileName = "devicestorage/" + randomFilename(12) + "/hi.png"; var gData = "My name is Doug Turner. My IRC nick is DougT. I like Maple cookies." var gDataBlob = new Blob([gData], {type: 'image/png'}); var gFileReader = new FileReader(); function getAfterDeleteSuccess(e) { ok(false, "file was deleted not successfully"); - devicestorage_cleanup(); + SimpleTest.finish(); } function getAfterDeleteError(e) { ok(true, "file was deleted successfully"); - devicestorage_cleanup(); + SimpleTest.finish(); } function deleteSuccess(e) { ok(e.target.result == gFileName, "File name should match"); dump(e.target.result + "\n") // File was deleted using the sdcard stoage area. It should be gone @@ -49,17 +49,17 @@ function deleteSuccess(e) { var storage = navigator.getDeviceStorage("pictures"); request = storage.get(e.target.result); request.onsuccess = getAfterDeleteSuccess; request.onerror = getAfterDeleteError; } function deleteError(e) { ok(false, "deleteError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function getSuccess(e) { // We wrote the file out using pictures type. Since we've over-ridden the // root directory, we should be able to read it back using the sdcard // storage area. var storage = navigator.getDeviceStorage("sdcard"); ok(navigator.getDeviceStorage, "Should have getDeviceStorage"); @@ -87,17 +87,17 @@ function readerCallback(e) { is(ab.byteLength, gData.length, "wrong arraybuffer byteLength"); var u8v = new Uint8Array(ab); is(String.fromCharCode.apply(String, u8v), gData, "wrong values"); } function getError(e) { ok(false, "getError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function addSuccess(e) { var filename = e.target.result; if (filename[0] == "/") { // We got /storageName/prefix/filename // Remove the storageName (this shows up on FirefoxOS) @@ -118,17 +118,17 @@ function addSuccess(e) { request.onsuccess = getSuccess; request.onerror = getError; ok(true, "addSuccess was called"); } function addError(e) { ok(false, "addError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function startTest() { ok(navigator.getDeviceStorage, "Should have getDeviceStorage"); var storage = navigator.getDeviceStorage("pictures"); ok(storage, "Should have gotten a storage"); @@ -151,13 +151,14 @@ try { } catch (e) {} SpecialPowers.pushPrefEnv({'set': [["device.storage.overrideRootDir", f.path], ["device.storage.testing", false]]}, function() { startTest(); }); } catch(e) {} +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_overwrite.html +++ b/dom/devicestorage/test/test_overwrite.html @@ -13,38 +13,38 @@ https://bugzilla.mozilla.org/show_bug.cg <script type="text/javascript" src="devicestorage_common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a> <p id="display"></p> <div id="content" style="display: none"> - + </div> <pre id="test"> <script class="testbody" type="text/javascript"> var filename = "devicestorage/aaaa.png" -devicestorage_setup(); +devicestorage_setup(function () { function deleteSuccess(e) { - devicestorage_cleanup(); + SimpleTest.finish(); } function deleteError(e) { ok(false, "deleteError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function addOverwritingSuccess(e) { ok(false, "addOverwritingSuccess was called."); - devicestorage_cleanup(); + SimpleTest.finish(); } function addOverwritingError(e) { ok(true, "Adding to the same location should fail"); ok(e.target.error.name == "NoModificationAllowedError", "Error must be NoModificationAllowedError"); var storage = navigator.getDeviceStorage("pictures"); request = storage.delete(filename) @@ -80,13 +80,14 @@ function runtest() { ok(request, "Should have a non-null request"); request.onsuccess = addSuccess; request.onerror = addError; } runtest(); +}); + </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_sanity.html +++ b/dom/devicestorage/test/test_sanity.html @@ -13,22 +13,22 @@ https://bugzilla.mozilla.org/show_bug.cg <script type="text/javascript" src="devicestorage_common.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a> <p id="display"></p> <div id="content" style="display: none"> - + </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup() +devicestorage_setup(function () { ok(navigator.getDeviceStorage, "Should have getDeviceStorage"); var storage; var throws = false; try { storage = navigator.getDeviceStorage(); @@ -45,15 +45,16 @@ storage = navigator.getDeviceStorage("mu ok(storage, "music - Should have getDeviceStorage"); storage = navigator.getDeviceStorage("videos"); ok(storage, "videos - Should have getDeviceStorage"); var cursor = storage.enumerate(); ok(cursor, "Should have a non-null cursor"); -devicestorage_cleanup(); +SimpleTest.finish(); + +}); </script> </pre> </body> </html> -
--- a/dom/devicestorage/test/test_storageAreaListener.html +++ b/dom/devicestorage/test/test_storageAreaListener.html @@ -18,17 +18,17 @@ https://bugzilla.mozilla.org/show_bug.cg <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1126694">Mozilla Bug 1126684</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script class="testbody" type="text/javascript"> - devicestorage_setup() + devicestorage_setup(function () { var XPCOMUtils = SpecialPowers.Cu.import("resource://gre/modules/XPCOMUtils.jsm").XPCOMUtils; var Ci = SpecialPowers.Ci; var volumeService = SpecialPowers.Cc["@mozilla.org/telephony/volume-service;1"].getService(Ci.nsIVolumeService); var volName = "dummy-volume"; var mountPoint = "/data/local/tmp/dummy"; @@ -44,21 +44,23 @@ https://bugzilla.mozilla.org/show_bug.cg if (e.operation == "added") { storage = navigator.getDeviceStorageByNameAndType(e.storageName, "sdcard"); ok (storage, "got storage"); volumeService.removeFakeVolume(volName); } else if (e.operation == "removed") { ok (true, "got removal event"); - devicestorage_cleanup(); + SimpleTest.finish(); } }); storage = navigator.getDeviceStorageByNameAndType(volName, "sdcard"); ok(!storage, "storage area doesn't exist"); volumeService.createFakeVolume(volName, mountPoint); + }); + </script> </pre> </body> </html>
--- a/dom/devicestorage/test/test_usedSpace.html +++ b/dom/devicestorage/test/test_usedSpace.html @@ -17,45 +17,47 @@ https://bugzilla.mozilla.org/show_bug.cg <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { function usedSpaceSuccess(e) { ok(e.target.result > 0, "total bytes should exist and be greater than zero"); - devicestorage_cleanup(); + SimpleTest.finish(); } function usedSpaceError(e) { ok(false, "usedSpaceError was called"); - devicestorage_cleanup(); + SimpleTest.finish(); } var storage = navigator.getDeviceStorage("pictures"); function addError(e) { ok(false, "addError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function addSuccess(e) { request = storage.usedSpace(); ok(request, "Should have a non-null request"); request.onsuccess = usedSpaceSuccess; request.onerror = usedSpaceError; } var prefix = "devicestorage/" + randomFilename(12); request = storage.addNamed(createRandomBlob('image/png'), prefix + "/a/b.png"); request.onsuccess = addSuccess; request.onerror = addError; +}); + </script> </pre> </body> </html>
--- a/dom/devicestorage/test/test_watch.html +++ b/dom/devicestorage/test/test_watch.html @@ -17,26 +17,26 @@ https://bugzilla.mozilla.org/show_bug.cg <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { var gFileName = randomFilename(20) + ".png" function addSuccess(e) { } function addError(e) { ok(false, "addError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function onChange(e) { dump("we saw: " + e.path + " " + e.reason + "\n"); var filename = e.path; if (filename[0] == "/") { @@ -46,17 +46,17 @@ function onChange(e) { var slashIndex = filename.indexOf("/"); if (slashIndex >= 0) { filename = filename.substring(slashIndex + 1); // Remove storageName } } if (filename == gFileName) { ok(true, "we saw the file get created"); storage.removeEventListener("change", onChange); - devicestorage_cleanup(); + SimpleTest.finish(); } else { // we may see other file changes during the test, and // that is completely ok } } var storage = navigator.getDeviceStorage("pictures"); @@ -64,12 +64,14 @@ ok(storage, "Should have storage"); storage.addEventListener("change", onChange); request = storage.addNamed(createRandomBlob('image/png'), gFileName); ok(request, "Should have a non-null request"); request.onsuccess = addSuccess; request.onerror = addError; +}); + </script> </pre> </body> </html>
--- a/dom/devicestorage/test/test_watchOther.html +++ b/dom/devicestorage/test/test_watchOther.html @@ -17,26 +17,26 @@ https://bugzilla.mozilla.org/show_bug.cg <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script class="testbody" type="text/javascript"> -devicestorage_setup(); +devicestorage_setup(function () { var gFileName = randomFilename(20) + ".png" function addSuccess(e) { } function addError(e) { ok(false, "addError was called : " + e.target.error.name); - devicestorage_cleanup(); + SimpleTest.finish(); } function onChange(e) { dump("we saw: " + e.path + " " + e.reason + "\n"); var filename = e.path; if (filename[0] == "/") { @@ -46,17 +46,17 @@ function onChange(e) { var slashIndex = filename.indexOf("/"); if (slashIndex >= 0) { filename = filename.substring(slashIndex + 1); // Remove storageName } } if (filename == gFileName) { ok(true, "we saw the file get created"); storage.removeEventListener("change", onChange); - devicestorage_cleanup(); + SimpleTest.finish(); } else { // we may see other file changes during the test, and // that is completely ok } } function onChangeFail(e) { @@ -73,12 +73,14 @@ ok(storageOther, "Should have storage"); storageOther.addEventListener("change", onChangeFail); request = storage.addNamed(createRandomBlob('image/png'), gFileName); ok(request, "Should have a non-null request"); request.onsuccess = addSuccess; request.onerror = addError; +}); + </script> </pre> </body> </html>
--- a/dom/encoding/TextDecoder.cpp +++ b/dom/encoding/TextDecoder.cpp @@ -21,17 +21,17 @@ TextDecoder::Init(const nsAString& aLabe { nsAutoCString encoding; // Let encoding be the result of getting an encoding from label. // If encoding is failure or replacement, throw a RangeError // (https://encoding.spec.whatwg.org/#dom-textdecoder). if (!EncodingUtils::FindEncodingForLabelNoReplacement(aLabel, encoding)) { nsAutoString label(aLabel); EncodingUtils::TrimSpaceCharacters(label); - aRv.ThrowRangeError<MSG_ENCODING_NOT_SUPPORTED>(&label); + aRv.ThrowRangeError<MSG_ENCODING_NOT_SUPPORTED>(label); return; } InitWithEncoding(encoding, aFatal); } void TextDecoder::InitWithEncoding(const nsACString& aEncoding, const bool aFatal) {
--- a/dom/encoding/TextEncoder.cpp +++ b/dom/encoding/TextEncoder.cpp @@ -16,17 +16,17 @@ TextEncoder::Init(const nsAString& aEnco { nsAutoString label(aEncoding); EncodingUtils::TrimSpaceCharacters(label); // Let encoding be the result of getting an encoding from label. // If encoding is failure, or is none of utf-8, utf-16, and utf-16be, // throw a RangeError (https://encoding.spec.whatwg.org/#dom-textencoder). if (!EncodingUtils::FindEncodingForLabel(label, mEncoding)) { - aRv.ThrowRangeError<MSG_ENCODING_NOT_SUPPORTED>(&label); + aRv.ThrowRangeError<MSG_ENCODING_NOT_SUPPORTED>(label); return; } if (!mEncoding.EqualsLiteral("UTF-8") && !mEncoding.EqualsLiteral("UTF-16LE") && !mEncoding.EqualsLiteral("UTF-16BE")) { aRv.ThrowRangeError<MSG_DOM_ENCODING_NOT_UTF>(); return;
--- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -1002,32 +1002,30 @@ FetchBody<Derived>::ContinueConsumeBody( ErrorResult error; switch (mConsumeType) { case CONSUME_ARRAYBUFFER: { JS::Rooted<JSObject*> arrayBuffer(cx); FetchUtil::ConsumeArrayBuffer(cx, &arrayBuffer, aResultLength, aResult, error); - error.WouldReportJSException(); if (!error.Failed()) { JS::Rooted<JS::Value> val(cx); val.setObjectOrNull(arrayBuffer); localPromise->MaybeResolve(cx, val); // ArrayBuffer takes over ownership. autoFree.Reset(); } break; } case CONSUME_BLOB: { RefPtr<dom::Blob> blob = FetchUtil::ConsumeBlob( DerivedClass()->GetParentObject(), NS_ConvertUTF8toUTF16(mMimeType), aResultLength, aResult, error); - error.WouldReportJSException(); if (!error.Failed()) { localPromise->MaybeResolve(blob); // File takes over ownership. autoFree.Reset(); } break; } case CONSUME_FORMDATA: { @@ -1061,23 +1059,17 @@ FetchBody<Derived>::ContinueConsumeBody( break; } default: NS_NOTREACHED("Unexpected consume body type"); } error.WouldReportJSException(); if (error.Failed()) { - if (error.IsJSException()) { - JS::Rooted<JS::Value> exn(cx); - error.StealJSException(cx, &exn); - localPromise->MaybeReject(cx, exn); - } else { - localPromise->MaybeReject(error); - } + localPromise->MaybeReject(error); } } template <class Derived> already_AddRefed<Promise> FetchBody<Derived>::ConsumeBody(ConsumeType aType, ErrorResult& aRv) { mConsumeType = aType;
--- a/dom/fetch/InternalHeaders.cpp +++ b/dom/fetch/InternalHeaders.cpp @@ -175,30 +175,30 @@ InternalHeaders::IsSimpleHeader(const ns } //static bool InternalHeaders::IsInvalidName(const nsACString& aName, ErrorResult& aRv) { if (!NS_IsValidHTTPToken(aName)) { NS_ConvertUTF8toUTF16 label(aName); - aRv.ThrowTypeError<MSG_INVALID_HEADER_NAME>(&label); + aRv.ThrowTypeError<MSG_INVALID_HEADER_NAME>(label); return true; } return false; } // static bool InternalHeaders::IsInvalidValue(const nsACString& aValue, ErrorResult& aRv) { if (!NS_IsReasonableHTTPHeaderValue(aValue)) { NS_ConvertUTF8toUTF16 label(aValue); - aRv.ThrowTypeError<MSG_INVALID_HEADER_VALUE>(&label); + aRv.ThrowTypeError<MSG_INVALID_HEADER_VALUE>(label); return true; } return false; } bool InternalHeaders::IsImmutable(ErrorResult& aRv) const {
--- a/dom/fetch/Request.cpp +++ b/dom/fetch/Request.cpp @@ -97,26 +97,26 @@ GetRequestURLFromDocument(nsIDocument* a { MOZ_ASSERT(aDocument); MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr<nsIURI> baseURI = aDocument->GetBaseURI(); nsCOMPtr<nsIURI> resolvedURI; aRv = NS_NewURI(getter_AddRefs(resolvedURI), aInput, nullptr, baseURI); if (NS_WARN_IF(aRv.Failed())) { - aRv.ThrowTypeError<MSG_INVALID_URL>(&aInput); + aRv.ThrowTypeError<MSG_INVALID_URL>(aInput); return; } // This fails with URIs with weird protocols, even when they are valid, // so we ignore the failure nsAutoCString credentials; Unused << resolvedURI->GetUserPass(credentials); if (!credentials.IsEmpty()) { - aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(&aInput); + aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(aInput); return; } nsCOMPtr<nsIURI> resolvedURIClone; // We use CloneIgnoringRef to strip away the fragment even if the original URI // is immutable. aRv = resolvedURI->CloneIgnoringRef(getter_AddRefs(resolvedURIClone)); if (NS_WARN_IF(aRv.Failed())) { @@ -136,26 +136,26 @@ void GetRequestURLFromChrome(const nsAString& aInput, nsAString& aRequestURL, ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr<nsIURI> uri; aRv = NS_NewURI(getter_AddRefs(uri), aInput, nullptr, nullptr); if (NS_WARN_IF(aRv.Failed())) { - aRv.ThrowTypeError<MSG_INVALID_URL>(&aInput); + aRv.ThrowTypeError<MSG_INVALID_URL>(aInput); return; } // This fails with URIs with weird protocols, even when they are valid, // so we ignore the failure nsAutoCString credentials; Unused << uri->GetUserPass(credentials); if (!credentials.IsEmpty()) { - aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(&aInput); + aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(aInput); return; } nsCOMPtr<nsIURI> uriClone; // We use CloneIgnoringRef to strip away the fragment even if the original URI // is immutable. aRv = uri->CloneIgnoringRef(getter_AddRefs(uriClone)); if (NS_WARN_IF(aRv.Failed())) { @@ -178,34 +178,34 @@ GetRequestURLFromWorker(const GlobalObje workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); worker->AssertIsOnWorkerThread(); NS_ConvertUTF8toUTF16 baseURL(worker->GetLocationInfo().mHref); RefPtr<workers::URL> url = workers::URL::Constructor(aGlobal, aInput, baseURL, aRv); if (NS_WARN_IF(aRv.Failed())) { - aRv.ThrowTypeError<MSG_INVALID_URL>(&aInput); + aRv.ThrowTypeError<MSG_INVALID_URL>(aInput); return; } nsString username; url->GetUsername(username, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } nsString password; url->GetPassword(password, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } if (!username.IsEmpty() || !password.IsEmpty()) { - aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(&aInput); + aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(aInput); return; } url->SetHash(EmptyString(), aRv); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -311,17 +311,17 @@ Request::Constructor(const GlobalObject& // Step 14.1. Disallow forbidden methods, and anything that is not a HTTP // token, since HTTP states that Method may be any of the defined values or // a token (extension method). nsAutoCString outMethod; nsresult rv = FetchUtil::GetValidRequestMethod(method, outMethod); if (NS_FAILED(rv)) { NS_ConvertUTF8toUTF16 label(method); - aRv.ThrowTypeError<MSG_INVALID_REQUEST_METHOD>(&label); + aRv.ThrowTypeError<MSG_INVALID_REQUEST_METHOD>(label); return nullptr; } // Step 14.2 request->ClearCreatedByFetchEvent(); request->SetMethod(outMethod); } @@ -345,17 +345,17 @@ Request::Constructor(const GlobalObject& requestHeaders->SetGuard(HeadersGuardEnum::Request, aRv); MOZ_ASSERT(!aRv.Failed()); if (request->Mode() == RequestMode::No_cors) { if (!request->HasSimpleMethod()) { nsAutoCString method; request->GetMethod(method); NS_ConvertUTF8toUTF16 label(method); - aRv.ThrowTypeError<MSG_INVALID_REQUEST_METHOD>(&label); + aRv.ThrowTypeError<MSG_INVALID_REQUEST_METHOD>(label); return nullptr; } requestHeaders->SetGuard(HeadersGuardEnum::Request_no_cors, aRv); if (aRv.Failed()) { return nullptr; } }
--- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -2404,16 +2404,17 @@ HTMLMediaElement::WakeLockCreate() } void HTMLMediaElement::WakeLockRelease() { if (mWakeLock) { ErrorResult rv; mWakeLock->Unlock(rv); + rv.SuppressException(); mWakeLock = nullptr; } } bool HTMLMediaElement::ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute, const nsAString& aValue, nsAttrValue& aResult) @@ -4771,16 +4772,21 @@ HTMLMediaElement::IsPlayingThroughTheAud return true; } // If we are seeking, we consider it as playing if (mPlayingThroughTheAudioChannelBeforeSeek) { return true; } + // If we are playing an external stream. + if (mSrcAttrStream) { + return true; + } + return false; } void HTMLMediaElement::UpdateAudioChannelPlayingState() { bool playingThroughTheAudioChannel = IsPlayingThroughTheAudioChannel();
--- a/dom/html/HTMLVideoElement.cpp +++ b/dom/html/HTMLVideoElement.cpp @@ -262,16 +262,17 @@ HTMLVideoElement::WakeLockRelease() void HTMLVideoElement::UpdateScreenWakeLock() { bool hidden = OwnerDoc()->Hidden(); if (mScreenWakeLock && (mPaused || hidden || !mUseScreenWakeLock)) { ErrorResult rv; mScreenWakeLock->Unlock(rv); + rv.SuppressException(); mScreenWakeLock = nullptr; return; } if (!mScreenWakeLock && !mPaused && !hidden && mUseScreenWakeLock && HasVideo()) { RefPtr<power::PowerManagerService> pmService = power::PowerManagerService::GetInstance();
--- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -3289,25 +3289,57 @@ nsGenericHTMLElement::NewURIFromString(c // and waiting for the subsequent load to fail. NS_RELEASE(*aURI); return NS_ERROR_DOM_INVALID_STATE_ERR; } return NS_OK; } +static bool +IsOrHasAncestorWithDisplayNone(Element* aElement, nsIPresShell* aPresShell) +{ + nsTArray<Element*> elementsToCheck; + for (Element* e = aElement; e; e = e->GetParentElement()) { + if (e->GetPrimaryFrame()) { + // e definitely isn't display:none and doesn't have a display:none + // ancestor. + break; + } + elementsToCheck.AppendElement(e); + } + + if (elementsToCheck.IsEmpty()) { + return false; + } + + nsStyleSet* styleSet = aPresShell->StyleSet(); + RefPtr<nsStyleContext> sc; + for (int32_t i = elementsToCheck.Length() - 1; i >= 0; --i) { + if (sc) { + sc = styleSet->ResolveStyleFor(elementsToCheck[i], sc); + } else { + sc = nsComputedDOMStyle::GetStyleContextForElementNoFlush(elementsToCheck[i], + nullptr, aPresShell); + } + if (sc->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_NONE) { + return true; + } + } + + return false; +} + void nsGenericHTMLElement::GetInnerText(mozilla::dom::DOMString& aValue, mozilla::ErrorResult& aError) { if (!GetPrimaryFrame(Flush_Layout)) { - RefPtr<nsStyleContext> sc = - nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr, nullptr); - if (!sc || sc->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_NONE || - !IsInComposedDoc()) { + nsIPresShell* presShell = nsComputedDOMStyle::GetPresShellForContent(this); + if (!presShell || IsOrHasAncestorWithDisplayNone(this, presShell)) { GetTextContentInternal(aValue, aError); return; } } nsRange::GetInnerTextNoFlush(aValue, aError, this, 0, this, GetChildCount()); }
--- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -955,20 +955,26 @@ public: ShouldExposeNameAsHTMLDocumentProperty(Element* aElement) { return aElement->IsHTMLElement() && CanHaveName(aElement->NodeInfo()->NameAtom()); } static inline bool ShouldExposeIdAsHTMLDocumentProperty(Element* aElement) { - return aElement->IsAnyOfHTMLElements(nsGkAtoms::img, - nsGkAtoms::applet, - nsGkAtoms::embed, - nsGkAtoms::object); + if (aElement->IsAnyOfHTMLElements(nsGkAtoms::applet, + nsGkAtoms::embed, + nsGkAtoms::object)) { + return true; + } + + // Per spec, <img> is exposed by id only if it also has a nonempty + // name (which doesn't have to match the id or anything). + // HasName() is true precisely when name is nonempty. + return aElement->IsHTMLElement(nsGkAtoms::img) && aElement->HasName(); } static bool IsScrollGrabAllowed(JSContext*, JSObject*); protected: /** * Add/remove this element to the documents name cache
--- a/dom/imptests/failures/html/html/dom/documents/dta/test_nameditem-06.html.json +++ b/dom/imptests/failures/html/html/dom/documents/dta/test_nameditem-06.html.json @@ -1,5 +1,3 @@ { - "If there are two imgs, a collection should be returned. (name)": true, - "If there is one img, it should not be returned (id)": true, - "If there are two imgs, nothing should be returned. (id)": true + "If there are two imgs, a collection should be returned. (name)": true }
--- a/dom/imptests/html/html/dom/documents/dta/test_nameditem-06.html +++ b/dom/imptests/html/html/dom/documents/dta/test_nameditem-06.html @@ -58,19 +58,17 @@ test(function() { test(function() { var img1 = document.getElementsByTagName("img")[4]; assert_equals(img1.id, "test4"); var img2 = document.getElementsByTagName("img")[5]; assert_equals(img2.id, "test4"); assert_false("test4" in document, '"test4" in document should be false'); - var collection = document.test4; - assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection"); - assert_array_equals(collection, [img1, img2]); + assert_equals(document.test4, undefined); }, "If there are two imgs, nothing should be returned. (id)"); test(function() { var img1 = document.getElementsByTagName("img")[6]; assert_equals(img1.name, "test5"); var img2 = document.getElementsByTagName("img")[7]; assert_equals(img2.id, "test5");
--- a/dom/indexedDB/ActorsChild.cpp +++ b/dom/indexedDB/ActorsChild.cpp @@ -1092,17 +1092,16 @@ PermissionRequestChildProcessActor::Recv } /******************************************************************************* * BackgroundRequestChildBase ******************************************************************************/ BackgroundRequestChildBase::BackgroundRequestChildBase(IDBRequest* aRequest) : mRequest(aRequest) - , mActorDestroyed(false) { MOZ_ASSERT(aRequest); aRequest->AssertIsOnOwningThread(); MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChildBase); } BackgroundRequestChildBase::~BackgroundRequestChildBase() @@ -1118,25 +1117,16 @@ void BackgroundRequestChildBase::AssertIsOnOwningThread() const { MOZ_ASSERT(mRequest); mRequest->AssertIsOnOwningThread(); } #endif // DEBUG -void -BackgroundRequestChildBase::NoteActorDestroyed() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(!mActorDestroyed); - - mActorDestroyed = true; -} - /******************************************************************************* * BackgroundFactoryChild ******************************************************************************/ BackgroundFactoryChild::BackgroundFactoryChild(IDBFactory* aFactory) : mFactory(aFactory) #ifdef DEBUG , mOwningThread(NS_GetCurrentThread()) @@ -1271,18 +1261,17 @@ BackgroundFactoryRequestChild::~Backgrou MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryRequestChild); } IDBOpenDBRequest* BackgroundFactoryRequestChild::GetOpenDBRequest() const { AssertIsOnOwningThread(); - IDBRequest* baseRequest = BackgroundRequestChildBase::GetDOMObject(); - return static_cast<IDBOpenDBRequest*>(baseRequest); + return static_cast<IDBOpenDBRequest*>(mRequest.get()); } bool BackgroundFactoryRequestChild::HandleResponse(nsresult aResponse) { AssertIsOnOwningThread(); MOZ_ASSERT(NS_FAILED(aResponse)); MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); @@ -1353,18 +1342,16 @@ BackgroundFactoryRequestChild::HandleRes void BackgroundFactoryRequestChild::ActorDestroy(ActorDestroyReason aWhy) { AssertIsOnOwningThread(); MaybeCollectGarbageOnIPCMessage(); - NoteActorDestroyed(); - if (aWhy != Deletion) { IDBOpenDBRequest* openRequest = GetOpenDBRequest(); if (openRequest) { openRequest->NoteComplete(); } } } @@ -2442,18 +2429,16 @@ BackgroundRequestChild::HandleResponse(u void BackgroundRequestChild::ActorDestroy(ActorDestroyReason aWhy) { AssertIsOnOwningThread(); MaybeCollectGarbageOnIPCMessage(); - NoteActorDestroyed(); - if (mTransaction) { mTransaction->AssertIsOnOwningThread(); mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ aWhy == Deletion); #ifdef DEBUG mTransaction = nullptr; #endif @@ -3056,11 +3041,72 @@ BackgroundCursorChild::CachedResponse::C } BackgroundCursorChild::CachedResponse::CachedResponse(CachedResponse&& aOther) : mKey(Move(aOther.mKey)) { mCloneInfo = Move(aOther.mCloneInfo); } +/******************************************************************************* + * BackgroundUtilsChild + ******************************************************************************/ + +BackgroundUtilsChild::BackgroundUtilsChild(IndexedDatabaseManager* aManager) + : mManager(aManager) +#ifdef DEBUG + , mOwningThread(NS_GetCurrentThread()) +#endif +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aManager); + + MOZ_COUNT_CTOR(indexedDB::BackgroundUtilsChild); +} + +BackgroundUtilsChild::~BackgroundUtilsChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundUtilsChild); +} + +#ifdef DEBUG + +void +BackgroundUtilsChild::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mOwningThread); + + bool current; + MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); + MOZ_ASSERT(current); +} + +#endif // DEBUG + +void +BackgroundUtilsChild::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + + if (mManager) { + mManager->ClearBackgroundActor(); + mManager = nullptr; + + MOZ_ALWAYS_TRUE(PBackgroundIndexedDBUtilsChild::SendDeleteMe()); + } +} + +void +BackgroundUtilsChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + if (mManager) { + mManager->ClearBackgroundActor(); +#ifdef DEBUG + mManager = nullptr; +#endif + } +} + } // namespace indexedDB } // namespace dom } // namespace mozilla
--- a/dom/indexedDB/ActorsChild.h +++ b/dom/indexedDB/ActorsChild.h @@ -15,16 +15,17 @@ #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseRequestChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBRequestChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsTArray.h" class nsIEventTarget; struct nsID; struct PRThread; @@ -39,16 +40,17 @@ namespace dom { namespace indexedDB { class IDBCursor; class IDBDatabase; class IDBFactory; class IDBMutableFile; class IDBOpenDBRequest; class IDBRequest; +class IndexedDatabaseManager; class Key; class PermissionRequestChild; class PermissionRequestParent; class SerializedStructuredCloneReadInfo; class ThreadLocal { friend class nsAutoPtr<ThreadLocal>; @@ -213,50 +215,30 @@ private: class BackgroundDatabaseChild; class BackgroundRequestChildBase { protected: RefPtr<IDBRequest> mRequest; -private: - bool mActorDestroyed; - public: void AssertIsOnOwningThread() const #ifdef DEBUG ; #else { } #endif - IDBRequest* - GetDOMObject() const - { - AssertIsOnOwningThread(); - return mRequest; - } - - bool - IsActorDestroyed() const - { - AssertIsOnOwningThread(); - return mActorDestroyed; - } - protected: explicit BackgroundRequestChildBase(IDBRequest* aRequest); virtual ~BackgroundRequestChildBase(); - - void - NoteActorDestroyed(); }; class BackgroundFactoryRequestChild final : public BackgroundRequestChildBase , public PBackgroundIDBFactoryRequestChild { typedef mozilla::dom::quota::PersistenceType PersistenceType; @@ -824,13 +806,52 @@ private: // Force callers to use SendContinueInternal. bool SendContinue(const CursorRequestParams& aParams, const Key& aKey) = delete; bool SendDeleteMe() = delete; }; +class BackgroundUtilsChild final + : public PBackgroundIndexedDBUtilsChild +{ + friend class mozilla::ipc::BackgroundChildImpl; + friend class IndexedDatabaseManager; + + IndexedDatabaseManager* mManager; + +#ifdef DEBUG + nsCOMPtr<nsIEventTarget> mOwningThread; +#endif + +public: + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + +private: + // Only created by IndexedDatabaseManager. + explicit BackgroundUtilsChild(IndexedDatabaseManager* aManager); + + // Only destroyed by mozilla::ipc::BackgroundChildImpl. + ~BackgroundUtilsChild(); + + void + SendDeleteMeInternal(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + bool + SendDeleteMe() = delete; +}; + } // namespace indexedDB } // namespace dom } // namespace mozilla #endif // mozilla_dom_indexeddb_actorschild_h__
--- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -17,17 +17,16 @@ #include "js/StructuredClone.h" #include "js/Value.h" #include "jsapi.h" #include "KeyPath.h" #include "mozilla/Attributes.h" #include "mozilla/AppProcessChecker.h" #include "mozilla/AutoRestore.h" #include "mozilla/Endian.h" -#include "mozilla/Hal.h" #include "mozilla/LazyIdleThread.h" #include "mozilla/Maybe.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" #include "mozilla/storage.h" #include "mozilla/unused.h" #include "mozilla/dom/ContentParent.h" @@ -39,16 +38,17 @@ #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseRequestParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsParent.h" #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestParent.h" #include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/dom/quota/Client.h" #include "mozilla/dom/quota/FileStreams.h" #include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/dom/quota/UsageInfo.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/BackgroundUtils.h" @@ -63,22 +63,19 @@ #include "nsEscape.h" #include "nsHashKeys.h" #include "nsNetUtil.h" #include "nsISimpleEnumerator.h" #include "nsIAppsService.h" #include "nsIEventTarget.h" #include "nsIFile.h" #include "nsIFileURL.h" -#include "nsIIdleService.h" #include "nsIInputStream.h" #include "nsIInterfaceRequestor.h" #include "nsInterfaceHashtable.h" -#include "nsIObserver.h" -#include "nsIObserverService.h" #include "nsIOutputStream.h" #include "nsIPrincipal.h" #include "nsIScriptSecurityManager.h" #include "nsISupports.h" #include "nsISupportsImpl.h" #include "nsISupportsPriority.h" #include "nsIThread.h" #include "nsITimer.h" @@ -237,37 +234,27 @@ const char kPrefIndexedDBEnabled[] = "do const char kPrefFileHandleEnabled[] = "dom.fileHandle.enabled"; #define IDB_PREFIX "indexedDB" #define PERMISSION_STRING_CHROME_BASE IDB_PREFIX "-chrome-" #define PERMISSION_STRING_CHROME_READ_SUFFIX "-read" #define PERMISSION_STRING_CHROME_WRITE_SUFFIX "-write" -const char kIdleServiceContractId[] = "@mozilla.org/widget/idleservice;1"; - #ifdef DEBUG const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL; const uint32_t kDEBUGThreadSleepMS = 0; const int32_t kDEBUGTransactionThreadPriority = nsISupportsPriority::PRIORITY_NORMAL; const uint32_t kDEBUGTransactionThreadSleepMS = 0; #endif -const bool kRunningXPCShellTests = -#ifdef ENABLE_TESTS - !!PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR") -#else - false -#endif - ; - struct FreeDeleter { void operator()(void* aPtr) const { free(aPtr); } }; @@ -6139,37 +6126,16 @@ private: MaybeWaitForFileHandles(); void CallCallback(); NS_DECL_NSIRUNNABLE }; -class UnlockDirectoryRunnable final - : public nsRunnable -{ - RefPtr<DirectoryLock> mDirectoryLock; - -public: - explicit - UnlockDirectoryRunnable(already_AddRefed<DirectoryLock> aDirectoryLock) - : mDirectoryLock(Move(aDirectoryLock)) - { } - -private: - ~UnlockDirectoryRunnable() - { - MOZ_ASSERT(!mDirectoryLock); - } - - NS_IMETHOD - Run() override; -}; - class Database final : public PBackgroundIDBDatabaseParent { friend class VersionChangeTransaction; class StartTransactionOp; private: @@ -7129,40 +7095,47 @@ class FactoryOp public: struct MaybeBlockedDatabaseInfo; protected: enum class State { // Just created on the PBackground thread, dispatched to the main thread. // Next step is either SendingResults if permission is denied, - // PermissionChallenge if the permission is unknown, or DirectoryOpenPending + // PermissionChallenge if the permission is unknown, or FinishOpen // if permission is granted. Initial, // Sending a permission challenge message to the child on the PBackground // thread. Next step is PermissionRetry. PermissionChallenge, // Retrying permission check after a challenge on the main thread. Next step - // is either SendingResults if permission is denied or DirectoryOpenPending + // is either SendingResults if permission is denied or FinishOpen // if permission is granted. PermissionRetry, - // Waiting for directory open allowed on the main thread. The next step is - // either SendingResults if directory lock failed to acquire, or - // DirectoryWorkOpen if directory lock is acquired. + // Opening directory or initializing quota manager on the PBackground + // thread. Next step is either DirectoryOpenPending if quota manager is + // already initialized or QuotaManagerPending if quota manager needs to be + // initialized. + FinishOpen, + + // Waiting for quota manager initialization to complete on the PBackground + // thread. Next step is either SendingResults if initialization failed or + // DirectoryOpenPending if initialization succeeded. + QuotaManagerPending, + + // Waiting for directory open allowed on the PBackground thread. The next + // step is either SendingResults if directory lock failed to acquire, or + // DatabaseOpenPending if directory lock is acquired. DirectoryOpenPending, - // Checking if database open needs to wait on the PBackground thread. The - // next step is DatabaseOpenPending. - DirectoryWorkOpen, - - // Waiting for database open allowed on the main thread. The next step is - // DatabaseWorkOpen. + // Waiting for database open allowed on the PBackground thread. The next + // step is DatabaseWorkOpen. DatabaseOpenPending, // Waiting to do/doing work on the QuotaManager IO thread. Its next step is // either BeginVersionChange if the requested version doesn't match the // existing database version or SendingResults if the versions match. DatabaseWorkOpen, // Starting a version change transaction or deleting a database on the @@ -7321,19 +7294,25 @@ private: nsresult CheckPermission(ContentParent* aContentParent, PermissionRequestBase::PermissionValue* aPermission); static bool CheckAtLeastOneAppHasPermission(ContentParent* aContentParent, const nsACString& aPermissionString); - nsresult + void FinishOpen(); + nsresult + QuotaManagerOpen(); + + void + OpenDirectory(); + // Test whether this FactoryOp needs to wait for the given op. bool MustWaitFor(const FactoryOp& aExistingOp); }; struct FactoryOp::MaybeBlockedDatabaseInfo final { RefPtr<Database> mDatabase; @@ -7579,21 +7558,16 @@ private: { MOZ_ASSERT(aDeleteDatabaseOp); MOZ_ASSERT(!aDeleteDatabaseOp->mDatabaseDirectoryPath.IsEmpty()); } ~VersionChangeOp() { } - // XXX This should be much simpler when the QuotaManager lives on the - // PBackground thread. - nsresult - RunOnMainThread(); - nsresult RunOnIOThread(); void RunOnOwningThread(); nsresult DeleteFile(nsIFile* aDirectory, @@ -7625,16 +7599,26 @@ protected: SendingResults, // All done. Completed }; State mState; +public: + void + RunImmediately() + { + MOZ_ASSERT(mState == State::Initial); + + Unused << this->Run(); + } + +protected: DatabaseOp(Database* aDatabase); virtual ~DatabaseOp() { MOZ_ASSERT_IF(OperationMayProceed(), mState == State::Initial || mState == State::Completed); } @@ -8500,16 +8484,96 @@ private: virtual nsresult DoDatabaseWork(DatabaseConnection* aConnection) override; virtual nsresult SendSuccessResult() override; }; +class Utils final + : public PBackgroundIndexedDBUtilsParent +{ +#ifdef DEBUG + bool mActorDestroyed; +#endif + +public: + Utils(); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Utils) + +private: + // Reference counted. + ~Utils(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual bool + RecvDeleteMe() override; + + virtual bool + RecvGetFileReferences(const PersistenceType& aPersistenceType, + const nsCString& aOrigin, + const nsString& aDatabaseName, + const int64_t& aFileId, + int32_t* aRefCnt, + int32_t* aDBRefCnt, + int32_t* aSliceRefCnt, + bool* aResult) override; +}; + +class GetFileReferencesHelper final + : public nsRunnable +{ + PersistenceType mPersistenceType; + nsCString mOrigin; + nsString mDatabaseName; + int64_t mFileId; + + mozilla::Mutex mMutex; + mozilla::CondVar mCondVar; + int32_t mMemRefCnt; + int32_t mDBRefCnt; + int32_t mSliceRefCnt; + bool mResult; + bool mWaiting; + +public: + GetFileReferencesHelper(PersistenceType aPersistenceType, + const nsACString& aOrigin, + const nsAString& aDatabaseName, + int64_t aFileId) + : mPersistenceType(aPersistenceType) + , mOrigin(aOrigin) + , mDatabaseName(aDatabaseName) + , mFileId(aFileId) + , mMutex("GetFileReferencesHelper::mMutex") + , mCondVar(mMutex, "GetFileReferencesHelper::mCondVar") + , mMemRefCnt(-1) + , mDBRefCnt(-1) + , mSliceRefCnt(-1) + , mResult(false) + , mWaiting(true) + { } + + nsresult + DispatchAndReturnFileReferences(int32_t* aMemRefCnt, + int32_t* aDBRefCnt, + int32_t* aSliceRefCnt, + bool* aResult); + +private: + ~GetFileReferencesHelper() {} + + NS_DECL_NSIRUNNABLE +}; + class PermissionRequestHelper final : public PermissionRequestBase , public PIndexedDBPermissionRequestParent { bool mActorDestroyed; public: PermissionRequestHelper(Element* aOwnerElement, @@ -8684,22 +8748,17 @@ private: return mFileInfo->Id(); } }; NS_DEFINE_STATIC_IID_ACCESSOR(BlobImplStoredFile, BLOB_IMPL_STORED_FILE_IID) class QuotaClient final : public mozilla::dom::quota::Client - , public nsIObserver -{ - // The number of seconds we will wait after receiving the idle-daily - // notification before beginning maintenance. - static const uint32_t kIdleObserverTimeSec = 1; - +{ // The minimum amount of time that has passed since the last vacuum before we // will attempt to analyze the database for fragmentation. static const PRTime kMinVacuumAge = PRTime(PR_USEC_PER_SEC) * 60 * 60 * 24 * 7; // If the percent of database pages that are not in contiguous order is higher // than this percentage we will attempt a vacuum. static const int32_t kPercentUnorderedThreshold = 30; @@ -8711,99 +8770,92 @@ class QuotaClient final // The number of freelist pages beyond which we will favor an incremental // vacuum over a full vacuum. static const int32_t kMaxFreelistThreshold = 5; // If the percent of unused file bytes in the database exceeds this percentage // then we will attempt a full vacuum. static const int32_t kPercentUnusedThreshold = 20; - class AbortOperationsRunnable; class AutoProgressHandler; class GetDirectoryLockListener; struct MaintenanceInfoBase; struct MultipleMaintenanceInfo; - class ShutdownWorkThreadsRunnable; struct SingleMaintenanceInfo; typedef nsClassHashtable<nsCStringHashKey, MultipleMaintenanceInfo> MaintenanceInfoHashtable; enum class MaintenanceAction { Nothing = 0, IncrementalVacuum, FullVacuum }; static QuotaClient* sInstance; nsCOMPtr<nsIEventTarget> mBackgroundThread; - RefPtr<ShutdownWorkThreadsRunnable> mShutdownRunnable; RefPtr<nsThreadPool> mMaintenanceThreadPool; PRTime mMaintenanceStartTime; Atomic<uint32_t> mMaintenanceRunId; UniquePtr<MaintenanceInfoHashtable> mMaintenanceInfoHashtable; bool mShutdownRequested; - bool mIdleObserverRegistered; public: QuotaClient(); static QuotaClient* GetInstance() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); return sInstance; } static bool - IsShuttingDownOnMainThread() - { - MOZ_ASSERT(NS_IsMainThread()); + IsShuttingDownOnBackgroundThread() + { + AssertIsOnBackgroundThread(); if (sInstance) { return sInstance->IsShuttingDown(); } return QuotaManager::IsShuttingDown(); } static bool - IsShuttingDownOnNonMainThread() - { - MOZ_ASSERT(!NS_IsMainThread()); + IsShuttingDownOnNonBackgroundThread() + { + MOZ_ASSERT(!IsOnBackgroundThread()); return QuotaManager::IsShuttingDown(); } bool IsShuttingDown() const { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); return mShutdownRequested; } bool IdleMaintenanceMustEnd(uint32_t aRunId) const { if (mMaintenanceRunId != aRunId) { MOZ_ASSERT(mMaintenanceRunId > aRunId); return true; } return false; } - void - NoteBackgroundThread(nsIEventTarget* aBackgroundThread); - - NS_DECL_THREADSAFE_ISUPPORTS + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override) virtual mozilla::dom::quota::Client::Type GetType() override; virtual nsresult InitOrigin(PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin, @@ -8825,17 +8877,20 @@ public: virtual void AbortOperations(const nsACString& aOrigin) override; virtual void AbortOperationsForProcess(ContentParentId aContentParentId) override; virtual void - PerformIdleMaintenance() override; + StartIdleMaintenance() override; + + virtual void + StopIdleMaintenance() override; virtual void ShutdownWorkThreads() override; private: ~QuotaClient(); nsresult @@ -8844,23 +8899,20 @@ private: nsIFile** aDirectory); nsresult GetUsageForDirectoryInternal(nsIFile* aDirectory, UsageInfo* aUsageInfo, bool aDatabaseFiles); void - RemoveIdleObserver(); - - void - StartIdleMaintenance(); - - void - StopIdleMaintenance(); + CreateManager(); + + void + StartIdleMaintenanceInternal(); // Runs on mMaintenanceThreadPool. Once it finds databases it will queue a // runnable that calls GetDirectoryLockForIdleMaintenance. void FindDatabasesForIdleMaintenance(uint32_t aRunId); // Runs on the main thread. Once QuotaManager has given a lock it will call // ScheduleIdleMaintenance. @@ -8910,18 +8962,16 @@ private: nsIFile* aDatabaseFile); // Runs on the main thread. Checks to see if all database maintenance has // finished and then releases the directory lock. void MaybeReleaseDirectoryLockForIdleMaintenance( const nsACString& aKey, const nsAString& aDatabasePath); - - NS_DECL_NSIOBSERVER }; class MOZ_STACK_CLASS QuotaClient::AutoProgressHandler final : public mozIStorageProgressHandler { QuotaClient* mQuotaClient; mozIStorageConnection* mConnection; uint32_t mRunId; @@ -9079,110 +9129,42 @@ struct QuotaClient::MultipleMaintenanceI MOZ_ASSERT(!databasePath.IsEmpty()); } #endif } MultipleMaintenanceInfo(const MultipleMaintenanceInfo& aOther) = delete; }; -class QuotaClient::ShutdownWorkThreadsRunnable final - : public nsRunnable -{ - RefPtr<QuotaClient> mQuotaClient; - -public: - explicit ShutdownWorkThreadsRunnable(QuotaClient* aQuotaClient) - : mQuotaClient(aQuotaClient) - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aQuotaClient); - MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient); - MOZ_ASSERT(aQuotaClient->mShutdownRequested); - } - - NS_DECL_ISUPPORTS_INHERITED - -private: - ~ShutdownWorkThreadsRunnable() - { - MOZ_ASSERT(!mQuotaClient); - } - - NS_DECL_NSIRUNNABLE -}; - -class QuotaClient::AbortOperationsRunnable final - : public nsRunnable -{ - const ContentParentId mContentParentId; - const nsCString mOrigin; - - nsTArray<RefPtr<Database>> mDatabases; - -public: - explicit AbortOperationsRunnable(const nsACString& aOrigin) - : mOrigin(aOrigin) - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mOrigin.IsEmpty()); - } - - explicit AbortOperationsRunnable(ContentParentId aContentParentId) - : mContentParentId(aContentParentId) - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mOrigin.IsEmpty()); - } - - NS_DECL_ISUPPORTS_INHERITED - -private: - ~AbortOperationsRunnable() - { } - - static PLDHashOperator - MatchOrigin(const nsACString& aKey, - DatabaseActorInfo* aValue, - void* aClosure); - - static PLDHashOperator - MatchContentParentId(const nsACString& aKey, - DatabaseActorInfo* aValue, - void* aClosure); - - NS_DECL_NSIRUNNABLE -}; - class QuotaClient::GetDirectoryLockListener final : public OpenDirectoryListener { RefPtr<QuotaClient> mQuotaClient; const uint32_t mRunId; const nsCString mKey; public: GetDirectoryLockListener(QuotaClient* aQuotaClient, uint32_t aRunId, const nsACString& aKey) : mQuotaClient(aQuotaClient) , mRunId(aRunId) , mKey(aKey) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); MOZ_ASSERT(aQuotaClient); MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient); } NS_INLINE_DECL_REFCOUNTING(QuotaClient::GetDirectoryLockListener, override) private: ~GetDirectoryLockListener() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); } // OpenDirectoryListener overrides. virtual void DirectoryLockAcquired(DirectoryLock* aLock) override; virtual void DirectoryLockFailed() override; @@ -9526,17 +9508,17 @@ TelemetryIdForFile(nsIFile* aFile) * Exported functions ******************************************************************************/ PBackgroundIDBFactoryParent* AllocPBackgroundIDBFactoryParent(const LoggingInfo& aLoggingInfo) { AssertIsOnBackgroundThread(); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread())) { + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) { return nullptr; } if (NS_WARN_IF(!aLoggingInfo.nextTransactionSerialNumber()) || NS_WARN_IF(!aLoggingInfo.nextVersionChangeTransactionSerialNumber()) || NS_WARN_IF(!aLoggingInfo.nextRequestSerialNumber())) { ASSERT_UNLESS_FUZZING(); return nullptr; @@ -9549,31 +9531,51 @@ AllocPBackgroundIDBFactoryParent(const L } bool RecvPBackgroundIDBFactoryConstructor(PBackgroundIDBFactoryParent* aActor, const LoggingInfo& /* aLoggingInfo */) { AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); - MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread()); + MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread()); return true; } bool DeallocPBackgroundIDBFactoryParent(PBackgroundIDBFactoryParent* aActor) { AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); RefPtr<Factory> actor = dont_AddRef(static_cast<Factory*>(aActor)); return true; } +PBackgroundIndexedDBUtilsParent* +AllocPBackgroundIndexedDBUtilsParent() +{ + AssertIsOnBackgroundThread(); + + RefPtr<Utils> actor = new Utils(); + + return actor.forget().take(); +} + +bool +DeallocPBackgroundIndexedDBUtilsParent(PBackgroundIndexedDBUtilsParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + RefPtr<Utils> actor = dont_AddRef(static_cast<Utils*>(aActor)); + return true; +} + PIndexedDBPermissionRequestParent* AllocPIndexedDBPermissionRequestParent(Element* aOwnerElement, nsIPrincipal* aPrincipal) { MOZ_ASSERT(NS_IsMainThread()); RefPtr<PermissionRequestHelper> actor = new PermissionRequestHelper(aOwnerElement, aPrincipal); @@ -9613,25 +9615,37 @@ DeallocPIndexedDBPermissionRequestParent RefPtr<PermissionRequestHelper> actor = dont_AddRef(static_cast<PermissionRequestHelper*>(aActor)); return true; } already_AddRefed<mozilla::dom::quota::Client> CreateQuotaClient() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); RefPtr<QuotaClient> client = new QuotaClient(); return client.forget(); } FileHandleThreadPool* GetFileHandleThreadPool() { + AssertIsOnBackgroundThread(); + + if (!gFileHandleThreadPool) { + RefPtr<FileHandleThreadPool> fileHandleThreadPool = + FileHandleThreadPool::Create(); + if (NS_WARN_IF(!fileHandleThreadPool)) { + return nullptr; + } + + gFileHandleThreadPool = fileHandleThreadPool; + } + return gFileHandleThreadPool; } /******************************************************************************* * DatabaseConnection implementation ******************************************************************************/ DatabaseConnection::DatabaseConnection( @@ -12637,30 +12651,30 @@ DatabaseLoggingInfo::~DatabaseLoggingInf uint64_t Factory::sFactoryInstanceCount = 0; Factory::Factory(already_AddRefed<DatabaseLoggingInfo> aLoggingInfo) : mLoggingInfo(Move(aLoggingInfo)) , mActorDestroyed(false) { AssertIsOnBackgroundThread(); - MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread()); + MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread()); } Factory::~Factory() { MOZ_ASSERT(mActorDestroyed); } // static already_AddRefed<Factory> Factory::Create(const LoggingInfo& aLoggingInfo) { AssertIsOnBackgroundThread(); - MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread()); + MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread()); // If this is the first instance then we need to do some initialization. if (!sFactoryInstanceCount) { MOZ_ASSERT(!gFactoryOps); gFactoryOps = new FactoryOpArray(); MOZ_ASSERT(!gLiveDatabaseHashtable); gLiveDatabaseHashtable = new DatabaseActorHashtable(); @@ -12789,17 +12803,17 @@ Factory::RecvIncrementLoggingRequestSeri PBackgroundIDBFactoryRequestParent* Factory::AllocPBackgroundIDBFactoryRequestParent( const FactoryRequestParams& aParams) { AssertIsOnBackgroundThread(); MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread())) { + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) { return nullptr; } const CommonFactoryRequestParams* commonParams; switch (aParams.type()) { case FactoryRequestParams::TOpenDatabaseRequestParams: { const OpenDatabaseRequestParams& params = @@ -12860,17 +12874,17 @@ Factory::AllocPBackgroundIDBFactoryReque bool Factory::RecvPBackgroundIDBFactoryRequestConstructor( PBackgroundIDBFactoryRequestParent* aActor, const FactoryRequestParams& aParams) { AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None); - MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread()); + MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread()); auto* op = static_cast<FactoryOp*>(aActor); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(op))); return true; } bool @@ -12998,27 +13012,16 @@ WaitForTransactionsHelper::Run() default: MOZ_CRASH("Should never get here!"); } return NS_OK; } -NS_IMETHODIMP -UnlockDirectoryRunnable::Run() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mDirectoryLock); - - mDirectoryLock = nullptr; - - return NS_OK; -} - /******************************************************************************* * Database ******************************************************************************/ Database::Database(Factory* aFactory, const PrincipalInfo& aPrincipalInfo, const OptionalContentId& aOptionalContentParentId, const nsACString& aGroup, @@ -13330,22 +13333,17 @@ Database::MaybeCloseConnection() void Database::ConnectionClosedCallback() { AssertIsOnBackgroundThread(); MOZ_ASSERT(mClosed); MOZ_ASSERT(!mTransactions.Count()); MOZ_ASSERT(!mActiveMutableFileCount); - if (mDirectoryLock) { - RefPtr<UnlockDirectoryRunnable> runnable = - new UnlockDirectoryRunnable(mDirectoryLock.forget()); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); - } + mDirectoryLock = nullptr; CleanupMetadata(); } void Database::CleanupMetadata() { AssertIsOnBackgroundThread(); @@ -13499,17 +13497,18 @@ Database::RecvPBackgroundIDBDatabaseRequ const DatabaseRequestParams& aParams) { AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); MOZ_ASSERT(aParams.type() != DatabaseRequestParams::T__None); auto* op = static_cast<DatabaseOp*>(aActor); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(op))); + op->RunImmediately(); + return true; } bool Database::DeallocPBackgroundIDBDatabaseRequestParent( PBackgroundIDBDatabaseRequestParent* aActor) { AssertIsOnBackgroundThread(); @@ -16282,17 +16281,17 @@ FileManager::GetUsage(nsIFile* aDirector } int64_t fileSize; rv = file->GetFileSize(&fileSize); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - quota::IncrementUsage(&usage, uint64_t(fileSize)); + UsageInfo::IncrementUsage(&usage, uint64_t(fileSize)); } if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } *aUsage = usage; return NS_OK; @@ -16311,58 +16310,44 @@ NS_IMPL_ISUPPORTS_INHERITED(BlobImplStor ******************************************************************************/ QuotaClient* QuotaClient::sInstance = nullptr; QuotaClient::QuotaClient() : mMaintenanceStartTime(0) , mMaintenanceRunId(0) , mShutdownRequested(false) - , mIdleObserverRegistered(false) -{ - MOZ_ASSERT(NS_IsMainThread()); +{ + AssertIsOnBackgroundThread(); MOZ_ASSERT(!sInstance, "We expect this to be a singleton!"); MOZ_ASSERT(!gTelemetryIdMutex); // Always create this so that later access to gTelemetryIdHashtable can be // properly synchronized. gTelemetryIdMutex = new Mutex("IndexedDB gTelemetryIdMutex"); sInstance = this; } QuotaClient::~QuotaClient() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!"); MOZ_ASSERT(gTelemetryIdMutex); MOZ_ASSERT(!mMaintenanceThreadPool); MOZ_ASSERT_IF(mMaintenanceInfoHashtable, !mMaintenanceInfoHashtable->Count()); - MOZ_ASSERT(!mIdleObserverRegistered); // No one else should be able to touch gTelemetryIdHashtable now that the // QuotaClient has gone away. gTelemetryIdHashtable = nullptr; gTelemetryIdMutex = nullptr; sInstance = nullptr; } -void -QuotaClient::NoteBackgroundThread(nsIEventTarget* aBackgroundThread) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aBackgroundThread); - MOZ_ASSERT(!mShutdownRequested); - - mBackgroundThread = aBackgroundThread; -} - -NS_IMPL_ISUPPORTS(QuotaClient, nsIObserver) - mozilla::dom::quota::Client::Type QuotaClient::GetType() { return QuotaClient::IDB; } struct FileManagerInitInfo { @@ -16703,128 +16688,122 @@ QuotaClient::ReleaseIOThreadObjects() if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) { mgr->InvalidateAllFileManagers(); } } void QuotaClient::AbortOperations(const nsACString& aOrigin) { - if (mBackgroundThread) { - RefPtr<AbortOperationsRunnable> runnable = - new AbortOperationsRunnable(aOrigin); - - if (NS_FAILED(mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) { - // This can happen if the thread has shut down already. - return; - } - } + AssertIsOnBackgroundThread(); + + if (!gLiveDatabaseHashtable) { + return; + } + + nsTArray<RefPtr<Database>> databases; + + for (auto iter = gLiveDatabaseHashtable->ConstIter(); + !iter.Done(); iter.Next()) { + for (Database* database : iter.Data()->mLiveDatabases) { + if (aOrigin.IsVoid() || database->Origin() == aOrigin) { + databases.AppendElement(database); + } + } + } + + for (Database* database : databases) { + database->Invalidate(); + } + + databases.Clear(); } void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId) { - if (mBackgroundThread) { - RefPtr<AbortOperationsRunnable> runnable = - new AbortOperationsRunnable(aContentParentId); - - if (NS_FAILED(mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) { - // This can happen if the thread has shut down already. - return; - } - } -} - -void -QuotaClient::PerformIdleMaintenance() -{ - using namespace mozilla::hal; - - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); + + if (!gLiveDatabaseHashtable) { + return; + } + + nsTArray<RefPtr<Database>> databases; + + for (auto iter = gLiveDatabaseHashtable->ConstIter(); + !iter.Done(); iter.Next()) { + for (Database* database : iter.Data()->mLiveDatabases) { + if (database->IsOwnedByProcess(aContentParentId)) { + databases.AppendElement(database); + } + } + } + + for (Database* database : databases) { + database->Invalidate(); + } + + databases.Clear(); +} + +void +QuotaClient::StartIdleMaintenance() +{ + AssertIsOnBackgroundThread(); MOZ_ASSERT(!mShutdownRequested); - // If we're running on battery power then skip all idle maintenance since we - // would otherwise be doing lots of disk I/O. - BatteryInformation batteryInfo; - -#ifdef MOZ_WIDGET_ANDROID - // Android XPCShell doesn't load the AndroidBridge that is needed to make - // GetCurrentBatteryInformation work... - if (!kRunningXPCShellTests) -#endif - { - GetCurrentBatteryInformation(&batteryInfo); - } - - // If we're running XPCShell because we always want to be able to test this - // code so pretend that we're always charging. - if (kRunningXPCShellTests) { - batteryInfo.level() = 100; - batteryInfo.charging() = true; - } - - if (NS_WARN_IF(!batteryInfo.charging())) { - return; - } - - // Make sure that the IndexedDatabaseManager is running so that we can check - // for low disk space mode. - IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate(); - if (NS_WARN_IF(!mgr)) { - return; - } - - if (kRunningXPCShellTests) { - // We don't want user activity to impact this code if we're running tests. - Unused << Observe(nullptr, OBSERVER_TOPIC_IDLE, nullptr); - } else if (!mIdleObserverRegistered) { - nsCOMPtr<nsIIdleService> idleService = - do_GetService(kIdleServiceContractId); - MOZ_ASSERT(idleService); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - idleService->AddIdleObserver(this, kIdleObserverTimeSec))); - - mIdleObserverRegistered = true; - } + mBackgroundThread = do_GetCurrentThread(); + + if (!IndexedDatabaseManager::Get()) { + nsCOMPtr<nsIRunnable> runnable = + NS_NewRunnableMethod(this, &QuotaClient::CreateManager); + MOZ_ASSERT(runnable); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); + return; + } + + StartIdleMaintenanceInternal(); +} + +void +QuotaClient::StopIdleMaintenance() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mShutdownRequested); + + mMaintenanceRunId++; } void QuotaClient::ShutdownWorkThreads() { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mShutdownRunnable); + AssertIsOnBackgroundThread(); MOZ_ASSERT(!mShutdownRequested); - StopIdleMaintenance(); - mShutdownRequested = true; - if (mBackgroundThread) { - RefPtr<ShutdownWorkThreadsRunnable> runnable = - new ShutdownWorkThreadsRunnable(this); - - if (NS_SUCCEEDED(mBackgroundThread->Dispatch(runnable, - NS_DISPATCH_NORMAL))) { - mShutdownRunnable = Move(runnable); - } - } - if (mMaintenanceThreadPool) { mMaintenanceThreadPool->Shutdown(); mMaintenanceThreadPool = nullptr; } - if (mShutdownRunnable) { - nsIThread* currentThread = NS_GetCurrentThread(); - MOZ_ASSERT(currentThread); - - while (mShutdownRunnable) { - MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread)); - } + RefPtr<ConnectionPool> connectionPool = gConnectionPool.get(); + if (connectionPool) { + connectionPool->Shutdown(); + + gConnectionPool = nullptr; + } + + RefPtr<FileHandleThreadPool> fileHandleThreadPool = + gFileHandleThreadPool.get(); + if (fileHandleThreadPool) { + fileHandleThreadPool->Shutdown(); + + gFileHandleThreadPool = nullptr; } } nsresult QuotaClient::GetDirectory(PersistenceType aPersistenceType, const nsACString& aOrigin, nsIFile** aDirectory) { QuotaManager* quotaManager = QuotaManager::Get(); @@ -16948,36 +16927,39 @@ QuotaClient::GetUsageForDirectoryInterna if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } void -QuotaClient::RemoveIdleObserver() +QuotaClient::CreateManager() { MOZ_ASSERT(NS_IsMainThread()); - if (mIdleObserverRegistered) { - nsCOMPtr<nsIIdleService> idleService = - do_GetService(kIdleServiceContractId); - MOZ_ASSERT(idleService); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - idleService->RemoveIdleObserver(this, kIdleObserverTimeSec))); - - mIdleObserverRegistered = false; - } -} - -void -QuotaClient::StartIdleMaintenance() -{ - MOZ_ASSERT(NS_IsMainThread()); + // Make sure that the IndexedDatabaseManager is running so that we can check + // for low disk space mode. + IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate(); + if (NS_WARN_IF(!mgr)) { + return; + } + + nsCOMPtr<nsIRunnable> runnable = + NS_NewRunnableMethod(this, &QuotaClient::StartIdleMaintenanceInternal); + MOZ_ASSERT(runnable); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mBackgroundThread->Dispatch(runnable, + NS_DISPATCH_NORMAL))); +} + +void +QuotaClient::StartIdleMaintenanceInternal() +{ + AssertIsOnBackgroundThread(); MOZ_ASSERT(!mShutdownRequested); if (!mMaintenanceThreadPool) { RefPtr<nsThreadPool> threadPool = new nsThreadPool(); // PR_GetNumberOfProcessors() can return -1 on error, so make sure we // don't set some huge number here. We add 2 in case some threads block on // the disk I/O. @@ -17016,27 +16998,16 @@ QuotaClient::StartIdleMaintenance() mMaintenanceRunId); MOZ_ASSERT(runnable); MOZ_ALWAYS_TRUE(NS_SUCCEEDED( mMaintenanceThreadPool->Dispatch(runnable, NS_DISPATCH_NORMAL))); } void -QuotaClient::StopIdleMaintenance() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mShutdownRequested); - - RemoveIdleObserver(); - - mMaintenanceRunId++; -} - -void QuotaClient::FindDatabasesForIdleMaintenance(uint32_t aRunId) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(!IsOnBackgroundThread()); MOZ_ASSERT(mMaintenanceThreadPool); // The storage directory is structured like this: // @@ -17242,28 +17213,29 @@ QuotaClient::FindDatabasesForIdleMainten aRunId, MultipleMaintenanceInfo(group, origin, persistenceType, isApp, Move(databasePaths))); MOZ_ASSERT(runnable); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL))); } } } } void QuotaClient::GetDirectoryLockForIdleMaintenance( uint32_t aRunId, MultipleMaintenanceInfo&& aMaintenanceInfo) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); if (IdleMaintenanceMustEnd(aRunId)) { return; } MOZ_ASSERT(mMaintenanceInfoHashtable); nsAutoCString key; @@ -17279,31 +17251,32 @@ QuotaClient::GetDirectoryLockForIdleMain mMaintenanceInfoHashtable->Put(key, maintenanceInfo); RefPtr<GetDirectoryLockListener> listener = new GetDirectoryLockListener(this, aRunId, key); QuotaManager* quotaManager = QuotaManager::Get(); MOZ_ASSERT(quotaManager); + // FIXME: This will update origin access time! quotaManager->OpenDirectory(maintenanceInfo->mPersistenceType, maintenanceInfo->mGroup, maintenanceInfo->mOrigin, maintenanceInfo->mIsApp, Client::IDB, /* aExclusive */ false, listener); } void QuotaClient::ScheduleIdleMaintenance(uint32_t aRunId, const nsACString& aKey, const MultipleMaintenanceInfo& aMaintenanceInfo) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); MOZ_ASSERT(!aKey.IsEmpty()); MOZ_ASSERT(mMaintenanceThreadPool); for (const nsString& databasePath : aMaintenanceInfo.mDatabasePaths) { nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethodWithArgs<uint32_t, nsCString, @@ -17342,17 +17315,18 @@ QuotaClient::PerformIdleMaintenanceOnDat nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethodWithArgs<nsCString, nsString>( this, &QuotaClient::MaybeReleaseDirectoryLockForIdleMaintenance, aKey, aMaintenanceInfo.mDatabasePath); MOZ_ASSERT(runnable); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL))); } void QuotaClient::PerformIdleMaintenanceOnDatabaseInternal( uint32_t aRunId, const SingleMaintenanceInfo& aMaintenanceInfo) { MOZ_ASSERT(!NS_IsMainThread()); @@ -17597,17 +17571,17 @@ QuotaClient::DetermineMaintenanceAction( // track the values needed for the heuristics below. if (schemaVersion < MakeSchemaVersion(18, 0)) { *aMaintenanceAction = MaintenanceAction::Nothing; return NS_OK; } bool lowDiskSpace = IndexedDatabaseManager::InLowDiskSpaceMode(); - if (kRunningXPCShellTests) { + if (QuotaManager::kRunningXPCShellTests) { // If we're running XPCShell then we want to test both the low disk space // and normal disk space code paths so pick semi-randomly based on the // current time. lowDiskSpace = ((PR_Now() / PR_USEC_PER_MSEC) % 2) == 0; } // If we're low on disk space then the best we can hope for is that an // incremental vacuum might free some space. That is a journaled operation so @@ -17858,17 +17832,17 @@ QuotaClient::FullVacuum(mozIStorageConne } } void QuotaClient::MaybeReleaseDirectoryLockForIdleMaintenance( const nsACString& aKey, const nsAString& aDatabasePath) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); MOZ_ASSERT(!aKey.IsEmpty()); MOZ_ASSERT(!aDatabasePath.IsEmpty()); MOZ_ASSERT(mMaintenanceInfoHashtable); MultipleMaintenanceInfo* maintenanceInfo; MOZ_ALWAYS_TRUE(mMaintenanceInfoHashtable->Get(aKey, &maintenanceInfo)); MOZ_ASSERT(maintenanceInfo); @@ -17878,37 +17852,16 @@ QuotaClient::MaybeReleaseDirectoryLockFo // That's it! maintenanceInfo->mDirectoryLock = nullptr; // This will delete |maintenanceInfo|. mMaintenanceInfoHashtable->Remove(aKey); } } -NS_IMETHODIMP -QuotaClient::Observe(nsISupports* aSubject, - const char* aTopic, - const char16_t* aData) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE)) { - StartIdleMaintenance(); - return NS_OK; - } - - if (!strcmp(aTopic, OBSERVER_TOPIC_ACTIVE)) { - StopIdleMaintenance(); - return NS_OK; - } - - MOZ_ASSERT_UNREACHABLE("Should never get here!"); - return NS_OK; -} - nsresult QuotaClient:: AutoProgressHandler::Register(mozIStorageConnection* aConnection) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(!IsOnBackgroundThread()); MOZ_ASSERT(aConnection); @@ -17977,133 +17930,21 @@ AutoProgressHandler::OnProgress(mozIStor MOZ_ASSERT(aConnection); MOZ_ASSERT(mConnection == aConnection); MOZ_ASSERT(_retval); *_retval = mQuotaClient->IdleMaintenanceMustEnd(mRunId); return NS_OK; } -NS_IMPL_ISUPPORTS_INHERITED0(QuotaClient::ShutdownWorkThreadsRunnable, - nsRunnable) - -NS_IMETHODIMP -QuotaClient:: -ShutdownWorkThreadsRunnable::Run() -{ - if (NS_IsMainThread()) { - MOZ_ASSERT(QuotaClient::GetInstance() == mQuotaClient); - MOZ_ASSERT(mQuotaClient->mShutdownRunnable == this); - - mQuotaClient->mShutdownRunnable = nullptr; - mQuotaClient = nullptr; - - return NS_OK; - } - - AssertIsOnBackgroundThread(); - - RefPtr<ConnectionPool> connectionPool = gConnectionPool.get(); - if (connectionPool) { - connectionPool->Shutdown(); - - gConnectionPool = nullptr; - } - - RefPtr<FileHandleThreadPool> fileHandleThreadPool = - gFileHandleThreadPool.get(); - if (fileHandleThreadPool) { - fileHandleThreadPool->Shutdown(); - - gFileHandleThreadPool = nullptr; - } - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); - - return NS_OK; -} - -// static -PLDHashOperator -QuotaClient:: -AbortOperationsRunnable::MatchOrigin(const nsACString& aKey, - DatabaseActorInfo* aValue, - void* aClosure) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!aKey.IsEmpty()); - MOZ_ASSERT(aValue); - MOZ_ASSERT(aClosure); - - auto* closure = static_cast<AbortOperationsRunnable*>(aClosure); - - for (Database* database : aValue->mLiveDatabases) { - if (closure->mOrigin.IsVoid() || closure->mOrigin == database->Origin()) { - closure->mDatabases.AppendElement(database); - } - } - - return PL_DHASH_NEXT; -} - -// static -PLDHashOperator -QuotaClient:: -AbortOperationsRunnable::MatchContentParentId(const nsACString& aKey, - DatabaseActorInfo* aValue, - void* aClosure) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!aKey.IsEmpty()); - MOZ_ASSERT(aValue); - MOZ_ASSERT(aClosure); - - auto* closure = static_cast<AbortOperationsRunnable*>(aClosure); - - for (Database* database : aValue->mLiveDatabases) { - if (database->IsOwnedByProcess(closure->mContentParentId)) { - closure->mDatabases.AppendElement(database); - } - } - - return PL_DHASH_NEXT; -} - -NS_IMPL_ISUPPORTS_INHERITED0(QuotaClient::AbortOperationsRunnable, nsRunnable) - -NS_IMETHODIMP -QuotaClient:: -AbortOperationsRunnable::Run() -{ - AssertIsOnBackgroundThread(); - - if (!gLiveDatabaseHashtable) { - return NS_OK; - } - - if (mOrigin.IsEmpty()) { - gLiveDatabaseHashtable->EnumerateRead(MatchContentParentId, this); - } else { - gLiveDatabaseHashtable->EnumerateRead(MatchOrigin, this); - } - - for (Database* database : mDatabases) { - database->Invalidate(); - } - - mDatabases.Clear(); - - return NS_OK; -} - void QuotaClient:: GetDirectoryLockListener::DirectoryLockAcquired(DirectoryLock* aLock) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); MultipleMaintenanceInfo* maintenanceInfo; MOZ_ALWAYS_TRUE( mQuotaClient->mMaintenanceInfoHashtable->Get(mKey, &maintenanceInfo)); MOZ_ASSERT(maintenanceInfo); MOZ_ASSERT(!maintenanceInfo->mDirectoryLock); if (mQuotaClient->IdleMaintenanceMustEnd(mRunId)) { @@ -18119,17 +17960,17 @@ GetDirectoryLockListener::DirectoryLockA mQuotaClient->ScheduleIdleMaintenance(mRunId, mKey, *maintenanceInfo); } void QuotaClient:: GetDirectoryLockListener::DirectoryLockFailed() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); DebugOnly<MultipleMaintenanceInfo*> maintenanceInfo; MOZ_ASSERT( mQuotaClient->mMaintenanceInfoHashtable->Get(mKey, &maintenanceInfo)); MOZ_ASSERT(maintenanceInfo); MOZ_ASSERT(!maintenanceInfo->mDirectoryLock); mQuotaClient->mMaintenanceInfoHashtable->Remove(mKey); @@ -19325,30 +19166,30 @@ FactoryOp::FactoryOp(Factory* aFactory, , mEnforcingQuota(true) , mDeleting(aDeleting) , mBlockedDatabaseOpen(false) , mChromeWriteAccessAllowed(false) , mFileHandleDisabled(false) { AssertIsOnBackgroundThread(); MOZ_ASSERT(aFactory); - MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread()); + MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread()); } nsresult FactoryOp::Open() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mState == State::Initial); // Swap this to the stack now to ensure that we release it on this thread. RefPtr<ContentParent> contentParent; mContentParent.swap(contentParent); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) || + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) || !OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } PermissionRequestBase::PermissionValue permission; nsresult rv = CheckPermission(contentParent, &permission); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -19360,40 +19201,31 @@ FactoryOp::Open() permission == PermissionRequestBase::kPermissionPrompt); if (permission == PermissionRequestBase::kPermissionDenied) { return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } { // These services have to be started on the main thread currently. - if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) { + + IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate(); + if (NS_WARN_IF(!mgr)) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } + mgr->NoteBackgroundThread(mOwningThread); + nsCOMPtr<mozIStorageService> ss; if (NS_WARN_IF(!(ss = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID)))) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - - if (NS_WARN_IF(!QuotaManager::GetOrCreate())) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - } - - QuotaClient* quotaClient = QuotaClient::GetInstance(); - if (NS_WARN_IF(!quotaClient)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - quotaClient->NoteBackgroundThread(mOwningThread); + } const DatabaseMetadata& metadata = mCommonParams.metadata(); QuotaManager::GetStorageId(metadata.persistenceType(), mOrigin, Client::IDB, mDatabaseId); @@ -19404,20 +19236,19 @@ FactoryOp::Open() mState = State::PermissionChallenge; MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL))); return NS_OK; } MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed); - rv = FinishOpen(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + mState = State::FinishOpen; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, + NS_DISPATCH_NORMAL))); return NS_OK; } nsresult FactoryOp::ChallengePermission() { AssertIsOnOwningThread(); @@ -19440,17 +19271,17 @@ FactoryOp::RetryCheckPermission() MOZ_ASSERT(mState == State::PermissionRetry); MOZ_ASSERT(mCommonParams.principalInfo().type() == PrincipalInfo::TContentPrincipalInfo); // Swap this to the stack now to ensure that we release it on this thread. RefPtr<ContentParent> contentParent; mContentParent.swap(contentParent); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) || + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) || !OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } PermissionRequestBase::PermissionValue permission; nsresult rv = CheckPermission(contentParent, &permission); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -19463,29 +19294,28 @@ FactoryOp::RetryCheckPermission() if (permission == PermissionRequestBase::kPermissionDenied || permission == PermissionRequestBase::kPermissionPrompt) { return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed); - rv = FinishOpen(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + mState = State::FinishOpen; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, + NS_DISPATCH_NORMAL))); return NS_OK; } nsresult FactoryOp::DirectoryOpen() { AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State::DirectoryWorkOpen); + MOZ_ASSERT(mState == State::DirectoryOpenPending); MOZ_ASSERT(mDirectoryLock); // gFactoryOps could be null here if the child process crashed or something // and that cleaned up the last Factory actor. if (!gFactoryOps) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -19505,29 +19335,32 @@ FactoryOp::DirectoryOpen() // Adding this to the factory ops list will block any additional ops from // proceeding until this one is done. gFactoryOps->AppendElement(this); mBlockedDatabaseOpen = true; mState = State::DatabaseOpenPending; if (!delayed) { - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); + nsresult rv = DatabaseOpen(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } } return NS_OK; } nsresult FactoryOp::SendToIOThread() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(mState == State::DatabaseOpenPending); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) || + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) || !OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } QuotaManager* quotaManager = QuotaManager::Get(); MOZ_ASSERT(quotaManager); @@ -19570,17 +19403,17 @@ FactoryOp::FinishSendResults() MOZ_ASSERT(mFactory); // Make sure to release the factory on this thread. RefPtr<Factory> factory; mFactory.swap(factory); if (mBlockedDatabaseOpen) { if (mDelayedOp) { - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(mDelayedOp))); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(mDelayedOp))); mDelayedOp = nullptr; } MOZ_ASSERT(gFactoryOps); gFactoryOps->RemoveElement(this); } mState = State::Completed; @@ -19859,44 +19692,68 @@ FactoryOp::CheckAtLeastOneAppHasPermissi } return false; #else return true; #endif // MOZ_CHILD_PERMISSIONS } -nsresult +void FactoryOp::FinishOpen() { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State::Initial || mState == State::PermissionRetry); + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State::FinishOpen); + MOZ_ASSERT(!mContentParent); + + if (QuotaManager::Get()) { + OpenDirectory(); + + return; + } + + mState = State::QuotaManagerPending; + QuotaManager::GetOrCreate(this); +} + +nsresult +FactoryOp::QuotaManagerOpen() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State::QuotaManagerPending); + + if (NS_WARN_IF(!QuotaManager::Get())) { + return NS_ERROR_FAILURE; + } + + OpenDirectory(); + + return NS_OK; +} + +void +FactoryOp::OpenDirectory() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State::FinishOpen || + mState == State::QuotaManagerPending); MOZ_ASSERT(!mOrigin.IsEmpty()); - MOZ_ASSERT(!mDatabaseId.IsEmpty()); MOZ_ASSERT(!mDirectoryLock); - MOZ_ASSERT(!mContentParent); - MOZ_ASSERT(!QuotaClient::IsShuttingDownOnMainThread()); - - QuotaManager* quotaManager = QuotaManager::GetOrCreate(); - if (NS_WARN_IF(!quotaManager)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } + MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread()); + MOZ_ASSERT(QuotaManager::Get()); mState = State::DirectoryOpenPending; - quotaManager->OpenDirectory(mCommonParams.metadata().persistenceType(), - mGroup, - mOrigin, - mIsApp, - Client::IDB, - /* aExclusive */ false, - this); - - return NS_OK; + QuotaManager::Get()->OpenDirectory(mCommonParams.metadata().persistenceType(), + mGroup, + mOrigin, + mIsApp, + Client::IDB, + /* aExclusive */ false, + this); } bool FactoryOp::MustWaitFor(const FactoryOp& aExistingOp) { AssertIsOnOwningThread(); // Things for the same persistence type, the same origin and the same @@ -19953,18 +19810,22 @@ FactoryOp::Run() case State::PermissionChallenge: rv = ChallengePermission(); break; case State::PermissionRetry: rv = RetryCheckPermission(); break; - case State::DirectoryWorkOpen: - rv = DirectoryOpen(); + case State::FinishOpen: + FinishOpen(); + return NS_OK; + + case State::QuotaManagerPending: + rv = QuotaManagerOpen(); break; case State::DatabaseOpenPending: rv = DatabaseOpen(); break; case State::DatabaseWorkOpen: rv = DoDatabaseWork(); @@ -20004,42 +19865,49 @@ FactoryOp::Run() } return NS_OK; } void FactoryOp::DirectoryLockAcquired(DirectoryLock* aLock) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(mState == State::DirectoryOpenPending); MOZ_ASSERT(!mDirectoryLock); mDirectoryLock = aLock; - mState = State::DirectoryWorkOpen; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, - NS_DISPATCH_NORMAL))); + nsresult rv = DirectoryOpen(); + if (NS_WARN_IF(NS_FAILED(rv))) { + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = rv; + } + + mState = State::SendingResults; + SendResults(); + + return; + } } void FactoryOp::DirectoryLockFailed() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(mState == State::DirectoryOpenPending); MOZ_ASSERT(!mDirectoryLock); if (NS_SUCCEEDED(mResultCode)) { IDB_REPORT_INTERNAL_ERR(); mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } mState = State::SendingResults; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, - NS_DISPATCH_NORMAL))); + SendResults(); } void FactoryOp::ActorDestroy(ActorDestroyReason aWhy) { AssertIsOnBackgroundThread(); NoteActorDestroyed(); @@ -20088,17 +19956,17 @@ OpenDatabaseOp::ActorDestroy(ActorDestro if (mVersionChangeOp) { mVersionChangeOp->NoteActorDestroyed(); } } nsresult OpenDatabaseOp::DatabaseOpen() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(mState == State::DatabaseOpenPending); nsresult rv = SendToIOThread(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; @@ -20109,17 +19977,17 @@ OpenDatabaseOp::DoDatabaseWork() { AssertIsOnIOThread(); MOZ_ASSERT(mState == State::DatabaseWorkOpen); PROFILER_LABEL("IndexedDB", "OpenDatabaseOp::DoDatabaseWork", js::ProfileEntry::Category::STORAGE); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) || !OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } const nsString& databaseName = mCommonParams.metadata().name(); PersistenceType persistenceType = mCommonParams.metadata().persistenceType(); @@ -20721,17 +20589,17 @@ OpenDatabaseOp::BeginVersionChange() { AssertIsOnOwningThread(); MOZ_ASSERT(mState == State::BeginVersionChange); MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); MOZ_ASSERT(mMetadata->mCommonMetadata.version() <= mRequestedVersion); MOZ_ASSERT(!mDatabase); MOZ_ASSERT(!mVersionChangeTransaction); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) || IsActorDestroyed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } EnsureDatabaseActor(); if (mDatabase->IsInvalidated()) { @@ -20851,17 +20719,17 @@ OpenDatabaseOp::DispatchToWorkThread() { AssertIsOnOwningThread(); MOZ_ASSERT(mState == State::WaitingForTransactionsToComplete); MOZ_ASSERT(mVersionChangeTransaction); MOZ_ASSERT(mVersionChangeTransaction->GetMode() == IDBTransaction::VERSION_CHANGE); MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) || IsActorDestroyed() || mDatabase->IsInvalidated()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } mState = State::DatabaseWorkVersionChange; @@ -20904,17 +20772,17 @@ OpenDatabaseOp::SendUpgradeNeeded() { AssertIsOnOwningThread(); MOZ_ASSERT(mState == State::DatabaseWorkVersionChange); MOZ_ASSERT(mVersionChangeTransaction); MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); MOZ_ASSERT_IF(!IsActorDestroyed(), mDatabase); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) || IsActorDestroyed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } RefPtr<VersionChangeTransaction> transaction; mVersionChangeTransaction.swap(transaction); @@ -21032,20 +20900,17 @@ OpenDatabaseOp::SendResults() void OpenDatabaseOp::ConnectionClosedCallback() { AssertIsOnOwningThread(); MOZ_ASSERT(NS_FAILED(mResultCode)); MOZ_ASSERT(mDirectoryLock); - RefPtr<UnlockDirectoryRunnable> runnable = - new UnlockDirectoryRunnable(mDirectoryLock.forget()); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); + mDirectoryLock = nullptr; } void OpenDatabaseOp::EnsureDatabaseActor() { AssertIsOnOwningThread(); MOZ_ASSERT(mState == State::BeginVersionChange || mState == State::DatabaseWorkVersionChange || @@ -21347,17 +21212,17 @@ nsresult OpenDatabaseOp:: VersionChangeOp::DoDatabaseWork(DatabaseConnection* aConnection) { MOZ_ASSERT(aConnection); aConnection->AssertIsOnConnectionThread(); MOZ_ASSERT(mOpenDatabaseOp); MOZ_ASSERT(mOpenDatabaseOp->mState == State::DatabaseWorkVersionChange); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) || !OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } PROFILER_LABEL("IndexedDB", "OpenDatabaseOp::VersionChangeOp::DoDatabaseWork", js::ProfileEntry::Category::STORAGE); @@ -21524,17 +21389,17 @@ DeleteDatabaseOp::LoadPreviousVersion(ns } mPreviousVersion = uint64_t(version); } nsresult DeleteDatabaseOp::DatabaseOpen() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(mState == State::DatabaseOpenPending); // Swap this to the stack now to ensure that we release it on this thread. RefPtr<ContentParent> contentParent; mContentParent.swap(contentParent); nsresult rv = SendToIOThread(); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -21549,17 +21414,17 @@ DeleteDatabaseOp::DoDatabaseWork() { AssertIsOnIOThread(); MOZ_ASSERT(mState == State::DatabaseWorkOpen); PROFILER_LABEL("IndexedDB", "DeleteDatabaseOp::DoDatabaseWork", js::ProfileEntry::Category::STORAGE); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) || !OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } const nsString& databaseName = mCommonParams.metadata().name(); PersistenceType persistenceType = mCommonParams.metadata().persistenceType(); @@ -21626,17 +21491,17 @@ DeleteDatabaseOp::DoDatabaseWork() nsresult DeleteDatabaseOp::BeginVersionChange() { AssertIsOnOwningThread(); MOZ_ASSERT(mState == State::BeginVersionChange); MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) || IsActorDestroyed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } DatabaseActorInfo* info; if (gLiveDatabaseHashtable->Get(mDatabaseId, &info)) { MOZ_ASSERT(!info->mWaitingFactoryOp); @@ -21665,27 +21530,35 @@ DeleteDatabaseOp::BeginVersionChange() nsresult DeleteDatabaseOp::DispatchToWorkThread() { AssertIsOnOwningThread(); MOZ_ASSERT(mState == State::WaitingForTransactionsToComplete); MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) || IsActorDestroyed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } mState = State::DatabaseWorkVersionChange; RefPtr<VersionChangeOp> versionChangeOp = new VersionChangeOp(this); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(versionChangeOp))); + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + nsresult rv = + quotaManager->IOThread()->Dispatch(versionChangeOp, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } return NS_OK; } void DeleteDatabaseOp::NoteDatabaseClosed(Database* aDatabase) { AssertIsOnOwningThread(); @@ -21749,54 +21622,23 @@ DeleteDatabaseOp::SendResults() } else { response = ClampResultCode(mResultCode); } Unused << PBackgroundIDBFactoryRequestParent::Send__delete__(this, response); } - if (mDirectoryLock) { - RefPtr<UnlockDirectoryRunnable> runnable = - new UnlockDirectoryRunnable(mDirectoryLock.forget()); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); - } + mDirectoryLock = nullptr; FinishSendResults(); } nsresult DeleteDatabaseOp:: -VersionChangeOp::RunOnMainThread() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mDeleteDatabaseOp->mState == State::DatabaseWorkVersionChange); - - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) || - !OperationMayProceed()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); - if (NS_WARN_IF(NS_FAILED(rv))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - return NS_OK; -} - - -nsresult -DeleteDatabaseOp:: VersionChangeOp::DeleteFile(nsIFile* aDirectory, const nsAString& aFilename, QuotaManager* aQuotaManager) { AssertIsOnIOThread(); MOZ_ASSERT(aDirectory); MOZ_ASSERT(!aFilename.IsEmpty()); MOZ_ASSERT_IF(aQuotaManager, mDeleteDatabaseOp->mEnforcingQuota); @@ -21863,17 +21705,17 @@ VersionChangeOp::RunOnIOThread() { AssertIsOnIOThread(); MOZ_ASSERT(mDeleteDatabaseOp->mState == State::DatabaseWorkVersionChange); PROFILER_LABEL("IndexedDB", "DeleteDatabaseOp::VersionChangeOp::RunOnIOThread", js::ProfileEntry::Category::STORAGE); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) || !OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } const PersistenceType& persistenceType = mDeleteDatabaseOp->mCommonParams.metadata().persistenceType(); @@ -22087,19 +21929,17 @@ VersionChangeOp::RunOnOwningThread() } nsresult DeleteDatabaseOp:: VersionChangeOp::Run() { nsresult rv; - if (NS_IsMainThread()) { - rv = RunOnMainThread(); - } else if (!IsOnBackgroundThread()) { + if (IsOnIOThread()) { rv = RunOnIOThread(); } else { RunOnOwningThread(); rv = NS_OK; } if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_SUCCEEDED(mResultCode)) { @@ -22604,17 +22444,17 @@ DatabaseOp::DatabaseOp(Database* aDataba { AssertIsOnBackgroundThread(); MOZ_ASSERT(aDatabase); } nsresult DatabaseOp::SendToIOThread() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); MOZ_ASSERT(mState == State::Initial); if (!OperationMayProceed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } QuotaManager* quotaManager = QuotaManager::Get(); @@ -27171,16 +27011,182 @@ ContinueOp::SendSuccessResult() } mCursor->SendResponseInternal(mResponse, mFiles); mResponseSent = true; return NS_OK; } +Utils::Utils() +#ifdef DEBUG + : mActorDestroyed(false) +#endif +{ + AssertIsOnBackgroundThread(); +} + +Utils::~Utils() +{ + MOZ_ASSERT(mActorDestroyed); +} + +void +Utils::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + +#ifdef DEBUG + mActorDestroyed = true; +#endif +} + +bool +Utils::RecvDeleteMe() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + return PBackgroundIndexedDBUtilsParent::Send__delete__(this); +} + +bool +Utils::RecvGetFileReferences(const PersistenceType& aPersistenceType, + const nsCString& aOrigin, + const nsString& aDatabaseName, + const int64_t& aFileId, + int32_t* aRefCnt, + int32_t* aDBRefCnt, + int32_t* aSliceRefCnt, + bool* aResult) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aRefCnt); + MOZ_ASSERT(aDBRefCnt); + MOZ_ASSERT(aSliceRefCnt); + MOZ_ASSERT(aResult); + MOZ_ASSERT(!mActorDestroyed); + + if (NS_WARN_IF(!IndexedDatabaseManager::Get() || + !QuotaManager::Get())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(!IndexedDatabaseManager::InTestingMode())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(aPersistenceType != quota::PERSISTENCE_TYPE_PERSISTENT && + aPersistenceType != quota::PERSISTENCE_TYPE_TEMPORARY && + aPersistenceType != quota::PERSISTENCE_TYPE_DEFAULT)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(aOrigin.IsEmpty())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(aDatabaseName.IsEmpty())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(aFileId == 0)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + RefPtr<GetFileReferencesHelper> helper = + new GetFileReferencesHelper(aPersistenceType, aOrigin, aDatabaseName, + aFileId); + + nsresult rv = + helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt, + aSliceRefCnt, aResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + return true; +} + +nsresult +GetFileReferencesHelper::DispatchAndReturnFileReferences(int32_t* aMemRefCnt, + int32_t* aDBRefCnt, + int32_t* aSliceRefCnt, + bool* aResult) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aMemRefCnt); + MOZ_ASSERT(aDBRefCnt); + MOZ_ASSERT(aSliceRefCnt); + MOZ_ASSERT(aResult); + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + nsresult rv = + quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mozilla::MutexAutoLock autolock(mMutex); + while (mWaiting) { + mCondVar.Wait(); + } + + *aMemRefCnt = mMemRefCnt; + *aDBRefCnt = mDBRefCnt; + *aSliceRefCnt = mSliceRefCnt; + *aResult = mResult; + + return NS_OK; +} + +NS_IMETHODIMP +GetFileReferencesHelper::Run() +{ + AssertIsOnIOThread(); + + IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); + MOZ_ASSERT(mgr); + + RefPtr<FileManager> fileManager = + mgr->GetFileManager(mPersistenceType, mOrigin, mDatabaseName); + + if (fileManager) { + RefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(mFileId); + + if (fileInfo) { + fileInfo->GetReferences(&mMemRefCnt, &mDBRefCnt, &mSliceRefCnt); + + if (mMemRefCnt != -1) { + // We added an extra temp ref, so account for that accordingly. + mMemRefCnt--; + } + + mResult = true; + } + } + + mozilla::MutexAutoLock lock(mMutex); + MOZ_ASSERT(mWaiting); + + mWaiting = false; + mCondVar.Notify(); + + return NS_OK; +} + void PermissionRequestHelper::OnPromptComplete(PermissionValue aPermissionValue) { MOZ_ASSERT(NS_IsMainThread()); if (!mActorDestroyed) { Unused << PIndexedDBPermissionRequestParent::Send__delete__(this, aPermissionValue);
--- a/dom/indexedDB/ActorsParent.h +++ b/dom/indexedDB/ActorsParent.h @@ -21,28 +21,35 @@ namespace quota { class Client; } // namespace quota namespace indexedDB { class LoggingInfo; class PBackgroundIDBFactoryParent; +class PBackgroundIndexedDBUtilsParent; class PIndexedDBPermissionRequestParent; PBackgroundIDBFactoryParent* AllocPBackgroundIDBFactoryParent(const LoggingInfo& aLoggingInfo); bool RecvPBackgroundIDBFactoryConstructor(PBackgroundIDBFactoryParent* aActor, const LoggingInfo& aLoggingInfo); bool DeallocPBackgroundIDBFactoryParent(PBackgroundIDBFactoryParent* aActor); +PBackgroundIndexedDBUtilsParent* +AllocPBackgroundIndexedDBUtilsParent(); + +bool +DeallocPBackgroundIndexedDBUtilsParent(PBackgroundIndexedDBUtilsParent* aActor); + PIndexedDBPermissionRequestParent* AllocPIndexedDBPermissionRequestParent(Element* aOwnerElement, nsIPrincipal* aPrincipal); bool RecvPIndexedDBPermissionRequestConstructor( PIndexedDBPermissionRequestParent* aActor);
--- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -468,26 +468,16 @@ IDBFactory::IsChrome() const { AssertIsOnOwningThread(); MOZ_ASSERT(mPrincipalInfo); return mPrincipalInfo->type() == PrincipalInfo::TSystemPrincipalInfo; } void -IDBFactory::SetBackgroundActor(BackgroundFactoryChild* aBackgroundActor) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(aBackgroundActor); - MOZ_ASSERT(!mBackgroundActor); - - mBackgroundActor = aBackgroundActor; -} - -void IDBFactory::IncrementParentLoggingRequestSerialNumber() { AssertIsOnOwningThread(); MOZ_ASSERT(mBackgroundActor); mBackgroundActor->SendIncrementLoggingRequestSerialNumber(); }
--- a/dom/indexedDB/IDBFactory.h +++ b/dom/indexedDB/IDBFactory.h @@ -117,19 +117,16 @@ public: OwningThread() const; #else void AssertIsOnOwningThread() const { } #endif void - SetBackgroundActor(BackgroundFactoryChild* aBackgroundActor); - - void ClearBackgroundActor() { AssertIsOnOwningThread(); mBackgroundActor = nullptr; } void
--- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -4,16 +4,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "IndexedDatabaseManager.h" #include "nsIConsoleService.h" #include "nsIDiskSpaceWatcher.h" #include "nsIDOMWindow.h" +#include "nsIEventTarget.h" #include "nsIFile.h" #include "nsIObserverService.h" #include "nsIScriptError.h" #include "nsIScriptGlobalObject.h" #include "jsapi.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/CondVar.h" @@ -21,16 +22,19 @@ #include "mozilla/EventDispatcher.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/DOMError.h" #include "mozilla/dom/ErrorEvent.h" #include "mozilla/dom/ErrorEventBinding.h" #include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "mozilla/ipc/PBackgroundChild.h" #include "nsContentUtils.h" #include "nsGlobalWindow.h" #include "nsThreadUtils.h" #include "mozilla/Logging.h" #include "FileInfo.h" #include "FileManager.h" #include "IDBEvents.h" @@ -67,16 +71,17 @@ #define LOW_DISK_SPACE_DATA_FREE "free" namespace mozilla { namespace dom { namespace indexedDB { using namespace mozilla::dom::quota; using namespace mozilla::dom::workers; +using namespace mozilla::ipc; class FileManagerInfo { public: already_AddRefed<FileManager> GetFileManager(PersistenceType aPersistenceType, const nsAString& aName) const; @@ -173,30 +178,36 @@ class DeleteFilesRunnable final // Notifying the QuotaManager that it can proceed to the next operation on // the main thread. Next step is State_Completed. State_UnblockingOpen, // All done. State_Completed }; + nsCOMPtr<nsIEventTarget> mBackgroundThread; + RefPtr<FileManager> mFileManager; nsTArray<int64_t> mFileIds; RefPtr<DirectoryLock> mDirectoryLock; nsCOMPtr<nsIFile> mDirectory; nsCOMPtr<nsIFile> mJournalDirectory; State mState; public: - DeleteFilesRunnable(FileManager* aFileManager, + DeleteFilesRunnable(nsIEventTarget* aBackgroundThread, + FileManager* aFileManager, nsTArray<int64_t>& aFileIds); + void + Dispatch(); + NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIRUNNABLE virtual void DirectoryLockAcquired(DirectoryLock* aLock) override; virtual void DirectoryLockFailed() override; @@ -215,82 +226,42 @@ private: void Finish(); void UnblockOpen(); }; -class GetFileReferencesHelper final : public nsIRunnable -{ -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIRUNNABLE - - GetFileReferencesHelper(PersistenceType aPersistenceType, - const nsACString& aOrigin, - const nsAString& aDatabaseName, - int64_t aFileId) - : mPersistenceType(aPersistenceType), - mOrigin(aOrigin), - mDatabaseName(aDatabaseName), - mFileId(aFileId), - mMutex(IndexedDatabaseManager::FileMutex()), - mCondVar(mMutex, "GetFileReferencesHelper::mCondVar"), - mMemRefCnt(-1), - mDBRefCnt(-1), - mSliceRefCnt(-1), - mResult(false), - mWaiting(true) - { } - - nsresult - DispatchAndReturnFileReferences(int32_t* aMemRefCnt, - int32_t* aDBRefCnt, - int32_t* aSliceRefCnt, - bool* aResult); - -private: - ~GetFileReferencesHelper() {} - - PersistenceType mPersistenceType; - nsCString mOrigin; - nsString mDatabaseName; - int64_t mFileId; - - mozilla::Mutex& mMutex; - mozilla::CondVar mCondVar; - int32_t mMemRefCnt; - int32_t mDBRefCnt; - int32_t mSliceRefCnt; - bool mResult; - bool mWaiting; -}; - void AtomicBoolPrefChangedCallback(const char* aPrefName, void* aClosure) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aClosure); *static_cast<Atomic<bool>*>(aClosure) = Preferences::GetBool(aPrefName); } } // namespace IndexedDatabaseManager::IndexedDatabaseManager() -: mFileMutex("IndexedDatabaseManager.mFileMutex") + : mFileMutex("IndexedDatabaseManager.mFileMutex") + , mBackgroundActor(nullptr) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); } IndexedDatabaseManager::~IndexedDatabaseManager() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (mBackgroundActor) { + mBackgroundActor->SendDeleteMeInternal(); + MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!"); + } } bool IndexedDatabaseManager::sIsMainProcess = false; bool IndexedDatabaseManager::sFullSynchronousMode = false; PRLogModuleInfo* IndexedDatabaseManager::sLoggingModule; Atomic<IndexedDatabaseManager::LoggingMode> @@ -758,16 +729,34 @@ IndexedDatabaseManager::IsFileHandleEnab { MOZ_ASSERT(gDBManager, "IsFileHandleEnabled() called before indexedDB has been " "initialized!"); return gFileHandleEnabled; } +void +IndexedDatabaseManager::ClearBackgroundActor() +{ + MOZ_ASSERT(NS_IsMainThread()); + + mBackgroundActor = nullptr; +} + +void +IndexedDatabaseManager::NoteBackgroundThread(nsIEventTarget* aBackgroundThread) +{ + MOZ_ASSERT(IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aBackgroundThread); + + mBackgroundThread = aBackgroundThread; +} + already_AddRefed<FileManager> IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType, const nsACString& aOrigin, const nsAString& aDatabaseName) { AssertIsOnIOThread(); FileManagerInfo* info; @@ -893,43 +882,42 @@ IndexedDatabaseManager::BlockAndGetFileR bool* aResult) { MOZ_ASSERT(NS_IsMainThread()); if (NS_WARN_IF(!InTestingMode())) { return NS_ERROR_UNEXPECTED; } - if (IsMainProcess()) { - RefPtr<GetFileReferencesHelper> helper = - new GetFileReferencesHelper(aPersistenceType, aOrigin, aDatabaseName, - aFileId); - - nsresult rv = - helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt, - aSliceRefCnt, aResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } else { - ContentChild* contentChild = ContentChild::GetSingleton(); - if (NS_WARN_IF(!contentChild)) { + if (!mBackgroundActor) { + PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread(); + if (NS_WARN_IF(!bgActor)) { return NS_ERROR_FAILURE; } - if (!contentChild->SendGetFileReferences(aPersistenceType, - nsCString(aOrigin), - nsString(aDatabaseName), - aFileId, - aRefCnt, - aDBRefCnt, - aSliceRefCnt, - aResult)) { - return NS_ERROR_FAILURE; - } + BackgroundUtilsChild* actor = new BackgroundUtilsChild(this); + + mBackgroundActor = + static_cast<BackgroundUtilsChild*>( + bgActor->SendPBackgroundIndexedDBUtilsConstructor(actor)); + } + + if (NS_WARN_IF(!mBackgroundActor)) { + return NS_ERROR_FAILURE; + } + + if (!mBackgroundActor->SendGetFileReferences(aPersistenceType, + nsCString(aOrigin), + nsString(aDatabaseName), + aFileId, + aRefCnt, + aDBRefCnt, + aSliceRefCnt, + aResult)) { + return NS_ERROR_FAILURE; } return NS_OK; } nsresult IndexedDatabaseManager::FlushPendingFileDeletions() { @@ -1053,21 +1041,21 @@ IndexedDatabaseManager::Notify(nsITimer* MOZ_ASSERT(NS_IsMainThread()); for (auto iter = mPendingDeleteInfos.ConstIter(); !iter.Done(); iter.Next()) { auto key = iter.Key(); auto value = iter.Data(); MOZ_ASSERT(!value->IsEmpty()); RefPtr<DeleteFilesRunnable> runnable = - new DeleteFilesRunnable(key, *value); + new DeleteFilesRunnable(mBackgroundThread, key, *value); MOZ_ASSERT(value->IsEmpty()); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); + runnable->Dispatch(); } mPendingDeleteInfos.Clear(); return NS_OK; } already_AddRefed<FileManager> @@ -1169,24 +1157,36 @@ FileManagerInfo::GetArray(PersistenceTyp return mDefaultStorageFileManagers; case PERSISTENCE_TYPE_INVALID: default: MOZ_CRASH("Bad storage type value!"); } } -DeleteFilesRunnable::DeleteFilesRunnable(FileManager* aFileManager, +DeleteFilesRunnable::DeleteFilesRunnable(nsIEventTarget* aBackgroundThread, + FileManager* aFileManager, nsTArray<int64_t>& aFileIds) - : mFileManager(aFileManager) + : mBackgroundThread(aBackgroundThread) + , mFileManager(aFileManager) , mState(State_Initial) { mFileIds.SwapElements(aFileIds); } +void +DeleteFilesRunnable::Dispatch() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_Initial); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL))); +} + NS_IMPL_ISUPPORTS(DeleteFilesRunnable, nsIRunnable) NS_IMETHODIMP DeleteFilesRunnable::Run() { nsresult rv; switch (mState) { @@ -1212,17 +1212,17 @@ DeleteFilesRunnable::Run() } return NS_OK; } void DeleteFilesRunnable::DirectoryLockAcquired(DirectoryLock* aLock) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); MOZ_ASSERT(mState == State_DirectoryOpenPending); MOZ_ASSERT(!mDirectoryLock); mDirectoryLock = aLock; QuotaManager* quotaManager = QuotaManager::Get(); MOZ_ASSERT(quotaManager); @@ -1234,27 +1234,27 @@ DeleteFilesRunnable::DirectoryLockAcquir Finish(); return; } } void DeleteFilesRunnable::DirectoryLockFailed() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); MOZ_ASSERT(mState == State_DirectoryOpenPending); MOZ_ASSERT(!mDirectoryLock); Finish(); } nsresult DeleteFilesRunnable::Open() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); MOZ_ASSERT(mState == State_Initial); QuotaManager* quotaManager = QuotaManager::Get(); if (NS_WARN_IF(!quotaManager)) { return NS_ERROR_FAILURE; } mState = State_DirectoryOpenPending; @@ -1339,93 +1339,26 @@ DeleteFilesRunnable::DoDatabaseWork() void DeleteFilesRunnable::Finish() { // Must set mState before dispatching otherwise we will race with the main // thread. mState = State_UnblockingOpen; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL))); } void DeleteFilesRunnable::UnblockOpen() { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnBackgroundThread(); MOZ_ASSERT(mState == State_UnblockingOpen); - if (mDirectoryLock) { - mDirectoryLock = nullptr; - } + mDirectoryLock = nullptr; mState = State_Completed; } -nsresult -GetFileReferencesHelper::DispatchAndReturnFileReferences(int32_t* aMemRefCnt, - int32_t* aDBRefCnt, - int32_t* aSliceRefCnt, - bool* aResult) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "Shouldn't be null!"); - - nsresult rv = - quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); - NS_ENSURE_SUCCESS(rv, rv); - - mozilla::MutexAutoLock autolock(mMutex); - while (mWaiting) { - mCondVar.Wait(); - } - - *aMemRefCnt = mMemRefCnt; - *aDBRefCnt = mDBRefCnt; - *aSliceRefCnt = mSliceRefCnt; - *aResult = mResult; - - return NS_OK; -} - -NS_IMPL_ISUPPORTS(GetFileReferencesHelper, - nsIRunnable) - -NS_IMETHODIMP -GetFileReferencesHelper::Run() -{ - AssertIsOnIOThread(); - - IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); - NS_ASSERTION(mgr, "This should never fail!"); - - RefPtr<FileManager> fileManager = - mgr->GetFileManager(mPersistenceType, mOrigin, mDatabaseName); - - if (fileManager) { - RefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(mFileId); - - if (fileInfo) { - fileInfo->GetReferences(&mMemRefCnt, &mDBRefCnt, &mSliceRefCnt); - - if (mMemRefCnt != -1) { - // We added an extra temp ref, so account for that accordingly. - mMemRefCnt--; - } - - mResult = true; - } - } - - mozilla::MutexAutoLock lock(mMutex); - NS_ASSERTION(mWaiting, "Huh?!"); - - mWaiting = false; - mCondVar.Notify(); - - return NS_OK; -} - } // namespace indexedDB } // namespace dom } // namespace mozilla
--- a/dom/indexedDB/IndexedDatabaseManager.h +++ b/dom/indexedDB/IndexedDatabaseManager.h @@ -13,26 +13,28 @@ #include "mozilla/Atomics.h" #include "mozilla/dom/quota/PersistenceType.h" #include "mozilla/Mutex.h" #include "nsClassHashtable.h" #include "nsCOMPtr.h" #include "nsHashKeys.h" #include "nsITimer.h" +class nsIEventTarget; struct PRLogModuleInfo; namespace mozilla { class EventChainPostVisitor; namespace dom { namespace indexedDB { +class BackgroundUtilsChild; class FileManager; class FileManagerInfo; class IDBFactory; class IndexedDatabaseManager final : public nsIObserver , public nsITimerCallback { @@ -116,16 +118,22 @@ public: ExperimentalFeaturesEnabled(JSContext* /* aCx */, JSObject* /* aGlobal */) { return ExperimentalFeaturesEnabled(); } static bool IsFileHandleEnabled(); + void + ClearBackgroundActor(); + + void + NoteBackgroundThread(nsIEventTarget* aBackgroundThread); + already_AddRefed<FileManager> GetFileManager(PersistenceType aPersistenceType, const nsACString& aOrigin, const nsAString& aDatabaseName); void AddFileManager(FileManager* aFileManager); @@ -189,16 +197,18 @@ private: Init(); void Destroy(); static void LoggingModePrefChangedCallback(const char* aPrefName, void* aClosure); + nsCOMPtr<nsIEventTarget> mBackgroundThread; + nsCOMPtr<nsITimer> mDeleteTimer; // Maintains a list of all file managers per origin. This list isn't // protected by any mutex but it is only ever touched on the IO thread. nsClassHashtable<nsCStringHashKey, FileManagerInfo> mFileManagerInfos; nsClassHashtable<nsRefPtrHashKey<FileManager>, nsTArray<int64_t>> mPendingDeleteInfos; @@ -207,16 +217,18 @@ private: // It's s also used to atomically update FileInfo.mRefCnt, FileInfo.mDBRefCnt // and FileInfo.mSliceRefCnt mozilla::Mutex mFileMutex; #ifdef ENABLE_INTL_API nsCString mLocale; #endif + BackgroundUtilsChild* mBackgroundActor; + static bool sIsMainProcess; static bool sFullSynchronousMode; static PRLogModuleInfo* sLoggingModule; static Atomic<LoggingMode> sLoggingMode; static mozilla::Atomic<bool> sLowDiskSpaceMode; }; } // namespace indexedDB
--- a/dom/indexedDB/PBackgroundIDBFactory.ipdl +++ b/dom/indexedDB/PBackgroundIDBFactory.ipdl @@ -4,16 +4,18 @@ include protocol PBackground; include protocol PBackgroundIDBDatabase; include protocol PBackgroundIDBFactoryRequest; include PBackgroundIDBSharedTypes; include PBackgroundSharedTypes; +include "mozilla/dom/quota/SerializationHelpers.h"; + using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; namespace mozilla { namespace dom { namespace indexedDB { struct CommonFactoryRequestParams
--- a/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh +++ b/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh @@ -4,16 +4,17 @@ include protocol PBackgroundIDBDatabaseFile; include protocol PBackgroundMutableFile; include protocol PBlob; include DOMTypes; include "mozilla/dom/indexedDB/SerializationHelpers.h"; +include "mozilla/dom/quota/SerializationHelpers.h"; using struct mozilla::null_t from "ipc/IPCMessageUtils.h"; using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; using mozilla::dom::indexedDB::IDBCursor::Direction
new file mode 100644 --- /dev/null +++ b/dom/indexedDB/PBackgroundIndexedDBUtils.ipdl @@ -0,0 +1,37 @@ +/* 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 protocol PBackground; + +include "mozilla/dom/quota/SerializationHelpers.h"; + +using mozilla::dom::quota::PersistenceType + from "mozilla/dom/quota/PersistenceType.h"; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +sync protocol PBackgroundIndexedDBUtils +{ + manager PBackground; + +parent: + DeleteMe(); + + // Use only for testing! + sync GetFileReferences(PersistenceType persistenceType, + nsCString origin, + nsString databaseName, + int64_t fileId) + returns (int32_t refCnt, int32_t dBRefCnt, int32_t sliceRefCnt, + bool result); + +child: + __delete__(); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla
--- a/dom/indexedDB/SerializationHelpers.h +++ b/dom/indexedDB/SerializationHelpers.h @@ -8,29 +8,20 @@ #define mozilla_dom_indexeddb_serializationhelpers_h__ #include "ipc/IPCMessageUtils.h" #include "mozilla/dom/indexedDB/Key.h" #include "mozilla/dom/indexedDB/KeyPath.h" #include "mozilla/dom/indexedDB/IDBCursor.h" #include "mozilla/dom/indexedDB/IDBTransaction.h" -#include "mozilla/dom/quota/PersistenceType.h" namespace IPC { template <> -struct ParamTraits<mozilla::dom::quota::PersistenceType> : - public ContiguousEnumSerializer< - mozilla::dom::quota::PersistenceType, - mozilla::dom::quota::PERSISTENCE_TYPE_PERSISTENT, - mozilla::dom::quota::PERSISTENCE_TYPE_INVALID> -{ }; - -template <> struct ParamTraits<mozilla::dom::indexedDB::Key> { typedef mozilla::dom::indexedDB::Key paramType; static void Write(Message* aMsg, const paramType& aParam) { WriteParam(aMsg, aParam.mBuffer); }
--- a/dom/indexedDB/moz.build +++ b/dom/indexedDB/moz.build @@ -77,16 +77,17 @@ IPDL_SOURCES += [ 'PBackgroundIDBDatabaseFile.ipdl', 'PBackgroundIDBDatabaseRequest.ipdl', 'PBackgroundIDBFactory.ipdl', 'PBackgroundIDBFactoryRequest.ipdl', 'PBackgroundIDBRequest.ipdl', 'PBackgroundIDBSharedTypes.ipdlh', 'PBackgroundIDBTransaction.ipdl', 'PBackgroundIDBVersionChangeTransaction.ipdl', + 'PBackgroundIndexedDBUtils.ipdl', 'PIndexedDBPermissionRequest.ipdl', ] include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' if CONFIG['GNU_CC']:
--- a/dom/indexedDB/test/bug839193.js +++ b/dom/indexedDB/test/bug839193.js @@ -1,26 +1,28 @@ /* 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/. */ -const nsIQuotaManager = Components.interfaces.nsIQuotaManager; +const nsIQuotaManagerService = Components.interfaces.nsIQuotaManagerService; var gURI = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService).newURI("http://localhost", null, null); -function onUsageCallback(principal, usage, fileUsage) {} +function onUsageCallback(request) {} function onLoad() { - var quotaManager = Components.classes["@mozilla.org/dom/quota/manager;1"] - .getService(nsIQuotaManager); + var quotaManagerService = + Components.classes["@mozilla.org/dom/quota-manager-service;1"] + .getService(nsIQuotaManagerService); let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"] .getService(Components.interfaces.nsIScriptSecurityManager) .createCodebasePrincipal(gURI, {}); - var quotaRequest = quotaManager.getUsageForPrincipal(principal, onUsageCallback); + var quotaRequest = quotaManagerService.getUsageForPrincipal(principal, + onUsageCallback); quotaRequest.cancel(); Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService) .notifyObservers(window, "bug839193-loaded", null); } function onUnload() {
--- a/dom/indexedDB/test/file.js +++ b/dom/indexedDB/test/file.js @@ -177,24 +177,27 @@ function verifyMutableFile(mutableFile1, ok(mutableFile1 instanceof IDBMutableFile, "Instance of IDBMutableFile"); is(mutableFile1.name, file2.name, "Correct name"); is(mutableFile1.type, file2.type, "Correct type"); executeSoon(function() { testGenerator.next(); }); } -function grabFileUsageAndContinueHandler(usage, fileUsage) +function grabFileUsageAndContinueHandler(request) { - testGenerator.send(fileUsage); + testGenerator.send(request.fileUsage); } function getUsage(usageHandler) { - SpecialPowers.getStorageUsageForDoc(SpecialPowers.wrap(document), usageHandler); + let qms = SpecialPowers.Services.qms; + let principal = SpecialPowers.wrap(document).nodePrincipal; + let cb = SpecialPowers.wrapCallback(usageHandler); + qms.getUsageForPrincipal(principal, cb); } function getFileId(file) { return utils.getFileId(file); } function getFilePath(file)
--- a/dom/indexedDB/test/helpers.js +++ b/dom/indexedDB/test/helpers.js @@ -29,17 +29,21 @@ function executeSoon(aFun) thread.dispatch({ run: function() { aFun(); } }, Components.interfaces.nsIThread.DISPATCH_NORMAL); } function clearAllDatabases(callback) { - SpecialPowers.clearStorageForDoc(SpecialPowers.wrap(document), callback); + let qms = SpecialPowers.Services.qms; + let principal = SpecialPowers.wrap(document).nodePrincipal;