Commit 077d63da authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[blink] Fix top-level-await error reporting

- Enable tests with TLA enabled
- Fix top-level error reporting with TLA

Based on the discussions on the spec, we fire error events on rejection
of the result promise:
https://github.com/whatwg/html/pull/4352#discussion_r435178136

Bug: 1022182, 1096455c, 1127215, v8:9344
Change-Id: I16e83cb4e279c1e44be7fa70a51a103ee94aacc3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2228881
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: default avatarHiroshige Hayashizaki <hiroshige@chromium.org>
Reviewed-by: default avatarKouhei Ueno <kouhei@chromium.org>
Reviewed-by: default avatarDominic Farolino <dom@chromium.org>
Reviewed-by: default avatarDomenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#806593}
parent d7d7da83
......@@ -56,9 +56,12 @@ v8::Local<v8::Value> ModuleEvaluationResult::GetException() const {
ScriptPromise ModuleEvaluationResult::GetPromise(
ScriptState* script_state) const {
DCHECK(base::FeatureList::IsEnabled(features::kTopLevelAwait));
DCHECK(IsSuccess());
DCHECK(!value_.IsEmpty());
return ScriptPromise(script_state, value_);
if (IsSuccess()) {
return ScriptPromise(script_state, value_);
} else {
return ScriptPromise::Reject(script_state, value_);
}
}
ModuleRecordProduceCacheData::ModuleRecordProduceCacheData(
......
......@@ -8,6 +8,7 @@
#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/bindings/core/v8/module_record.h"
#include "third_party/blink/renderer/bindings/core/v8/script_function.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
......@@ -48,6 +49,24 @@ ModulatorImplBase::ModulatorImplBase(ScriptState* script_state)
ModulatorImplBase::~ModulatorImplBase() {}
class ModuleEvaluationRejectionCallback final : public ScriptFunction {
public:
explicit ModuleEvaluationRejectionCallback(ScriptState* script_state)
: ScriptFunction(script_state) {}
static v8::Local<v8::Function> CreateFunction(ScriptState* script_state) {
ModuleEvaluationRejectionCallback* self =
MakeGarbageCollected<ModuleEvaluationRejectionCallback>(script_state);
return self->BindToV8Function();
}
private:
ScriptValue Call(ScriptValue value) override {
ModuleRecord::ReportException(GetScriptState(), value.V8Value());
return ScriptValue();
}
};
bool ModulatorImplBase::IsScriptingDisabled() const {
return !GetExecutionContext()->CanExecuteScripts(kAboutToExecuteScript);
}
......@@ -351,15 +370,17 @@ ModuleEvaluationResult ModulatorImplBase::ExecuteModule(
v8::MicrotasksScope::kRunMicrotasks);
ScriptState::EscapableScope scope(script_state_);
// <spec step="5">Let evaluationStatus be null.</spec>
//
// |result| corresponds to "evaluationStatus of [[Type]]: throw".
// Without TLA: <spec step="5">Let evaluationStatus be null.</spec>
ModuleEvaluationResult result = ModuleEvaluationResult::Empty();
// <spec step="6">If script's error to rethrow is not null, ...</spec>
if (module_script->HasErrorToRethrow()) {
// <spec step="6">... then set evaluationStatus to Completion { [[Type]]:
// throw, [[Value]]: script's error to rethrow, [[Target]]: empty }.</spec>
// Without TLA: <spec step="6">... then set evaluationStatus to Completion
// { [[Type]]: throw, [[Value]]: script's error to rethrow,
// [[Target]]: empty }.</spec>
// With TLA: <spec step="5">If script's error to rethrow is not null,
// then let valuationPromise be a promise rejected with script's error
// to rethrow.</spec>
result = ModuleEvaluationResult::FromException(
module_script->CreateErrorToRethrow().V8Value());
} else {
......@@ -387,25 +408,36 @@ ModuleEvaluationResult ModulatorImplBase::ExecuteModule(
}
}
// <spec step="8">If evaluationStatus is an abrupt completion, then:</spec>
if (result.IsException()) {
// <spec step="8.1">If rethrow errors is true, rethrow the exception given
// by evaluationStatus.[[Value]].</spec>
if (capture_error == CaptureEvalErrorFlag::kCapture)
return result.Escape(&scope);
// <spec step="8.2">Otherwise, report the exception given by
// evaluationStatus.[[Value]] for script.</spec>
ModuleRecord::ReportException(script_state_, result.GetException());
}
// <spec step="9">Clean up after running script with settings.</spec>
// - Partially implemented in MicrotaskScope destructor and the
// - ScriptState::EscapableScope destructor.
if (base::FeatureList::IsEnabled(features::kTopLevelAwait))
if (base::FeatureList::IsEnabled(features::kTopLevelAwait)) {
if (capture_error == CaptureEvalErrorFlag::kReport) {
// <spec step="7"> If report errors is true, then upon rejection of
// evaluationPromise with reason, report the exception given by reason
// for script.</spec>
v8::Local<v8::Function> callback_failure =
ModuleEvaluationRejectionCallback::CreateFunction(script_state_);
// Add a rejection handler to report back errors once the result promise
// is rejected.
result.GetPromise(script_state_)
.Then(v8::Local<v8::Function>(), callback_failure);
}
return result.Escape(&scope);
else
} else {
// <spec step="8">If evaluationStatus is an abrupt completion, then:</spec>
if (result.IsException()) {
// <spec step="8.1">If rethrow errors is true, rethrow the exception given
// by evaluationStatus.[[Value]].</spec>
if (capture_error == CaptureEvalErrorFlag::kCapture)
return result.Escape(&scope);
// <spec step="8.2">Otherwise, report the exception given by
// evaluationStatus.[[Value]] for script.</spec>
ModuleRecord::ReportException(script_state_, result.GetException());
}
// <spec step="8">Clean up after running script with settings.</spec>
// - Partially implement in MicrotaskScope destructor and the
// - ScriptState::EscapableScope destructor.
return ModuleEvaluationResult::Empty();
}
}
void ModulatorImplBase::Trace(Visitor* visitor) const {
......
......@@ -1624,10 +1624,17 @@ crbug.com/441840 external/wpt/css/css-shapes/shape-outside/values/shape-outside-
crbug.com/441840 external/wpt/css/css-shapes/shape-outside/values/shape-outside-polygon-004.html [ Failure ]
crbug.com/441840 [ Win ] external/wpt/css/css-shapes/shape-outside/values/shape-outside-shape-arguments-000.html [ Failure ]
crbug.com/1022182 external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import.html [ Failure ]
crbug.com/1022182 external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed.html [ Timeout ]
crbug.com/1022182 external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-immediate-promise.html [ Timeout ]
crbug.com/1022182 external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise.html [ Timeout ]
# Fix once top-level-await is enabled
crbug.com/1022182 external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-4-tla.html [ Skip ]
crbug.com/1022182 external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed.html [ Skip ]
crbug.com/1022182 external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-immediate-promise.html [ Skip ]
crbug.com/1022182 external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import.html [ Skip ]
crbug.com/1022182 external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise.html [ Skip ]
crbug.com/1022182 virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-4-tla.html [ Pass ]
crbug.com/1022182 virtual/module-top-level-await/external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed.html [ Pass ]
crbug.com/1022182 virtual/module-top-level-await/external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-immediate-promise.html [ Pass ]
crbug.com/1022182 virtual/module-top-level-await/external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import.html [ Pass ]
crbug.com/1022182 virtual/module-top-level-await/external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise.html [ Pass ]
crbug.com/676270 [ Mac ] external/wpt/css/css-text/hyphens/hyphens-auto-001.html [ Failure ]
crbug.com/1022415 [ Mac ] external/wpt/css/css-text/hyphens/hyphens-auto-010.html [ Failure ]
......
[
{
"prefix": "module-top-level-await",
"bases": [],
"bases": [
"external/wpt/html/webappapis/dynamic-markup-insertion/document-write",
"external/wpt/html/semantics/scripting-1/the-script-element/module",
"fast/dom/HTMLScriptElement/module-script.html",
"fast/dom/script-module-with-export-leak.html",
"fast/loader/invalid-module-specifier.html",
"http/tests/devtools/isolated-code-cache/"
],
"args": ["--enable-features=TopLevelAwait"]
},
{
......
......@@ -9,6 +9,7 @@
window.log = [];
window.addEventListener("error", ev => log.push(ev.error));
window.addEventListener("onunhandledrejection", unreachable);
const test_load = async_test(
"Parse errors in different files should be reported " +
......
......@@ -9,6 +9,7 @@
window.log = [];
window.addEventListener("error", ev => log.push(ev.error));
window.addEventListener("onunhandledrejection", unreachable);
const test_load = async_test(
"Instantiation errors in different files should be reported " +
......
......@@ -9,6 +9,7 @@
window.log = [];
window.addEventListener("error", ev => log.push(ev.error));
window.addEventListener("onunhandledrejection", unreachable);
const test_load = async_test(
"Evaluation errors are cached in intermediate module scripts");
......
......@@ -9,6 +9,7 @@
window.log = [];
window.addEventListener("error", ev => log.push(ev.error));
window.addEventListener("onunhandledrejection", unreachable);
const test_load = async_test(
"network error has higher priority than parse error");
......
......@@ -9,6 +9,7 @@
window.log = [];
window.addEventListener("error", ev => log.push(ev.error));
window.addEventListener("onunhandledrejection", unreachable);
const test_load = async_test(
"parse error has higher priority than instantiation error");
......
......@@ -9,6 +9,7 @@
window.log = [];
window.addEventListener("error", ev => log.push(ev.error));
window.addEventListener("onunhandledrejection", unreachable);
const test_load = async_test(
"instantiation error has higher priority than evaluation error");
......
<!DOCTYPE html>
<title>Testing evaluation order</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
setup({allow_uncaught_exception: true});
window.log = [];
window.addEventListener("error", ev => testDone());
window.addEventListener("onunhandledrejection", unreachable);
const test_load = async_test("Test evaluation order of modules");
window.addEventListener("load", ev => log.push("window-load"));
function unreachable() { log.push("unexpected"); }
function testDone() {
test_load.step(() => {
assert_array_equals(log, [
"step-4.1-1", "step-4.1-2", "microtask-4.1",
"script-load", "window-load",
"step-4.2-1", "step-4.2-2", "microtask-4.2",
]);
});
test_load.done();
}
</script>
<script type="module" src="evaluation-order-4.1.mjs"
onerror="unreachable()" onload="log.push('script-load')">
</script>
log.push("step-4.1-1");
queueMicrotask(() => log.push("microtask-4.1"));
log.push("step-4.1-2");
await import("./evaluation-order-4.2.mjs");
// Not happening as we throw in the above module.
log.push("step-4.1-3");
log.push("step-4.2-1");
queueMicrotask(() => log.push("microtask-4.2"));
log.push("step-4.2-2");
throw new Error("error");
......@@ -10,7 +10,9 @@ async_test(t => {
const iframe = document.createElement("iframe");
iframe.onunhandledrejection = t.unreached_func("Unhandled promise rejection detected");
iframe.onerror = t.unreached_func("Error loading iframe");
let onLoadWasCalled = false;
iframe.onload = t.step_func(() => {
assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
......
......@@ -6,17 +6,20 @@
<script>
async_test(t => {
const iframe = document.createElement("iframe");
iframe.onerror = t.unreached_func("Error loading iframe");
let testEndWasCalled = false;
document.addEventListener("documentWriteDone", t.step_func(() => {
assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
testEndWasCalled = true;
}));
iframe.onload = t.step_func_done(() => {
assert_true(testEndWasCalled, 'documentWriteDone event was not sent');
let onLoadWasCalled = false;
iframe.onload = t.step_func(() => {
onLoadWasCalled = true;
assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
// Don't call the event handler another time after document.write.
iframe.onload = null;
});
document.addEventListener("documentWriteDone", t.step_func_done(() => {
assert_true(onLoadWasCalled, "onload must be called");
assert_equals(iframe.contentDocument.body.textContent, "document.write body contents\n");
}));
iframe.src = "module-tla-import-iframe.html";
document.body.appendChild(iframe);
......
<!doctype html>
<script type=module>
await new Promise(resolve => {
window.parent.document.test.step_timeout(resolve, 0));
window.parent.document.test.step_timeout(resolve, 0);
document.write("document.write body contents\n");
document.close();
window.parent.document.dispatchEvent(new CustomEvent("documentWriteDone"));
});
</script>
Initial body contents
......@@ -9,18 +9,14 @@ async_test(t => {
document.test = t;
const iframe = document.createElement("iframe");
iframe.onerror = t.unreached_func("Error loading iframe");
let onLoadWasCalled = false;
iframe.onload = t.step_func(() => {
assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
iframe.onload = null;
onLoadWasCalled = true;
});
document.addEventListener("documentWriteDone", t.step_func_done(() => {
assert_true(onLoadWasCalled);
document.addEventListener("documentWriteDone", t.step_func(() => {
assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
}));
iframe.onload = t.step_func_done(() => {
assert_equals(iframe.contentDocument.body.textContent, "Initial body contents\n");
});
iframe.src = "module-tla-promise-iframe.html";
document.body.appendChild(iframe);
......
This is a testharness.js-based test.
FAIL Test evaluation order of modules assert_array_equals: expected property 2 to be "global-error" but got "microtask" (expected array ["step-1-1", "step-1-2", "global-error", "error", "microtask", "script-load", "global-load"] got ["step-1-1", "step-1-2", "microtask", "global-error", "error", "script-load", "global-load"])
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Test evaluation order of modules assert_array_equals: expected property 2 to be "global-error" but got "microtask" (expected array ["step-1-1", "step-1-2", "global-error", "error", "microtask"] got ["step-1-1", "step-1-2", "microtask", "global-error", "error"])
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Test evaluation order of modules assert_array_equals: expected property 2 to be "global-error" but got "microtask-2.2" (expected array ["step-2.2-1", "step-2.2-2", "global-error", "error", "microtask-2.2", "script-load", "global-load"] got ["step-2.2-1", "step-2.2-2", "microtask-2.2", "global-error", "error", "script-load", "global-load"])
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Test evaluation order of modules assert_array_equals: expected property 2 to be "global-error" but got "microtask-2.2" (expected array ["step-2.2-1", "step-2.2-2", "global-error", "error", "microtask-2.2"] got ["step-2.2-1", "step-2.2-2", "microtask-2.2", "global-error", "error"])
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL module script in XHTML documents should be evaluated. assert_true: expected true got undefined
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL document.write in an imported module assert_equals: expected "Initial body contents\n" but got "FAIL"
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL document.write in an imported module assert_true: expected true got false
Harness: the test ran to completion.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment