mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 09:35:59 -04:00
Merge branch 'main' into agent
This commit is contained in:
2
.github/workflows/e2e-test.yml
vendored
2
.github/workflows/e2e-test.yml
vendored
@@ -258,7 +258,7 @@ jobs:
|
||||
|
||||
- name: start http
|
||||
run: |
|
||||
go run ws/main.go & echo $! > WS.pid
|
||||
go run runner/main.go -serve & echo $! > WS.pid
|
||||
sleep 2
|
||||
|
||||
- name: run lightpanda in cgroup
|
||||
|
||||
7
.github/workflows/wpt.yml
vendored
7
.github/workflows/wpt.yml
vendored
@@ -36,13 +36,8 @@ jobs:
|
||||
os: ${{env.OS}}
|
||||
arch: ${{env.ARCH}}
|
||||
|
||||
- uses: ./.github/actions/v8-snapshot
|
||||
with:
|
||||
os: ${{env.OS}}
|
||||
arch: ${{env.ARCH}}
|
||||
|
||||
- name: zig build release
|
||||
run: zig build -Dsnapshot_path=../../snapshot.bin -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast -Dcpu=generic
|
||||
run: zig build -Dwpt_extensions -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast -Dcpu=generic
|
||||
|
||||
- name: upload artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
|
||||
@@ -722,7 +722,6 @@ fn scheduleNavigationWithArena(originator: *Frame, arena: Allocator, request_url
|
||||
if (!opts.force and URL.eqlDocument(target.url, resolved_url)) {
|
||||
target.url = try target.arena.dupeZ(u8, resolved_url);
|
||||
target.window._location = try Location.init(target.url, target);
|
||||
target.document._location = target.window._location;
|
||||
if (target.parent == null) {
|
||||
try session.navigation.updateEntries(target.url, opts.kind, target, true);
|
||||
}
|
||||
|
||||
@@ -113,6 +113,9 @@ pub fn createPage(self: *Session) !*Frame {
|
||||
|
||||
self.page = @as(Page, undefined);
|
||||
const page = &self.page.?;
|
||||
|
||||
errdefer self.page = null;
|
||||
|
||||
try Page.init(page, self, self.nextFrameId());
|
||||
const frame = &page.frame;
|
||||
|
||||
@@ -184,6 +187,9 @@ pub fn replacePage(self: *Session) !*Frame {
|
||||
|
||||
self.page = @as(Page, undefined);
|
||||
const page = &self.page.?;
|
||||
|
||||
errdefer self.page = null;
|
||||
|
||||
try Page.init(page, self, frame_id);
|
||||
return &page.frame;
|
||||
}
|
||||
@@ -302,8 +308,9 @@ fn processFrameNavigation(self: *Session, frame: *Frame, qn: *QueuedNavigation)
|
||||
frame.deinit(true);
|
||||
frame.* = undefined;
|
||||
|
||||
try Frame.init(frame, frame_id, page, parent);
|
||||
errdefer {
|
||||
// If anything fails from this point on, frame.deinit will be called
|
||||
// and we need to remove the frame from the parent's frame list.
|
||||
for (parent.child_frames.items, 0..) |f, i| {
|
||||
if (f == frame) {
|
||||
parent.child_frames_sorted = false;
|
||||
@@ -311,6 +318,10 @@ fn processFrameNavigation(self: *Session, frame: *Frame, qn: *QueuedNavigation)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try Frame.init(frame, frame_id, page, parent);
|
||||
errdefer {
|
||||
if (parent_notified) {
|
||||
parent._pending_loads -= 1;
|
||||
}
|
||||
@@ -349,11 +360,16 @@ fn processRootQueuedNavigation(self: *Session) !void {
|
||||
|
||||
self.page = @as(Page, undefined);
|
||||
const page = &self.page.?;
|
||||
|
||||
errdefer self.page = null;
|
||||
|
||||
try Page.init(page, self, frame_id);
|
||||
const new_frame = &page.frame;
|
||||
|
||||
// Creates a new NavigationEventTarget for this frame.
|
||||
try self.navigation.onNewFrame(new_frame);
|
||||
self.navigation.onNewFrame(new_frame) catch |err| {
|
||||
log.err(.browser, "createPage onNewNewFrame", .{ .err = err });
|
||||
};
|
||||
|
||||
// start JS env
|
||||
// Inform CDP the main frame has been created such that additional context for other Worlds can be created as well
|
||||
|
||||
@@ -587,6 +587,12 @@ fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *const v8.F
|
||||
const prototype = v8.v8__FunctionTemplate__PrototypeTemplate(template);
|
||||
const signature = v8.v8__Signature__New(isolate, template);
|
||||
|
||||
// Namespace objects (e.g. console) expose their members as own properties
|
||||
// of each instance rather than via the prototype, so Object.entries(...)
|
||||
// returns them. See https://console.spec.whatwg.org/#console-namespace.
|
||||
const own_properties = @hasDecl(JsApi.Meta, "own_properties") and JsApi.Meta.own_properties;
|
||||
const member_template = if (own_properties) instance else prototype;
|
||||
|
||||
const declarations = @typeInfo(JsApi).@"struct".decls;
|
||||
var has_named_index_getter = false;
|
||||
|
||||
@@ -652,7 +658,7 @@ fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *const v8.F
|
||||
if (value.static) {
|
||||
v8.v8__Template__Set(@ptrCast(template), js_name, @ptrCast(function_template), v8.None);
|
||||
} else {
|
||||
v8.v8__Template__Set(@ptrCast(prototype), js_name, @ptrCast(function_template), v8.None);
|
||||
v8.v8__Template__Set(@ptrCast(member_template), js_name, @ptrCast(function_template), v8.None);
|
||||
}
|
||||
},
|
||||
bridge.Indexed => {
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<script id=entries>
|
||||
{
|
||||
const entries = Object.entries(console);
|
||||
testing.expectEqual(true, entries.length > 10);
|
||||
|
||||
const log_entry = entries.find((e) => e[0] == 'log');
|
||||
testing.expectEqual(true, log_entry[1] === console.log);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="time">
|
||||
// should not crash
|
||||
console.time();
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
testing.expectEqual('function', typeof CSS.supports);
|
||||
</script>
|
||||
|
||||
<script id="entries">
|
||||
const entries = Object.entries(CSS);
|
||||
testing.expectEqual(true, entries.length >= 2);
|
||||
</script>
|
||||
|
||||
<script id="escape_basic">
|
||||
{
|
||||
testing.expectEqual('hello', CSS.escape('hello'));
|
||||
|
||||
@@ -69,6 +69,77 @@
|
||||
testing.expectEqual(['mark', 'measure'], PerformanceObserver.supportedEntryTypes);
|
||||
</script>
|
||||
|
||||
<script id="list_get_entries_by_type">
|
||||
{
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
const observer = new PerformanceObserver((list) => {
|
||||
const marks = list.getEntriesByType("mark");
|
||||
testing.expectEqual(true, marks instanceof Array);
|
||||
testing.expectEqual(2, marks.length);
|
||||
testing.expectEqual("lbt1", marks[0].name);
|
||||
testing.expectEqual("mark", marks[0].entryType);
|
||||
testing.expectEqual("lbt2", marks[1].name);
|
||||
|
||||
const measures = list.getEntriesByType("measure");
|
||||
testing.expectEqual(1, measures.length);
|
||||
testing.expectEqual("lbtMeasure", measures[0].name);
|
||||
testing.expectEqual("measure", measures[0].entryType);
|
||||
|
||||
// A type that's observed but has no entries returns an empty array.
|
||||
const resources = list.getEntriesByType("resource");
|
||||
testing.expectEqual(true, resources instanceof Array);
|
||||
testing.expectEqual(0, resources.length);
|
||||
|
||||
observer.disconnect();
|
||||
});
|
||||
|
||||
observer.observe({ entryTypes: ["mark", "measure"] });
|
||||
performance.mark("lbt1");
|
||||
performance.mark("lbt2");
|
||||
performance.measure("lbtMeasure");
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="list_get_entries_by_name">
|
||||
{
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
const observer = new PerformanceObserver((list) => {
|
||||
// Without entryType, any type matching the name is returned.
|
||||
const shared = list.getEntriesByName("lbnShared");
|
||||
testing.expectEqual(true, shared instanceof Array);
|
||||
testing.expectEqual(2, shared.length);
|
||||
|
||||
// With entryType, only entries of that type are returned.
|
||||
const sharedMark = list.getEntriesByName("lbnShared", "mark");
|
||||
testing.expectEqual(1, sharedMark.length);
|
||||
testing.expectEqual("mark", sharedMark[0].entryType);
|
||||
|
||||
const sharedMeasure = list.getEntriesByName("lbnShared", "measure");
|
||||
testing.expectEqual(1, sharedMeasure.length);
|
||||
testing.expectEqual("measure", sharedMeasure[0].entryType);
|
||||
|
||||
// Name mismatch -> empty array.
|
||||
const missing = list.getEntriesByName("lbnMissing");
|
||||
testing.expectEqual(0, missing.length);
|
||||
|
||||
// Name matches but type doesn't -> empty array.
|
||||
const wrongType = list.getEntriesByName("lbnOnlyMark", "measure");
|
||||
testing.expectEqual(0, wrongType.length);
|
||||
|
||||
observer.disconnect();
|
||||
});
|
||||
|
||||
observer.observe({ entryTypes: ["mark", "measure"] });
|
||||
performance.mark("lbnShared");
|
||||
performance.measure("lbnShared");
|
||||
performance.mark("lbnOnlyMark");
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="buffered_option">
|
||||
{
|
||||
// Clear marks from previous tests so we get a precise count
|
||||
|
||||
@@ -165,6 +165,11 @@ pub const JsApi = struct {
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "Css";
|
||||
|
||||
// Per the CSSOM spec, CSS is a namespace object — members are own
|
||||
// properties so Object.entries(CSS) returns them.
|
||||
pub const own_properties = true;
|
||||
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
pub const empty_with_no_proto = true;
|
||||
|
||||
@@ -195,6 +195,10 @@ pub const JsApi = struct {
|
||||
pub const Meta = struct {
|
||||
pub const name = "Console";
|
||||
|
||||
// Per the console spec, members are own properties of the namespace
|
||||
// object, so Object.entries(console) returns them.
|
||||
pub const own_properties = true;
|
||||
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
@@ -197,7 +197,8 @@ pub fn getCurrentScript(self: *const HTMLDocument) ?*Element.Html.Script {
|
||||
}
|
||||
|
||||
pub fn getLocation(self: *const HTMLDocument) ?*@import("Location.zig") {
|
||||
return self._proto._location;
|
||||
const frame = self._proto._frame orelse return null;
|
||||
return frame.window._location;
|
||||
}
|
||||
|
||||
pub fn setLocation(self: *HTMLDocument, url: [:0]const u8, frame: *Frame) !void {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
const std = @import("std");
|
||||
const js = @import("../js/js.zig");
|
||||
const Frame = @import("../Frame.zig");
|
||||
const datetime = @import("../../datetime.zig");
|
||||
|
||||
const EventCounts = @import("EventCounts.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub fn registerTypes() []const type {
|
||||
return &.{ Performance, Entry, Mark, Measure, PerformanceTiming, PerformanceNavigation };
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
const Performance = @This();
|
||||
|
||||
_time_origin: u64,
|
||||
@@ -188,32 +189,34 @@ pub fn getEntries(self: *const Performance) []*Entry {
|
||||
}
|
||||
|
||||
pub fn getEntriesByType(self: *const Performance, entry_type: []const u8, frame: *Frame) ![]const *Entry {
|
||||
var result: std.ArrayList(*Entry) = .empty;
|
||||
|
||||
for (self._entries.items) |entry| {
|
||||
if (std.mem.eql(u8, entry.getEntryType(), entry_type)) {
|
||||
try result.append(frame.call_arena, entry);
|
||||
}
|
||||
}
|
||||
|
||||
return result.items;
|
||||
return filterEntriesByType(frame.call_arena, self._entries.items, entry_type);
|
||||
}
|
||||
|
||||
pub fn getEntriesByName(self: *const Performance, name: []const u8, entry_type: ?[]const u8, frame: *Frame) ![]const *Entry {
|
||||
return filterEntriesByName(frame.call_arena, self._entries.items, name, entry_type);
|
||||
}
|
||||
|
||||
// Also used by PerformanceObserver
|
||||
pub fn filterEntriesByType(arena: Allocator, list: []*Entry, entry_type: []const u8) ![]const *Entry {
|
||||
var result: std.ArrayList(*Entry) = .empty;
|
||||
for (list) |entry| {
|
||||
if (std.mem.eql(u8, entry.getEntryType(), entry_type)) {
|
||||
try result.append(arena, entry);
|
||||
}
|
||||
}
|
||||
return result.items;
|
||||
}
|
||||
|
||||
// Also used by PerformanceObserver
|
||||
pub fn filterEntriesByName(arena: Allocator, list: []*Entry, name: []const u8, entry_type: ?[]const u8) ![]const *Entry {
|
||||
var result: std.ArrayList(*Entry) = .empty;
|
||||
|
||||
for (self._entries.items) |entry| {
|
||||
for (list) |entry| {
|
||||
if (!std.mem.eql(u8, entry._name, name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const et = entry_type orelse {
|
||||
try result.append(frame.call_arena, entry);
|
||||
continue;
|
||||
};
|
||||
|
||||
if (std.mem.eql(u8, entry.getEntryType(), et)) {
|
||||
try result.append(frame.call_arena, entry);
|
||||
if (entry_type == null or std.mem.eql(u8, entry.getEntryType(), entry_type.?)) {
|
||||
try result.append(arena, entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ const Frame = @import("../Frame.zig");
|
||||
const Performance = @import("Performance.zig");
|
||||
|
||||
const log = lp.log;
|
||||
const Execution = js.Execution;
|
||||
|
||||
pub fn registerTypes() []const type {
|
||||
return &.{ PerformanceObserver, EntryList };
|
||||
@@ -203,10 +204,18 @@ pub const JsApi = struct {
|
||||
pub const EntryList = struct {
|
||||
_entries: []*Performance.Entry,
|
||||
|
||||
pub fn getEntries(self: *const EntryList) []*Performance.Entry {
|
||||
pub fn getEntries(self: *const EntryList) []const *Performance.Entry {
|
||||
return self._entries;
|
||||
}
|
||||
|
||||
pub fn getEntriesByType(self: *const EntryList, entry_type: []const u8, exec: *Execution) ![]const *Performance.Entry {
|
||||
return Performance.filterEntriesByType(exec.call_arena, self._entries, entry_type);
|
||||
}
|
||||
|
||||
pub fn getEntriesByName(self: *const EntryList, name: []const u8, entry_type: ?[]const u8, exec: *Execution) ![]const *Performance.Entry {
|
||||
return Performance.filterEntriesByName(exec.call_arena, self._entries, name, entry_type);
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(EntryList);
|
||||
|
||||
@@ -217,6 +226,8 @@ pub const EntryList = struct {
|
||||
};
|
||||
|
||||
pub const getEntries = bridge.function(EntryList.getEntries, .{});
|
||||
pub const getEntriesByType = bridge.function(EntryList.getEntriesByType, .{});
|
||||
pub const getEntriesByName = bridge.function(EntryList.getEntriesByName, .{});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -19,15 +19,15 @@
|
||||
const std = @import("std");
|
||||
|
||||
const js = @import("../js/js.zig");
|
||||
const Session = @import("../Session.zig");
|
||||
const Page = @import("../Page.zig");
|
||||
|
||||
// This type is only included when the binary is built with the -Dwpt_extensions flag
|
||||
const WebDriver = @This();
|
||||
|
||||
_pad: bool = false,
|
||||
|
||||
pub fn deleteAllCookies(_: *const WebDriver, session: *Session) void {
|
||||
session.cookie_jar.clearRetainingCapacity();
|
||||
pub fn deleteAllCookies(_: *const WebDriver, page: *Page) void {
|
||||
page.session.cookie_jar.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
|
||||
@@ -170,14 +170,14 @@ pub fn getOrigin(self: *const Window) []const u8 {
|
||||
return self._frame.origin orelse "null";
|
||||
}
|
||||
|
||||
pub fn getLocation(self: *const Window) *Location {
|
||||
return self._location;
|
||||
}
|
||||
|
||||
pub fn getSelection(self: *const Window) *Selection {
|
||||
return &self._document._selection;
|
||||
}
|
||||
|
||||
pub fn getLocation(self: *const Window) *Location {
|
||||
return self._location;
|
||||
}
|
||||
|
||||
pub fn setLocation(self: *Window, url: [:0]const u8, frame: *Frame) !void {
|
||||
return frame.scheduleNavigation(url, .{ .reason = .script, .kind = .{ .push = null } }, .{ .script = self._frame });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user