Skip to content

Commit b55b9bb

Browse files
committed
functional NavigationCurrentEntryChangeEvent
1 parent 82a4525 commit b55b9bb

File tree

15 files changed

+351
-209
lines changed

15 files changed

+351
-209
lines changed

src/browser/dom/event_target.zig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub const Union = union(enum) {
3434
screen_orientation: *@import("../html/screen.zig").ScreenOrientation,
3535
performance: *@import("performance.zig").Performance,
3636
media_query_list: *@import("../html/media_query_list.zig").MediaQueryList,
37+
navigation: *@import("../navigation/Navigation.zig"),
3738
};
3839

3940
// EventTarget implementation
@@ -82,6 +83,11 @@ pub const EventTarget = struct {
8283
.media_query_list => {
8384
return .{ .media_query_list = @fieldParentPtr("base", @as(*parser.EventTargetTBase, @ptrCast(et))) };
8485
},
86+
.navigation => {
87+
const NavigationEventTarget = @import("../navigation/NavigationEventTarget.zig");
88+
const base: *NavigationEventTarget = @fieldParentPtr("base", @as(*parser.EventTargetTBase, @ptrCast(et)));
89+
return .{ .navigation = @fieldParentPtr("proto", base) };
90+
},
8591
}
8692
}
8793

src/browser/events/event.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const ErrorEvent = @import("../html/error_event.zig").ErrorEvent;
3939
const MessageEvent = @import("../dom/MessageChannel.zig").MessageEvent;
4040
const PopStateEvent = @import("../html/History.zig").PopStateEvent;
4141
const CompositionEvent = @import("composition_event.zig").CompositionEvent;
42-
const NavigationCurrentEntryChangeEvent = @import("../html/Navigation.zig").NavigationCurrentEntryChangeEvent;
42+
const NavigationCurrentEntryChangeEvent = @import("../navigation/navigation.zig").NavigationCurrentEntryChangeEvent;
4343

4444
// Event interfaces
4545
pub const Interfaces = .{

src/browser/html/History.zig

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const log = @import("../../log.zig");
2121

2222
const js = @import("../js/js.zig");
2323
const Page = @import("../page.zig").Page;
24+
const Window = @import("window.zig").Window;
2425

2526
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-history-interface
2627
const History = @This();
@@ -60,7 +61,7 @@ pub fn _pushState(_: *const History, state: js.Object, _: ?[]const u8, _url: ?[]
6061
const url = if (_url) |u| try arena.dupe(u8, u) else try arena.dupe(u8, page.url.raw);
6162

6263
const json = state.toJson(arena) catch return error.DataClone;
63-
_ = try page.session.navigation.pushEntry(url, json, page);
64+
_ = try page.session.navigation.pushEntry(url, json, page, true);
6465
}
6566

6667
pub fn _replaceState(_: *const History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
@@ -163,7 +164,7 @@ pub const PopStateEvent = struct {
163164
};
164165

165166
_ = parser.eventTargetDispatchEvent(
166-
@as(*parser.EventTarget, @ptrCast(&page.window)),
167+
parser.toEventTarget(Window, &page.window),
167168
&evt.proto,
168169
) catch |err| {
169170
log.err(.app, "dispatch popstate event error", .{

src/browser/html/html.zig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ pub const Interfaces = .{
3434
Window,
3535
Navigator,
3636
History,
37-
@import("Navigation.zig").Interfaces,
3837
Location,
3938
MediaQueryList,
4039
@import("DataSet.zig"),

src/browser/html/location.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ pub const Location = struct {
7373
return self.url.get_origin(page);
7474
}
7575

76+
pub fn set_href(_: *Location, url: []const u8, page: *Page) !void {
77+
return page.navigateFromWebAPI(url, .{ .reason = .script }, .{ .push = null });
78+
}
79+
7680
pub fn _assign(_: *const Location, url: []const u8, page: *Page) !void {
7781
return page.navigateFromWebAPI(url, .{ .reason = .script }, .{ .push = null });
7882
}

src/browser/html/window.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const Page = @import("../page.zig").Page;
2525

2626
const Navigator = @import("navigator.zig").Navigator;
2727
const History = @import("History.zig");
28-
const Navigation = @import("Navigation.zig");
28+
const Navigation = @import("../navigation/Navigation.zig");
2929
const Location = @import("location.zig").Location;
3030
const Crypto = @import("../crypto/crypto.zig").Crypto;
3131
const Console = @import("../console/console.zig").Console;

src/browser/js/types.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const Interfaces = generate.Tuple(.{
1616
@import("../storage/storage.zig").Interfaces,
1717
@import("../url/url.zig").Interfaces,
1818
@import("../xhr/xhr.zig").Interfaces,
19+
@import("../navigation/navigation.zig").Interfaces,
1920
@import("../xhr/form_data.zig").Interfaces,
2021
@import("../xhr/File.zig"),
2122
@import("../xmlserializer/xmlserializer.zig").Interfaces,

src/browser/html/Navigation.zig renamed to src/browser/navigation/Navigation.zig

Lines changed: 17 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -32,122 +32,21 @@ const parser = @import("../netsurf.zig");
3232
// https://developer.mozilla.org/en-US/docs/Web/API/Navigation
3333
const Navigation = @This();
3434

35-
pub const Interfaces = .{
36-
Navigation,
37-
NavigationActivation,
38-
NavigationTransition,
39-
NavigationHistoryEntry,
40-
};
41-
42-
pub const NavigationType = enum {
43-
pub const ENUM_JS_USE_TAG = true;
44-
45-
push,
46-
replace,
47-
traverse,
48-
reload,
49-
};
35+
const NavigationKind = @import("navigation.zig").NavigationKind;
36+
const NavigationHistoryEntry = @import("navigation.zig").NavigationHistoryEntry;
37+
const NavigationTransition = @import("navigation.zig").NavigationTransition;
38+
const NavigationEventTarget = @import("NavigationEventTarget.zig");
5039

51-
pub const NavigationKind = union(NavigationType) {
52-
push: ?[]const u8,
53-
replace,
54-
traverse: usize,
55-
reload,
56-
};
40+
const NavigationCurrentEntryChangeEvent = @import("navigation.zig").NavigationCurrentEntryChangeEvent;
5741

58-
pub const prototype = *EventTarget;
59-
base: parser.EventTargetTBase = parser.EventTargetTBase{ .internal_target_type = .plain },
42+
pub const prototype = *NavigationEventTarget;
43+
proto: NavigationEventTarget = NavigationEventTarget{},
6044

6145
index: usize = 0,
6246
// Need to be stable pointers, because Events can reference entries.
6347
entries: std.ArrayListUnmanaged(*NavigationHistoryEntry) = .empty,
6448
next_entry_id: usize = 0,
6549

66-
oncurrententrychange_callback: ?js.Function = null,
67-
68-
// https://developer.mozilla.org/en-US/docs/Web/API/NavigationHistoryEntry
69-
const NavigationHistoryEntry = struct {
70-
pub const prototype = *EventTarget;
71-
base: parser.EventTargetTBase = parser.EventTargetTBase{ .internal_target_type = .plain },
72-
73-
id: []const u8,
74-
key: []const u8,
75-
url: ?[]const u8,
76-
state: ?[]const u8,
77-
78-
pub fn get_id(self: *const NavigationHistoryEntry) []const u8 {
79-
return self.id;
80-
}
81-
82-
pub fn get_index(self: *const NavigationHistoryEntry, page: *Page) i32 {
83-
const navigation = page.session.navigation;
84-
for (navigation.entries.items, 0..) |entry, i| {
85-
if (std.mem.eql(u8, entry.id, self.id)) {
86-
return @intCast(i);
87-
}
88-
}
89-
90-
return -1;
91-
}
92-
93-
pub fn get_key(self: *const NavigationHistoryEntry) []const u8 {
94-
return self.key;
95-
}
96-
97-
pub fn get_sameDocument(self: *const NavigationHistoryEntry, page: *Page) !bool {
98-
const _url = self.url orelse return false;
99-
const url = try URL.parse(_url, null);
100-
return page.url.eqlDocument(&url, page.arena);
101-
}
102-
103-
pub fn get_url(self: *const NavigationHistoryEntry) ?[]const u8 {
104-
return self.url;
105-
}
106-
107-
pub fn _getState(self: *const NavigationHistoryEntry, page: *Page) !?js.Value {
108-
if (self.state) |state| {
109-
return try js.Value.fromJson(page.js, state);
110-
} else {
111-
return null;
112-
}
113-
}
114-
};
115-
116-
// https://developer.mozilla.org/en-US/docs/Web/API/NavigationActivation
117-
const NavigationActivation = struct {
118-
const NavigationActivationType = enum {
119-
pub const ENUM_JS_USE_TAG = true;
120-
121-
push,
122-
reload,
123-
replace,
124-
traverse,
125-
};
126-
127-
entry: NavigationHistoryEntry,
128-
from: ?NavigationHistoryEntry = null,
129-
type: NavigationActivationType,
130-
131-
pub fn get_entry(self: *const NavigationActivation) NavigationHistoryEntry {
132-
return self.entry;
133-
}
134-
135-
pub fn get_from(self: *const NavigationActivation) ?NavigationHistoryEntry {
136-
return self.from;
137-
}
138-
139-
pub fn get_navigationType(self: *const NavigationActivation) NavigationActivationType {
140-
return self.type;
141-
}
142-
};
143-
144-
// https://developer.mozilla.org/en-US/docs/Web/API/NavigationTransition
145-
const NavigationTransition = struct {
146-
finished: js.Promise,
147-
from: NavigationHistoryEntry,
148-
navigation_type: NavigationActivation.NavigationActivationType,
149-
};
150-
15150
pub fn get_canGoBack(self: *const Navigation) bool {
15251
return self.index > 0;
15352
}
@@ -202,16 +101,6 @@ pub fn _forward(self: *Navigation, page: *Page) !NavigationReturn {
202101
return self.navigate(next_entry.url, .{ .traverse = new_index }, page);
203102
}
204103

205-
/// Returns `oncurrententrychange_callback`.
206-
pub fn get_oncurrententrychange(self: *const Navigation) ?js.Function {
207-
return self.oncurrententrychange_callback;
208-
}
209-
210-
/// Sets `oncurrententrychange_callback`.
211-
pub fn set_oncurrententrychange(self: *Navigation, maybe_listener: ?EventHandler.Listener, page: *Page) !void {
212-
try DirectEventHandler(Navigation, self, "currententrychange", maybe_listener, &self.oncurrententrychange_callback, page.arena);
213-
}
214-
215104
// This is for after true navigation processing, where we need to ensure that our entries are up to date.
216105
// This is only really safe to run in the `pageDoneCallback` where we can guarantee that the URL and NavigationKind are correct.
217106
pub fn processNavigation(self: *Navigation, page: *Page) !void {
@@ -227,18 +116,18 @@ pub fn processNavigation(self: *Navigation, page: *Page) !void {
227116
entry.state = null;
228117
},
229118
.push => |state| {
230-
_ = try self.pushEntry(url, state, page);
119+
_ = try self.pushEntry(url, state, page, false);
231120
},
232121
.traverse, .reload => {},
233122
}
234123
} else {
235-
_ = try self.pushEntry(url, null, page);
124+
_ = try self.pushEntry(url, null, page, false);
236125
}
237126
}
238127

239128
/// Pushes an entry into the Navigation stack WITHOUT actually navigating to it.
240129
/// For that, use `navigate`.
241-
pub fn pushEntry(self: *Navigation, _url: ?[]const u8, state: ?[]const u8, page: *Page) !*NavigationHistoryEntry {
130+
pub fn pushEntry(self: *Navigation, _url: ?[]const u8, state: ?[]const u8, page: *Page, dispatch: bool) !*NavigationHistoryEntry {
242131
const arena = page.session.arena;
243132

244133
const url = if (_url) |u| try arena.dupe(u8, u) else null;
@@ -267,7 +156,9 @@ pub fn pushEntry(self: *Navigation, _url: ?[]const u8, state: ?[]const u8, page:
267156
const previous = if (self.entries.items.len > 0) self.currentEntry() else null;
268157
try self.entries.append(arena, entry);
269158
if (previous) |prev| {
270-
NavigationCurrentEntryChangeEvent.dispatch(prev, .push, page);
159+
if (dispatch) {
160+
NavigationCurrentEntryChangeEvent.dispatch(self, prev, .push);
161+
}
271162
}
272163

273164
self.index = index;
@@ -312,11 +203,12 @@ pub fn navigate(
312203
.push => |state| {
313204
if (is_same_document) {
314205
page.url = new_url;
206+
315207
try committed.resolve({});
316208
// todo: Fire navigate event
317209
try finished.resolve({});
318210

319-
_ = try self.pushEntry(url, state, page);
211+
_ = try self.pushEntry(url, state, page, true);
320212
} else {
321213
try page.navigateFromWebAPI(url, .{ .reason = .navigation }, kind);
322214
}
@@ -365,7 +257,7 @@ pub fn _reload(self: *Navigation, _opts: ?ReloadOptions, page: *Page) !Navigatio
365257
if (opts.state) |state| {
366258
const previous = entry;
367259
entry.state = state.toJson(arena) catch return error.DataClone;
368-
NavigationCurrentEntryChangeEvent.dispatch(previous, .reload, page);
260+
NavigationCurrentEntryChangeEvent.dispatch(self, previous, .reload);
369261
}
370262

371263
return self.navigate(entry.url, .reload, page);
@@ -396,78 +288,5 @@ pub fn _updateCurrentEntry(self: *Navigation, options: UpdateCurrentEntryOptions
396288

397289
const previous = self.currentEntry();
398290
self.currentEntry().state = options.state.toJson(arena) catch return error.DataClone;
399-
NavigationCurrentEntryChangeEvent.dispatch(previous, null, page);
400-
}
401-
402-
const Event = @import("../events/event.zig").Event;
403-
404-
pub const NavigationCurrentEntryChangeEvent = struct {
405-
pub const prototype = *Event;
406-
pub const union_make_copy = true;
407-
408-
pub const EventInit = struct {
409-
from: *NavigationHistoryEntry,
410-
navigation_type: ?NavigationType = null,
411-
};
412-
413-
proto: parser.Event,
414-
from: *NavigationHistoryEntry,
415-
navigation_type: ?NavigationType,
416-
417-
pub fn constructor(event_type: []const u8, opts: EventInit) !NavigationCurrentEntryChangeEvent {
418-
const event = try parser.eventCreate();
419-
defer parser.eventDestroy(event);
420-
try parser.eventInit(event, event_type, .{});
421-
parser.eventSetInternalType(event, .navigation_current_entry_change_event);
422-
423-
return .{
424-
.proto = event.*,
425-
.from = opts.from,
426-
.navigation_type = opts.navigation_type,
427-
};
428-
}
429-
430-
pub fn get_from(self: *NavigationCurrentEntryChangeEvent) *NavigationHistoryEntry {
431-
return self.from;
432-
}
433-
434-
pub fn get_navigationType(self: *const NavigationCurrentEntryChangeEvent) ?NavigationType {
435-
return self.navigation_type;
436-
}
437-
438-
pub fn dispatch(from: *NavigationHistoryEntry, typ: ?NavigationType, page: *Page) void {
439-
log.debug(.script_event, "dispatch event", .{
440-
.type = "currententrychange",
441-
.source = "navigation",
442-
});
443-
444-
var evt = NavigationCurrentEntryChangeEvent.constructor(
445-
"currententrychange",
446-
.{ .from = from, .navigation_type = typ },
447-
) catch |err| {
448-
log.err(.app, "event constructor error", .{
449-
.err = err,
450-
.type = "currententrychange",
451-
.source = "navigation",
452-
});
453-
454-
return;
455-
};
456-
457-
_ = parser.eventTargetDispatchEvent(
458-
@as(*parser.EventTarget, @ptrCast(&page.session.navigation)),
459-
&evt.proto,
460-
) catch |err| {
461-
log.err(.app, "dispatch event error", .{
462-
.err = err,
463-
.type = "currententrychange",
464-
.source = "navigation",
465-
});
466-
};
467-
}
468-
};
469-
470-
const testing = @import("../../testing.zig");
471-
test "Browser: Navigation" {
472-
try testing.htmlRunner("html/navigation/navigation.html");
291+
NavigationCurrentEntryChangeEvent.dispatch(self, previous, null);
473292
}

0 commit comments

Comments
 (0)