Explorar el Código

LibWeb: Add initial implementation of global.reportError()

Natsuki Ikeguchi hace 1 año
padre
commit
ccb3a2f7ad

+ 5 - 0
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!

+ 16 - 0
Tests/LibWeb/Text/input/HTML/WindowOrWorkerGlobalScope-reportError.html

@@ -0,0 +1,16 @@
+<script src="../include.js"></script>
+<script>
+    test(() => {
+        window.onerror = (message, filename, lineno, colno, error) => {
+            println(`message = ${message}`);
+            println(`filename = ${filename}`);
+            println(`lineno = ${lineno}`);
+            println(`colno = ${colno}`);
+            println(`error = ${error}`);
+
+            return true;
+        };
+
+        window.reportError(new Error('Reporting an Error!'));
+    });
+</script>

+ 1 - 0
Userland/Libraries/LibWeb/HTML/Window.h

@@ -64,6 +64,7 @@ public:
     using WindowOrWorkerGlobalScopeMixin::create_image_bitmap;
     using WindowOrWorkerGlobalScopeMixin::create_image_bitmap;
     using WindowOrWorkerGlobalScopeMixin::fetch;
     using WindowOrWorkerGlobalScopeMixin::fetch;
     using WindowOrWorkerGlobalScopeMixin::queue_microtask;
     using WindowOrWorkerGlobalScopeMixin::queue_microtask;
+    using WindowOrWorkerGlobalScopeMixin::report_error;
     using WindowOrWorkerGlobalScopeMixin::set_interval;
     using WindowOrWorkerGlobalScopeMixin::set_interval;
     using WindowOrWorkerGlobalScopeMixin::set_timeout;
     using WindowOrWorkerGlobalScopeMixin::set_timeout;
     using WindowOrWorkerGlobalScopeMixin::structured_clone;
     using WindowOrWorkerGlobalScopeMixin::structured_clone;

+ 89 - 0
Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp

@@ -17,6 +17,7 @@
 #include <LibWeb/Bindings/MainThreadVM.h>
 #include <LibWeb/Bindings/MainThreadVM.h>
 #include <LibWeb/Fetch/FetchMethod.h>
 #include <LibWeb/Fetch/FetchMethod.h>
 #include <LibWeb/HTML/CanvasRenderingContext2D.h>
 #include <LibWeb/HTML/CanvasRenderingContext2D.h>
+#include <LibWeb/HTML/ErrorEvent.h>
 #include <LibWeb/HTML/EventLoop/EventLoop.h>
 #include <LibWeb/HTML/EventLoop/EventLoop.h>
 #include <LibWeb/HTML/EventSource.h>
 #include <LibWeb/HTML/EventSource.h>
 #include <LibWeb/HTML/ImageBitmap.h>
 #include <LibWeb/HTML/ImageBitmap.h>
@@ -742,4 +743,92 @@ JS::NonnullGCPtr<JS::Object> WindowOrWorkerGlobalScopeMixin::supported_entry_typ
     return *m_supported_entry_types_array;
     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<DOM::EventTarget&>(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>& js_script) {
+            if (verify_cast<ClassicScript>(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);
+    }
+}
+
 }
 }

+ 4 - 0
Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h

@@ -76,6 +76,8 @@ public:
 
 
     JS::NonnullGCPtr<IndexedDB::IDBFactory> indexed_db();
     JS::NonnullGCPtr<IndexedDB::IDBFactory> indexed_db();
 
 
+    void report_error(JS::Value e);
+
 protected:
 protected:
     void initialize(JS::Realm&);
     void initialize(JS::Realm&);
     void visit_edges(JS::Cell::Visitor&);
     void visit_edges(JS::Cell::Visitor&);
@@ -114,6 +116,8 @@ private:
     JS::GCPtr<IndexedDB::IDBFactory> m_indexed_db;
     JS::GCPtr<IndexedDB::IDBFactory> m_indexed_db;
 
 
     mutable JS::GCPtr<JS::Object> m_supported_entry_types_array;
     mutable JS::GCPtr<JS::Object> m_supported_entry_types_array;
+
+    bool m_error_reporting_mode { false };
 };
 };
 
 
 }
 }

+ 2 - 1
Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.idl

@@ -17,7 +17,8 @@ interface mixin WindowOrWorkerGlobalScope {
     readonly attribute boolean isSecureContext;
     readonly attribute boolean isSecureContext;
     readonly attribute boolean crossOriginIsolated;
     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
     // base64 utility methods
     DOMString btoa(DOMString data);
     DOMString btoa(DOMString data);