tor-browser

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

commit e1d22ea27f3632f31e15b603fa1b6a3029d30221
parent 4002601fa705d444acbe2c659c681f16678dada3
Author: Vincent Hilla <vhilla@mozilla.com>
Date:   Wed,  3 Dec 2025 19:06:46 +0000

Bug 2003255 - Force sync about:blank despite busy DocLoader. r=dom-core,smaug

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

Diffstat:
Mdocshell/test/browser/browser.toml | 2++
Adocshell/test/browser/browser_initialDocument_noLoadBlocking.js | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdom/base/Document.h | 2++
Muriloader/base/nsDocLoader.cpp | 20+++++++++++++++++---
4 files changed, 92 insertions(+), 3 deletions(-)

diff --git a/docshell/test/browser/browser.toml b/docshell/test/browser/browser.toml @@ -316,6 +316,8 @@ skip-if = [ ["browser_history_triggeringprincipal_viewsource.js"] https_first_disabled = true +["browser_initialDocument_noLoadBlocking.js"] + ["browser_initial_entry_without_interaction.js"] ["browser_isInitialDocument.js"] diff --git a/docshell/test/browser/browser_initialDocument_noLoadBlocking.js b/docshell/test/browser/browser_initialDocument_noLoadBlocking.js @@ -0,0 +1,71 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// See bug 2003255 +add_task(async function test_extension_cannot_block_initial_load() { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + manifest_version: 2, + name: "insert script to about:blank", + version: "1.0", + content_scripts: [ + { + matches: ["<all_urls>"], + js: ["content.js"], + run_at: "document_start", + all_frames: true, + match_about_blank: true, + }, + ], + }, + + files: { + "content.js": function () { + if (window.location.href != "about:blank#unique-hash") { + return; + } + + const script = document.createElement("script"); + script.src = + "data:,(window.parent.postMessage(`script ${window.location.href}`))()"; + (document.documentElement || document).appendChild(script); + }, + }, + }); + + await extension.startup(); + + const url = "https://example.com/"; + const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url); + + await ContentTask.spawn(tab.linkedBrowser, [url], async function (url) { + Assert.equal(content.location.href, url, "Correct content document"); + + const scriptEvaluated = new Promise( + res => + (content.onmessage = ({ data }) => { + if (data == "script about:blank#unique-hash") { + res(); + } + }) + ); + + let loaded = false; + const iframe = content.document.createElement("iframe"); + iframe.onload = () => (loaded = true); + // Let's set a unique hash in case there are other about:blank + iframe.src = "about:blank#unique-hash"; + content.document.body.append(iframe); + Assert.ok(loaded, "Load occurred synchronously"); + + const extScript = iframe.contentDocument.querySelector("script"); + Assert.ok(!!extScript, "Extension inserted script synchronously"); + + await scriptEvaluated; + }); + + BrowserTestUtils.removeTab(tab); + await extension.unload(); +}); diff --git a/dom/base/Document.h b/dom/base/Document.h @@ -4197,6 +4197,8 @@ class Document : public nsINode, bool DOMNotificationsSuspended() const { return mSuspendDOMNotifications; } + bool IsExpectingEndLoad() { return mDidCallBeginLoad; } + protected: RefPtr<DocumentL10n> mDocumentL10n; diff --git a/uriloader/base/nsDocLoader.cpp b/uriloader/base/nsDocLoader.cpp @@ -756,8 +756,19 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout, alive long enough to survive this function call. */ nsCOMPtr<nsIDocumentLoader> kungFuDeathGrip(this); + nsCOMPtr<Document> doc = do_GetInterface(GetAsSupports(this)); + // Force load if + // - we are currently in the synchronous load path of the docshell, i.e. we + // are completing the initial about:blank load + // - and Document::EndLoad was already called (we're likely called from + // there right now). + const bool forceInitialSyncLoad = doc && + doc->InitialAboutBlankLoadCompleting() && + !doc->IsExpectingEndLoad(); + MOZ_ASSERT_IF(forceInitialSyncLoad, !mIsFlushingLayout); + // Don't flush layout if we're still busy. - if (IsBusy()) { + if (IsBusy() && !forceInitialSyncLoad) { return; } @@ -792,8 +803,11 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout, // // Note, mDocumentRequest can be null while mDocumentOpenedButNotLoaded is // false if the flushing above re-entered this method. - if (IsBusy() || (!mDocumentRequest && !mDocumentOpenedButNotLoaded && - !mIsLoadingJavascriptURI)) { + const bool hasActiveLoad = mDocumentRequest || + mDocumentOpenedButNotLoaded || + mIsLoadingJavascriptURI; + MOZ_ASSERT_IF(forceInitialSyncLoad, hasActiveLoad); + if ((IsBusy() && !forceInitialSyncLoad) || !hasActiveLoad) { return; }