mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 20:54:43 +00:00
Store Env.Contexts in an [fixed-length] array.
We currently store all of the env's contexts in an ArrayList. When performing micro/macro tasks, we iterate through this list and perform the micro/macro tasks. This can result in the ArrayList being invalidated (e.g. a microtask can result in a context being created, a promise resolving and creating an iframe). Invalidating the arrylist while we iterate through it is a use-after-free. This commit stores contexts in a fixed array (64) so that it doesn't move. Iteration is slower, unfortunately, as the new `env.context_count` has to be checked. Fixes WPT crash on /html/cross-origin-embedder-policy/cross-origin-isolated-permission-iframe.https.window.html url=http://web-platform.test:8000/html/cross-origin-embedder-policy/cross-origin-isolated-permission-iframe.https.window.html This commit also prevents microtasks execution from causing microtask execution. On the above test, I saw runMicrotasks which I don't think we're supposed to do.
This commit is contained in:
@@ -62,7 +62,8 @@ platform: *const Platform,
|
|||||||
// the global isolate
|
// the global isolate
|
||||||
isolate: js.Isolate,
|
isolate: js.Isolate,
|
||||||
|
|
||||||
contexts: std.ArrayList(*js.Context),
|
contexts: [64]*Context,
|
||||||
|
context_count: usize,
|
||||||
|
|
||||||
// just kept around because we need to free it on deinit
|
// just kept around because we need to free it on deinit
|
||||||
isolate_params: *v8.CreateParams,
|
isolate_params: *v8.CreateParams,
|
||||||
@@ -85,6 +86,8 @@ inspector: ?*Inspector,
|
|||||||
// which an be created once per isolaet.
|
// which an be created once per isolaet.
|
||||||
private_symbols: PrivateSymbols,
|
private_symbols: PrivateSymbols,
|
||||||
|
|
||||||
|
microtask_queues_are_running: bool,
|
||||||
|
|
||||||
pub const InitOpts = struct {
|
pub const InitOpts = struct {
|
||||||
with_inspector: bool = false,
|
with_inspector: bool = false,
|
||||||
};
|
};
|
||||||
@@ -203,7 +206,8 @@ pub fn init(app: *App, opts: InitOpts) !Env {
|
|||||||
return .{
|
return .{
|
||||||
.app = app,
|
.app = app,
|
||||||
.context_id = 0,
|
.context_id = 0,
|
||||||
.contexts = .empty,
|
.contexts = undefined,
|
||||||
|
.context_count = 0,
|
||||||
.isolate = isolate,
|
.isolate = isolate,
|
||||||
.platform = &app.platform,
|
.platform = &app.platform,
|
||||||
.templates = templates,
|
.templates = templates,
|
||||||
@@ -211,15 +215,16 @@ pub fn init(app: *App, opts: InitOpts) !Env {
|
|||||||
.inspector = inspector,
|
.inspector = inspector,
|
||||||
.global_template = global_eternal,
|
.global_template = global_eternal,
|
||||||
.private_symbols = private_symbols,
|
.private_symbols = private_symbols,
|
||||||
|
.microtask_queues_are_running = false,
|
||||||
.eternal_function_templates = eternal_function_templates,
|
.eternal_function_templates = eternal_function_templates,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Env) void {
|
pub fn deinit(self: *Env) void {
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
std.debug.assert(self.contexts.items.len == 0);
|
std.debug.assert(self.context_count == 0);
|
||||||
}
|
}
|
||||||
for (self.contexts.items) |ctx| {
|
for (self.contexts[0..self.context_count]) |ctx| {
|
||||||
ctx.deinit();
|
ctx.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,8 +233,6 @@ pub fn deinit(self: *Env) void {
|
|||||||
i.deinit(allocator);
|
i.deinit(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.contexts.deinit(allocator);
|
|
||||||
|
|
||||||
allocator.free(self.templates);
|
allocator.free(self.templates);
|
||||||
allocator.free(self.eternal_function_templates);
|
allocator.free(self.eternal_function_templates);
|
||||||
self.private_symbols.deinit();
|
self.private_symbols.deinit();
|
||||||
@@ -312,14 +315,22 @@ pub fn createContext(self: *Env, page: *Page) !*Context {
|
|||||||
// a v8 context, we can get our context out
|
// a v8 context, we can get our context out
|
||||||
v8.v8__Context__SetAlignedPointerInEmbedderData(v8_context, 1, @ptrCast(context));
|
v8.v8__Context__SetAlignedPointerInEmbedderData(v8_context, 1, @ptrCast(context));
|
||||||
|
|
||||||
try self.contexts.append(self.app.allocator, context);
|
const count = self.context_count;
|
||||||
|
if (count >= self.contexts.len) {
|
||||||
|
return error.TooManyContexts;
|
||||||
|
}
|
||||||
|
self.contexts[count] = context;
|
||||||
|
self.context_count = count + 1;
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroyContext(self: *Env, context: *Context) void {
|
pub fn destroyContext(self: *Env, context: *Context) void {
|
||||||
for (self.contexts.items, 0..) |ctx, i| {
|
for (self.contexts[0..self.context_count], 0..) |ctx, i| {
|
||||||
if (ctx == context) {
|
if (ctx == context) {
|
||||||
_ = self.contexts.swapRemove(i);
|
// Swap with last element and decrement count
|
||||||
|
self.context_count -= 1;
|
||||||
|
self.contexts[i] = self.contexts[self.context_count];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -339,16 +350,24 @@ pub fn destroyContext(self: *Env, context: *Context) void {
|
|||||||
context.deinit();
|
context.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runMicrotasks(self: *const Env) void {
|
pub fn runMicrotasks(self: *Env) void {
|
||||||
const v8_isolate = self.isolate.handle;
|
if (self.microtask_queues_are_running == false) {
|
||||||
for (self.contexts.items) |ctx| {
|
const v8_isolate = self.isolate.handle;
|
||||||
v8.v8__MicrotaskQueue__PerformCheckpoint(ctx.microtask_queue, v8_isolate);
|
|
||||||
|
self.microtask_queues_are_running = true;
|
||||||
|
defer self.microtask_queues_are_running = false;
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < self.context_count) : (i += 1) {
|
||||||
|
const ctx = self.contexts[i];
|
||||||
|
v8.v8__MicrotaskQueue__PerformCheckpoint(ctx.microtask_queue, v8_isolate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runMacrotasks(self: *Env) !?u64 {
|
pub fn runMacrotasks(self: *Env) !?u64 {
|
||||||
var ms_to_next_task: ?u64 = null;
|
var ms_to_next_task: ?u64 = null;
|
||||||
for (self.contexts.items) |ctx| {
|
for (self.contexts[0..self.context_count]) |ctx| {
|
||||||
if (comptime builtin.is_test == false) {
|
if (comptime builtin.is_test == false) {
|
||||||
// I hate this comptime check as much as you do. But we have tests
|
// I hate this comptime check as much as you do. But we have tests
|
||||||
// which rely on short execution before shutdown. In real world, it's
|
// which rely on short execution before shutdown. In real world, it's
|
||||||
|
|||||||
@@ -713,7 +713,17 @@ pub fn normalize(self: *Node, page: *Page) !void {
|
|||||||
return self._normalize(page.call_arena, &buffer, page);
|
return self._normalize(page.call_arena, &buffer, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cloneNode(self: *Node, deep_: ?bool, page: *Page) error{ OutOfMemory, StringTooLarge, NotSupported, NotImplemented, InvalidCharacterError, CloneError, IFrameLoadError }!*Node {
|
const CloneError = error{
|
||||||
|
OutOfMemory,
|
||||||
|
StringTooLarge,
|
||||||
|
NotSupported,
|
||||||
|
NotImplemented,
|
||||||
|
InvalidCharacterError,
|
||||||
|
CloneError,
|
||||||
|
IFrameLoadError,
|
||||||
|
TooManyContexts,
|
||||||
|
};
|
||||||
|
pub fn cloneNode(self: *Node, deep_: ?bool, page: *Page) CloneError!*Node {
|
||||||
const deep = deep_ orelse false;
|
const deep = deep_ orelse false;
|
||||||
switch (self._type) {
|
switch (self._type) {
|
||||||
.cdata => |cd| {
|
.cdata => |cd| {
|
||||||
|
|||||||
Reference in New Issue
Block a user