tor-browser

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

commit 4e61329ac0c2ce9b66733e8d65d3c8d3c02b9dcb
parent 74bc43245c32f0349182dea50406446df875ad7a
Author: Yoshi Cheng-Hao Huang <allstars.chh@gmail.com>
Date:   Wed,  8 Oct 2025 08:35:27 +0000

Bug 1992588 - Align error handling with the HTML spec when loading imported module scripts. r=jonco,frontend-codestyle-reviewers

According to the spec, a ThrowCompletion should be performed if the module
script is null or has a parse error.
See https://html.spec.whatwg.org/#hostloadimportedmodule:fetch-a-single-imported-module-script
Step 14.

So this patch reverts ModuleLoadRequest::IsErrored() update in https://phabricator.services.mozilla.com/D264807

Additionally, a spec issue has been filed to clarify whether an error to
rethrow should also be treated as a ThrowCompletion:
https://github.com/whatwg/html/issues/11755

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

Diffstat:
M.prettierignore | 1+
Mdom/base/test/jsmodules/chrome.toml | 11++++++++++-
Adom/base/test/jsmodules/import_parse_error.mjs | 2++
Adom/base/test/jsmodules/import_parse_error_1.mjs | 2++
Adom/base/test/jsmodules/import_parse_error_2.mjs | 2++
Adom/base/test/jsmodules/import_parse_error_3.mjs | 2++
Adom/base/test/jsmodules/parse_error.js | 1+
Adom/base/test/jsmodules/test_fetchedModuleHasErrorToRethrow.html | 23+++++++++++++++++++++++
Adom/base/test/jsmodules/test_fetchedModuleHasErrorToRethrow_dynamic.html | 24++++++++++++++++++++++++
Meslint-ignores.config.mjs | 1+
Mjs/loader/ModuleLoadRequest.cpp | 16+++++++++-------
Mjs/loader/ModuleLoaderBase.cpp | 9+++++++++
12 files changed, 86 insertions(+), 8 deletions(-)

diff --git a/.prettierignore b/.prettierignore @@ -1119,6 +1119,7 @@ dom/base/test/jsmodules/test_syntaxErrorAsync.html dom/base/test/jsmodules/module_badSyntax.mjs dom/base/test/jsmodules/test_syntaxErrorInline.html dom/base/test/jsmodules/test_syntaxErrorInlineAsync.html +dom/base/test/jsmodules/parse_error.js dom/base/test/test_bug687859.html dom/media/webrtc/tests/mochitests/identity/idp-bad.js dom/security/test/general/file_nonscript.json diff --git a/dom/base/test/jsmodules/chrome.toml b/dom/base/test/jsmodules/chrome.toml @@ -10,6 +10,10 @@ support-files = [ "import_circular_1.mjs", "import_no_export.mjs", "import_no_indirect_export.mjs", + "import_parse_error.mjs", + "import_parse_error_1.mjs", + "import_parse_error_2.mjs", + "import_parse_error_3.mjs", "exportA1.mjs", "exportA2.mjs", "export_ambiguous.mjs", @@ -35,6 +39,7 @@ support-files = [ "module_multiLargeImports.mjs", "no_export.mjs", "no_indirect_export.mjs", + "parse_error.js", "script_simple2.js", "module_large1.mjs", "module_large2.mjs", @@ -48,9 +53,13 @@ support-files = [ ["test_cyclicImport.html"] +["test_dynamicImportErrorMessage.html"] + ["test_dynamicImport_link_failure.html"] -["test_dynamicImportErrorMessage.html"] +["test_fetchedModuleHasErrorToRethrow.html"] + +["test_fetchedModuleHasErrorToRethrow_dynamic.html"] ["test_importIntroType.html"] diff --git a/dom/base/test/jsmodules/import_parse_error.mjs b/dom/base/test/jsmodules/import_parse_error.mjs @@ -0,0 +1,2 @@ +/* eslint-disable import/named */ +import { foo } from "./parse_error.js"; diff --git a/dom/base/test/jsmodules/import_parse_error_1.mjs b/dom/base/test/jsmodules/import_parse_error_1.mjs @@ -0,0 +1,2 @@ +/* eslint-disable import/named */ +import { foo } from "./import_parse_error_2.mjs"; diff --git a/dom/base/test/jsmodules/import_parse_error_2.mjs b/dom/base/test/jsmodules/import_parse_error_2.mjs @@ -0,0 +1,2 @@ +/* eslint-disable import/named */ +import { foo } from "./import_parse_error_3.mjs"; diff --git a/dom/base/test/jsmodules/import_parse_error_3.mjs b/dom/base/test/jsmodules/import_parse_error_3.mjs @@ -0,0 +1,2 @@ +/* eslint-disable import/named */ +import { foo } from "./import_parse_error.mjs"; diff --git a/dom/base/test/jsmodules/parse_error.js b/dom/base/test/jsmodules/parse_error.js @@ -0,0 +1 @@ +const foo;=1 diff --git a/dom/base/test/jsmodules/test_fetchedModuleHasErrorToRethrow.html b/dom/base/test/jsmodules/test_fetchedModuleHasErrorToRethrow.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Test an imported module has an error to rethrow</title> +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +<script> + SimpleTest.waitForExplicitFinish(); + + let count = 0; + let hasErrorToRethrow = false; + window.onerror = (e) => { + hasErrorToRethrow = true; + count++; + }; + + function testLoaded() { + is(hasErrorToRethrow, true, "hasErrorToRethrow should be set to true"); + is(count, 2, "window.onerror should be called twice"); + SimpleTest.finish(); + } +</script> +<script type="module" src="./import_parse_error.mjs"></script> +<script type="module" src="./import_parse_error_1.mjs"></script> +<body onload='testLoaded()'></body> diff --git a/dom/base/test/jsmodules/test_fetchedModuleHasErrorToRethrow_dynamic.html b/dom/base/test/jsmodules/test_fetchedModuleHasErrorToRethrow_dynamic.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Test an imported module has an error to rethrow when import()</title> +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +<script type="module" src="./import_parse_error.mjs"></script> +<script> + SimpleTest.waitForExplicitFinish(); + + let hasErrorToRethrow = false; + window.onerror = (e) => { + hasErrorToRethrow = true; + }; + + function testLoaded() { + is(hasErrorToRethrow, true, "hasErrorToRethrow should be set to true"); + import("./import_parse_error_3.mjs").then(() => { + ok(false, "Should have thrown SyntaxError"); + }).catch((e) => { + ok(e instanceof SyntaxError , "Should throw a SyntaxError"); + SimpleTest.finish(); + }); + } +</script> +<body onload='testLoaded()'></body> diff --git a/eslint-ignores.config.mjs b/eslint-ignores.config.mjs @@ -159,6 +159,7 @@ export default [ "dom/base/test/jsmodules/module_badSyntax.mjs", "dom/base/test/jsmodules/test_syntaxErrorInline.html", "dom/base/test/jsmodules/test_syntaxErrorInlineAsync.html", + "dom/base/test/jsmodules/parse_error.js", "dom/base/test/test_bug687859.html", "dom/media/webrtc/tests/mochitests/identity/idp-bad.js", "dom/security/test/general/file_nonscript.json", diff --git a/js/loader/ModuleLoadRequest.cpp b/js/loader/ModuleLoadRequest.cpp @@ -74,12 +74,7 @@ nsIGlobalObject* ModuleLoadRequest::GetGlobalObject() { } bool ModuleLoadRequest::IsErrored() const { - if (!mModuleScript || mErroredLoadingImports) { - return true; - } - - MOZ_ASSERT_IF(mModuleScript->HasErrorToRethrow(), !IsDynamicImport()); - return mModuleScript->HasParseError() || mModuleScript->HasErrorToRethrow(); + return !mModuleScript || mModuleScript->HasParseError(); } void ModuleLoadRequest::SetReady() { @@ -134,7 +129,14 @@ void ModuleLoadRequest::ModuleErrored() { } MOZ_ASSERT(!IsFinished()); - MOZ_ASSERT(IsErrored()); + + mozilla::DebugOnly<bool> hasRethrow = + mModuleScript && mModuleScript->HasErrorToRethrow(); + MOZ_ASSERT_IF(hasRethrow, !IsDynamicImport()); + + // When LoadRequestedModules fails, we will set error to rethrow to the module + // script or call SetErroredLoadingImports() and then call ModuleErrored(). + MOZ_ASSERT(IsErrored() || hasRethrow || mErroredLoadingImports); if (IsFinished()) { // Cancelling an outstanding import will error this request. diff --git a/js/loader/ModuleLoaderBase.cpp b/js/loader/ModuleLoaderBase.cpp @@ -770,6 +770,15 @@ nsresult ModuleLoaderBase::OnFetchComplete(ModuleLoadRequest* aRequest, bool success = bool(aRequest->mModuleScript); MOZ_ASSERT(NS_SUCCEEDED(rv) == success); + // TODO: https://github.com/whatwg/html/issues/11755 + // Should we check if the module script has an error to rethrow as well? + // + // https://html.spec.whatwg.org/#hostloadimportedmodule:fetch-a-single-imported-module-script + // Step 14. onSingleFetchComplete: step 2, and step 3 + // return ThrowCompletion if moduleScript is null or its parse error is not + // null: + // Step 4. Otherwise, set completion to NormalCompletion(moduleScript's + // record). if (!aRequest->IsErrored()) { OnFetchSucceeded(aRequest); } else {