@@ -21,140 +21,79 @@ const log = @import("../../log.zig");
2121
2222const js = @import ("../js/js.zig" );
2323const 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
2627const History = @This ();
2728
28- const HistoryEntry = struct {
29- url : []const u8 ,
30- // This is serialized as JSON because
31- // History must survive a JsContext.
32- state : ? []u8 ,
33- };
34-
3529const ScrollRestorationMode = enum {
30+ pub const ENUM_JS_USE_TAG = true ;
31+
3632 auto ,
3733 manual ,
38-
39- pub fn fromString (str : []const u8 ) ? ScrollRestorationMode {
40- for (std .enums .values (ScrollRestorationMode )) | mode | {
41- if (std .ascii .eqlIgnoreCase (str , @tagName (mode ))) {
42- return mode ;
43- }
44- } else {
45- return null ;
46- }
47- }
48-
49- pub fn toString (self : ScrollRestorationMode ) []const u8 {
50- return @tagName (self );
51- }
5234};
5335
5436scroll_restoration : ScrollRestorationMode = .auto ,
55- stack : std .ArrayListUnmanaged (HistoryEntry ) = .empty ,
56- current : ? usize = null ,
5737
58- pub fn get_length (self : * History ) u32 {
59- return @intCast (self . stack .items .len );
38+ pub fn get_length (_ : * History , page : * Page ) u32 {
39+ return @intCast (page . session . navigation . entries .items .len );
6040}
6141
6242pub fn get_scrollRestoration (self : * History ) ScrollRestorationMode {
6343 return self .scroll_restoration ;
6444}
6545
66- pub fn set_scrollRestoration (self : * History , mode : [] const u8 ) void {
67- self .scroll_restoration = ScrollRestorationMode . fromString ( mode ) orelse self . scroll_restoration ;
46+ pub fn set_scrollRestoration (self : * History , mode : ScrollRestorationMode ) void {
47+ self .scroll_restoration = mode ;
6848}
6949
70- pub fn get_state (self : * History , page : * Page ) ! ? js.Value {
71- if (self .current ) | curr | {
72- const entry = self .stack .items [curr ];
73- if (entry .state ) | state | {
74- const value = try js .Value .fromJson (page .js , state );
75- return value ;
76- } else {
77- return null ;
78- }
50+ pub fn get_state (_ : * History , page : * Page ) ! ? js.Value {
51+ if (page .session .navigation .currentEntry ().state ) | state | {
52+ const value = try js .Value .fromJson (page .js , state );
53+ return value ;
7954 } else {
8055 return null ;
8156 }
8257}
8358
84- pub fn pushNavigation ( self : * History , _url : []const u8 , page : * Page ) ! void {
59+ pub fn _pushState ( _ : * const History , state : js.Object , _ : ? [] const u8 , _url : ? []const u8 , page : * Page ) ! void {
8560 const arena = page .session .arena ;
86- const url = try arena .dupe (u8 , _url );
87-
88- const entry = HistoryEntry { .state = null , .url = url };
89- try self .stack .append (arena , entry );
90- self .current = self .stack .items .len - 1 ;
91- }
92-
93- pub fn dispatchPopStateEvent (state : ? []const u8 , page : * Page ) void {
94- log .debug (.script_event , "dispatch popstate event" , .{
95- .type = "popstate" ,
96- .source = "history" ,
97- });
98- History ._dispatchPopStateEvent (state , page ) catch | err | {
99- log .err (.app , "dispatch popstate event error" , .{
100- .err = err ,
101- .type = "popstate" ,
102- .source = "history" ,
103- });
104- };
105- }
106-
107- fn _dispatchPopStateEvent (state : ? []const u8 , page : * Page ) ! void {
108- var evt = try PopStateEvent .constructor ("popstate" , .{ .state = state });
61+ const url = if (_url ) | u | try arena .dupe (u8 , u ) else try arena .dupe (u8 , page .url .raw );
10962
110- _ = try parser .eventTargetDispatchEvent (
111- @as (* parser .EventTarget , @ptrCast (& page .window )),
112- & evt .proto ,
113- );
63+ const json = state .toJson (arena ) catch return error .DataClone ;
64+ _ = try page .session .navigation .pushEntry (url , json , page , true );
11465}
11566
116- pub fn _pushState ( self : * History , state : js.Object , _ : ? []const u8 , _url : ? []const u8 , page : * Page ) ! void {
67+ pub fn _replaceState ( _ : * const History , state : js.Object , _ : ? []const u8 , _url : ? []const u8 , page : * Page ) ! void {
11768 const arena = page .session .arena ;
11869
70+ const entry = page .session .navigation .currentEntry ();
11971 const json = try state .toJson (arena );
12072 const url = if (_url ) | u | try arena .dupe (u8 , u ) else try arena .dupe (u8 , page .url .raw );
121- const entry = HistoryEntry { .state = json , .url = url };
122- try self .stack .append (arena , entry );
123- self .current = self .stack .items .len - 1 ;
124- }
12573
126- pub fn _replaceState (self : * History , state : js.Object , _ : ? []const u8 , _url : ? []const u8 , page : * Page ) ! void {
127- const arena = page .session .arena ;
128-
129- if (self .current ) | curr | {
130- const entry = & self .stack .items [curr ];
131- const json = try state .toJson (arena );
132- const url = if (_url ) | u | try arena .dupe (u8 , u ) else try arena .dupe (u8 , page .url .raw );
133- entry .* = HistoryEntry { .state = json , .url = url };
134- } else {
135- try self ._pushState (state , "" , _url , page );
136- }
74+ entry .state = json ;
75+ entry .url = url ;
13776}
13877
139- pub fn go (self : * History , delta : i32 , page : * Page ) ! void {
78+ pub fn go (_ : * const History , delta : i32 , page : * Page ) ! void {
14079 // 0 behaves the same as no argument, both reloading the page.
141- // If this is getting called, there SHOULD be an entry, atleast from pushNavigation.
142- const current = self .current .? ;
14380
81+ const current = page .session .navigation .index ;
14482 const index_s : i64 = @intCast (@as (i64 , @intCast (current )) + @as (i64 , @intCast (delta )));
145- if (index_s < 0 or index_s > self . stack .items .len - 1 ) {
83+ if (index_s < 0 or index_s > page . session . navigation . entries .items .len - 1 ) {
14684 return ;
14785 }
14886
14987 const index = @as (usize , @intCast (index_s ));
150- const entry = self .stack .items [index ];
151- self .current = index ;
88+ const entry = page .session .navigation .entries .items [index ];
15289
153- if (try page .isSameOrigin (entry .url )) {
154- History .dispatchPopStateEvent (entry .state , page );
90+ if (entry .url ) | url | {
91+ if (try page .isSameOrigin (url )) {
92+ PopStateEvent .dispatch (entry .state , page );
93+ }
15594 }
15695
157- try page .navigateFromWebAPI (entry .url , .{ .reason = .history } );
96+ _ = try page .session . navigation . navigate (entry .url , .{ .traverse = index }, page );
15897}
15998
16099pub fn _go (self : * History , _delta : ? i32 , page : * Page ) ! void {
@@ -207,9 +146,38 @@ pub const PopStateEvent = struct {
207146 return null ;
208147 }
209148 }
149+
150+ pub fn dispatch (state : ? []const u8 , page : * Page ) void {
151+ log .debug (.script_event , "dispatch popstate event" , .{
152+ .type = "popstate" ,
153+ .source = "history" ,
154+ });
155+
156+ var evt = PopStateEvent .constructor ("popstate" , .{ .state = state }) catch | err | {
157+ log .err (.app , "event constructor error" , .{
158+ .err = err ,
159+ .type = "popstate" ,
160+ .source = "history" ,
161+ });
162+
163+ return ;
164+ };
165+
166+ _ = parser .eventTargetDispatchEvent (
167+ parser .toEventTarget (Window , & page .window ),
168+ & evt .proto ,
169+ ) catch | err | {
170+ log .err (.app , "dispatch popstate event error" , .{
171+ .err = err ,
172+ .type = "popstate" ,
173+ .source = "history" ,
174+ });
175+ };
176+ }
210177};
211178
212179const testing = @import ("../../testing.zig" );
213180test "Browser: HTML.History" {
214- try testing .htmlRunner ("html/history.html" );
181+ try testing .htmlRunner ("html/history/history.html" );
182+ try testing .htmlRunner ("html/history/history2.html" );
215183}
0 commit comments