diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 5f12f001c46..6646028a537 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -474,6 +474,7 @@ set(SOURCES HTML/ElementInternals.cpp HTML/EmbedderPolicy.cpp HTML/ErrorEvent.cpp + HTML/ErrorInformation.cpp HTML/EventHandler.cpp HTML/EventLoop/EventLoop.cpp HTML/EventLoop/Task.cpp diff --git a/Libraries/LibWeb/HTML/ErrorInformation.cpp b/Libraries/LibWeb/HTML/ErrorInformation.cpp new file mode 100644 index 00000000000..595e3ed2ce7 --- /dev/null +++ b/Libraries/LibWeb/HTML/ErrorInformation.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2025, Shannon Booth + * Copyright (c) 2025, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/webappapis.html#extract-error +ErrorInformation extract_error_information(JS::VM& vm, JS::Value exception) +{ + // 1. Let attributes be an empty map keyed by IDL attributes. + ErrorInformation attributes; + + // 2. Set attributes[error] to exception. + attributes.error = exception; + + // 3. Set attributes[message], attributes[filename], attributes[lineno], and attributes[colno] to + // implementation-defined values derived from exception. + attributes.message = [&] { + if (exception.is_object()) { + auto& object = exception.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: {}", exception.to_string_without_side_effects())); + }(); + + // FIXME: This offset is relative to the javascript source. Other browsers appear to do it relative + // to the entire source document! Calculate that somehow. + + // NB: If we got an Error object, then try and extract the information from the location the object was made. + if (exception.is_object() && is(exception.as_object())) { + auto const& error = static_cast(exception.as_object()); + for (auto const& frame : error.traceback()) { + auto source_range = frame.source_range(); + if (source_range.start.line != 0 || source_range.start.column != 0) { + attributes.filename = MUST(String::from_byte_string(source_range.filename())); + attributes.lineno = source_range.start.line; + attributes.colno = source_range.start.column; + break; + } + } + } + // NB: Otherwise, we fall back to try and find the location of the invocation of the function itself. + else { + for (ssize_t i = vm.execution_context_stack().size() - 1; i >= 0; --i) { + auto& frame = vm.execution_context_stack()[i]; + if (frame->executable) { + auto source_range = frame->executable->source_range_at(frame->program_counter).realize(); + attributes.filename = MUST(String::from_byte_string(source_range.filename())); + attributes.lineno = source_range.start.line; + attributes.colno = source_range.start.column; + break; + } + } + } + + // 4. Return attributes. + return attributes; +} + +} diff --git a/Libraries/LibWeb/HTML/ErrorInformation.h b/Libraries/LibWeb/HTML/ErrorInformation.h new file mode 100644 index 00000000000..9e1e1a3da41 --- /dev/null +++ b/Libraries/LibWeb/HTML/ErrorInformation.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025, Shannon Booth + * Copyright (c) 2025, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/webappapis.html#extract-error +struct ErrorInformation { + String message; + String filename; + JS::Value error; + size_t lineno { 0 }; + size_t colno { 0 }; +}; + +ErrorInformation extract_error_information(JS::VM&, JS::Value exception); + +} diff --git a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp index 0c22a9018d1..458a72ef219 100644 --- a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp +++ b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1151,72 +1152,6 @@ void WindowOrWorkerGlobalScopeMixin::report_error(JS::Value e) report_an_exception(e); } -// https://html.spec.whatwg.org/multipage/webappapis.html#extract-error -struct ErrorInformation { - String message; - String filename; - JS::Value error; - size_t lineno { 0 }; - size_t colno { 0 }; -}; - -// https://html.spec.whatwg.org/multipage/webappapis.html#extract-error -static ErrorInformation extract_error_information(JS::VM& vm, JS::Value exception) -{ - // 1. Let attributes be an empty map keyed by IDL attributes. - ErrorInformation attributes; - - // 2. Set attributes[error] to exception. - attributes.error = exception; - - // 3. Set attributes[message], attributes[filename], attributes[lineno], and attributes[colno] to - // implementation-defined values derived from exception. - attributes.message = [&] { - if (exception.is_object()) { - auto& object = exception.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: {}", exception.to_string_without_side_effects())); - }(); - - // FIXME: This offset is relative to the javascript source. Other browsers appear to do it relative - // to the entire source document! Calculate that somehow. - - // If we got an Error object, then try and extract the information from the location the object was made. - if (exception.is_object() && is(exception.as_object())) { - auto const& error = static_cast(exception.as_object()); - for (auto const& frame : error.traceback()) { - auto source_range = frame.source_range(); - if (source_range.start.line != 0 || source_range.start.column != 0) { - attributes.filename = MUST(String::from_byte_string(source_range.filename())); - attributes.lineno = source_range.start.line; - attributes.colno = source_range.start.column; - break; - } - } - } - // Otherwise, we fall back to try and find the location of the invocation of the function itself. - else { - for (ssize_t i = vm.execution_context_stack().size() - 1; i >= 0; --i) { - auto& frame = vm.execution_context_stack()[i]; - if (frame->executable) { - auto source_range = frame->executable->source_range_at(frame->program_counter).realize(); - attributes.filename = MUST(String::from_byte_string(source_range.filename())); - attributes.lineno = source_range.start.line; - attributes.colno = source_range.start.column; - break; - } - } - } - - // 4. Return attributes. - return attributes; -} - // https://html.spec.whatwg.org/multipage/webappapis.html#report-an-exception void WindowOrWorkerGlobalScopeMixin::report_an_exception(JS::Value exception, OmitError omit_error) {