mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 06:23:45 +00:00
Support multiple blocking scripts waiting for the same response
I thought we eliminated this from being possible, but I guess not. It seems to be related to multiple async scripts direct or indirectly (or a specific combination?) waiting for the same synchronous script. The first async script to block, grabs it and deletes it, making it invalid for the next. This puts back a counter on blocking scripts which I took out when I thought this could no longer happen.
This commit is contained in:
@@ -75,7 +75,7 @@ script_pool: std.heap.MemoryPool(Script),
|
|||||||
// The type is confusing (too confusing? move to a union). Starts of as `null`
|
// The type is confusing (too confusing? move to a union). Starts of as `null`
|
||||||
// then transitions to either an error (from errorCalback) or the completed
|
// then transitions to either an error (from errorCalback) or the completed
|
||||||
// buffer from doneCallback
|
// buffer from doneCallback
|
||||||
imported_modules: std.StringHashMapUnmanaged(?error{Failed}!std.ArrayList(u8)),
|
imported_modules: std.StringHashMapUnmanaged(ImportedModule),
|
||||||
|
|
||||||
// Mapping between module specifier and resolution.
|
// Mapping between module specifier and resolution.
|
||||||
// see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script/type/importmap
|
// see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script/type/importmap
|
||||||
@@ -114,17 +114,11 @@ pub fn deinit(self: *ScriptManager) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(self: *ScriptManager) void {
|
pub fn reset(self: *ScriptManager) void {
|
||||||
{
|
var it = self.imported_modules.valueIterator();
|
||||||
var it = self.imported_modules.valueIterator();
|
while (it.next()) |value_ptr| {
|
||||||
while (it.next()) |value_ptr| {
|
self.buffer_pool.release(value_ptr.buffer);
|
||||||
// might have not been loaded yet (null)
|
|
||||||
const result = value_ptr.* orelse continue;
|
|
||||||
// might have loaded an error, in which case there's nothing to free
|
|
||||||
var buf = result catch continue;
|
|
||||||
buf.deinit(self.allocator);
|
|
||||||
}
|
|
||||||
self.imported_modules.clearRetainingCapacity();
|
|
||||||
}
|
}
|
||||||
|
self.imported_modules.clearRetainingCapacity();
|
||||||
|
|
||||||
// Our allocator is the page arena, it's been reset. We cannot use
|
// Our allocator is the page arena, it's been reset. We cannot use
|
||||||
// clearAndRetainCapacity, since that space is no longer ours
|
// clearAndRetainCapacity, since that space is no longer ours
|
||||||
@@ -328,6 +322,7 @@ pub fn resolveSpecifier(self: *ScriptManager, arena: Allocator, base: [:0]const
|
|||||||
pub fn preloadImport(self: *ScriptManager, url: [:0]const u8, referrer: []const u8) !void {
|
pub fn preloadImport(self: *ScriptManager, url: [:0]const u8, referrer: []const u8) !void {
|
||||||
const gop = try self.imported_modules.getOrPut(self.allocator, url);
|
const gop = try self.imported_modules.getOrPut(self.allocator, url);
|
||||||
if (gop.found_existing) {
|
if (gop.found_existing) {
|
||||||
|
gop.value_ptr.waiters += 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
errdefer _ = self.imported_modules.remove(url);
|
errdefer _ = self.imported_modules.remove(url);
|
||||||
@@ -346,7 +341,9 @@ pub fn preloadImport(self: *ScriptManager, url: [:0]const u8, referrer: []const
|
|||||||
.mode = .import,
|
.mode = .import,
|
||||||
};
|
};
|
||||||
|
|
||||||
gop.value_ptr.* = null;
|
gop.value_ptr.* = ImportedModule{
|
||||||
|
.manager = self,
|
||||||
|
};
|
||||||
|
|
||||||
var headers = try self.client.newHeaders();
|
var headers = try self.client.newHeaders();
|
||||||
try self.page.requestCookie(.{}).headersForRequest(self.page.arena, url, &headers);
|
try self.page.requestCookie(.{}).headersForRequest(self.page.arena, url, &headers);
|
||||||
@@ -393,20 +390,32 @@ pub fn waitForImport(self: *ScriptManager, url: [:0]const u8) !ModuleSource {
|
|||||||
defer self.is_evaluating = was_evaluating;
|
defer self.is_evaluating = was_evaluating;
|
||||||
|
|
||||||
var client = self.client;
|
var client = self.client;
|
||||||
while (entry.value_ptr.* == null) {
|
while (true) {
|
||||||
// rely on http's timeout settings to avoid an endless/long loop.
|
switch (entry.value_ptr.state) {
|
||||||
_ = try client.tick(200);
|
.loading => {
|
||||||
|
_ = try client.tick(200);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
.done => {
|
||||||
|
var shared = false;
|
||||||
|
const buffer = entry.value_ptr.buffer;
|
||||||
|
const waiters = entry.value_ptr.waiters;
|
||||||
|
|
||||||
|
if (waiters == 0) {
|
||||||
|
self.imported_modules.removeByPtr(entry.key_ptr);
|
||||||
|
} else {
|
||||||
|
shared = true;
|
||||||
|
entry.value_ptr.waiters = waiters - 1;
|
||||||
|
}
|
||||||
|
return .{
|
||||||
|
.buffer = buffer,
|
||||||
|
.shared = shared,
|
||||||
|
.buffer_pool = &self.buffer_pool,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
.err => return error.Failed,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defer self.imported_modules.removeByPtr(entry.key_ptr);
|
|
||||||
|
|
||||||
// it's possible we stored an error in the map, if so, we'll return it now
|
|
||||||
const buf = try (entry.value_ptr.*.?);
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.buffer = buf,
|
|
||||||
.buffer_pool = &self.buffer_pool,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getAsyncImport(self: *ScriptManager, url: [:0]const u8, cb: ImportAsync.Callback, cb_data: *anyopaque, referrer: []const u8) !void {
|
pub fn getAsyncImport(self: *ScriptManager, url: [:0]const u8, cb: ImportAsync.Callback, cb_data: *anyopaque, referrer: []const u8) !void {
|
||||||
@@ -496,6 +505,7 @@ fn evaluate(self: *ScriptManager) void {
|
|||||||
ia.callback(ia.data, error.FailedToLoad);
|
ia.callback(ia.data, error.FailedToLoad);
|
||||||
} else {
|
} else {
|
||||||
ia.callback(ia.data, .{
|
ia.callback(ia.data, .{
|
||||||
|
.shared = false,
|
||||||
.buffer = script.source.remote,
|
.buffer = script.source.remote,
|
||||||
.buffer_pool = &self.buffer_pool,
|
.buffer_pool = &self.buffer_pool,
|
||||||
});
|
});
|
||||||
@@ -691,7 +701,8 @@ pub const Script = struct {
|
|||||||
} else if (self.mode == .import) {
|
} else if (self.mode == .import) {
|
||||||
manager.async_scripts.remove(&self.node);
|
manager.async_scripts.remove(&self.node);
|
||||||
const entry = manager.imported_modules.getPtr(self.url).?;
|
const entry = manager.imported_modules.getPtr(self.url).?;
|
||||||
entry.* = self.source.remote;
|
entry.state = .done;
|
||||||
|
entry.buffer = self.source.remote;
|
||||||
self.deinit(false);
|
self.deinit(false);
|
||||||
}
|
}
|
||||||
manager.evaluate();
|
manager.evaluate();
|
||||||
@@ -724,7 +735,7 @@ pub const Script = struct {
|
|||||||
|
|
||||||
if (self.mode == .import) {
|
if (self.mode == .import) {
|
||||||
const entry = self.manager.imported_modules.getPtr(self.url).?;
|
const entry = self.manager.imported_modules.getPtr(self.url).?;
|
||||||
entry.* = error.Failed;
|
entry.state = .err;
|
||||||
}
|
}
|
||||||
self.deinit(true);
|
self.deinit(true);
|
||||||
manager.evaluate();
|
manager.evaluate();
|
||||||
@@ -938,11 +949,14 @@ const ImportAsync = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const ModuleSource = struct {
|
pub const ModuleSource = struct {
|
||||||
|
shared: bool,
|
||||||
buffer_pool: *BufferPool,
|
buffer_pool: *BufferPool,
|
||||||
buffer: std.ArrayListUnmanaged(u8),
|
buffer: std.ArrayList(u8),
|
||||||
|
|
||||||
pub fn deinit(self: *ModuleSource) void {
|
pub fn deinit(self: *ModuleSource) void {
|
||||||
self.buffer_pool.release(self.buffer);
|
if (self.shared == false) {
|
||||||
|
self.buffer_pool.release(self.buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn src(self: *const ModuleSource) []const u8 {
|
pub fn src(self: *const ModuleSource) []const u8 {
|
||||||
@@ -950,6 +964,19 @@ pub const ModuleSource = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ImportedModule = struct {
|
||||||
|
manager: *ScriptManager,
|
||||||
|
state: State = .loading,
|
||||||
|
buffer: std.ArrayList(u8) = .{},
|
||||||
|
waiters: u16 = 1,
|
||||||
|
|
||||||
|
const State = enum {
|
||||||
|
err,
|
||||||
|
done,
|
||||||
|
loading,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// Parses data:[<media-type>][;base64],<data>
|
// Parses data:[<media-type>][;base64],<data>
|
||||||
fn parseDataURI(allocator: Allocator, src: []const u8) !?[]const u8 {
|
fn parseDataURI(allocator: Allocator, src: []const u8) !?[]const u8 {
|
||||||
if (!std.mem.startsWith(u8, src, "data:")) {
|
if (!std.mem.startsWith(u8, src, "data:")) {
|
||||||
|
|||||||
Reference in New Issue
Block a user