diff --git a/Tests/LibWeb/Text/expected/HTML/WindowOrWorkerGlobalScope-reportError.txt b/Tests/LibWeb/Text/expected/HTML/WindowOrWorkerGlobalScope-reportError.txt new file mode 100644 index 00000000000..9d7a5126046 --- /dev/null +++ b/Tests/LibWeb/Text/expected/HTML/WindowOrWorkerGlobalScope-reportError.txt @@ -0,0 +1,5 @@ +message = Reporting an Error! +filename = +lineno = 0 +colno = 0 +error = Error: Reporting an Error! diff --git a/Tests/LibWeb/Text/input/HTML/WindowOrWorkerGlobalScope-reportError.html b/Tests/LibWeb/Text/input/HTML/WindowOrWorkerGlobalScope-reportError.html new file mode 100644 index 00000000000..64cf70b730a --- /dev/null +++ b/Tests/LibWeb/Text/input/HTML/WindowOrWorkerGlobalScope-reportError.html @@ -0,0 +1,16 @@ + + diff --git a/Userland/Libraries/LibWeb/HTML/Window.h b/Userland/Libraries/LibWeb/HTML/Window.h index 44422e4d9f4..51ab20ced3c 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.h +++ b/Userland/Libraries/LibWeb/HTML/Window.h @@ -64,6 +64,7 @@ public: using WindowOrWorkerGlobalScopeMixin::create_image_bitmap; using WindowOrWorkerGlobalScopeMixin::fetch; using WindowOrWorkerGlobalScopeMixin::queue_microtask; + using WindowOrWorkerGlobalScopeMixin::report_error; using WindowOrWorkerGlobalScopeMixin::set_interval; using WindowOrWorkerGlobalScopeMixin::set_timeout; using WindowOrWorkerGlobalScopeMixin::structured_clone; diff --git a/Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp b/Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp index 128fcfa6a6b..ecbb0ec8b71 100644 --- a/Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp +++ b/Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -742,4 +743,92 @@ JS::NonnullGCPtr WindowOrWorkerGlobalScopeMixin::supported_entry_typ return *m_supported_entry_types_array; } +// https://html.spec.whatwg.org/multipage/webappapis.html#dom-reporterror +void WindowOrWorkerGlobalScopeMixin::report_error(JS::Value e) +{ + auto& target = static_cast(this_impl()); + auto& realm = relevant_realm(target); + auto& vm = realm.vm(); + auto script_or_module = vm.get_active_script_or_module(); + + // FIXME: Get the current position in the script. + auto line = 0; + auto col = 0; + + // 1. If target is in error reporting mode, then return; the error is not handled. + if (m_error_reporting_mode) { + report_exception_to_console(e, realm, ErrorInPromise::No); + return; + } + + // 2. Let target be in error reporting mode. + m_error_reporting_mode = true; + + // 3. Let message be an implementation-defined string describing the error in a helpful manner. + auto message = [&] { + if (e.is_object()) { + auto& object = e.as_object(); + if (MUST(object.has_own_property(vm.names.message))) { + auto message = object.get_without_side_effects(vm.names.message); + return message.to_string_without_side_effects(); + } + } + + return MUST(String::formatted("Uncaught exception: {}", e.to_string_without_side_effects())); + }(); + + // 4. Let errorValue be the value that represents the error: in the case of an uncaught exception, + // that would be the value that was thrown; in the case of a JavaScript error that would be an Error object + // If there is no corresponding value, then the null value must be used instead. + auto error_value = e; + + // 5. Let urlString be the result of applying the URL serializer to the URL record that corresponds to the resource from which script was obtained. + // FIXME: Use the URL of the current running script. + auto url_string = String {}; + + // 6. If script is a classic script and script's muted errors is true, then set message to "Script error.", + // urlString to the empty string, line and col to 0, and errorValue to null. + script_or_module.visit( + [&](const JS::NonnullGCPtr& js_script) { + if (verify_cast(js_script->host_defined())->muted_errors() == ClassicScript::MutedErrors::Yes) { + message = "Script error."_string; + url_string = String {}; + line = 0; + col = 0; + error_value = JS::js_null(); + } + }, + [](auto const&) {}); + + // 7. Let notHandled be true. + auto not_handled = true; + + // 8. If target implements EventTarget, then set notHandled to the result of firing an event named error at target, + // using ErrorEvent, with the cancelable attribute initialized to true, the message attribute initialized to message, + // the filename attribute initialized to urlString, the lineno attribute initialized to line, the colno attribute initialized to col, + // and the error attribute initialized to errorValue. + ErrorEventInit event_init = {}; + event_init.cancelable = true; + event_init.message = message; + event_init.filename = url_string; + event_init.lineno = line; + event_init.colno = col; + event_init.error = error_value; + + not_handled = target.dispatch_event(ErrorEvent::create(realm, EventNames::error, event_init)); + + // 9. Let target no longer be in error reporting mode. + m_error_reporting_mode = false; + + // 10. If notHandled is false, then the error is handled. Otherwise, the error is not handled. + if (not_handled) { + // When the user agent is to report an exception E, the user agent must report the error for the relevant script, + // with the problematic position (line number and column number) in the resource containing the script, + // using the global object specified by the script's settings object as the target. + // If the error is still not handled after this, then the error may be reported to a developer console. + // https://html.spec.whatwg.org/multipage/webappapis.html#report-the-exception + report_exception_to_console(e, realm, ErrorInPromise::No); + } +} + } diff --git a/Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h b/Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h index 5dbdd307e33..2e008ac107c 100644 --- a/Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h +++ b/Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h @@ -76,6 +76,8 @@ public: JS::NonnullGCPtr indexed_db(); + void report_error(JS::Value e); + protected: void initialize(JS::Realm&); void visit_edges(JS::Cell::Visitor&); @@ -114,6 +116,8 @@ private: JS::GCPtr m_indexed_db; mutable JS::GCPtr m_supported_entry_types_array; + + bool m_error_reporting_mode { false }; }; } diff --git a/Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.idl b/Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.idl index 36a47862683..6ec57d8596a 100644 --- a/Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.idl +++ b/Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.idl @@ -17,7 +17,8 @@ interface mixin WindowOrWorkerGlobalScope { readonly attribute boolean isSecureContext; readonly attribute boolean crossOriginIsolated; - [FIXME] undefined reportError(any e); + // https://html.spec.whatwg.org/multipage/webappapis.html#dom-reporterror + undefined reportError(any e); // base64 utility methods DOMString btoa(DOMString data);