UNPKG

4.16 kBJavaScriptView Raw
1/**
2 * Copyright 2017 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17const {helper} = require('./helper');
18const {FrameManager} = require('./FrameManager');
19
20class NavigatorWatcher {
21 /**
22 * @param {!FrameManager} frameManager
23 * @param {!Puppeteer.Frame} frame
24 * @param {number} timeout
25 * @param {!Object=} options
26 */
27 constructor(frameManager, frame, timeout, options = {}) {
28 console.assert(options.networkIdleTimeout === undefined, 'ERROR: networkIdleTimeout option is no longer supported.');
29 console.assert(options.networkIdleInflight === undefined, 'ERROR: networkIdleInflight option is no longer supported.');
30 console.assert(options.waitUntil !== 'networkidle', 'ERROR: "networkidle" option is no longer supported. Use "networkidle2" instead');
31 let waitUntil = ['load'];
32 if (Array.isArray(options.waitUntil))
33 waitUntil = options.waitUntil.slice();
34 else if (typeof options.waitUntil === 'string')
35 waitUntil = [options.waitUntil];
36 this._expectedLifecycle = waitUntil.map(value => {
37 const protocolEvent = puppeteerToProtocolLifecycle[value];
38 console.assert(protocolEvent, 'Unknown value for options.waitUntil: ' + value);
39 return protocolEvent;
40 });
41
42 this._frameManager = frameManager;
43 this._frame = frame;
44 this._initialLoaderId = frame._loaderId;
45 this._timeout = timeout;
46 this._eventListeners = [
47 helper.addEventListener(this._frameManager, FrameManager.Events.LifecycleEvent, this._checkLifecycleComplete.bind(this)),
48 helper.addEventListener(this._frameManager, FrameManager.Events.FrameDetached, this._checkLifecycleComplete.bind(this))
49 ];
50
51 const lifecycleCompletePromise = new Promise(fulfill => {
52 this._lifecycleCompleteCallback = fulfill;
53 });
54 this._navigationPromise = Promise.race([
55 this._createTimeoutPromise(),
56 lifecycleCompletePromise
57 ]).then(error => {
58 this._cleanup();
59 return error;
60 });
61 }
62
63 /**
64 * @return {!Promise<?Error>}
65 */
66 _createTimeoutPromise() {
67 if (!this._timeout)
68 return new Promise(() => {});
69 const errorMessage = 'Navigation Timeout Exceeded: ' + this._timeout + 'ms exceeded';
70 return new Promise(fulfill => this._maximumTimer = setTimeout(fulfill, this._timeout))
71 .then(() => new Error(errorMessage));
72 }
73
74 /**
75 * @return {!Promise<?Error>}
76 */
77 async navigationPromise() {
78 return this._navigationPromise;
79 }
80
81 _checkLifecycleComplete() {
82 // We expect navigation to commit.
83 if (this._frame._loaderId === this._initialLoaderId)
84 return;
85 if (!checkLifecycle(this._frame, this._expectedLifecycle))
86 return;
87 this._lifecycleCompleteCallback();
88
89 /**
90 * @param {!Puppeteer.Frame} frame
91 * @param {!Array<string>} expectedLifecycle
92 * @return {boolean}
93 */
94 function checkLifecycle(frame, expectedLifecycle) {
95 for (const event of expectedLifecycle) {
96 if (!frame._lifecycleEvents.has(event))
97 return false;
98 }
99 for (const child of frame.childFrames()) {
100 if (!checkLifecycle(child, expectedLifecycle))
101 return false;
102 }
103 return true;
104 }
105 }
106
107 cancel() {
108 this._cleanup();
109 }
110
111 _cleanup() {
112 helper.removeEventListeners(this._eventListeners);
113 this._lifecycleCompleteCallback(new Error('Navigation failed'));
114 clearTimeout(this._maximumTimer);
115 }
116}
117
118const puppeteerToProtocolLifecycle = {
119 'load': 'load',
120 'domcontentloaded': 'DOMContentLoaded',
121 'networkidle0': 'networkIdle',
122 'networkidle2': 'networkAlmostIdle',
123};
124
125module.exports = NavigatorWatcher;