@@ -134,6 +134,10 @@ pub const Session = struct {
134134 storage_shed : storage.Shed ,
135135 cookie_jar : storage.CookieJar ,
136136
137+ // arbitrary that we pass to the inspector, which the inspector will include
138+ // in any response/event that it emits.
139+ aux_data : ? []const u8 = null ,
140+
137141 page : ? Page = null ,
138142 http_client : * http.Client ,
139143
@@ -158,6 +162,7 @@ pub const Session = struct {
158162 const allocator = app .allocator ;
159163 self .* = .{
160164 .app = app ,
165+ .aux_data = null ,
161166 .browser = browser ,
162167 .notify_ctx = any_ctx ,
163168 .inspector = undefined ,
@@ -250,8 +255,12 @@ pub const Session = struct {
250255 // load polyfills
251256 try polyfill .load (self .arena .allocator (), self .executor );
252257
258+ if (aux_data ) | ad | {
259+ self .aux_data = try self .arena .allocator ().dupe (u8 , ad );
260+ }
261+
253262 // inspector
254- self .contextCreated (page , aux_data );
263+ self .contextCreated (page );
255264
256265 return page ;
257266 }
@@ -279,9 +288,28 @@ pub const Session = struct {
279288 return &(self .page orelse return null );
280289 }
281290
282- fn contextCreated (self : * Session , page : * Page , aux_data : ? []const u8 ) void {
291+ fn pageNavigate (self : * Session , url_string : []const u8 ) ! void {
292+ // currently, this is only called from the page, so let's hope
293+ // it isn't null!
294+ std .debug .assert (self .page != null );
295+
296+ // can't use the page arena, because we're about to reset it
297+ // and don't want to use the session's arena, because that'll start to
298+ // look like a leak if we navigate from page to page a lot.
299+ var buf : [1024 ]u8 = undefined ;
300+ var fba = std .heap .FixedBufferAllocator .init (& buf );
301+ const url = try self .page .? .url .? .resolve (fba .allocator (), url_string );
302+
303+ self .removePage ();
304+ var page = try self .createPage (null );
305+ return page .navigate (url , .{
306+ .reason = .anchor ,
307+ });
308+ }
309+
310+ fn contextCreated (self : * Session , page : * Page ) void {
283311 log .debug ("inspector context created" , .{});
284- self .inspector .contextCreated (self .executor , "" , (page .origin () catch "://" ) orelse "://" , aux_data );
312+ self .inspector .contextCreated (self .executor , "" , (page .origin () catch "://" ) orelse "://" , self . aux_data );
285313 }
286314
287315 fn notify (self : * const Session , notification : * const Notification ) void {
@@ -361,7 +389,7 @@ pub const Page = struct {
361389 // spec reference: https://html.spec.whatwg.org/#document-lifecycle
362390 // - aux_data: extra data forwarded to the Inspector
363391 // see Inspector.contextCreated
364- pub fn navigate (self : * Page , request_url : URL , aux_data : ? [] const u8 ) ! void {
392+ pub fn navigate (self : * Page , request_url : URL , opts : NavigateOpts ) ! void {
365393 const arena = self .arena ;
366394 const session = self .session ;
367395
@@ -387,7 +415,12 @@ pub const Page = struct {
387415 var request = try self .newHTTPRequest (.GET , url , .{ .navigation = true });
388416 defer request .deinit ();
389417
390- session .notify (&.{ .page_navigate = .{ .url = url , .timestamp = timestamp () } });
418+ session .notify (&.{ .page_navigate = .{
419+ .url = url ,
420+ .reason = opts .reason ,
421+ .timestamp = timestamp (),
422+ } });
423+
391424 var response = try request .sendSync (.{});
392425
393426 // would be different than self.url in the case of a redirect
@@ -417,7 +450,7 @@ pub const Page = struct {
417450 var mime = try Mime .parse (arena , ct );
418451
419452 if (mime .isHTML ()) {
420- try self .loadHTMLDoc (& response , mime .charset orelse "utf-8" , aux_data );
453+ try self .loadHTMLDoc (& response , mime .charset orelse "utf-8" );
421454 } else {
422455 log .info ("non-HTML document: {s}" , .{ct });
423456 var arr : std .ArrayListUnmanaged (u8 ) = .{};
@@ -428,44 +461,14 @@ pub const Page = struct {
428461 self .raw_data = arr .items ;
429462 }
430463
431- session .notify (&.{ .page_navigated = .{ .url = url , .timestamp = timestamp () } });
432- }
433-
434- pub const ClickResult = union (enum ) {
435- navigate : std.Uri ,
436- };
437-
438- pub const MouseEvent = struct {
439- x : i32 ,
440- y : i32 ,
441- type : Type ,
442-
443- const Type = enum {
444- pressed ,
445- released ,
446- };
447- };
448-
449- pub fn mouseEvent (self : * Page , me : MouseEvent ) ! void {
450- if (me .type != .pressed ) {
451- return ;
452- }
453-
454- const element = self .renderer .getElementAtPosition (me .x , me .y ) orelse return ;
455-
456- const event = try parser .mouseEventCreate ();
457- defer parser .mouseEventDestroy (event );
458- try parser .mouseEventInit (event , "click" , .{
459- .bubbles = true ,
460- .cancelable = true ,
461- .x = me .x ,
462- .y = me .y ,
463- });
464- _ = try parser .elementDispatchEvent (element , @ptrCast (event ));
464+ session .notify (&.{ .page_navigated = .{
465+ .url = url ,
466+ .timestamp = timestamp (),
467+ } });
465468 }
466469
467470 // https://html.spec.whatwg.org/#read-html
468- fn loadHTMLDoc (self : * Page , reader : anytype , charset : []const u8 , aux_data : ? [] const u8 ) ! void {
471+ fn loadHTMLDoc (self : * Page , reader : anytype , charset : []const u8 ) ! void {
469472 const arena = self .arena ;
470473
471474 // start netsurf memory arena.
@@ -507,7 +510,7 @@ pub const Page = struct {
507510 // https://html.spec.whatwg.org/#read-html
508511
509512 // inspector
510- session .contextCreated (self , aux_data );
513+ session .contextCreated (self );
511514
512515 {
513516 // update the sessions state
@@ -738,6 +741,35 @@ pub const Page = struct {
738741 return request ;
739742 }
740743
744+ pub const MouseEvent = struct {
745+ x : i32 ,
746+ y : i32 ,
747+ type : Type ,
748+
749+ const Type = enum {
750+ pressed ,
751+ released ,
752+ };
753+ };
754+
755+ pub fn mouseEvent (self : * Page , me : MouseEvent ) ! void {
756+ if (me .type != .pressed ) {
757+ return ;
758+ }
759+
760+ const element = self .renderer .getElementAtPosition (me .x , me .y ) orelse return ;
761+
762+ const event = try parser .mouseEventCreate ();
763+ defer parser .mouseEventDestroy (event );
764+ try parser .mouseEventInit (event , "click" , .{
765+ .bubbles = true ,
766+ .cancelable = true ,
767+ .x = me .x ,
768+ .y = me .y ,
769+ });
770+ _ = try parser .elementDispatchEvent (element , @ptrCast (event ));
771+ }
772+
741773 fn windowClicked (ctx : * anyopaque , event : * parser.Event ) void {
742774 const self : * Page = @alignCast (@ptrCast (ctx ));
743775 self ._windowClicked (event ) catch | err | {
@@ -746,19 +778,22 @@ pub const Page = struct {
746778 }
747779
748780 fn _windowClicked (self : * Page , event : * parser.Event ) ! void {
749- _ = self ;
750-
751781 const target = (try parser .eventTarget (event )) orelse return ;
752782
753783 const node = parser .eventTargetToNode (target );
754784 if (try parser .nodeType (node ) != .element ) {
755785 return ;
756786 }
757787
758- const element : * parser.ElementHTML = @ptrCast (node );
759- const tag_name = try parser .elementHTMLGetTagType (element );
760- // TODO https://github.com/lightpanda-io/browser/pull/501
761- _ = tag_name ;
788+ const html_element : * parser.ElementHTML = @ptrCast (node );
789+ switch (try parser .elementHTMLGetTagType (html_element )) {
790+ .a = > {
791+ const element : * parser.Element = @ptrCast (node );
792+ const href = (try parser .elementGetAttribute (element , "href" )) orelse return ;
793+ return self .session .pageNavigate (href );
794+ },
795+ else = > {},
796+ }
762797 }
763798
764799 const Script = struct {
@@ -825,8 +860,17 @@ pub const Page = struct {
825860 };
826861};
827862
863+ pub const NavigateReason = enum {
864+ anchor ,
865+ address_bar ,
866+ };
867+
868+ const NavigateOpts = struct {
869+ reason : NavigateReason = .address_bar ,
870+ };
871+
828872// provide very poor abstration to the rest of the code. In theory, we can change
829- // the FlatRendere to a different implementation, and it'll all just work.
873+ // the FlatRenderer to a different implementation, and it'll all just work.
830874pub const Renderer = FlatRenderer ;
831875
832876// This "renderer" positions elements in a single row in an unspecified order.
0 commit comments