mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 14:33:47 +00:00
invoke window.onerror callback in reportError
This commit is contained in:
211
src/browser/tests/window/onerror.html
Normal file
211
src/browser/tests/window/onerror.html
Normal file
@@ -0,0 +1,211 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<script id=onerrorBasicCallback>
|
||||
{
|
||||
let callbackCalled = false;
|
||||
let receivedArgs = null;
|
||||
|
||||
window.onerror = function(message, source, lineno, colno, error) {
|
||||
callbackCalled = true;
|
||||
receivedArgs = { message, source, lineno, colno, error };
|
||||
};
|
||||
|
||||
const err = new Error('Test error');
|
||||
window.reportError(err);
|
||||
|
||||
testing.expectEqual(true, callbackCalled);
|
||||
testing.expectEqual(true, receivedArgs.message.includes('Test error'));
|
||||
testing.expectEqual(err, receivedArgs.error);
|
||||
|
||||
window.onerror = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=onerrorFiveArguments>
|
||||
{
|
||||
let argCount = 0;
|
||||
|
||||
window.onerror = function() {
|
||||
argCount = arguments.length;
|
||||
};
|
||||
|
||||
window.reportError(new Error('Five args test'));
|
||||
|
||||
// Per WHATWG spec, onerror receives exactly 5 arguments
|
||||
testing.expectEqual(5, argCount);
|
||||
|
||||
window.onerror = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=onerrorReturnTrueCancelsEvent>
|
||||
{
|
||||
let listenerCalled = false;
|
||||
|
||||
window.onerror = function() {
|
||||
return true; // Should cancel the event
|
||||
};
|
||||
|
||||
const listener = function() {
|
||||
listenerCalled = true;
|
||||
};
|
||||
window.addEventListener('error', listener);
|
||||
|
||||
window.reportError(new Error('Should be cancelled'));
|
||||
|
||||
// The event listener should still be called (onerror returning true
|
||||
// only prevents default, not propagation)
|
||||
testing.expectEqual(true, listenerCalled);
|
||||
|
||||
window.onerror = null;
|
||||
window.removeEventListener('error', listener);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=onerrorAndEventListenerBothCalled>
|
||||
{
|
||||
let onerrorCalled = false;
|
||||
let listenerCalled = false;
|
||||
|
||||
window.onerror = function() {
|
||||
onerrorCalled = true;
|
||||
};
|
||||
|
||||
const listener = function() {
|
||||
listenerCalled = true;
|
||||
};
|
||||
window.addEventListener('error', listener);
|
||||
|
||||
window.reportError(new Error('Both should fire'));
|
||||
|
||||
testing.expectEqual(true, onerrorCalled);
|
||||
testing.expectEqual(true, listenerCalled);
|
||||
|
||||
window.onerror = null;
|
||||
window.removeEventListener('error', listener);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=onerrorCalledBeforeEventListener>
|
||||
{
|
||||
let callOrder = [];
|
||||
|
||||
window.onerror = function() {
|
||||
callOrder.push('onerror');
|
||||
};
|
||||
|
||||
const listener = function() {
|
||||
callOrder.push('listener');
|
||||
};
|
||||
window.addEventListener('error', listener);
|
||||
|
||||
window.reportError(new Error('Order test'));
|
||||
|
||||
// onerror should be called before addEventListener handlers
|
||||
testing.expectEqual('onerror', callOrder[0]);
|
||||
testing.expectEqual('listener', callOrder[1]);
|
||||
|
||||
window.onerror = null;
|
||||
window.removeEventListener('error', listener);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=onerrorGetterSetter>
|
||||
{
|
||||
const handler = function() {};
|
||||
|
||||
testing.expectEqual(null, window.onerror);
|
||||
|
||||
window.onerror = handler;
|
||||
testing.expectEqual(handler, window.onerror);
|
||||
|
||||
window.onerror = null;
|
||||
testing.expectEqual(null, window.onerror);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=onerrorWithNonFunction>
|
||||
{
|
||||
// Setting onerror to a non-function should not throw
|
||||
// but should not be stored as the handler
|
||||
window.onerror = "not a function";
|
||||
testing.expectEqual(null, window.onerror);
|
||||
|
||||
window.onerror = {};
|
||||
testing.expectEqual(null, window.onerror);
|
||||
|
||||
window.onerror = 123;
|
||||
testing.expectEqual(null, window.onerror);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=onerrorArgumentTypes>
|
||||
{
|
||||
let receivedTypes = null;
|
||||
|
||||
window.onerror = function(message, source, lineno, colno, error) {
|
||||
receivedTypes = {
|
||||
message: typeof message,
|
||||
source: typeof source,
|
||||
lineno: typeof lineno,
|
||||
colno: typeof colno,
|
||||
error: typeof error
|
||||
};
|
||||
};
|
||||
|
||||
window.reportError(new Error('Type check'));
|
||||
|
||||
testing.expectEqual('string', receivedTypes.message);
|
||||
testing.expectEqual('string', receivedTypes.source);
|
||||
testing.expectEqual('number', receivedTypes.lineno);
|
||||
testing.expectEqual('number', receivedTypes.colno);
|
||||
testing.expectEqual('object', receivedTypes.error);
|
||||
|
||||
window.onerror = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=onerrorReturnFalseDoesNotCancel>
|
||||
{
|
||||
let eventDefaultPrevented = false;
|
||||
|
||||
window.onerror = function() {
|
||||
return false; // Should NOT cancel the event
|
||||
};
|
||||
|
||||
const listener = function(e) {
|
||||
eventDefaultPrevented = e.defaultPrevented;
|
||||
};
|
||||
window.addEventListener('error', listener);
|
||||
|
||||
window.reportError(new Error('Return false test'));
|
||||
|
||||
testing.expectEqual(false, eventDefaultPrevented);
|
||||
|
||||
window.onerror = null;
|
||||
window.removeEventListener('error', listener);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=onerrorReturnTruePreventsDefault>
|
||||
{
|
||||
let eventDefaultPrevented = false;
|
||||
|
||||
window.onerror = function() {
|
||||
return true; // Should cancel (prevent default)
|
||||
};
|
||||
|
||||
const listener = function(e) {
|
||||
eventDefaultPrevented = e.defaultPrevented;
|
||||
};
|
||||
window.addEventListener('error', listener);
|
||||
|
||||
window.reportError(new Error('Return true test'));
|
||||
|
||||
testing.expectEqual(true, eventDefaultPrevented);
|
||||
|
||||
window.onerror = null;
|
||||
window.removeEventListener('error', listener);
|
||||
}
|
||||
</script>
|
||||
@@ -58,7 +58,7 @@ _storage_bucket: *storage.Bucket,
|
||||
_on_load: ?js.Function.Global = null,
|
||||
_on_pageshow: ?js.Function.Global = null,
|
||||
_on_popstate: ?js.Function.Global = null,
|
||||
_on_error: ?js.Function.Global = null, // TODO: invoke on error?
|
||||
_on_error: ?js.Function.Global = null,
|
||||
_on_unhandled_rejection: ?js.Function.Global = null, // TODO: invoke on error
|
||||
_location: *Location,
|
||||
_timer_id: u30 = 0,
|
||||
@@ -283,6 +283,32 @@ pub fn reportError(self: *Window, err: js.Value, page: *Page) !void {
|
||||
}, page);
|
||||
|
||||
const event = error_event.asEvent();
|
||||
|
||||
// Invoke window.onerror callback if set (per WHATWG spec, this is called
|
||||
// with 5 arguments: message, source, lineno, colno, error)
|
||||
// If it returns true, the event is cancelled.
|
||||
if (self._on_error) |on_error| {
|
||||
var ls: js.Local.Scope = undefined;
|
||||
page.js.localScope(&ls);
|
||||
defer ls.deinit();
|
||||
|
||||
const local_func = ls.toLocal(on_error);
|
||||
const result = local_func.call(js.Value, .{
|
||||
error_event._message,
|
||||
error_event._filename,
|
||||
error_event._line_number,
|
||||
error_event._column_number,
|
||||
err,
|
||||
}) catch null;
|
||||
|
||||
// Per spec: returning true from onerror cancels the event
|
||||
if (result) |r| {
|
||||
if (r.isTrue()) {
|
||||
event._prevent_default = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try page._event_manager.dispatch(self.asEventTarget(), event);
|
||||
|
||||
if (comptime builtin.is_test == false) {
|
||||
@@ -665,7 +691,7 @@ pub const JsApi = struct {
|
||||
pub const onload = bridge.accessor(Window.getOnLoad, Window.setOnLoad, .{});
|
||||
pub const onpageshow = bridge.accessor(Window.getOnPageShow, Window.setOnPageShow, .{});
|
||||
pub const onpopstate = bridge.accessor(Window.getOnPopState, Window.setOnPopState, .{});
|
||||
pub const onerror = bridge.accessor(Window.getOnError, Window.getOnError, .{});
|
||||
pub const onerror = bridge.accessor(Window.getOnError, Window.setOnError, .{});
|
||||
pub const onunhandledrejection = bridge.accessor(Window.getOnUnhandledRejection, Window.setOnUnhandledRejection, .{});
|
||||
pub const fetch = bridge.function(Window.fetch, .{});
|
||||
pub const queueMicrotask = bridge.function(Window.queueMicrotask, .{});
|
||||
|
||||
Reference in New Issue
Block a user