Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const { setTimeout } = ChromeUtils.importESModule(
);
const {
DEFAULT_UNLOAD_TIMEOUT,
getUnloadTimeoutMultiplier,
ProgressListener,
waitForInitialNavigationCompleted,
} = ChromeUtils.importESModule(
);
const CURRENT_URI = Services.io.newURI("http://foo.bar/");
const INITIAL_URI = Services.io.newURI("about:blank");
const TARGET_URI = Services.io.newURI("http://foo.cheese/");
const TARGET_URI_IS_ERROR_PAGE = Services.io.newURI("doesnotexist://");
const TARGET_URI_WITH_HASH = Services.io.newURI("http://foo.cheese/#foo");
function wait(time) {
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
return new Promise(resolve => setTimeout(resolve, time));
}
class MockRequest {
constructor(uri) {
this.originalURI = uri;
}
get QueryInterface() {
return ChromeUtils.generateQI(["nsIRequest", "nsIChannel"]);
}
}
class MockWebProgress {
constructor(browsingContext) {
this.browsingContext = browsingContext;
this.documentRequest = null;
this.isLoadingDocument = false;
this.listener = null;
this.progressListenerRemoved = false;
}
addProgressListener(listener) {
if (this.listener) {
throw new Error("Cannot register listener twice");
}
this.listener = listener;
}
removeProgressListener(listener) {
if (listener === this.listener) {
this.listener = null;
this.progressListenerRemoved = true;
} else {
throw new Error("Unknown listener");
}
}
sendLocationChange(options = {}) {
const { flag = 0 } = options;
this.documentRequest = null;
if (flag & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
this.browsingContext.currentURI = TARGET_URI_WITH_HASH;
} else if (flag & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) {
this.browsingContext.currentURI = TARGET_URI_IS_ERROR_PAGE;
}
this.listener?.onLocationChange(
this,
this.documentRequest,
TARGET_URI_WITH_HASH,
flag
);
return new Promise(executeSoon);
}
sendStartState(options = {}) {
const { coop = false, isInitial = false } = options;
if (coop) {
this.browsingContext = new MockTopContext(this);
}
if (!this.browsingContext.currentWindowGlobal) {
this.browsingContext.currentWindowGlobal = {};
}
this.browsingContext.currentWindowGlobal.isInitialDocument = isInitial;
this.isLoadingDocument = true;
const uri = isInitial ? INITIAL_URI : TARGET_URI;
this.documentRequest = new MockRequest(uri);
this.listener?.onStateChange(
this,
this.documentRequest,
Ci.nsIWebProgressListener.STATE_START,
null
);
return new Promise(executeSoon);
}
sendStopState(options = {}) {
const { errorFlag = 0 } = options;
this.browsingContext.currentURI = this.documentRequest.originalURI;
this.isLoadingDocument = false;
this.documentRequest = null;
this.listener?.onStateChange(
this,
this.documentRequest,
Ci.nsIWebProgressListener.STATE_STOP,
errorFlag
);
return new Promise(executeSoon);
}
}
class MockTopContext {
constructor(webProgress = null) {
this.currentURI = CURRENT_URI;
this.currentWindowGlobal = { isInitialDocument: true };
this.id = 7;
this.top = this;
this.webProgress = webProgress || new MockWebProgress(this);
}
}
const hasPromiseResolved = async function (promise) {
let resolved = false;
promise.finally(() => (resolved = true));
// Make sure microtasks have time to run.
await new Promise(resolve => Services.tm.dispatchToMainThread(resolve));
return resolved;
};
const hasPromiseRejected = async function (promise) {
let rejected = false;
promise.catch(() => (rejected = true));
// Make sure microtasks have time to run.
await new Promise(resolve => Services.tm.dispatchToMainThread(resolve));
return rejected;
};
add_task(
async function test_waitForInitialNavigation_initialDocumentNoWindowGlobal() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
// In some cases there might be no window global yet.
delete browsingContext.currentWindowGlobal;
ok(!webProgress.isLoadingDocument, "Document is not loading");
const navigated = waitForInitialNavigationCompleted(webProgress);
await webProgress.sendStartState({ isInitial: true });
ok(
!(await hasPromiseResolved(navigated)),
"waitForInitialNavigationCompleted has not resolved yet"
);
await webProgress.sendStopState();
const { currentURI, targetURI } = await navigated;
ok(!webProgress.isLoadingDocument, "Document is not loading");
ok(
webProgress.browsingContext.currentWindowGlobal.isInitialDocument,
"Is initial document"
);
equal(
currentURI.spec,
INITIAL_URI.spec,
"Expected current URI has been set"
);
equal(targetURI.spec, INITIAL_URI.spec, "Expected target URI has been set");
}
);
add_task(
async function test_waitForInitialNavigation_initialDocumentNotLoaded() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
ok(!webProgress.isLoadingDocument, "Document is not loading");
const navigated = waitForInitialNavigationCompleted(webProgress);
await webProgress.sendStartState({ isInitial: true });
ok(
!(await hasPromiseResolved(navigated)),
"waitForInitialNavigationCompleted has not resolved yet"
);
await webProgress.sendStopState();
const { currentURI, targetURI } = await navigated;
ok(!webProgress.isLoadingDocument, "Document is not loading");
ok(
webProgress.browsingContext.currentWindowGlobal.isInitialDocument,
"Is initial document"
);
equal(
currentURI.spec,
INITIAL_URI.spec,
"Expected current URI has been set"
);
equal(targetURI.spec, INITIAL_URI.spec, "Expected target URI has been set");
}
);
add_task(
async function test_waitForInitialNavigation_initialDocumentLoadingAndNoAdditionalLoad() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
await webProgress.sendStartState({ isInitial: true });
ok(webProgress.isLoadingDocument, "Document is loading");
const navigated = waitForInitialNavigationCompleted(webProgress);
ok(
!(await hasPromiseResolved(navigated)),
"waitForInitialNavigationCompleted has not resolved yet"
);
await webProgress.sendStopState();
const { currentURI, targetURI } = await navigated;
ok(!webProgress.isLoadingDocument, "Document is not loading");
ok(
webProgress.browsingContext.currentWindowGlobal.isInitialDocument,
"Is initial document"
);
equal(
currentURI.spec,
INITIAL_URI.spec,
"Expected current URI has been set"
);
equal(targetURI.spec, INITIAL_URI.spec, "Expected target URI has been set");
}
);
add_task(
async function test_waitForInitialNavigation_initialDocumentFinishedLoadingNoAdditionalLoad() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
await webProgress.sendStartState({ isInitial: true });
await webProgress.sendStopState();
ok(!webProgress.isLoadingDocument, "Document is not loading");
const navigated = waitForInitialNavigationCompleted(webProgress);
ok(
!(await hasPromiseResolved(navigated)),
"waitForInitialNavigationCompleted has not resolved yet"
);
const { currentURI, targetURI } = await navigated;
ok(!webProgress.isLoadingDocument, "Document is not loading");
ok(
webProgress.browsingContext.currentWindowGlobal.isInitialDocument,
"Is initial document"
);
equal(
currentURI.spec,
INITIAL_URI.spec,
"Expected current URI has been set"
);
equal(targetURI.spec, INITIAL_URI.spec, "Expected target URI has been set");
}
);
add_task(
async function test_waitForInitialNavigation_initialDocumentLoadingAndAdditionalLoad() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
await webProgress.sendStartState({ isInitial: true });
ok(webProgress.isLoadingDocument, "Document is loading");
const navigated = waitForInitialNavigationCompleted(webProgress);
ok(
!(await hasPromiseResolved(navigated)),
"waitForInitialNavigationCompleted has not resolved yet"
);
await webProgress.sendStopState();
await wait(100);
await webProgress.sendStartState({ isInitial: false });
await webProgress.sendStopState();
const { currentURI, targetURI } = await navigated;
ok(!webProgress.isLoadingDocument, "Document is not loading");
ok(
!webProgress.browsingContext.currentWindowGlobal.isInitialDocument,
"Is not initial document"
);
equal(
currentURI.spec,
TARGET_URI.spec,
"Expected current URI has been set"
);
equal(targetURI.spec, TARGET_URI.spec, "Expected target URI has been set");
}
);
add_task(
async function test_waitForInitialNavigation_initialDocumentFinishedLoadingAndAdditionalLoad() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
await webProgress.sendStartState({ isInitial: true });
await webProgress.sendStopState();
ok(!webProgress.isLoadingDocument, "Document is not loading");
const navigated = waitForInitialNavigationCompleted(webProgress);
ok(
!(await hasPromiseResolved(navigated)),
"waitForInitialNavigationCompleted has not resolved yet"
);
await wait(100);
await webProgress.sendStartState({ isInitial: false });
await webProgress.sendStopState();
const { currentURI, targetURI } = await navigated;
ok(!webProgress.isLoadingDocument, "Document is not loading");
ok(
!webProgress.browsingContext.currentWindowGlobal.isInitialDocument,
"Is not initial document"
);
equal(
currentURI.spec,
TARGET_URI.spec,
"Expected current URI has been set"
);
equal(targetURI.spec, TARGET_URI.spec, "Expected target URI has been set");
}
);
add_task(
async function test_waitForInitialNavigation_notInitialDocumentNotLoading() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
ok(!webProgress.isLoadingDocument, "Document is not loading");
const navigated = waitForInitialNavigationCompleted(webProgress);
await webProgress.sendStartState({ isInitial: false });
ok(
!(await hasPromiseResolved(navigated)),
"waitForInitialNavigationCompleted has not resolved yet"
);
await webProgress.sendStopState();
const { currentURI, targetURI } = await navigated;
ok(!webProgress.isLoadingDocument, "Document is not loading");
ok(
!browsingContext.currentWindowGlobal.isInitialDocument,
"Is not initial document"
);
equal(
currentURI.spec,
TARGET_URI.spec,
"Expected current URI has been set"
);
equal(targetURI.spec, TARGET_URI.spec, "Expected target URI has been set");
}
);
add_task(
async function test_waitForInitialNavigation_notInitialDocumentAlreadyLoading() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
await webProgress.sendStartState({ isInitial: false });
ok(webProgress.isLoadingDocument, "Document is loading");
const navigated = waitForInitialNavigationCompleted(webProgress);
ok(
!(await hasPromiseResolved(navigated)),
"waitForInitialNavigationCompleted has not resolved yet"
);
await webProgress.sendStopState();
const { currentURI, targetURI } = await navigated;
ok(!webProgress.isLoadingDocument, "Document is not loading");
ok(
!browsingContext.currentWindowGlobal.isInitialDocument,
"Is not initial document"
);
equal(
currentURI.spec,
TARGET_URI.spec,
"Expected current URI has been set"
);
equal(targetURI.spec, TARGET_URI.spec, "Expected target URI has been set");
}
);
add_task(
async function test_waitForInitialNavigation_notInitialDocumentFinishedLoading() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
await webProgress.sendStartState({ isInitial: false });
await webProgress.sendStopState();
ok(!webProgress.isLoadingDocument, "Document is not loading");
const { currentURI, targetURI } = await waitForInitialNavigationCompleted(
webProgress
);
ok(!webProgress.isLoadingDocument, "Document is not loading");
ok(
!webProgress.browsingContext.currentWindowGlobal.isInitialDocument,
"Is not initial document"
);
equal(
currentURI.spec,
TARGET_URI.spec,
"Expected current URI has been set"
);
equal(targetURI.spec, TARGET_URI.spec, "Expected target URI has been set");
}
);
add_task(async function test_waitForInitialNavigation_resolveWhenStarted() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
await webProgress.sendStartState({ isInitial: true });
ok(webProgress.isLoadingDocument, "Document is already loading");
const { currentURI, targetURI } = await waitForInitialNavigationCompleted(
webProgress,
{
resolveWhenStarted: true,
}
);
ok(webProgress.isLoadingDocument, "Document is still loading");
ok(
webProgress.browsingContext.currentWindowGlobal.isInitialDocument,
"Is initial document"
);
equal(currentURI.spec, CURRENT_URI.spec, "Expected current URI has been set");
equal(targetURI.spec, INITIAL_URI.spec, "Expected target URI has been set");
});
add_task(async function test_waitForInitialNavigation_crossOrigin() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
ok(!webProgress.isLoadingDocument, "Document is not loading");
const navigated = waitForInitialNavigationCompleted(webProgress);
await webProgress.sendStartState({ coop: true });
ok(
!(await hasPromiseResolved(navigated)),
"waitForInitialNavigationCompleted has not resolved yet"
);
await webProgress.sendStopState();
const { currentURI, targetURI } = await navigated;
notEqual(
browsingContext,
webProgress.browsingContext,
"Got new browsing context"
);
ok(!webProgress.isLoadingDocument, "Document is not loading");
ok(
!webProgress.browsingContext.currentWindowGlobal.isInitialDocument,
"Is not initial document"
);
equal(currentURI.spec, TARGET_URI.spec, "Expected current URI has been set");
equal(targetURI.spec, TARGET_URI.spec, "Expected target URI has been set");
});
add_task(async function test_waitForInitialNavigation_unloadTimeout_default() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
// Stop the navigation on an initial page which is not loading anymore.
// This situation happens with new tabs on Android, even though they are on
// the initial document, they will not start another navigation on their own.
await webProgress.sendStartState({ isInitial: true });
await webProgress.sendStopState();
ok(!webProgress.isLoadingDocument, "Document is not loading");
const navigated = waitForInitialNavigationCompleted(webProgress);
// Start a timer longer than the timeout which will be used by
// waitForInitialNavigationCompleted, and check that navigated resolves first.
const waitForMoreThanDefaultTimeout = wait(
DEFAULT_UNLOAD_TIMEOUT * 1.5 * getUnloadTimeoutMultiplier()
);
await Promise.race([navigated, waitForMoreThanDefaultTimeout]);
ok(
await hasPromiseResolved(navigated),
"waitForInitialNavigationCompleted has resolved"
);
ok(!webProgress.isLoadingDocument, "Document is not loading");
ok(
webProgress.browsingContext.currentWindowGlobal.isInitialDocument,
"Document is still on the initial document"
);
});
add_task(async function test_waitForInitialNavigation_unloadTimeout_longer() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
// Stop the navigation on an initial page which is not loading anymore.
// This situation happens with new tabs on Android, even though they are on
// the initial document, they will not start another navigation on their own.
await webProgress.sendStartState({ isInitial: true });
await webProgress.sendStopState();
ok(!webProgress.isLoadingDocument, "Document is not loading");
const navigated = waitForInitialNavigationCompleted(webProgress, {
unloadTimeout: DEFAULT_UNLOAD_TIMEOUT * 3,
});
// Start a timer longer than the default timeout of the Navigate module.
// However here we used a custom timeout, so we expect that the navigation
// will not be done yet by the time this timer is done.
const waitForMoreThanDefaultTimeout = wait(
DEFAULT_UNLOAD_TIMEOUT * 1.5 * getUnloadTimeoutMultiplier()
);
await Promise.race([navigated, waitForMoreThanDefaultTimeout]);
// The promise should not have resolved because we didn't reached the custom
// timeout which is 3 times the default one.
ok(
!(await hasPromiseResolved(navigated)),
"waitForInitialNavigationCompleted has not resolved yet"
);
// The navigation should eventually resolve once we reach the custom timeout.
await navigated;
ok(!webProgress.isLoadingDocument, "Document is not loading");
ok(
webProgress.browsingContext.currentWindowGlobal.isInitialDocument,
"Document is still on the initial document"
);
});
add_task(async function test_ProgressListener_expectNavigation() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
const progressListener = new ProgressListener(webProgress, {
expectNavigation: true,
unloadTimeout: 10,
});
const navigated = progressListener.start();
// Wait for unloadTimeout to finish in case it started
await wait(30);
ok(!(await hasPromiseResolved(navigated)), "Listener has not resolved yet");
await webProgress.sendStartState();
await webProgress.sendStopState();
ok(await hasPromiseResolved(navigated), "Listener has resolved");
});
add_task(
async function test_ProgressListener_expectNavigation_initialDocumentFinishedLoading() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
const progressListener = new ProgressListener(webProgress, {
expectNavigation: true,
unloadTimeout: 10,
});
const navigated = progressListener.start();
ok(!(await hasPromiseResolved(navigated)), "Listener has not resolved yet");
await webProgress.sendStartState({ isInitial: true });
await webProgress.sendStopState();
// Wait for unloadTimeout to finish in case it started
await wait(30);
ok(!(await hasPromiseResolved(navigated)), "Listener has not resolved yet");
await webProgress.sendStartState();
await webProgress.sendStopState();
ok(await hasPromiseResolved(navigated), "Listener has resolved");
}
);
add_task(async function test_ProgressListener_isStarted() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
const progressListener = new ProgressListener(webProgress);
ok(!progressListener.isStarted);
progressListener.start();
ok(progressListener.isStarted);
progressListener.stop();
ok(!progressListener.isStarted);
});
add_task(async function test_ProgressListener_notWaitForExplicitStart() {
// Create a webprogress and start it before creating the progress listener.
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
await webProgress.sendStartState();
// Create the progress listener for a webprogress already in a navigation.
const progressListener = new ProgressListener(webProgress, {
waitForExplicitStart: false,
});
const navigated = progressListener.start();
// Send stop state to complete the initial navigation
await webProgress.sendStopState();
ok(
await hasPromiseResolved(navigated),
"Listener has resolved after initial navigation"
);
});
add_task(async function test_ProgressListener_waitForExplicitStart() {
// Create a webprogress and start it before creating the progress listener.
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
await webProgress.sendStartState();
// Create the progress listener for a webprogress already in a navigation.
const progressListener = new ProgressListener(webProgress, {
waitForExplicitStart: true,
});
const navigated = progressListener.start();
// Send stop state to complete the initial navigation
await webProgress.sendStopState();
ok(
!(await hasPromiseResolved(navigated)),
"Listener has not resolved after initial navigation"
);
// Start a new navigation
await webProgress.sendStartState();
ok(
!(await hasPromiseResolved(navigated)),
"Listener has not resolved after starting new navigation"
);
// Finish the new navigation
await webProgress.sendStopState();
ok(
await hasPromiseResolved(navigated),
"Listener resolved after finishing the new navigation"
);
});
add_task(
async function test_ProgressListener_waitForExplicitStartAndResolveWhenStarted() {
// Create a webprogress and start it before creating the progress listener.
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
await webProgress.sendStartState();
// Create the progress listener for a webprogress already in a navigation.
const progressListener = new ProgressListener(webProgress, {
resolveWhenStarted: true,
waitForExplicitStart: true,
});
const navigated = progressListener.start();
// Send stop state to complete the initial navigation
await webProgress.sendStopState();
ok(
!(await hasPromiseResolved(navigated)),
"Listener has not resolved after initial navigation"
);
// Start a new navigation
await webProgress.sendStartState();
ok(
await hasPromiseResolved(navigated),
"Listener resolved after starting the new navigation"
);
}
);
add_task(
async function test_ProgressListener_resolveWhenNavigatingInsideDocument() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
const progressListener = new ProgressListener(webProgress);
const navigated = progressListener.start();
ok(!(await hasPromiseResolved(navigated)), "Listener has not resolved");
// Send hash change location change notification to complete the navigation
await webProgress.sendLocationChange({
flag: Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT,
});
ok(await hasPromiseResolved(navigated), "Listener has resolved");
const { currentURI, targetURI } = progressListener;
equal(
currentURI.spec,
TARGET_URI_WITH_HASH.spec,
"Expected current URI has been set"
);
equal(
targetURI.spec,
TARGET_URI_WITH_HASH.spec,
"Expected target URI has been set"
);
}
);
add_task(async function test_ProgressListener_ignoreCacheError() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
const progressListener = new ProgressListener(webProgress);
const navigated = progressListener.start();
ok(!(await hasPromiseResolved(navigated)), "Listener has not resolved");
await webProgress.sendStartState();
await webProgress.sendStopState({
errorFlag: Cr.NS_ERROR_PARSED_DATA_CACHED,
});
ok(await hasPromiseResolved(navigated), "Listener has resolved");
});
add_task(async function test_ProgressListener_navigationRejectedOnErrorPage() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
const progressListener = new ProgressListener(webProgress, {
waitForExplicitStart: false,
});
const navigated = progressListener.start();
await webProgress.sendStartState();
await webProgress.sendLocationChange({
flag:
Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT |
Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE,
});
ok(
await hasPromiseRejected(navigated),
"Listener has rejected in location change for error page"
);
});
add_task(async function test_ProgressListener_navigationRejectedOnStopState() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
const progressListener = new ProgressListener(webProgress, {
waitForExplicitStart: false,
});
const navigated = progressListener.start();
await webProgress.sendStartState();
await webProgress.sendStopState({ errorFlag: Cr.NS_BINDING_ABORTED });
ok(
await hasPromiseRejected(navigated),
"Listener has rejected in stop state for erroneous navigation"
);
});
add_task(async function test_ProgressListener_stopIfStarted() {
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
const progressListener = new ProgressListener(webProgress);
const navigated = progressListener.start();
progressListener.stopIfStarted();
ok(!(await hasPromiseResolved(navigated)), "Listener has not resolved");
await webProgress.sendStartState();
progressListener.stopIfStarted();
ok(await hasPromiseResolved(navigated), "Listener has resolved");
});
add_task(async function test_ProgressListener_stopIfStarted_alreadyStarted() {
// Create an already navigating browsing context.
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
await webProgress.sendStartState();
// Create a progress listener which accepts already ongoing navigations.
const progressListener = new ProgressListener(webProgress, {
waitForExplicitStart: false,
});
const navigated = progressListener.start();
// stopIfStarted should stop the listener because of the ongoing navigation.
progressListener.stopIfStarted();
ok(await hasPromiseResolved(navigated), "Listener has resolved");
});
add_task(
async function test_ProgressListener_stopIfStarted_alreadyStarted_waitForExplicitStart() {
// Create an already navigating browsing context.
const browsingContext = new MockTopContext();
const webProgress = browsingContext.webProgress;
await webProgress.sendStartState();
// Create a progress listener which rejects already ongoing navigations.
const progressListener = new ProgressListener(webProgress, {
waitForExplicitStart: true,
});
const navigated = progressListener.start();
// stopIfStarted will not stop the listener for the existing navigation.
progressListener.stopIfStarted();
ok(!(await hasPromiseResolved(navigated)), "Listener has not resolved");
// stopIfStarted will stop the listener when called after starting a new
// navigation.
await webProgress.sendStartState();
progressListener.stopIfStarted();
ok(await hasPromiseResolved(navigated), "Listener has resolved");
}
);