From be2daabb08ce622c1b335173c4f113ab94d629c1 Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Tue, 3 Dec 2024 22:21:38 +0000 Subject: [PATCH] LibWeb: Update `run_timer_initialization_steps` to the latest spec This fixes a number of WPT tests, which expect an error to be reported if an exception is thrown in the timer callback. --- .../LibWeb/HTML/WindowOrWorkerGlobalScope.cpp | 77 ++++++++++++------- ...-cross-realm-callback-report-exception.txt | 6 ++ ...-cross-realm-callback-report-exception.txt | 6 ++ ...-cross-realm-callback-report-exception.txt | 6 ++ ...-cross-realm-callback-report-exception.txt | 6 ++ ...-cross-realm-callback-report-exception.txt | 6 ++ .../callback-cross-realm-report-exception.txt | 6 ++ .../callback-cross-realm-report-exception.txt | 6 ++ ...cross-realm-callback-report-exception.html | 32 ++++++++ ...cross-realm-callback-report-exception.html | 29 +++++++ ...cross-realm-callback-report-exception.html | 28 +++++++ ...cross-realm-callback-report-exception.html | 31 ++++++++ ...cross-realm-callback-report-exception.html | 27 +++++++ ...callback-cross-realm-report-exception.html | 30 ++++++++ ...callback-cross-realm-report-exception.html | 30 ++++++++ 15 files changed, 299 insertions(+), 27 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/wpt-import/dom/nodes/MutationObserver-cross-realm-callback-report-exception.txt create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/embedded-content/the-canvas-element/toBlob-cross-realm-callback-report-exception.txt create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.txt create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.txt create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.txt create mode 100644 Tests/LibWeb/Text/expected/wpt-import/intersection-observer/callback-cross-realm-report-exception.txt create mode 100644 Tests/LibWeb/Text/expected/wpt-import/resize-observer/callback-cross-realm-report-exception.txt create mode 100644 Tests/LibWeb/Text/input/wpt-import/dom/nodes/MutationObserver-cross-realm-callback-report-exception.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/embedded-content/the-canvas-element/toBlob-cross-realm-callback-report-exception.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/intersection-observer/callback-cross-realm-report-exception.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/resize-observer/callback-cross-realm-report-exception.html diff --git a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp index 137cf034441f9..7b66fdfc97832 100644 --- a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp +++ b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp @@ -245,7 +245,7 @@ i32 WindowOrWorkerGlobalScopeMixin::run_timer_initialization_steps(TimerHandler { // 1. Let thisArg be global if that is a WorkerGlobalScope object; otherwise let thisArg be the WindowProxy that corresponds to global. - // 2. If previousId was given, let id be previousId; otherwise, let id be an implementation-defined integer that is greater than zero and does not already exist in global's map of active timers. + // 2. If previousId was given, let id be previousId; otherwise, let id be an implementation-defined integer that is greater than zero and does not already exist in global's map of setTimeout and setInterval IDs. auto id = previous_id.has_value() ? previous_id.value() : m_timer_id_allocator.allocate(); // FIXME: 3. If the surrounding agent's event loop's currently running task is a task that was created by this algorithm, then let nesting level be the task's timer nesting level. Otherwise, let nesting level be zero. @@ -255,42 +255,60 @@ i32 WindowOrWorkerGlobalScopeMixin::run_timer_initialization_steps(TimerHandler timeout = 0; // FIXME: 5. If nesting level is greater than 5, and timeout is less than 4, then set timeout to 4. - - // 6. Let callerRealm be the current Realm Record, and calleeRealm be global's relevant Realm. - // FIXME: Implement this when step 9.3.2 is implemented. + // FIXME: 6. Let realm be global's relevant realm. // 7. Let initiating script be the active script. auto const* initiating_script = Web::Bindings::active_script(); auto& vm = this_impl().vm(); - // 8. Let task be a task that runs the following substeps: - auto task = GC::create_function(vm.heap(), Function([this, handler = move(handler), timeout, arguments = move(arguments), repeat, id, initiating_script]() { - // 1. If id does not exist in global's map of active timers, then abort these steps. + // FIXME 8. Let uniqueHandle be null. + + // 9. Let task be a task that runs the following substeps: + auto task = GC::create_function(vm.heap(), Function([this, handler = move(handler), timeout, arguments = move(arguments), repeat, id, initiating_script, previous_id]() { + // FIXME: 1. Assert: uniqueHandle is a unique internal value, not null. + + // 2. If id does not exist in global's map of setTimeout and setInterval IDs, then abort these steps. if (!m_timers.contains(id)) return; + // FIXME: 3. If global's map of setTimeout and setInterval IDs[id] does not equal uniqueHandle, then abort these steps. + // FIXME: 4. Record timing info for timer handler given handler, global's relevant settings object, and repeat. + handler.visit( - // 2. If handler is a Function, then invoke handler given arguments with the callback this value set to thisArg. If this throws an exception, catch it, and report the exception. + // 5. If handler is a Function, then invoke handler given arguments and "report", and with callback this value set to thisArg. [&](GC::Root const& callback) { - if (auto result = WebIDL::invoke_callback(*callback, &this_impl(), arguments); result.is_error()) - report_exception(result, this_impl().realm()); + (void)WebIDL::invoke_callback(*callback, &this_impl(), WebIDL::ExceptionBehavior::Report, arguments); }, - // 3. Otherwise: + // 6. Otherwise: [&](String const& source) { - // 1. Assert: handler is a string. - // FIXME: 2. Perform HostEnsureCanCompileStrings(callerRealm, calleeRealm). If this throws an exception, catch it, report the exception, and abort these steps. + // 1. If previousId was not given: + if (!previous_id.has_value()) { + // 1. Let globalName be "Window" if global is a Window object; "Worker" otherwise. + auto global_name = is(this_impl()) ? "Window"sv : "Worker"sv; + + // 2. Let methodName be "setInterval" if repeat is true; "setTimeout" otherwise. + auto method_name = repeat == Repeat::Yes ? "setInterval"sv : "setTimeout"sv; + + // 3. Let sink be a concatenation of globalName, U+0020 SPACE, and methodName. + [[maybe_unused]] auto sink = String::formatted("{} {}", global_name, method_name); - // 3. Let settings object be global's relevant settings object. + // FIXME: 4. Set handler to the result of invoking the Get Trusted Type compliant string algorithm with TrustedScript, global, handler, sink, and "script". + } + + // FIXME: 2. Assert: handler is a string. + // FIXME: 3. Perform EnsureCSPDoesNotBlockStringCompilation(realm, « », handler, handler, timer, « », handler). If this throws an exception, catch it, report it for global, and abort these steps. + + // 4. Let settings object be global's relevant settings object. auto& settings_object = relevant_settings_object(this_impl()); - // 4. Let fetch options be the default classic script fetch options. + // 5. Let fetch options be the default classic script fetch options. ScriptFetchOptions options {}; - // 5. Let base URL be settings object's API base URL. + // 6. Let base URL be settings object's API base URL. auto base_url = settings_object.api_base_url(); - // 6. If initiating script is not null, then: + // 7. If initiating script is not null, then: if (initiating_script) { // FIXME: 1. Set fetch options to a script fetch options whose cryptographic nonce is initiating script's fetch options's cryptographic nonce, // integrity metadata is the empty string, parser metadata is "not-parser-inserted", credentials mode is initiating script's fetch @@ -303,36 +321,38 @@ i32 WindowOrWorkerGlobalScopeMixin::run_timer_initialization_steps(TimerHandler // done by eval(). That is, module script fetches via import() will behave the same in both contexts. } - // 7. Let script be the result of creating a classic script given handler, realm, base URL, and fetch options. + // 8. Let script be the result of creating a classic script given handler, realm, base URL, and fetch options. // FIXME: Pass fetch options. auto basename = base_url.basename(); auto script = ClassicScript::create(basename, source, this_impl().realm(), move(base_url)); - // 8. Run the classic script script. + // 9. Run the classic script script. (void)script->run(); }); - // 4. If id does not exist in global's map of active timers, then abort these steps. + // 7. If id does not exist in global's map of setTimeout and setInterval IDs, then abort these steps. if (!m_timers.contains(id)) return; + // FIXME: 8. If global's map of setTimeout and setInterval IDs[id] does not equal uniqueHandle, then abort these steps. + switch (repeat) { - // 5. If repeat is true, then perform the timer initialization steps again, given global, handler, timeout, arguments, true, and id. + // 9. If repeat is true, then perform the timer initialization steps again, given global, handler, timeout, arguments, true, and id. case Repeat::Yes: run_timer_initialization_steps(handler, timeout, move(arguments), repeat, id); break; - // 6. Otherwise, remove global's map of active timers[id]. + // 10. Otherwise, remove global's map of active timers[id]. case Repeat::No: m_timers.remove(id); break; } })); - // FIXME: 9. Increment nesting level by one. - // FIXME: 10. Set task's timer nesting level to nesting level. + // FIXME: 10. Increment nesting level by one. + // FIXME: 11. Set task's timer nesting level to nesting level. - // 11. Let completionStep be an algorithm step which queues a global task on the timer task source given global to run task. + // 12. Let completionStep be an algorithm step which queues a global task on the timer task source given global to run task. Function completion_step = [this, task = move(task)]() mutable { queue_global_task(Task::Source::TimerTask, this_impl(), GC::create_function(this_impl().heap(), [this, task] { HTML::TemporaryExecutionContext execution_context { this_impl().realm(), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes }; @@ -340,10 +360,13 @@ i32 WindowOrWorkerGlobalScopeMixin::run_timer_initialization_steps(TimerHandler })); }; - // 12. Run steps after a timeout given global, "setTimeout/setInterval", timeout, completionStep, and id. + // 13. Set uniqueHandle to the result of running steps after a timeout given global, "setTimeout/setInterval", timeout, completionStep. + // FIXME: run_steps_after_a_timeout() needs to be updated to return a unique internal value that can be used here. run_steps_after_a_timeout_impl(timeout, move(completion_step), id); - // 13. Return id. + // FIXME: 14. Set global's map of setTimeout and setInterval IDs[id] to uniqueHandle. + + // 15. Return id. return id; } diff --git a/Tests/LibWeb/Text/expected/wpt-import/dom/nodes/MutationObserver-cross-realm-callback-report-exception.txt b/Tests/LibWeb/Text/expected/wpt-import/dom/nodes/MutationObserver-cross-realm-callback-report-exception.txt new file mode 100644 index 0000000000000..7348cd8846477 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/dom/nodes/MutationObserver-cross-realm-callback-report-exception.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass MutationObserver reports the exception from its callback in the callback's global object \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/embedded-content/the-canvas-element/toBlob-cross-realm-callback-report-exception.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/embedded-content/the-canvas-element/toBlob-cross-realm-callback-report-exception.txt new file mode 100644 index 0000000000000..b03164168bb55 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/embedded-content/the-canvas-element/toBlob-cross-realm-callback-report-exception.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass toBlob() reports the exception from its callback in the callback's global object \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.txt b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.txt new file mode 100644 index 0000000000000..1519c3376f638 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass queueMicrotask() reports the exception from its callback in the callback's global object \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.txt b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.txt new file mode 100644 index 0000000000000..bf3e0cdcbce1d --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass window.setInterval() reports the exception from its callback in the callback's global object \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.txt b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.txt new file mode 100644 index 0000000000000..030fced17aeb9 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass window.setTimeout() reports the exception from its callback in the callback's global object \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/intersection-observer/callback-cross-realm-report-exception.txt b/Tests/LibWeb/Text/expected/wpt-import/intersection-observer/callback-cross-realm-report-exception.txt new file mode 100644 index 0000000000000..0047e60d5b4fe --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/intersection-observer/callback-cross-realm-report-exception.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass IntersectionObserver reports the exception from its callback in the callback's global object \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/resize-observer/callback-cross-realm-report-exception.txt b/Tests/LibWeb/Text/expected/wpt-import/resize-observer/callback-cross-realm-report-exception.txt new file mode 100644 index 0000000000000..3ee00764f5149 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/resize-observer/callback-cross-realm-report-exception.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass ResizeObserver reports the exception from its callback in the callback's global object \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/dom/nodes/MutationObserver-cross-realm-callback-report-exception.html b/Tests/LibWeb/Text/input/wpt-import/dom/nodes/MutationObserver-cross-realm-callback-report-exception.html new file mode 100644 index 0000000000000..946c795bd34bc --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/dom/nodes/MutationObserver-cross-realm-callback-report-exception.html @@ -0,0 +1,32 @@ + + +MutationObserver reports the exception from its callback in the callback's global object + + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/embedded-content/the-canvas-element/toBlob-cross-realm-callback-report-exception.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/embedded-content/the-canvas-element/toBlob-cross-realm-callback-report-exception.html new file mode 100644 index 0000000000000..9e7faad6ff9d0 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/embedded-content/the-canvas-element/toBlob-cross-realm-callback-report-exception.html @@ -0,0 +1,29 @@ + + +toBlob() reports the exception from its callback in the callback's global object + + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.html new file mode 100644 index 0000000000000..9c1113eab8a91 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.html @@ -0,0 +1,28 @@ + + +queueMicrotask() reports the exception from its callback in the callback's global object + + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.html new file mode 100644 index 0000000000000..165184cbbf42f --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.html @@ -0,0 +1,31 @@ + + +window.setInterval() reports the exception from its callback in the callback's global object + + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.html new file mode 100644 index 0000000000000..6c10af5a682ff --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.html @@ -0,0 +1,27 @@ + + +window.setTimeout() reports the exception from its callback in the callback's global object + + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/intersection-observer/callback-cross-realm-report-exception.html b/Tests/LibWeb/Text/input/wpt-import/intersection-observer/callback-cross-realm-report-exception.html new file mode 100644 index 0000000000000..f60d60327c4a5 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/intersection-observer/callback-cross-realm-report-exception.html @@ -0,0 +1,30 @@ + + +IntersectionObserver reports the exception from its callback in the callback's global object + + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/resize-observer/callback-cross-realm-report-exception.html b/Tests/LibWeb/Text/input/wpt-import/resize-observer/callback-cross-realm-report-exception.html new file mode 100644 index 0000000000000..2e21fc61fc14f --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/resize-observer/callback-cross-realm-report-exception.html @@ -0,0 +1,30 @@ + + +ResizeObserver reports the exception from its callback in the callback's global object + + + + + +