Skip to content

Commit d21c872

Browse files
feat(api): add an option for showing / hiding the native tab bar (#156)
Co-authored-by: Robert Craigie <robert@craigie.dev>
1 parent f7899b0 commit d21c872

10 files changed

Lines changed: 329 additions & 0 deletions

File tree

@types/gecko.d.mts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ declare namespace MockedExports {
219219
"chrome://glide/content/utils/objects.mjs": typeof import("../src/glide/browser/base/content/utils/objects.mts");
220220
"chrome://glide/content/utils/strings.mjs": typeof import("../src/glide/browser/base/content/utils/strings.mts");
221221
"chrome://glide/content/utils/promises.mjs": typeof import("../src/glide/browser/base/content/utils/promises.mts");
222+
"chrome://glide/content/utils/browser-ui.mjs":
223+
typeof import("../src/glide/browser/base/content/utils/browser-ui.mts");
222224
"chrome://glide/content/utils/resources.mjs":
223225
typeof import("../src/glide/browser/base/content/utils/resources.mts");
224226
"chrome://glide/content/browser.mjs": typeof import("../src/glide/browser/base/content/browser.mts");

scripts/polyfill-chromeutils.cjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ globalThis.ChromeUtils = {
5656
return a_require(`${SRC_DIR}/glide/browser/base/content/utils/resources.mts`);
5757
case "chrome://glide/content/utils/ipc.mjs":
5858
return a_require(`${SRC_DIR}/glide/browser/base/content/utils/ipc.mts`);
59+
case "chrome://glide/content/utils/browser-ui.mjs":
60+
return a_require(`${SRC_DIR}/glide/browser/base/content/utils/browser-ui.mts`);
5961
case "chrome://glide/content/please.mjs":
6062
return a_require(`${SRC_DIR}/glide/browser/base/content/please.mts`);
6163
case "chrome://glide/content/event-utils.mjs":

src/glide/browser/base/content/browser-api.mts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const CommandLine = ChromeUtils.importESModule("chrome://glide/content/browser-c
1111
const Strings = ChromeUtils.importESModule("chrome://glide/content/utils/strings.mjs");
1212
const DOM = ChromeUtils.importESModule("chrome://glide/content/utils/dom.mjs", { global: "current" });
1313
const IPC = ChromeUtils.importESModule("chrome://glide/content/utils/ipc.mjs");
14+
const CSS = ChromeUtils.importESModule("chrome://glide/content/utils/browser-ui.mjs");
1415
const { ensure, assert_never, assert_present, is_present } = ChromeUtils.importESModule(
1516
"chrome://glide/content/utils/guards.mjs",
1617
);
@@ -75,6 +76,28 @@ class GlideOptions implements GlideO {
7576
set hint_label_generator(value: glide.Options["hint_label_generator"]) {
7677
this.#hint_label_generator = value;
7778
}
79+
80+
#native_tabs: (typeof glide)["o"]["native_tabs"] = "show";
81+
get native_tabs() {
82+
return this.#native_tabs;
83+
}
84+
set native_tabs(value: (typeof glide)["o"]["native_tabs"]) {
85+
const id = "$glide.o.native_tabs";
86+
GlideBrowser.api.styles.remove(id);
87+
this.#native_tabs = value;
88+
switch (value) {
89+
case "hide":
90+
GlideBrowser.api.styles.add(CSS.hide_tabs_toolbar_v2, { id });
91+
break;
92+
case "autohide":
93+
GlideBrowser.api.styles.add(CSS.autohide_tabstoolbar_v2, { id });
94+
break;
95+
case "show":
96+
break;
97+
default:
98+
throw assert_never(value);
99+
}
100+
}
78101
}
79102

80103
export function make_glide_api(

src/glide/browser/base/content/glide.d.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,34 @@ declare global {
11081108
* @default "keys"
11091109
*/
11101110
scroll_implementation: "keys" | "legacy";
1111+
1112+
/**
1113+
* Configure the behavior of the native tab bar.
1114+
*
1115+
* - `show`
1116+
* - `hide`
1117+
* - `autohide` (animated) shows the bar when the cursor is hovering over its default position
1118+
*
1119+
* This works for both horizontal and vertical tabs.
1120+
*
1121+
* For **vertical** tabs, the default collapsed width can be adjusted like this:
1122+
* ```typescript
1123+
* glide.o.native_tabs = "autohide";
1124+
* // fully collapse vertical tabs
1125+
* glide.styles.add(css`
1126+
* :root {
1127+
* --uc-tab-collapsed-width: 2px;
1128+
* }
1129+
* `);
1130+
* ```
1131+
*
1132+
* See [firefox-csshacks](https://mrotherguy.github.io/firefox-csshacks/?file=autohide_tabstoolbar_v2.css) for more information.
1133+
*
1134+
* **warning**: `autohide` does not work on MacOS at the moment.
1135+
*
1136+
* @default "show"
1137+
*/
1138+
native_tabs: "show" | "hide" | "autohide";
11111139
}
11121140

11131141
/**
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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/.
4+
5+
[DEFAULT]
6+
support-files = []
7+
8+
["dist/browser_native_tabs.js"]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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/.
4+
5+
/* Any copyright is dedicated to the Public Domain.
6+
* https://creativecommons.org/publicdomain/zero/1.0/ */
7+
8+
"use strict";
9+
10+
add_task(async function test_native_tabs() {
11+
const navigator_toolbox = document!.getElementById("navigator-toolbox");
12+
ok(navigator_toolbox, "Element 'navigator-toolbox' should exist.");
13+
14+
await GlideTestUtils.reload_config(() => {});
15+
const height_default = navigator_toolbox!.clientHeight;
16+
17+
await GlideTestUtils.reload_config(function _() {
18+
glide.o.native_tabs = "show";
19+
});
20+
const height_show = navigator_toolbox!.clientHeight;
21+
is(height_default, height_show, "glide.o.native_tabs 'show' option should keep initial toolbox dimensions.");
22+
23+
await GlideTestUtils.reload_config(function _() {
24+
glide.o.native_tabs = "hide";
25+
});
26+
const height_hide = navigator_toolbox!.clientHeight;
27+
Assert.greater(height_default, height_hide, "glide.o.native_tabs 'hide' option should shrink the toolbox height.");
28+
29+
await GlideTestUtils.reload_config(function _() {
30+
glide.o.native_tabs = "autohide";
31+
});
32+
await waiter(() => {
33+
const height_autohide = navigator_toolbox!.clientHeight;
34+
return height_default > height_autohide && height_autohide > height_hide;
35+
}).ok("glide.o.native_tabs 'autohide' toolbox height should be in range 'show' - 'hide'.");
36+
37+
await GlideTestUtils.reload_config(() => {});
38+
const height_reset_default = navigator_toolbox!.clientHeight;
39+
is(height_default, height_reset_default, "Resetting the config should yield the default window height");
40+
});
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5+
const { css } = ChromeUtils.importESModule("chrome://glide/content/utils/dedent.mjs");
6+
7+
/*
8+
* Contains CSS snippets for manipulating the Browser UI.
9+
* Obtained from here: https://github.com/MrOtherGuy/firefox-csshacks
10+
*/
11+
12+
export const autohide_tabstoolbar_v2 = css`
13+
/* Source file https://github.com/MrOtherGuy/firefox-csshacks/tree/master/chrome/autohide_tabstoolbar_v2.css made available under Mozilla Public License v. 2.0
14+
See the above repository for updates as well as full license text. */
15+
16+
/* Requires Firefox 133 */
17+
18+
:root {
19+
--uc-tabs-hide-animation-duration: 48ms;
20+
--uc-tabs-hide-animation-delay: 200ms;
21+
/* Modification keeping the default behavior and setting a custom collapse width */
22+
--uc-tab-collapsed-width: var(--tab-collapsed-width);
23+
}
24+
25+
@media -moz-pref("sidebar.verticalTabs") {
26+
#sidebar-main {
27+
overflow: visible !important;
28+
max-width: var(--uc-tab-collapsed-width) !important;
29+
z-index: var(--browser-area-z-index-toolbox-while-animating);
30+
transition: z-index 0s linear var(--uc-tabs-hide-duration);
31+
background: inherit;
32+
}
33+
sidebar-main {
34+
--tab-pinned-horizontal-count: 5; /* This needs to match whatever is used in sidebar-main.css - currently 5 */
35+
background: inherit;
36+
overflow: hidden;
37+
min-width: var(--uc-tab-collapsed-width);
38+
transition: min-width var(--uc-tabs-hide-animation-duration) ease-out
39+
var(--uc-tabs-hide-animation-delay);
40+
border-inline: 0.01px solid var(--chrome-content-separator-color);
41+
border-inline-width: 0 0.01px;
42+
&:is([sidebar-positionend], [positionend]) {
43+
transition-property: min-width, transform;
44+
border-inline-width: 0.01px 0;
45+
}
46+
}
47+
:where(#navigator-toolbox[movingtab] + #browser > #sidebar-main)
48+
> sidebar-main[expanded],
49+
sidebar-main[expanded]:hover {
50+
min-width: calc(
51+
var(--tab-pinned-horizontal-count) *
52+
var(--tab-pinned-min-width-expanded) + 2 *
53+
var(--tab-pinned-container-margin-inline-expanded) + 1px
54+
);
55+
transition-delay: 0ms !important;
56+
&:is([sidebar-positionend], [positionend]) {
57+
transform: translateX(calc(var(--tab-collapsed-width) - 100%));
58+
}
59+
}
60+
#sidebar-wrapper {
61+
background: inherit;
62+
}
63+
}
64+
@media not -moz-pref("sidebar.verticalTabs") {
65+
:root:not([customizing], [chromehidden~="menubar"])
66+
#navigator-toolbox:has(> :is(#toolbar-menubar, #TabsToolbar):hover),
67+
:root:not([customizing], [chromehidden~="menubar"]) #TabsToolbar {
68+
margin-bottom: calc(
69+
0px - 2 * var(--tab-block-margin) - var(--tab-min-height)
70+
);
71+
}
72+
#toolbar-menubar:is([autohide=""], [autohide="true"])
73+
+ #TabsToolbar:not(:hover) {
74+
-moz-window-dragging: no-drag !important;
75+
}
76+
#toolbar-menubar:is([autohide=""], [autohide="true"])
77+
+ #TabsToolbar:not(:hover, [customizing])::before {
78+
content: "";
79+
display: flex;
80+
position: absolute;
81+
height: 6px;
82+
width: 100vw;
83+
visibility: visible;
84+
}
85+
#navigator-toolbox {
86+
transition: margin-bottom var(--uc-tabs-hide-animation-duration) ease-out
87+
var(--uc-tabs-hide-animation-delay) !important;
88+
--browser-area-z-index-toolbox: 3;
89+
}
90+
#TabsToolbar:not([customizing]) {
91+
visibility: hidden;
92+
position: relative;
93+
z-index: 1;
94+
transition:
95+
visibility 0ms linear var(--uc-tabs-hide-animation-delay),
96+
margin-bottom var(--uc-tabs-hide-animation-duration) ease-out
97+
var(--uc-tabs-hide-animation-delay) !important;
98+
}
99+
#mainPopupSet:has(
100+
> #tab-group-editor > [panelopen],
101+
> #tabgroup-preview-panel[panelopen]
102+
)
103+
~ #navigator-toolbox,
104+
#navigator-toolbox:has(> :is(#toolbar-menubar, #TabsToolbar):hover),
105+
#navigator-toolbox[movingtab] {
106+
transition-delay: 0s !important;
107+
margin-bottom: calc(
108+
0px - 2 * var(--tab-block-margin) - var(--tab-min-height)
109+
);
110+
> #TabsToolbar {
111+
visibility: visible;
112+
margin-bottom: 0px;
113+
transition-delay: 0ms, 0ms !important;
114+
}
115+
}
116+
@media -moz-pref("userchrome.autohidetabs.show-while-inactive.enabled") {
117+
#navigator-toolbox:-moz-window-inactive {
118+
margin-bottom: calc(
119+
0px - 2 * var(--tab-block-margin) - var(--tab-min-height)
120+
);
121+
> #TabsToolbar {
122+
visibility: visible;
123+
margin-bottom: 0px;
124+
}
125+
}
126+
}
127+
/* These rules make sure that height of tabs toolbar doesn't exceed tab-min-height */
128+
#tabbrowser-tabs:not([secondarytext-unsupported]) .tab-label-container {
129+
max-height: var(--tab-min-height);
130+
}
131+
.tab-label {
132+
line-height: 20px !important;
133+
}
134+
:root[uidensity="compact"] .tab-label {
135+
line-height: 18px !important;
136+
}
137+
}
138+
`;
139+
140+
export const hide_tabs_toolbar_v2 = css`
141+
/* Source file https://github.com/MrOtherGuy/firefox-csshacks/tree/master/chrome/hide_tabs_toolbar_v2.css made available under Mozilla Public License v. 2.0
142+
See the above repository for updates as well as full license text. */
143+
144+
/* This requires Firefox 133+ to work */
145+
146+
@media -moz-pref("sidebar.verticalTabs") {
147+
#sidebar-launcher-splitter,
148+
#sidebar-main {
149+
visibility: collapse;
150+
}
151+
}
152+
@media -moz-pref("userchrome.force-window-controls-on-left.enabled") {
153+
#nav-bar > .titlebar-buttonbox-container {
154+
order: -1 !important;
155+
> .titlebar-buttonbox {
156+
flex-direction: row-reverse;
157+
}
158+
}
159+
}
160+
@media not -moz-pref("sidebar.verticalTabs") {
161+
#TabsToolbar:not([customizing]) {
162+
visibility: collapse;
163+
}
164+
:root[sizemode="fullscreen"] #nav-bar > .titlebar-buttonbox-container {
165+
display: flex !important;
166+
}
167+
:root[customtitlebar]
168+
#toolbar-menubar:is([autohide=""], [autohide="true"])
169+
~ #nav-bar {
170+
> .titlebar-buttonbox-container {
171+
display: flex !important;
172+
}
173+
:root[sizemode="normal"] & {
174+
> .titlebar-spacer {
175+
display: flex !important;
176+
}
177+
}
178+
:root[sizemode="maximized"] & {
179+
> .titlebar-spacer[type="post-tabs"] {
180+
display: flex !important;
181+
}
182+
@media -moz-pref("userchrome.force-window-controls-on-left.enabled"),
183+
(-moz-gtk-csd-reversed-placement),
184+
(-moz-platform: macos) {
185+
> .titlebar-spacer[type="post-tabs"] {
186+
display: none !important;
187+
}
188+
> .titlebar-spacer[type="pre-tabs"] {
189+
display: flex !important;
190+
}
191+
}
192+
}
193+
}
194+
}
195+
`;

src/glide/browser/base/jar.mn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ glide.jar:
4242
content/utils/strings.mjs (content/utils/dist/strings.mjs)
4343
content/utils/promises.mjs (content/utils/dist/promises.mjs)
4444
content/utils/resources.mjs (content/utils/dist/resources.mjs)
45+
content/utils/browser-ui.mjs (content/utils/dist/browser-ui.mjs)
4546

4647
# default plugins
4748
content/plugins/shims.mjs (content/plugins/dist/shims.mjs)

src/glide/browser/base/moz.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ BROWSER_CHROME_MANIFESTS += [
2424
"content/test/scrolling/browser.toml",
2525
"content/test/split-views/browser.toml",
2626
"content/test/tutor/browser.toml",
27+
"content/test/ui/browser.toml",
2728
"content/test/utils/browser.toml",
2829
]

src/glide/docs/api.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ text-decoration: none;
5050
[`glide.o.hint_label_generator`](#glide.o.hint_label_generator)\
5151
[`glide.o.switch_mode_on_focus`](#glide.o.switch_mode_on_focus)\
5252
[`glide.o.scroll_implementation`](#glide.o.scroll_implementation)\
53+
[`glide.o.native_tabs`](#glide.o.native_tabs)\
5354
[`glide.bo`](#glide.bo)\
5455
[`glide.options`](#glide.options)\
5556
[`glide.options.get()`](#glide.options.get)\
@@ -331,6 +332,34 @@ This will be removed in the future when the kinks with the `keys` implementation
331332

332333
`ts:@default "keys"`
333334

335+
### `glide.o.native_tabs` {% id="glide.o.native_tabs" %}
336+
337+
Configure the behavior of the native tab bar.
338+
339+
- `show`
340+
- `hide`
341+
- `autohide` (animated) shows the bar when the cursor is hovering over its default position
342+
343+
This works for both horizontal and vertical tabs.
344+
345+
For **vertical** tabs, the default collapsed width can be adjusted like this:
346+
347+
```typescript
348+
glide.o.native_tabs = "autohide";
349+
// fully collapse vertical tabs
350+
glide.styles.add(css`
351+
:root {
352+
--uc-tab-collapsed-width: 2px;
353+
}
354+
`);
355+
```
356+
357+
See [firefox-csshacks](https://mrotherguy.github.io/firefox-csshacks/?file=autohide_tabstoolbar_v2.css) for more information.
358+
359+
**warning**: `autohide` does not work on MacOS at the moment.
360+
361+
`ts:@default "show"`
362+
334363
## `glide.bo: Partial<glide.Options>` {% id="glide.bo" %}
335364

336365
Set buffer specific options.

0 commit comments

Comments
 (0)