1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 ChromeUtils.defineESModuleGetters(lazy, {
8 EventEmitter: "resource://gre/modules/EventEmitter.sys.mjs",
11 const OBSERVER_TOPIC_ATTACHED = "browsing-context-attached";
12 const OBSERVER_TOPIC_DISCARDED = "browsing-context-discarded";
14 const OBSERVER_TOPIC_SET_EMBEDDER = "browsing-context-did-set-embedder";
17 * The BrowsingContextListener can be used to listen for notifications coming
18 * from browsing contexts that get attached or discarded.
22 * const listener = new BrowsingContextListener();
23 * listener.on("attached", onAttached);
24 * listener.startListening();
26 * const onAttached = (eventName, data = {}) => {
27 * const { browsingContext, why } = data;
33 * The BrowsingContextListener emits "attached" and "discarded" events,
34 * with the following object as payload:
35 * - {BrowsingContext} browsingContext
36 * Browsing context the notification relates to.
38 * Usually "attach" or "discard", but will contain "replace" if the
39 * browsing context gets replaced by a cross-group navigation.
41 export class BrowsingContextListener {
46 * Create a new BrowsingContextListener instance.
49 lazy.EventEmitter.decorate(this);
51 // A map that temporarily holds attached top-level browsing contexts until
52 // their embedder element is set, which is required to successfully
53 // retrieve a unique id for the content browser by the TabManager.
54 this.#topContextsToAttach = new Map();
56 this.#listening = false;
61 this.#topContextsToAttach = null;
64 observe(subject, topic, data) {
66 case OBSERVER_TOPIC_ATTACHED:
67 // Delay emitting the event for top-level browsing contexts until
68 // the embedder element has been set.
69 if (!subject.parent) {
70 this.#topContextsToAttach.set(subject, data);
74 this.emit("attached", { browsingContext: subject, why: data });
77 case OBSERVER_TOPIC_DISCARDED:
78 // Remove a recently attached top-level browsing context if it's
79 // immediately discarded.
80 if (this.#topContextsToAttach.has(subject)) {
81 this.#topContextsToAttach.delete(subject);
84 this.emit("discarded", { browsingContext: subject, why: data });
87 case OBSERVER_TOPIC_SET_EMBEDDER:
88 const why = this.#topContextsToAttach.get(subject);
89 if (why !== undefined) {
90 this.emit("attached", { browsingContext: subject, why });
91 this.#topContextsToAttach.delete(subject);
98 if (this.#listening) {
102 Services.obs.addObserver(this, OBSERVER_TOPIC_ATTACHED);
103 Services.obs.addObserver(this, OBSERVER_TOPIC_DISCARDED);
104 Services.obs.addObserver(this, OBSERVER_TOPIC_SET_EMBEDDER);
106 this.#listening = true;
110 if (!this.#listening) {
114 Services.obs.removeObserver(this, OBSERVER_TOPIC_ATTACHED);
115 Services.obs.removeObserver(this, OBSERVER_TOPIC_DISCARDED);
116 Services.obs.removeObserver(this, OBSERVER_TOPIC_SET_EMBEDDER);
118 this.#topContextsToAttach.clear();
120 this.#listening = false;