mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-28 14:43:28 +00:00
add popstate event for History
This commit is contained in:
@@ -36,6 +36,7 @@ const MouseEvent = @import("mouse_event.zig").MouseEvent;
|
||||
const KeyboardEvent = @import("keyboard_event.zig").KeyboardEvent;
|
||||
const ErrorEvent = @import("../html/error_event.zig").ErrorEvent;
|
||||
const MessageEvent = @import("../dom/MessageChannel.zig").MessageEvent;
|
||||
const PopStateEvent = @import("../html/History.zig").PopStateEvent;
|
||||
|
||||
// Event interfaces
|
||||
pub const Interfaces = .{
|
||||
@@ -46,6 +47,7 @@ pub const Interfaces = .{
|
||||
KeyboardEvent,
|
||||
ErrorEvent,
|
||||
MessageEvent,
|
||||
PopStateEvent,
|
||||
};
|
||||
|
||||
pub const Union = generate.Union(Interfaces);
|
||||
@@ -73,6 +75,7 @@ pub const Event = struct {
|
||||
.error_event => .{ .ErrorEvent = @as(*ErrorEvent, @ptrCast(evt)).* },
|
||||
.message_event => .{ .MessageEvent = @as(*MessageEvent, @ptrCast(evt)).* },
|
||||
.keyboard_event => .{ .KeyboardEvent = @as(*parser.KeyboardEvent, @ptrCast(evt)) },
|
||||
.pop_state => .{ .PopStateEvent = @as(*PopStateEvent, @ptrCast(evt)).* },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ pub const FetchContext = struct {
|
||||
var headers: Headers = .{};
|
||||
|
||||
// seems to be the highest priority
|
||||
const same_origin = try isSameOriginAsPage(self.url, self.page);
|
||||
const same_origin = try self.page.isSameOrigin(self.url);
|
||||
|
||||
// If the mode is "no-cors", we need to return this opaque/stripped Response.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Response/type
|
||||
@@ -237,11 +237,6 @@ pub fn fetch(input: RequestInput, options: ?RequestInit, page: *Page) !Env.Promi
|
||||
return resolver.promise();
|
||||
}
|
||||
|
||||
fn isSameOriginAsPage(url: []const u8, page: *const Page) !bool {
|
||||
const origin = try page.origin(page.call_arena);
|
||||
return std.mem.startsWith(u8, url, origin);
|
||||
}
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "fetch: fetch" {
|
||||
try testing.htmlRunner("fetch/fetch.html");
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const log = @import("../../log.zig");
|
||||
|
||||
const Env = @import("../env.zig").Env;
|
||||
const Page = @import("../page.zig").Page;
|
||||
@@ -87,6 +88,32 @@ pub fn pushNavigation(self: *History, _url: []const u8, page: *Page) !void {
|
||||
self.current = self.stack.items.len - 1;
|
||||
}
|
||||
|
||||
pub fn dispatchPopStateEvent(state: ?[]const u8, page: *Page) void {
|
||||
log.debug(.script_event, "dispatch popstate event", .{
|
||||
.type = "popstate",
|
||||
.source = "history",
|
||||
});
|
||||
History._dispatchPopStateEvent(state, page) catch |err| {
|
||||
log.err(.app, "dispatch popstate event error", .{
|
||||
.err = err,
|
||||
.type = "popstate",
|
||||
.source = "history",
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
fn _dispatchPopStateEvent(
|
||||
state: ?[]const u8,
|
||||
page: *Page,
|
||||
) !void {
|
||||
var evt = try PopStateEvent.constructor("popstate", .{ .state = state });
|
||||
|
||||
_ = try parser.eventTargetDispatchEvent(
|
||||
@as(*parser.EventTarget, @ptrCast(&page.window)),
|
||||
@as(*parser.Event, @ptrCast(&evt)),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn _pushState(self: *History, state: Env.JsObject, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
|
||||
const arena = page.session.arena;
|
||||
|
||||
@@ -123,6 +150,15 @@ pub fn go(self: *History, delta: i32, page: *Page) !void {
|
||||
const index = @as(usize, @intCast(index_s));
|
||||
const entry = self.stack.items[index];
|
||||
self.current = index;
|
||||
|
||||
if (try page.isSameOrigin(entry.url)) {
|
||||
if (entry.state) |state| {
|
||||
History.dispatchPopStateEvent(state, page);
|
||||
} else {
|
||||
History.dispatchPopStateEvent(null, page);
|
||||
}
|
||||
}
|
||||
|
||||
try page.navigateFromWebAPI(entry.url, .{ .reason = .history });
|
||||
}
|
||||
|
||||
@@ -138,6 +174,46 @@ pub fn _forward(self: *History, page: *Page) !void {
|
||||
try self.go(1, page);
|
||||
}
|
||||
|
||||
const parser = @import("../netsurf.zig");
|
||||
const Event = @import("../events/event.zig").Event;
|
||||
|
||||
pub const PopStateEvent = struct {
|
||||
pub const prototype = *Event;
|
||||
pub const union_make_copy = true;
|
||||
|
||||
pub const EventInit = struct {
|
||||
state: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
proto: parser.Event,
|
||||
state: ?[]const u8,
|
||||
|
||||
pub fn constructor(event_type: []const u8, opts: ?EventInit) !PopStateEvent {
|
||||
const event = try parser.eventCreate();
|
||||
defer parser.eventDestroy(event);
|
||||
try parser.eventInit(event, event_type, .{});
|
||||
parser.eventSetInternalType(event, .pop_state);
|
||||
|
||||
const o = opts orelse EventInit{};
|
||||
|
||||
return .{
|
||||
.proto = event.*,
|
||||
.state = o.state,
|
||||
};
|
||||
}
|
||||
|
||||
// `hasUAVisualTransition` is not implemented. It isn't baseline so this is okay.
|
||||
|
||||
pub fn get_state(self: *const PopStateEvent, page: *Page) !?Env.Value {
|
||||
if (self.state) |state| {
|
||||
const value = try Env.Value.fromJson(page.main_context, state);
|
||||
return value;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "Browser: HTML.History" {
|
||||
try testing.htmlRunner("html/history.html");
|
||||
|
||||
@@ -558,6 +558,7 @@ pub const EventType = enum(u8) {
|
||||
xhr_event = 6,
|
||||
message_event = 7,
|
||||
keyboard_event = 8,
|
||||
pop_state = 9,
|
||||
};
|
||||
|
||||
pub const MutationEvent = c.dom_mutation_event;
|
||||
|
||||
@@ -1132,6 +1132,11 @@ pub const Page = struct {
|
||||
}
|
||||
self.slot_change_monitor = try SlotChangeMonitor.init(self);
|
||||
}
|
||||
|
||||
pub fn isSameOrigin(self: *const Page, url: []const u8) !bool {
|
||||
const current_origin = try self.origin(self.call_arena);
|
||||
return std.mem.startsWith(u8, url, current_origin);
|
||||
}
|
||||
};
|
||||
|
||||
pub const NavigateReason = enum {
|
||||
|
||||
@@ -19,8 +19,18 @@
|
||||
let state = { "new": "field", testComplete: true };
|
||||
testing.expectEqual(state, history.state);
|
||||
|
||||
testing.expectEqual(undefined, history.back());
|
||||
testing.expectEqual(undefined, history.forward());
|
||||
let popstateEventFired = false;
|
||||
let popstateEventState = null;
|
||||
|
||||
window.addEventListener('popstate', (event) => {
|
||||
popstateEventFired = true;
|
||||
popstateEventState = event.state;
|
||||
});
|
||||
|
||||
testing.eventually(() => {
|
||||
testing.expectEqual(true, popstateEventFired);
|
||||
testing.expectEqual(state, popstateEventState);
|
||||
})
|
||||
|
||||
testing.onPageWait(() => {
|
||||
testing.expectEqual(true, history.state && history.state.testComplete);
|
||||
|
||||
Reference in New Issue
Block a user