diff --git a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp index c8621bf3a81..2d8a23b3fe8 100644 --- a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp +++ b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -1248,17 +1250,16 @@ void WindowOrWorkerGlobalScopeMixin::report_an_exception(JS::Value exception, Om // 1. Set errorInfo[error] to null. error_info.error = JS::js_null(); - // FIXME: 2. If global implements DedicatedWorkerGlobalScope, queue a global task on the DOM manipulation - // task source with the global's associated Worker's relevant global object to run these steps: - if (false) { - // FIXME: 1. Let workerObject be the Worker object associated with global. - - // FIXME: 2. Set notHandled to the result of firing an event named error at workerObject, using ErrorEvent, - // with the cancelable attribute initialized to true, and additional attributes initialized - // according to errorInfo. - - // FIXME: 3. If notHandled is true, then report exception for workerObject's relevant global object with - // omitError set to true. + // 2. If global implements DedicatedWorkerGlobalScope, queue a global task on the DOM manipulation + // task source with the global's associated Worker's relevant global object to run these steps: + if (auto* dedicated_worker_global_scope = as_if(target)) { + if (auto* page = dedicated_worker_global_scope->page()) { + page->client().page_did_report_worker_exception( + error_info.message, + error_info.filename, + error_info.lineno, + error_info.colno); + } } // 3. Otherwise, the user agent may report exception to a developer console. else { diff --git a/Libraries/LibWeb/HTML/WorkerAgentParent.cpp b/Libraries/LibWeb/HTML/WorkerAgentParent.cpp index 4d4361dd22c..ab551f1f0f2 100644 --- a/Libraries/LibWeb/HTML/WorkerAgentParent.cpp +++ b/Libraries/LibWeb/HTML/WorkerAgentParent.cpp @@ -8,10 +8,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -75,6 +77,36 @@ void WorkerAgentParent::setup_worker_ipc_callbacks(JS::Realm& realm) auto response = client.request_worker_agent(worker_type); return { move(response.worker_handle), move(response.request_server_handle), move(response.image_decoder_handle) }; }; + m_worker_ipc->on_worker_exception = [self = GC::Weak { *this }](String message, String filename, u32 lineno, u32 colno) { + if (!self) + return; + // https://html.spec.whatwg.org/multipage/webappapis.html#report-an-exception + // 7.2: If global implements DedicatedWorkerGlobalScope, queue a global task on the DOM manipulation task source with the global's associated Worker's relevant global object to run these steps: + auto& outside_settings = *self->m_outside_settings; + auto worker_event_target = GC::Ref { *self->m_worker_event_target }; + queue_global_task(Task::Source::DOMManipulation, outside_settings.global_object(), GC::create_function(outside_settings.heap(), [&outside_settings, worker_event_target, message = move(message), filename = move(filename), lineno, colno]() { + // 1. Let workerObject be the Worker object associated with global. + auto& worker_object = as(*worker_event_target); + + auto& realm = outside_settings.realm(); + + // 2. Set notHandled to the result of firing an event named error at workerObject, using ErrorEvent, with the + // cancelable attribute initialized to true, and additional attributes initialized according to errorInfo. + ErrorEventInit event_init {}; + event_init.cancelable = true; + event_init.message = message; + event_init.filename = filename; + event_init.lineno = lineno; + event_init.colno = colno; + event_init.error = JS::js_null(); + auto error = ErrorEvent::create(realm, EventNames::error, event_init); + bool not_handled = worker_object.dispatch_event(error); + + // 3. If notHandled is true, then report exception for workerObject's relevant global object with omitError set to true. + if (not_handled) + as(outside_settings.global_object()).report_an_exception(error, WindowOrWorkerGlobalScopeMixin::OmitError::Yes); + })); + }; m_worker_ipc->on_worker_script_load_failure = [self = GC::Weak { *this }]() { if (!self) return; diff --git a/Libraries/LibWeb/Page/Page.h b/Libraries/LibWeb/Page/Page.h index 0b138244359..3437deebf72 100644 --- a/Libraries/LibWeb/Page/Page.h +++ b/Libraries/LibWeb/Page/Page.h @@ -469,6 +469,7 @@ public: virtual void page_did_receive_network_response_headers([[maybe_unused]] u64 request_id, [[maybe_unused]] u32 status_code, [[maybe_unused]] Optional reason_phrase, [[maybe_unused]] Vector const& response_headers) { } virtual void page_did_receive_network_response_body([[maybe_unused]] u64 request_id, [[maybe_unused]] ReadonlyBytes data) { } virtual void page_did_finish_network_request([[maybe_unused]] u64 request_id, [[maybe_unused]] u64 body_size, [[maybe_unused]] Requests::RequestTimingInfo const& timing_info, [[maybe_unused]] Optional const& network_error) { } + virtual void page_did_report_worker_exception([[maybe_unused]] String const& message, [[maybe_unused]] String const& filename, [[maybe_unused]] u32 lineno, [[maybe_unused]] u32 colno) { } struct WorkerAgentResponse { IPC::TransportHandle worker_handle; diff --git a/Libraries/LibWeb/Worker/WebWorkerClient.cpp b/Libraries/LibWeb/Worker/WebWorkerClient.cpp index 098df651643..eb71d8de56e 100644 --- a/Libraries/LibWeb/Worker/WebWorkerClient.cpp +++ b/Libraries/LibWeb/Worker/WebWorkerClient.cpp @@ -25,6 +25,12 @@ void WebWorkerClient::did_fail_loading_worker_script() on_worker_script_load_failure(); } +void WebWorkerClient::did_report_worker_exception(String message, String filename, u32 lineno, u32 colno) +{ + if (on_worker_exception) + on_worker_exception(move(message), move(filename), lineno, colno); +} + Messages::WebWorkerClient::DidRequestCookieResponse WebWorkerClient::did_request_cookie(URL::URL url, HTTP::Cookie::Source source) { if (on_request_cookie) diff --git a/Libraries/LibWeb/Worker/WebWorkerClient.h b/Libraries/LibWeb/Worker/WebWorkerClient.h index 7c8b488c999..6ceaf393e54 100644 --- a/Libraries/LibWeb/Worker/WebWorkerClient.h +++ b/Libraries/LibWeb/Worker/WebWorkerClient.h @@ -25,11 +25,13 @@ public: virtual void did_close_worker() override; virtual void did_fail_loading_worker_script() override; + virtual void did_report_worker_exception(String message, String filename, u32 lineno, u32 colno) override; virtual Messages::WebWorkerClient::DidRequestCookieResponse did_request_cookie(URL::URL, HTTP::Cookie::Source) override; virtual Messages::WebWorkerClient::RequestWorkerAgentResponse request_worker_agent(Web::Bindings::AgentType worker_type) override; Function on_worker_close; Function on_worker_script_load_failure; + Function on_worker_exception; Function on_request_cookie; Function on_request_worker_agent; diff --git a/Libraries/LibWeb/Worker/WebWorkerClient.ipc b/Libraries/LibWeb/Worker/WebWorkerClient.ipc index a4c087338da..6f3d5c22b52 100644 --- a/Libraries/LibWeb/Worker/WebWorkerClient.ipc +++ b/Libraries/LibWeb/Worker/WebWorkerClient.ipc @@ -6,6 +6,7 @@ endpoint WebWorkerClient { did_close_worker() =| did_fail_loading_worker_script() =| + did_report_worker_exception(String message, String filename, u32 lineno, u32 colno) =| did_request_cookie(URL::URL url, HTTP::Cookie::Source source) => (HTTP::Cookie::VersionedCookie cookie) request_worker_agent(Web::Bindings::AgentType worker_type) => (IPC::TransportHandle handle, IPC::TransportHandle request_server_handle, IPC::TransportHandle image_decoder_handle) } diff --git a/Services/WebWorker/PageHost.cpp b/Services/WebWorker/PageHost.cpp index d41492b5b4d..6a9c5cd4b19 100644 --- a/Services/WebWorker/PageHost.cpp +++ b/Services/WebWorker/PageHost.cpp @@ -92,6 +92,11 @@ HTTP::Cookie::VersionedCookie PageHost::page_did_request_cookie(URL::URL const& return m_client.did_request_cookie(url, source); } +void PageHost::page_did_report_worker_exception(String const& message, String const& filename, u32 lineno, u32 colno) +{ + m_client.async_did_report_worker_exception(message, filename, lineno, colno); +} + void PageHost::request_file(Web::FileRequest request) { m_client.request_file(move(request)); diff --git a/Services/WebWorker/PageHost.h b/Services/WebWorker/PageHost.h index ac6c8f0c50a..3fcaa1b7c2f 100644 --- a/Services/WebWorker/PageHost.h +++ b/Services/WebWorker/PageHost.h @@ -37,6 +37,7 @@ public: virtual Web::CSS::PreferredMotion preferred_motion() const override; virtual size_t screen_count() const override { return 1; } virtual HTTP::Cookie::VersionedCookie page_did_request_cookie(URL::URL const&, HTTP::Cookie::Source) override; + virtual void page_did_report_worker_exception(String const& message, String const& filename, u32 lineno, u32 colno) override; virtual void request_file(Web::FileRequest) override; virtual WorkerAgentResponse request_worker_agent(Web::Bindings::AgentType) override; virtual Web::DisplayListPlayerType display_list_player_type() const override { VERIFY_NOT_REACHED(); } diff --git a/Tests/LibWeb/TestConfig.ini b/Tests/LibWeb/TestConfig.ini index e16a383f5aa..dbf71833d04 100644 --- a/Tests/LibWeb/TestConfig.ini +++ b/Tests/LibWeb/TestConfig.ini @@ -5,6 +5,7 @@ Text/input/cookie-working.html ; Performs cross-origin Worker fetch Text/input/wpt-import/html/infrastructure/safe-passing-of-structured-data/structuredclone_0.html Text/input/wpt-import/workers/constructors/Worker/same-origin.html +Text/input/wpt-import/workers/Worker_ErrorEvent_type.htm Text/input/Worker/XHttpRequest-responseXML-unavailable-in-worker.html ; fetching a file only works from an HTTP server. diff --git a/Tests/LibWeb/Text/expected/wpt-import/workers/Worker_ErrorEvent_type.txt b/Tests/LibWeb/Text/expected/wpt-import/workers/Worker_ErrorEvent_type.txt new file mode 100644 index 00000000000..a01041f5b14 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/workers/Worker_ErrorEvent_type.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass AbstractWorker ErrorEvent.type \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/workers/Worker_ErrorEvent_type.htm b/Tests/LibWeb/Text/input/wpt-import/workers/Worker_ErrorEvent_type.htm new file mode 100644 index 00000000000..d65aadfc7d0 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/workers/Worker_ErrorEvent_type.htm @@ -0,0 +1,19 @@ + + AbstractWorker ErrorEvent.type + + +
+ diff --git a/Tests/LibWeb/Text/input/wpt-import/workers/support/ErrorEvent.js b/Tests/LibWeb/Text/input/wpt-import/workers/support/ErrorEvent.js new file mode 100644 index 00000000000..22ea6d4fb51 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/workers/support/ErrorEvent.js @@ -0,0 +1,10 @@ +onmessage = function(evt) +{ + throw(new Error(evt.data)); +} + +onerror = function(message, location, line, col) +{ + postMessage( {"message": message, "filename": location, "lineno": line, "colno": col} ); + return false; // "not handled" so the error propagates up to the Worker object +}