author | Sarah Clements <sclements@mozilla.com> |
Sat, 15 Jun 2024 10:43:19 +0000 (13 months ago) | |
changeset 743140 | 1dc1daaeb25612bc4be8868dbd766f9566c6ac31 |
parent 743139 | 4132a83fedbd1b69b8c1613f6c13286364f3cc30 |
child 743141 | dc5e463eb0f570b9b89aef8cddd47f6659bdd16e |
push id | 41904 |
push user | nfay@mozilla.com |
push date | Sat, 15 Jun 2024 21:31:13 +0000 (13 months ago) |
treeherder | mozilla-central@d00c580fa44d [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | dao, sidebar-reviewers, desktop-theme-reviewers, tabbrowser-reviewers, Gijs |
bugs | 1893655 |
milestone | 129.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1897,16 +1897,17 @@ pref("pdfjs.previousHandler.alwaysAskBef // Try to convert PDFs sent as octet-stream pref("pdfjs.handleOctetStream", true); // Is the sidebar positioned ahead of the content browser pref("sidebar.position_start", true); pref("sidebar.revamp", false); pref("sidebar.main.tools", "history,syncedtabs"); +pref("sidebar.verticalTabs", false); pref("browser.ml.chat.enabled", false); pref("browser.ml.chat.prompt.prefix", 'I’m on page "%currentTabTitle%" with "%selection|12000%" selected. '); pref("browser.ml.chat.prompts.0", '{"label":"Summarize","value":"Please summarize the selection using precise and concise language. Highlight the main themes and conclusions. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning of the selection."}'); pref("browser.ml.chat.prompts.1", '{"label":"Simplify language","value":"Please rewrite the selection in plain, clear language suitable for a general audience without specialized knowledge. Use all of the following tactics: simple vocabulary; short sentences; active voice; examples where applicable to make explanations clearer; explanations for jargon and technical terms; headers and bulleted lists for scannability. Maintain factual accuracy while simplifying."}'); pref("browser.ml.chat.prompts.2", '{"label":"Quiz me","value":"Please create questions related to the selection. Ask the questions one by one. Wait for my response before moving on to the next question. Evaluate each response. Ask a variety of types of questions, like multiple choice, true or false and short answer."}'); pref("browser.ml.chat.provider", "");
--- a/browser/base/content/browser-box.inc.xhtml +++ b/browser/base/content/browser-box.inc.xhtml @@ -1,15 +1,18 @@ # 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/. <hbox flex="1" id="browser"> <box context="sidebar-context-menu" id="sidebar-main" hidden="true"> - <html:sidebar-main flex="1"></html:sidebar-main> + <html:sidebar-main flex="1"> + <html:div id="vertical-tabs" slot="tabstrip"> + </html:div> + </html:sidebar-main> </box> <vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome"> <box id="sidebar-header" align="center"> <toolbarbutton id="sidebar-switcher-target" class="tabbable" aria-expanded="false"> <image id="sidebar-icon" consumeanchor="sidebar-switcher-target"/> <label id="sidebar-title" crop="end" control="sidebar"/> <image id="sidebar-switcher-arrow"/> </toolbarbutton>
--- a/browser/components/sidebar/browser-sidebar.js +++ b/browser/components/sidebar/browser-sidebar.js @@ -239,16 +239,20 @@ var SidebarController = { }); if (this.sidebarRevampEnabled) { await import("chrome://browser/content/sidebar/sidebar-main.mjs"); document.getElementById("sidebar-main").hidden = !window.toolbar.visible; document.getElementById("sidebar-header").hidden = true; this._sidebarMain = document.querySelector("sidebar-main"); mainResizeObserver.observe(this._sidebarMain); + + if (this.sidebarVerticalTabsEnabled) { + this.toggleTabstrip(); + } } else { this._switcherTarget.addEventListener("command", () => { this.toggleSwitcherPanel(); }); this._switcherTarget.addEventListener("keydown", event => { this.handleKeydown(event); }); } @@ -1055,16 +1059,53 @@ var SidebarController = { menu.removeAttribute("checked"); if (triggerbutton) { triggerbutton.removeAttribute("checked"); updateToggleControlLabel(triggerbutton); } } } }, + + toggleTabstrip() { + let tabStrip = document.getElementById("tabbrowser-tabs"); + let arrowScrollbox = document.getElementById("tabbrowser-arrowscrollbox"); + let verticalTabs = document.getElementById("vertical-tabs"); + + let tabsToolbarWidgets = CustomizableUI.getWidgetIdsInArea("TabsToolbar"); + let tabstripPlacement = tabsToolbarWidgets.findIndex( + item => item == "tabbrowser-tabs" + ); + + if (this.sidebarVerticalTabsEnabled) { + arrowScrollbox.setAttribute("orient", "vertical"); + tabStrip.setAttribute("orient", "vertical"); + tabStrip.removeAttribute("overflow"); + tabStrip._positionPinnedTabs(); + verticalTabs.append(tabStrip); + } else { + arrowScrollbox.setAttribute("orient", "horizontal"); + tabStrip.removeAttribute("orient"); + + // make sure we put the tabstrip back in its original position in the TabsToolbar + if (tabstripPlacement < tabsToolbarWidgets.length) { + document + .getElementById("TabsToolbar-customization-target") + .insertBefore( + tabStrip, + document.getElementById(tabsToolbarWidgets[tabstripPlacement + 1]) + ); + } else { + document + .getElementById("TabsToolbar-customization-target") + .append(tabStrip); + } + } + verticalTabs.toggleAttribute("activated", this.sidebarVerticalTabsEnabled); + }, }; // Add getters related to the position here, since we will want them // available for both startDelayedLoad and init. XPCOMUtils.defineLazyPreferenceGetter( SidebarController, "_positionStart", SidebarController.POSITION_START_PREF, @@ -1078,8 +1119,15 @@ XPCOMUtils.defineLazyPreferenceGetter( false ); XPCOMUtils.defineLazyPreferenceGetter( SidebarController, "sidebarRevampTools", "sidebar.main.tools", "history, syncedtabs" ); +XPCOMUtils.defineLazyPreferenceGetter( + SidebarController, + "sidebarVerticalTabsEnabled", + "sidebar.verticalTabs", + false, + SidebarController.toggleTabstrip.bind(SidebarController) +);
--- a/browser/components/sidebar/sidebar-main.css +++ b/browser/components/sidebar/sidebar-main.css @@ -2,17 +2,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ .wrapper { display: grid; grid-template-rows: auto 1fr; box-sizing: border-box; height: 100%; - padding: var(--space-medium); + min-width: 50px; + padding-inline-start: var(--space-medium); border-inline-end: 1px solid var(--chrome-content-separator-color); background-color: var(--sidebar-background-color); color: var(--sidebar-text-color); :host([positionend]) & { border-inline-start: 1px solid var(--chrome-content-separator-color); border-inline-end: none; } }
--- a/browser/components/sidebar/sidebar-main.mjs +++ b/browser/components/sidebar/sidebar-main.mjs @@ -15,17 +15,17 @@ import "chrome://global/content/elements /** * Sidebar with expanded and collapsed states that provides entry points * to various sidebar panels and sidebar extensions. */ export default class SidebarMain extends MozLitElement { static properties = { bottomActions: { type: Array }, - expanded: { type: Boolean }, + expanded: { type: Boolean, reflect: true }, selectedView: { type: String }, sidebarItems: { type: Array }, open: { type: Boolean }, }; static queries = { allButtons: { all: "moz-button" }, extensionButtons: { all: ".tools-and-extensions > moz-button[extension]" }, @@ -82,19 +82,23 @@ export default class SidebarMain extends window.removeEventListener("SidebarItemChanged", this); window.removeEventListener("SidebarItemRemoved", this); } onSidebarPopupShowing(event) { // Store the context menu target which holds the id required for managing sidebar items this.contextMenuTarget = event.explicitOriginalTarget.flattenedTreeParentNode; - if (!this.contextMenuTarget.getAttribute("extensionId")) { - event.preventDefault(); + if ( + this.contextMenuTarget.getAttribute("extensionId") || + this.contextMenuTarget.className.includes("tab") + ) { + return; } + event.preventDefault(); } async manageExtension() { await window.BrowserAddonUI.manageAddon( this.contextMenuTarget.getAttribute("extensionId"), "sidebar-context-menu" ); } @@ -214,16 +218,17 @@ export default class SidebarMain extends render() { return html` <link rel="stylesheet" href="chrome://browser/content/sidebar/sidebar-main.css" /> <div class="wrapper"> + <slot name="tabstrip"></slot> <button-group class="tools-and-extensions actions-list" orientation="vertical" > ${repeat( this.getToolsAndExtensions().values(), action => action.view, action => this.entrypointTemplate(action)
--- a/browser/components/sidebar/tests/browser/browser.toml +++ b/browser/components/sidebar/tests/browser/browser.toml @@ -1,14 +1,14 @@ [DEFAULT] support-files = ["head.js"] -["browser_adopt_sidebar_from_opener.js"] +["browser_a11y_sidebar.js"] -["browser_a11y_sidebar.js"] +["browser_adopt_sidebar_from_opener.js"] ["browser_customize_sidebar.js"] ["browser_domfullscreen_sidebar.js"] ["browser_extensions_sidebar.js"] ["browser_hide_sidebar_on_popup.js"] @@ -18,9 +18,11 @@ support-files = ["head.js"] ["browser_sidebar_context_menu.js"] ["browser_sidebar_max_width.js"] ["browser_sidebar_prefs.js"] ["browser_toolbar_sidebar_button.js"] +["browser_vertical_tabs.js"] + ["browser_view_sidebar_menu.js"]
new file mode 100644 --- /dev/null +++ b/browser/components/sidebar/tests/browser/browser_vertical_tabs.js @@ -0,0 +1,91 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_setup(() => + SpecialPowers.pushPrefEnv({ + set: [ + ["sidebar.revamp", true], + ["sidebar.verticalTabs", false], + ], + }) +); +registerCleanupFunction(() => SpecialPowers.popPrefEnv()); + +add_task(async function test_toggle_vertical_tabs() { + const win = await BrowserTestUtils.openNewBrowserWindow(); + await waitForBrowserWindowActive(win); + const { document } = win; + const sidebar = document.querySelector("sidebar-main"); + ok(sidebar, "Sidebar is shown."); + + let tabStrip = document.getElementById("tabbrowser-tabs"); + let defaultTabstripParent = document.getElementById( + "TabsToolbar-customization-target" + ); + let verticalTabs = document.querySelector("#vertical-tabs"); + ok( + !BrowserTestUtils.isVisible(verticalTabs), + "Vertical tabs slot is not visible" + ); + + is( + tabStrip.parentNode, + defaultTabstripParent, + "Tabstrip is in default horizontal position" + ); + is( + tabStrip.nextElementSibling.id, + "new-tab-button", + "Tabstrip is before the new tab button" + ); + + // flip the pref to move the tabstrip into the sidebar + await SpecialPowers.pushPrefEnv({ set: [["sidebar.verticalTabs", true]] }); + ok(BrowserTestUtils.isVisible(verticalTabs), "Vertical tabs slot is visible"); + is( + tabStrip.parentNode, + verticalTabs, + "Tabstrip is slotted into the sidebar vertical tabs container" + ); + is(win.gBrowser.tabs.length, 1, "Tabstrip now has one tab"); + + // make sure the tab context menu still works + const contextMenu = document.getElementById("tabContextMenu"); + win.gBrowser.selectedTab.focus(); + EventUtils.synthesizeMouseAtCenter( + win.gBrowser.selectedTab, + { + type: "contextmenu", + button: 2, + }, + win + ); + + await openAndWaitForContextMenu(contextMenu, win.gBrowser.selectedTab, () => { + document.getElementById("context_openANewTab").click(); + }); + + is(win.gBrowser.tabs.length, 2, "Tabstrip now has two tabs"); + + // flip the pref to move the tabstrip horizontally + await SpecialPowers.pushPrefEnv({ set: [["sidebar.verticalTabs", false]] }); + + ok( + !BrowserTestUtils.isVisible(verticalTabs), + "Vertical tabs slot is not visible" + ); + is( + tabStrip.parentNode, + defaultTabstripParent, + "Tabstrip is in default horizontal position" + ); + is( + tabStrip.nextElementSibling.id, + "new-tab-button", + "Tabstrip is before the new tab button" + ); + + await BrowserTestUtils.closeWindow(win); +});
--- a/browser/components/tabbrowser/content/tabs.js +++ b/browser/components/tabbrowser/content/tabs.js @@ -1177,17 +1177,18 @@ ); arrowScrollbox.shadowRoot.addEventListener("overflow", event => { // Ignore overflow events: // - from nested scrollable elements // - for vertical orientation if ( event.originalTarget != arrowScrollbox.scrollbox || - event.detail == 0 + event.detail == 0 || + event.originalTarget.getAttribute("orient") == "vertical" ) { return; } this.toggleAttribute("overflow", true); this._positionPinnedTabs(); this._updateCloseButtons(); this._handleTabSelect(true);
--- a/browser/themes/shared/tabbrowser/tabs.css +++ b/browser/themes/shared/tabbrowser/tabs.css @@ -495,17 +495,17 @@ margin-inline-end: calc(var(--inline-tab-padding) / -2); width: 24px; height: 24px; padding: 6px; border-radius: var(--tab-border-radius); list-style-image: url(chrome://global/skin/icons/close-12.svg); &[pinned], - #tabbrowser-tabs[closebuttons="activetab"] > #tabbrowser-arrowscrollbox > .tabbrowser-tab > .tab-stack > .tab-content > &:not([selected]) { + #tabbrowser-tabs[closebuttons="activetab"]:not([orient="vertical"]) > #tabbrowser-arrowscrollbox > .tabbrowser-tab > .tab-stack > .tab-content > &:not([selected]) { display: none; } } /* The following rulesets allow showing more of the tab title */ .tabbrowser-tab:not([labelendaligned], :hover) > .tab-stack > .tab-content > .tab-close-button { padding-inline-start: 0; width: 18px; @@ -715,16 +715,51 @@ &:not([scrolledtostart=true])::part(scrollbutton-up):hover:active, &:not([scrolledtoend=true])::part(scrollbutton-down):hover:active { background-color: var(--toolbarbutton-active-background); color: inherit; } } +/* Vertical tabs styling */ +#tabbrowser-arrowscrollbox[orient="vertical"] { + overflow-y: auto; + + &::part(scrollbutton-up), + &::part(scrollbutton-down) { + display: none; + } + + &::part(scrollbox-clip) { + min-height: inherit; + } +} + +#vertical-tabs { + overflow-y: hidden; + scrollbar-width: thin; + display: none; + + &[activated] { + display: flex; + } +} + +sidebar-main:not([expanded]) > #vertical-tabs > #tabbrowser-tabs[orient="vertical"] .tabbrowser-tab { + /* TODO look into handlings this by setting --tab-min-width in tabs.js in bug 1899336. */ + min-width: inherit; + width: inherit; +} + +sidebar-main:not([expanded]) > #vertical-tabs > #tabbrowser-tabs[orient="vertical"] .tab-close-button, +sidebar-main[expanded] > #vertical-tabs > #tabbrowser-tabs[orient="vertical"] .tab-close-button:not([selected]) { + display: none; +} + /* Tab drag and drop */ .tab-drop-indicator { width: 12px; margin-inline-start: -12px; background: url(chrome://browser/skin/tabbrowser/tab-drag-indicator.svg) no-repeat center; position: relative; z-index: 2;
--- a/toolkit/content/widgets/arrowscrollbox.js +++ b/toolkit/content/widgets/arrowscrollbox.js @@ -25,17 +25,17 @@ <spacer part="overflow-start-indicator"/> <box class="scrollbox-clip" part="scrollbox-clip" flex="1"> <scrollbox part="scrollbox" flex="1"> <html:slot/> </scrollbox> </box> <spacer part="overflow-end-indicator"/> <toolbarbutton id="scrollbutton-down" part="scrollbutton-down" keyNav="false" data-l10n-id="overflow-scroll-button-forwards"/> - `; + `; } constructor() { super(); this.attachShadow({ mode: "open" }); this.shadowRoot.appendChild(this.fragment); this.scrollbox = this.shadowRoot.querySelector("scrollbox"); @@ -126,31 +126,32 @@ this.scrollbox.addEventListener("scrollend", event => { this.on_scrollend(event); this.dispatchEvent(new Event("scrollend")); }); } connectedCallback() { + this.removeAttribute("overflowing"); + if (this.hasConnected) { return; } this.hasConnected = true; document.l10n.connectRoot(this.shadowRoot); if (!this.hasAttribute("smoothscroll")) { this.smoothScroll = Services.prefs.getBoolPref( "toolkit.scrollbox.smoothScroll", true ); } - this.removeAttribute("overflowing"); this.initializeAttributeInheritance(); this._updateScrollButtonsDisabledState(); } get fragment() { if (!this.constructor.hasOwnProperty("_fragment")) { this.constructor._fragment = MozXULElement.parseXULToFragment( this.markup