From 2a436540d07f8656104625bbbe05d2ba0b5571f5 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 24 Nov 2025 13:47:42 +0000 Subject: [PATCH] feat(api): add glide.containers --- @types/gecko.d.mts | 15 ++++++ .../browser/base/content/browser-api.mts | 18 +++++++ src/glide/browser/base/content/glide.d.ts | 50 +++++++++++++++++++ .../base/content/test/config/browser.toml | 3 ++ .../content/test/config/browser_containers.ts | 35 +++++++++++++ src/glide/docs/api.md | 34 +++++++++++++ 6 files changed, 155 insertions(+) create mode 100644 src/glide/browser/base/content/test/config/browser_containers.ts diff --git a/@types/gecko.d.mts b/@types/gecko.d.mts index 2182bab6..361c6a4b 100644 --- a/@types/gecko.d.mts +++ b/@types/gecko.d.mts @@ -496,6 +496,21 @@ declare var ReadableStream: { new(underlyingSource?: UnderlyingSource, strategy?: QueuingStrategy): ReadableStream; }; +declare var ContextualIdentityService: { + create(name: string, icon: string, color: string): ContextualIdentityService.Container; + remove(id: number): boolean; +}; + +declare namespace ContextualIdentityService { + export type Container = { + userContextId: number; + public: boolean; + icon: string; + color: string; + name: string; + }; +} + ////////////////////////////////////////// ////////////// typed actors ////////////// ////////////////////////////////////////// diff --git a/src/glide/browser/base/content/browser-api.mts b/src/glide/browser/base/content/browser-api.mts index ae86c13f..38517163 100644 --- a/src/glide/browser/base/content/browser-api.mts +++ b/src/glide/browser/base/content/browser-api.mts @@ -466,6 +466,24 @@ export function make_glide_api( }, }, + containers: { + create(props): glide.Container { + const container = ContextualIdentityService.create(props.name, props.icon, props.color); + return { + icon: container.icon, + name: container.name, + id: container.userContextId, + color: container.color, + + // toolkit/components/extensions/parent/ext-toolkit.js::getCookieStoreIdForContainer + cookie_store_id: `firefox-container-${container.userContextId}`, + }; + }, + remove(id) { + return ContextualIdentityService.remove(id); + }, + }, + addons: { async install(xpi_url, opts): Promise { const cache = GlideBrowser.resolved_addons_cache_file; diff --git a/src/glide/browser/base/content/glide.d.ts b/src/glide/browser/base/content/glide.d.ts index 93c56bb2..1de80b2d 100644 --- a/src/glide/browser/base/content/glide.d.ts +++ b/src/glide/browser/base/content/glide.d.ts @@ -948,6 +948,18 @@ declare global { }): Promise; }; + containers: { + /** + * Create a new container. + */ + create(props: { name: string; icon: glide.ContainerIcon; color: glide.ContainerColor }): glide.Container; + + /** + * Remove a container. + */ + remove(id: number): boolean; + }; + messengers: { /** * Create a {@link glide.ParentMessenger} that can be used to communicate with the content process. @@ -1846,6 +1858,44 @@ declare global { path: string | undefined; size: number | undefined; }; + + export type Container = { + id: number; + + name: string; + icon: string; + color: string; + + cookie_store_id: string; + }; + + // browser/components/preferences/dialogs/containers.js::gContainersManager.icons + export type ContainerIcon = + | "fingerprint" + | "briefcase" + | "dollar" + | "cart" + | "vacation" + | "gift" + | "food" + | "fruit" + | "pet" + | "tree" + | "chill" + | "circle" + | "fence"; + + // browser/components/preferences/dialogs/containers.js::gContainersManager.colors + export type ContainerColor = + | "blue" + | "turquoise" + | "green" + | "yellow" + | "orange" + | "red" + | "pink" + | "purple" + | "toolbar"; } type TabID = number; diff --git a/src/glide/browser/base/content/test/config/browser.toml b/src/glide/browser/base/content/test/config/browser.toml index a1cbcb0a..464b306e 100644 --- a/src/glide/browser/base/content/test/config/browser.toml +++ b/src/glide/browser/base/content/test/config/browser.toml @@ -14,6 +14,9 @@ https_first_disabled = true ["dist/browser_sandbox.js"] https_first_disabled = true +["dist/browser_containers.js"] +https_first_disabled = true + ["dist/browser_document_mirror.js"] https_first_disabled = true diff --git a/src/glide/browser/base/content/test/config/browser_containers.ts b/src/glide/browser/base/content/test/config/browser_containers.ts new file mode 100644 index 00000000..7eed4553 --- /dev/null +++ b/src/glide/browser/base/content/test/config/browser_containers.ts @@ -0,0 +1,35 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +add_task(async function test_containers() { + await GlideTestUtils.reload_config(function _() { + glide.keymaps.set("normal", "~", () => { + glide.g.value = glide.containers.create({ + name: "my-test", + color: "red", + icon: "tree", + }); + }); + + glide.keymaps.set("normal", "Z", () => { + glide.g.value = glide.containers.remove((glide.g.value as glide.Container).id); + }); + }); + + await keys("~"); + + const container = glide.g.value as glide.Container; + ok(container); + is(typeof container.id, "number"); + is(container.name, "my-test"); + is(container.icon, "tree"); + is(container.color, "red"); + is(container.cookie_store_id, `firefox-container-${container.id}`); + + await keys("Z"); + + is(glide.g.value, true); +}); diff --git a/src/glide/docs/api.md b/src/glide/docs/api.md index fece9a75..dd9f199d 100644 --- a/src/glide/docs/api.md +++ b/src/glide/docs/api.md @@ -139,6 +139,9 @@ text-decoration: none; [`glide.fs.exists()`](#glide.fs.exists)\ [`glide.fs.stat()`](#glide.fs.stat)\ [`glide.fs.mkdir()`](#glide.fs.mkdir)\ +[`glide.containers`](#glide.containers)\ +[`glide.containers.create()`](#glide.containers.create)\ +[`glide.containers.remove()`](#glide.containers.remove)\ [`glide.messengers`](#glide.messengers)\ [`glide.messengers.create()`](#glide.messengers.create)\ [`glide.modes`](#glide.modes)\ @@ -175,6 +178,9 @@ text-decoration: none; [`glide.CommandLineShowOpts`](#glide.CommandLineShowOpts)\ [`glide.CommandLineCustomOption`](#glide.CommandLineCustomOption)\ [`glide.FileInfo`](#glide.FileInfo)\ +[`glide.Container`](#glide.Container)\ +[`glide.ContainerIcon`](#glide.ContainerIcon)\ +[`glide.ContainerColor`](#glide.ContainerColor)\ [`DOM.create_element()`](#DOM.create_element) {% html %} @@ -1078,6 +1084,20 @@ Parent directories are created by default, if desired you can turn this off with By default this will _not_ error if the `path` already exists, if you would like it to do so, pass `ts:glide.fs.mkdir('...', { exists_ok: false })` +## • `glide.containers` {% id="glide.containers" %} + +{% api-heading id="glide.containers.create" %} +glide.containers.create(props): glide.Container +{% /api-heading %} + +Create a new container. + +{% api-heading id="glide.containers.remove" %} +glide.containers.remove(id): boolean +{% /api-heading %} + +Remove a container. + ## • `glide.messengers` {% id="glide.messengers" %} {% api-heading id="glide.messengers.create" %} @@ -1583,6 +1603,20 @@ path: string | undefined; size: number | undefined; ``` +## • `glide.Container` {% id="glide.Container" %} + +```typescript {% highlight_prefix="type x = {" %} +id: number; +name: string; +icon: string; +color: string; +cookie_store_id: string; +``` + +## • `glide.ContainerIcon: "fingerprint" | "briefcase" | "dollar" | "cart" | "vacation" | "gift" | "food" | "fruit" | "pet" | "tree" | "chill" | "circle" | "fence"` {% id="glide.ContainerIcon" %} + +## • `glide.ContainerColor: "blue" | "turquoise" | "green" | "yellow" | "orange" | "red" | "pink" | "purple" | "toolbar"` {% id="glide.ContainerColor" %} + # `DOM` {% id="DOM" %} Helper functions for interacting with the DOM.