mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +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
|
||||
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
|
||||
isolate_params: *v8.CreateParams,
|
||||
@@ -85,6 +86,8 @@ inspector: ?*Inspector,
|
||||
// which an be created once per isolaet.
|
||||
private_symbols: PrivateSymbols,
|
||||
|
||||
microtask_queues_are_running: bool,
|
||||
|
||||
pub const InitOpts = struct {
|
||||
with_inspector: bool = false,
|
||||
};
|
||||
@@ -203,7 +206,8 @@ pub fn init(app: *App, opts: InitOpts) !Env {
|
||||
return .{
|
||||
.app = app,
|
||||
.context_id = 0,
|
||||
.contexts = .empty,
|
||||
.contexts = undefined,
|
||||
.context_count = 0,
|
||||
.isolate = isolate,
|
||||
.platform = &app.platform,
|
||||
.templates = templates,
|
||||
@@ -211,15 +215,16 @@ pub fn init(app: *App, opts: InitOpts) !Env {
|
||||
.inspector = inspector,
|
||||
.global_template = global_eternal,
|
||||
.private_symbols = private_symbols,
|
||||
.microtask_queues_are_running = false,
|
||||
.eternal_function_templates = eternal_function_templates,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Env) void {
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -228,8 +233,6 @@ pub fn deinit(self: *Env) void {
|
||||
i.deinit(allocator);
|
||||
}
|
||||
|
||||
self.contexts.deinit(allocator);
|
||||
|
||||
allocator.free(self.templates);
|
||||
allocator.free(self.eternal_function_templates);
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
_ = self.contexts.swapRemove(i);
|
||||
// Swap with last element and decrement count
|
||||
self.context_count -= 1;
|
||||
self.contexts[i] = self.contexts[self.context_count];
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -339,16 +350,24 @@ pub fn destroyContext(self: *Env, context: *Context) void {
|
||||
context.deinit();
|
||||
}
|
||||
|
||||
pub fn runMicrotasks(self: *const Env) void {
|
||||
const v8_isolate = self.isolate.handle;
|
||||
for (self.contexts.items) |ctx| {
|
||||
v8.v8__MicrotaskQueue__PerformCheckpoint(ctx.microtask_queue, v8_isolate);
|
||||
pub fn runMicrotasks(self: *Env) void {
|
||||
if (self.microtask_queues_are_running == false) {
|
||||
const v8_isolate = self.isolate.handle;
|
||||
|
||||
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 {
|
||||
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) {
|
||||
// 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
|
||||
|
||||
@@ -713,7 +713,17 @@ pub fn normalize(self: *Node, page: *Page) !void {
|
||||
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;
|
||||
switch (self._type) {
|
||||
.cdata => |cd| {
|
||||
|
||||
Reference in New Issue
Block a user