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 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",
16 const DEFAULT_CONTEXT_ID = "default";
17 const DEFAULT_INTERNAL_ID = 0;
20 * A UserContextManager instance keeps track of all public user contexts and
21 * maps their internal platform.
23 * This class is exported for test purposes. Otherwise the UserContextManager
24 * singleton should be used.
26 export class UserContextManagerClass {
27 #contextualIdentityListener;
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)
44 this.#contextualIdentityListener = new lazy.ContextualIdentityListener();
45 this.#contextualIdentityListener.on("created", this.#onIdentityCreated);
46 this.#contextualIdentityListener.on("deleted", this.#onIdentityDeleted);
47 this.#contextualIdentityListener.startListening();
51 this.#contextualIdentityListener.off("created", this.#onIdentityCreated);
52 this.#contextualIdentityListener.off("deleted", this.#onIdentityDeleted);
53 this.#contextualIdentityListener.destroy();
55 this.#userContextIds = null;
59 * Retrieve the user context id corresponding to the default user context.
62 * The default user context id.
64 get defaultUserContextId() {
65 return DEFAULT_CONTEXT_ID;
69 * Creates a new user context.
71 * @param {string} prefix
72 * The prefix to use for the name of the user context.
75 * The user context id of the new user context.
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);
94 * Retrieve the user context id corresponding to the provided browsing context.
96 * @param {BrowsingContext} browsingContext
97 * The browsing context to get the user context id from.
100 * The corresponding user context id.
102 * @throws {TypeError}
103 * If `browsingContext` is not a CanonicalBrowsingContext instance.
105 getIdByBrowsingContext(browsingContext) {
106 if (!CanonicalBrowsingContext.isInstance(browsingContext)) {
108 `Expected browsingContext to be a CanonicalBrowsingContext, got ${browsingContext}`
112 return this.getIdByInternalId(
113 browsingContext.originAttributes.userContextId
118 * Retrieve the user context id corresponding to the provided internal id.
120 * @param {number} internalId
121 * The internal user context id.
123 * @returns {string|null}
124 * The corresponding user context id or null if the user context does not
127 getIdByInternalId(internalId) {
128 if (this.#userContextIds.has(internalId)) {
129 return this.#userContextIds.get(internalId);
135 * Retrieve the internal id corresponding to the provided user
138 * @param {string} userContextId
139 * The user context id.
141 * @returns {number|null}
142 * The internal user context id or null if the user context does not
145 getInternalIdById(userContextId) {
146 for (const [internalId, id] of this.#userContextIds) {
147 if (userContextId == id) {
155 * Returns an array of all known user context ids.
157 * @returns {Array<string>}
158 * The array of user context ids.
160 getUserContextIds() {
161 return Array.from(this.#userContextIds.values());
165 * Checks if the provided user context id is known by this UserContextManager.
167 * @param {string} userContextId
168 * The id of the user context to check.
170 hasUserContextId(userContextId) {
171 return this.getUserContextIds().includes(userContextId);
175 * Removes a user context and closes all related container tabs.
177 * Note: When closing the related container tabs possible "beforeunload"
178 * prompts will be ignored.
180 * @param {string} userContextId
181 * The id of the user context to remove.
182 * @param {object=} options
183 * @param {boolean=} options.closeContextTabs
184 * Pass true if the tabs owned by the user context should also be closed.
187 removeUserContext(userContextId, options = {}) {
188 const { closeContextTabs = false } = options;
190 if (!this.hasUserContextId(userContextId)) {
194 const internalId = this.getInternalIdById(userContextId);
195 if (closeContextTabs) {
196 lazy.ContextualIdentityService.closeContainerTabs(internalId, {
197 skipPermitUnload: true,
200 lazy.ContextualIdentityService.remove(internalId);
203 #onIdentityCreated = (eventName, data) => {
204 this.#registerIdentity(data.identity);
207 #onIdentityDeleted = (eventName, data) => {
208 this.#userContextIds.delete(data.identity.userContextId);
211 #registerIdentity(identity) {
212 // Note: the id for identities created via UserContextManagerClass.createContext
213 // are overridden in createContext.
214 this.#userContextIds.set(identity.userContextId, lazy.generateUUID());
218 // Expose a shared singleton.
219 export const UserContextManager = new UserContextManagerClass();