tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 2fe2da3e4b00a6f242e68aa97c519034c4f304c0
parent dbad5d1e6bb41cfe17061a0258a872ac7a642a8c
Author: Vincent Hilla <vhilla@mozilla.com>
Date:   Tue,  9 Dec 2025 15:18:38 +0000

Bug 2004407 - Propagate base URI to initial top level about:blank. r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D275446

Diffstat:
Mdom/base/nsGlobalWindowOuter.cpp | 35++++++++++++++++-------------------
Mdom/base/nsGlobalWindowOuter.h | 5+----
Mdom/base/nsPIDOMWindow.h | 9++++-----
Mdom/ipc/ContentChild.cpp | 4+---
Mdom/tests/browser/browser_alert_from_about_blank.js | 2+-
Dtesting/web-platform/meta/html/infrastructure/urls/base-url/document-base-url-window-open-about-blank.https.window.js.ini | 3---
Mtesting/web-platform/meta/service-workers/service-worker/register-closed-window.https.html.ini | 3+++
Atesting/web-platform/tests/html/infrastructure/urls/base-url/initial-about-blank-baseURI.window.js | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtesting/web-platform/tests/service-workers/service-worker/resources/register-closed-window-iframe.html | 6+++++-
Mtoolkit/components/windowwatcher/nsWindowWatcher.cpp | 2++
10 files changed, 118 insertions(+), 36 deletions(-)

diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp @@ -1759,8 +1759,7 @@ bool nsGlobalWindowOuter::WouldReuseInnerWindow(Document* aNewDocument) { } void nsGlobalWindowOuter::SetInitialPrincipal( - nsIPrincipal* aNewWindowPrincipal, nsIPolicyContainer* aPolicyContainer, - const Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>& aCOEP) { + nsIPrincipal* aNewWindowPrincipal) { // We should never create windows with an expanded principal. // If we have a system principal, make sure we're not using it for a content // docshell. @@ -1772,30 +1771,28 @@ void nsGlobalWindowOuter::SetInitialPrincipal( aNewWindowPrincipal = nullptr; } - // If there's an existing document, bail if it either: - if (mDoc) { - // (a) is not an initial about:blank document, or - if (!mDoc->IsInitialDocument()) return; - // (b) already has the correct principal. - if (mDoc->NodePrincipal() == aNewWindowPrincipal) return; + MOZ_ASSERT(mDoc, "Some document should've been eagerly created"); + + // Bail if the existing document is (a) not initial + if (!mDoc->IsUncommittedInitialDocument()) return; + // or (b) already has the correct principal. + if (mDoc->NodePrincipal() == aNewWindowPrincipal) return; #ifdef DEBUG - // If we have a document loaded at this point, it had better be about:blank. - // Otherwise, something is really weird. An about:blank page has a - // NullPrincipal. - bool isNullPrincipal; - MOZ_ASSERT(NS_SUCCEEDED(mDoc->NodePrincipal()->GetIsNullPrincipal( - &isNullPrincipal)) && - isNullPrincipal); + // The current document should be a dummy and therefore have a null principal + bool isNullPrincipal; + MOZ_ASSERT(NS_SUCCEEDED( + mDoc->NodePrincipal()->GetIsNullPrincipal(&isNullPrincipal)) && + isNullPrincipal); #endif - } // Use the subject (or system) principal as the storage principal too until // the new window finishes navigating and gets a real storage principal. nsDocShell::Cast(GetDocShell()) - ->CreateAboutBlankDocumentViewer(aNewWindowPrincipal, aNewWindowPrincipal, - aPolicyContainer, nullptr, - /* aIsInitialDocument */ true, aCOEP); + ->CreateAboutBlankDocumentViewer( + aNewWindowPrincipal, aNewWindowPrincipal, mDoc->GetPolicyContainer(), + mDoc->GetDocBaseURI(), + /* aIsInitialDocument */ true, mDoc->GetEmbedderPolicy()); if (mDoc) { MOZ_ASSERT(mDoc->IsInitialDocument(), diff --git a/dom/base/nsGlobalWindowOuter.h b/dom/base/nsGlobalWindowOuter.h @@ -284,10 +284,7 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget, mozilla::dom::EventTarget* aChromeEventHandler) override; // Outer windows only. - virtual void SetInitialPrincipal( - nsIPrincipal* aNewWindowPrincipal, nsIPolicyContainer* aPolicyContainer, - const mozilla::Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>& aCoep) - override; + virtual void SetInitialPrincipal(nsIPrincipal* aNewWindowPrincipal) override; virtual already_AddRefed<nsISupports> SaveWindowState() override; MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual nsresult RestoreWindowState( diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h @@ -875,11 +875,10 @@ class nsPIDOMWindowOuter : public mozIDOMWindowProxy { return mDoc; } - // Set the window up with an about:blank document with the given principal and - // potentially a policyContainer and a COEP. - virtual void SetInitialPrincipal( - nsIPrincipal* aNewWindowPrincipal, nsIPolicyContainer* aPolicyContainer, - const mozilla::Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>& aCoep) = 0; + // Set the window up with an about:blank document with the given principal. + // Base URI, COEP and PolicyContainer of the current document will be + // retained. + virtual void SetInitialPrincipal(nsIPrincipal* aNewWindowPrincipal) = 0; // Returns an object containing the window's state. This also suspends // all running timeouts in the window. diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp @@ -1170,9 +1170,7 @@ nsresult ContentChild::ProvideWindowCommon( // This creates a new document and the timing is quite fragile. NS_ENSURE_TRUE(browsingContext->GetDOMWindow(), NS_ERROR_ABORT); browsingContext->GetDOMWindow()->SetInitialPrincipal( - aOpenWindowInfo->PrincipalToInheritForAboutBlank(), - aOpenWindowInfo->PolicyContainerToInheritForAboutBlank(), - aOpenWindowInfo->CoepToInheritForAboutBlank()); + aOpenWindowInfo->PrincipalToInheritForAboutBlank()); // Set to true when we're ready to return from this function. bool ready = false; diff --git a/dom/tests/browser/browser_alert_from_about_blank.js b/dom/tests/browser/browser_alert_from_about_blank.js @@ -25,7 +25,7 @@ add_task(async function test_check_alert_from_blank() { // Start an async navigation that will close the alert // Previously this wasn't needed as the initial about:blank // was followed by an async about:blank load. See Bug 543435 - newWin.location = "/blank"; + newWin.location = "about:blank?0"; newWin.alert("Alert from the popup."); info("Button onclick: finished"); }); diff --git a/testing/web-platform/meta/html/infrastructure/urls/base-url/document-base-url-window-open-about-blank.https.window.js.ini b/testing/web-platform/meta/html/infrastructure/urls/base-url/document-base-url-window-open-about-blank.https.window.js.ini @@ -1,3 +0,0 @@ -[document-base-url-window-open-about-blank.https.window.html] - [window.open() gets base url from initiator.] - expected: FAIL diff --git a/testing/web-platform/meta/service-workers/service-worker/register-closed-window.https.html.ini b/testing/web-platform/meta/service-workers/service-worker/register-closed-window.https.html.ini @@ -1,3 +1,6 @@ [register-closed-window.https.html] expected: if (os == "android") and fission: [OK, TIMEOUT] + TIMEOUT + [Call register() on ServiceWorkerContainer owned by closed window.] + expected: TIMEOUT diff --git a/testing/web-platform/tests/html/infrastructure/urls/base-url/initial-about-blank-baseURI.window.js b/testing/web-platform/tests/html/infrastructure/urls/base-url/initial-about-blank-baseURI.window.js @@ -0,0 +1,85 @@ +// META: script=/common/get-host-info.sub.js +// Test that the initial about:blank gets the about base URL +// from the entry global object. + +async function withIframe(src, cb) { + const ifr = document.createElement("iframe"); + ifr.src = src; + document.body.append(ifr); + cb(ifr); + ifr.remove(); +} + +async function withWindow(src, cb) { + const w = window.open(src); + cb(w); + w.close(); +} + +// Need a trailing '/' for equality checks +const REMOTE_ORIGIN = new URL("/", get_host_info().REMOTE_ORIGIN).href; + +async function withWindowOpenerNotInitiator(src, cb) { + window.deferredIframeWindow = Promise.withResolvers(); + + // Create an iframe with a different base URL. + // If it opens a window with window.top being the opener, + // the base URL should come from the initiator, i.e. this iframe. + const ifr = document.createElement("iframe"); + ifr.srcdoc = ` + <head> + <base href='${REMOTE_ORIGIN}'> + <script> + const w = window.top.open('${src}'); + window.top.deferredIframeWindow.resolve(w); + </scr` + `ipt> + </head> + <body></body> + `; + document.body.append(ifr); + + const w = await window.deferredIframeWindow.promise; + + cb(w); + + w.close(); + ifr.remove(); +} + +promise_test(async t => { + await withIframe("", ifr => { + assert_equals(ifr.contentDocument.baseURI, document.baseURI, "about:blank has creator's base URI"); + }) +}, "Initial iframe about:blank gets base url from creator"); + +promise_test(async t => { + await withIframe("/arbitrary-sameorigin-src", ifr => { + assert_equals(ifr.contentDocument.baseURI, document.baseURI, "about:blank has creator's base URI"); + }) +}, "Transient iframe about:blank gets base url from creator"); + +promise_test(async t => { + await withWindow("", w => { + assert_equals(w.document.baseURI, document.baseURI, "about:blank has creator's base URI"); + }) +}, "Initial top-level about:blank gets base url from creator = opener"); + +promise_test(async t => { + await withWindow("/arbitrary-sameorigin-src", w => { + assert_equals(w.document.baseURI, document.baseURI, "about:blank has creator's base URI"); + }) +}, "Transient top-level about:blank gets base url from creator = opener"); + +promise_test(async t => { + await withWindowOpenerNotInitiator("", w => { + assert_not_equals(REMOTE_ORIGIN, document.baseURI, "These need to be different"); + assert_equals(w.document.baseURI, REMOTE_ORIGIN, "about:blank has creator's base URI"); + }) +}, "Initial top-level about:blank gets base url from creator != opener"); + +promise_test(async t => { + await withWindowOpenerNotInitiator("/arbitrary-sameorigin-src", w => { + assert_not_equals(REMOTE_ORIGIN, document.baseURI, "These need to be different"); + assert_equals(w.document.baseURI, REMOTE_ORIGIN, "about:blank has creator's base URI"); + }) +}, "Transient top-level about:blank gets base url from creator != opener"); diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/register-closed-window-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/register-closed-window-iframe.html @@ -7,8 +7,12 @@ window.addEventListener('message', async function(evt) { var sw = w.navigator.serviceWorker; w.close(); w = null; + + // Ensure sw.register receives a valid URL. The popup might have + // about:blank as base so sw.register('doestnmatter.js') would be invalid. + const swUrl = new URL('doestnmatter.js', document.baseURI).href; try { - await sw.register('doesntmatter.js'); + await sw.register(swUrl); } finally { parent.postMessage('OK', '*'); } diff --git a/toolkit/components/windowwatcher/nsWindowWatcher.cpp b/toolkit/components/windowwatcher/nsWindowWatcher.cpp @@ -964,6 +964,8 @@ nsresult nsWindowWatcher::OpenWindowInternal( entryDoc->GetPolicyContainer(); openWindowInfo->mCoepToInheritForAboutBlank = entryDoc->GetEmbedderPolicy(); + openWindowInfo->mBaseUriToInheritForAboutBlank = + entryDoc->GetBaseURI(); } }