Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 143 additions & 2 deletions @types/gecko.d.mts
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,149 @@ interface nsIWindowMediator {

declare var MozElements: MozElements;

// TODO(glide-types)
declare type BrowserTab = any;
interface BrowserTab extends MozTabbrowserTab {
_glide_commandline: GlideCommandLine | null;
}
// Source browser/components/tabbrowser/content/tab.js
interface MozTabbrowserTab extends XULElement {
// Source toolkit/content/customElements.js
disabled: boolean;
tabIndex: number;
// Source toolkit/content/customElements.js
label: string;
image: string;
command: string;
accessKey: string;
// Source toolkit/content/widgets/tabbox.js
value: string;
container: MozTabbrowserTabs;
control: MozTabbrowserTabs;
selected: boolean;
visible: boolean;
linkedPanel: string;
// Source browser/components/tabbrowser/content/tab.js
closing: boolean;
pinned: boolean;
isOpen: boolean;
readonly hidden: boolean;
muted: boolean;
multiselected: boolean;
userContextId: number;
soundPlaying: boolean;
pictureinpicture: boolean;
activeMediaBlocked: boolean;
undiscardable: boolean;
animationsEnabled: boolean;
isEmpty: boolean;
lastAccessed: number;
lastSeenActive: number;
attention: boolean;
owner: MozTabbrowserTab | null;
elementIndex: number;
group: MozTabbrowserTabGroup | null;
splitview: GlobalBrowser.MozTabSplitViewWrapper | null;
hasTabNote: boolean;
muteReason: string | undefined | null;

readonly overlayIcon: Element | null;
readonly audioButton: Element | null;
readonly throbber: Element | null;
readonly iconImage: Element | null;
readonly sharingIcon: Element | null;
readonly textLabel: Element | null;
readonly closeButton: Element | null;
readonly noteIcon: Element | null;
readonly noteIconOverlay: Element | null;

initialize(): void;
updateLastAccessed(date?: number): void;
updateLastSeenActive(): void;
updateLastUnloadedByTabUnloader(): void;
recordTimeFromUnloadToReload(): void;
toggleMuteAudio(muteReason?: string | null): void;
resumeDelayedMedia(): void;
setUserContextId(userContextId: number): void;
updateA11yDescription(): void;
updateSplitViewAriaLabel(index: number): void;
// Dynamically set by tabbrowser.js
linkedBrowser: GlobalBrowser.GlobalBrowser | null;
permanentKey: object | undefined;
description: string | undefined;
successor: MozTabbrowserTab | null;
predecessors: Set<MozTabbrowserTab>;
initializingTab: boolean | undefined;
openerTab: MozTabbrowserTab | undefined;
addedByAdoption: boolean | undefined;
removedByAdoption: boolean | undefined;
collapsed: boolean;
}

// Source browser/components/tabbrowser/content/tabgroup.js
interface MozTabbrowserTabGroup extends XULElement {
overflowContainer: XULElement;
saveOnWindowClose: boolean;

color: string;
defaultGroupName: string;
id: string;
hasActiveTab: boolean;
label: string;
name: string;
collapsed: boolean;
lastSeenActive: number;
tabs: MozTabbrowserTab[];
tabsAndSplitViews: (MozTabbrowserTab | GlobalBrowser.MozTabSplitViewWrapper)[];
labelElement: Element;
labelContainerElement: XULElement;
overflowCountLabel: Element;
wasCreatedByAdoption: boolean;
isBeingDragged: boolean;
hoverPreviewPanelActive: boolean;

addTabs(
tabsOrSplitViews: (MozTabbrowserTab | GlobalBrowser.MozTabSplitViewWrapper)[],
metricsContext?: object,
): void;
ungroupTabs(metricsContext?: object): void;
save(options?: object): void;
saveAndClose(options?: object): void;
select(): void;
isTabVisibleInGroup(tab: MozTabbrowserTab): boolean;
}

// Source browser/components/tabbrowser/content/tabs.js
interface MozTabbrowserTabs extends XULElement {
startupTime: number;
arrowScrollbox: Element;
pinnedTabsContainer: Element;
previewPanel: object | null;
tooltip: string;
tabDragAndDrop: object;
emptyTabTitle: string;
tabbox: XULElement;
newTabButton: Element;
verticalMode: boolean;
expandOnHover: boolean;
overflowing: boolean;
allTabs: MozTabbrowserTab[];
allGroups: MozTabbrowserTabGroup[];
allSplitViews: MozTabSplitViewWrapper[];
openTabs: MozTabbrowserTab[];
nonHiddenTabs: MozTabbrowserTab[];
visibleTabs: MozTabbrowserTab[];
tablistHasFocus: boolean;
ariaFocusableItems: Element[];
dragAndDropElements: Element[];
init(): void;
destroy(): void;
advanceSelectedItem(direction: number, wrap: boolean): void;
ensureTabPreviewPanelLoaded(): Promise<void>;
updateTabSoundLabel(tab: MozTabbrowserTab): void;
getRelatedElement(tab: MozTabbrowserTab): Element;
isContainerVerticalPinnedGrid(tab: MozTabbrowserTab): boolean;
cancelTabGroupPreview(): void;
showTabGroupPreview(group: MozTabbrowserTabGroup): void;
}

declare var GlideBrowser: typeof import("../src/glide/browser/base/content/browser.mts").GlideBrowser;

Expand Down
2 changes: 1 addition & 1 deletion src/glide/browser/base/content/GlideTestUtils.sys.mts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class GlideTestUtilsClass {
* Behaves the same as `BrowserTestUtils.openNewForegroundTab()` but also defines
* `[Symbol.dispose]()` so you can use it with `using`.
*/
async new_tab(uri?: string): Promise<BrowserTab> {
async new_tab(uri?: string): Promise<BrowserTab & Disposable> {
const tab = await g.BrowserTestUtils.openNewForegroundTab(g.gBrowser, uri);
return Object.assign(tab, {
[Symbol.dispose]() {
Expand Down
2 changes: 1 addition & 1 deletion src/glide/browser/base/content/browser-commandline.mts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export class TabsCompletionSource implements GlideCompletionSource<TabCompletion
tab.label,
],
}),
DOM.create_element("td", { className: "url", children: tab.linkedBrowser.currentURI.spec }),
DOM.create_element("td", { className: "url", children: tab.linkedBrowser?.currentURI.spec }),
],
}),

Expand Down
8 changes: 7 additions & 1 deletion src/glide/browser/base/content/browser-excmds.mts
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ class GlideExcmdsClass {
}

case "tab_next": {
const all_tabs = gBrowser.tabContainer.allTabs as unknown[];
const all_tabs = gBrowser.tabContainer.allTabs;
let next_index = gBrowser.tabContainer.selectedIndex + 1;
if (next_index >= all_tabs.length) {
next_index = 0;
Expand Down Expand Up @@ -349,6 +349,9 @@ class GlideExcmdsClass {

case "tab_pin_toggle": {
const tab = gBrowser.selectedTab;
if (!tab) {
throw new Error("No tab to pin/unpin");
}
if (tab.pinned) {
gBrowser.unpinTab(tab);
} else {
Expand All @@ -363,6 +366,9 @@ class GlideExcmdsClass {
}

case "tab_duplicate": {
if (!gBrowser.selectedTab) {
throw new Error("No tab to duplicate");
}
gBrowser.duplicateTab(gBrowser.selectedTab, undefined, { inBackground: false });
break;
}
Expand Down
18 changes: 16 additions & 2 deletions src/glide/browser/base/content/browser.mts
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,9 @@ class GlideBrowserClass {
url: location.spec,
get tab_id() {
return assert_present(
GlideBrowser.extension.tabManager.getWrapper(gBrowser.selectedTab),
GlideBrowser.extension.tabManager.getWrapper(
assert_present(gBrowser.selectedTab, "could not resolve selected tab"),
),
"could not resolve tab wrapper",
).id;
},
Expand Down Expand Up @@ -1899,6 +1901,9 @@ class GlideBrowserClass {
*/
async upsert_commandline(opts: GlideCommandLineShowOptions = {}) {
const tab = gBrowser.selectedTab;
if (!tab) {
return null;
}
const cached = this.#get_cached_commandline(tab);
if (cached) {
cached.show(opts);
Expand Down Expand Up @@ -1931,6 +1936,9 @@ class GlideBrowserClass {
}

async toggle_commandline() {
if (!gBrowser.selectedTab) {
return;
}
const commandline = this.#get_cached_commandline(gBrowser.selectedTab);
if (!commandline) {
await this.upsert_commandline();
Expand All @@ -1950,6 +1958,9 @@ class GlideBrowserClass {
* This only returns anything **if** the commandline is open **and** it is focused.
*/
#get_active_commandline(): GlideCommandLine | null {
if (!gBrowser.selectedTab) {
return null;
}
const commandline = this.#get_cached_commandline(gBrowser.selectedTab);
if (!commandline) {
return null;
Expand All @@ -1971,6 +1982,9 @@ class GlideBrowserClass {
}

get_commandline(): GlideCommandLine | null {
if (!gBrowser.selectedTab) {
return null;
}
return this.#get_cached_commandline(gBrowser.selectedTab);
}

Expand All @@ -1982,7 +1996,7 @@ class GlideBrowserClass {
return tab._glide_commandline;
}

#cache_commandline(tab: BrowserTab, excmdbar: Element): void {
#cache_commandline(tab: BrowserTab, excmdbar: GlideCommandLine): void {
tab._glide_commandline = excmdbar;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ async function newtab() {

return {
[Symbol.dispose]() {
if (!tab) {
return;
}
gBrowser.removeTab(tab);
},
};
Expand Down
29 changes: 16 additions & 13 deletions src/glide/browser/base/content/test/excmds/browser_excmds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ function current_url() {
}

add_task(async function test_tab_switching() {
const browser = gBrowser.tabContainer.allTabs.at(0).linkedBrowser;
const browser = gBrowser.tabContainer.allTabs.at(0)?.linkedBrowser;
if (!browser) {
throw new Error("No browser found");
}
BrowserTestUtils.startLoadingURIString(browser, INPUT_TEST_FILE + "?i=0");
await BrowserTestUtils.browserLoaded(browser);
using _tab2 = await GlideTestUtils.new_tab(INPUT_TEST_FILE + "?i=1");
Expand Down Expand Up @@ -274,9 +277,9 @@ add_task(async function test_tab_pin() {
using tab1 = await GlideTestUtils.new_tab(INPUT_TEST_FILE + "?i=1");
using _tab2 = await GlideTestUtils.new_tab(INPUT_TEST_FILE + "?i=2");

is(gBrowser.selectedTab.pinned, false, "Current tab should not be pinned initially");
is(gBrowser.selectedTab!.pinned, false, "Current tab should not be pinned initially");
await keys(":tab_pin<CR>");
is(gBrowser.selectedTab.pinned, true, "Current tab should be pinned after :tab_pin");
is(gBrowser.selectedTab!.pinned, true, "Current tab should be pinned after :tab_pin");

const tab1_id = GlideBrowser.extension?.tabManager?.getWrapper?.(tab1)?.id;
isnot(tab1_id, undefined, "Tab ID should be available");
Expand All @@ -294,11 +297,11 @@ add_task(async function test_tab_unpin() {
using tab1 = await GlideTestUtils.new_tab(INPUT_TEST_FILE + "?i=1");
using _tab2 = await GlideTestUtils.new_tab(INPUT_TEST_FILE + "?i=2");

gBrowser.pinTab(gBrowser.selectedTab);
gBrowser.pinTab(gBrowser.selectedTab!);

is(gBrowser.selectedTab.pinned, true, "Current tab should be pinned initially");
is(gBrowser.selectedTab!.pinned, true, "Current tab should be pinned initially");
await keys(":tab_unpin<CR>");
is(gBrowser.selectedTab.pinned, false, "Current tab should be unpinned after :tab_unpin");
is(gBrowser.selectedTab!.pinned, false, "Current tab should be unpinned after :tab_unpin");

gBrowser.pinTab(tab1);
const tab1_id = GlideBrowser.extension?.tabManager?.getWrapper?.(tab1)?.id;
Expand All @@ -316,15 +319,15 @@ add_task(async function test_tab_pin_toggle_excmd() {
using tab = await GlideTestUtils.new_tab(KEY_TEST_FILE + "?i=1");
const initial_tab_count = gBrowser.tabs.length;
is(gBrowser.selectedTab, tab);
is(gBrowser.selectedTab.pinned, false, "Current tab should not be pinned initially");
is(gBrowser.selectedTab!.pinned, false, "Current tab should not be pinned initially");

await keys(":tab_pin_toggle<CR>");

is(gBrowser.tabs.length, initial_tab_count, "Tab count should remain the same");
is(gBrowser.selectedTab.pinned, true, "Current tab should be pinned after :tab_pin_toggle");
is(gBrowser.selectedTab!.pinned, true, "Current tab should be pinned after :tab_pin_toggle");

await keys(":tab_pin_toggle<CR>");
is(gBrowser.selectedTab.pinned, false, "Current tab should be unpinned after :tab_pin_toggle");
is(gBrowser.selectedTab!.pinned, false, "Current tab should be unpinned after :tab_pin_toggle");
is(gBrowser.tabs.length, initial_tab_count, "Tab count should remain the same");
});

Expand All @@ -334,18 +337,18 @@ add_task(async function test_tab_pin_toggle_keymap() {
using tab = await GlideTestUtils.new_tab(KEY_TEST_FILE + "?i=1");
const initial_tab_count = gBrowser.tabs.length;
is(gBrowser.selectedTab, tab);
is(gBrowser.selectedTab.pinned, false, "Current tab should not be pinned initially");
is(gBrowser.selectedTab!.pinned, false, "Current tab should not be pinned initially");

await keys("<esc>");
await wait_for_mode("normal");

await keys("<A-p>");
is(gBrowser.tabs.length, initial_tab_count, "Tab count should remain the same");

await waiter(() => gBrowser.selectedTab.pinned).is(true, "Tab should be pinned after <A-p>");
await waiter(() => gBrowser.selectedTab!.pinned).is(true, "Tab should be pinned after <A-p>");

await keys("<A-p>");
await waiter(() => gBrowser.selectedTab.pinned).is(false, "Tab should be unpinned after <A-p>");
await waiter(() => gBrowser.selectedTab!.pinned).is(false, "Tab should be unpinned after <A-p>");
is(gBrowser.tabs.length, initial_tab_count, "Tab count should remain the same");
});

Expand All @@ -355,7 +358,7 @@ add_task(async function test_tab_reopen() {
const initial_tab_count = gBrowser.tabs.length;
const test_url = INPUT_TEST_FILE + "?reopen_test";

const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, test_url);
using tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, test_url);
is(gBrowser.tabs.length, initial_tab_count + 1, "New tab should be created");
is(current_url(), test_url, "New tab should have the test URL");

Expand Down
4 changes: 2 additions & 2 deletions src/glide/browser/base/content/test/hints/browser_hints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ add_task(async function test_F_opens_new_tab() {
is(GlideBrowser.state.mode, "normal", "Mode should return to 'normal' after following hint");

if (final_tab_count > initial_tab_count) {
gBrowser.removeTab(gBrowser.selectedTab);
gBrowser.removeTab(gBrowser.selectedTab!);
}
});
});
Expand Down Expand Up @@ -438,7 +438,7 @@ add_task(async function test_numeric_hint_generator() {
is(GlideBrowser.state.mode, "normal", "Mode should return to 'normal' after following hint");

if (final_tab_count > initial_tab_count) {
gBrowser.removeTab(gBrowser.selectedTab);
gBrowser.removeTab(gBrowser.selectedTab!);
}
});
});
Expand Down
Loading