commit 5dafdf8873c60bd49d3d905bdb44654502177409
parent 6deebe02578a7cc69dff2ec7686948f2f436f195
Author: Stephen Thompson <sthompson@mozilla.com>
Date: Wed, 19 Nov 2025 18:35:58 +0000
Bug 1994535 - tab note icon r=desktop-theme-reviewers,tabbrowser-reviewers,dao
Displays a tab note icon on a tab when a tab has the new `"tab-note"` attribute. There is no code connected to this yet; this is display only.
When there is enough space on the tab, the .tab-note-icon appears as a new element next to the close button at the inline end of the tab.
When space is constrained, the .tab-note-icon-overlay appears in the same place as existing media/crash indicators (.tab-icon-overlay). The tab notes icon takes lowest precedence.
Differential Revision: https://phabricator.services.mozilla.com/D271255
Diffstat:
5 files changed, 165 insertions(+), 18 deletions(-)
diff --git a/browser/components/tabbrowser/content/tab.js b/browser/components/tabbrowser/content/tab.js
@@ -27,6 +27,7 @@
<html:img class="tab-icon-image" role="presentation" decoding="sync" />
<image class="tab-sharing-icon-overlay" role="presentation"/>
<image class="tab-icon-overlay" role="presentation"/>
+ <image class="tab-note-icon-overlay" role="presentation"/>
</stack>
<html:moz-button type="icon ghost" size="small" class="tab-audio-button" tabindex="-1"></html:moz-button>
<vbox class="tab-label-container"
@@ -38,6 +39,7 @@
<label class="tab-icon-sound-label tab-icon-sound-pip-label" data-l10n-id="browser-tab-audio-pip" role="presentation"/>
</hbox>
</vbox>
+ <image class="tab-note-icon" role="presentation"/>
<image class="tab-close-button close-icon" role="button" data-l10n-id="tabbrowser-close-tabs-button" data-l10n-args='{"tabCount": 1}' keyNav="false"/>
</hbox>
</stack>
@@ -392,6 +394,20 @@
return null;
}
+ /**
+ * @returns {boolean}
+ */
+ get hasTabNote() {
+ return this.hasAttribute("tab-note");
+ }
+
+ /**
+ * @param {boolean} val
+ */
+ set hasTabNote(val) {
+ this.toggleAttribute("tab-note", val);
+ }
+
updateLastAccessed(aDate) {
this._lastAccessed = this.selected ? Infinity : aDate || Date.now();
}
diff --git a/browser/themes/shared/tabbrowser/tabs.css b/browser/themes/shared/tabbrowser/tabs.css
@@ -131,10 +131,13 @@
min-height: var(--tabstrip-min-height);
--tab-min-width: 76px;
+ --tab-min-width-extra-icons: 0px;
+ --tab-min-width-uidensity: 0px;
--tab-max-width: 225px;
- :root[uidensity="touch"] & .tabbrowser-tab[fadein]:not([pinned]) {
- /* Touch mode needs additional space for the close button. */
- min-width: max(var(--tab-min-width-pref, var(--tab-min-width)), 86px);
+
+ /* Touch mode needs additional space for the close button. */
+ :root[uidensity="touch"] & {
+ --tab-min-width-uidensity: 10px;
}
/* Make it easier to drag tabs by expanding the drag area downwards. */
@@ -205,13 +208,28 @@
/* Needed to avoid clipping the tab-background shadow, which has a 4px blur. */
overflow-clip-margin: var(--tab-overflow-clip-margin);
- &:not([pinned]) {
+ #tabbrowser-tabs[orient="horizontal"] &:not([pinned]) {
flex: 100 100;
max-width: var(--tab-max-width);
transition: var(--tab-width-transition);
- #tabbrowser-tabs[orient="horizontal"] &[fadein] {
- min-width: var(--tab-min-width-pref, var(--tab-min-width));
+ &:not([fadein]) {
+ max-width: 0.1px;
+ min-width: 0.1px;
+ visibility: hidden;
+ }
+
+ &[fadein] {
+ min-width: calc(var(--tab-min-width-pref, var(--tab-min-width)) + var(--tab-min-width-extra-icons) + var(--tab-min-width-uidensity));
+ &:is([muted], [soundplaying], [activemedia-blocked]):not([tab-note]) {
+ --tab-min-width-extra-icons: 24px;
+ }
+ &[tab-note]:not([muted], [soundplaying], [activemedia-blocked]) {
+ --tab-min-width-extra-icons: 24px;
+ }
+ &[tab-note]:is([muted], [soundplaying], [activemedia-blocked]) {
+ --tab-min-width-extra-icons: 48px;
+ }
}
}
@@ -230,12 +248,6 @@
}
}
- &:not([pinned], [fadein]) {
- max-width: 0.1px;
- min-width: 0.1px;
- visibility: hidden;
- }
-
#tabbrowser-tabs[movingtab] & {
position: relative;
}
@@ -268,10 +280,6 @@
removed from root such as when toggling the sidebar to expand with the toolbar button. */
&:is([muted], [soundplaying], [activemedia-blocked]) {
--tab-icon-end-margin: 2px;
-
- #tabbrowser-tabs[orient="horizontal"] &[fadein]:not([pinned]) {
- min-width: max(var(--tab-min-width-pref, var(--tab-min-width)), 100px);
- }
}
}
}
@@ -558,7 +566,8 @@
}
.tab-sharing-icon-overlay,
-.tab-icon-overlay {
+.tab-icon-overlay,
+.tab-note-icon-overlay {
display: none;
}
@@ -744,6 +753,72 @@
}
}
+/*
+ * Tab note icon that overlays the favicon for pinned tabs and the collapsed
+ * vertical tab strip. The horizontal tab strip and expanded vertical tab strip
+ * get a separate, more spacious treatment using the `.tab-notes-icon` image.
+ * Note: This is intended to display instead of the `.tab-icon-overlay`. This
+ * is defined separately from `.tab-icon-overlay` since the tab notes icon has
+ * fewer features (e.g. no icon interactivity) and
+ */
+.tab-note-icon-overlay {
+ position: relative;
+ padding: 2px;
+ top: -7px;
+ inset-inline-end: -7px;
+ z-index: 1; /* Overlay tab title */
+ list-style-image: url("chrome://global/skin/icons/tab-notes-12.svg");
+
+ background-color: var(--lwt-accent-color);
+ background-image: linear-gradient(transparent), linear-gradient(var(--toolbox-bgcolor));
+ -moz-context-properties: fill;
+ fill: var(--icon-color);
+ color-scheme: var(--tab-selected-color-scheme);
+ border-radius: var(--border-radius-circle);
+
+ .browser-toolbox-background:-moz-window-inactive .tabbrowser-tab:not([selected]) & {
+ background-image: linear-gradient(transparent), linear-gradient(var(--toolbox-bgcolor-inactive));
+ }
+ .tabbrowser-tab[selected] & {
+ background-image: linear-gradient(transparent), linear-gradient(var(--tab-selected-bgcolor)), linear-gradient(var(--toolbox-bgcolor));
+ }
+
+ .browser-toolbox-background:-moz-window-inactive .tabbrowser-tab[selected] & {
+ background-image: linear-gradient(transparent), linear-gradient(var(--tab-selected-bgcolor)), linear-gradient(var(--toolbox-bgcolor-inactive));
+ }
+
+ #tabbrowser-tabs[orient="vertical"] & {
+ top: 7px;
+ }
+
+ #tabbrowser-tabs[orient="vertical"]:not([expanded]) .tabbrowser-tab[tab-note]:not([crashed], [soundplaying], [muted], [activemedia-blocked]) &,
+ .tabbrowser-tab[pinned][tab-note]:not([crashed], [soundplaying], [muted], [activemedia-blocked]) & {
+ display: revert;
+ }
+
+ /* stylelint-disable-next-line media-query-no-invalid */
+ @media -moz-pref("sidebar.visibility", "expand-on-hover") {
+ /* We need these rules to apply at all times when the sidebar.visibility
+ pref is set to "expand-on-hover" as opposed to when the "sidebar-expand-on-hover" attribute
+ has been added to root. There are certain scenarios when that attribute is temporarily
+ removed from root such as when toggling the sidebar to expand with the toolbar button. */
+ #tabbrowser-tabs[orient="vertical"] .tabbrowser-tab[tab-note]:not([crashed], [soundplaying], [muted], [activemedia-blocked]) & {
+ display: revert;
+ }
+ }
+
+ /* stylelint-disable-next-line media-query-no-invalid */
+ @media not -moz-pref("sidebar.visibility", "expand-on-hover") {
+ /* We need these rules to apply at all times when the sidebar.visibility
+ pref is not set to "expand-on-hover" as opposed to when the "sidebar-expand-on-hover" attribute
+ has not been added to root. There are certain scenarios when that attribute is temporarily
+ removed from root such as when toggling the sidebar to expand with the toolbar button. */
+ #tabbrowser-tabs[orient="vertical"]:not([expanded]) .tabbrowser-tab[tab-note]:not([crashed], [soundplaying], [muted], [activemedia-blocked]) & {
+ display: revert;
+ }
+ }
+}
+
.tab-audio-button {
display: none;
margin-inline-end: var(--tab-icon-end-margin);
@@ -836,7 +911,7 @@
* the button has a visible outline shown */
@media not (forced-colors) {
#tabbrowser-tabs[orient="horizontal"] {
- .tabbrowser-tab:not([labelendaligned], :hover) > .tab-stack > .tab-content > .tab-close-button {
+ .tabbrowser-tab:not([labelendaligned], [tab-note], :hover) > .tab-stack > .tab-content > .tab-close-button {
padding-inline-start: 0;
width: 18px;
}
@@ -2445,6 +2520,49 @@ toolbar:not(#TabsToolbar) #firefox-view-button {
}
}
+/*
+ * Tab note icon that displays inline on unpinned tabs in the horizontal tab
+ * strip and the expanded vertical tab strip. Pinned tabs and the collapsed
+ * vertical tab strip get a separate space-constrained treatment using the
+ * `.tab-notes-icon-overlay` image.
+ */
+.tab-note-icon {
+ display: none;
+ box-sizing: border-box;
+
+ width: calc(var(--icon-size) + 8px);
+ height: calc(var(--icon-size) + 8px);
+ padding: 4px;
+ margin-inline-end: calc(var(--tab-inline-padding) / -2);
+
+ list-style-image: url("chrome://global/skin/icons/tab-notes.svg");
+ -moz-context-properties: fill;
+ fill: var(--icon-color);
+
+ #tabbrowser-tabs[orient="vertical"][expanded] & {
+ /*
+ * Keep the tab note icon out of the way of the close button, even when
+ * the close button is not showing because the tab is not being hovered.
+ */
+ .tabbrowser-tab:not(:hover) & {
+ margin-inline-end: calc(var(--icon-size) + 8px - var(--tab-close-button-padding));
+ }
+ .tabbrowser-tab:is([visuallyselected], :hover) & {
+ margin-inline-end: calc(var(--tab-close-button-padding) / -2);
+ }
+ }
+}
+
+/* stylelint-disable-next-line media-query-no-invalid */
+@media -moz-pref("browser.tabs.notes.enabled") and (not -moz-pref("sidebar.visibility", "expand-on-hover")) {
+ .tabbrowser-tab[tab-note]:not([pinned]) .tab-note-icon {
+ #tabbrowser-tabs[orient="horizontal"] &,
+ #tabbrowser-tabs[orient="vertical"][expanded] & {
+ display: revert;
+ }
+ }
+}
+
/* NOTE(emilio): The double class is an specificity hack to win over the
* .subviewbutton-iconic rules */
.tab-group-icon.tab-group-icon {
diff --git a/toolkit/themes/shared/desktop-jar.inc.mn b/toolkit/themes/shared/desktop-jar.inc.mn
@@ -115,6 +115,8 @@
skin/classic/global/icons/sort-arrow.svg (../../shared/icons/sort-arrow.svg)
skin/classic/global/icons/trending.svg (../../shared/icons/trending.svg)
skin/classic/global/icons/trophy.svg (../../shared/icons/trophy.svg)
+ skin/classic/global/icons/tab-notes-12.svg (../../shared/icons/tab-notes-12.svg)
+ skin/classic/global/icons/tab-notes.svg (../../shared/icons/tab-notes.svg)
skin/classic/global/icons/thumbs-down-20.svg (../../shared/icons/thumbs-down-20.svg)
skin/classic/global/icons/thumbs-up-20.svg (../../shared/icons/thumbs-up-20.svg)
skin/classic/global/icons/update-icon.svg (../../shared/icons/update-icon.svg)
diff --git a/toolkit/themes/shared/icons/tab-notes-12.svg b/toolkit/themes/shared/icons/tab-notes-12.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg width="12" height="12" viewBox="0 0 12 12" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.52441 1C10.3396 1.00022 10.9992 1.66089 11.001 2.47461V7.33789C11.001 7.49909 10.9387 7.65415 10.8271 7.77051L7.91016 10.8086C7.7923 10.9313 7.62915 11.001 7.45898 11.001H2.47559C1.66073 11.0008 1.00021 10.3403 1 9.52539V2.47559C1.00022 1.66074 1.66074 1.00022 2.47559 1H9.52441ZM2.47559 2.25C2.35109 2.25022 2.25022 2.35109 2.25 2.47559V9.52539C2.25021 9.6499 2.35109 9.75075 2.47559 9.75098H7V7.5C7 7.224 7.224 7 7.5 7H9.75098V2.47754L9.74609 2.43164C9.72477 2.32779 9.63331 2.2502 9.52441 2.25H2.47559Z"/>
+</svg>
diff --git a/toolkit/themes/shared/icons/tab-notes.svg b/toolkit/themes/shared/icons/tab-notes.svg
@@ -0,0 +1,4 @@
+<!-- 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/. -->
+<svg width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"><path d="m14.78 10.78-4 4a.747.747 0 0 1-.53.22H3a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v7.25c0 .199-.079.39-.22.53zm-1.28-.841V3c0-.275-.225-.5-.5-.5H3c-.275 0-.5.225-.5.5v10c0 .275.225.5.5.5h6.939l.061-.061V10.5a.5.5 0 0 1 .5-.5h2.939l.061-.061z"/></svg>
+\ No newline at end of file