@@ -90,10 +90,8 @@ pub const Page = struct {
9090
9191 load_state : LoadState = .parsing ,
9292
93- // We should only emit these events once per page. To make sure of that, we
94- // track whether or not we've already emitted the notifications.
95- notified_network_idle : bool = false ,
96- notified_network_almost_idle : bool = false ,
93+ notified_network_idle : IdleNotification = .init ,
94+ notified_network_almost_idle : IdleNotification = .init ,
9795
9896 const Mode = union (enum ) {
9997 pre : void ,
@@ -325,8 +323,7 @@ pub const Page = struct {
325323 // scheduler.run could trigger new http transfers, so do not
326324 // store http_client.active BEFORE this call and then use
327325 // it AFTER.
328- const ms_to_next_task = try scheduler .runHighPriority ();
329- _ = try scheduler .runLowPriority ();
326+ const ms_to_next_task = try scheduler .run ();
330327
331328 if (try_catch .hasCaught ()) {
332329 const msg = (try try_catch .err (self .arena )) orelse "unknown" ;
@@ -335,6 +332,14 @@ pub const Page = struct {
335332 }
336333
337334 const http_active = http_client .active ;
335+ const total_network_activity = http_active + http_client .intercepted ;
336+ if (self .notified_network_almost_idle .check (total_network_activity <= 2 )) {
337+ self .notifyNetworkAlmostIdle ();
338+ }
339+ if (self .notified_network_idle .check (total_network_activity == 0 )) {
340+ self .notifyNetworkIdle ();
341+ }
342+
338343 if (http_active == 0 and exit_when_done ) {
339344 // we don't need to consider http_client.intercepted here
340345 // because exit_when_done is true, and that can only be
@@ -359,7 +364,8 @@ pub const Page = struct {
359364
360365 if (ms > ms_remaining ) {
361366 // Same as above, except we have a scheduled task,
362- // it just happens to be too far into the future.
367+ // it just happens to be too far into the future
368+ // compared to how long we were told to wait.
363369 return .done ;
364370 }
365371
@@ -371,9 +377,6 @@ pub const Page = struct {
371377 // loop to see if anything new can be processed.
372378 std .Thread .sleep (std .time .ns_per_ms * @as (u64 , @intCast (@min (ms , 20 ))));
373379 } else {
374- if (self .notified_network_idle == false and http_active == 0 and http_client .intercepted == 0 ) {
375- self .notifyNetworkIdle ();
376- }
377380 // We're here because we either have active HTTP
378381 // connections, or exit_when_done == false (aka, there's
379382 // an extra_socket registered with the http client).
@@ -484,25 +487,14 @@ pub const Page = struct {
484487 }
485488
486489 fn notifyNetworkIdle (self : * Page ) void {
487- // caller should always check that we haven't sent this already;
488- std .debug .assert (self .notified_network_idle == false );
489-
490- // if we're going to send networkIdle, we should first send networkAlmostIdle
491- // if it hasn't already been sent.
492- if (self .notified_network_almost_idle == false ) {
493- self .notifyNetworkAlmostIdle ();
494- }
495-
496- self .notified_network_idle = true ;
490+ std .debug .assert (self .notified_network_idle == .done );
497491 self .session .browser .notification .dispatch (.page_network_idle , &.{
498492 .timestamp = timestamp (),
499493 });
500494 }
501495
502496 fn notifyNetworkAlmostIdle (self : * Page ) void {
503- // caller should always check that we haven't sent this already;
504- std .debug .assert (self .notified_network_almost_idle == false );
505- self .notified_network_almost_idle = true ;
497+ std .debug .assert (self .notified_network_almost_idle == .done );
506498 self .session .browser .notification .dispatch (.page_network_almost_idle , &.{
507499 .timestamp = timestamp (),
508500 });
@@ -1140,9 +1132,82 @@ pub const NavigateOpts = struct {
11401132 header : ? [:0 ]const u8 = null ,
11411133};
11421134
1135+ const IdleNotification = union (enum ) {
1136+ // hasn't started yet.
1137+ init ,
1138+
1139+ // timestamp where the state was first triggered. If the state stays
1140+ // true (e.g. 0 nework activity for NetworkIdle, or <= 2 for NetworkAlmostIdle)
1141+ // for 500ms, it'll send the notification and transition to .done. If
1142+ // the state doesn't stay true, it'll revert to .init.
1143+ triggered : u64 ,
1144+
1145+ // notification sent - should never be reset
1146+ done ,
1147+
1148+ // Returns `true` if we should send a notification. Only returns true if it
1149+ // was previously triggered 500+ milliseconds ago.
1150+ // active == true when the condition for the notification is true
1151+ // active == false when the condition for the notification is false
1152+ pub fn check (self : * IdleNotification , active : bool ) bool {
1153+ if (active ) {
1154+ switch (self .* ) {
1155+ .done = > {
1156+ // Notification was already sent.
1157+ },
1158+ .init = > {
1159+ // This is the first time the condition was triggered (or
1160+ // the first time after being un-triggered). Record the time
1161+ // so that if the condition holds for long enough, we can
1162+ // send a notification.
1163+ self .* = .{ .triggered = milliTimestamp () };
1164+ },
1165+ .triggered = > | ms | {
1166+ // The condition was already triggered and was triggered
1167+ // again. When this condition holds for 500+ms, we'll send
1168+ // a notification.
1169+ if (milliTimestamp () - ms >= 500 ) {
1170+ // This is the only place in this function where we can
1171+ // return true. The only place where we can tell our caller
1172+ // "send the notification!".
1173+ self .* = .done ;
1174+ return true ;
1175+ }
1176+ // the state hasn't held for 500ms.
1177+ },
1178+ }
1179+ } else {
1180+ switch (self .* ) {
1181+ .done = > {
1182+ // The condition became false, but we already sent the notification
1183+ // There's nothing we can do, it stays .done. We never re-send
1184+ // a notification or "undo" a sent notification (not that we can).
1185+ },
1186+ .init = > {
1187+ // The condition remains false
1188+ },
1189+ .triggered = > {
1190+ // The condition _had_ been true, and we were waiting (500ms)
1191+ // for it to hold, but it hasn't. So we go back to waiting.
1192+ self .* = .init ;
1193+ },
1194+ }
1195+ }
1196+
1197+ // See above for the only case where we ever return true. All other
1198+ // paths go here. This means "don't send the notification". Maybe
1199+ // because it's already been sent, maybe because active is false, or
1200+ // maybe because the condition hasn't held long enough.
1201+ return false ;
1202+ }
1203+ };
1204+
11431205fn timestamp () u32 {
11441206 return @import ("../datetime.zig" ).timestamp ();
11451207}
1208+ fn milliTimestamp () u64 {
1209+ return @import ("../datetime.zig" ).milliTimestamp ();
1210+ }
11461211
11471212// A callback from libdom whenever a script tag is added to the DOM.
11481213// element is guaranteed to be a script element.
0 commit comments