diff --git a/browser/base/content/sanitize.js b/browser/base/content/sanitize.js index 95a10f831b1c..faafaa686299 100644 --- a/browser/base/content/sanitize.js +++ b/browser/base/content/sanitize.js @@ -19,6 +19,14 @@ XPCOMUtils.defineLazyModuleGetters(this, { setTimeout: "resource://gre/modules/Timer.jsm", }); + +XPCOMUtils.defineLazyServiceGetter(this, "serviceWorkerManager", + "@mozilla.org/serviceworkers/manager;1", + "nsIServiceWorkerManager"); +XPCOMUtils.defineLazyServiceGetter(this, "quotaManagerService", + "@mozilla.org/dom/quota-manager-service;1", + "nsIQuotaManagerService"); + var {classes: Cc, interfaces: Ci} = Components; /** @@ -280,9 +288,41 @@ Sanitizer.prototype = { offlineApps: { async clear(range) { + // AppCache Components.utils.import("resource:///modules/offlineAppCache.jsm"); // This doesn't wait for the cleanup to be complete. OfflineAppCacheHelper.clear(); + + // LocalStorage + Services.obs.notifyObservers(null, "extension:purge-localStorage"); + + // ServiceWorkers + let serviceWorkers = serviceWorkerManager.getAllRegistrations(); + for (let i = 0; i < serviceWorkers.length; i++) { + let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo); + let host = sw.principal.URI.host; + serviceWorkerManager.removeAndPropagate(host); + } + + // QuotaManager + let promises = []; + await new Promise(resolve => { + quotaManagerService.getUsage(request => { + for (let item of request.result) { + let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin); + let uri = principal.URI; + if (uri.scheme == "http" || uri.scheme == "https" || uri.scheme == "file") { + promises.push(new Promise(r => { + let req = quotaManagerService.clearStoragesForPrincipal(principal, null, true); + req.callback = () => { r(); }; + })); + } + } + resolve(); + }); + }); + + return Promise.all(promises); } }, diff --git a/browser/components/extensions/ext-browsingData.js b/browser/components/extensions/ext-browsingData.js index e313319333a0..0424d415d48d 100644 --- a/browser/components/extensions/ext-browsingData.js +++ b/browser/components/extensions/ext-browsingData.js @@ -17,6 +17,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "setTimeout", XPCOMUtils.defineLazyServiceGetter(this, "serviceWorkerManager", "@mozilla.org/serviceworkers/manager;1", "nsIServiceWorkerManager"); +XPCOMUtils.defineLazyServiceGetter(this, "quotaManagerService", + "@mozilla.org/dom/quota-manager-service;1", + "nsIQuotaManagerService"); /** * A number of iterations after which to yield time back @@ -80,6 +83,29 @@ const clearHistory = options => { return sanitizer.items.history.clear(makeRange(options)); }; +const clearIndexedDB = async function(options) { + let promises = []; + + await new Promise(resolve => { + quotaManagerService.getUsage(request => { + for (let item of request.result) { + let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin); + let uri = principal.URI; + if (uri.scheme == "http" || uri.scheme == "https" || uri.scheme == "file") { + promises.push(new Promise(r => { + let req = quotaManagerService.clearStoragesForPrincipal(principal, null, true); + req.callback = () => { r(); }; + })); + } + } + + resolve(); + }); + }); + + return Promise.all(promises); +}; + const clearLocalStorage = async function(options) { Services.obs.notifyObservers(null, "extension:purge-localStorage"); }; @@ -153,6 +179,9 @@ const doRemoval = (options, dataToRemove, extension) => { case "history": removalPromises.push(clearHistory(options)); break; + case "indexedDB": + removalPromises.push(clearIndexedDB(options)); + break; case "localStorage": removalPromises.push(clearLocalStorage(options)); break; @@ -229,6 +258,9 @@ this.browsingData = class extends ExtensionAPI { removeHistory(options) { return doRemoval(options, {history: true}); }, + removeIndexedDB(options) { + return doRemoval(options, {indexedDB: true}); + }, removeLocalStorage(options) { return doRemoval(options, {localStorage: true}); }, diff --git a/browser/components/extensions/test/browser/browser-common.ini b/browser/components/extensions/test/browser/browser-common.ini index 73f8bef4cdd5..2c3d43b20a53 100644 --- a/browser/components/extensions/test/browser/browser-common.ini +++ b/browser/components/extensions/test/browser/browser-common.ini @@ -22,6 +22,7 @@ support-files = file_dummy.html file_title.html file_inspectedwindow_reload_target.sjs + file_indexedDB.html file_serviceWorker.html locale/chrome.manifest webNav_createdTarget.html @@ -50,6 +51,7 @@ skip-if = (os == 'win' && !debug) # bug 1352668 [browser_ext_browserAction_theme_icons.js] [browser_ext_browsingData_formData.js] [browser_ext_browsingData_history.js] +[browser_ext_browsingData_indexedDB.js] [browser_ext_browsingData_localStorage.js] [browser_ext_browsingData_pluginData.js] [browser_ext_browsingData_serviceWorkers.js] diff --git a/browser/components/extensions/test/browser/browser_ext_browsingData_indexedDB.js b/browser/components/extensions/test/browser/browser_ext_browsingData_indexedDB.js new file mode 100644 index 000000000000..c10d1642d6ca --- /dev/null +++ b/browser/components/extensions/test/browser/browser_ext_browsingData_indexedDB.js @@ -0,0 +1,81 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ +"use strict"; + +add_task(async function testIndexedDB() { + function background() { + const PAGE = "/browser/browser/components/extensions/test/browser/file_indexedDB.html"; + + browser.test.onMessage.addListener(async (msg) => { + await browser.browsingData.remove({}, {indexedDB: true}); + browser.test.sendMessage("indexedDBRemoved"); + }); + + // Create two tabs. + browser.tabs.create({url: `http://mochi.test:8888${PAGE}`}); + browser.tabs.create({url: `http://example.com${PAGE}`}); + } + + function contentScript() { + window.addEventListener("message", msg => { // eslint-disable-line mozilla/balanced-listeners + browser.test.sendMessage("indexedDBCreated"); + }, true); + } + + let extension = ExtensionTestUtils.loadExtension({ + background, + manifest: { + permissions: ["browsingData", "tabs"], + "content_scripts": [{ + "matches": [ + "http://mochi.test/*/file_indexedDB.html", + "http://example.com/*/file_indexedDB.html", + ], + "js": ["script.js"], + "run_at": "document_start", + }], + }, + files: { + "script.js": contentScript, + }, + }); + + let win = await BrowserTestUtils.openNewBrowserWindow(); + await focusWindow(win); + + await extension.startup(); + await extension.awaitMessage("indexedDBCreated"); + await extension.awaitMessage("indexedDBCreated"); + + let qms = SpecialPowers.Cc["@mozilla.org/dom/quota-manager-service;1"] + .getService(Ci.nsIQuotaManagerService); + + function getOrigins() { + return new Promise(resolve => { + let origins = []; + qms.getUsage(request => { + for (let i = 0; i < request.result.length; ++i) { + if (request.result[i].origin.startsWith("http://mochi.test") || + request.result[i].origin.startsWith("http://example.com")) { + origins.push(request.result[i].origin); + } + } + resolve(origins); + }); + }); + } + + let origins = await getOrigins(); + is(origins.length, 2, "IndexedDB databases have been populated."); + + extension.sendMessage(); + + await extension.awaitMessage("indexedDBRemoved"); + + origins = await getOrigins(); + is(origins.length, 0, "IndexedDB data has been removed."); + + await extension.unload(); + await BrowserTestUtils.closeWindow(win); +}); diff --git a/browser/components/extensions/test/browser/file_indexedDB.html b/browser/components/extensions/test/browser/file_indexedDB.html new file mode 100644 index 000000000000..65b7e0ad2f4f --- /dev/null +++ b/browser/components/extensions/test/browser/file_indexedDB.html @@ -0,0 +1,28 @@ + + + + + + + + This is a test page. + + diff --git a/browser/components/extensions/test/xpcshell/test_ext_browsingData.js b/browser/components/extensions/test/xpcshell/test_ext_browsingData.js index 0b8972058e64..bb5cea4ba703 100644 --- a/browser/components/extensions/test/xpcshell/test_ext_browsingData.js +++ b/browser/components/extensions/test/xpcshell/test_ext_browsingData.js @@ -41,26 +41,3 @@ add_task(async function testInvalidArguments() { await extension.awaitFinish("invalidArguments"); await extension.unload(); }); - -add_task(async function testUnimplementedDataType() { - function background() { - browser.browsingData.remove({}, {indexedDB: true}); - browser.test.sendMessage("finished"); - } - - let {messages} = await promiseConsoleOutput(async function() { - let extension = ExtensionTestUtils.loadExtension({ - background: background, - manifest: { - permissions: ["browsingData"], - }, - }); - - await extension.startup(); - await extension.awaitMessage("finished"); - await extension.unload(); - }); - - let warningObserved = messages.find(line => /Firefox does not support dataTypes: indexedDB/.test(line)); - ok(warningObserved, "Warning issued when calling remove with an unimplemented dataType."); -}); diff --git a/browser/components/preferences/SiteDataManager.jsm b/browser/components/preferences/SiteDataManager.jsm index d6dd5c78656a..8f2ac2a34188 100644 --- a/browser/components/preferences/SiteDataManager.jsm +++ b/browser/components/preferences/SiteDataManager.jsm @@ -9,6 +9,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "OfflineAppCacheHelper", "resource:///modules/offlineAppCache.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService", "resource://gre/modules/ContextualIdentityService.jsm"); +XPCOMUtils.defineLazyServiceGetter(this, "serviceWorkerManager", + "@mozilla.org/serviceworkers/manager;1", + "nsIServiceWorkerManager"); this.EXPORTED_SYMBOLS = [ "SiteDataManager" @@ -217,6 +220,19 @@ this.SiteDataManager = { } }, + _removeServiceWorkers(site) { + let serviceWorkers = serviceWorkerManager.getAllRegistrations(); + for (let i = 0; i < serviceWorkers.length; i++) { + let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo); + for (let principal of site.principals) { + if (sw.principal.equals(principal)) { + serviceWorkerManager.removeAndPropagate(sw.principal.URI.host); + break; + } + } + } + }, + remove(hosts) { let promises = []; let unknownHost = ""; @@ -226,6 +242,7 @@ this.SiteDataManager = { this._removePermission(site); this._removeAppCache(site); this._removeCookie(site); + this._removeServiceWorkers(site); promises.push(this._removeQuotaUsage(site)); } else { unknownHost = host; @@ -244,6 +261,15 @@ this.SiteDataManager = { Services.cache2.clear(); Services.cookies.removeAll(); OfflineAppCacheHelper.clear(); + + // Iterate through the service workers and remove them. + let serviceWorkers = serviceWorkerManager.getAllRegistrations(); + for (let i = 0; i < serviceWorkers.length; i++) { + let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo); + let host = sw.principal.URI.host; + serviceWorkerManager.removeAndPropagate(host); + } + // Refresh sites using quota usage again. // This is for the case: // 1. User goes to the about:preferences Site Data section. diff --git a/browser/components/preferences/in-content/privacy.js b/browser/components/preferences/in-content/privacy.js index f0edd6df662e..b58389d026c8 100644 --- a/browser/components/preferences/in-content/privacy.js +++ b/browser/components/preferences/in-content/privacy.js @@ -1647,6 +1647,13 @@ var gPrivacyPane = { }, updateA11yPrefs(checked) { - Services.prefs.setIntPref("accessibility.force_disabled", checked ? 1 : 0); + let buttonIndex = confirmRestartPrompt(checked, 0, true, false); + if (buttonIndex == CONFIRM_RESTART_PROMPT_RESTART_NOW) { + Services.prefs.setIntPref("accessibility.force_disabled", checked ? 1 : 0); + Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart); + } + + // Revert the checkbox in case we didn't quit + document.getElementById("a11yPrivacyCheckbox").checked = !checked; } }; diff --git a/build/moz.configure/toolchain.configure b/build/moz.configure/toolchain.configure index ae397f77fd12..c336d1abdd7d 100755 --- a/build/moz.configure/toolchain.configure +++ b/build/moz.configure/toolchain.configure @@ -702,11 +702,17 @@ def compiler(language, host_or_target, c_compiler=None, other_compiler=None, host_or_target) # Check that the additional flags we got are enough to not require any - # more flags. - if info.flags: - flags += info.flags - info = check_compiler(wrapper + [compiler] + flags, language, - host_or_target) + # more flags. If we get an exception, just ignore it; it's liable to be + # invalid command-line flags, which means the compiler we're checking + # doesn't support those command-line flags and will fail one or more of + # the checks below. + try: + if info.flags: + flags += info.flags + info = check_compiler(wrapper + [compiler] + flags, language, + host_or_target) + except FatalCheckError: + pass if not info.target_cpu or info.target_cpu != host_or_target.cpu: raise FatalCheckError( @@ -732,10 +738,6 @@ def compiler(language, host_or_target, c_compiler=None, other_compiler=None, info.target_endianness or 'unknown', host_or_target_str, host_or_target.endianness)) - if info.flags: - raise FatalCheckError( - 'Unknown compiler or compiler not supported.') - # Compiler version checks # =================================================== # Check the compiler version here instead of in `compiler_version` so @@ -747,7 +749,7 @@ def compiler(language, host_or_target, c_compiler=None, other_compiler=None, % info.version) # If you want to bump the version check here search for - # __cpp_static_assert above, and see the associated comment. + # cxx_alignof above, and see the associated comment. if info.type == 'clang' and not info.version: raise FatalCheckError( 'Only clang/llvm 3.6 or newer is supported.') @@ -762,6 +764,10 @@ def compiler(language, host_or_target, c_compiler=None, other_compiler=None, 'See https://developer.mozilla.org/en/' 'Windows_Build_Prerequisites' % info.version) + if info.flags: + raise FatalCheckError( + 'Unknown compiler or compiler not supported.') + return namespace( wrapper=wrapper, compiler=compiler, diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 6748a2722e5a..d566bdde944d 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -1609,7 +1609,7 @@ nsDocShell::LoadURI(nsIURI* aURI, flags, target, nullptr, // No type hint - NullString(), // No forced download + VoidString(), // No forced download postStream, headersStream, loadType, @@ -5488,8 +5488,8 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL, mozilla::net::RP_Unset, nsContentUtils::GetSystemPrincipal(), nullptr, INTERNAL_LOAD_FLAGS_NONE, EmptyString(), - nullptr, NullString(), nullptr, nullptr, LOAD_ERROR_PAGE, - nullptr, true, NullString(), this, nullptr, false, + nullptr, VoidString(), nullptr, nullptr, LOAD_ERROR_PAGE, + nullptr, true, VoidString(), this, nullptr, false, nullptr, nullptr); } @@ -5589,7 +5589,7 @@ nsDocShell::Reload(uint32_t aReloadFlags) flags, EmptyString(), // No window target NS_LossyConvertUTF16toASCII(contentTypeHint).get(), - NullString(), // No forced download + VoidString(), // No forced download nullptr, // No post data nullptr, // No headers data loadType, // Load type @@ -9757,7 +9757,7 @@ public: mFlags, EmptyString(), mTypeHint.IsVoid() ? nullptr : mTypeHint.get(), - NullString(), mPostData, mHeadersData, + VoidString(), mPostData, mHeadersData, mLoadType, mSHEntry, mFirstParty, mSrcdoc, mSourceDocShell, mBaseURI, mCheckForPrerender, nullptr, nullptr); @@ -10226,7 +10226,7 @@ nsDocShell::InternalLoad(nsIURI* aURI, aFlags, EmptyString(), // No window target aTypeHint, - NullString(), // No forced download + VoidString(), // No forced download aPostData, aHeadersData, aLoadType, @@ -10837,7 +10837,7 @@ nsDocShell::InternalLoad(nsIURI* aURI, if (aFlags & INTERNAL_LOAD_FLAGS_IS_SRCDOC) { srcdoc = aSrcdoc; } else { - srcdoc = NullString(); + srcdoc = VoidString(); } bool isTopLevelDoc = mItemType == typeContent && @@ -12860,7 +12860,7 @@ nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType) aEntry->GetBaseURI(getter_AddRefs(baseURI)); flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC; } else { - srcdoc = NullString(); + srcdoc = VoidString(); } if (!triggeringPrincipal) { @@ -12884,7 +12884,7 @@ nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType) flags, EmptyString(), // No window target contentType.get(), // Type hint - NullString(), // No forced file download + VoidString(), // No forced file download postData, // Post data stream nullptr, // No headers stream aLoadType, // Load type @@ -14454,7 +14454,7 @@ nsDocShell::OnLinkClickSync(nsIContent* aContent, LOAD_LINK, // Load type nullptr, // No SHEntry true, // first party site - NullString(), // No srcdoc + VoidString(), // No srcdoc this, // We are the source nullptr, // baseURI not needed true, // Check for prerendered doc diff --git a/docshell/shistory/nsSHEntry.cpp b/docshell/shistory/nsSHEntry.cpp index 2ada9d07261a..5bae3bb3feab 100644 --- a/docshell/shistory/nsSHEntry.cpp +++ b/docshell/shistory/nsSHEntry.cpp @@ -476,7 +476,7 @@ nsSHEntry::Create(nsIURI* aURI, const nsAString& aTitle, mShared->mExpired = false; mIsSrcdocEntry = false; - mSrcdocData = NullString(); + mSrcdocData = VoidString(); mLoadedInThisProcess = true; diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp index e5b8520fff16..5299d500cb1a 100644 --- a/dom/base/CustomElementRegistry.cpp +++ b/dom/base/CustomElementRegistry.cpp @@ -593,6 +593,9 @@ CustomElementRegistry::Define(const nsAString& aName, } JSContext *cx = jsapi.cx(); + // Note: No calls that might run JS or trigger CC before this point, or + // there's a (vanishingly small) chance of our constructor being nulled + // before we access it. JS::Rooted constructor(cx, aFunctionConstructor.CallableOrNull()); /** @@ -878,7 +881,7 @@ CustomElementRegistry::Get(JSContext* aCx, const nsAString& aName, return; } - aRetVal.setObjectOrNull(data->mConstructor->CallableOrNull()); + aRetVal.setObject(*data->mConstructor->Callback(aCx)); } already_AddRefed @@ -970,9 +973,9 @@ CustomElementRegistry::Upgrade(Element* aElement, LifecycleCallbackArgs args = { nsDependentAtomString(attrName), - NullString(), - (attrValue.IsEmpty() ? NullString() : attrValue), - (namespaceURI.IsEmpty() ? NullString() : namespaceURI) + VoidString(), + (attrValue.IsEmpty() ? VoidString() : attrValue), + (namespaceURI.IsEmpty() ? VoidString() : namespaceURI) }; EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, aElement, &args, aDefinition); diff --git a/dom/base/DOMImplementation.cpp b/dom/base/DOMImplementation.cpp index 65fd8a5cbdda..24fe7ca3798c 100644 --- a/dom/base/DOMImplementation.cpp +++ b/dom/base/DOMImplementation.cpp @@ -70,7 +70,7 @@ DOMImplementation::CreateDocumentType(const nsAString& aQualifiedName, // Indicate that there is no internal subset (not just an empty one) RefPtr docType = NS_NewDOMDocumentType(mOwner->NodeInfoManager(), name, aPublicId, - aSystemId, NullString(), aRv); + aSystemId, VoidString(), aRv); return docType.forget(); } @@ -188,7 +188,7 @@ DOMImplementation::CreateHTMLDocument(const nsAString& aTitle, nsGkAtoms::html, // aName EmptyString(), // aPublicId EmptyString(), // aSystemId - NullString()); // aInternalSubset + VoidString()); // aInternalSubset NS_ENSURE_SUCCESS(rv, rv); @@ -249,8 +249,7 @@ DOMImplementation::CreateHTMLDocument(const Optional& aTitle, { nsCOMPtr document; nsCOMPtr domDocument; - aRv = CreateHTMLDocument(aTitle.WasPassed() ? aTitle.Value() - : NullString(), + aRv = CreateHTMLDocument(aTitle.WasPassed() ? aTitle.Value() : VoidString(), getter_AddRefs(document), getter_AddRefs(domDocument)); return document.forget(); diff --git a/dom/base/DOMIntersectionObserver.cpp b/dom/base/DOMIntersectionObserver.cpp index 606a1de5ed8c..1ca909e3cfbf 100644 --- a/dom/base/DOMIntersectionObserver.cpp +++ b/dom/base/DOMIntersectionObserver.cpp @@ -308,7 +308,11 @@ DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time } root = rootFrame->GetContent()->AsElement(); nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame); - rootRect = scrollFrame->GetScrollPortRect(); + // If we end up with a null root frame for some reason, we'll proceed + // with an empty root intersection rect. + if (scrollFrame) { + rootRect = scrollFrame->GetScrollPortRect(); + } } } } diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index fe666c633009..fa2221077767 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -2642,9 +2642,9 @@ Element::SetAttrAndNotify(int32_t aNamespaceID, LifecycleCallbackArgs args = { nsDependentAtomString(aName), aModType == nsIDOMMutationEvent::ADDITION ? - NullString() : nsDependentAtomString(oldValueAtom), + VoidString() : nsDependentAtomString(oldValueAtom), nsDependentAtomString(newValueAtom), - (ns.IsEmpty() ? NullString() : ns) + (ns.IsEmpty() ? VoidString() : ns) }; nsContentUtils::EnqueueLifecycleCallback( @@ -2938,8 +2938,8 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, LifecycleCallbackArgs args = { nsDependentAtomString(aName), nsDependentAtomString(oldValueAtom), - NullString(), - (ns.IsEmpty() ? NullString() : ns) + VoidString(), + (ns.IsEmpty() ? VoidString() : ns) }; nsContentUtils::EnqueueLifecycleCallback( diff --git a/dom/base/nsDOMMutationObserver.h b/dom/base/nsDOMMutationObserver.h index 98fdffc2ba9b..d386b6466e34 100644 --- a/dom/base/nsDOMMutationObserver.h +++ b/dom/base/nsDOMMutationObserver.h @@ -39,7 +39,7 @@ public: typedef nsTArray> AnimationArray; nsDOMMutationRecord(nsIAtom* aType, nsISupports* aOwner) - : mType(aType), mAttrNamespace(NullString()), mPrevValue(NullString()), mOwner(aOwner) + : mType(aType), mAttrNamespace(VoidString()), mPrevValue(VoidString()), mOwner(aOwner) { } diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 55198595d0ec..335f0b305dc6 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -3584,7 +3584,7 @@ nsIDocument::GetSrcdocData(nsAString &aSrcdocData) return inStrmChan->GetSrcdocData(aSrcdocData); } } - aSrcdocData = NullString(); + aSrcdocData = VoidString(); return NS_OK; } @@ -6549,6 +6549,9 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType, options.mExtends.Construct(lcName); } + // Note: No calls that might run JS or trigger CC after this, or there's a + // (vanishingly small) risk of our constructor being nulled before Define() + // can access it. RefPtr functionConstructor = new Function(aCx, wrappedConstructor, sgo); @@ -9793,7 +9796,7 @@ nsDocument::PreloadPictureImageSource(const nsAString& aSrcsetAttr, // selects the first matching source, so if this returns a URI we // needn't consider new sources until a new is encountered. bool found = - HTMLImageElement::SelectSourceForTagWithAttrs(this, true, NullString(), + HTMLImageElement::SelectSourceForTagWithAttrs(this, true, VoidString(), aSrcsetAttr, aSizesAttr, aTypeAttr, aMediaAttr, mPreloadPictureFoundSource); @@ -9822,7 +9825,7 @@ nsDocument::ResolvePreloadImage(nsIURI *aBaseURI, // Otherwise try to use this as a source HTMLImageElement::SelectSourceForTagWithAttrs(this, false, aSrcAttr, aSrcsetAttr, aSizesAttr, - NullString(), NullString(), + VoidString(), VoidString(), sourceURL); isImgSet = !aSrcsetAttr.IsEmpty(); } @@ -10920,11 +10923,12 @@ public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler) - explicit UnblockParsingPromiseHandler(nsIDocument* aDocument, Promise* aPromise) + explicit UnblockParsingPromiseHandler(nsIDocument* aDocument, Promise* aPromise, + const BlockParsingOptions& aOptions) : mPromise(aPromise) { nsCOMPtr parser = aDocument->CreatorParserOrNull(); - if (parser) { + if (parser && (aOptions.mBlockScriptCreated || !parser->IsScriptCreated())) { parser->BlockParser(); mParser = do_GetWeakReference(parser); mDocument = aDocument; @@ -10987,14 +10991,15 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(UnblockParsingPromiseHandler) NS_IMPL_CYCLE_COLLECTING_RELEASE(UnblockParsingPromiseHandler) already_AddRefed -nsIDocument::BlockParsing(Promise& aPromise, ErrorResult& aRv) +nsIDocument::BlockParsing(Promise& aPromise, const BlockParsingOptions& aOptions, ErrorResult& aRv) { RefPtr resultPromise = Promise::Create(aPromise.GetParentObject(), aRv); if (aRv.Failed()) { return nullptr; } - RefPtr promiseHandler = new UnblockParsingPromiseHandler(this, resultPromise); + RefPtr promiseHandler = new UnblockParsingPromiseHandler(this, resultPromise, + aOptions); aPromise.AppendNativeHandler(promiseHandler); return resultPromise.forget(); @@ -12799,7 +12804,7 @@ nsIDocument::Constructor(const GlobalObject& aGlobal, nsCOMPtr document; nsresult res = NS_NewDOMDocument(getter_AddRefs(document), - NullString(), + VoidString(), EmptyString(), nullptr, uri, diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index 2f287ae2ccb7..512de896fddf 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -2952,6 +2952,7 @@ public: void ObsoleteSheet(const nsAString& aSheetURI, mozilla::ErrorResult& rv); already_AddRefed BlockParsing(mozilla::dom::Promise& aPromise, + const mozilla::dom::BlockParsingOptions& aOptions, mozilla::ErrorResult& aRv); already_AddRefed GetMozDocumentURIIfNotForErrorPages(); diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 059e4f19882c..b62d2b7aa6ce 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -1918,9 +1918,9 @@ GetOrCreateDOMReflectorNoWrap(JSContext* cx, T& value, template inline JSObject* -GetCallbackFromCallbackObject(T* aObj) +GetCallbackFromCallbackObject(JSContext* aCx, T* aObj) { - return aObj->CallbackOrNull(); + return aObj->Callback(aCx); } // Helper for getting the callback JSObject* of a smart ptr around a @@ -1929,26 +1929,26 @@ GetCallbackFromCallbackObject(T* aObj) template ::value> struct GetCallbackFromCallbackObjectHelper { - static inline JSObject* Get(const T& aObj) + static inline JSObject* Get(JSContext* aCx, const T& aObj) { - return GetCallbackFromCallbackObject(aObj.get()); + return GetCallbackFromCallbackObject(aCx, aObj.get()); } }; template struct GetCallbackFromCallbackObjectHelper { - static inline JSObject* Get(T& aObj) + static inline JSObject* Get(JSContext* aCx, T& aObj) { - return GetCallbackFromCallbackObject(&aObj); + return GetCallbackFromCallbackObject(aCx, &aObj); } }; template inline JSObject* -GetCallbackFromCallbackObject(T& aObj) +GetCallbackFromCallbackObject(JSContext* aCx, T& aObj) { - return GetCallbackFromCallbackObjectHelper::Get(aObj); + return GetCallbackFromCallbackObjectHelper::Get(aCx, aObj); } static inline bool diff --git a/dom/bindings/CallbackObject.cpp b/dom/bindings/CallbackObject.cpp index 44898cc61733..f8ddc8e005ac 100644 --- a/dom/bindings/CallbackObject.cpp +++ b/dom/bindings/CallbackObject.cpp @@ -115,6 +115,18 @@ CallbackObject::FinishSlowJSInitIfMoreThanOneOwner(JSContext* aCx) } } +JSObject* +CallbackObject::Callback(JSContext* aCx) +{ + JSObject* callback = CallbackOrNull(); + if (!callback) { + callback = JS_NewDeadWrapper(aCx); + } + + MOZ_DIAGNOSTIC_ASSERT(callback); + return callback; +} + CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback, ErrorResult& aRv, const char* aExecutionReason, diff --git a/dom/bindings/CallbackObject.h b/dom/bindings/CallbackObject.h index 843611dea8df..94605cd37758 100644 --- a/dom/bindings/CallbackObject.h +++ b/dom/bindings/CallbackObject.h @@ -93,6 +93,10 @@ public: return CallbackPreserveColor(); } + // Like CallbackOrNull(), but will return a new dead proxy object in the + // caller's compartment if the callback is null. + JSObject* Callback(JSContext* aCx); + JSObject* GetCreationStack() const { return mCreationStack; @@ -594,12 +598,17 @@ public: this->get().operator=(arg); } - // Codegen relies on being able to do CallbackOrNull() on us. + // Codegen relies on being able to do CallbackOrNull() and Callback() on us. JS::Handle CallbackOrNull() const { return this->get()->CallbackOrNull(); } + JSObject* Callback(JSContext* aCx) const + { + return this->get()->Callback(aCx); + } + ~RootedCallback() { // Ensure that our callback starts holding on to its own JS objects as diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 616f3f4c41b1..9e3ea13f68ab 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -6912,7 +6912,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode, # Callbacks can store null if we nuked the compartments their # objects lived in. wrapCode = setObjectOrNull( - "GetCallbackFromCallbackObject(%(result)s)", + "GetCallbackFromCallbackObject(cx, %(result)s)", wrapAsType=type) if type.nullable(): wrapCode = ( diff --git a/dom/bindings/ToJSValue.h b/dom/bindings/ToJSValue.h index e1cb287be787..797870e8af65 100644 --- a/dom/bindings/ToJSValue.h +++ b/dom/bindings/ToJSValue.h @@ -132,7 +132,7 @@ ToJSValue(JSContext* aCx, // Make sure we're called in a compartment MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx)); - aValue.setObjectOrNull(aArgument.CallbackOrNull()); + aValue.setObjectOrNull(aArgument.Callback(aCx)); return MaybeWrapValue(aCx, aValue); } diff --git a/dom/console/Console.cpp b/dom/console/Console.cpp index 37e1acaafe7b..42e97ee52918 100644 --- a/dom/console/Console.cpp +++ b/dom/console/Console.cpp @@ -1844,7 +1844,7 @@ Console::ProcessArguments(JSContext* aCx, int32_t diff = aSequence.Length() - aStyles.Length(); if (diff > 0) { for (int32_t i = 0; i < diff; i++) { - if (NS_WARN_IF(!aStyles.AppendElement(NullString(), fallible))) { + if (NS_WARN_IF(!aStyles.AppendElement(VoidString(), fallible))) { return false; } } diff --git a/dom/events/DOMEventTargetHelper.cpp b/dom/events/DOMEventTargetHelper.cpp index 35c106a478b6..f04585cd3e57 100644 --- a/dom/events/DOMEventTargetHelper.cpp +++ b/dom/events/DOMEventTargetHelper.cpp @@ -324,7 +324,7 @@ DOMEventTargetHelper::GetEventHandler(nsIAtom* aType, { EventHandlerNonNull* handler = GetEventHandler(aType, EmptyString()); if (handler) { - *aValue = JS::ObjectOrNullValue(handler->CallableOrNull()); + *aValue = JS::ObjectValue(*handler->Callback(aCx)); } else { *aValue = JS::NullValue(); } diff --git a/dom/events/test/test_bug574663.html b/dom/events/test/test_bug574663.html index b3351288cb37..2b1b01eabf6f 100644 --- a/dom/events/test/test_bug574663.html +++ b/dom/events/test/test_bug574663.html @@ -105,12 +105,20 @@ function runTest() { // browser to 0 using the 'blank' attribute. // Until the blank attribute is removed, we can't send scroll events. SimpleTest.waitForFocus(function() { - SpecialPowers.loadChromeScript(_ => { + let chromeScript = SpecialPowers.loadChromeScript(_ => { Components.utils.import("resource://gre/modules/Services.jsm"); - Services.wm.getMostRecentWindow("navigator:browser") - .gBrowser.selectedBrowser.removeAttribute("blank"); + let win = Services.wm.getMostRecentWindow("navigator:browser"); + win.requestAnimationFrame(() => { + win.gBrowser.selectedBrowser.removeAttribute("blank"); + win.requestAnimationFrame(() => { + sendAsyncMessage("blank-attribute-removed"); + }); + }); + }); + chromeScript.promiseOneMessage("blank-attribute-removed").then(() => { + chromeScript.destroy(); + waitForPaint(win, winUtils, callback); }); - waitForPaint(win, winUtils, callback); }, win); }; diff --git a/dom/events/test/window_wheel_default_action.html b/dom/events/test/window_wheel_default_action.html index 53f3ddb27a10..84ad330ad9b9 100644 --- a/dom/events/test/window_wheel_default_action.html +++ b/dom/events/test/window_wheel_default_action.html @@ -52,12 +52,20 @@ // browser to 0 using the 'blank' attribute. // Until the blank attribute is removed, we can't send scroll events. SimpleTest.waitForFocus(function() { - SpecialPowers.loadChromeScript(_ => { + let chromeScript = SpecialPowers.loadChromeScript(_ => { Components.utils.import("resource://gre/modules/Services.jsm"); - Services.wm.getMostRecentWindow("navigator:browser") - .gBrowser.selectedBrowser.removeAttribute("blank"); + let win = Services.wm.getMostRecentWindow("navigator:browser"); + win.requestAnimationFrame(() => { + win.gBrowser.selectedBrowser.removeAttribute("blank"); + win.requestAnimationFrame(() => { + sendAsyncMessage("blank-attribute-removed"); + }); + }); + }); + chromeScript.promiseOneMessage("blank-attribute-removed").then(() => { + chromeScript.destroy(); + runTests(); }); - runTests(); }, window); SimpleTest.requestFlakyTimeout("untriaged"); diff --git a/dom/file/ipc/PendingIPCBlobChild.cpp b/dom/file/ipc/PendingIPCBlobChild.cpp index d24b00bd5df3..fb7430d2333d 100644 --- a/dom/file/ipc/PendingIPCBlobChild.cpp +++ b/dom/file/ipc/PendingIPCBlobChild.cpp @@ -43,7 +43,7 @@ PendingIPCBlobChild::SetPendingInfoAndDeleteActor(const nsString& aContentType, RefPtr blobImpl; blobImpl.swap(mBlobImpl); - blobImpl->SetLazyData(NullString(), aContentType, aLength, INT64_MAX); + blobImpl->SetLazyData(VoidString(), aContentType, aLength, INT64_MAX); PendingIPCBlobData data(nsString(aContentType), aLength, void_t()); Unused << Send__delete__(this, data); diff --git a/dom/file/ipc/PendingIPCBlobParent.cpp b/dom/file/ipc/PendingIPCBlobParent.cpp index cfd542deb4f7..a2605bd7b063 100644 --- a/dom/file/ipc/PendingIPCBlobParent.cpp +++ b/dom/file/ipc/PendingIPCBlobParent.cpp @@ -44,7 +44,7 @@ IPCResult PendingIPCBlobParent::Recv__delete__(const PendingIPCBlobData& aData) { if (aData.file().type() == PendingIPCFileUnion::Tvoid_t) { - mBlobImpl->SetLazyData(NullString(), aData.type(), aData.size(), INT64_MAX); + mBlobImpl->SetLazyData(VoidString(), aData.type(), aData.size(), INT64_MAX); } else { const PendingIPCFileData& fileData = aData.file().get_PendingIPCFileData(); diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp index 6b87c8b54255..2afcfeea9432 100644 --- a/dom/html/HTMLFormElement.cpp +++ b/dom/html/HTMLFormElement.cpp @@ -802,7 +802,7 @@ HTMLFormElement::SubmitSubmission(HTMLFormSubmission* aFormSubmission) rv = linkHandler->OnLinkClickSync(this, actionURI, target.get(), - NullString(), + VoidString(), postDataStream, nullptr, false, getter_AddRefs(docShell), getter_AddRefs(mSubmittingRequest)); diff --git a/dom/html/HTMLImageElement.cpp b/dom/html/HTMLImageElement.cpp index 891d8cda26a5..6aa12eb1df04 100644 --- a/dom/html/HTMLImageElement.cpp +++ b/dom/html/HTMLImageElement.cpp @@ -353,7 +353,7 @@ HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, if (InResponsiveMode()) { if (mResponsiveSelector && mResponsiveSelector->Content() == this) { - mResponsiveSelector->SetDefaultSource(NullString()); + mResponsiveSelector->SetDefaultSource(VoidString()); } QueueImageLoadTask(true); } else { diff --git a/dom/html/test/forms/mochitest.ini b/dom/html/test/forms/mochitest.ini index 45d775b9263a..e9fe9799f58f 100644 --- a/dom/html/test/forms/mochitest.ini +++ b/dom/html/test/forms/mochitest.ini @@ -78,6 +78,7 @@ skip-if = os == "android" skip-if = os == "android" [test_input_types_pref.html] [test_input_typing_sanitization.html] +skip-if = os == "android" && debug # bug 1397615 [test_input_untrusted_key_events.html] [test_input_url.html] [test_interactive_content_in_label.html] diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index 42aa9e38d630..9a34d821ad6e 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -17378,7 +17378,7 @@ FileManager::InitDirectory(nsIFile* aDirectory, nsCOMPtr connection; rv = CreateStorageConnection(aDatabaseFile, aDirectory, - NullString(), + VoidString(), aPersistenceType, aGroup, aOrigin, diff --git a/dom/indexedDB/IDBFileHandle.h b/dom/indexedDB/IDBFileHandle.h index c35a7b3fdcd0..84c4d62f0a92 100644 --- a/dom/indexedDB/IDBFileHandle.h +++ b/dom/indexedDB/IDBFileHandle.h @@ -203,7 +203,7 @@ public: ReadAsArrayBuffer(uint64_t aSize, ErrorResult& aRv) { AssertIsOnOwningThread(); - return Read(aSize, false, NullString(), aRv); + return Read(aSize, false, VoidString(), aRv); } already_AddRefed diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index 4c395ae7e760..4567bcc12579 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -925,7 +925,7 @@ public: if (aData.tag == SCTAG_DOM_BLOB) { aFile.mBlob->Impl()->SetLazyData( - NullString(), aData.type, aData.size, INT64_MAX); + VoidString(), aData.type, aData.size, INT64_MAX); MOZ_ASSERT(!aFile.mBlob->IsFile()); // ActorsParent sends here a kind of half blob and half file wrapped into diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 8d6d3cd6db07..90e748d16890 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -768,7 +768,7 @@ private: AppInfo mAppInfo; bool mIsForBrowser; - nsString mRemoteType = NullString(); + nsString mRemoteType = VoidString(); bool mIsAlive; nsString mProcessName; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 7e7274e503ab..55bd1fe03166 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -4696,7 +4696,7 @@ ContentParent::RecvCreateWindow(PBrowserParent* aThisTab, CommonCreateWindow(aThisTab, /* aSetOpener = */ true, aChromeFlags, aCalledFromJS, aPositionSpecified, aSizeSpecified, nullptr, aFeatures, aBaseURI, aFullZoom, - nextTabParentId, NullString(), rv, + nextTabParentId, VoidString(), rv, newRemoteTab, &cwi.windowOpened(), aTriggeringPrincipal); if (!ipcResult) { diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index be387905ef8c..e14e646f0519 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -1599,8 +1599,8 @@ TabChild::MaybeDispatchCoalescedMouseMoveEvents() // Dispatch the coalesced mousemove event. Using RecvRealMouseButtonEvent to // bypass the coalesce handling in RecvRealMouseMoveEvent. RecvRealMouseButtonEvent(*event, - mCoalescedWheelData.GetScrollableLayerGuid(), - mCoalescedWheelData.GetInputBlockId()); + mCoalescedMouseData.GetScrollableLayerGuid(), + mCoalescedMouseData.GetInputBlockId()); if (mCoalescedMouseEventFlusher) { mCoalescedMouseData.Reset(); mCoalescedMouseEventFlusher->RemoveObserver(); diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index a06296086a28..98572fb7fa16 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -426,7 +426,7 @@ public: if (globalWindow) { RefPtr req = new GetUserMediaRequest(globalWindow->AsInner(), - NullString(), NullString()); + VoidString(), VoidString()); obs->NotifyObservers(req, "recording-device-stopped", nullptr); } return; diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index 3a5d8f5216bf..733c2396340d 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -491,7 +491,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetURL(const char *aURL, triggeringPrincipal = BasePrincipal::CreateCodebasePrincipal(uri, attrs); } - rv = lh->OnLinkClick(content, uri, unitarget.get(), NullString(), + rv = lh->OnLinkClick(content, uri, unitarget.get(), VoidString(), aPostStream, headersDataStream, true, triggeringPrincipal); return rv; diff --git a/dom/plugins/ipc/PluginMessageUtils.h b/dom/plugins/ipc/PluginMessageUtils.h index 2fa06278d77a..ee567a3bf2b1 100644 --- a/dom/plugins/ipc/PluginMessageUtils.h +++ b/dom/plugins/ipc/PluginMessageUtils.h @@ -307,7 +307,7 @@ inline nsCString NullableString(const char* aString) { if (!aString) { - return NullCString(); + return VoidCString(); } return nsCString(aString); } diff --git a/dom/presentation/PresentationRequest.cpp b/dom/presentation/PresentationRequest.cpp index 9ad0499f8453..4dccf68e0cc2 100644 --- a/dom/presentation/PresentationRequest.cpp +++ b/dom/presentation/PresentationRequest.cpp @@ -140,7 +140,7 @@ PresentationRequest::WrapObject(JSContext* aCx, already_AddRefed PresentationRequest::Start(ErrorResult& aRv) { - return StartWithDevice(NullString(), aRv); + return StartWithDevice(VoidString(), aRv); } already_AddRefed diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp index 48881fe188f3..9db706351a61 100644 --- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -5843,7 +5843,7 @@ QuotaManager::ShutdownTimerCallback(nsITimer* aTimer, void* aClosure) // Abort all operations. for (RefPtr& client : quotaManager->mClients) { - client->AbortOperations(NullCString()); + client->AbortOperations(VoidCString()); } } diff --git a/dom/storage/LocalStorage.cpp b/dom/storage/LocalStorage.cpp index 78c9193eaa00..7e581eeec669 100644 --- a/dom/storage/LocalStorage.cpp +++ b/dom/storage/LocalStorage.cpp @@ -154,7 +154,7 @@ LocalStorage::RemoveItem(const nsAString& aKey, nsIPrincipal& aSubjectPrincipal, } if (!aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION)) { - BroadcastChangeNotification(aKey, old, NullString()); + BroadcastChangeNotification(aKey, old, VoidString()); } } @@ -172,7 +172,7 @@ LocalStorage::Clear(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) } if (!aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION)) { - BroadcastChangeNotification(NullString(), NullString(), NullString()); + BroadcastChangeNotification(VoidString(), VoidString(), VoidString()); } } diff --git a/dom/storage/SessionStorage.cpp b/dom/storage/SessionStorage.cpp index 6f6e2540301a..3c5ef855beae 100644 --- a/dom/storage/SessionStorage.cpp +++ b/dom/storage/SessionStorage.cpp @@ -155,7 +155,7 @@ SessionStorage::RemoveItem(const nsAString& aKey, return; } - BroadcastChangeNotification(aKey, oldValue, NullString()); + BroadcastChangeNotification(aKey, oldValue, VoidString()); } void @@ -168,7 +168,7 @@ SessionStorage::Clear(nsIPrincipal& aSubjectPrincipal, } mCache->Clear(DATASET); - BroadcastChangeNotification(NullString(), NullString(), NullString()); + BroadcastChangeNotification(VoidString(), VoidString(), VoidString()); } void diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl index 8a629502a6ce..f83874fa7315 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -385,13 +385,22 @@ partial interface Document { // Blocks the initial document parser until the given promise is settled. [ChromeOnly, Throws] - Promise blockParsing(Promise promise); + Promise blockParsing(Promise promise, + optional BlockParsingOptions options); // like documentURI, except that for error pages, it returns the URI we were // trying to load when we hit an error, rather than the error page's own URI. [ChromeOnly] readonly attribute URI? mozDocumentURIIfNotForErrorPages; }; +dictionary BlockParsingOptions { + /** + * If true, blocks script-created parsers (created via document.open()) in + * addition to network-created parsers. + */ + boolean blockScriptCreated = true; +}; + // Extension to give chrome JS the ability to determine when a document was // created to satisfy an iframe with srcdoc attribute. partial interface Document { diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 518a0b32a2e1..40a951f2ba35 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -2421,7 +2421,7 @@ RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx, if (!workerPrivate) { workerPrivate = WorkerPrivate::Constructor(aCx, aScriptURL, false, - WorkerTypeShared, aName, NullCString(), + WorkerTypeShared, aName, VoidCString(), aLoadInfo, rv); NS_ENSURE_TRUE(workerPrivate, rv.StealNSResult()); diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp index b3099e3b15f7..f5a85f611544 100644 --- a/dom/workers/ServiceWorkerPrivate.cpp +++ b/dom/workers/ServiceWorkerPrivate.cpp @@ -1878,7 +1878,7 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy, mWorkerPrivate = WorkerPrivate::Constructor(jsapi.cx(), scriptSpec, false, WorkerTypeService, - NullString(), + VoidString(), mInfo->Scope(), &info, error); if (NS_WARN_IF(error.Failed())) { diff --git a/dom/workers/ServiceWorkerRegistrar.cpp b/dom/workers/ServiceWorkerRegistrar.cpp index f90bb735e064..0e3a570ef6c6 100644 --- a/dom/workers/ServiceWorkerRegistrar.cpp +++ b/dom/workers/ServiceWorkerRegistrar.cpp @@ -1061,6 +1061,34 @@ ServiceWorkerRegistrar::ProfileStopped() PBackgroundChild* child = BackgroundChild::GetForCurrentThread(); if (!child) { + // Mutations to the ServiceWorkerRegistrar happen on the PBackground thread, + // issued by the ServiceWorkerManagerService, so the appropriate place to + // trigger shutdown is on that thread. + // + // However, it's quite possible that the PBackground thread was not brought + // into existence for xpcshell tests. We don't cause it to be created + // ourselves for any reason, for example. + // + // In this scenario, we know that: + // - We will receive exactly one call to ourself from BlockShutdown() and + // BlockShutdown() will be called (at most) once. + // - The only way our Shutdown() method gets called is via + // BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar() being + // invoked, which only happens if we get to that send below here that we + // can't get to. + // - All Shutdown() does is set mShuttingDown=true (essential for + // invariants) and invoke MaybeScheduleShutdownCompleted(). + // - Since there is no PBackground thread, mRunnableCounter must be 0 + // because only ScheduleSaveData() increments it and it only runs on the + // background thread, so it cannot have run. And so we would expect + // MaybeScheduleShutdownCompleted() to schedule an invocation of + // ShutdownCompleted on the main thread. + // + // So it's appropriate for us to set mShuttingDown=true (as Shutdown would + // do) and directly invoke ShutdownCompleted() (as Shutdown would indirectly + // do via MaybeScheduleShutdownCompleted). + mShuttingDown = true; + ShutdownCompleted(); return; } diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index a3cae4224eca..4b349fbfd0d7 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -4646,7 +4646,7 @@ WorkerPrivate::Constructor(const GlobalObject& aGlobal, { JSContext* cx = aGlobal.Context(); return Constructor(cx, aScriptURL, aIsChromeWorker, aWorkerType, - aWorkerName, NullCString(), aLoadInfo, aRv); + aWorkerName, VoidCString(), aLoadInfo, aRv); } // static diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp index 8175df40db8c..9a54e6e0987e 100644 --- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -1530,7 +1530,7 @@ void XMLHttpRequestMainThread::Open(const nsACString& aMethod, const nsAString& aUrl, ErrorResult& aRv) { - Open(aMethod, aUrl, true, NullString(), NullString(), aRv); + Open(aMethod, aUrl, true, VoidString(), VoidString(), aRv); } // This case is hit when the async parameter is specified, even if the diff --git a/dom/xhr/XMLHttpRequestWorker.cpp b/dom/xhr/XMLHttpRequestWorker.cpp index 057503917bd4..963335dd19ab 100644 --- a/dom/xhr/XMLHttpRequestWorker.cpp +++ b/dom/xhr/XMLHttpRequestWorker.cpp @@ -1429,8 +1429,8 @@ OpenRunnable::MainThreadRunInternal() ErrorResult rv2; mProxy->mXHR->Open(mMethod, mURL, true, - mUser.WasPassed() ? mUser.Value() : NullString(), - mPassword.WasPassed() ? mPassword.Value() : NullString(), + mUser.WasPassed() ? mUser.Value() : VoidString(), + mPassword.WasPassed() ? mPassword.Value() : VoidString(), rv2); MOZ_ASSERT(mProxy->mInOpen); @@ -2046,7 +2046,7 @@ XMLHttpRequestWorker::Send(JSContext* aCx, RefPtr sendRunnable; if (aData.IsNull()) { - sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, NullString()); + sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, VoidString()); // Nothing to clone. } diff --git a/dom/xslt/xslt/txMozillaXMLOutput.cpp b/dom/xslt/xslt/txMozillaXMLOutput.cpp index b3678e858016..f03a02ce3a7b 100644 --- a/dom/xslt/xslt/txMozillaXMLOutput.cpp +++ b/dom/xslt/xslt/txMozillaXMLOutput.cpp @@ -904,7 +904,7 @@ txMozillaXMLOutput::createResultDocument(const nsAString& aName, int32_t aNsID, doctypeName, mOutputFormat.mPublicId, mOutputFormat.mSystemId, - NullString()); + VoidString()); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr docType = do_QueryInterface(documentType); diff --git a/dom/xul/templates/nsXULTemplateBuilder.cpp b/dom/xul/templates/nsXULTemplateBuilder.cpp index bf72f07d5f86..a350ed4bd943 100644 --- a/dom/xul/templates/nsXULTemplateBuilder.cpp +++ b/dom/xul/templates/nsXULTemplateBuilder.cpp @@ -487,7 +487,7 @@ nsXULTemplateBuilder::HasGeneratedContent(nsIRDFResource* aResource, bool* aGenerated) { ErrorResult rv; - const nsAString& tag = aTag ? nsDependentAtomString(aTag) : NullString(); + const nsAString& tag = aTag ? nsDependentAtomString(aTag) : VoidString(); *aGenerated = HasGeneratedContent(aResource, tag, rv); return rv.StealNSResult(); } diff --git a/editor/libeditor/HTMLEditUtils.cpp b/editor/libeditor/HTMLEditUtils.cpp index 16e750d089e7..d45cadfe06ea 100644 --- a/editor/libeditor/HTMLEditUtils.cpp +++ b/editor/libeditor/HTMLEditUtils.cpp @@ -572,7 +572,7 @@ HTMLEditUtils::SupportsAlignAttr(nsINode& aNode) struct ElementInfo final { #ifdef DEBUG - eHTMLTags mTag; + nsHTMLTag mTag; #endif uint32_t mGroup; uint32_t mCanContainGroups; @@ -787,7 +787,7 @@ HTMLEditUtils::CanContain(int32_t aParent, int32_t aChild) // Special-case button. if (aParent == eHTMLTag_button) { - static const eHTMLTags kButtonExcludeKids[] = { + static const nsHTMLTag kButtonExcludeKids[] = { eHTMLTag_a, eHTMLTag_fieldset, eHTMLTag_form, diff --git a/gfx/angle/src/tests/preprocessor_tests/input_test.cpp b/gfx/angle/src/tests/preprocessor_tests/input_test.cpp index ca1878c5fafc..5e8d5931bdd6 100644 --- a/gfx/angle/src/tests/preprocessor_tests/input_test.cpp +++ b/gfx/angle/src/tests/preprocessor_tests/input_test.cpp @@ -21,7 +21,7 @@ TEST_F(InitTest, ZeroCount) EXPECT_EQ(pp::Token::LAST, token.type); } -TEST_F(InitTest, NullString) +TEST_F(InitTest, VoidString) { EXPECT_FALSE(mPreprocessor.init(1, NULL, NULL)); } diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp index f033b2ae7161..95a6d09597f0 100644 --- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -135,6 +135,16 @@ ImageContainer::GetImageClient() return imageClient.forget(); } +void +ImageContainer::DropImageClient() +{ + RecursiveMutexAutoLock mon(mRecursiveMutex); + if (mImageClient) { + mImageClient->ClearCachedResources(); + mImageClient = nullptr; + } +} + void ImageContainer::EnsureImageClient() { diff --git a/gfx/layers/ImageContainer.h b/gfx/layers/ImageContainer.h index 3b5c91d128f0..b95229ec116b 100644 --- a/gfx/layers/ImageContainer.h +++ b/gfx/layers/ImageContainer.h @@ -618,6 +618,8 @@ public: */ static ProducerID AllocateProducerID(); + void DropImageClient(); + private: typedef mozilla::RecursiveMutex RecursiveMutex; diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp index 00d238bd4e41..b92ab75b0066 100644 --- a/gfx/layers/ipc/ImageBridgeChild.cpp +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -726,7 +726,24 @@ MessageLoop * ImageBridgeChild::GetMessageLoop() const ImageBridgeChild::IdentifyCompositorTextureHost(const TextureFactoryIdentifier& aIdentifier) { if (RefPtr child = GetSingleton()) { - child->IdentifyTextureHost(aIdentifier); + child->UpdateTextureFactoryIdentifier(aIdentifier); + } +} + +void +ImageBridgeChild::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aIdentifier) +{ + bool disablingWebRender = GetCompositorBackendType() == LayersBackend::LAYERS_WR && + aIdentifier.mParentBackend != LayersBackend::LAYERS_WR; + IdentifyTextureHost(aIdentifier); + if (disablingWebRender) { + // ImageHost is incompatible between WebRender enabled and WebRender disabled. + // Then drop all ImageContainers' ImageClients during disabling WebRender. + MutexAutoLock lock(mContainerMapLock); + for (auto iter = mImageContainers.Iter(); !iter.Done(); iter.Next()) { + ImageContainer* container = iter.Data(); + container->DropImageClient(); + } } } diff --git a/gfx/layers/ipc/ImageBridgeChild.h b/gfx/layers/ipc/ImageBridgeChild.h index a81605df5fad..5b99769d99d0 100644 --- a/gfx/layers/ipc/ImageBridgeChild.h +++ b/gfx/layers/ipc/ImageBridgeChild.h @@ -250,6 +250,8 @@ private: void ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams); void ProxyDeallocShmemNow(SynchronousTask* aTask, Shmem* aShmem, bool* aResult); + void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aIdentifier); + public: // CompositableForwarder diff --git a/image/SurfaceCache.cpp b/image/SurfaceCache.cpp index 543dd3a4142c..bdb3f5a8da3d 100644 --- a/image/SurfaceCache.cpp +++ b/image/SurfaceCache.cpp @@ -279,18 +279,7 @@ public: RefPtr surface; mSurfaces.Remove(aSurface->GetSurfaceKey(), getter_AddRefs(surface)); - - if (IsEmpty() && mFactor2Mode) { - // The last surface for this cache was removed. This can happen if the - // surface was stored in a volatile buffer and got purged, or the surface - // expired from the cache. If the cache itself lingers for some reason - // (e.g. in the process of performing a lookup, the cache itself is - // locked), then we need to reset the factor of 2 state because it - // requires at least one surface present to get the native size - // information from the image. - mFactor2Mode = mFactor2Pruned = false; - } - + AfterMaybeRemove(); return surface.forget(); } @@ -537,6 +526,11 @@ public: if (!hasNotFactorSize) { mFactor2Pruned = true; } + + // We should never leave factor of 2 mode due to pruning in of itself, but + // if we discarded surfaces due to the volatile buffers getting released, + // it is possible. + AfterMaybeRemove(); } IntSize SuggestedSize(const IntSize& aSize) const @@ -548,7 +542,10 @@ public: // We cannot enter factor of 2 mode unless we have a minimum number of // surfaces, and we should have left it if the cache was emptied. - MOZ_ASSERT(!IsEmpty()); + if (MOZ_UNLIKELY(IsEmpty())) { + MOZ_ASSERT_UNREACHABLE("Should not be empty and in factor of 2 mode!"); + return aSize; + } // This bit of awkwardness gets the largest native size of the image. auto iter = ConstIter(); @@ -644,6 +641,8 @@ public: } report.Add(surface, factor2Size); } + + AfterMaybeRemove(); } SurfaceTable::Iterator ConstIter() const @@ -655,6 +654,20 @@ public: bool IsLocked() const { return mLocked; } private: + void AfterMaybeRemove() + { + if (IsEmpty() && mFactor2Mode) { + // The last surface for this cache was removed. This can happen if the + // surface was stored in a volatile buffer and got purged, or the surface + // expired from the cache. If the cache itself lingers for some reason + // (e.g. in the process of performing a lookup, the cache itself is + // locked), then we need to reset the factor of 2 state because it + // requires at least one surface present to get the native size + // information from the image. + mFactor2Mode = mFactor2Pruned = false; + } + } + SurfaceTable mSurfaces; bool mLocked; @@ -816,13 +829,7 @@ public: // Individual surfaces must be freed outside the lock. mCachedSurfacesDiscard.AppendElement(cache->Remove(aSurface)); - // Remove the per-image cache if it's unneeded now. Keep it if the image is - // locked, since the per-image cache is where we store that state. Note that - // we don't push it into mImageCachesDiscard because all of its surfaces - // have been removed, so it is safe to free while holding the lock. - if (cache->IsEmpty() && !cache->IsLocked()) { - mImageCaches.Remove(imageKey); - } + MaybeRemoveEmptyCache(imageKey, cache); } bool StartTracking(NotNull aSurface, @@ -1093,6 +1100,8 @@ public: // Individual surfaces must be freed outside the lock. mCachedSurfacesDiscard.AppendElement(aSurface); }); + + MaybeRemoveEmptyCache(aImageKey, cache); } void DiscardAll(const StaticMutexAutoLock& aAutoLock) @@ -1199,6 +1208,8 @@ public: // Individual surfaces must be freed outside the lock. mCachedSurfacesDiscard.AppendElement(aSurface); }); + + MaybeRemoveEmptyCache(aImageKey, cache); } private: @@ -1209,6 +1220,18 @@ private: return imageCache.forget(); } + void MaybeRemoveEmptyCache(const ImageKey aImageKey, + ImageSurfaceCache* aCache) + { + // Remove the per-image cache if it's unneeded now. Keep it if the image is + // locked, since the per-image cache is where we store that state. Note that + // we don't push it into mImageCachesDiscard because all of its surfaces + // have been removed, so it is safe to free while holding the lock. + if (aCache->IsEmpty() && !aCache->IsLocked()) { + mImageCaches.Remove(aImageKey); + } + } + // This is similar to CanHold() except that it takes into account the costs of // locked surfaces. It's used internally in Insert(), but it's not exposed // publicly because we permit multithreaded access to the surface cache, which diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index eec71a620cd2..7443da313089 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -17,6 +17,7 @@ #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" #include "nsAppRunner.h" +#include "mozilla/UniquePtr.h" #include "nsAutoPtr.h" #include "nsDebug.h" #include "nsISupportsImpl.h" @@ -416,7 +417,7 @@ public: MOZ_RELEASE_ASSERT(aMessage.transaction_id() == mTransaction); MOZ_RELEASE_ASSERT(!mReply); IPC_LOG("Reply received on worker thread: seqno=%d", mSeqno); - mReply = new IPC::Message(Move(aMessage)); + mReply = MakeUnique(Move(aMessage)); MOZ_RELEASE_ASSERT(IsComplete()); } @@ -455,7 +456,7 @@ public: return mReply->is_reply_error(); } - nsAutoPtr GetReply() { + UniquePtr GetReply() { return Move(mReply); } @@ -480,7 +481,7 @@ private: AutoEnterTransaction *mNext; // Pointer the a reply received for this message, if one was received. - nsAutoPtr mReply; + UniquePtr mReply; }; class PromiseReporter final : public nsIMemoryReporter @@ -844,7 +845,7 @@ MessageChannel::CommonThreadOpenInit(MessageChannel *aTargetChan, Side aSide) bool MessageChannel::Echo(Message* aMsg) { - nsAutoPtr msg(aMsg); + UniquePtr msg(aMsg); AssertWorkerThread(); mMonitor->AssertNotCurrentThreadOwns(); if (MSG_ROUTING_NONE == msg->routing_id()) { @@ -855,11 +856,11 @@ MessageChannel::Echo(Message* aMsg) MonitorAutoLock lock(*mMonitor); if (!Connected()) { - ReportConnectionError("MessageChannel", msg); + ReportConnectionError("MessageChannel", msg.get()); return false; } - mLink->EchoMessage(msg.forget()); + mLink->EchoMessage(msg.release()); return true; } @@ -887,7 +888,7 @@ MessageChannel::Send(Message* aMsg) CxxStackFrame frame(*this, OUT_MESSAGE, aMsg); - nsAutoPtr msg(aMsg); + UniquePtr msg(aMsg); AssertWorkerThread(); mMonitor->AssertNotCurrentThreadOwns(); if (MSG_ROUTING_NONE == msg->routing_id()) { @@ -897,11 +898,10 @@ MessageChannel::Send(Message* aMsg) MonitorAutoLock lock(*mMonitor); if (!Connected()) { - ReportConnectionError("MessageChannel", msg); + ReportConnectionError("MessageChannel", msg.get()); return false; } - - SendMessageToLink(msg.forget()); + SendMessageToLink(msg.release()); return true; } @@ -1353,7 +1353,7 @@ MessageChannel::Send(Message* aMsg, Message* aReply) Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE2, aMsg->size()); } - nsAutoPtr msg(aMsg); + UniquePtr msg(aMsg); // Sanity checks. AssertWorkerThread(); @@ -1367,7 +1367,7 @@ MessageChannel::Send(Message* aMsg, Message* aReply) AutoScopedLabel autolabel("sync message %s", aMsg->name()); #endif - CxxStackFrame f(*this, OUT_MESSAGE, msg); + CxxStackFrame f(*this, OUT_MESSAGE, msg.get()); MonitorAutoLock lock(*mMonitor); @@ -1427,7 +1427,7 @@ MessageChannel::Send(Message* aMsg, Message* aReply) "not allowed to send messages while dispatching urgent messages"); if (!Connected()) { - ReportConnectionError("MessageChannel::SendAndWait", msg); + ReportConnectionError("MessageChannel::SendAndWait", msg.get()); mLastSendError = SyncSendError::NotConnectedBeforeSend; return false; } @@ -1456,7 +1456,7 @@ MessageChannel::Send(Message* aMsg, Message* aReply) // msg will be destroyed soon, but name() is not owned by msg. const char* msgName = msg->name(); - SendMessageToLink(msg.forget()); + SendMessageToLink(msg.release()); while (true) { MOZ_RELEASE_ASSERT(!transact.IsCanceled()); @@ -1534,7 +1534,7 @@ MessageChannel::Send(Message* aMsg, Message* aReply) IPC_LOG("Got reply: seqno=%d, xid=%d, msgName=%s, latency=%ums", seqno, transaction, msgName, latencyMs); - nsAutoPtr reply = transact.GetReply(); + UniquePtr reply = transact.GetReply(); MOZ_RELEASE_ASSERT(reply); MOZ_RELEASE_ASSERT(reply->is_reply(), "expected reply"); @@ -1560,7 +1560,7 @@ MessageChannel::Send(Message* aMsg, Message* aReply) bool MessageChannel::Call(Message* aMsg, Message* aReply) { - nsAutoPtr msg(aMsg); + UniquePtr msg(aMsg); AssertWorkerThread(); mMonitor->AssertNotCurrentThreadOwns(); @@ -1573,11 +1573,11 @@ MessageChannel::Call(Message* aMsg, Message* aReply) // This must come before MonitorAutoLock, as its destructor acquires the // monitor lock. - CxxStackFrame cxxframe(*this, OUT_MESSAGE, msg); + CxxStackFrame cxxframe(*this, OUT_MESSAGE, msg.get()); MonitorAutoLock lock(*mMonitor); if (!Connected()) { - ReportConnectionError("MessageChannel::Call", msg); + ReportConnectionError("MessageChannel::Call", msg.get()); return false; } @@ -1593,7 +1593,7 @@ MessageChannel::Call(Message* aMsg, Message* aReply) msg->set_interrupt_remote_stack_depth_guess(mRemoteStackDepthGuess); msg->set_interrupt_local_stack_depth(1 + InterruptStackDepth()); mInterruptStack.push(MessageInfo(*msg)); - mLink->SendMessage(msg.forget()); + mLink->SendMessage(msg.release()); while (true) { // if a handler invoked by *Dispatch*() spun a nested event diff --git a/ipc/mscom/ActivationContext.cpp b/ipc/mscom/ActivationContext.cpp index 188ddd1137cb..a87ce0985b2f 100644 --- a/ipc/mscom/ActivationContext.cpp +++ b/ipc/mscom/ActivationContext.cpp @@ -93,6 +93,8 @@ ActivationContext::~ActivationContext() Release(); } +#if defined(MOZILLA_INTERNAL_API) + /* static */ Result ActivationContext::GetCurrent() { @@ -104,6 +106,68 @@ ActivationContext::GetCurrent() return reinterpret_cast(actCtx); } +/* static */ HRESULT +ActivationContext::GetCurrentManifestPath(nsAString& aOutManifestPath) +{ + aOutManifestPath.Truncate(); + + SIZE_T bytesNeeded; + BOOL ok = ::QueryActCtxW(QUERY_ACTCTX_FLAG_USE_ACTIVE_ACTCTX, nullptr, + nullptr, ActivationContextDetailedInformation, + nullptr, 0, &bytesNeeded); + if (!ok) { + DWORD err = ::GetLastError(); + if (err != ERROR_INSUFFICIENT_BUFFER) { + return HRESULT_FROM_WIN32(err); + } + } + + auto ctxBuf = MakeUnique(bytesNeeded); + + ok = ::QueryActCtxW(QUERY_ACTCTX_FLAG_USE_ACTIVE_ACTCTX, nullptr, nullptr, + ActivationContextDetailedInformation, ctxBuf.get(), + bytesNeeded, nullptr); + if (!ok) { + return HRESULT_FROM_WIN32(::GetLastError()); + } + + auto ctxInfo = + reinterpret_cast(ctxBuf.get()); + + // assemblyIndex is 1-based, and we want the last index, so we can just copy + // ctxInfo->ulAssemblyCount directly. + DWORD assemblyIndex = ctxInfo->ulAssemblyCount; + ok = ::QueryActCtxW(QUERY_ACTCTX_FLAG_USE_ACTIVE_ACTCTX, nullptr, + &assemblyIndex, + AssemblyDetailedInformationInActivationContext, nullptr, + 0, &bytesNeeded); + if (!ok) { + DWORD err = ::GetLastError(); + if (err != ERROR_INSUFFICIENT_BUFFER) { + return HRESULT_FROM_WIN32(err); + } + } + + auto assemblyBuf = MakeUnique(bytesNeeded); + + ok = ::QueryActCtxW(QUERY_ACTCTX_FLAG_USE_ACTIVE_ACTCTX, nullptr, + &assemblyIndex, + AssemblyDetailedInformationInActivationContext, + assemblyBuf.get(), bytesNeeded, &bytesNeeded); + if (!ok) { + return HRESULT_FROM_WIN32(::GetLastError()); + } + + auto assemblyInfo = + reinterpret_cast(assemblyBuf.get()); + aOutManifestPath = nsDependentString(assemblyInfo->lpAssemblyManifestPath, + (assemblyInfo->ulManifestPathLength + 1) / sizeof(wchar_t)); + + return S_OK; +} + +#endif // defined(MOZILLA_INTERNAL_API) + ActivationContextRegion::ActivationContextRegion(const ActivationContext& aActCtx) : mActCtx(aActCtx) , mActCookie(0) diff --git a/ipc/mscom/ActivationContext.h b/ipc/mscom/ActivationContext.h index 00c0f3bd354f..e214f8dfb73e 100644 --- a/ipc/mscom/ActivationContext.h +++ b/ipc/mscom/ActivationContext.h @@ -11,6 +11,10 @@ #include "mozilla/Move.h" #include "mozilla/Result.h" +#if defined(MOZILLA_INTERNAL_API) +#include "nsString.h" +#endif // defined(MOZILLA_INTERNAL_API) + #include namespace mozilla { @@ -35,7 +39,10 @@ public: return mActCtx != INVALID_HANDLE_VALUE; } +#if defined(MOZILLA_INTERNAL_API) static Result GetCurrent(); + static HRESULT GetCurrentManifestPath(nsAString& aOutManifestPath); +#endif // defined(MOZILLA_INTERNAL_API) private: void Init(ACTCTX& aActCtx); diff --git a/ipc/mscom/ProxyStream.cpp b/ipc/mscom/ProxyStream.cpp index 9dabd54d2e2e..11360294a747 100644 --- a/ipc/mscom/ProxyStream.cpp +++ b/ipc/mscom/ProxyStream.cpp @@ -83,6 +83,7 @@ ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf, #if defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER) const uint32_t expectedStreamLen = GetOBJREFSize(WrapNotNull(mStream)); nsAutoCString strActCtx; + nsAutoString manifestPath; #endif // defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER) HRESULT unmarshalResult = S_OK; @@ -92,7 +93,7 @@ ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf, // actual interface later. #if defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER) - auto marshalFn = [this, &strActCtx, &unmarshalResult, &aIID]() -> void + auto marshalFn = [this, &strActCtx, &manifestPath, &unmarshalResult, &aIID]() -> void #else auto marshalFn = [this, &unmarshalResult, &aIID]() -> void #endif // defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER) @@ -104,6 +105,8 @@ ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf, } else { strActCtx.AppendPrintf("HRESULT 0x%08X", curActCtx.unwrapErr()); } + + ActivationContext::GetCurrentManifestPath(manifestPath); #endif // defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER) unmarshalResult = @@ -137,6 +140,8 @@ ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf, AnnotateClassRegistration(CLSID_AccessibleHandler); CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("UnmarshalActCtx"), strActCtx); + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("UnmarshalActCtxManifestPath"), + NS_ConvertUTF16toUTF8(manifestPath)); CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("A11yHandlerRegistered"), a11y::IsHandlerRegistered() ? NS_LITERAL_CSTRING("true") : @@ -285,13 +290,28 @@ ProxyStream::ProxyStream(REFIID aIID, IUnknown* aObject, HRESULT statResult = S_OK; HRESULT getHGlobalResult = S_OK; - auto marshalFn = [&]() -> void +#if defined(MOZ_CRASHREPORTER) + nsAutoString manifestPath; + + auto marshalFn = [this, &aIID, aObject, mshlFlags, &stream, &streamSize, + &hglobal, &createStreamResult, &marshalResult, &statResult, + &getHGlobalResult, &manifestPath]() -> void +#else + auto marshalFn = [this, &aIID, aObject, mshlFlags, &stream, &streamSize, + &hglobal, &createStreamResult, &marshalResult, &statResult, + &getHGlobalResult]() -> void +#endif // defined(MOZ_CRASHREPORTER) { - createStreamResult = ::CreateStreamOnHGlobal(nullptr, TRUE, getter_AddRefs(stream)); + createStreamResult = ::CreateStreamOnHGlobal(nullptr, TRUE, + getter_AddRefs(stream)); if (FAILED(createStreamResult)) { return; } +#if defined(MOZ_CRASHREPORTER) + ActivationContext::GetCurrentManifestPath(manifestPath); +#endif // defined(MOZ_CRASHREPORTER) + marshalResult = ::CoMarshalInterface(stream, aIID, aObject, MSHCTX_LOCAL, nullptr, mshlFlags); MOZ_DIAGNOSTIC_ASSERT(marshalResult != E_INVALIDARG); @@ -333,6 +353,8 @@ ProxyStream::ProxyStream(REFIID aIID, IUnknown* aObject, nsPrintfCString hrAsStr("0x%08X", marshalResult); CrashReporter::AnnotateCrashReport( NS_LITERAL_CSTRING("CoMarshalInterfaceFailure"), hrAsStr); + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("MarshalActCtxManifestPath"), + NS_ConvertUTF16toUTF8(manifestPath)); } if (FAILED(statResult)) { diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 3e25fd7b7120..baec7b63b8f0 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -669,12 +669,16 @@ BaselineScript::maybeICEntryFromPCOffset(uint32_t pcOffset) if (!ComputeBinarySearchMid(this, pcOffset, &mid)) return nullptr; + MOZ_ASSERT(mid < numICEntries()); + // Found an IC entry with a matching PC offset. Search backward, and then // forward from this IC entry, looking for one with the same PC offset which // has isForOp() set. - for (size_t i = mid; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i--) { + for (size_t i = mid; icEntry(i).pcOffset() == pcOffset; i--) { if (icEntry(i).isForOp()) return &icEntry(i); + if (i == 0) + break; } for (size_t i = mid+1; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i++) { if (icEntry(i).isForOp()) @@ -728,10 +732,13 @@ BaselineScript::callVMEntryFromPCOffset(uint32_t pcOffset) // inserted by VM calls. size_t mid; MOZ_ALWAYS_TRUE(ComputeBinarySearchMid(this, pcOffset, &mid)); + MOZ_ASSERT(mid < numICEntries()); - for (size_t i = mid; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i--) { + for (size_t i = mid; icEntry(i).pcOffset() == pcOffset; i--) { if (icEntry(i).kind() == ICEntry::Kind_CallVM) return icEntry(i); + if (i == 0) + break; } for (size_t i = mid+1; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i++) { if (icEntry(i).kind() == ICEntry::Kind_CallVM) diff --git a/js/src/jit/IonControlFlow.cpp b/js/src/jit/IonControlFlow.cpp index 4f27cd53dcaa..8d8a796d3f3b 100644 --- a/js/src/jit/IonControlFlow.cpp +++ b/js/src/jit/IonControlFlow.cpp @@ -1619,7 +1619,7 @@ ControlFlowGenerator::processBreak(JSOp op, jssrcnote* sn) DebugOnly found = false; if (SN_TYPE(sn) == SRC_BREAK2LABEL) { - for (size_t i = labels_.length() - 1; i < labels_.length(); i--) { + for (size_t i = labels_.length() - 1; ; i--) { CFGState& cfg = cfgStack_[labels_[i].cfgEntry]; MOZ_ASSERT(cfg.state == CFGState::LABEL); if (cfg.stopAt == target) { @@ -1627,9 +1627,11 @@ ControlFlowGenerator::processBreak(JSOp op, jssrcnote* sn) found = true; break; } + if (i == 0) + break; } } else { - for (size_t i = loops_.length() - 1; i < loops_.length(); i--) { + for (size_t i = loops_.length() - 1; ; i--) { CFGState& cfg = cfgStack_[loops_[i].cfgEntry]; MOZ_ASSERT(cfg.isLoop()); if (cfg.loop.exitpc == target) { @@ -1637,6 +1639,8 @@ ControlFlowGenerator::processBreak(JSOp op, jssrcnote* sn) found = true; break; } + if (i == 0) + break; } } @@ -1665,7 +1669,7 @@ ControlFlowGenerator::processContinue(JSOp op) // Find the target loop. CFGState* found = nullptr; jsbytecode* target = pc + GetJumpOffset(pc); - for (size_t i = loops_.length() - 1; i < loops_.length(); i--) { + for (size_t i = loops_.length() - 1; ; i--) { // +1 to skip JSOP_JUMPTARGET. if (loops_[i].continuepc == target + 1 || EffectiveContinue(loops_[i].continuepc) == target) @@ -1673,6 +1677,8 @@ ControlFlowGenerator::processContinue(JSOp op) found = &cfgStack_[loops_[i].cfgEntry]; break; } + if (i == 0) + break; } // There must always be a valid target loop structure. If not, there's @@ -1698,11 +1704,13 @@ ControlFlowGenerator::processSwitchBreak(JSOp op) // Find the target switch. CFGState* found = nullptr; jsbytecode* target = pc + GetJumpOffset(pc); - for (size_t i = switches_.length() - 1; i < switches_.length(); i--) { + for (size_t i = switches_.length() - 1; ; i--) { if (switches_[i].continuepc == target) { found = &cfgStack_[switches_[i].cfgEntry]; break; } + if (i == 0) + break; } // There must always be a valid target loop structure. If not, there's diff --git a/js/xpconnect/loader/URLPreloader.h b/js/xpconnect/loader/URLPreloader.h index db5a1d7ed379..8825a02db980 100644 --- a/js/xpconnect/loader/URLPreloader.h +++ b/js/xpconnect/loader/URLPreloader.h @@ -224,7 +224,7 @@ private: { MOZ_IMPLICIT URLEntry(const CacheKey& key) : CacheKey(key) - , mData(NullCString()) + , mData(VoidCString()) {} explicit URLEntry(nsIFile* file) diff --git a/js/xpconnect/loader/mozJSSubScriptLoader.cpp b/js/xpconnect/loader/mozJSSubScriptLoader.cpp index bc98e48f5640..4dc62f8540d9 100644 --- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp +++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp @@ -46,7 +46,7 @@ public: JSObject* options = nullptr) : OptionsBase(cx, options) , target(cx) - , charset(NullString()) + , charset(VoidString()) , ignoreCache(false) , async(false) , wantReturnValue(false) diff --git a/js/xpconnect/src/XPCJSMemoryReporter.h b/js/xpconnect/src/XPCJSMemoryReporter.h index 7881ac0f587b..28a66f4fd590 100644 --- a/js/xpconnect/src/XPCJSMemoryReporter.h +++ b/js/xpconnect/src/XPCJSMemoryReporter.h @@ -8,7 +8,7 @@ #define XPCJSMemoryReporter_h class nsISupports; -class nsIMemoryReporterCallback; +class nsIHandleReportCallback; namespace xpc { @@ -23,7 +23,7 @@ class JSReporter public: static void CollectReports(WindowPaths* windowPaths, WindowPaths* topWindowPaths, - nsIMemoryReporterCallback* handleReport, + nsIHandleReportCallback* handleReport, nsISupports* data, bool anonymize); }; diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index cf8c03fb015e..05ce7d870f1a 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -30,7 +30,7 @@ class nsGlobalWindow; class nsIPrincipal; class nsScriptNameSpaceManager; -class nsIMemoryReporterCallback; +class nsIHandleReportCallback; namespace mozilla { namespace dom { @@ -431,7 +431,7 @@ private: void ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats& rtStats, const nsACString& rtPath, - nsIMemoryReporterCallback* handleReport, + nsIHandleReportCallback* handleReport, nsISupports* data, bool anonymize, size_t* rtTotal = nullptr); diff --git a/js/xpconnect/tests/unit/test_nuke_sandbox_event_listeners.js b/js/xpconnect/tests/unit/test_nuke_sandbox_event_listeners.js index ba055c3b3360..dfd837210a5b 100644 --- a/js/xpconnect/tests/unit/test_nuke_sandbox_event_listeners.js +++ b/js/xpconnect/tests/unit/test_nuke_sandbox_event_listeners.js @@ -29,6 +29,8 @@ add_task(async function() { let sandbox = Cu.Sandbox(window, {sandboxPrototype: window}); function sandboxContent() { + window.onload = function SandboxOnLoad() {}; + window.addEventListener("FromTest", () => { window.dispatchEvent(new CustomEvent("FromSandbox")); }, true); @@ -40,6 +42,11 @@ add_task(async function() { let fromTestPromise = promiseEvent(window, "FromTest"); let fromSandboxPromise = promiseEvent(window, "FromSandbox"); + equal(typeof window.onload, "function", + "window.onload should contain sandbox event listener"); + equal(window.onload.name, "SandboxOnLoad", + "window.onload have the correct function name"); + do_print("Dispatch FromTest event"); window.dispatchEvent(new window.CustomEvent("FromTest")); @@ -70,6 +77,8 @@ add_task(async function() { Cu.forceGC(); Cu.forceCC(); + ok(Cu.isDeadWrapper(window.onload), + "window.onload should contain a dead wrapper after sandbox is nuked"); do_print("Dispatch FromTest event"); fromTestPromise = promiseEvent(window, "FromTest"); diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini index 3ec1b228c36b..dafe0434981c 100644 --- a/layout/style/test/mochitest.ini +++ b/layout/style/test/mochitest.ini @@ -323,6 +323,7 @@ skip-if = toolkit == 'android' # bug 775227 for android [test_value_computation.html] skip-if = toolkit == 'android' [test_value_storage.html] +skip-if = toolkit == 'android' && debug # bug 1397615 [test_variable_serialization_computed.html] [test_variable_serialization_specified.html] [test_variables.html] diff --git a/mfbt/CheckedInt.h b/mfbt/CheckedInt.h index 3eef24365b11..77fc1bb232cc 100644 --- a/mfbt/CheckedInt.h +++ b/mfbt/CheckedInt.h @@ -14,6 +14,23 @@ #include "mozilla/Attributes.h" #include "mozilla/IntegerTypeTraits.h" +// Probe for builtin math overflow support. Disabled for 32-bit builds for now +// since "gcc -m32" claims to support these but its implementation is buggy. +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82274 +#if defined(HAVE_64BIT_BUILD) +#if defined(__has_builtin) +#define MOZ_HAS_BUILTIN_OP_OVERFLOW (__has_builtin(__builtin_add_overflow)) +#elif defined(__GNUC__) +// (clang also defines __GNUC__ but it supports __has_builtin since at least +// v3.1 (released in 2012) so it won't get here.) +#define MOZ_HAS_BUILTIN_OP_OVERFLOW (__GNUC__ >= 5) +#else +#define MOZ_HAS_BUILTIN_OP_OVERFLOW (0) +#endif +#else +#define MOZ_HAS_BUILTIN_OP_OVERFLOW (0) +#endif + namespace mozilla { template class CheckedInt; @@ -255,6 +272,10 @@ template inline bool IsAddValid(T aX, T aY) { +#if MOZ_HAS_BUILTIN_OP_OVERFLOW + T dummy; + return !__builtin_add_overflow(aX, aY, &dummy); +#else // Addition is valid if the sign of aX+aY is equal to either that of aX or // that of aY. Since the value of aX+aY is undefined if we have a signed // type, we compute it using the unsigned type of the same size. Beware! @@ -267,12 +288,17 @@ IsAddValid(T aX, T aY) return IsSigned::value ? HasSignBit(BinaryComplement(T((result ^ aX) & (result ^ aY)))) : BinaryComplement(aX) >= aY; +#endif } template inline bool IsSubValid(T aX, T aY) { +#if MOZ_HAS_BUILTIN_OP_OVERFLOW + T dummy; + return !__builtin_sub_overflow(aX, aY, &dummy); +#else // Subtraction is valid if either aX and aY have same sign, or aX-aY and aX // have same sign. Since the value of aX-aY is undefined if we have a signed // type, we compute it using the unsigned type of the same size. @@ -283,6 +309,7 @@ IsSubValid(T aX, T aY) return IsSigned::value ? HasSignBit(BinaryComplement(T((result ^ aX) & (aX ^ aY)))) : aX >= aY; +#endif } template inline bool IsMulValid(T aX, T aY) { +#if MOZ_HAS_BUILTIN_OP_OVERFLOW + T dummy; + return !__builtin_mul_overflow(aX, aY, &dummy); +#else return IsMulValidImpl::run(aX, aY); +#endif } template @@ -687,12 +719,30 @@ private: aLhs.mIsValid && aRhs.mIsValid); \ } +#if MOZ_HAS_BUILTIN_OP_OVERFLOW +#define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(NAME, OP, FUN) \ + template \ + inline CheckedInt \ + operator OP(const CheckedInt& aLhs, const CheckedInt& aRhs) \ + { \ + T result; \ + if (FUN(aLhs.mValue, aRhs.mValue, &result)) { \ + return CheckedInt(0, false); \ + } \ + return CheckedInt(result, aLhs.mIsValid && aRhs.mIsValid); \ + } +MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Add, +, __builtin_add_overflow) +MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Sub, -, __builtin_sub_overflow) +MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Mul, *, __builtin_mul_overflow) +#undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2 +#else MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add, +) MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub, -) MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mul, *) +#endif + MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Div, /) MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mod, %) - #undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR // Implement castToCheckedInt(x), making sure that diff --git a/mobile/android/app/src/main/res/values/arrays.xml b/mobile/android/app/src/main/res/values/arrays.xml index 555797ce7db2..f273e3814017 100644 --- a/mobile/android/app/src/main/res/values/arrays.xml +++ b/mobile/android/app/src/main/res/values/arrays.xml @@ -136,6 +136,13 @@ wifi disabled + + + 0 + 1 + 20 + 21 + @string/pref_tap_to_load_images_enabled diff --git a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java index be9fcdf9f62c..05839669fa1b 100644 --- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java @@ -49,7 +49,6 @@ import android.support.v4.view.MenuItemCompat; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; -import android.view.HapticFeedbackConstants; import android.view.InputDevice; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -699,7 +698,7 @@ public class BrowserApp extends GeckoApp final TabHistoryFragment fragment = TabHistoryFragment.newInstance(historyPageList, toIndex); final FragmentManager fragmentManager = getSupportFragmentManager(); - GeckoAppShell.getHapticFeedbackDelegate().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + GeckoAppShell.vibrateOnHapticFeedbackEnabled(getResources().getIntArray(R.array.long_press_vibrate_msec)); fragment.show(R.id.tab_history_panel, fragmentManager.beginTransaction(), TAB_HISTORY_FRAGMENT_TAG); } }); @@ -1659,6 +1658,7 @@ public class BrowserApp extends GeckoApp mLayerView.getDynamicToolbarAnimator().addMetricsListener(this); mLayerView.getDynamicToolbarAnimator().setToolbarChromeProxy(this); } + mDynamicToolbar.setLayerView(mLayerView); setDynamicToolbarEnabled(mDynamicToolbar.isEnabled()); // Intercept key events for gamepad shortcuts diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java index 62dc7a8c0909..44ce03190eb6 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java @@ -673,7 +673,7 @@ public abstract class GeckoApp extends GeckoActivity GeckoAccessibility.updateAccessibilitySettings(this); } else if ("Accessibility:Event".equals(event)) { - GeckoAccessibility.sendAccessibilityEvent(mLayerView, message); + GeckoAccessibility.sendAccessibilityEvent(message); } else if ("Bookmark:Insert".equals(event)) { final BrowserDB db = BrowserDB.from(getProfile()); diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java index 4698e14004e6..f53d035c4e28 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java @@ -57,8 +57,7 @@ import java.io.File; import java.lang.reflect.Method; import java.util.UUID; -public class GeckoApplication extends Application - implements HapticFeedbackDelegate { +public class GeckoApplication extends Application { private static final String LOG_TAG = "GeckoApplication"; private static final String MEDIA_DECODING_PROCESS_CRASH = "MEDIA_DECODING_PROCESS_CRASH"; @@ -230,7 +229,6 @@ public class GeckoApplication extends Application final Context context = getApplicationContext(); GeckoAppShell.setApplicationContext(context); - GeckoAppShell.setHapticFeedbackDelegate(this); GeckoAppShell.setGeckoInterface(new GeckoAppShell.GeckoInterface() { @Override public boolean openUriExternal(final String targetURI, final String mimeType, @@ -636,13 +634,4 @@ public class GeckoApplication extends Application return bitmap; } - - @Override // HapticFeedbackDelegate - public void performHapticFeedback(final int effect) { - final Activity currentActivity = - GeckoActivityMonitor.getInstance().getCurrentActivity(); - if (currentActivity != null) { - currentActivity.getWindow().getDecorView().performHapticFeedback(effect); - } - } } diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoService.java b/mobile/android/base/java/org/mozilla/gecko/GeckoService.java index 27084dc2f343..b467d240748d 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoService.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoService.java @@ -71,10 +71,22 @@ public class GeckoService extends Service { private static final EventListener EVENT_LISTENER = new EventListener(); + private static RuntimeException bug1401737Diag; + public static void register() { if (DEBUG) { Log.d(LOGTAG, "Registered listener"); } + + // Diagnose bug 1401737. register() is unexpectedly getting called a second time. + // We know the stack for the second call, but we want to collect stack for the first call. + if (bug1401737Diag == null) { + bug1401737Diag = new IllegalStateException("Bug 1401737 diagnostic crash"); + } else { + Log.e(LOGTAG, "register() is called a second time", new Exception()); + throw bug1401737Diag; + } + EventDispatcher.getInstance().registerGeckoThreadListener(EVENT_LISTENER, "Gecko:ScheduleRun"); } diff --git a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java index 02ce0bdf3e62..5335cfc28987 100644 --- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java +++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java @@ -218,6 +218,10 @@ public class CustomTabsActivity extends AppCompatActivity @Override public void finish() { + if (mGeckoView != null) { + mGeckoView.loadUri("about:blank"); + } + super.finish(); final SafeIntent intent = new SafeIntent(getIntent()); diff --git a/mobile/android/base/java/org/mozilla/gecko/menu/GeckoMenu.java b/mobile/android/base/java/org/mozilla/gecko/menu/GeckoMenu.java index fd9d936ecddb..5d7c4c7b1cef 100644 --- a/mobile/android/base/java/org/mozilla/gecko/menu/GeckoMenu.java +++ b/mobile/android/base/java/org/mozilla/gecko/menu/GeckoMenu.java @@ -17,7 +17,6 @@ import android.content.Intent; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; -import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; @@ -260,8 +259,7 @@ public class GeckoMenu extends ListView @Override public boolean onLongClick(View view) { if (handleMenuItemLongClick(menuItem)) { - GeckoAppShell.getHapticFeedbackDelegate().performHapticFeedback( - HapticFeedbackConstants.LONG_PRESS); + GeckoAppShell.vibrateOnHapticFeedbackEnabled(getResources().getIntArray(R.array.long_press_vibrate_msec)); return true; } return false; @@ -278,8 +276,7 @@ public class GeckoMenu extends ListView @Override public boolean onLongClick(View view) { if (handleMenuItemLongClick(menuItem)) { - GeckoAppShell.getHapticFeedbackDelegate().performHapticFeedback( - HapticFeedbackConstants.LONG_PRESS); + GeckoAppShell.vibrateOnHapticFeedbackEnabled(getResources().getIntArray(R.array.long_press_vibrate_msec)); return true; } return false; diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index cfb18d7cf558..e7fab3e465c3 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -415,7 +415,6 @@ gvjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x 'gfx/SurfaceTextureListener.java', 'gfx/ViewTransform.java', 'gfx/VsyncSource.java', - 'HapticFeedbackDelegate.java', 'InputConnectionListener.java', 'InputMethods.java', 'media/AsyncCodec.java', diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java index b23fa5d47439..9a6875ac588a 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java @@ -7,6 +7,8 @@ package org.mozilla.gecko; import org.json.JSONException; import org.json.JSONObject; +import org.mozilla.gecko.EventDispatcher; +import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.ThreadUtils; @@ -108,8 +110,7 @@ public class GeckoAccessibility { return sEnabled; } - public static void sendAccessibilityEvent(final GeckoView view, - final GeckoBundle message) { + public static void sendAccessibilityEvent(final GeckoBundle message) { if (!sEnabled) return; @@ -119,11 +120,10 @@ public class GeckoAccessibility { return; } - sendAccessibilityEvent(view, message, eventType); + sendAccessibilityEvent(message, eventType); } - public static void sendAccessibilityEvent(final GeckoView view, final GeckoBundle message, - final int eventType) { + public static void sendAccessibilityEvent(final GeckoBundle message, final int eventType) { if (!sEnabled) return; @@ -148,6 +148,10 @@ public class GeckoAccessibility { } else { // In Jelly Bean we populate an AccessibilityNodeInfo with the minimal amount of data to have // it work with TalkBack. + final LayerView view = GeckoAppShell.getLayerView(); + if (view == null) + return; + if (sVirtualCursorNode == null) { sVirtualCursorNode = AccessibilityNodeInfo.obtain(view, VIRTUAL_CURSOR_POSITION); } @@ -262,12 +266,7 @@ public class GeckoAccessibility { AccessibilityNodeProvider mAccessibilityNodeProvider; @Override - public AccessibilityNodeProvider getAccessibilityNodeProvider(final View hostView) { - if (!(hostView instanceof GeckoView)) { - return super.getAccessibilityNodeProvider(hostView); - } - final GeckoView host = (GeckoView) hostView; - + public AccessibilityNodeProvider getAccessibilityNodeProvider(final View host) { if (mAccessibilityNodeProvider == null) // The accessibility node structure for web content consists of 3 LayerView child nodes: // 1. VIRTUAL_ENTRY_POINT_BEFORE: Represents the entry point before the LayerView. @@ -320,7 +319,7 @@ public class GeckoAccessibility { // The accessibility focus is permanently on the middle node, VIRTUAL_CURSOR_POSITION. // When we enter the view forward or backward we just ask Gecko to get focus, keeping the current position. if (virtualViewId == VIRTUAL_CURSOR_POSITION && sHoverEnter != null) { - GeckoAccessibility.sendAccessibilityEvent(host, sHoverEnter, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); + GeckoAccessibility.sendAccessibilityEvent(sHoverEnter, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); } else { final GeckoBundle data = new GeckoBundle(1); data.putBoolean("gainFocus", true); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java index 46e4a73a901f..1264a4a8afb5 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java @@ -28,6 +28,7 @@ import org.mozilla.gecko.annotation.JNITarget; import org.mozilla.gecko.annotation.RobocopTarget; import org.mozilla.gecko.annotation.WrapForJNI; import org.mozilla.gecko.gfx.BitmapUtils; +import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.permissions.Permissions; import org.mozilla.gecko.process.GeckoProcessManager; import org.mozilla.gecko.util.HardwareCodecCapabilityUtils; @@ -221,8 +222,21 @@ public class GeckoAppShell @WrapForJNI(dispatchTo = "gecko") public static native void notifyUriVisited(String uri); + private static LayerView sLayerView; private static Rect sScreenSize; + public static void setLayerView(LayerView lv) { + if (sLayerView == lv) { + return; + } + sLayerView = lv; + } + + @RobocopTarget + public static LayerView getLayerView() { + return sLayerView; + } + @WrapForJNI(stubName = "NotifyObservers", dispatchTo = "gecko") private static native void nativeNotifyObservers(String topic, String data); @@ -367,8 +381,7 @@ public class GeckoAppShell LocationListener, NotificationListener, ScreenOrientationDelegate, - WakeLockDelegate, - HapticFeedbackDelegate { + WakeLockDelegate { @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @@ -548,22 +561,6 @@ public class GeckoAppShell mWakeLocks.remove(lock); } } - - @Override - public void performHapticFeedback(final int effect) { - final int[] pattern; - // Use default platform values. - if (effect == HapticFeedbackConstants.KEYBOARD_TAP) { - pattern = new int[] { 40 }; - } else if (effect == HapticFeedbackConstants.LONG_PRESS) { - pattern = new int[] { 0, 1, 20, 21 }; - } else if (effect == HapticFeedbackConstants.VIRTUAL_KEY) { - pattern = new int[] { 0, 10, 20, 30 }; - } else { - return; - } - vibrateOnHapticFeedbackEnabled(pattern); - } } private static final DefaultListeners DEFAULT_LISTENERS = new DefaultListeners(); @@ -571,7 +568,6 @@ public class GeckoAppShell private static LocationListener sLocationListener = DEFAULT_LISTENERS; private static NotificationListener sNotificationListener = DEFAULT_LISTENERS; private static WakeLockDelegate sWakeLockDelegate = DEFAULT_LISTENERS; - private static HapticFeedbackDelegate sHapticFeedbackDelegate = DEFAULT_LISTENERS; /** * A delegate for supporting the Screen Orientation API. @@ -618,14 +614,6 @@ public class GeckoAppShell sWakeLockDelegate = (delegate != null) ? delegate : DEFAULT_LISTENERS; } - public static HapticFeedbackDelegate getHapticFeedbackDelegate() { - return sHapticFeedbackDelegate; - } - - public static void setHapticFeedbackDelegate(final HapticFeedbackDelegate delegate) { - sHapticFeedbackDelegate = (delegate != null) ? delegate : DEFAULT_LISTENERS; - } - @WrapForJNI(calledFrom = "gecko") private static void enableSensor(int aSensortype) { final SensorManager sm = (SensorManager) @@ -1014,11 +1002,10 @@ public class GeckoAppShell // Don't perform haptic feedback if a vibration is currently playing, // because the haptic feedback will nuke the vibration. if (!sVibrationMaybePlaying || System.nanoTime() >= sVibrationEndTime) { - getHapticFeedbackDelegate().performHapticFeedback( - aIsLongPress ? HapticFeedbackConstants.LONG_PRESS - : HapticFeedbackConstants.VIRTUAL_KEY); - sVibrationMaybePlaying = false; - sVibrationEndTime = 0; + LayerView layerView = getLayerView(); + layerView.performHapticFeedback(aIsLongPress ? + HapticFeedbackConstants.LONG_PRESS : + HapticFeedbackConstants.VIRTUAL_KEY); } } @@ -1036,14 +1023,10 @@ public class GeckoAppShell } // Vibrate only if haptic feedback is enabled. - private static void vibrateOnHapticFeedbackEnabled(int[] milliseconds) { + public static void vibrateOnHapticFeedbackEnabled(int[] milliseconds) { if (Settings.System.getInt(getApplicationContext().getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) > 0) { - if (milliseconds.length == 1) { - vibrate(milliseconds[0]); - } else { - vibrate(convertIntToLongArray(milliseconds), -1); - } + vibrate(convertIntToLongArray(milliseconds), -1); } } @@ -1056,10 +1039,10 @@ public class GeckoAppShell @WrapForJNI(calledFrom = "gecko") private static void vibrate(long[] pattern, int repeat) { - // If pattern.length is odd, the last element in the pattern is a + // If pattern.length is even, the last element in the pattern is a // meaningless delay, so don't include it in vibrationDuration. long vibrationDuration = 0; - int iterLen = pattern.length & ~1; + int iterLen = pattern.length - (pattern.length % 2 == 0 ? 1 : 0); for (int i = 0; i < iterLen; i++) { vibrationDuration += pattern[i]; } @@ -1570,6 +1553,21 @@ public class GeckoAppShell return HardwareUtils.isTablet(); } + private static boolean sImeWasEnabledOnLastResize = false; + public static void viewSizeChanged() { + GeckoView v = (GeckoView) getLayerView(); + if (v == null) { + return; + } + boolean imeIsEnabled = v.isIMEEnabled(); + if (imeIsEnabled && !sImeWasEnabledOnLastResize) { + // The IME just came up after not being up, so let's scroll + // to the focused input. + EventDispatcher.getInstance().dispatch("ScrollTo:FocusedInput", null); + } + sImeWasEnabledOnLastResize = imeIsEnabled; + } + @WrapForJNI(calledFrom = "gecko") private static double[] getCurrentNetworkInformation() { return GeckoNetworkManager.getInstance().getCurrentInformation(); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java index a7800885e7d1..6e0feb7cffa4 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java @@ -72,7 +72,7 @@ class GeckoInputConnection private String mCurrentInputMethod = ""; - private final GeckoView mView; + private final View mView; private final GeckoEditableClient mEditableClient; protected int mBatchEditCount; private ExtractedTextRequest mUpdateRequest; @@ -83,7 +83,7 @@ class GeckoInputConnection // Prevent showSoftInput and hideSoftInput from causing reentrant calls on some devices. private volatile boolean mSoftInputReentrancyGuard; - public static GeckoEditableListener create(GeckoView targetView, + public static GeckoEditableListener create(View targetView, GeckoEditableClient editable) { if (DEBUG) return DebugGeckoInputConnection.create(targetView, editable); @@ -91,7 +91,7 @@ class GeckoInputConnection return new GeckoInputConnection(targetView, editable); } - protected GeckoInputConnection(GeckoView targetView, + protected GeckoInputConnection(View targetView, GeckoEditableClient editable) { super(targetView, true); mView = targetView; @@ -204,7 +204,7 @@ class GeckoInputConnection return extract; } - private GeckoView getView() { + private View getView() { return mView; } @@ -236,10 +236,7 @@ class GeckoInputConnection v.clearFocus(); v.requestFocus(); } - final GeckoView view = getView(); - if (view != null) { - view.getDynamicToolbarAnimator().showToolbar(/*immediately*/ true); - } + GeckoAppShell.getLayerView().getDynamicToolbarAnimator().showToolbar(/*immediately*/true); mSoftInputReentrancyGuard = true; imm.showSoftInput(v, 0); mSoftInputReentrancyGuard = false; @@ -362,18 +359,18 @@ class GeckoInputConnection mCursorAnchorInfoBuilder.reset(); // Calculate Gecko logical coords to screen coords - final GeckoView view = getView(); - if (view == null) { + final View v = getView(); + if (v == null) { return; } int[] viewCoords = new int[2]; - view.getLocationOnScreen(viewCoords); + v.getLocationOnScreen(viewCoords); - DynamicToolbarAnimator animator = view.getDynamicToolbarAnimator(); - float toolbarHeight = (float) animator.getCurrentToolbarHeight(); + DynamicToolbarAnimator animator = GeckoAppShell.getLayerView().getDynamicToolbarAnimator(); + float toolbarHeight = (float)animator.getCurrentToolbarHeight(); - Matrix matrix = view.getMatrixForLayerRectToViewRect(); + Matrix matrix = GeckoAppShell.getLayerView().getMatrixForLayerRectToViewRect(); if (matrix == null) { if (DEBUG) { Log.d(LOGTAG, "Cannot get Matrix to convert from Gecko coords to layer view coords"); @@ -1037,13 +1034,13 @@ final class DebugGeckoInputConnection private InputConnection mProxy; private final StringBuilder mCallLevel; - private DebugGeckoInputConnection(GeckoView targetView, + private DebugGeckoInputConnection(View targetView, GeckoEditableClient editable) { super(targetView, editable); mCallLevel = new StringBuilder(); } - public static GeckoEditableListener create(GeckoView targetView, + public static GeckoEditableListener create(View targetView, GeckoEditableClient editable) { final Class[] PROXY_INTERFACES = { InputConnection.class, InputConnectionListener.class, diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java index 9ab8dba9df26..0bbb9c07aac2 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java @@ -488,6 +488,9 @@ public class GeckoView extends LayerView { private void init(final Context context, final GeckoViewSettings settings) { preload(context); + // Perform common initialization for Fennec/GeckoView. + GeckoAppShell.setLayerView(this); + initializeView(); mListener.registerListeners(); @@ -748,8 +751,7 @@ public class GeckoView extends LayerView { mInputConnectionListener.onKeyMultiple(keyCode, repeatCount, event); } - @Override - public boolean isIMEEnabled() { + /* package */ boolean isIMEEnabled() { return mInputConnectionListener != null && mInputConnectionListener.isIMEEnabled(); } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/HapticFeedbackDelegate.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/HapticFeedbackDelegate.java deleted file mode 100644 index b2d1e56afa99..000000000000 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/HapticFeedbackDelegate.java +++ /dev/null @@ -1,20 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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/. */ - -package org.mozilla.gecko; - -import android.view.HapticFeedbackConstants; - -/** - * A HapticFeedbackDelegate is responsible for performing haptic feedback. - */ -public interface HapticFeedbackDelegate { - /** - * Perform a haptic feedback effect. Called from the Gecko thread. - * - * @param effect Effect to perform from android.view.HapticFeedbackConstants. - */ - void performHapticFeedback(int effect); -} diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java index b3ed19271b7e..ff7077f9f00e 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java @@ -33,7 +33,6 @@ class GeckoLayerClient implements LayerView.Listener private IntSize mWindowSize; private boolean mForceRedraw; - private boolean mImeWasEnabledOnLastResize; /* The current viewport metrics. * This is volatile so that we can read and write to it from different threads. @@ -154,13 +153,7 @@ class GeckoLayerClient implements LayerView.Listener // the following call also sends gecko a message, which will be processed after the resize // message above has updated the viewport. this message ensures that if we have just put // focus in a text field, we scroll the content so that the text field is in view. - final boolean imeIsEnabled = mView.isIMEEnabled(); - if (imeIsEnabled && !mImeWasEnabledOnLastResize) { - // The IME just came up after not being up, so let's scroll - // to the focused input. - EventDispatcher.getInstance().dispatch("ScrollTo:FocusedInput", null); - } - mImeWasEnabledOnLastResize = imeIsEnabled; + GeckoAppShell.viewSizeChanged(); } return true; } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java index c28f2e6c2ddf..286d6cc9d515 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java @@ -824,9 +824,5 @@ public class LayerView extends FrameLayout { if (isCompositorReady()) { mCompositor.setDefaultClearColor(mDefaultClearColor); } - } - - public boolean isIMEEnabled() { - return false; - } + } } diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/FennecNativeActions.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/FennecNativeActions.java index 86405acc9855..73193b195370 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/FennecNativeActions.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/FennecNativeActions.java @@ -356,7 +356,7 @@ public class FennecNativeActions implements Actions { PaintExpecter() { final PaintExpecter expecter = this; - mLayerView = (LayerView) mSolo.getView(R.id.layer_view); + mLayerView = GeckoAppShell.getLayerView(); mDrawListener = new DrawListener() { @Override public void drawFinished() { diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java index 1a5c8c8589d0..b921bda24295 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java @@ -356,8 +356,7 @@ abstract class BaseTest extends BaseRobocopTest { // If we don't see the item, scroll down once in case it's off-screen. // Hacky way to scroll down. solo.scroll* does not work in dialogs. - MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mSolo, - mDriver.getGeckoLeft(), mDriver.getGeckoTop()); + MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); meh.dragSync(mScreenMidWidth, mScreenMidHeight+100, mScreenMidWidth, mScreenMidHeight-100); foundText = mSolo.waitForText(txt); diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/MotionEventHelper.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/MotionEventHelper.java index 639b79fe97b6..5b8254e99d8b 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/MotionEventHelper.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/MotionEventHelper.java @@ -5,11 +5,8 @@ package org.mozilla.gecko.tests; import org.mozilla.gecko.GeckoAppShell; -import org.mozilla.gecko.PrefsHelper; -import org.mozilla.gecko.R; import org.mozilla.gecko.gfx.LayerView; - -import com.robotium.solo.Solo; +import org.mozilla.gecko.PrefsHelper; import android.app.Instrumentation; import android.os.SystemClock; @@ -29,12 +26,11 @@ class MotionEventHelper { private float mTouchStartTolerance; private final int mDpi; - public MotionEventHelper(Instrumentation inst, Solo solo, - int surfaceOffsetX, int surfaceOffsetY) { + public MotionEventHelper(Instrumentation inst, int surfaceOffsetX, int surfaceOffsetY) { mInstrumentation = inst; mSurfaceOffsetX = surfaceOffsetX; mSurfaceOffsetY = surfaceOffsetY; - layerView = (LayerView) solo.getView(R.id.layer_view); + layerView = GeckoAppShell.getLayerView(); mApzEnabled = false; mTouchStartTolerance = 0.0f; mDpi = GeckoAppShell.getDpi(); diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAxisLocking.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAxisLocking.java index 0a786e44d6d7..72bf62e0426a 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAxisLocking.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAxisLocking.java @@ -19,8 +19,7 @@ public class testAxisLocking extends PixelTest { public void testAxisLocking() { String url = getAbsoluteUrl(mStringHelper.ROBOCOP_BOXES_URL); - MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mSolo, - mDriver.getGeckoLeft(), mDriver.getGeckoTop()); + MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); blockForGeckoReady(); diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testFlingCorrectness.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testFlingCorrectness.java index 12f6260207fc..e173a8c16506 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testFlingCorrectness.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testFlingCorrectness.java @@ -17,8 +17,7 @@ public class testFlingCorrectness extends PixelTest { public void testFlingCorrectness() { String url = getAbsoluteUrl(mStringHelper.ROBOCOP_BOXES_URL); - MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mSolo, - mDriver.getGeckoLeft(), mDriver.getGeckoTop()); + MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); blockForGeckoReady(); diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPanCorrectness.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPanCorrectness.java index 13c995013487..e7d402607dc4 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPanCorrectness.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPanCorrectness.java @@ -17,8 +17,7 @@ public class testPanCorrectness extends PixelTest { public void testPanCorrectness() { String url = getAbsoluteUrl(mStringHelper.ROBOCOP_BOXES_URL); - MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mSolo, - mDriver.getGeckoLeft(), mDriver.getGeckoTop()); + MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); blockForGeckoReady(); diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testVkbOverlap.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testVkbOverlap.java index 60a4fd9fa70a..f5a54a0e96d3 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testVkbOverlap.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testVkbOverlap.java @@ -29,8 +29,7 @@ public class testVkbOverlap extends PixelTest { // scroll to the bottom of the page and let it settle Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint(); - MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mSolo, - mDriver.getGeckoLeft(), mDriver.getGeckoTop()); + MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); meh.dragSync(10, 150, 10, 50); // the input field has a green background, so let's count the number of green pixels diff --git a/parser/html/nsHtml5TreeOperation.cpp b/parser/html/nsHtml5TreeOperation.cpp index 0e64fb53b26b..31ebd3e1e317 100644 --- a/parser/html/nsHtml5TreeOperation.cpp +++ b/parser/html/nsHtml5TreeOperation.cpp @@ -684,7 +684,7 @@ nsHtml5TreeOperation::AppendDoctypeToDocument(nsIAtom* aName, aName, aPublicId, aSystemId, - NullString()); + VoidString()); NS_ASSERTION(docType, "Doctype creation failed."); nsCOMPtr asContent = do_QueryInterface(docType); return AppendToDocument(asContent, aBuilder); diff --git a/parser/htmlparser/nsElementTable.cpp b/parser/htmlparser/nsElementTable.cpp index 52507a9765ba..84e3af6ba72b 100644 --- a/parser/htmlparser/nsElementTable.cpp +++ b/parser/htmlparser/nsElementTable.cpp @@ -1,18 +1,66 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 sw=2 et tw=78: */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsIAtom.h" #include "nsElementTable.h" -/***************************************************************************** - Now it's time to list all the html elements all with their capabilities... -******************************************************************************/ +static const int kNone= 0x0; -// The Element Table (sung to the tune of Modern Major General) +static const int kHTMLContent = 0x0001; // HEAD, (FRAMESET | BODY) +static const int kHeadContent = 0x0002; // Elements that *must* be in the head. +static const int kHeadMisc = 0x0004; // Elements that *can* be in the head. + +static const int kSpecial = 0x0008; // A, IMG, APPLET, OBJECT, FONT, BASEFONT, BR, SCRIPT, + // MAP, Q, SUB, SUP, SPAN, BDO, IFRAME + +static const int kFormControl = 0x0010; // INPUT SELECT TEXTAREA LABEL BUTTON +static const int kPreformatted = 0x0020; // PRE +static const int kPreExclusion = 0x0040; // IMG, OBJECT, APPLET, BIG, SMALL, SUB, SUP, FONT, BASEFONT +static const int kFontStyle = 0x0080; // TT, I, B, U, S, STRIKE, BIG, SMALL +static const int kPhrase = 0x0100; // EM, STRONG, DFN, CODE, SAMP, KBD, VAR, CITE, ABBR, ACRONYM +static const int kHeading = 0x0200; // H1..H6 +static const int kBlockMisc = 0x0400; // OBJECT, SCRIPT +static const int kBlock = 0x0800; // ADDRESS, BLOCKQUOTE, CENTER, DIV, DL, FIELDSET, FORM, + // ISINDEX, HR, NOSCRIPT, NOFRAMES, P, TABLE +static const int kList = 0x1000; // UL, OL, DIR, MENU +static const int kPCDATA = 0x2000; // plain text and entities... +static const int kSelf = 0x4000; // whatever THIS tag is... +static const int kExtensions = 0x8000; // BGSOUND, WBR, NOBR +static const int kTable = 0x10000;// TR,TD,THEAD,TBODY,TFOOT,CAPTION,TH +static const int kDLChild = 0x20000;// DL, DT +static const int kCDATA = 0x40000;// just plain text... + +static const int kInlineEntity = (kPCDATA|kFontStyle|kPhrase|kSpecial|kFormControl|kExtensions); // #PCDATA, %fontstyle, %phrase, %special, %formctrl +static const int kBlockEntity = (kHeading|kList|kPreformatted|kBlock); // %heading, %list, %preformatted, %block +static const int kFlowEntity = (kBlockEntity|kInlineEntity); // %blockentity, %inlineentity +static const int kAllTags = 0xffffff; + +// Is aTest a member of aBitset? +static bool +TestBits(int32_t aBitset, int32_t aTest) +{ + if (aTest) { + int32_t result = aBitset & aTest; + return result == aTest; + } + return false; +} + +struct HTMLElement +{ + bool IsMemberOf(int32_t aBitset) const + { + return TestBits(aBitset, mParentBits); + } + +#ifdef DEBUG + nsHTMLTag mTagID; +#endif + int mParentBits; // defines groups that can contain this element + bool mLeaf; +}; #ifdef DEBUG #define ELEM(tag, parent, leaf) { eHTMLTag_##tag, parent, leaf }, @@ -20,7 +68,7 @@ #define ELEM(tag, parent, leaf) { parent, leaf }, #endif -const nsHTMLElement gHTMLElements[] = { +static const HTMLElement gHTMLElements[] = { ELEM(unknown, kNone, true) ELEM(a, kSpecial, false) ELEM(abbr, kPhrase, false) @@ -172,19 +220,14 @@ const nsHTMLElement gHTMLElements[] = { #undef ELEM -/*********************************************************************************************/ - -bool nsHTMLElement::IsMemberOf(int32_t aSet) const -{ - return TestBits(aSet, mParentBits); -} - -bool nsHTMLElement::IsContainer(eHTMLTags aId) +bool +nsHTMLElement::IsContainer(nsHTMLTag aId) { return !gHTMLElements[aId].mLeaf; } -bool nsHTMLElement::IsBlock(eHTMLTags aId) +bool +nsHTMLElement::IsBlock(nsHTMLTag aId) { return gHTMLElements[aId].IsMemberOf(kBlock) || gHTMLElements[aId].IsMemberOf(kBlockEntity) || @@ -194,10 +237,14 @@ bool nsHTMLElement::IsBlock(eHTMLTags aId) } #ifdef DEBUG -void CheckElementTable() +void +CheckElementTable() { - for (eHTMLTags t = eHTMLTag_unknown; t <= eHTMLTag_userdefined; t = eHTMLTags(t + 1)) { - NS_ASSERTION(gHTMLElements[t].mTagID == t, "gHTMLElements entries does match tag list."); + for (nsHTMLTag t = eHTMLTag_unknown; + t <= eHTMLTag_userdefined; + t = nsHTMLTag(t + 1)) { + MOZ_ASSERT(gHTMLElements[t].mTagID == t, + "gHTMLElements entries does match tag list."); } } #endif diff --git a/parser/htmlparser/nsElementTable.h b/parser/htmlparser/nsElementTable.h index e3479db8d192..f9f63e3db46d 100644 --- a/parser/htmlparser/nsElementTable.h +++ b/parser/htmlparser/nsElementTable.h @@ -1,95 +1,22 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- 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/. */ - -/** - * MODULE NOTES: - * @update gess 4/1/98 - * - */ - - - -#ifndef _NSELEMENTABLE -#define _NSELEMENTABLE +#ifndef nsElementTable_h +#define nsElementTable_h #include "nsHTMLTags.h" -#include "nsIDTD.h" - -//********************************************************************************************* -// The following ints define the standard groups of HTML elements... -//********************************************************************************************* - -static const int kNone= 0x0; - -static const int kHTMLContent = 0x0001; // HEAD, (FRAMESET | BODY) -static const int kHeadContent = 0x0002; // Elements that *must* be in the head. -static const int kHeadMisc = 0x0004; // Elements that *can* be in the head. - -static const int kSpecial = 0x0008; // A, IMG, APPLET, OBJECT, FONT, BASEFONT, BR, SCRIPT, - // MAP, Q, SUB, SUP, SPAN, BDO, IFRAME - -static const int kFormControl = 0x0010; // INPUT SELECT TEXTAREA LABEL BUTTON -static const int kPreformatted = 0x0020; // PRE -static const int kPreExclusion = 0x0040; // IMG, OBJECT, APPLET, BIG, SMALL, SUB, SUP, FONT, BASEFONT -static const int kFontStyle = 0x0080; // TT, I, B, U, S, STRIKE, BIG, SMALL -static const int kPhrase = 0x0100; // EM, STRONG, DFN, CODE, SAMP, KBD, VAR, CITE, ABBR, ACRONYM -static const int kHeading = 0x0200; // H1..H6 -static const int kBlockMisc = 0x0400; // OBJECT, SCRIPT -static const int kBlock = 0x0800; // ADDRESS, BLOCKQUOTE, CENTER, DIV, DL, FIELDSET, FORM, - // ISINDEX, HR, NOSCRIPT, NOFRAMES, P, TABLE -static const int kList = 0x1000; // UL, OL, DIR, MENU -static const int kPCDATA = 0x2000; // plain text and entities... -static const int kSelf = 0x4000; // whatever THIS tag is... -static const int kExtensions = 0x8000; // BGSOUND, WBR, NOBR -static const int kTable = 0x10000;// TR,TD,THEAD,TBODY,TFOOT,CAPTION,TH -static const int kDLChild = 0x20000;// DL, DT -static const int kCDATA = 0x40000;// just plain text... - -static const int kInlineEntity = (kPCDATA|kFontStyle|kPhrase|kSpecial|kFormControl|kExtensions); // #PCDATA, %fontstyle, %phrase, %special, %formctrl -static const int kBlockEntity = (kHeading|kList|kPreformatted|kBlock); // %heading, %list, %preformatted, %block -static const int kFlowEntity = (kBlockEntity|kInlineEntity); // %blockentity, %inlineentity -static const int kAllTags = 0xffffff; - - -//********************************************************************************************* -// The following ints define the standard groups of HTML elements... -//********************************************************************************************* #ifdef DEBUG -extern void CheckElementTable(); +void CheckElementTable(); #endif - -/** - * We're asking the question: is aTest a member of bitset. - * - * @param - * @return TRUE or FALSE - */ -inline bool TestBits(int aBitset,int aTest) { - if(aTest) { - int32_t result=(aBitset & aTest); - return bool(result==aTest); - } - return false; -} - -struct nsHTMLElement { - bool IsMemberOf(int32_t aType) const; - -#ifdef DEBUG - eHTMLTags mTagID; -#endif - int mParentBits; //defines groups that can contain this element - bool mLeaf; - - static bool IsContainer(eHTMLTags aTag); - static bool IsBlock(eHTMLTags aTag); +struct nsHTMLElement +{ + static bool IsContainer(nsHTMLTag aTag); + static bool IsBlock(nsHTMLTag aTag); }; -extern const nsHTMLElement gHTMLElements[]; - -#endif +#endif // nsElementTable_h diff --git a/parser/htmlparser/nsHTMLTags.h b/parser/htmlparser/nsHTMLTags.h index 67375df9de48..5838e244d1f0 100644 --- a/parser/htmlparser/nsHTMLTags.h +++ b/parser/htmlparser/nsHTMLTags.h @@ -84,6 +84,4 @@ private: static PLHashTable* gTagAtomTable; }; -#define eHTMLTags nsHTMLTag - #endif /* nsHTMLTags_h___ */ diff --git a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul index 9d56890b3965..e9401bd118af 100644 --- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul +++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul @@ -340,7 +340,7 @@ } } }, - // nsIMemoryReporterCallback + // nsIHandleReportCallback callback: function(process, path, kind, units, amount, data) { if (path in this.tests) { this.seen++; diff --git a/toolkit/components/backgroundhangmonitor/HangDetails.h b/toolkit/components/backgroundhangmonitor/HangDetails.h index 29a4f29c27c1..ff1e1af02d33 100644 --- a/toolkit/components/backgroundhangmonitor/HangDetails.h +++ b/toolkit/components/backgroundhangmonitor/HangDetails.h @@ -32,7 +32,7 @@ public: HangDetails() : mDuration(0) , mProcess(GeckoProcessType_Invalid) - , mRemoteType(NullString()) + , mRemoteType(VoidString()) {} HangDetails(const HangDetails& aOther) = default; @@ -45,7 +45,7 @@ public: HangMonitor::HangAnnotations&& aAnnotations) : mDuration(aDuration) , mProcess(aProcess) - , mRemoteType(NullString()) + , mRemoteType(VoidString()) , mThreadName(aThreadName) , mRunnableName(aRunnableName) , mStack(Move(aStack)) diff --git a/toolkit/components/extensions/ExtensionContent.jsm b/toolkit/components/extensions/ExtensionContent.jsm index 3429ae14daff..9af6870a829b 100644 --- a/toolkit/components/extensions/ExtensionContent.jsm +++ b/toolkit/components/extensions/ExtensionContent.jsm @@ -137,8 +137,16 @@ class CacheMap extends DefaultMap { class ScriptCache extends CacheMap { constructor(options) { - super(SCRIPT_EXPIRY_TIMEOUT_MS, - url => ChromeUtils.compileScript(url, options)); + super(SCRIPT_EXPIRY_TIMEOUT_MS); + this.options = options; + } + + defaultConstructor(url) { + let promise = ChromeUtils.compileScript(url, this.options); + promise.then(script => { + promise.script = script; + }); + return promise; } } @@ -317,17 +325,25 @@ class Script { } } - let scriptsPromise = Promise.all(this.compileScripts()); + let scriptPromises = this.compileScripts(); - // If we're supposed to inject at the start of the document load, - // and we haven't already missed that point, block further parsing - // until the scripts have been loaded. - let {document} = context.contentWindow; - if (this.runAt === "document_start" && document.readyState !== "complete") { - document.blockParsing(scriptsPromise); + let scripts = scriptPromises.map(promise => promise.script); + // If not all scripts are already available in the cache, block + // parsing and wait all promises to resolve. + if (!scripts.every(script => script)) { + let promise = Promise.all(scriptPromises); + + // If we're supposed to inject at the start of the document load, + // and we haven't already missed that point, block further parsing + // until the scripts have been loaded. + let {document} = context.contentWindow; + if (this.runAt === "document_start" && document.readyState !== "complete") { + document.blockParsing(promise, {blockScriptCreated: false}); + } + + scripts = await promise; } - let scripts = await scriptsPromise; let result; // The evaluations below may throw, in which case the promise will be diff --git a/toolkit/components/extensions/ExtensionUtils.jsm b/toolkit/components/extensions/ExtensionUtils.jsm index a3ed895de6df..4a5fcb19c58d 100644 --- a/toolkit/components/extensions/ExtensionUtils.jsm +++ b/toolkit/components/extensions/ExtensionUtils.jsm @@ -67,9 +67,11 @@ function instanceOf(value, type) { * constructor if one is not present. */ class DefaultWeakMap extends WeakMap { - constructor(defaultConstructor, init) { + constructor(defaultConstructor = undefined, init = undefined) { super(init); - this.defaultConstructor = defaultConstructor; + if (defaultConstructor) { + this.defaultConstructor = defaultConstructor; + } } get(key) { @@ -83,9 +85,11 @@ class DefaultWeakMap extends WeakMap { } class DefaultMap extends Map { - constructor(defaultConstructor, init) { + constructor(defaultConstructor = undefined, init = undefined) { super(init); - this.defaultConstructor = defaultConstructor; + if (defaultConstructor) { + this.defaultConstructor = defaultConstructor; + } } get(key) { diff --git a/toolkit/components/extensions/ExtensionXPCShellUtils.jsm b/toolkit/components/extensions/ExtensionXPCShellUtils.jsm index d16003d79944..3420ce175d8a 100644 --- a/toolkit/components/extensions/ExtensionXPCShellUtils.jsm +++ b/toolkit/components/extensions/ExtensionXPCShellUtils.jsm @@ -59,6 +59,11 @@ function frameScript() { Components.utils.import("resource://gre/modules/Services.jsm"); Services.obs.notifyObservers(this, "tab-content-frameloader-created"); + + // eslint-disable-next-line mozilla/balanced-listeners + addEventListener("MozHeapMinimize", () => { + Services.obs.notifyObservers(null, "memory-pressure", "heap-minimize"); + }, true, true); } const FRAME_SCRIPT = `data:text/javascript,(${encodeURI(frameScript)}).call(this)`; diff --git a/toolkit/components/extensions/test/mochitest/mochitest-common.ini b/toolkit/components/extensions/test/mochitest/mochitest-common.ini index 8cca0e639cb1..c1b59d94dc24 100644 --- a/toolkit/components/extensions/test/mochitest/mochitest-common.ini +++ b/toolkit/components/extensions/test/mochitest/mochitest-common.ini @@ -123,11 +123,13 @@ skip-if = os == 'android' [test_ext_unload_frame.html] [test_ext_listener_proxies.html] [test_ext_web_accessible_resources.html] +skip-if = os == 'android' && debug # bug 1397615 [test_ext_webrequest_auth.html] skip-if = os == 'android' [test_ext_webrequest_background_events.html] [test_ext_webrequest_hsts.html] [test_ext_webrequest_basic.html] +skip-if = os == 'android' && debug # bug 1397615 [test_ext_webrequest_filter.html] [test_ext_webrequest_frameId.html] [test_ext_webrequest_responseBody.html] @@ -138,7 +140,9 @@ skip-if = os == 'android' # Currently fails in emulator tests [test_ext_webrequest_permission.html] [test_ext_webrequest_websocket.html] [test_ext_webnavigation.html] +skip-if = os == 'android' && debug # bug 1397615 [test_ext_webnavigation_filters.html] +skip-if = os == 'android' && debug # bug 1397615 [test_ext_window_postMessage.html] [test_ext_subframes_privileges.html] [test_ext_xhr_capabilities.html] diff --git a/toolkit/components/extensions/test/xpcshell/data/file_document_write.html b/toolkit/components/extensions/test/xpcshell/data/file_document_write.html new file mode 100644 index 000000000000..fbae3d6d763a --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/data/file_document_write.html @@ -0,0 +1,35 @@ + + + + + + + + + + + diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_scriptCreated.js b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_scriptCreated.js new file mode 100644 index 000000000000..00407b914015 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_scriptCreated.js @@ -0,0 +1,49 @@ +"use strict"; + +const server = createHttpServer(); +server.registerDirectory("/data/", do_get_file("data")); + +const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`; + +// ExtensionContent.jsm needs to know when it's running from xpcshell, +// to use the right timeout for content scripts executed at document_idle. +ExtensionTestUtils.mockAppInfo(); + +// Test that document_start content scripts don't block script-created +// parsers. +add_task(async function test_contentscript_scriptCreated() { + let extensionData = { + manifest: { + content_scripts: [{ + "matches": ["http://*/*/file_document_write.html"], + "js": ["content_script.js"], + "run_at": "document_start", + "match_about_blank": true, + "all_frames": true, + }], + }, + + files: { + "content_script.js": function() { + if (window === top) { + addEventListener("message", msg => { + browser.test.assertEq("ok", msg.data, "document.write() succeeded"); + browser.test.sendMessage("content-script-done"); + }, {once: true}); + } + }, + }, + }; + + let extension = ExtensionTestUtils.loadExtension(extensionData); + + await extension.startup(); + + let contentPage = await ExtensionTestUtils.loadContentPage(`${BASE_URL}/file_document_write.html`); + + await extension.awaitMessage("content-script-done"); + + await contentPage.close(); + + await extension.unload(); +}); diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini b/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini index e34ec0717691..65814ff5da97 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-content.ini @@ -2,4 +2,5 @@ skip-if = os == "android" || (os == "win" && debug) [test_ext_i18n_css.js] [test_ext_contentscript.js] +[test_ext_contentscript_scriptCreated.js] [test_ext_contentscript_xrays.js] diff --git a/toolkit/components/passwordmgr/test/mochitest/mochitest.ini b/toolkit/components/passwordmgr/test/mochitest/mochitest.ini index 18f44b6f5f0f..e22cfd43a1f1 100644 --- a/toolkit/components/passwordmgr/test/mochitest/mochitest.ini +++ b/toolkit/components/passwordmgr/test/mochitest/mochitest.ini @@ -47,8 +47,11 @@ skip-if = toolkit == 'android' # autocomplete [test_form_action_javascript.html] [test_formless_autofill.html] [test_formless_submit.html] +skip-if = toolkit == 'android' && debug # bug 1397615 [test_formless_submit_navigation.html] +skip-if = toolkit == 'android' && debug # bug 1397615 [test_formless_submit_navigation_negative.html] +skip-if = toolkit == 'android' && debug # bug 1397615 [test_input_events.html] [test_input_events_for_identical_values.html] [test_maxlength.html] diff --git a/toolkit/components/places/tests/head_common.js b/toolkit/components/places/tests/head_common.js index 70288c6514be..610cb597ce52 100644 --- a/toolkit/components/places/tests/head_common.js +++ b/toolkit/components/places/tests/head_common.js @@ -75,7 +75,7 @@ XPCOMUtils.defineLazyGetter(this, "SMALLSVG_DATA_URI", function() { var gTestDir = do_get_cwd(); // Initialize profile. -var gProfD = do_get_profile(); +var gProfD = do_get_profile(true); Services.prefs.setBoolPref("browser.urlbar.usepreloadedtopurls.enabled", false); do_register_cleanup(() => diff --git a/tools/profiler/core/platform.cpp b/tools/profiler/core/platform.cpp index b3f8a2aa3d61..2ff23bf2e721 100644 --- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -3189,66 +3189,6 @@ ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) delete aBacktrace; } -// Fill the output buffer with the following pattern: -// "Label 1" "\0" "Label 2" "\0" ... "Label N" "\0" "\0" -// TODO: use the unwinder instead of pseudo stack. -void -profiler_get_backtrace_noalloc(char *output, size_t outputSize) -{ - MOZ_RELEASE_ASSERT(CorePS::Exists()); - - MOZ_ASSERT(outputSize >= 2); - char *bound = output + outputSize - 2; - output[0] = output[1] = '\0'; - - PSAutoLock lock(gPSMutex); - - if (!ActivePS::Exists(lock)) { - return; - } - - PseudoStack* pseudoStack = TLSInfo::Stack(); - if (!pseudoStack) { - return; - } - - bool includeDynamicString = !ActivePS::FeaturePrivacy(lock); - - js::ProfileEntry* pseudoEntries = pseudoStack->entries; - uint32_t pseudoCount = pseudoStack->stackSize(); - - for (uint32_t i = 0; i < pseudoCount; i++) { - const char* label = pseudoEntries[i].label(); - const char* dynamicString = - includeDynamicString ? pseudoEntries[i].dynamicString() : nullptr; - size_t labelLength = strlen(label); - if (dynamicString) { - // Put the label, maybe a space, and the dynamic string into output. - size_t spaceLength = label[0] == '\0' ? 0 : 1; - size_t dynamicStringLength = strlen(dynamicString); - if (output + labelLength + spaceLength + dynamicStringLength >= bound) { - break; - } - strcpy(output, label); - output += labelLength; - if (spaceLength != 0) { - *output++ = ' '; - } - strcpy(output, dynamicString); - output += dynamicStringLength; - } else { - // Only put the label into output. - if (output + labelLength >= bound) { - break; - } - strcpy(output, label); - output += labelLength; - } - *output++ = '\0'; - *output = '\0'; - } -} - static void racy_profiler_add_marker(const char* aMarkerName, UniquePtr aPayload) diff --git a/tools/profiler/public/GeckoProfiler.h b/tools/profiler/public/GeckoProfiler.h index c343ca450367..df6a9050641b 100644 --- a/tools/profiler/public/GeckoProfiler.h +++ b/tools/profiler/public/GeckoProfiler.h @@ -319,9 +319,6 @@ using UniqueProfilerBacktrace = // if the profiler is inactive or in privacy mode. PROFILER_FUNC(UniqueProfilerBacktrace profiler_get_backtrace(), nullptr) -PROFILER_FUNC_VOID(profiler_get_backtrace_noalloc(char* aOutput, - size_t aOutputSize)) - // Get information about the current buffer status. A no-op when the profiler // is inactive. Do not call this function; call profiler_get_buffer_info() // instead. diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index 14a9d4367aac..446604f201e8 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -1862,7 +1862,14 @@ nsCocoaWindow::SetTitle(const nsAString& aTitle) const unichar* uniTitle = reinterpret_cast(strTitle.get()); NSString* title = [NSString stringWithCharacters:uniTitle length:strTitle.Length()]; - [mWindow setTitle:title]; + if ([mWindow drawsContentsIntoWindowFrame] && ![mWindow wantsTitleDrawn]) { + // Don't cause invalidations when the title isn't displayed. + [mWindow disableSetNeedsDisplay]; + [mWindow setTitle:title]; + [mWindow enableSetNeedsDisplay]; + } else { + [mWindow setTitle:title]; + } return NS_OK; diff --git a/widget/nsShmImage.cpp b/widget/nsShmImage.cpp index 25d0a8c4b5eb..906409f1b387 100644 --- a/widget/nsShmImage.cpp +++ b/widget/nsShmImage.cpp @@ -14,6 +14,7 @@ #include "nsPrintfCString.h" #include "nsTArray.h" +#include #include #include #include @@ -119,6 +120,17 @@ nsShmImage::InitExtension() gShmInitialized = true; + // Bugs 1397918, 1293474 - race condition in libxcb fixed upstream as of + // version 1.11. Since we can't query libxcb's version directly, the only + // other option is to check for symbols that were added after 1.11. + // xcb_discard_reply64 was added in 1.11.1, so check for existence of + // that to verify we are using a version of libxcb with the bug fixed. + // Otherwise, we can't risk using libxcb due to aforementioned crashes. + if (!dlsym(RTLD_DEFAULT, "xcb_discard_reply64")) { + gShmAvailable = false; + return false; + } + const xcb_query_extension_reply_t* extReply; extReply = xcb_get_extension_data(mConnection, &xcb_shm_id); if (!extReply || !extReply->present) { diff --git a/xpcom/base/nsIMemoryReporter.idl b/xpcom/base/nsIMemoryReporter.idl index 30db029520b7..306504a897db 100644 --- a/xpcom/base/nsIMemoryReporter.idl +++ b/xpcom/base/nsIMemoryReporter.idl @@ -22,7 +22,7 @@ interface nsISimpleEnumerator; */ [scriptable, function, uuid(62ef0e1c-dbd6-11e3-aa75-3c970e9f4238)] -interface nsIMemoryReporterCallback : nsISupports +interface nsIHandleReportCallback : nsISupports { /* * The arguments to the callback are as follows. @@ -179,19 +179,19 @@ interface nsIMemoryReporter : nsISupports * - Chrome domains and URLs. * - Information about installed extensions. */ - void collectReports(in nsIMemoryReporterCallback callback, + void collectReports(in nsIHandleReportCallback callback, in nsISupports data, in boolean anonymize); /* - * Kinds. See the |kind| comment in nsIMemoryReporterCallback. + * Kinds. See the |kind| comment in nsIHandleReportCallback. */ const int32_t KIND_NONHEAP = 0; const int32_t KIND_HEAP = 1; const int32_t KIND_OTHER = 2; /* - * Units. See the |units| comment in nsIMemoryReporterCallback. + * Units. See the |units| comment in nsIHandleReportCallback. */ const int32_t UNITS_BYTES = 0; const int32_t UNITS_COUNT = 1; @@ -275,7 +275,7 @@ interface nsIMemoryReporterManager : nsISupports * If |anonymize| is true, it indicates that the memory reporters should * anonymize any privacy-sensitive data (see above). */ - void getReports(in nsIMemoryReporterCallback handleReport, + void getReports(in nsIHandleReportCallback handleReport, in nsISupports handleReportData, in nsIFinishReportingCallback finishReporting, in nsISupports finishReportingData, @@ -289,7 +289,7 @@ interface nsIMemoryReporterManager : nsISupports * |dumpMemoryInfoToTempDir| in |nsIMemoryInfoDumper|.) */ [noscript] void - getReportsExtended(in nsIMemoryReporterCallback handleReport, + getReportsExtended(in nsIHandleReportCallback handleReport, in nsISupports handleReportData, in nsIFinishReportingCallback finishReporting, in nsISupports finishReportingData, @@ -302,7 +302,7 @@ interface nsIMemoryReporterManager : nsISupports * write a DMD report to that file and close it. */ [noscript] void - getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport, + getReportsForThisProcessExtended(in nsIHandleReportCallback handleReport, in nsISupports handleReportData, in boolean anonymize, in FILE DMDFile, @@ -458,10 +458,6 @@ interface nsIMemoryReporterManager : nsISupports class nsPIDOMWindowOuter; -// nsIHandleReportCallback is a better name, but keep nsIMemoryReporterCallback -// around for backwards compatibility. -typedef nsIMemoryReporterCallback nsIHandleReportCallback; - namespace mozilla { // All the following registration/unregistration functions don't use diff --git a/xpcom/ds/PLDHashTable.cpp b/xpcom/ds/PLDHashTable.cpp index ee8f65ba5641..9f4954c158f7 100644 --- a/xpcom/ds/PLDHashTable.cpp +++ b/xpcom/ds/PLDHashTable.cpp @@ -199,15 +199,22 @@ PLDHashTable::HashShift(uint32_t aEntrySize, uint32_t aLength) PLDHashTable::PLDHashTable(const PLDHashTableOps* aOps, uint32_t aEntrySize, uint32_t aLength) : mOps(aOps) + , mEntryStore() + , mGeneration(0) , mHashShift(HashShift(aEntrySize, aLength)) , mEntrySize(aEntrySize) , mEntryCount(0) , mRemovedCount(0) - , mEntryStore() #ifdef DEBUG , mChecker() #endif { + // An entry size greater than 0xff is unlikely, but let's check anyway. If + // you hit this, your hashtable would waste lots of space for unused entries + // and you should change your hash table's entries to pointers. + if (aEntrySize != uint32_t(mEntrySize)) { + MOZ_CRASH("Entry size is too large"); + } } PLDHashTable& @@ -232,7 +239,7 @@ PLDHashTable::operator=(PLDHashTable&& aOther) mHashShift = Move(aOther.mHashShift); mEntryCount = Move(aOther.mEntryCount); mRemovedCount = Move(aOther.mRemovedCount); - mEntryStore = Move(aOther.mEntryStore); + mEntryStore.Set(aOther.mEntryStore.Get(), &mGeneration); #ifdef DEBUG mChecker = Move(aOther.mChecker); #endif @@ -242,7 +249,7 @@ PLDHashTable::operator=(PLDHashTable&& aOther) #ifdef DEBUG AutoDestructorOp op(mChecker); #endif - aOther.mEntryStore.Set(nullptr); + aOther.mEntryStore.Set(nullptr, &aOther.mGeneration); } return *this; @@ -483,7 +490,7 @@ PLDHashTable::ChangeTable(int32_t aDeltaLog2) char* oldEntryStore; char* oldEntryAddr; oldEntryAddr = oldEntryStore = mEntryStore.Get(); - mEntryStore.Set(newEntryStore); + mEntryStore.Set(newEntryStore, &mGeneration); PLDHashMoveEntry moveEntry = mOps->moveEntry; // Copy only live entries, leaving removed ones behind. @@ -548,7 +555,7 @@ PLDHashTable::Add(const void* aKey, const mozilla::fallible_t&) // We already checked this in the constructor, so it must still be true. MOZ_RELEASE_ASSERT(SizeOfEntryStore(CapacityFromHashShift(), mEntrySize, &nbytes)); - mEntryStore.Set((char*)malloc(nbytes)); + mEntryStore.Set((char*)malloc(nbytes), &mGeneration); if (!mEntryStore.Get()) { return nullptr; } diff --git a/xpcom/ds/PLDHashTable.h b/xpcom/ds/PLDHashTable.h index cd1323dbe7b5..6f533a2fba02 100644 --- a/xpcom/ds/PLDHashTable.h +++ b/xpcom/ds/PLDHashTable.h @@ -209,40 +209,45 @@ class PLDHashTable private: // This class maintains the invariant that every time the entry store is // changed, the generation is updated. + // + // Note: It would be natural to store the generation within this class, but + // we can't do that without bloating sizeof(PLDHashTable) on 64-bit machines. + // So instead we store it outside this class, and Set() takes a pointer to it + // and ensures it is updated as necessary. class EntryStore { private: char* mEntryStore; - uint32_t mGeneration; public: - EntryStore() : mEntryStore(nullptr), mGeneration(0) {} + EntryStore() : mEntryStore(nullptr) {} ~EntryStore() { free(mEntryStore); mEntryStore = nullptr; - mGeneration++; // a little paranoid, but why not be extra safe? } char* Get() { return mEntryStore; } const char* Get() const { return mEntryStore; } - void Set(char* aEntryStore) + void Set(char* aEntryStore, uint16_t* aGeneration) { mEntryStore = aEntryStore; - mGeneration++; + *aGeneration += 1; } - - uint32_t Generation() const { return mGeneration; } }; + // These fields are packed carefully. On 32-bit platforms, + // sizeof(PLDHashTable) is 20. On 64-bit platforms, sizeof(PLDHashTable) is + // 32; 28 bytes of data followed by 4 bytes of padding for alignment. const PLDHashTableOps* const mOps; // Virtual operations; see below. - int16_t mHashShift; // Multiplicative hash shift. - const uint32_t mEntrySize; // Number of bytes in an entry. + EntryStore mEntryStore; // (Lazy) entry storage and generation. + uint16_t mGeneration; // The storage generation. + uint8_t mHashShift; // Multiplicative hash shift. + const uint8_t mEntrySize; // Number of bytes in an entry. uint32_t mEntryCount; // Number of entries in table. uint32_t mRemovedCount; // Removed entry sentinels in table. - EntryStore mEntryStore; // (Lazy) entry storage and generation. #ifdef DEBUG mutable Checker mChecker; @@ -278,13 +283,16 @@ public: uint32_t aLength = kDefaultInitialLength); PLDHashTable(PLDHashTable&& aOther) - // These two fields are |const|. Initialize them here because the - // move assignment operator cannot modify them. + // We initialize mOps and mEntrySize here because they are |const|, and + // the move assignment operator cannot modify them. + // We initialize mEntryStore because it is required for a safe call to + // the destructor, which the move assignment operator does. + // We initialize mGeneration because it is modified by the move + // assignment operator. : mOps(aOther.mOps) - , mEntrySize(aOther.mEntrySize) - // Initialize this field because it is required for a safe call to the - // destructor, which the move assignment operator does. , mEntryStore() + , mGeneration(0) + , mEntrySize(aOther.mEntrySize) #ifdef DEBUG , mChecker() #endif @@ -309,7 +317,7 @@ public: uint32_t EntrySize() const { return mEntrySize; } uint32_t EntryCount() const { return mEntryCount; } - uint32_t Generation() const { return mEntryStore.Generation(); } + uint32_t Generation() const { return mGeneration; } // To search for a |key| in |table|, call: // diff --git a/xpcom/string/nsReadableUtils.cpp b/xpcom/string/nsReadableUtils.cpp index 952cf84f9b44..eb8c74fe4170 100644 --- a/xpcom/string/nsReadableUtils.cpp +++ b/xpcom/string/nsReadableUtils.cpp @@ -1166,7 +1166,7 @@ EmptyCString() } const nsString& -NullString() +VoidString() { static const nsString sNull(mozilla::detail::StringDataFlags::VOIDED); @@ -1174,7 +1174,7 @@ NullString() } const nsCString& -NullCString() +VoidCString() { static const nsCString sNull(mozilla::detail::StringDataFlags::VOIDED); diff --git a/xpcom/string/nsReadableUtils.h b/xpcom/string/nsReadableUtils.h index 3ef1db247c3e..37ada693dd3d 100644 --- a/xpcom/string/nsReadableUtils.h +++ b/xpcom/string/nsReadableUtils.h @@ -426,8 +426,8 @@ bool StringEndsWith(const nsACString& aSource, const nsACString& aSubstring, const nsString& EmptyString(); const nsCString& EmptyCString(); -const nsString& NullString(); -const nsCString& NullCString(); +const nsString& VoidString(); +const nsCString& VoidCString(); /** * Compare a UTF-8 string to an UTF-16 string. diff --git a/xpcom/string/nsTString.h b/xpcom/string/nsTString.h index 4f7396e5599a..246756ec0588 100644 --- a/xpcom/string/nsTString.h +++ b/xpcom/string/nsTString.h @@ -501,8 +501,8 @@ protected: { } - friend const nsTString& NullCString(); - friend const nsTString& NullString(); + friend const nsTString& VoidCString(); + friend const nsTString& VoidString(); // Used by Null[C]String. explicit nsTString(DataFlags aDataFlags) diff --git a/xpcom/tests/gtest/TestPLDHash.cpp b/xpcom/tests/gtest/TestPLDHash.cpp index e7a73ae1bb97..7291732faa5d 100644 --- a/xpcom/tests/gtest/TestPLDHash.cpp +++ b/xpcom/tests/gtest/TestPLDHash.cpp @@ -105,7 +105,14 @@ InitCapacityOk_InitialEntryStoreTooBig() // Try the smallest disallowed power-of-two entry store size, which is 2^32 // bytes (which overflows to 0). (Note that the 2^23 *length* gets converted // to a 2^24 *capacity*.) - PLDHashTable t(PLDHashTable::StubOps(), (uint32_t)1 << 23, (uint32_t)1 << 8); + PLDHashTable t(PLDHashTable::StubOps(), (uint32_t)1 << 8, (uint32_t)1 << 23); +} + +void +InitCapacityOk_EntrySizeTooBig() +{ + // Try the smallest disallowed entry size, which is 256 bytes. + PLDHashTable t(PLDHashTable::StubOps(), 256); } TEST(PLDHashTableTest, InitCapacityOk) @@ -118,7 +125,7 @@ TEST(PLDHashTableTest, InitCapacityOk) // Try the largest allowed power-of-two entry store size, which is 2^31 bytes // (Note that the 2^23 *length* gets converted to a 2^24 *capacity*.) - PLDHashTable t2(PLDHashTable::StubOps(), (uint32_t)1 << 23, (uint32_t)1 << 7); + PLDHashTable t2(PLDHashTable::StubOps(), (uint32_t)1 << 7, (uint32_t)1 << 23); // Try a too-large capacity (which aborts). TestCrashyOperation(InitCapacityOk_InitialLengthTooBig); @@ -127,6 +134,12 @@ TEST(PLDHashTableTest, InitCapacityOk) // overflow (causing abort). TestCrashyOperation(InitCapacityOk_InitialEntryStoreTooBig); + // Try the largest allowed entry size. + PLDHashTable t3(PLDHashTable::StubOps(), 255); + + // Try an overly large entry size. + TestCrashyOperation(InitCapacityOk_EntrySizeTooBig); + // Ideally we'd also try a large-but-ok capacity that almost but doesn't // quite overflow, but that would result in allocating slightly less than 4 // GiB of entry storage. That would be very likely to fail on 32-bit diff --git a/xpfe/components/directory/nsDirectoryViewer.cpp b/xpfe/components/directory/nsDirectoryViewer.cpp index 4a1442199747..1999a676bd27 100644 --- a/xpfe/components/directory/nsDirectoryViewer.cpp +++ b/xpfe/components/directory/nsDirectoryViewer.cpp @@ -931,7 +931,7 @@ nsHTTPIndex::FireTimer(nsITimer* aTimer, void* aClosure) do_QueryElementAt(httpIndex->mConnectionList, 0); httpIndex->mConnectionList->RemoveElementAt(0); - nsCString uri = NullCString(); + nsCString uri = VoidCString(); if (source) { httpIndex->GetDestination(source, uri); }