mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Allow iframe.src to renavigate the page
Unlike a script, an iframe can be re-navigated simply by setting the src.
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const URL = @import("browser/URL.zig");
|
||||
|
||||
const TestHTTPServer = @This();
|
||||
|
||||
@@ -97,7 +98,10 @@ fn handleConnection(self: *TestHTTPServer, conn: std.net.Server.Connection) !voi
|
||||
}
|
||||
|
||||
pub fn sendFile(req: *std.http.Server.Request, file_path: []const u8) !void {
|
||||
var file = std.fs.cwd().openFile(file_path, .{}) catch |err| switch (err) {
|
||||
var url_buf: [1024]u8 = undefined;
|
||||
var fba = std.heap.FixedBufferAllocator.init(&url_buf);
|
||||
const unescaped_file_path = try URL.unescape(fba.allocator(), file_path);
|
||||
var file = std.fs.cwd().openFile(unescaped_file_path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => return req.respond("server error", .{ .status = .not_found }),
|
||||
else => return err,
|
||||
};
|
||||
|
||||
@@ -346,7 +346,10 @@ pub fn deinit(self: *Page) void {
|
||||
session.browser.env.destroyContext(self.js);
|
||||
|
||||
self._script_manager.shutdown = true;
|
||||
session.browser.http_client.abort();
|
||||
if (self.parent == null) {
|
||||
// only the root frame needs to abort this. It's more efficient this way
|
||||
session.browser.http_client.abort();
|
||||
}
|
||||
self._script_manager.deinit();
|
||||
|
||||
if (comptime IS_DEBUG) {
|
||||
@@ -796,7 +799,12 @@ fn pageDataCallback(transfer: *Http.Transfer, data: []const u8) !void {
|
||||
} orelse .unknown;
|
||||
|
||||
if (comptime IS_DEBUG) {
|
||||
log.debug(.page, "navigate first chunk", .{ .content_type = mime.content_type, .len = data.len, .type = self._type, .url = self.url });
|
||||
log.debug(.page, "navigate first chunk", .{
|
||||
.content_type = mime.content_type,
|
||||
.len = data.len,
|
||||
.type = self._type,
|
||||
.url = self.url,
|
||||
});
|
||||
}
|
||||
|
||||
switch (mime.content_type) {
|
||||
@@ -850,7 +858,11 @@ fn pageDoneCallback(ctx: *anyopaque) !void {
|
||||
try self._session.navigation.commitNavigation(self);
|
||||
|
||||
defer if (comptime IS_DEBUG) {
|
||||
log.debug(.page, "page.load.complete", .{ .url = self.url, .type = self._type });
|
||||
log.debug(.page, "page load complete", .{
|
||||
.url = self.url,
|
||||
.type = self._type,
|
||||
.state = std.meta.activeTag(self._parse_state),
|
||||
});
|
||||
};
|
||||
|
||||
const parse_arena = try self.getArena(.{ .debug = "Page.parse" });
|
||||
@@ -962,21 +974,28 @@ pub fn iframeAddedCallback(self: *Page, iframe: *Element.Html.IFrame) !void {
|
||||
}
|
||||
|
||||
iframe._executed = true;
|
||||
|
||||
const session = self._session;
|
||||
const frame_id = session.nextFrameId();
|
||||
|
||||
// A frame can be re-navigated by setting the src.
|
||||
const existing_window = iframe._content_window;
|
||||
|
||||
const page_frame = try self.arena.create(Page);
|
||||
const frame_id = blk: {
|
||||
if (existing_window) |w| {
|
||||
const existing_frame_id = w._page._frame_id;
|
||||
session.browser.http_client.abortFrame(existing_frame_id);
|
||||
break :blk existing_frame_id;
|
||||
}
|
||||
break :blk session.nextFrameId();
|
||||
};
|
||||
|
||||
try Page.init(page_frame, frame_id, session, self);
|
||||
errdefer page_frame.deinit();
|
||||
|
||||
self._pending_loads += 1;
|
||||
page_frame.iframe = iframe;
|
||||
iframe._content_window = page_frame.window;
|
||||
|
||||
self._session.notification.dispatch(.page_frame_created, &.{
|
||||
.frame_id = frame_id,
|
||||
.parent_id = self._frame_id,
|
||||
.timestamp = timestamp(.monotonic),
|
||||
});
|
||||
errdefer iframe._content_window = null;
|
||||
|
||||
// navigate will dupe the url
|
||||
const url = try URL.resolve(
|
||||
@@ -986,6 +1005,15 @@ pub fn iframeAddedCallback(self: *Page, iframe: *Element.Html.IFrame) !void {
|
||||
.{ .encode = true },
|
||||
);
|
||||
|
||||
if (existing_window == null) {
|
||||
// on first load, dispatch frame_created evnet
|
||||
self._session.notification.dispatch(.page_frame_created, &.{
|
||||
.page_id = page_id,
|
||||
.parent_id = self.id,
|
||||
.timestamp = timestamp(.monotonic),
|
||||
});
|
||||
}
|
||||
|
||||
page_frame.navigate(url, .{ .reason = .initialFrameNavigation }) catch |err| {
|
||||
log.warn(.page, "iframe navigate failure", .{ .url = url, .err = err });
|
||||
self._pending_loads -= 1;
|
||||
@@ -994,6 +1022,25 @@ pub fn iframeAddedCallback(self: *Page, iframe: *Element.Html.IFrame) !void {
|
||||
return error.IFrameLoadError;
|
||||
};
|
||||
|
||||
if (existing_window) |w| {
|
||||
const existing_page = w._page;
|
||||
if (existing_page._parent_notified == false) {
|
||||
self._pending_loads -= 1;
|
||||
}
|
||||
|
||||
for (self.frames.items, 0..) |p, i| {
|
||||
if (p == existing_page) {
|
||||
self.frames.items[i] = page_frame;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
lp.assert(false, "Existing frame not found", .{ .len = self.frames.items.len });
|
||||
}
|
||||
|
||||
existing_page.deinit();
|
||||
return;
|
||||
}
|
||||
|
||||
// window[N] is based on document order. For now we'll just append the frame
|
||||
// at the end of our list and set frames_sorted == false. window.getFrame
|
||||
// will check this flag to decide if it needs to sort the frames or not.
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
<iframe id=f2 src="support/sub2.html"></iframe>
|
||||
|
||||
<script id="basic">
|
||||
// reload it
|
||||
$('#f1').src = 'support/sub 1.html';
|
||||
|
||||
testing.eventually(() => {
|
||||
testing.expectEqual(undefined, window[10]);
|
||||
|
||||
@@ -47,8 +50,10 @@
|
||||
// child frame's top.parent is itself (root has no parent)
|
||||
testing.expectEqual(window, window[0].top.parent);
|
||||
|
||||
// Todo: Context security tokens
|
||||
// testing.expectEqual(true, window.sub1_loaded);
|
||||
// testing.expectEqual(true, window.sub2_loaded);
|
||||
// testing.expectEqual(2, window.sub1_count);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
<script>
|
||||
// should not have access to the parent's JS context
|
||||
window.top.sub1_loaded = window.testing == undefined;
|
||||
window.top.sub1_count = (window.top.sub1_count || 0) + 1;
|
||||
</script>
|
||||
|
||||
@@ -58,6 +58,9 @@ pub fn setSrc(self: *IFrame, src: []const u8, page: *Page) !void {
|
||||
try element.setAttributeSafe(comptime .wrap("src"), .wrap(src), page);
|
||||
self._src = element.getAttributeSafe(comptime .wrap("src")) orelse unreachable;
|
||||
if (element.asNode().isConnected()) {
|
||||
// unlike script, an iframe is reloaded every time the src is set
|
||||
// even if it's set to the same URL.
|
||||
self._executed = false;
|
||||
try page.iframeAddedCallback(self);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user