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

View File

@ -263,7 +263,7 @@ class Page extends EventEmitter {
* @return {!Promise<boolean>}
*/
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();
}
/**