mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 23:23:28 +00:00
Shutdown clean async scripts
Set parent current script
This commit is contained in:
@@ -37,16 +37,16 @@ page: *Page,
|
|||||||
// Only once this is true can deferred scripts be run
|
// Only once this is true can deferred scripts be run
|
||||||
static_scripts_done: bool,
|
static_scripts_done: bool,
|
||||||
|
|
||||||
// when async_count == 0 and static_script_done == true, the document is completed
|
// List of async scripts. We don't care about the execution order of these, but
|
||||||
// loading (i.e. page.documentIsComplete should be called).
|
// on shutdown/abort, we need to co cleanup any pending ones.
|
||||||
async_count: usize,
|
asyncs: OrderList,
|
||||||
|
|
||||||
// Normal scripts (non-deffered & non-async). These must be executed ni order
|
// Normal scripts (non-deffered & non-async). These must be executed ni order
|
||||||
scripts: OrderList,
|
scripts: OrderList,
|
||||||
|
|
||||||
// List of deferred scripts. These must be executed in order, but only once
|
// List of deferred scripts. These must be executed in order, but only once
|
||||||
// dom_loaded == true,
|
// dom_loaded == true,
|
||||||
deferred: OrderList,
|
deferreds: OrderList,
|
||||||
|
|
||||||
client: *HttpClient,
|
client: *HttpClient,
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
@@ -60,9 +60,9 @@ pub fn init(browser: *Browser, page: *Page) ScriptManager {
|
|||||||
const allocator = browser.allocator;
|
const allocator = browser.allocator;
|
||||||
return .{
|
return .{
|
||||||
.page = page,
|
.page = page,
|
||||||
|
.asyncs = .{},
|
||||||
.scripts = .{},
|
.scripts = .{},
|
||||||
.deferred = .{},
|
.deferreds = .{},
|
||||||
.async_count = 0,
|
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.client = browser.http_client,
|
.client = browser.http_client,
|
||||||
.static_scripts_done = false,
|
.static_scripts_done = false,
|
||||||
@@ -77,8 +77,9 @@ pub fn deinit(self: *ScriptManager) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(self: *ScriptManager) void {
|
pub fn reset(self: *ScriptManager) void {
|
||||||
|
self.clearList(&self.asyncs);
|
||||||
self.clearList(&self.scripts);
|
self.clearList(&self.scripts);
|
||||||
self.clearList(&self.deferred);
|
self.clearList(&self.deferreds);
|
||||||
self.static_scripts_done = false;
|
self.static_scripts_done = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,15 +201,9 @@ pub fn addFromElement(self: *ScriptManager, element: *parser.Element) !void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.getList(&pending_script.script)) |list| {
|
const list = self.getList(&pending_script.script);
|
||||||
pending_script.node = .{.data = pending_script};
|
pending_script.node = .{ .data = pending_script };
|
||||||
list.append(&pending_script.node);
|
list.append(&pending_script.node);
|
||||||
} else {
|
|
||||||
// async scripts don't get added to a list, because we can execute
|
|
||||||
// them in any order
|
|
||||||
std.debug.assert(script.is_async);
|
|
||||||
self.async_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
errdefer pending_script.deinit();
|
errdefer pending_script.deinit();
|
||||||
|
|
||||||
@@ -282,7 +277,7 @@ fn evaluate(self: *ScriptManager) void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (self.deferred.first) |n| {
|
while (self.deferreds.first) |n| {
|
||||||
var pending_script = n.data;
|
var pending_script = n.data;
|
||||||
if (pending_script.complete == false) {
|
if (pending_script.complete == false) {
|
||||||
return;
|
return;
|
||||||
@@ -295,7 +290,7 @@ fn evaluate(self: *ScriptManager) void {
|
|||||||
// state changes (this ultimately triggers the DOMContentLoaded event)
|
// state changes (this ultimately triggers the DOMContentLoaded event)
|
||||||
page.documentIsLoaded();
|
page.documentIsLoaded();
|
||||||
|
|
||||||
if (self.async_count == 0) {
|
if (self.asyncs.first == null) {
|
||||||
// if we're here, then its like `asyncDone`
|
// if we're here, then its like `asyncDone`
|
||||||
// 1 - there are no async scripts pending
|
// 1 - there are no async scripts pending
|
||||||
// 2 - we checkecked static_scripts_done == true above
|
// 2 - we checkecked static_scripts_done == true above
|
||||||
@@ -306,28 +301,26 @@ fn evaluate(self: *ScriptManager) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn isDone(self: *const ScriptManager) bool {
|
pub fn isDone(self: *const ScriptManager) bool {
|
||||||
return self.async_count == 0 and // there are no more async scripts
|
return self.asyncs.first == null and // there are no more async scripts
|
||||||
self.static_scripts_done and // and we've finished parsing the HTML to queue all <scripts>
|
self.static_scripts_done and // and we've finished parsing the HTML to queue all <scripts>
|
||||||
self.scripts.first == null and // and there are no more <script src=> to wait for
|
self.scripts.first == null and // and there are no more <script src=> to wait for
|
||||||
self.deferred.first == null; // and there are no more <script defer src=> to wait for
|
self.deferreds.first == null; // and there are no more <script defer src=> to wait for
|
||||||
}
|
}
|
||||||
|
|
||||||
fn asyncDone(self: *ScriptManager) void {
|
fn asyncDone(self: *ScriptManager) void {
|
||||||
self.async_count -= 1;
|
|
||||||
if (self.isDone()) {
|
if (self.isDone()) {
|
||||||
// then the document is considered complete
|
// then the document is considered complete
|
||||||
self.page.documentIsComplete();
|
self.page.documentIsComplete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getList(self: *ScriptManager, script: *const Script) ?*OrderList {
|
fn getList(self: *ScriptManager, script: *const Script) *OrderList {
|
||||||
if (script.is_defer) {
|
if (script.is_defer) {
|
||||||
return &self.deferred;
|
return &self.deferreds;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (script.is_async) {
|
if (script.is_async) {
|
||||||
// async don't need to execute in order.
|
return &self.asyncs;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &self.scripts;
|
return &self.scripts;
|
||||||
@@ -389,12 +382,7 @@ const PendingScript = struct {
|
|||||||
manager.buffer_pool.release(script.source.remote);
|
manager.buffer_pool.release(script.source.remote);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (manager.getList(script)) |list| {
|
manager.getList(script).remove(&self.node);
|
||||||
list.remove(&self.node);
|
|
||||||
} else {
|
|
||||||
std.debug.assert(script.is_async);
|
|
||||||
manager.asyncDone();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn startCallback(self: *PendingScript, transfer: *HttpClient.Transfer) !void {
|
fn startCallback(self: *PendingScript, transfer: *HttpClient.Transfer) !void {
|
||||||
@@ -429,7 +417,6 @@ const PendingScript = struct {
|
|||||||
|
|
||||||
// @newhttp TODO: max-length enforcement ??
|
// @newhttp TODO: max-length enforcement ??
|
||||||
try self.script.source.remote.appendSlice(self.manager.allocator, data);
|
try self.script.source.remote.appendSlice(self.manager.allocator, data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doneCallback(self: *PendingScript) void {
|
fn doneCallback(self: *PendingScript) void {
|
||||||
@@ -438,8 +425,9 @@ const PendingScript = struct {
|
|||||||
const manager = self.manager;
|
const manager = self.manager;
|
||||||
if (self.script.is_async) {
|
if (self.script.is_async) {
|
||||||
// async script can be evaluated immediately
|
// async script can be evaluated immediately
|
||||||
defer self.deinit();
|
|
||||||
self.script.eval(self.manager.page);
|
self.script.eval(self.manager.page);
|
||||||
|
self.deinit();
|
||||||
|
manager.asyncDone();
|
||||||
} else {
|
} else {
|
||||||
self.complete = true;
|
self.complete = true;
|
||||||
manager.evaluate();
|
manager.evaluate();
|
||||||
@@ -448,10 +436,10 @@ const PendingScript = struct {
|
|||||||
|
|
||||||
fn errorCallback(self: *PendingScript, err: anyerror) void {
|
fn errorCallback(self: *PendingScript, err: anyerror) void {
|
||||||
log.warn(.http, "script fetch error", .{ .req = self.script.url, .err = err });
|
log.warn(.http, "script fetch error", .{ .req = self.script.url, .err = err });
|
||||||
defer self.deinit();
|
const manager = self.manager;
|
||||||
|
self.deinit();
|
||||||
|
|
||||||
// this script might have been blocking others;
|
manager.evaluate();
|
||||||
self.manager.evaluate();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -488,6 +476,14 @@ const Script = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn eval(self: *Script, page: *Page) void {
|
fn eval(self: *Script, page: *Page) void {
|
||||||
|
page.setCurrentScript(@ptrCast(self.element)) catch |err| {
|
||||||
|
log.err(.browser, "set document script", .{ .err = err });
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
defer page.setCurrentScript(null) catch |err| {
|
||||||
|
log.err(.browser, "clear document script", .{ .err = err });
|
||||||
|
};
|
||||||
|
|
||||||
// inline scripts aren't cached. remote ones are.
|
// inline scripts aren't cached. remote ones are.
|
||||||
const cacheable = self.source == .remote;
|
const cacheable = self.source == .remote;
|
||||||
@@ -643,7 +639,7 @@ const BufferPool = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
b.clearRetainingCapacity();
|
b.clearRetainingCapacity();
|
||||||
node.data = b;
|
node.* = .{ .data = b };
|
||||||
self.count += 1;
|
self.count += 1;
|
||||||
self.available.append(node);
|
self.available.append(node);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -398,6 +398,11 @@ pub const Page = struct {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn setCurrentScript(self: *Page, script: ?*parser.Script) !void {
|
||||||
|
const html_doc = self.window.document;
|
||||||
|
try parser.documentHTMLSetCurrentScript(html_doc, script);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn documentIsLoaded(self: *Page) void {
|
pub fn documentIsLoaded(self: *Page) void {
|
||||||
if (self.load_state != .parsing) {
|
if (self.load_state != .parsing) {
|
||||||
// Ideally, documentIsLoaded would only be called once, but if a
|
// Ideally, documentIsLoaded would only be called once, but if a
|
||||||
@@ -909,6 +914,7 @@ pub export fn scriptAddedCallback(ctx: ?*anyopaque, element: ?*parser.Element) c
|
|||||||
// if we're planning on navigating to another page, don't run this script
|
// if we're planning on navigating to another page, don't run this script
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.script_manager.addFromElement(element.?) catch |err| {
|
self.script_manager.addFromElement(element.?) catch |err| {
|
||||||
log.warn(.browser, "dynamcic script", .{ .err = err });
|
log.warn(.browser, "dynamcic script", .{ .err = err });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ pub const Connection = struct {
|
|||||||
const easy = c.curl_easy_init() orelse return error.FailedToInitializeEasy;
|
const easy = c.curl_easy_init() orelse return error.FailedToInitializeEasy;
|
||||||
errdefer _ = c.curl_easy_cleanup(easy);
|
errdefer _ = c.curl_easy_cleanup(easy);
|
||||||
|
|
||||||
// timeouts
|
// timeouts
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_TIMEOUT_MS, @as(c_long, @intCast(opts.timeout_ms))));
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_TIMEOUT_MS, @as(c_long, @intCast(opts.timeout_ms))));
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CONNECTTIMEOUT_MS, @as(c_long, @intCast(opts.connect_timeout_ms))));
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CONNECTTIMEOUT_MS, @as(c_long, @intCast(opts.connect_timeout_ms))));
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ pub const Connection = struct {
|
|||||||
// Note, this can be difference for the proxy and for the main
|
// Note, this can be difference for the proxy and for the main
|
||||||
// request. Might be something worth exposting as command
|
// request. Might be something worth exposting as command
|
||||||
// line arguments at some point.
|
// line arguments at some point.
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PROXY_CAINFO_BLOB , ca_blob));
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PROXY_CAINFO_BLOB, ca_blob));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std.debug.assert(opts.tls_verify_host == false);
|
std.debug.assert(opts.tls_verify_host == false);
|
||||||
@@ -136,7 +136,7 @@ pub const Connection = struct {
|
|||||||
// request. Might be something worth exposting as command
|
// request. Might be something worth exposting as command
|
||||||
// line arguments at some point.
|
// line arguments at some point.
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PROXY_SSL_VERIFYHOST, @as(c_long, 0)));
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PROXY_SSL_VERIFYHOST, @as(c_long, 0)));
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PROXY_SSL_VERIFYPEER , @as(c_long, 0)));
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PROXY_SSL_VERIFYPEER, @as(c_long, 0)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +204,6 @@ pub const Connection = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
pub fn errorCheck(code: c.CURLcode) errors.Error!void {
|
pub fn errorCheck(code: c.CURLcode) errors.Error!void {
|
||||||
if (code == c.CURLE_OK) {
|
if (code == c.CURLE_OK) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -231,6 +231,8 @@ const Command = struct {
|
|||||||
\\ disabling host verification.
|
\\ disabling host verification.
|
||||||
\\
|
\\
|
||||||
\\--http_proxy The HTTP proxy to use for all HTTP requests.
|
\\--http_proxy The HTTP proxy to use for all HTTP requests.
|
||||||
|
\\ A username:password can be included to use basic
|
||||||
|
\\ authentication.
|
||||||
\\ Defaults to none.
|
\\ Defaults to none.
|
||||||
\\
|
\\
|
||||||
\\--proxy_bearer_token
|
\\--proxy_bearer_token
|
||||||
|
|||||||
Reference in New Issue
Block a user