1717// along with this program. If not, see <https://www.gnu.org/licenses/>.
1818
1919const std = @import ("std" );
20+ const log = @import ("../../log.zig" );
2021
2122const Env = @import ("../env.zig" ).Env ;
2223const Page = @import ("../page.zig" ).Page ;
@@ -87,6 +88,32 @@ pub fn pushNavigation(self: *History, _url: []const u8, page: *Page) !void {
8788 self .current = self .stack .items .len - 1 ;
8889}
8990
91+ pub fn dispatchPopStateEvent (state : ? []const u8 , page : * Page ) void {
92+ log .debug (.script_event , "dispatch popstate event" , .{
93+ .type = "popstate" ,
94+ .source = "history" ,
95+ });
96+ History ._dispatchPopStateEvent (state , page ) catch | err | {
97+ log .err (.app , "dispatch popstate event error" , .{
98+ .err = err ,
99+ .type = "popstate" ,
100+ .source = "history" ,
101+ });
102+ };
103+ }
104+
105+ fn _dispatchPopStateEvent (
106+ state : ? []const u8 ,
107+ page : * Page ,
108+ ) ! void {
109+ var evt = try PopStateEvent .constructor ("popstate" , .{ .state = state });
110+
111+ _ = try parser .eventTargetDispatchEvent (
112+ @as (* parser .EventTarget , @ptrCast (& page .window )),
113+ @as (* parser .Event , @ptrCast (& evt )),
114+ );
115+ }
116+
90117pub fn _pushState (self : * History , state : Env.JsObject , _ : ? []const u8 , _url : ? []const u8 , page : * Page ) ! void {
91118 const arena = page .session .arena ;
92119
@@ -123,6 +150,15 @@ pub fn go(self: *History, delta: i32, page: *Page) !void {
123150 const index = @as (usize , @intCast (index_s ));
124151 const entry = self .stack .items [index ];
125152 self .current = index ;
153+
154+ if (try page .isSameOrigin (entry .url )) {
155+ if (entry .state ) | state | {
156+ History .dispatchPopStateEvent (state , page );
157+ } else {
158+ History .dispatchPopStateEvent (null , page );
159+ }
160+ }
161+
126162 try page .navigateFromWebAPI (entry .url , .{ .reason = .history });
127163}
128164
@@ -138,6 +174,46 @@ pub fn _forward(self: *History, page: *Page) !void {
138174 try self .go (1 , page );
139175}
140176
177+ const parser = @import ("../netsurf.zig" );
178+ const Event = @import ("../events/event.zig" ).Event ;
179+
180+ pub const PopStateEvent = struct {
181+ pub const prototype = * Event ;
182+ pub const union_make_copy = true ;
183+
184+ pub const EventInit = struct {
185+ state : ? []const u8 = null ,
186+ };
187+
188+ proto : parser.Event ,
189+ state : ? []const u8 ,
190+
191+ pub fn constructor (event_type : []const u8 , opts : ? EventInit ) ! PopStateEvent {
192+ const event = try parser .eventCreate ();
193+ defer parser .eventDestroy (event );
194+ try parser .eventInit (event , event_type , .{});
195+ parser .eventSetInternalType (event , .pop_state );
196+
197+ const o = opts orelse EventInit {};
198+
199+ return .{
200+ .proto = event .* ,
201+ .state = o .state ,
202+ };
203+ }
204+
205+ // `hasUAVisualTransition` is not implemented. It isn't baseline so this is okay.
206+
207+ pub fn get_state (self : * const PopStateEvent , page : * Page ) ! ? Env.Value {
208+ if (self .state ) | state | {
209+ const value = try Env .Value .fromJson (page .main_context , state );
210+ return value ;
211+ } else {
212+ return null ;
213+ }
214+ }
215+ };
216+
141217const testing = @import ("../../testing.zig" );
142218test "Browser: HTML.History" {
143219 try testing .htmlRunner ("html/history.html" );
0 commit comments