tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 47a9b982fe0f06b949f1a4cca60e56860a24df46
parent 538edbdc2c0bbddb47975b263dee52796c17e276
Author: Alexandra Borovova <aborovova@mozilla.com>
Date:   Fri,  5 Dec 2025 11:33:35 +0000

Bug 2002721 - [webdriver-bidi] Make sure that "script.realmCreated" event is sent after a browser element is attached to a browser context. r=jdescottes

Differential Revision: https://phabricator.services.mozilla.com/D274756

Diffstat:
Mremote/webdriver-bidi/modules/root/browsingContext.sys.mjs | 9+++++++++
Mremote/webdriver-bidi/modules/root/script.sys.mjs | 106++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
2 files changed, 98 insertions(+), 17 deletions(-)

diff --git a/remote/webdriver-bidi/modules/root/browsingContext.sys.mjs b/remote/webdriver-bidi/modules/root/browsingContext.sys.mjs @@ -1980,6 +1980,15 @@ class BrowsingContextModule extends RootBiDiModule { "browsingContext.contextCreated", browsingContextInfo ); + + // This is an internal event is used by the script module + // to ensure that "script.realmCreated" event is emitted + // after "browsingContext.contextCreated". + this.messageHandler.emitEvent( + "browsingContext._contextCreatedEmitted", + { browsingContext }, + browsingContextInfo + ); } }; diff --git a/remote/webdriver-bidi/modules/root/script.sys.mjs b/remote/webdriver-bidi/modules/root/script.sys.mjs @@ -8,6 +8,8 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs", + BrowsingContextListener: + "chrome://remote/content/shared/listeners/BrowsingContextListener.sys.mjs", ContextDescriptorType: "chrome://remote/content/shared/messagehandler/MessageHandler.sys.mjs", error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs", @@ -63,22 +65,35 @@ const ScriptEvaluateResultType = { */ class ScriptModule extends RootBiDiModule { + #contextListener; #preloadScriptMap; + #realmInfoMap; #subscribedEvents; constructor(messageHandler) { super(messageHandler); + this.#contextListener = new lazy.BrowsingContextListener(); + this.#contextListener.on("attached", this.#onContextAttached); + // Map in which the keys are UUIDs, and the values are structs // of the type PreloadScript. this.#preloadScriptMap = new Map(); + // Map with browsing contexts as keys and realm info object + // as values. + this.#realmInfoMap = new WeakMap(); + // Set of event names which have active subscriptions. this.#subscribedEvents = new Set(); } destroy() { + this.#contextListener.off("attached", this.#onContextAttached); + this.#contextListener.destroy(); + this.#preloadScriptMap = null; + this.#realmInfoMap = null; this.#subscribedEvents = null; } @@ -903,24 +918,51 @@ class ScriptModule extends RootBiDiModule { .filter(realm => realm.context !== null); } + #hasEventSubscriptionToContextCreated(browsingContext) { + const sessionData = + this.messageHandler.sessionData.getSessionDataForContext( + "browsingContext", + "event", + browsingContext + ); + + return sessionData.some( + item => item.value === "browsingContext.contextCreated" + ); + } + + #onContextAttached = (eventName, data) => { + const { browsingContext } = data; + // If there is a subscription for "browsingContext.contextCreated" event, + // do not send "script.realmCreated" event yet and + // wait until the "browsingContext.contextCreated" event is submitted + if ( + this.#realmInfoMap.has(browsingContext) && + !this.#hasEventSubscriptionToContextCreated(browsingContext) + ) { + this.#sendDelayedRealmCreatedEvent(browsingContext); + } + }; + + #onContextCreatedSubmitted = (eventName, { browsingContext }) => { + if (this.#realmInfoMap.has(browsingContext)) { + this.#sendDelayedRealmCreatedEvent(browsingContext); + } + }; + #onRealmCreated = (eventName, { realmInfo }) => { // Resolve browsing context to a TabManager id. const context = lazy.NavigableManager.getIdForBrowsingContext( realmInfo.context ); - const browsingContextId = realmInfo.context.id; - // Do not emit the event, if the browsing context is gone. + // Do not emit the event, if the browsing context is gone or not created yet. if (context === null) { + // Save the realm info to send it when the browsing context is ready. + this.#realmInfoMap.set(realmInfo.context, realmInfo); return; } - - realmInfo.context = context; - this._emitEventForBrowsingContext( - browsingContextId, - "script.realmCreated", - realmInfo - ); + this.#sendRealmCreatedEvent(realmInfo, realmInfo.context, context); }; #onRealmDestroyed = (eventName, { realm, context }) => { @@ -929,25 +971,55 @@ class ScriptModule extends RootBiDiModule { }); }; - #startListingOnRealmCreated() { + #sendDelayedRealmCreatedEvent(browsingContext) { + const realmInfo = this.#realmInfoMap.get(browsingContext); + // Resolve browsing context to a TabManager id. + const browsingContextId = lazy.NavigableManager.getIdForBrowsingContext( + realmInfo.context + ); + this.#sendRealmCreatedEvent(realmInfo, browsingContext, browsingContextId); + this.#realmInfoMap.delete(browsingContext); + } + + #sendRealmCreatedEvent(realmInfo, context, browsingContextId) { + realmInfo.context = browsingContextId; + + this._emitEventForBrowsingContext( + context.id, + "script.realmCreated", + realmInfo + ); + } + + #startListeningOnRealmCreated() { if (!this.#subscribedEvents.has("script.realmCreated")) { this.messageHandler.on("realm-created", this.#onRealmCreated); + this.messageHandler.on( + "browsingContext._contextCreatedEmitted", + this.#onContextCreatedSubmitted + ); + this.#contextListener.startListening(); } } - #stopListingOnRealmCreated() { + #stopListeningOnRealmCreated() { if (this.#subscribedEvents.has("script.realmCreated")) { this.messageHandler.off("realm-created", this.#onRealmCreated); + this.messageHandler.off( + "browsingContext._contextCreatedEmitted", + this.#onContextCreatedSubmitted + ); + this.#contextListener.stopListening(); } } - #startListingOnRealmDestroyed() { + #startListeningOnRealmDestroyed() { if (!this.#subscribedEvents.has("script.realmDestroyed")) { this.messageHandler.on("realm-destroyed", this.#onRealmDestroyed); } } - #stopListingOnRealmDestroyed() { + #stopListeningOnRealmDestroyed() { if (this.#subscribedEvents.has("script.realmDestroyed")) { this.messageHandler.off("realm-destroyed", this.#onRealmDestroyed); } @@ -956,12 +1028,12 @@ class ScriptModule extends RootBiDiModule { #subscribeEvent(event) { switch (event) { case "script.realmCreated": { - this.#startListingOnRealmCreated(); + this.#startListeningOnRealmCreated(); this.#subscribedEvents.add(event); break; } case "script.realmDestroyed": { - this.#startListingOnRealmDestroyed(); + this.#startListeningOnRealmDestroyed(); this.#subscribedEvents.add(event); break; } @@ -971,12 +1043,12 @@ class ScriptModule extends RootBiDiModule { #unsubscribeEvent(event) { switch (event) { case "script.realmCreated": { - this.#stopListingOnRealmCreated(); + this.#stopListeningOnRealmCreated(); this.#subscribedEvents.delete(event); break; } case "script.realmDestroyed": { - this.#stopListingOnRealmDestroyed(); + this.#stopListeningOnRealmDestroyed(); this.#subscribedEvents.delete(event); break; }