@@ -11,7 +11,7 @@ namespace PuppeteerSharp
1111 internal class NavigationWatcher
1212 {
1313 private FrameManager _frameManager ;
14- private Frame _mainFrame ;
14+ private Frame _frame ;
1515 private dynamic _options ;
1616 private static readonly Dictionary < string , string > _puppeteerToProtocolLifecycle = new Dictionary < string , string > ( )
1717 {
@@ -24,16 +24,17 @@ internal class NavigationWatcher
2424 private int _timeout ;
2525 private string _initialLoaderId ;
2626 private Task _navigationPromise ;
27+ private Timer _timer = null ;
2728
2829 public NavigationWatcher ( FrameManager frameManager , Frame mainFrame , int timeout , dynamic options )
2930 {
3031 var waitUntil = new [ ] { "load" } ;
3132
32- if ( options . waitUntil is Array )
33+ if ( options != null && options . waitUntil is Array )
3334 {
3435 waitUntil = options . waitUntil ;
3536 }
36- else if ( options . waitUntil is string )
37+ else if ( options != null && options . waitUntil is string )
3738 {
3839 waitUntil = new string [ ] { options . waitUntil . ToString ( ) } ;
3940 }
@@ -46,7 +47,7 @@ public NavigationWatcher(FrameManager frameManager, Frame mainFrame, int timeout
4647 } ) ;
4748
4849 _frameManager = frameManager ;
49- _mainFrame = mainFrame ;
50+ _frame = mainFrame ;
5051 _options = options ;
5152 _initialLoaderId = mainFrame . LoaderId ;
5253 _timeout = timeout ;
@@ -75,40 +76,72 @@ public NavigationWatcher(FrameManager frameManager, Frame mainFrame, int timeout
7576 #region Public methods
7677 public void Cancel ( )
7778 {
78- throw new NotImplementedException ( ) ;
79+ CleanUp ( ) ;
7980 }
8081 #endregion
81- #region Private methods
82- void FrameManager_LifecycleEvent ( object sender , PuppeteerSharp . FrameEventArgs e )
83- {
82+ #region Private methods
8483
85- }
86-
87- private void CleanUp ( )
88- {
89- throw new NotImplementedException ( ) ;
90- }
91-
92- private Task CreateTimeoutTask ( )
93- {
94- var wrapper = new TaskCompletionSource < bool > ( ) ;
95-
96- if ( _timeout == 0 )
84+ void FrameManager_LifecycleEvent ( object sender , FrameEventArgs e )
85+ {
86+ // We expect navigation to commit.
87+ if ( _frame . LoaderId == _initialLoaderId )
9788 {
98- wrapper . SetResult ( true ) ;
89+ return ;
9990 }
100- else
91+ if ( ! CheckLifecycle ( _frame , _expectedLifecycle ) )
10192 {
102- Timer timer = null ;
103- timer = new Timer ( ( state ) =>
104- {
105- wrapper . SetException (
106- new ChromeProcessException ( $ "Navigation Timeout Exceeded: '{ _timeout } 'ms exceeded") ) ;
107- timer . Dispose ( ) ;
108- } , null , _timeout , 0 ) ;
93+ return ;
10994 }
11095
111- return wrapper . Task ;
96+ LifeCycleCompleteTaskWrapper . SetResult ( true ) ;
97+ }
98+
99+ private bool CheckLifecycle ( Frame frame , IEnumerable < string > expectedLifecycle )
100+ {
101+ foreach ( var item in expectedLifecycle )
102+ {
103+ if ( ! frame . LifecycleEvents . Contains ( item ) )
104+ {
105+ return false ;
106+ }
107+ }
108+ foreach ( var child in frame . ChildFrames )
109+ {
110+ if ( ! CheckLifecycle ( child , expectedLifecycle ) )
111+ {
112+ return false ;
113+ }
114+ }
115+ return true ;
116+ }
117+
118+ private void CleanUp ( )
119+ {
120+ _frameManager . LifecycleEvent -= FrameManager_LifecycleEvent ;
121+ _frameManager . FrameDetached -= FrameManager_LifecycleEvent ;
122+ _timer ? . Dispose ( ) ;
123+ }
124+
125+ private Task CreateTimeoutTask ( )
126+ {
127+ var wrapper = new TaskCompletionSource < bool > ( ) ;
128+
129+ if ( _timeout == 0 )
130+ {
131+ wrapper . SetResult ( true ) ;
132+ }
133+ else
134+ {
135+ _timer = new Timer ( ( state ) =>
136+ {
137+ wrapper . SetException (
138+ new ChromeProcessException ( $ "Navigation Timeout Exceeded: '{ _timeout } 'ms exceeded") ) ;
139+ _timer . Dispose ( ) ;
140+ _timer = null ;
141+ } , null , _timeout , 0 ) ;
142+ }
143+
144+ return wrapper . Task ;
112145 }
113146 #endregion
114147 }
0 commit comments