fix: emit load/domcontentloaded events as reported by the browser (#16861)
Instead of requiring all frames in the subtree to receive a particular
event, we rely on the browser's definition of load and DOMContentLoaded.
This changes logic in a few edge cases:
- Some browsers do not emit load event upon window.stop() at all.
- DOMContentLoaded does not wait for subframes, so they might not be
  ready when passing `{ waitUntil: 'domcontentloaded' }`.
`networkidle` preserves the old logic.
			
			
This commit is contained in:
		
							parent
							
								
									b93668e301
								
							
						
					
					
						commit
						fea8772d95
					
				| 
						 | 
					@ -435,7 +435,6 @@ class FrameSession {
 | 
				
			||||||
      eventsHelper.addEventListener(this._client, 'Page.frameDetached', event => this._onFrameDetached(event.frameId, event.reason)),
 | 
					      eventsHelper.addEventListener(this._client, 'Page.frameDetached', event => this._onFrameDetached(event.frameId, event.reason)),
 | 
				
			||||||
      eventsHelper.addEventListener(this._client, 'Page.frameNavigated', event => this._onFrameNavigated(event.frame, false)),
 | 
					      eventsHelper.addEventListener(this._client, 'Page.frameNavigated', event => this._onFrameNavigated(event.frame, false)),
 | 
				
			||||||
      eventsHelper.addEventListener(this._client, 'Page.frameRequestedNavigation', event => this._onFrameRequestedNavigation(event)),
 | 
					      eventsHelper.addEventListener(this._client, 'Page.frameRequestedNavigation', event => this._onFrameRequestedNavigation(event)),
 | 
				
			||||||
      eventsHelper.addEventListener(this._client, 'Page.frameStoppedLoading', event => this._onFrameStoppedLoading(event.frameId)),
 | 
					 | 
				
			||||||
      eventsHelper.addEventListener(this._client, 'Page.javascriptDialogOpening', event => this._onDialog(event)),
 | 
					      eventsHelper.addEventListener(this._client, 'Page.javascriptDialogOpening', event => this._onDialog(event)),
 | 
				
			||||||
      eventsHelper.addEventListener(this._client, 'Page.navigatedWithinDocument', event => this._onFrameNavigatedWithinDocument(event.frameId, event.url)),
 | 
					      eventsHelper.addEventListener(this._client, 'Page.navigatedWithinDocument', event => this._onFrameNavigatedWithinDocument(event.frameId, event.url)),
 | 
				
			||||||
      eventsHelper.addEventListener(this._client, 'Runtime.bindingCalled', event => this._onBindingCalled(event)),
 | 
					      eventsHelper.addEventListener(this._client, 'Runtime.bindingCalled', event => this._onBindingCalled(event)),
 | 
				
			||||||
| 
						 | 
					@ -601,12 +600,6 @@ class FrameSession {
 | 
				
			||||||
      this._page._frameManager.frameLifecycleEvent(event.frameId, 'domcontentloaded');
 | 
					      this._page._frameManager.frameLifecycleEvent(event.frameId, 'domcontentloaded');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _onFrameStoppedLoading(frameId: string) {
 | 
					 | 
				
			||||||
    if (this._eventBelongsToStaleFrame(frameId))
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    this._page._frameManager.frameStoppedLoading(frameId);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  _handleFrameTree(frameTree: Protocol.Page.FrameTree) {
 | 
					  _handleFrameTree(frameTree: Protocol.Page.FrameTree) {
 | 
				
			||||||
    this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId || null);
 | 
					    this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId || null);
 | 
				
			||||||
    this._onFrameNavigated(frameTree.frame, true);
 | 
					    this._onFrameNavigated(frameTree.frame, true);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,7 +48,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
 | 
				
			||||||
      url: frame.url(),
 | 
					      url: frame.url(),
 | 
				
			||||||
      name: frame.name(),
 | 
					      name: frame.name(),
 | 
				
			||||||
      parentFrame: FrameDispatcher.fromNullable(scope, frame.parentFrame()),
 | 
					      parentFrame: FrameDispatcher.fromNullable(scope, frame.parentFrame()),
 | 
				
			||||||
      loadStates: Array.from(frame._subtreeLifecycleEvents),
 | 
					      loadStates: Array.from(frame._firedLifecycleEvents),
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    this._frame = frame;
 | 
					    this._frame = frame;
 | 
				
			||||||
    this.addObjectListener(Frame.Events.AddLifecycle, lifecycleEvent => {
 | 
					    this.addObjectListener(Frame.Events.AddLifecycle, lifecycleEvent => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -62,6 +62,8 @@ export type GotoResult = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ConsoleTagHandler = () => void;
 | 
					type ConsoleTagHandler = () => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type RegularLifecycleEvent = Exclude<types.LifecycleEvent, 'networkidle'>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type FunctionWithSource = (source: { context: BrowserContext, page: Page, frame: Frame}, ...args: any) => any;
 | 
					export type FunctionWithSource = (source: { context: BrowserContext, page: Page, frame: Frame}, ...args: any) => any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type NavigationEvent = {
 | 
					export type NavigationEvent = {
 | 
				
			||||||
| 
						 | 
					@ -279,17 +281,11 @@ export class FrameManager {
 | 
				
			||||||
    const frame = this._frames.get(frameId);
 | 
					    const frame = this._frames.get(frameId);
 | 
				
			||||||
    if (frame) {
 | 
					    if (frame) {
 | 
				
			||||||
      this._removeFramesRecursively(frame);
 | 
					      this._removeFramesRecursively(frame);
 | 
				
			||||||
      // Recalculate subtree lifecycle for the whole tree - it should not be that big.
 | 
					      this._page.mainFrame()._recalculateNetworkIdle();
 | 
				
			||||||
      this._page.mainFrame()._recalculateLifecycle();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  frameStoppedLoading(frameId: string) {
 | 
					  frameLifecycleEvent(frameId: string, event: RegularLifecycleEvent) {
 | 
				
			||||||
    this.frameLifecycleEvent(frameId, 'domcontentloaded');
 | 
					 | 
				
			||||||
    this.frameLifecycleEvent(frameId, 'load');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  frameLifecycleEvent(frameId: string, event: types.LifecycleEvent) {
 | 
					 | 
				
			||||||
    const frame = this._frames.get(frameId);
 | 
					    const frame = this._frames.get(frameId);
 | 
				
			||||||
    if (frame)
 | 
					    if (frame)
 | 
				
			||||||
      frame._onLifecycleEvent(event);
 | 
					      frame._onLifecycleEvent(event);
 | 
				
			||||||
| 
						 | 
					@ -478,8 +474,8 @@ export class Frame extends SdkObject {
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _id: string;
 | 
					  _id: string;
 | 
				
			||||||
  private _firedLifecycleEvents = new Set<types.LifecycleEvent>();
 | 
					  _firedLifecycleEvents = new Set<types.LifecycleEvent>();
 | 
				
			||||||
  _subtreeLifecycleEvents = new Set<types.LifecycleEvent>();
 | 
					  private _firedNetworkIdleSelf = false;
 | 
				
			||||||
  _currentDocument: DocumentInfo;
 | 
					  _currentDocument: DocumentInfo;
 | 
				
			||||||
  private _pendingDocument: DocumentInfo | undefined;
 | 
					  private _pendingDocument: DocumentInfo | undefined;
 | 
				
			||||||
  readonly _page: Page;
 | 
					  readonly _page: Page;
 | 
				
			||||||
| 
						 | 
					@ -516,7 +512,6 @@ export class Frame extends SdkObject {
 | 
				
			||||||
      this._parentFrame._childFrames.add(this);
 | 
					      this._parentFrame._childFrames.add(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this._firedLifecycleEvents.add('commit');
 | 
					    this._firedLifecycleEvents.add('commit');
 | 
				
			||||||
    this._subtreeLifecycleEvents.add('commit');
 | 
					 | 
				
			||||||
    if (id !== kDummyFrameId)
 | 
					    if (id !== kDummyFrameId)
 | 
				
			||||||
      this._startNetworkIdleTimer();
 | 
					      this._startNetworkIdleTimer();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -525,23 +520,26 @@ export class Frame extends SdkObject {
 | 
				
			||||||
    return this._detached;
 | 
					    return this._detached;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _onLifecycleEvent(event: types.LifecycleEvent) {
 | 
					  _onLifecycleEvent(event: RegularLifecycleEvent) {
 | 
				
			||||||
    if (this._firedLifecycleEvents.has(event))
 | 
					    if (this._firedLifecycleEvents.has(event))
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    this._firedLifecycleEvents.add(event);
 | 
					    this._firedLifecycleEvents.add(event);
 | 
				
			||||||
    // Recalculate subtree lifecycle for the whole tree - it should not be that big.
 | 
					    this.emit(Frame.Events.AddLifecycle, event);
 | 
				
			||||||
    this._page.mainFrame()._recalculateLifecycle();
 | 
					    if (this === this._page.mainFrame() && this._url !== 'about:blank')
 | 
				
			||||||
 | 
					      debugLogger.log('api', `  "${event}" event fired`);
 | 
				
			||||||
 | 
					    this._page.mainFrame()._recalculateNetworkIdle();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _onClearLifecycle() {
 | 
					  _onClearLifecycle() {
 | 
				
			||||||
 | 
					    for (const event of this._firedLifecycleEvents)
 | 
				
			||||||
 | 
					      this.emit(Frame.Events.RemoveLifecycle, event);
 | 
				
			||||||
    this._firedLifecycleEvents.clear();
 | 
					    this._firedLifecycleEvents.clear();
 | 
				
			||||||
    // Recalculate subtree lifecycle for the whole tree - it should not be that big.
 | 
					 | 
				
			||||||
    this._page.mainFrame()._recalculateLifecycle(this);
 | 
					 | 
				
			||||||
    // Keep the current navigation request if any.
 | 
					    // Keep the current navigation request if any.
 | 
				
			||||||
    this._inflightRequests = new Set(Array.from(this._inflightRequests).filter(request => request === this._currentDocument.request));
 | 
					    this._inflightRequests = new Set(Array.from(this._inflightRequests).filter(request => request === this._currentDocument.request));
 | 
				
			||||||
    this._stopNetworkIdleTimer();
 | 
					    this._stopNetworkIdleTimer();
 | 
				
			||||||
    if (this._inflightRequests.size === 0)
 | 
					    if (this._inflightRequests.size === 0)
 | 
				
			||||||
      this._startNetworkIdleTimer();
 | 
					      this._startNetworkIdleTimer();
 | 
				
			||||||
 | 
					    this._page.mainFrame()._recalculateNetworkIdle(this);
 | 
				
			||||||
    this._onLifecycleEvent('commit');
 | 
					    this._onLifecycleEvent('commit');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -599,37 +597,26 @@ export class Frame extends SdkObject {
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _recalculateLifecycle(frameThatAllowsRemovingLifecycleEvents?: Frame) {
 | 
					  _recalculateNetworkIdle(frameThatAllowsRemovingNetworkIdle?: Frame) {
 | 
				
			||||||
    const events = new Set<types.LifecycleEvent>(this._firedLifecycleEvents);
 | 
					    let isNetworkIdle = this._firedNetworkIdleSelf;
 | 
				
			||||||
    for (const child of this._childFrames) {
 | 
					    for (const child of this._childFrames) {
 | 
				
			||||||
      child._recalculateLifecycle(frameThatAllowsRemovingLifecycleEvents);
 | 
					      child._recalculateNetworkIdle(frameThatAllowsRemovingNetworkIdle);
 | 
				
			||||||
      // We require a particular lifecycle event to be fired in the whole
 | 
					      // We require networkidle event to be fired in the whole frame subtree, and then consider it done.
 | 
				
			||||||
      // frame subtree, and then consider it done.
 | 
					      if (!child._firedLifecycleEvents.has('networkidle'))
 | 
				
			||||||
      for (const event of events) {
 | 
					        isNetworkIdle = false;
 | 
				
			||||||
        if (!child._subtreeLifecycleEvents.has(event))
 | 
					 | 
				
			||||||
          events.delete(event);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (isNetworkIdle && !this._firedLifecycleEvents.has('networkidle')) {
 | 
				
			||||||
 | 
					      this._firedLifecycleEvents.add('networkidle');
 | 
				
			||||||
 | 
					      this.emit(Frame.Events.AddLifecycle, 'networkidle');
 | 
				
			||||||
 | 
					      if (this === this._page.mainFrame() && this._url !== 'about:blank')
 | 
				
			||||||
 | 
					        debugLogger.log('api', `  "networkidle" event fired`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (frameThatAllowsRemovingLifecycleEvents !== this) {
 | 
					    if (frameThatAllowsRemovingNetworkIdle !== this && this._firedLifecycleEvents.has('networkidle') && !isNetworkIdle) {
 | 
				
			||||||
      // Usually, lifecycle events are fired once and not removed after that, so we keep existing ones.
 | 
					      // Usually, networkidle is fired once and not removed after that.
 | 
				
			||||||
      // However, when we clear them right before a new commit, this is allowed for a particular frame.
 | 
					      // However, when we clear them right before a new commit, this is allowed for a particular frame.
 | 
				
			||||||
      for (const event of this._subtreeLifecycleEvents)
 | 
					      this._firedLifecycleEvents.delete('networkidle');
 | 
				
			||||||
        events.add(event);
 | 
					      this.emit(Frame.Events.RemoveLifecycle, 'networkidle');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const mainFrame = this._page.mainFrame();
 | 
					 | 
				
			||||||
    for (const event of events) {
 | 
					 | 
				
			||||||
      // Checking whether we have already notified about this event.
 | 
					 | 
				
			||||||
      if (!this._subtreeLifecycleEvents.has(event)) {
 | 
					 | 
				
			||||||
        this.emit(Frame.Events.AddLifecycle, event);
 | 
					 | 
				
			||||||
        if (this === mainFrame && this._url !== 'about:blank')
 | 
					 | 
				
			||||||
          debugLogger.log('api', `  "${event}" event fired`);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    for (const event of this._subtreeLifecycleEvents) {
 | 
					 | 
				
			||||||
      if (!events.has(event))
 | 
					 | 
				
			||||||
        this.emit(Frame.Events.RemoveLifecycle, event);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    this._subtreeLifecycleEvents = events;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async raceNavigationAction(progress: Progress, options: types.GotoOptions, action: () => Promise<network.Response | null>): Promise<network.Response | null> {
 | 
					  async raceNavigationAction(progress: Progress, options: types.GotoOptions, action: () => Promise<network.Response | null>): Promise<network.Response | null> {
 | 
				
			||||||
| 
						 | 
					@ -706,7 +693,7 @@ export class Frame extends SdkObject {
 | 
				
			||||||
      event = await sameDocument.promise;
 | 
					      event = await sameDocument.promise;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!this._subtreeLifecycleEvents.has(waitUntil))
 | 
					    if (!this._firedLifecycleEvents.has(waitUntil))
 | 
				
			||||||
      await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
 | 
					      await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const request = event.newDocument ? event.newDocument.request : undefined;
 | 
					    const request = event.newDocument ? event.newDocument.request : undefined;
 | 
				
			||||||
| 
						 | 
					@ -731,7 +718,7 @@ export class Frame extends SdkObject {
 | 
				
			||||||
    if (navigationEvent.error)
 | 
					    if (navigationEvent.error)
 | 
				
			||||||
      throw navigationEvent.error;
 | 
					      throw navigationEvent.error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!this._subtreeLifecycleEvents.has(waitUntil))
 | 
					    if (!this._firedLifecycleEvents.has(waitUntil))
 | 
				
			||||||
      await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
 | 
					      await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const request = navigationEvent.newDocument ? navigationEvent.newDocument.request : undefined;
 | 
					    const request = navigationEvent.newDocument ? navigationEvent.newDocument.request : undefined;
 | 
				
			||||||
| 
						 | 
					@ -740,7 +727,7 @@ export class Frame extends SdkObject {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async _waitForLoadState(progress: Progress, state: types.LifecycleEvent): Promise<void> {
 | 
					  async _waitForLoadState(progress: Progress, state: types.LifecycleEvent): Promise<void> {
 | 
				
			||||||
    const waitUntil = verifyLifecycle('state', state);
 | 
					    const waitUntil = verifyLifecycle('state', state);
 | 
				
			||||||
    if (!this._subtreeLifecycleEvents.has(waitUntil))
 | 
					    if (!this._firedLifecycleEvents.has(waitUntil))
 | 
				
			||||||
      await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
 | 
					      await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1629,7 +1616,10 @@ export class Frame extends SdkObject {
 | 
				
			||||||
    // after the frame was detached - probably a race in the Firefox itself.
 | 
					    // after the frame was detached - probably a race in the Firefox itself.
 | 
				
			||||||
    if (this._firedLifecycleEvents.has('networkidle') || this._detached)
 | 
					    if (this._firedLifecycleEvents.has('networkidle') || this._detached)
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    this._networkIdleTimer = setTimeout(() => this._onLifecycleEvent('networkidle'), 500);
 | 
					    this._networkIdleTimer = setTimeout(() => {
 | 
				
			||||||
 | 
					      this._firedNetworkIdleSelf = true;
 | 
				
			||||||
 | 
					      this._page.mainFrame()._recalculateNetworkIdle();
 | 
				
			||||||
 | 
					    }, 500);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _stopNetworkIdleTimer() {
 | 
					  _stopNetworkIdleTimer() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -380,9 +380,8 @@ export class WKPage implements PageDelegate {
 | 
				
			||||||
      eventsHelper.addEventListener(this._session, 'Page.willCheckNavigationPolicy', event => this._onWillCheckNavigationPolicy(event.frameId)),
 | 
					      eventsHelper.addEventListener(this._session, 'Page.willCheckNavigationPolicy', event => this._onWillCheckNavigationPolicy(event.frameId)),
 | 
				
			||||||
      eventsHelper.addEventListener(this._session, 'Page.didCheckNavigationPolicy', event => this._onDidCheckNavigationPolicy(event.frameId, event.cancel)),
 | 
					      eventsHelper.addEventListener(this._session, 'Page.didCheckNavigationPolicy', event => this._onDidCheckNavigationPolicy(event.frameId, event.cancel)),
 | 
				
			||||||
      eventsHelper.addEventListener(this._session, 'Page.frameScheduledNavigation', event => this._onFrameScheduledNavigation(event.frameId)),
 | 
					      eventsHelper.addEventListener(this._session, 'Page.frameScheduledNavigation', event => this._onFrameScheduledNavigation(event.frameId)),
 | 
				
			||||||
      eventsHelper.addEventListener(this._session, 'Page.frameStoppedLoading', event => this._onFrameStoppedLoading(event.frameId)),
 | 
					      eventsHelper.addEventListener(this._session, 'Page.loadEventFired', event => this._page._frameManager.frameLifecycleEvent(event.frameId, 'load')),
 | 
				
			||||||
      eventsHelper.addEventListener(this._session, 'Page.loadEventFired', event => this._onLifecycleEvent(event.frameId, 'load')),
 | 
					      eventsHelper.addEventListener(this._session, 'Page.domContentEventFired', event => this._page._frameManager.frameLifecycleEvent(event.frameId, 'domcontentloaded')),
 | 
				
			||||||
      eventsHelper.addEventListener(this._session, 'Page.domContentEventFired', event => this._onLifecycleEvent(event.frameId, 'domcontentloaded')),
 | 
					 | 
				
			||||||
      eventsHelper.addEventListener(this._session, 'Runtime.executionContextCreated', event => this._onExecutionContextCreated(event.context)),
 | 
					      eventsHelper.addEventListener(this._session, 'Runtime.executionContextCreated', event => this._onExecutionContextCreated(event.context)),
 | 
				
			||||||
      eventsHelper.addEventListener(this._session, 'Runtime.bindingCalled', event => this._onBindingCalled(event.contextId, event.argument)),
 | 
					      eventsHelper.addEventListener(this._session, 'Runtime.bindingCalled', event => this._onBindingCalled(event.contextId, event.argument)),
 | 
				
			||||||
      eventsHelper.addEventListener(this._session, 'Console.messageAdded', event => this._onConsoleMessage(event)),
 | 
					      eventsHelper.addEventListener(this._session, 'Console.messageAdded', event => this._onConsoleMessage(event)),
 | 
				
			||||||
| 
						 | 
					@ -450,14 +449,6 @@ export class WKPage implements PageDelegate {
 | 
				
			||||||
    this._page._frameManager.frameRequestedNavigation(frameId);
 | 
					    this._page._frameManager.frameRequestedNavigation(frameId);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private _onFrameStoppedLoading(frameId: string) {
 | 
					 | 
				
			||||||
    this._page._frameManager.frameStoppedLoading(frameId);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private _onLifecycleEvent(frameId: string, event: types.LifecycleEvent) {
 | 
					 | 
				
			||||||
    this._page._frameManager.frameLifecycleEvent(frameId, event);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private _handleFrameTree(frameTree: Protocol.Page.FrameResourceTree) {
 | 
					  private _handleFrameTree(frameTree: Protocol.Page.FrameResourceTree) {
 | 
				
			||||||
    this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId || null);
 | 
					    this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId || null);
 | 
				
			||||||
    this._onFrameNavigated(frameTree.frame, true);
 | 
					    this._onFrameNavigated(frameTree.frame, true);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,7 +53,7 @@ it('should work with mixed content', async ({ browser, server, httpsServer }) =>
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  const context = await browser.newContext({ ignoreHTTPSErrors: true });
 | 
					  const context = await browser.newContext({ ignoreHTTPSErrors: true });
 | 
				
			||||||
  const page = await context.newPage();
 | 
					  const page = await context.newPage();
 | 
				
			||||||
  await page.goto(httpsServer.PREFIX + '/mixedcontent.html', { waitUntil: 'domcontentloaded' });
 | 
					  await page.goto(httpsServer.PREFIX + '/mixedcontent.html', { waitUntil: 'load' });
 | 
				
			||||||
  expect(page.frames().length).toBe(2);
 | 
					  expect(page.frames().length).toBe(2);
 | 
				
			||||||
  // Make sure blocked iframe has functional execution context
 | 
					  // Make sure blocked iframe has functional execution context
 | 
				
			||||||
  // @see https://github.com/GoogleChrome/puppeteer/issues/2709
 | 
					  // @see https://github.com/GoogleChrome/puppeteer/issues/2709
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -629,9 +629,20 @@ it('should properly wait for load', async ({ page, server, browserName }) => {
 | 
				
			||||||
  ]);
 | 
					  ]);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
it('should properly report window.stop()', async ({ page, server }) => {
 | 
					it('should not resolve goto upon window.stop()', async ({ browserName, page, server }) => {
 | 
				
			||||||
  server.setRoute('/module.js', async (req, res) => void 0);
 | 
					  let response;
 | 
				
			||||||
  await page.goto(server.PREFIX + '/window-stop.html');
 | 
					  server.setRoute('/module.js', (req, res) => {
 | 
				
			||||||
 | 
					    res.writeHead(200, { 'Content-Type': 'text/javascript' });
 | 
				
			||||||
 | 
					    response = res;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  let done = false;
 | 
				
			||||||
 | 
					  page.goto(server.PREFIX + '/window-stop.html').then(() => done = true).catch(() => {});
 | 
				
			||||||
 | 
					  await server.waitForRequest('/module.js');
 | 
				
			||||||
 | 
					  expect(done).toBe(false);
 | 
				
			||||||
 | 
					  await page.waitForTimeout(1000);  // give it some time to erroneously resolve
 | 
				
			||||||
 | 
					  response.end('');
 | 
				
			||||||
 | 
					  await page.waitForTimeout(1000);  // give it more time to erroneously resolve
 | 
				
			||||||
 | 
					  expect(done).toBe(browserName === 'firefox'); // Firefox fires DOMContentLoaded and load events in this case.
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
it('should return from goto if new navigation is started', async ({ page, server, browserName, isAndroid }) => {
 | 
					it('should return from goto if new navigation is started', async ({ page, server, browserName, isAndroid }) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -155,18 +155,18 @@ it('should work with DOM history.back()/history.forward()', async ({ page, serve
 | 
				
			||||||
  expect(page.url()).toBe(server.PREFIX + '/second.html');
 | 
					  expect(page.url()).toBe(server.PREFIX + '/second.html');
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
it('should work when subframe issues window.stop()', async ({ page, server }) => {
 | 
					it('should work when subframe issues window.stop()', async ({ browserName, page, server }) => {
 | 
				
			||||||
  server.setRoute('/frames/style.css', (req, res) => {});
 | 
					  server.setRoute('/frames/style.css', (req, res) => {});
 | 
				
			||||||
  const navigationPromise = page.goto(server.PREFIX + '/frames/one-frame.html');
 | 
					  let done = false;
 | 
				
			||||||
 | 
					  page.goto(server.PREFIX + '/frames/one-frame.html').then(() => done = true).catch(() => {});
 | 
				
			||||||
  const frame = await new Promise<Frame>(f => page.once('frameattached', f));
 | 
					  const frame = await new Promise<Frame>(f => page.once('frameattached', f));
 | 
				
			||||||
  await new Promise<void>(fulfill => page.on('framenavigated', f => {
 | 
					  await new Promise<void>(fulfill => page.on('framenavigated', f => {
 | 
				
			||||||
    if (f === frame)
 | 
					    if (f === frame)
 | 
				
			||||||
      fulfill();
 | 
					      fulfill();
 | 
				
			||||||
  }));
 | 
					  }));
 | 
				
			||||||
  await Promise.all([
 | 
					  await frame.evaluate(() => window.stop());
 | 
				
			||||||
    frame.evaluate(() => window.stop()),
 | 
					  await page.waitForTimeout(2000);  // give it some time to erroneously resolve
 | 
				
			||||||
    navigationPromise
 | 
					  expect(done).toBe(browserName !== 'webkit');  // Chromium and Firefox issue load event in this case.
 | 
				
			||||||
  ]);
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
it('should work with url match', async ({ page, server }) => {
 | 
					it('should work with url match', async ({ page, server }) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue