From ad1be13e59903ee005d438dc7c447369ed1e18b6 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Tue, 16 Apr 2019 22:49:25 +0000 Subject: [PATCH] Bug 1543786 - Ensure that we revoke a top frame's storage access when it is navigated away; r=baku Differential Revision: https://phabricator.services.mozilla.com/D27155 --HG-- extra : moz-landing-system : lando --- docshell/base/nsDocShell.cpp | 20 ++++++++- docshell/base/nsDocShell.h | 3 ++ dom/base/nsFrameLoader.cpp | 8 ++++ dom/ipc/TabChild.cpp | 9 +++- .../test/browser/antitracking_head.js | 45 ++++++++++++++++++- .../antitracking/test/browser/browser.ini | 2 + ...er_storageAccessRemovalNavigateTopframe.js | 40 +++++++++++++++++ 7 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 toolkit/components/antitracking/test/browser/browser_storageAccessRemovalNavigateTopframe.js diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 5d9461d752d0..9ae08e9f1750 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -2642,8 +2642,8 @@ nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) { RecomputeCanExecuteScripts(); // Inform windows when they're being removed from their parent. - if (!aParent && mScriptGlobal) { - mScriptGlobal->ParentWindowChanged(); + if (!aParent) { + MaybeClearStorageAccessFlag(); } NS_ASSERTION(mInheritPrivateBrowsingId || wasPrivate == UsePrivateBrowsing(), @@ -2652,6 +2652,22 @@ nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) { return NS_OK; } +void nsDocShell::MaybeClearStorageAccessFlag() { + if (mScriptGlobal) { + // Tell our window that the parent has now changed. + mScriptGlobal->ParentWindowChanged(); + + // Tell all of our children about the change recursively as well. + nsTObserverArray::ForwardIterator iter(mChildList); + while (iter.HasMore()) { + nsCOMPtr child = do_QueryObject(iter.GetNext()); + if (child) { + static_cast(child.get())->MaybeClearStorageAccessFlag(); + } + } + } +} + NS_IMETHODIMP nsDocShell::GetSameTypeParent(nsIDocShellTreeItem** aParent) { NS_ENSURE_ARG_POINTER(aParent); diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index a3b01e04ddfb..c3f74809cb5a 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -399,6 +399,9 @@ class nsDocShell final : public nsDocLoader, nsresult InternalLoad(nsDocShellLoadState* aLoadState, nsIDocShell** aDocShell, nsIRequest** aRequest); + // Clear the document's storage access flag if needed. + void MaybeClearStorageAccessFlag(); + private: // member functions friend class nsDSURIContentListener; friend class FramingChecker; diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 5f198e773a92..98da1960c407 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -498,6 +498,12 @@ nsresult nsFrameLoader::ReallyStartLoadingInternal() { return NS_OK; } + if (GetDocShell()) { + // If we already have a docshell, ensure that the docshell's storage access + // flag is cleared. + GetDocShell()->MaybeClearStorageAccessFlag(); + } + nsresult rv = MaybeCreateDocShell(); if (NS_FAILED(rv)) { return rv; @@ -1019,6 +1025,8 @@ void nsFrameLoader::Hide() { return; } + GetDocShell()->MaybeClearStorageAccessFlag(); + nsCOMPtr contentViewer; GetDocShell()->GetContentViewer(getter_AddRefs(contentViewer)); if (contentViewer) contentViewer->SetSticky(false); diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index e6173f744d8b..5d93ec74e54f 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -1050,14 +1050,19 @@ mozilla::ipc::IPCResult TabChild::RecvLoadURL(const nsCString& aURI, nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL; - nsresult rv = - WebNavigation()->LoadURI(NS_ConvertUTF8toUTF16(aURI), loadURIOptions); + nsIWebNavigation* webNav = WebNavigation(); + nsresult rv = webNav->LoadURI(NS_ConvertUTF8toUTF16(aURI), loadURIOptions); if (NS_FAILED(rv)) { NS_WARNING( "WebNavigation()->LoadURI failed. Eating exception, what else can I " "do?"); } + nsCOMPtr docShell = do_GetInterface(WebNavigation()); + if (docShell) { + nsDocShell::Cast(docShell)->MaybeClearStorageAccessFlag(); + } + CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, aURI); return IPC_OK(); diff --git a/toolkit/components/antitracking/test/browser/antitracking_head.js b/toolkit/components/antitracking/test/browser/antitracking_head.js index 76463b613440..892a76e9e8ec 100644 --- a/toolkit/components/antitracking/test/browser/antitracking_head.js +++ b/toolkit/components/antitracking/test/browser/antitracking_head.js @@ -335,7 +335,7 @@ this.AntiTracking = { } else { thirdPartyPage = TEST_3RD_PARTY_PAGE; } - await ContentTask.spawn(browser, + let id = await ContentTask.spawn(browser, { page: thirdPartyPage, nextPage: TEST_4TH_PARTY_PAGE, callback: options.callback.toString(), @@ -420,14 +420,57 @@ this.AntiTracking = { ifr.src = obj.nextPage; }); + case "navigate-topframe": + // pass-through break; default: ok(false, "Unexpected accessRemoval code passed: " + obj.accessRemoval); break; } } + + return id; }); + if (doAccessRemovalChecks && + options.accessRemoval == "navigate-topframe") { + await BrowserTestUtils.loadURI(browser, TEST_4TH_PARTY_PAGE); + await BrowserTestUtils.browserLoaded(browser); + + let pageshow = BrowserTestUtils.waitForContentEvent(tab.linkedBrowser, "pageshow"); + gBrowser.goBack(); + await pageshow; + + await ContentTask.spawn(browser, + { id, + callbackAfterRemoval: options.callbackAfterRemoval ? + options.callbackAfterRemoval.toString() : null, + }, + async function(obj) { + let ifr = content.document.getElementById(obj.id); + ifr.contentWindow.postMessage(obj.callbackAfterRemoval, "*"); + + content.addEventListener("message", function msg(event) { + if (event.data.type == "finish") { + content.removeEventListener("message", msg); + return; + } + + if (event.data.type == "ok") { + ok(event.data.what, event.data.msg); + return; + } + + if (event.data.type == "info") { + info(event.data.msg); + return; + } + + ok(false, "Unknown message"); + }); + }); + } + if (options.allowList) { info("Enabling content blocking for this page"); win.ContentBlocking.enableForCurrentPage(); diff --git a/toolkit/components/antitracking/test/browser/browser.ini b/toolkit/components/antitracking/test/browser/browser.ini index b2fe8e22f93f..83483da67cfa 100644 --- a/toolkit/components/antitracking/test/browser/browser.ini +++ b/toolkit/components/antitracking/test/browser/browser.ini @@ -79,6 +79,8 @@ skip-if = serviceworker_e10s [browser_storageAccessPromiseResolveHandlerUserInteraction.js] [browser_storageAccessRemovalNavigateSubframe.js] skip-if = serviceworker_e10s +[browser_storageAccessRemovalNavigateTopframe.js] +skip-if = serviceworker_e10s [browser_storageAccessSandboxed.js] skip-if = serviceworker_e10s [browser_storageAccessWithHeuristics.js] diff --git a/toolkit/components/antitracking/test/browser/browser_storageAccessRemovalNavigateTopframe.js b/toolkit/components/antitracking/test/browser/browser_storageAccessRemovalNavigateTopframe.js new file mode 100644 index 000000000000..71b1340c7e0a --- /dev/null +++ b/toolkit/components/antitracking/test/browser/browser_storageAccessRemovalNavigateTopframe.js @@ -0,0 +1,40 @@ +AntiTracking.runTest("Storage Access is removed when topframe navigates", + // blocking callback + async _ => { + /* import-globals-from storageAccessAPIHelpers.js */ + await noStorageAccessInitially(); + }, + + // non-blocking callback + async _ => { + /* import-globals-from storageAccessAPIHelpers.js */ + if (allowListed) { + await hasStorageAccessInitially(); + } else { + await noStorageAccessInitially(); + } + + /* import-globals-from storageAccessAPIHelpers.js */ + let [threw, rejected] = await callRequestStorageAccess(); + ok(!threw, "requestStorageAccess should not throw"); + ok(!rejected, "requestStorageAccess should be available"); + }, + // cleanup function + async _ => { + await new Promise(resolve => { + Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve()); + }); + }, + null, // extra prefs + false, // no window open test + false, // no user-interaction test + 0, // no blocking notifications + false, // run in normal window + null, // no iframe sandbox + "navigate-topframe", // access removal type + // after-removal callback + async _ => { + /* import-globals-from storageAccessAPIHelpers.js */ + await noStorageAccessInitially(); + } +);