diff --git a/lib/FrameManager.js b/lib/FrameManager.js index 7cdc7c2290b..1d32da62531 100644 --- a/lib/FrameManager.js +++ b/lib/FrameManager.js @@ -422,7 +422,7 @@ class WaitTask { constructor(frame, pageScript, timeout) { this._frame = frame; this._pageScript = pageScript; - this._runningTask = null; + this._runCount = 0; frame._waitTasks.add(this); this.promise = new Promise((resolve, reject) => { this._resolve = resolve; @@ -438,30 +438,34 @@ class WaitTask { * @param {!Error} error */ terminate(error) { + this._terminated = true; this._reject(error); this._cleanup(); } - rerun() { - let runningTask = this._frame._evaluateExpression(this._pageScript, true).then(finish.bind(this), finish.bind(this, false)); - this._runningTask = runningTask; - - /** - * @param {boolean} success - * @param {?Error=} error - */ - function finish(success, error) { - if (runningTask !== this._runningTask) - return; - // Ignore timeouts in pageScript - we track timeouts ourselves. - if (!success && !error) - return; - if (error) - this._reject(error); - else - this._resolve(); - this._cleanup(); + async rerun() { + const runCount = ++this._runCount; + let success = false; + let error = null; + try { + success = await this._frame._evaluateExpression(this._pageScript, true); + } catch (e) { + error = e; } + + if (this._terminated || runCount !== this._runCount) + return; + + // Ignore timeouts in pageScript - we track timeouts ourselves. + if (!success && !error) + return; + + if (error) + this._reject(error); + else + this._resolve(); + + this._cleanup(); } _cleanup() { @@ -477,30 +481,28 @@ class WaitTask { * @param {number} timeout * @return {!Promise} */ -function waitForSelectorPageFunction(selector, visible, timeout) { - const resultPromise = visible ? waitForVisible(selector) : waitInDOM(selector); - const timeoutPromise = new Promise(fulfill => setTimeout(fulfill, timeout)); - return Promise.race([ - resultPromise.then(() => true), - timeoutPromise.then(() => false) - ]); +async function waitForSelectorPageFunction(selector, visible, timeout) { + let timedOut = false; + setTimeout(() => timedOut = true, timeout); + await waitForDOM(); + await waitForVisible(); + return !timedOut; /** - * @param {string} selector * @return {!Promise} */ - function waitInDOM(selector) { + function waitForDOM() { let node = document.querySelector(selector); if (node) - return Promise.resolve(node); + return Promise.resolve(); let fulfill; const result = new Promise(x => fulfill = x); const observer = new MutationObserver(mutations => { const node = document.querySelector(selector); - if (node) { + if (node || timedOut) { observer.disconnect(); - fulfill(node); + fulfill(); } }); observer.observe(document, { @@ -511,23 +513,26 @@ function waitForSelectorPageFunction(selector, visible, timeout) { } /** - * @param {string} selector * @return {!Promise} */ - async function waitForVisible(selector) { + function waitForVisible() { let fulfill; const result = new Promise(x => fulfill = x); onRaf(); return result; - async function onRaf() { - const node = await waitInDOM(selector); - const style = window.getComputedStyle(node); - if (style.display === 'none' || style.visibility === 'hidden') { + function onRaf() { + if (timedOut) { + fulfill(); + return; + } + const node = document.querySelector(selector); + const style = node ? window.getComputedStyle(node) : null; + if (!style || style.display === 'none' || style.visibility === 'hidden') { requestAnimationFrame(onRaf); return; } - fulfill(node); + fulfill(); } } }