mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 23:23:28 +00:00
isolated polyfill + create when needed
This commit is contained in:
@@ -163,11 +163,16 @@ pub const Session = struct {
|
|||||||
|
|
||||||
// start JS env
|
// start JS env
|
||||||
log.debug("start new js scope", .{});
|
log.debug("start new js scope", .{});
|
||||||
|
// Inform CDP the main page has been created such that additional context for other Worlds can be created as well
|
||||||
|
self.browser.notification.dispatch(.page_created, page);
|
||||||
|
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn removePage(self: *Session) void {
|
pub fn removePage(self: *Session) void {
|
||||||
|
// Inform CDP the page is going to be removed, allowing other worlds to remove themselves before the main one
|
||||||
|
self.browser.notification.dispatch(.page_remove, .{});
|
||||||
|
|
||||||
std.debug.assert(self.page != null);
|
std.debug.assert(self.page != null);
|
||||||
// Reset all existing callbacks.
|
// Reset all existing callbacks.
|
||||||
self.browser.app.loop.resetJS();
|
self.browser.app.loop.resetJS();
|
||||||
|
|||||||
@@ -340,6 +340,8 @@ pub fn BrowserContext(comptime CDP_T: type) type {
|
|||||||
self.node_search_list = Node.Search.List.init(allocator, &self.node_registry);
|
self.node_search_list = Node.Search.List.init(allocator, &self.node_registry);
|
||||||
errdefer self.deinit();
|
errdefer self.deinit();
|
||||||
|
|
||||||
|
try cdp.browser.notification.register(.page_remove, self, onPageRemove);
|
||||||
|
try cdp.browser.notification.register(.page_created, self, onPageCreated);
|
||||||
try cdp.browser.notification.register(.page_navigate, self, onPageNavigate);
|
try cdp.browser.notification.register(.page_navigate, self, onPageNavigate);
|
||||||
try cdp.browser.notification.register(.page_navigated, self, onPageNavigated);
|
try cdp.browser.notification.register(.page_navigated, self, onPageNavigated);
|
||||||
}
|
}
|
||||||
@@ -365,7 +367,7 @@ pub fn BrowserContext(comptime CDP_T: type) type {
|
|||||||
self.node_search_list.reset();
|
self.node_search_list.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn createIsolatedWorld(self: *Self, page: *Page) !void {
|
pub fn createIsolatedWorld(self: *Self, page: *Page, world_name: []const u8, grant_universal_access: bool) !*IsolatedWorld {
|
||||||
if (self.isolated_world != null) {
|
if (self.isolated_world != null) {
|
||||||
return error.CurrentlyOnly1IsolatedWorldSupported;
|
return error.CurrentlyOnly1IsolatedWorldSupported;
|
||||||
}
|
}
|
||||||
@@ -374,19 +376,14 @@ pub fn BrowserContext(comptime CDP_T: type) type {
|
|||||||
errdefer executor.deinit();
|
errdefer executor.deinit();
|
||||||
|
|
||||||
self.isolated_world = .{
|
self.isolated_world = .{
|
||||||
.name = "",
|
.name = try self.arena.dupe(u8, world_name),
|
||||||
.scope = undefined,
|
.scope = undefined,
|
||||||
.executor = executor,
|
.executor = executor,
|
||||||
.grant_universal_access = true,
|
.grant_universal_access = grant_universal_access,
|
||||||
};
|
};
|
||||||
var world = &self.isolated_world.?;
|
const world = &self.isolated_world.?;
|
||||||
|
try world.createContext(page);
|
||||||
// The isolate world must share at least some of the state with the related page, specifically the DocumentHTML
|
return world;
|
||||||
// (assuming grantUniveralAccess will be set to True!).
|
|
||||||
// We just created the world and the page. The page's state lives in the session, but is update on navigation.
|
|
||||||
// This also means this pointer becomes invalid after removePage untill a new page is created.
|
|
||||||
// Currently we have only 1 page/frame and thus also only 1 state in the isolate world.
|
|
||||||
world.scope = try world.executor.startScope(&page.window, &page.state, {}, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nodeWriter(self: *Self, node: *const Node, opts: Node.Writer.Opts) Node.Writer {
|
pub fn nodeWriter(self: *Self, node: *const Node, opts: Node.Writer.Opts) Node.Writer {
|
||||||
@@ -403,6 +400,16 @@ pub fn BrowserContext(comptime CDP_T: type) type {
|
|||||||
return if (raw_url.len == 0) null else raw_url;
|
return if (raw_url.len == 0) null else raw_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn onPageRemove(ctx: *anyopaque, _: Notification.PageRemove) !void {
|
||||||
|
const self: *Self = @alignCast(@ptrCast(ctx));
|
||||||
|
return @import("domains/page.zig").pageRemove(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onPageCreated(ctx: *anyopaque, page: *Page) !void {
|
||||||
|
const self: *Self = @alignCast(@ptrCast(ctx));
|
||||||
|
return @import("domains/page.zig").pageCreated(self, page);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn onPageNavigate(ctx: *anyopaque, data: *const Notification.PageNavigate) !void {
|
pub fn onPageNavigate(ctx: *anyopaque, data: *const Notification.PageNavigate) !void {
|
||||||
const self: *Self = @alignCast(@ptrCast(ctx));
|
const self: *Self = @alignCast(@ptrCast(ctx));
|
||||||
return @import("domains/page.zig").pageNavigate(self, data);
|
return @import("domains/page.zig").pageNavigate(self, data);
|
||||||
@@ -506,12 +513,26 @@ pub fn BrowserContext(comptime CDP_T: type) type {
|
|||||||
/// An object id is unique across all contexts, different object ids can refer to the same Node in different contexts.
|
/// An object id is unique across all contexts, different object ids can refer to the same Node in different contexts.
|
||||||
const IsolatedWorld = struct {
|
const IsolatedWorld = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
scope: *Env.Scope,
|
scope: ?*Env.Scope,
|
||||||
executor: Env.Executor,
|
executor: Env.Executor,
|
||||||
grant_universal_access: bool,
|
grant_universal_access: bool,
|
||||||
|
|
||||||
pub fn deinit(self: *IsolatedWorld) void {
|
pub fn deinit(self: *IsolatedWorld) void {
|
||||||
self.executor.deinit();
|
self.executor.deinit();
|
||||||
|
self.scope = null;
|
||||||
|
}
|
||||||
|
pub fn removeContext(self: *IsolatedWorld) void {
|
||||||
|
self.executor.endScope();
|
||||||
|
self.scope = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The isolate world must share at least some of the state with the related page, specifically the DocumentHTML
|
||||||
|
// (assuming grantUniveralAccess will be set to True!).
|
||||||
|
// We just created the world and the page. The page's state lives in the session, but is update on navigation.
|
||||||
|
// This also means this pointer becomes invalid after removePage untill a new page is created.
|
||||||
|
// Currently we have only 1 page/frame and thus also only 1 state in the isolate world.
|
||||||
|
pub fn createContext(self: *IsolatedWorld, page: *Page) !void {
|
||||||
|
self.scope = try self.executor.startScope(&page.window, &page.state, {}, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ fn resolveNode(cmd: anytype) !void {
|
|||||||
if (params.executionContextId) |context_id| {
|
if (params.executionContextId) |context_id| {
|
||||||
if (scope.context.debugContextId() != context_id) {
|
if (scope.context.debugContextId() != context_id) {
|
||||||
const isolated_world = bc.isolated_world orelse return error.ContextNotFound;
|
const isolated_world = bc.isolated_world orelse return error.ContextNotFound;
|
||||||
scope = isolated_world.scope;
|
scope = isolated_world.scope orelse return error.ContextNotFound;
|
||||||
|
|
||||||
if (scope.context.debugContextId() != context_id) return error.ContextNotFound;
|
if (scope.context.debugContextId() != context_id) return error.ContextNotFound;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const std = @import("std");
|
|||||||
const runtime = @import("runtime.zig");
|
const runtime = @import("runtime.zig");
|
||||||
const URL = @import("../../url.zig").URL;
|
const URL = @import("../../url.zig").URL;
|
||||||
const Notification = @import("../../notification.zig").Notification;
|
const Notification = @import("../../notification.zig").Notification;
|
||||||
|
const Page = @import("../../browser/browser.zig").Page;
|
||||||
|
|
||||||
pub fn processMessage(cmd: anytype) !void {
|
pub fn processMessage(cmd: anytype) !void {
|
||||||
const action = std.meta.stringToEnum(enum {
|
const action = std.meta.stringToEnum(enum {
|
||||||
@@ -112,15 +113,16 @@ fn createIsolatedWorld(cmd: anytype) !void {
|
|||||||
}
|
}
|
||||||
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||||
|
|
||||||
const world = &bc.isolated_world.?;
|
const page = bc.session.currentPage().?;
|
||||||
world.name = try bc.arena.dupe(u8, params.worldName);
|
const world = try bc.createIsolatedWorld(page, params.worldName, params.grantUniveralAccess);
|
||||||
world.grant_universal_access = params.grantUniveralAccess;
|
const scope = world.scope.?;
|
||||||
|
|
||||||
// Create the auxdata json for the contextCreated event
|
// Create the auxdata json for the contextCreated event
|
||||||
// Calling contextCreated will assign a Id to the context and send the contextCreated event
|
// Calling contextCreated will assign a Id to the context and send the contextCreated event
|
||||||
const aux_data = try std.fmt.allocPrint(cmd.arena, "{{\"isDefault\":false,\"type\":\"isolated\",\"frameId\":\"{s}\"}}", .{params.frameId});
|
const aux_data = try std.fmt.allocPrint(cmd.arena, "{{\"isDefault\":false,\"type\":\"isolated\",\"frameId\":\"{s}\"}}", .{params.frameId});
|
||||||
bc.inspector.contextCreated(world.scope, world.name, "", aux_data, false);
|
bc.inspector.contextCreated(scope, world.name, "", aux_data, false);
|
||||||
|
|
||||||
return cmd.sendResult(.{ .executionContextId = world.scope.context.debugContextId() }, .{});
|
return cmd.sendResult(.{ .executionContextId = scope.context.debugContextId() }, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn navigate(cmd: anytype) !void {
|
fn navigate(cmd: anytype) !void {
|
||||||
@@ -230,12 +232,11 @@ pub fn pageNavigate(bc: anytype, event: *const Notification.PageNavigate) !void
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bc.isolated_world) |*isolated_world| {
|
if (bc.isolated_world) |*isolated_world| {
|
||||||
const aux_json = try std.fmt.bufPrint(&buffer, "{{\"isDefault\":false,\"type\":\"isolated\",\"frameId\":\"{s}\"}}", .{target_id});
|
const aux_json = try std.fmt.bufPrint(&buffer, "{{\"isDefault\":false,\"type\":\"isolated\",\"frameId\":\"{s}\"}}", .{target_id});
|
||||||
// Calling contextCreated will assign a new Id to the context and send the contextCreated event
|
// Calling contextCreated will assign a new Id to the context and send the contextCreated event
|
||||||
bc.inspector.contextCreated(
|
bc.inspector.contextCreated(
|
||||||
isolated_world.scope,
|
isolated_world.scope.?,
|
||||||
isolated_world.name,
|
isolated_world.name,
|
||||||
"://",
|
"://",
|
||||||
aux_json,
|
aux_json,
|
||||||
@@ -244,6 +245,23 @@ pub fn pageNavigate(bc: anytype, event: *const Notification.PageNavigate) !void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pageRemove(bc: anytype) !void {
|
||||||
|
// The main page is going to be removed, we need to remove contexts from other worlds first.
|
||||||
|
if (bc.isolated_world) |*isolated_world| {
|
||||||
|
isolated_world.removeContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pageCreated(bc: anytype, page: *Page) !void {
|
||||||
|
if (bc.isolated_world) |*isolated_world| {
|
||||||
|
// We need to recreate the isolated world context
|
||||||
|
try isolated_world.createContext(page);
|
||||||
|
|
||||||
|
const polyfill = @import("../../browser/polyfill/polyfill.zig");
|
||||||
|
try polyfill.load(bc.arena, isolated_world.scope.?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pageNavigated(bc: anytype, event: *const Notification.PageNavigated) !void {
|
pub fn pageNavigated(bc: anytype, event: *const Notification.PageNavigated) !void {
|
||||||
// I don't think it's possible that we get these notifications and don't
|
// I don't think it's possible that we get these notifications and don't
|
||||||
// have these things setup.
|
// have these things setup.
|
||||||
|
|||||||
@@ -125,8 +125,6 @@ fn createTarget(cmd: anytype) !void {
|
|||||||
bc.target_id = target_id;
|
bc.target_id = target_id;
|
||||||
|
|
||||||
var page = try bc.session.createPage();
|
var page = try bc.session.createPage();
|
||||||
try bc.createIsolatedWorld(page);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const aux_data = try std.fmt.allocPrint(cmd.arena, "{{\"isDefault\":true,\"type\":\"default\",\"frameId\":\"{s}\"}}", .{target_id});
|
const aux_data = try std.fmt.allocPrint(cmd.arena, "{{\"isDefault\":true,\"type\":\"default\",\"frameId\":\"{s}\"}}", .{target_id});
|
||||||
bc.inspector.contextCreated(
|
bc.inspector.contextCreated(
|
||||||
|
|||||||
@@ -55,18 +55,24 @@ pub const Notification = struct {
|
|||||||
node_pool: std.heap.MemoryPool(Node),
|
node_pool: std.heap.MemoryPool(Node),
|
||||||
|
|
||||||
const EventListeners = struct {
|
const EventListeners = struct {
|
||||||
|
page_remove: List = .{},
|
||||||
|
page_created: List = .{},
|
||||||
page_navigate: List = .{},
|
page_navigate: List = .{},
|
||||||
page_navigated: List = .{},
|
page_navigated: List = .{},
|
||||||
notification_created: List = .{},
|
notification_created: List = .{},
|
||||||
};
|
};
|
||||||
|
|
||||||
const Events = union(enum) {
|
const Events = union(enum) {
|
||||||
|
page_remove: PageRemove,
|
||||||
|
page_created: *browser.Page,
|
||||||
page_navigate: *const PageNavigate,
|
page_navigate: *const PageNavigate,
|
||||||
page_navigated: *const PageNavigated,
|
page_navigated: *const PageNavigated,
|
||||||
notification_created: *Notification,
|
notification_created: *Notification,
|
||||||
};
|
};
|
||||||
const EventType = std.meta.FieldEnum(Events);
|
const EventType = std.meta.FieldEnum(Events);
|
||||||
|
|
||||||
|
pub const PageRemove = struct {};
|
||||||
|
|
||||||
pub const PageNavigate = struct {
|
pub const PageNavigate = struct {
|
||||||
timestamp: u32,
|
timestamp: u32,
|
||||||
url: *const URL,
|
url: *const URL,
|
||||||
|
|||||||
Reference in New Issue
Block a user