Bug 1763134 - Stop navigation with "wait" equal "none" when beforeunload prompt is open. r=webdriver-reviewers,jdescottes

Differential Revision: https://phabricator.services.mozilla.com/D218331
This commit is contained in:
Alexandra Borovova 2024-08-05 16:16:00 +00:00
parent 305e77adde
commit 3ce23807a4
3 changed files with 50 additions and 5 deletions

View File

@ -12,6 +12,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
Deferred: "chrome://remote/content/shared/Sync.sys.mjs", Deferred: "chrome://remote/content/shared/Sync.sys.mjs",
Log: "chrome://remote/content/shared/Log.sys.mjs", Log: "chrome://remote/content/shared/Log.sys.mjs",
PromptListener:
"chrome://remote/content/shared/listeners/PromptListener.sys.mjs",
truncate: "chrome://remote/content/shared/Format.sys.mjs", truncate: "chrome://remote/content/shared/Format.sys.mjs",
}); });
@ -140,6 +142,7 @@ export class ProgressListener {
#deferredNavigation; #deferredNavigation;
#errorName; #errorName;
#promptListener;
#seenStartFlag; #seenStartFlag;
#targetURI; #targetURI;
#unloadTimerId; #unloadTimerId;
@ -159,6 +162,8 @@ export class ProgressListener {
* Flag to indicate that the Promise has to be resolved when the * Flag to indicate that the Promise has to be resolved when the
* page load has been started. Otherwise wait until the page has * page load has been started. Otherwise wait until the page has
* finished loading. Defaults to `false`. * finished loading. Defaults to `false`.
* @param {string=} options.targetURI
* The target URI for the navigation.
* @param {number=} options.unloadTimeout * @param {number=} options.unloadTimeout
* Time to allow before the page gets unloaded. Defaults to 200ms on * Time to allow before the page gets unloaded. Defaults to 200ms on
* regular platforms. A multiplier will be applied on slower platforms * regular platforms. A multiplier will be applied on slower platforms
@ -174,6 +179,7 @@ export class ProgressListener {
const { const {
expectNavigation = false, expectNavigation = false,
resolveWhenStarted = false, resolveWhenStarted = false,
targetURI,
unloadTimeout = DEFAULT_UNLOAD_TIMEOUT, unloadTimeout = DEFAULT_UNLOAD_TIMEOUT,
waitForExplicitStart = false, waitForExplicitStart = false,
} = options; } = options;
@ -187,8 +193,18 @@ export class ProgressListener {
this.#deferredNavigation = null; this.#deferredNavigation = null;
this.#errorName = null; this.#errorName = null;
this.#seenStartFlag = false; this.#seenStartFlag = false;
this.#targetURI = null; this.#targetURI = targetURI;
this.#unloadTimerId = null; this.#unloadTimerId = null;
this.#promptListener = new lazy.PromptListener();
this.#promptListener.on("opened", this.#onPromptOpened);
this.#promptListener.startListening();
}
destroy() {
this.#promptListener.stopListening();
this.#promptListener.off("opened", this.#onPromptOpened);
this.#promptListener.destroy();
} }
get #messagePrefix() { get #messagePrefix() {
@ -322,6 +338,31 @@ export class ProgressListener {
return null; return null;
} }
#onPromptOpened = (eventName, data) => {
const { prompt, contentBrowser } = data;
const { promptType } = prompt;
this.#trace(`A prompt of type=${promptType} is open`);
// Prompt open events come for top level context,
// that's why in case of navigation in iframe we also have to find
// top level context to identify if this navigation is affected.
const topLevelContext = this.browsingContext.top
? this.browsingContext.top
: this.browsingContext;
if (
topLevelContext === contentBrowser.browsingContext &&
promptType === "beforeunload" &&
this.#resolveWhenStarted
) {
this.#trace(
"A beforeunload prompt is open in the context of the navigated context and resolveWhenStarted=true. " +
"Stopping the navigation."
);
this.#seenStartFlag = true;
this.stop();
}
};
#setUnloadTimer() { #setUnloadTimer() {
if (this.#expectNavigation) { if (this.#expectNavigation) {
this.#trace("Skip setting the unload timer"); this.#trace("Skip setting the unload timer");

View File

@ -294,7 +294,9 @@ class NavigationRegistry extends EventEmitter {
let navigation = this.#navigations.get(navigableId); let navigation = this.#navigations.get(navigableId);
if (navigation && !navigation.finished) { if (navigation && !navigation.finished) {
if (navigation.url === url) { // Bug 1908952. As soon as we have support for the "url" field in case of beforeunload
// prompt being open, we can remove "!navigation.url" check.
if (!navigation.url || navigation.url === url) {
// If we are already monitoring a navigation for this navigable and the same url, // If we are already monitoring a navigation for this navigable and the same url,
// for which we did not receive a navigation-stopped event, this navigation // for which we did not receive a navigation-stopped event, this navigation
// is already tracked and we don't want to create another id & event. // is already tracked and we don't want to create another id & event.

View File

@ -175,8 +175,6 @@ class BrowsingContextModule extends RootBiDiModule {
this.#contextListener.on("attached", this.#onContextAttached); this.#contextListener.on("attached", this.#onContextAttached);
this.#contextListener.on("discarded", this.#onContextDiscarded); this.#contextListener.on("discarded", this.#onContextDiscarded);
// Create the navigation listener and listen to "fragment-navigated" and
// "navigation-started" events.
this.#navigationListener = new lazy.NavigationListener( this.#navigationListener = new lazy.NavigationListener(
this.messageHandler.navigationManager this.messageHandler.navigationManager
); );
@ -1133,6 +1131,7 @@ class BrowsingContextModule extends RootBiDiModule {
}); });
}, },
{ {
targetURI,
wait, wait,
} }
); );
@ -1539,6 +1538,8 @@ class BrowsingContextModule extends RootBiDiModule {
* @param {Function} startNavigationFn * @param {Function} startNavigationFn
* A callback that starts a navigation. * A callback that starts a navigation.
* @param {object} options * @param {object} options
* @param {string=} options.targetURI
* The target URI for the navigation.
* @param {WaitCondition} options.wait * @param {WaitCondition} options.wait
* The WaitCondition to use to wait for the navigation. * The WaitCondition to use to wait for the navigation.
* *
@ -1546,7 +1547,7 @@ class BrowsingContextModule extends RootBiDiModule {
* A Promise that resolves to navigate results when the navigation is done. * A Promise that resolves to navigate results when the navigation is done.
*/ */
async #awaitNavigation(webProgress, startNavigationFn, options) { async #awaitNavigation(webProgress, startNavigationFn, options) {
const { wait } = options; const { targetURI, wait } = options;
const context = webProgress.browsingContext; const context = webProgress.browsingContext;
const browserId = context.browserId; const browserId = context.browserId;
@ -1555,6 +1556,7 @@ class BrowsingContextModule extends RootBiDiModule {
const listener = new lazy.ProgressListener(webProgress, { const listener = new lazy.ProgressListener(webProgress, {
expectNavigation: true, expectNavigation: true,
resolveWhenStarted, resolveWhenStarted,
targetURI,
// In case the webprogress is already navigating, always wait for an // In case the webprogress is already navigating, always wait for an
// explicit start flag. // explicit start flag.
waitForExplicitStart: true, waitForExplicitStart: true,