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:
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;
}