Cleanup navigator logic

This patch cleans up navigator logic. (This is a result of a pair
programming with @pavelfeldman).
This commit is contained in:
Andrey Lushnikov 2017-07-07 15:43:17 -07:00
parent 090ecfa6b9
commit f7cd0048af
2 changed files with 42 additions and 58 deletions

View File

@ -17,64 +17,43 @@
class Navigator { class Navigator {
/** /**
* @param {!Connection} client * @param {!Connection} client
* @param {string} url
* @param {string=} referrer
* @param {!Object=} options * @param {!Object=} options
*/ */
constructor(client, options = {}) { constructor(client, url, referrer, options = {}) {
this._client = client; this._client = client;
this._minTime = typeof options.minTime === 'number' ? options.minTime : 0; this._url = url;
this._maxTime = typeof options.maxTime === 'number' ? options.maxTime : 30000; this._referrer = referrer;
this._idleTime = typeof options.networkIdleTimeout === 'number' ? options.networkIdleTimeout : 1000; this._maxTime = typeof options['maxTime'] === 'number' ? options['maxTime'] : 30000;
this._idleInflight = typeof options.networkIdleInflight === 'number' ? options.networkIdleInflight : 2; this._idleTime = typeof options['networkIdleTimeout'] === 'number' ? options['networkIdleTimeout'] : 1000;
this._waitFor = typeof options.waitFor === 'string' ? options.waitFor : 'load'; this._idleInflight = typeof options['networkIdleInflight'] === 'number' ? options['networkIdleInflight'] : 2;
this._inflightRequests = 0; this._waitFor = typeof options['waitFor'] === 'string' ? options['waitFor'] : 'load';
console.assert(this._waitFor === 'load' || this._waitFor === 'networkidle', 'Unknown value for options.waitFor: ' + this._waitFor); console.assert(this._waitFor === 'load' || this._waitFor === 'networkidle', 'Unknown value for options.waitFor: ' + this._waitFor);
if (this._waitFor === 'networkidle') {
client.on('Network.requestWillBeSent', event => this._onLoadingStarted(event));
client.on('Network.loadingFinished', event => this._onLoadingCompleted(event));
client.on('Network.loadingFailed', event => this._onLoadingCompleted(event));
client.on('Network.webSocketCreated', event => this._onLoadingStarted(event));
client.on('Network.webSocketClosed', event => this._onLoadingCompleted(event));
}
} }
/** /**
* @param {string} url * @return {!Promise<boolean>}
* @param {string=} referrer
*/ */
async navigate(url, referrer) { async navigate() {
this._requestIds = new Set(); this._init();
this._navigationStartTime = Date.now(); let certificateError = new Promise(fulfill => this._client.once('Security.certificateError', fulfill)).then(() => false);
this._idleReached = false; let networkIdle = new Promise(fulfill => this._networkIdleCallback = fulfill).then(() => true);
let loadEventFired = new Promise(fulfill => this._client.once('Page.loadEventFired', fulfill)).then(() => true);
let navigationComplete; let watchdog = new Promise(fulfill => setTimeout(fulfill, this._maxTime)).then(() => false);
let navigationFailure = new Promise(fulfill => this._client.once('Security.certificateError', fulfill)).then(() => false);
if (this._waitFor === 'load')
navigationComplete = new Promise(fulfill => this._client.once('Page.loadEventFired', fulfill));
else
navigationComplete = new Promise(fulfill => this._navigationLoadCallback = fulfill);
this._inflightRequests = 0;
this._minimumTimer = setTimeout(this._completeNavigation.bind(this, false), this._minTime);
this._maximumTimer = setTimeout(this._completeNavigation.bind(this, true), this._maxTime);
this._idleTimer = setTimeout(this._onIdleReached.bind(this), this._idleTime);
// Await for the command to throw exception in case of illegal arguments. // Await for the command to throw exception in case of illegal arguments.
try { try {
await this._client.send('Page.navigate', {url, referrer}); await this._client.send('Page.navigate', {url: this._url, referrer: this._referrer});
} catch (e) { } catch (e) {
this._cleanup();
return false; return false;
} }
return await Promise.race([navigationComplete.then(() => true), navigationFailure]).then(retVal => { let result = await Promise.race([certificateError, watchdog, this._waitFor === 'load' ? loadEventFired : networkIdle]);
clearTimeout(this._idleTimer); this._cleanup();
clearTimeout(this._minimumTimer); return result;
clearTimeout(this._maximumTimer);
return retVal;
});
} }
/** /**
@ -99,26 +78,31 @@ class Navigator {
--this._inflightRequests; --this._inflightRequests;
if (this._inflightRequests <= this._idleInflight && !this._idleTimer) if (this._inflightRequests <= this._idleInflight && !this._idleTimer)
this._idleTimer = setTimeout(this._onIdleReached.bind(this), this._idleTime); this._idleTimer = setTimeout(this._networkIdleCallback, this._idleTime);
} }
_onIdleReached() { _init() {
this._idleReached = true; this._loadingStartedHandler = this._onLoadingStarted.bind(this);
this._completeNavigation(false); this._loadingCompletedHandler = this._onLoadingCompleted.bind(this);
this._client.on('Network.requestWillBeSent', this._loadingStartedHandler);
this._client.on('Network.loadingFinished', this._loadingCompletedHandler);
this._client.on('Network.loadingFailed', this._loadingCompletedHandler);
this._client.on('Network.webSocketCreated', this._loadingStartedHandler);
this._client.on('Network.webSocketClosed', this._loadingCompletedHandler);
this._inflightRequests = 0;
this._requestIds = new Set();
} }
/** _cleanup() {
* @param {boolean} force this._client.removeListener('Network.requestWillBeSent', this._loadingStartedHandler);
*/ this._client.removeListener('Network.loadingFinished', this._loadingCompletedHandler);
_completeNavigation(force) { this._client.removeListener('Network.loadingFailed', this._loadingCompletedHandler);
if (!this._navigationLoadCallback) this._client.removeListener('Network.webSocketCreated', this._loadingStartedHandler);
return; this._client.removeListener('Network.webSocketClosed', this._loadingCompletedHandler);
const elapsedTime = Date.now() - this._navigationStartTime; clearTimeout(this._idleTimer);
if ((elapsedTime >= this._minTime && this._idleReached) || force) { clearTimeout(this._maximumTimer);
this._navigationLoadCallback();
this._navigationLoadCallback = null;
}
} }
} }

View File

@ -263,7 +263,7 @@ class Page extends EventEmitter {
* @return {!Promise<boolean>} * @return {!Promise<boolean>}
*/ */
navigate(url, options) { navigate(url, options) {
return new Navigator(this._client, options).navigate(url, this._networkManager.httpHeaders().referer); return new Navigator(this._client, url, this._networkManager.httpHeaders().referer, options).navigate();
} }
/** /**