Bug 1890689 accumulate input in LargerReceiverBlockSizeThanDesiredBuffering GTest...
[gecko.git] / remote / shared / UserContextManager.sys.mjs
blob679b24b2bc020a1954d772c45c7745dcf7d9ec1f
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/. */
5 const lazy = {};
7 ChromeUtils.defineESModuleGetters(lazy, {
8   ContextualIdentityService:
9     "resource://gre/modules/ContextualIdentityService.sys.mjs",
11   ContextualIdentityListener:
12     "chrome://remote/content/shared/listeners/ContextualIdentityListener.sys.mjs",
13   generateUUID: "chrome://remote/content/shared/UUID.sys.mjs",
14 });
16 const DEFAULT_CONTEXT_ID = "default";
17 const DEFAULT_INTERNAL_ID = 0;
19 /**
20  * A UserContextManager instance keeps track of all public user contexts and
21  * maps their internal platform.
22  *
23  * This class is exported for test purposes. Otherwise the UserContextManager
24  * singleton should be used.
25  */
26 export class UserContextManagerClass {
27   #contextualIdentityListener;
28   #userContextIds;
30   constructor() {
31     // Map from internal ids (numbers) from the ContextualIdentityService to
32     // opaque UUIDs (string).
33     this.#userContextIds = new Map();
35     // The default user context is always using 0 as internal user context id
36     // and should be exposed as "default" instead of a randomly generated id.
37     this.#userContextIds.set(DEFAULT_INTERNAL_ID, DEFAULT_CONTEXT_ID);
39     // Register other (non-default) public contexts.
40     lazy.ContextualIdentityService.getPublicIdentities().forEach(identity =>
41       this.#registerIdentity(identity)
42     );
44     this.#contextualIdentityListener = new lazy.ContextualIdentityListener();
45     this.#contextualIdentityListener.on("created", this.#onIdentityCreated);
46     this.#contextualIdentityListener.on("deleted", this.#onIdentityDeleted);
47     this.#contextualIdentityListener.startListening();
48   }
50   destroy() {
51     this.#contextualIdentityListener.off("created", this.#onIdentityCreated);
52     this.#contextualIdentityListener.off("deleted", this.#onIdentityDeleted);
53     this.#contextualIdentityListener.destroy();
55     this.#userContextIds = null;
56   }
58   /**
59    * Retrieve the user context id corresponding to the default user context.
60    *
61    * @returns {string}
62    *     The default user context id.
63    */
64   get defaultUserContextId() {
65     return DEFAULT_CONTEXT_ID;
66   }
68   /**
69    * Creates a new user context.
70    *
71    * @param {string} prefix
72    *     The prefix to use for the name of the user context.
73    *
74    * @returns {string}
75    *     The user context id of the new user context.
76    */
77   createContext(prefix = "remote") {
78     // Prepare the opaque id and name beforehand.
79     const userContextId = lazy.generateUUID();
80     const name = `${prefix}-${userContextId}`;
82     // Create the user context.
83     const identity = lazy.ContextualIdentityService.create(name);
84     const internalId = identity.userContextId;
86     // An id has been set already by the contextual-identity-created observer.
87     // Override it with `userContextId` to match the container name.
88     this.#userContextIds.set(internalId, userContextId);
90     return userContextId;
91   }
93   /**
94    * Retrieve the user context id corresponding to the provided browsing context.
95    *
96    * @param {BrowsingContext} browsingContext
97    *     The browsing context to get the user context id from.
98    *
99    * @returns {string}
100    *     The corresponding user context id.
101    *
102    * @throws {TypeError}
103    *     If `browsingContext` is not a CanonicalBrowsingContext instance.
104    */
105   getIdByBrowsingContext(browsingContext) {
106     if (!CanonicalBrowsingContext.isInstance(browsingContext)) {
107       throw new TypeError(
108         `Expected browsingContext to be a CanonicalBrowsingContext, got ${browsingContext}`
109       );
110     }
112     return this.getIdByInternalId(
113       browsingContext.originAttributes.userContextId
114     );
115   }
117   /**
118    * Retrieve the user context id corresponding to the provided internal id.
119    *
120    * @param {number} internalId
121    *     The internal user context id.
122    *
123    * @returns {string|null}
124    *     The corresponding user context id or null if the user context does not
125    *     exist.
126    */
127   getIdByInternalId(internalId) {
128     if (this.#userContextIds.has(internalId)) {
129       return this.#userContextIds.get(internalId);
130     }
131     return null;
132   }
134   /**
135    * Retrieve the internal id corresponding to the provided user
136    * context id.
137    *
138    * @param {string} userContextId
139    *     The user context id.
140    *
141    * @returns {number|null}
142    *     The internal user context id or null if the user context does not
143    *     exist.
144    */
145   getInternalIdById(userContextId) {
146     for (const [internalId, id] of this.#userContextIds) {
147       if (userContextId == id) {
148         return internalId;
149       }
150     }
151     return null;
152   }
154   /**
155    * Returns an array of all known user context ids.
156    *
157    * @returns {Array<string>}
158    *     The array of user context ids.
159    */
160   getUserContextIds() {
161     return Array.from(this.#userContextIds.values());
162   }
164   /**
165    * Checks if the provided user context id is known by this UserContextManager.
166    *
167    * @param {string} userContextId
168    *     The id of the user context to check.
169    */
170   hasUserContextId(userContextId) {
171     return this.getUserContextIds().includes(userContextId);
172   }
174   /**
175    * Removes a user context and closes all related container tabs.
176    *
177    * @param {string} userContextId
178    *     The id of the user context to remove.
179    * @param {object=} options
180    * @param {boolean=} options.closeContextTabs
181    *     Pass true if the tabs owned by the user context should also be closed.
182    *     Defaults to false.
183    */
184   removeUserContext(userContextId, options = {}) {
185     const { closeContextTabs = false } = options;
187     if (!this.hasUserContextId(userContextId)) {
188       return;
189     }
191     const internalId = this.getInternalIdById(userContextId);
192     if (closeContextTabs) {
193       lazy.ContextualIdentityService.closeContainerTabs(internalId);
194     }
195     lazy.ContextualIdentityService.remove(internalId);
196   }
198   #onIdentityCreated = (eventName, data) => {
199     this.#registerIdentity(data.identity);
200   };
202   #onIdentityDeleted = (eventName, data) => {
203     this.#userContextIds.delete(data.identity.userContextId);
204   };
206   #registerIdentity(identity) {
207     // Note: the id for identities created via UserContextManagerClass.createContext
208     // are overridden in createContext.
209     this.#userContextIds.set(identity.userContextId, lazy.generateUUID());
210   }
213 // Expose a shared singleton.
214 export const UserContextManager = new UserContextManagerClass();