Scripts now properly block rendering

Re-enabled CDP tests

Fixed more tests
This commit is contained in:
Karl Seguin
2025-10-29 16:37:11 +08:00
parent 1a04ebce35
commit fb9cce747d
14 changed files with 175 additions and 205 deletions

View File

@@ -130,16 +130,6 @@ pub fn dispatchWithFunction(self: *EventManager, target: *EventTarget, event: *E
} }
fn dispatchNode(self: *EventManager, target: *Node, event: *Event) !void { fn dispatchNode(self: *EventManager, target: *Node, event: *Event) !void {
if (event._bubbles == false) {
event._event_phase = .at_target;
const target_et = target.asEventTarget();
if (self.lookup.getPtr(@intFromPtr(target_et))) |list| {
try self.dispatchPhase(list, target_et, event, null);
}
event._event_phase = .none;
return;
}
var path_len: usize = 0; var path_len: usize = 0;
var path_buffer: [128]*EventTarget = undefined; var path_buffer: [128]*EventTarget = undefined;
@@ -150,7 +140,8 @@ fn dispatchNode(self: *EventManager, target: *Node, event: *Event) !void {
path_len += 1; path_len += 1;
} }
// Even though the window isn't part of the DOM, events bubble to it // Even though the window isn't part of the DOM, events always propagate
// through it in the capture phase
if (path_len < path_buffer.len) { if (path_len < path_buffer.len) {
path_buffer[path_len] = self.page.window.asEventTarget(); path_buffer[path_len] = self.page.window.asEventTarget();
path_len += 1; path_len += 1;
@@ -159,6 +150,7 @@ fn dispatchNode(self: *EventManager, target: *Node, event: *Event) !void {
const path = path_buffer[0..path_len]; const path = path_buffer[0..path_len];
// Phase 1: Capturing phase (root → target, excluding target) // Phase 1: Capturing phase (root → target, excluding target)
// This happens for all events, regardless of bubbling
event._event_phase = .capturing_phase; event._event_phase = .capturing_phase;
var i: usize = path_len; var i: usize = path_len;
while (i > 1) { while (i > 1) {
@@ -173,6 +165,7 @@ fn dispatchNode(self: *EventManager, target: *Node, event: *Event) !void {
} }
} }
// Phase 2: At target
event._event_phase = .at_target; event._event_phase = .at_target;
const target_et = target.asEventTarget(); const target_et = target.asEventTarget();
if (self.lookup.getPtr(@intFromPtr(target_et))) |list| { if (self.lookup.getPtr(@intFromPtr(target_et))) |list| {
@@ -183,12 +176,16 @@ fn dispatchNode(self: *EventManager, target: *Node, event: *Event) !void {
} }
} }
event._event_phase = .bubbling_phase; // Phase 3: Bubbling phase (target → root, excluding target)
for (path[1..]) |current_target| { // This only happens if the event bubbles
if (self.lookup.getPtr(@intFromPtr(current_target))) |list| { if (event._bubbles) {
try self.dispatchPhase(list, current_target, event, false); event._event_phase = .bubbling_phase;
if (event._stop_propagation) { for (path[1..]) |current_target| {
break; if (self.lookup.getPtr(@intFromPtr(current_target))) |list| {
try self.dispatchPhase(list, current_target, event, false);
if (event._stop_propagation) {
break;
}
} }
} }
} }

View File

@@ -17,6 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin");
const js = @import("js/js.zig"); const js = @import("js/js.zig");
const log = @import("../log.zig"); const log = @import("../log.zig");
@@ -31,11 +32,13 @@ const Element = @import("webapi/Element.zig");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const ArrayListUnmanaged = std.ArrayListUnmanaged; const ArrayListUnmanaged = std.ArrayListUnmanaged;
const IS_DEBUG = builtin.mode == .Debug;
const ScriptManager = @This(); const ScriptManager = @This();
page: *Page, page: *Page,
// used to prevent recursive evalutaion // used to prevent recursive evaluation
is_evaluating: bool, is_evaluating: bool,
// Only once this is true can deferred scripts be run // Only once this is true can deferred scripts be run
@@ -45,9 +48,6 @@ static_scripts_done: bool,
// on shutdown/abort, we need to cleanup any pending ones. // on shutdown/abort, we need to cleanup any pending ones.
asyncs: OrderList, asyncs: OrderList,
// Normal scripts (non-deferred & non-async). These must be executed in order
scripts: OrderList,
// List of deferred scripts. These must be executed in order, but only once // List of deferred scripts. These must be executed in order, but only once
// dom_loaded == true, // dom_loaded == true,
deferreds: OrderList, deferreds: OrderList,
@@ -85,7 +85,6 @@ pub fn init(page: *Page) ScriptManager {
return .{ return .{
.page = page, .page = page,
.asyncs = .{}, .asyncs = .{},
.scripts = .{},
.deferreds = .{}, .deferreds = .{},
.importmap = .empty, .importmap = .empty,
.sync_modules = .empty, .sync_modules = .empty,
@@ -130,7 +129,6 @@ pub fn reset(self: *ScriptManager) void {
self.importmap = .empty; self.importmap = .empty;
self.clearList(&self.asyncs); self.clearList(&self.asyncs);
self.clearList(&self.scripts);
self.clearList(&self.deferreds); self.clearList(&self.deferreds);
self.static_scripts_done = false; self.static_scripts_done = false;
} }
@@ -212,57 +210,78 @@ pub fn add(self: *ScriptManager, script_element: *Element.Html.Script, comptime
.is_async = if (remote_url == null) false else element.getAttributeSafe("async") != null, .is_async = if (remote_url == null) false else element.getAttributeSafe("async") != null,
}; };
if (source == .@"inline" and self.scripts.first == null) { if (source == .@"inline") {
// inline script with no pending scripts, execute it immediately. // inline script gets executed immediately
// (if there is a pending script, then we cannot execute this immediately
// as it needs to be executed in order)
return script.eval(page); return script.eval(page);
} }
const pending_script = try self.script_pool.create(); const pending_script = blk: {
errdefer self.script_pool.destroy(pending_script); // Done in a block this way so that, if something fails in this block
pending_script.* = .{ // it's cleaned up with these errdefers
.script = script, // BUT, if we need to load/execute the script immediately, cleanup/lifetimes
.complete = false, // become the responsibility of the outer block.
.manager = self, const pending_script = try self.script_pool.create();
.node = .{}, errdefer self.script_pool.destroy(pending_script);
pending_script.* = .{
.script = script,
.complete = false,
.manager = self,
.node = .{},
};
errdefer pending_script.deinit();
if (comptime IS_DEBUG) {
log.debug(.http, "script queue", .{
.ctx = ctx,
.url = remote_url.?,
.stack = page.js.stackTrace() catch "???",
});
}
var headers = try self.client.newHeaders();
try page.requestCookie(.{}).headersForRequest(page.arena, remote_url.?, &headers);
try self.client.request(.{
.url = remote_url.?,
.ctx = pending_script,
.method = .GET,
.headers = headers,
.resource_type = .script,
.cookie_jar = &page._session.cookie_jar,
.start_callback = if (log.enabled(.http, .debug)) startCallback else null,
.header_callback = headerCallback,
.data_callback = dataCallback,
.done_callback = doneCallback,
.error_callback = errorCallback,
});
if (script.is_defer) {
// non-blocking loading, track the list this belongs to, and return
pending_script.list = &self.deferreds;
return;
}
if (script.is_async) {
// non-blocking loading, track the list this belongs to, and return
pending_script.list = &self.asyncs;
return;
}
break :blk pending_script;
}; };
if (source == .@"inline") { defer pending_script.deinit();
// if we're here, it means that we have pending scripts (i.e. self.scripts
// is not empty). Because the script is inline, it's complete/ready, but // this is <script src="..."></script>, it needs to block the caller
// we need to process them in order // until it's evaluated
pending_script.complete = true; var client = self.client;
self.scripts.append(&pending_script.node); while (true) {
return; if (pending_script.complete) {
} else { return pending_script.script.eval(page);
log.debug(.http, "script queue", .{ }
.ctx = ctx, _ = try client.tick(200);
.url = remote_url.?,
.stack = page.js.stackTrace() catch "???",
});
} }
pending_script.getList().append(&pending_script.node);
errdefer pending_script.deinit();
var headers = try self.client.newHeaders();
try page.requestCookie(.{}).headersForRequest(page.arena, remote_url.?, &headers);
try self.client.request(.{
.url = remote_url.?,
.ctx = pending_script,
.method = .GET,
.headers = headers,
.resource_type = .script,
.cookie_jar = &page._session.cookie_jar,
.start_callback = if (log.enabled(.http, .debug)) startCallback else null,
.header_callback = headerCallback,
.data_callback = dataCallback,
.done_callback = doneCallback,
.error_callback = errorCallback,
});
} }
// Resolve a module specifier to an valid URL. // Resolve a module specifier to an valid URL.
@@ -394,6 +413,7 @@ pub fn getAsyncModule(self: *ScriptManager, url: [:0]const u8, cb: AsyncModule.C
.error_callback = AsyncModule.errorCallback, .error_callback = AsyncModule.errorCallback,
}); });
} }
pub fn pageIsLoaded(self: *ScriptManager) void { pub fn pageIsLoaded(self: *ScriptManager) void {
std.debug.assert(self.static_scripts_done == false); std.debug.assert(self.static_scripts_done == false);
self.static_scripts_done = true; self.static_scripts_done = true;
@@ -415,15 +435,6 @@ fn evaluate(self: *ScriptManager) void {
self.is_evaluating = true; self.is_evaluating = true;
defer self.is_evaluating = false; defer self.is_evaluating = false;
while (self.scripts.first) |n| {
var pending_script: *PendingScript = @fieldParentPtr("node", n);
if (pending_script.complete == false) {
return;
}
defer pending_script.deinit();
pending_script.script.eval(page);
}
if (self.static_scripts_done == false) { if (self.static_scripts_done == false) {
// We can only execute deferred scripts if // We can only execute deferred scripts if
// 1 - all the normal scripts are done // 1 - all the normal scripts are done
@@ -460,7 +471,6 @@ fn evaluate(self: *ScriptManager) void {
pub fn isDone(self: *const ScriptManager) bool { pub fn isDone(self: *const ScriptManager) bool {
return self.asyncs.first == null and // there are no more async scripts return self.asyncs.first == null and // there are no more async scripts
self.static_scripts_done and // and we've finished parsing the HTML to queue all <scripts> self.static_scripts_done and // and we've finished parsing the HTML to queue all <scripts>
self.scripts.first == null and // and there are no more <script src=> to wait for
self.deferreds.first == null; // and there are no more <script defer src=> to wait for self.deferreds.first == null; // and there are no more <script defer src=> to wait for
} }
@@ -536,12 +546,13 @@ fn parseImportmap(self: *ScriptManager, script: *const Script) !void {
// A script which is pending execution. // A script which is pending execution.
// It could be pending because: // It could be pending because:
// (a) we're still downloading its content or // (a) we're still downloading its content or
// (b) this is a non-async script that has to be executed in order // (b) it's a deferred script which has to be executed in order
pub const PendingScript = struct { pub const PendingScript = struct {
script: Script, script: Script,
complete: bool, complete: bool,
node: OrderList.Node, node: OrderList.Node,
manager: *ScriptManager, manager: *ScriptManager,
list: ?*std.DoublyLinkedList = null,
fn deinit(self: *PendingScript) void { fn deinit(self: *PendingScript) void {
const script = &self.script; const script = &self.script;
@@ -550,14 +561,11 @@ pub const PendingScript = struct {
if (script.source == .remote) { if (script.source == .remote) {
manager.buffer_pool.release(script.source.remote); manager.buffer_pool.release(script.source.remote);
} }
self.getList().remove(&self.node);
}
fn remove(self: *PendingScript) void { if (self.list) |list| {
if (self.node) |*node| { list.remove(&self.node);
self.getList().remove(node);
self.node = null;
} }
manager.script_pool.destroy(self);
} }
fn startCallback(self: *PendingScript, transfer: *Http.Transfer) !void { fn startCallback(self: *PendingScript, transfer: *Http.Transfer) !void {
@@ -614,6 +622,7 @@ pub const PendingScript = struct {
manager.evaluate(); manager.evaluate();
return; return;
} }
// async script can be evaluated immediately // async script can be evaluated immediately
self.script.eval(manager.page); self.script.eval(manager.page);
self.deinit(); self.deinit();
@@ -634,23 +643,6 @@ pub const PendingScript = struct {
manager.evaluate(); manager.evaluate();
} }
fn getList(self: *const PendingScript) *OrderList {
// When a script has both the async and defer flag set, it should be
// treated as async. Async is newer, so some websites use both so that
// if async isn't known, it'll fallback to defer.
const script = &self.script;
if (script.is_async) {
return &self.manager.asyncs;
}
if (script.is_defer) {
return &self.manager.deferreds;
}
return &self.manager.scripts;
}
}; };
const Script = struct { const Script = struct {

View File

@@ -571,7 +571,9 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_: ?v8.Object, value: anytype) !
return self.mapZigInstanceToJs(js_obj_, heap); return self.mapZigInstanceToJs(js_obj_, heap);
}, },
.pointer => |ptr| { .pointer => |ptr| {
const gop = try self.identity_map.getOrPut(arena, @intFromPtr(value)); const resolved = resolveValue(value);
const gop = try self.identity_map.getOrPut(arena, @intFromPtr(resolved.ptr));
if (gop.found_existing) { if (gop.found_existing) {
// we've seen this instance before, return the same // we've seen this instance before, return the same
// PersistentObject. // PersistentObject.
@@ -579,8 +581,6 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_: ?v8.Object, value: anytype) !
} }
const isolate = self.isolate; const isolate = self.isolate;
const resolved = resolveValue(value);
// Sometimes we're creating a new v8.Object, like when // Sometimes we're creating a new v8.Object, like when
// we're returning a value from a function. In those cases // we're returning a value from a function. In those cases
// we have to get the object template, and we can get an object // we have to get the object template, and we can get an object
@@ -1874,11 +1874,6 @@ fn zigJsonToJs(isolate: v8.Isolate, v8_context: v8.Context, value: std.json.Valu
} }
} }
pub fn getGlobalThis(self: *Context) js.This {
const js_global = self.v8_context.getGlobal();
return .{ .obj = .{ .js_obj = js_global, .context = self } };
}
// == Misc == // == Misc ==
// An interface for types that want to have their jsDeinit function to be // An interface for types that want to have their jsDeinit function to be
// called when the call context ends // called when the call context ends

View File

@@ -241,7 +241,11 @@ pub fn attachClass(comptime JsApi: type, isolate: v8.Isolate, template: v8.Funct
bridge.Function => { bridge.Function => {
const function_template = v8.FunctionTemplate.initCallback(isolate, value.func); const function_template = v8.FunctionTemplate.initCallback(isolate, value.func);
const js_name: v8.Name = v8.String.initUtf8(isolate, name).toName(); const js_name: v8.Name = v8.String.initUtf8(isolate, name).toName();
template_proto.set(js_name, function_template, v8.PropertyAttribute.None); if (value.static) {
template.set(js_name, function_template, v8.PropertyAttribute.None);
} else {
template_proto.set(js_name, function_template, v8.PropertyAttribute.None);
}
}, },
bridge.Indexed => { bridge.Indexed => {
const configuration = v8.IndexedPropertyHandlerConfiguration{ const configuration = v8.IndexedPropertyHandlerConfiguration{

View File

@@ -85,6 +85,7 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool, global_cal
defer temp_scope.deinit(); defer temp_scope.deinit();
const js_global = v8.FunctionTemplate.initDefault(isolate); const js_global = v8.FunctionTemplate.initDefault(isolate);
js_global.setClassName(v8.String.initUtf8(isolate, "Window"));
Env.attachClass(@TypeOf(page.window.*).JsApi, isolate, js_global); Env.attachClass(@TypeOf(page.window.*).JsApi, isolate, js_global);
const global_template = js_global.getInstanceTemplate(); const global_template = js_global.getInstanceTemplate();
@@ -205,38 +206,6 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool, global_cal
// } // }
// } // }
// @ZIGDOM
// Primitive attributes are set directly on the FunctionTemplate
// when we setup the environment. But we cannot set more complex
// types (v8 will crash).
//
// Plus, just to create more complex types, we always need a
// context, i.e. an Array has to have a Context to exist.
//
// As far as I can tell, getting the FunctionTemplate's object
// and setting values directly on it, for each context, is the
// way to do this.
// inline for (JsApis, 0..) |Jsapi, i| {
// inline for (@typeInfo(Struct).@"struct".decls) |declaration| {
// const name = declaration.name;
// if (comptime name[0] == '_') {
// const value = @field(Struct, name);
// if (comptime js.isComplexAttributeType(@typeInfo(@TypeOf(value)))) {
// const js_obj = templates[i].getFunction(v8_context).toObject();
// const js_name = v8.String.initUtf8(isolate, name[1..]).toName();
// const js_val = try context.zigValueToJs(value);
// if (!js_obj.setValue(v8_context, js_name, js_val)) {
// log.fatal(.app, "set class attribute", .{
// .@"struct" = @typeName(Struct),
// .name = name,
// });
// }
// }
// }
// }
// }
try context.setupGlobal(); try context.setupGlobal();
return context; return context;
} }

View File

@@ -105,6 +105,7 @@ pub const Constructor = struct {
}; };
pub const Function = struct { pub const Function = struct {
static: bool,
func: *const fn (?*const v8.C_FunctionCallbackInfo) callconv(.c) void, func: *const fn (?*const v8.C_FunctionCallbackInfo) callconv(.c) void,
const Opts = struct { const Opts = struct {
@@ -115,27 +116,30 @@ pub const Function = struct {
}; };
fn init(comptime T: type, comptime func: anytype, comptime opts: Opts) Function { fn init(comptime T: type, comptime func: anytype, comptime opts: Opts) Function {
return .{ .func = struct { return .{
fn wrap(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void { .static = opts.static,
const info = v8.FunctionCallbackInfo.initFromV8(raw_info); .func = struct {
var caller = Caller.init(info); fn wrap(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void {
defer caller.deinit(); const info = v8.FunctionCallbackInfo.initFromV8(raw_info);
var caller = Caller.init(info);
defer caller.deinit();
if (comptime opts.static) { if (comptime opts.static) {
caller.function(T, func, info, .{ caller.function(T, func, info, .{
.dom_exception = opts.dom_exception, .dom_exception = opts.dom_exception,
.as_typed_array = opts.as_typed_array, .as_typed_array = opts.as_typed_array,
.null_as_undefined = opts.null_as_undefined, .null_as_undefined = opts.null_as_undefined,
}); });
} else { } else {
caller.method(T, func, info, .{ caller.method(T, func, info, .{
.dom_exception = opts.dom_exception, .dom_exception = opts.dom_exception,
.as_typed_array = opts.as_typed_array, .as_typed_array = opts.as_typed_array,
.null_as_undefined = opts.null_as_undefined, .null_as_undefined = opts.null_as_undefined,
}); });
}
} }
} }.wrap,
}.wrap }; };
} }
}; };

View File

@@ -498,5 +498,5 @@ pub export fn v8_inspector__Client__IMPL__descriptionForValueSubtype(
test "TaggedAnyOpaque" { test "TaggedAnyOpaque" {
// If we grow this, fine, but it should be a conscious decision // If we grow this, fine, but it should be a conscious decision
try std.testing.expectEqual(16, @sizeOf(TaggedAnyOpaque)); try std.testing.expectEqual(24, @sizeOf(TaggedAnyOpaque));
} }

View File

@@ -10,7 +10,7 @@
<script id=document> <script id=document>
testing.expectEqual(null, document.parentNode); testing.expectEqual(null, document.parentNode);
testing.expectEqual(undefined, document.getCurrentScript); testing.expectEqual(undefined, document.getCurrentScript);
testing.expectEqual("http://localhost:9601/src/browser/tests/document/document.html", document.URL); testing.expectEqual("http://127.0.0.1:9582/src/browser/tests/document/document.html", document.URL);
</script> </script>
<script id=headAndbody> <script id=headAndbody>

View File

@@ -117,7 +117,7 @@
parent.dispatchEvent(new Event('dupe')); parent.dispatchEvent(new Event('dupe'));
testing.expectEqual(101, parent_calls); testing.expectEqual(101, parent_calls);
<!-- </script> --> </script>
<div id=parent4><div id=child4></div></div> <div id=parent4><div id=child4></div></div>
<script id=stpoPropagation> <script id=stpoPropagation>
@@ -264,11 +264,14 @@
<div id=child9></div> <div id=child9></div>
<script id=nonBubblingNoCapture> <script id=nonBubblingNoCapture>
// Test that non-bubbling events don't trigger capture phase // Test that non-bubbling events still have capture phase but no bubble phase
let non_bubble_calls = []; let non_bubble_calls = [];
const child9 = $('#child9'); const child9 = $('#child9');
window.addEventListener('nobubble', () => non_bubble_calls.push('window-capture'), true); window.addEventListener('nobubble', (e) => {
non_bubble_calls.push('window-capture');
testing.expectEqual(Event.CAPTURING_PHASE, e.eventPhase);
}, true);
window.addEventListener('nobubble', () => non_bubble_calls.push('window-bubble'), false); window.addEventListener('nobubble', () => non_bubble_calls.push('window-bubble'), false);
child9.addEventListener('nobubble', (e) => { child9.addEventListener('nobubble', (e) => {
non_bubble_calls.push('child'); non_bubble_calls.push('child');
@@ -277,7 +280,8 @@
child9.dispatchEvent(new Event('nobubble', {bubbles: false})); child9.dispatchEvent(new Event('nobubble', {bubbles: false}));
// Should only call child listener, not window // Should call window-capture (during capture phase) and child, but NOT window-bubble
testing.expectEqual(1, non_bubble_calls.length); testing.expectEqual(2, non_bubble_calls.length);
testing.expectEqual('child', non_bubble_calls[0]); testing.expectEqual('window-capture', non_bubble_calls[0]);
testing.expectEqual('child', non_bubble_calls[1]);
</script> </script>

View File

@@ -20,8 +20,8 @@
<script id=urlSearchParams> <script id=urlSearchParams>
const inputs = [ const inputs = [
[["over", "9000!!"], ["abc", 123], ["key1", ""], ["key2", ""]], // @ZIGDOM [["over", "9000!!"], ["abc", 123], ["key1", ""], ["key2", ""]],
{over: "9000!!", abc: 123, key1: "", key2: ""}, // @ZIGDOM {over: "9000!!", abc: 123, key1: "", key2: ""},
"over=9000!!&abc=123&key1&key2=", "over=9000!!&abc=123&key1&key2=",
"?over=9000!!&abc=123&key1&key2=", "?over=9000!!&abc=123&key1&key2=",
] ]
@@ -73,7 +73,7 @@
} }
</script> </script>
<script id=encoding> x<script id=encoding>
{ {
const usp = new URLSearchParams('key=hello%20world&special=%21%40%23%24&plus=a+b'); const usp = new URLSearchParams('key=hello%20world&special=%21%40%23%24&plus=a+b');
testing.expectEqual('hello world', usp.get('key')); testing.expectEqual('hello world', usp.get('key'));
@@ -351,4 +351,4 @@
testing.expectEqual('a=2&a=4&b=1&b=3&c=5', usp.toString()); testing.expectEqual('a=2&a=4&b=1&b=3&c=5', usp.toString());
testing.expectEqual(['a', 'a', 'b', 'b', 'c'], Array.from(usp.keys())); testing.expectEqual(['a', 'a', 'b', 'b', 'c'], Array.from(usp.keys()));
} }
</script> </script> -->

View File

@@ -56,6 +56,15 @@ pub fn removeEventListener(self: *EventTarget, typ: []const u8, callback: js.Fun
return page._event_manager.remove(self, typ, callback, use_capture); return page._event_manager.remove(self, typ, callback, use_capture);
} }
pub fn format(self: *EventTarget, writer: *std.Io.Writer) !void {
return switch (self._type) {
.node => |n| n.format(writer),
.window => writer.writeAll("<window>"),
.xhr => writer.writeAll("<XMLHttpRequestEventTarget>"),
.abort_signal => writer.writeAll("<abort_signal>"),
};
}
pub const JsApi = struct { pub const JsApi = struct {
pub const bridge = js.Bridge(EventTarget); pub const bridge = js.Bridge(EventTarget);

View File

@@ -9,7 +9,7 @@ pub fn registerTypes() []const type {
} }
pub const Jar = @import("cookie.zig").Jar; pub const Jar = @import("cookie.zig").Jar;
pub const Cookie =@import("cookie.zig").Cookie; pub const Cookie = @import("cookie.zig").Cookie;
pub const Shed = struct { pub const Shed = struct {
_origins: std.StringHashMapUnmanaged(*Bucket) = .empty, _origins: std.StringHashMapUnmanaged(*Bucket) = .empty,

View File

@@ -117,11 +117,12 @@ const TestContext = struct {
bc.session_id = sid; bc.session_id = sid;
} }
if (opts.html) |html| { // @ZIGDOM
if (bc.session_id == null) bc.session_id = "SID-X"; // if (opts.html) |html| {
const page = try bc.session.createPage(); // if (bc.session_id == null) bc.session_id = "SID-X";
page.window.document = (try Document.init(html)).doc; // const page = try bc.session.createPage();
} // page.window._document = (try Document.init(html)).doc;
// }
return bc; return bc;
} }

View File

@@ -422,9 +422,8 @@ test {
const log = @import("log.zig"); const log = @import("log.zig");
const TestHTTPServer = @import("TestHTTPServer.zig"); const TestHTTPServer = @import("TestHTTPServer.zig");
// @ZIGDOM-CDP const Server = @import("Server.zig");
// const Server = @import("Server.zig"); var test_cdp_server: ?Server = null;
// var test_cdp_server: ?Server = null;
var test_http_server: ?TestHTTPServer = null; var test_http_server: ?TestHTTPServer = null;
test "tests:beforeAll" { test "tests:beforeAll" {
@@ -446,12 +445,10 @@ test "tests:beforeAll" {
var wg: std.Thread.WaitGroup = .{}; var wg: std.Thread.WaitGroup = .{};
wg.startMany(2); wg.startMany(2);
// @ZIGDOM-CDP {
// { const thread = try std.Thread.spawn(.{}, serveCDP, .{&wg});
// const thread = try std.Thread.spawn(.{}, serveCDP, .{&wg}); thread.detach();
// thread.detach(); }
// }
wg.finish(); // @ZIGDOM-CDP REMOVE
test_http_server = TestHTTPServer.init(testHTTPHandler); test_http_server = TestHTTPServer.init(testHTTPHandler);
{ {
@@ -465,10 +462,9 @@ test "tests:beforeAll" {
} }
test "tests:afterAll" { test "tests:afterAll" {
// @ZIGDOM-CDP if (test_cdp_server) |*server| {
// if (test_cdp_server) |*server| { server.deinit();
// server.deinit(); }
// }
if (test_http_server) |*server| { if (test_http_server) |*server| {
server.deinit(); server.deinit();
} }
@@ -477,20 +473,19 @@ test "tests:afterAll" {
test_app.deinit(); test_app.deinit();
} }
// @ZIGDOM-CDP fn serveCDP(wg: *std.Thread.WaitGroup) !void {
// fn serveCDP(wg: *std.Thread.WaitGroup) !void { const address = try std.net.Address.parseIp("127.0.0.1", 9583);
// const address = try std.net.Address.parseIp("127.0.0.1", 9583); test_cdp_server = try Server.init(test_app, address);
// test_cdp_server = try Server.init(test_app, address);
// var server = try Server.init(test_app, address); var server = try Server.init(test_app, address);
// defer server.deinit(); defer server.deinit();
// wg.finish(); wg.finish();
// test_cdp_server.?.run(address, 5) catch |err| { test_cdp_server.?.run(address, 5) catch |err| {
// std.debug.print("CDP server error: {}", .{err}); std.debug.print("CDP server error: {}", .{err});
// return err; return err;
// }; };
// } }
fn testHTTPHandler(req: *std.http.Server.Request) !void { fn testHTTPHandler(req: *std.http.Server.Request) !void {
const path = req.head.target; const path = req.head.target;