diff --git a/src/browser/components/tabbrowser/content/tabs-js.patch b/src/browser/components/tabbrowser/content/tabs-js.patch index 03038c82c..7a101841a 100644 --- a/src/browser/components/tabbrowser/content/tabs-js.patch +++ b/src/browser/components/tabbrowser/content/tabs-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js -index ef9c0389ec926e6bc01c0dc3b883beceaf1f7d43..1a3aa415d08379b00960de398808e771c299f2ca 100644 +index ef9c0389ec926e6bc01c0dc3b883beceaf1f7d43..3001dd54fccfcac3b96288dd769fbaa16cb76ffb 100644 --- a/browser/components/tabbrowser/content/tabs.js +++ b/browser/components/tabbrowser/content/tabs.js @@ -83,7 +83,7 @@ @@ -229,7 +229,7 @@ index ef9c0389ec926e6bc01c0dc3b883beceaf1f7d43..1a3aa415d08379b00960de398808e771 } + let glanceTab = child.querySelector("tab[zen-glance-tab]"); + if (isTab(child) && glanceTab) { -+ glanceTab.elementIndex = elementIndex++; ++ glanceTab.elementIndex = elementIndex - 1; + focusableItems.push(glanceTab); + } } diff --git a/src/zen/glance/ZenGlanceManager.mjs b/src/zen/glance/ZenGlanceManager.mjs index 2960ef291..e28d2106a 100644 --- a/src/zen/glance/ZenGlanceManager.mjs +++ b/src/zen/glance/ZenGlanceManager.mjs @@ -84,7 +84,7 @@ } getTabPosition(tab) { - return Math.max(gBrowser.pinnedTabCount, tab._tPos); + return tab._tPos; } createBrowserElement(url, currentTab, existingTab = null) { @@ -171,66 +171,69 @@ this.overlay.classList.add('zen-glance-overlay'); this.browserWrapper.removeAttribute('animate-end'); - window.requestAnimationFrame(() => { - this.quickOpenGlance({ dontOpenButtons: true }); - this.showSidebarButtons(true); + return new Promise((resolve) => { + window.requestAnimationFrame(() => { + this.quickOpenGlance({ dontOpenButtons: true }); + this.showSidebarButtons(true); - gZenUIManager.motion.animate( - this.#currentParentTab.linkedBrowser.closest('.browserSidebarContainer'), - { - scale: [1, 0.98], - backdropFilter: ['blur(0px)', 'blur(5px)'], - opacity: [1, 0.5], - }, - { - duration: 0.4, - type: 'spring', - bounce: 0.2, - } - ); - this.#currentBrowser.setAttribute('animate-glance-open', true); - this.overlay.removeAttribute('fade-out'); - this.browserWrapper.setAttribute('animate', true); - const top = initialY + initialHeight / 2; - const left = initialX + initialWidth / 2; - this.browserWrapper.style.top = `${top}px`; - this.browserWrapper.style.left = `${left}px`; - this.browserWrapper.style.width = `${initialWidth}px`; - this.browserWrapper.style.height = `${initialHeight}px`; - this.browserWrapper.style.opacity = 0.8; - this.#glances.get(this.#currentGlanceID).originalPosition = { - top: this.browserWrapper.style.top, - left: this.browserWrapper.style.left, - width: this.browserWrapper.style.width, - height: this.browserWrapper.style.height, - }; - this.browserWrapper.style.transform = 'translate(-50%, -50%)'; - this.overlay.style.overflow = 'visible'; - gZenUIManager.motion - .animate( - this.browserWrapper, + gZenUIManager.motion.animate( + this.#currentParentTab.linkedBrowser.closest('.browserSidebarContainer'), { - top: '50%', - left: '50%', - width: '85%', - height: '100%', - opacity: 1, + scale: [1, 0.98], + backdropFilter: ['blur(0px)', 'blur(5px)'], + opacity: [1, 0.5], }, { - duration: 0.3, + duration: 0.4, type: 'spring', bounce: 0.2, } - ) - .then(() => { - this.#currentBrowser.removeAttribute('animate-glance-open'); - this.overlay.style.removeProperty('overflow'); - this.browserWrapper.removeAttribute('animate'); - this.browserWrapper.setAttribute('animate-end', true); - this.browserWrapper.setAttribute('has-finished-animation', true); - this._animating = false; - this.animatingOpen = false; - }); + ); + this.#currentBrowser.setAttribute('animate-glance-open', true); + this.overlay.removeAttribute('fade-out'); + this.browserWrapper.setAttribute('animate', true); + const top = initialY + initialHeight / 2; + const left = initialX + initialWidth / 2; + this.browserWrapper.style.top = `${top}px`; + this.browserWrapper.style.left = `${left}px`; + this.browserWrapper.style.width = `${initialWidth}px`; + this.browserWrapper.style.height = `${initialHeight}px`; + this.browserWrapper.style.opacity = 0.8; + this.#glances.get(this.#currentGlanceID).originalPosition = { + top: this.browserWrapper.style.top, + left: this.browserWrapper.style.left, + width: this.browserWrapper.style.width, + height: this.browserWrapper.style.height, + }; + this.browserWrapper.style.transform = 'translate(-50%, -50%)'; + this.overlay.style.overflow = 'visible'; + gZenUIManager.motion + .animate( + this.browserWrapper, + { + top: '50%', + left: '50%', + width: '85%', + height: '100%', + opacity: 1, + }, + { + duration: 0.3, + type: 'spring', + bounce: 0.2, + } + ) + .then(() => { + this.#currentBrowser.removeAttribute('animate-glance-open'); + this.overlay.style.removeProperty('overflow'); + this.browserWrapper.removeAttribute('animate'); + this.browserWrapper.setAttribute('animate-end', true); + this.browserWrapper.setAttribute('has-finished-animation', true); + this._animating = false; + this.animatingOpen = false; + resolve(this.#currentTab); + }); + }); }); } @@ -320,69 +323,73 @@ ); }); this.browserWrapper.style.opacity = 1; - gZenUIManager.motion - .animate( - this.browserWrapper, - { - ...originalPosition, - opacity: 0, - }, - { type: 'spring', bounce: 0, duration: 0.5, easing: 'ease-in' } - ) - .then(() => { - this.browserWrapper.removeAttribute('animate'); - this.browserWrapper.removeAttribute('animate-end'); - if (!this.#currentParentTab) { - return; - } + return new Promise((resolve) => { + gZenUIManager.motion + .animate( + this.browserWrapper, + { + ...originalPosition, + opacity: 0, + }, + { type: 'spring', bounce: 0, duration: 0.5, easing: 'ease-in' } + ) + .then(() => { + this.browserWrapper.removeAttribute('animate'); + this.browserWrapper.removeAttribute('animate-end'); + if (!this.#currentParentTab) { + return; + } - if (!onTabClose || quikcCloseZen) { - this.quickCloseGlance({ clearID: false }); - } - this.overlay.removeAttribute('fade-out'); - this.browserWrapper.removeAttribute('animate'); + if (!onTabClose || quikcCloseZen) { + this.quickCloseGlance({ clearID: false }); + } + this.overlay.removeAttribute('fade-out'); + this.browserWrapper.removeAttribute('animate'); - this.lastCurrentTab = this.#currentTab; + this.lastCurrentTab = this.#currentTab; - this.overlay.classList.remove('zen-glance-overlay'); - gBrowser - ._getSwitcher() - .setTabStateNoAction(this.lastCurrentTab, gBrowser.AsyncTabSwitcher.STATE_UNLOADED); + this.overlay.classList.remove('zen-glance-overlay'); + gBrowser + ._getSwitcher() + .setTabStateNoAction(this.lastCurrentTab, gBrowser.AsyncTabSwitcher.STATE_UNLOADED); - if (!onTabClose) { - this.#currentParentTab._visuallySelected = false; - } + if (!onTabClose) { + this.#currentParentTab._visuallySelected = false; + } - // reset everything - this.browserWrapper = null; - this.overlay = null; - this.contentWrapper = null; + // reset everything + this.browserWrapper = null; + this.overlay = null; + this.contentWrapper = null; - this.lastCurrentTab.removeAttribute('zen-glance-tab'); - this.lastCurrentTab._closingGlance = true; + this.lastCurrentTab.removeAttribute('zen-glance-tab'); + this.lastCurrentTab._closingGlance = true; - if (!isDifferent) { - gBrowser.selectedTab = this.#currentParentTab; - } - this._ignoreClose = true; - gBrowser.removeTab(this.lastCurrentTab, { animate: true, skipPermitUnload: true }); - gBrowser.tabContainer._invalidateCachedTabs(); + if (!isDifferent) { + gBrowser.selectedTab = this.#currentParentTab; + } + this._ignoreClose = true; + gBrowser.removeTab(this.lastCurrentTab, { animate: true, skipPermitUnload: true }); + gBrowser.tabContainer._invalidateCachedTabs(); - this.#currentParentTab.removeAttribute('glance-id'); + this.#currentParentTab.removeAttribute('glance-id'); - this.#glances.delete(this.#currentGlanceID); - this.#currentGlanceID = setNewID; + this.#glances.delete(this.#currentGlanceID); + this.#currentGlanceID = setNewID; - this.lastCurrentTab = null; - this._duringOpening = false; + this.lastCurrentTab = null; + this._duringOpening = false; - this._animating = false; - this.closingGlance = false; + this._animating = false; + this.closingGlance = false; - if (this.#currentGlanceID) { - this.quickOpenGlance(); - } - }); + if (this.#currentGlanceID) { + this.quickOpenGlance(); + } + + resolve(); + }); + }); } quickOpenGlance({ dontOpenButtons = false } = {}) { @@ -605,9 +612,7 @@ this.animatingFullOpen = true; this.#currentTab.setAttribute('zen-dont-split-glance', true); - gBrowser.zenInsertTabAtIndex(this.#currentTab, { - index: this.getTabPosition(this.#currentTab), - }); + gBrowser.zenInsertTabAtIndex(this.#currentTab, this.getTabPosition(this.#currentTab)); this.#currentTab.removeAttribute('zen-glance-tab'); this._clearContainerStyles(this.browserWrapper); diff --git a/src/zen/tests/glance/browser.toml b/src/zen/tests/glance/browser.toml new file mode 100644 index 000000000..d4863a9bd --- /dev/null +++ b/src/zen/tests/glance/browser.toml @@ -0,0 +1,7 @@ +[DEFAULT] +support-files = [ + "head.js", +] + +["browser_glance_basic.js"] +["browser_glance_expand.js"] diff --git a/src/zen/tests/glance/browser_glance_basic.js b/src/zen/tests/glance/browser_glance_basic.js new file mode 100644 index 000000000..881a26c86 --- /dev/null +++ b/src/zen/tests/glance/browser_glance_basic.js @@ -0,0 +1,13 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +'use strict'; + +add_task(async function test_Glance_Basic_Open() { + await openGlanceOnTab(async (glanceTab) => { + ok( + glanceTab.hasAttribute('zen-glance-tab'), + 'The glance tab should have the zen-glance-tab attribute' + ); + }); +}); diff --git a/src/zen/tests/glance/browser_glance_expand.js b/src/zen/tests/glance/browser_glance_expand.js new file mode 100644 index 000000000..b9db4b100 --- /dev/null +++ b/src/zen/tests/glance/browser_glance_expand.js @@ -0,0 +1,105 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +'use strict'; + +add_task(async function test_Glance_Basic_Open() { + const selectedTab = gBrowser.selectedTab; + await openGlanceOnTab(async (glanceTab) => { + await gZenGlanceManager.fullyOpenGlance(); + ok( + !glanceTab.hasAttribute('zen-glance-tab'), + 'The glance tab should not have the zen-glance-tab attribute' + ); + ok( + gBrowser.tabs.filter((tab) => tab.hasAttribute('zen-glance-tab')).length === 0, + 'There should be no zen-glance-tab attribute on any tab' + ); + Assert.greater( + glanceTab._tPos, + selectedTab._tPos, + 'The glance tab should be on the right of the selected tab' + ); + Assert.equal( + glanceTab._tPos, + gBrowser.tabs.length - 1, + 'The glance tab should be the last tab' + ); + BrowserTestUtils.removeTab(glanceTab); + }, false); +}); + +add_task(async function test_Glance_Open_Sibling() { + const tabsToRemove = []; + for (let i = 0; i < 5; i++) { + await BrowserTestUtils.openNewForegroundTab(window.gBrowser, 'https://example.com/', true); + tabsToRemove.push(gBrowser.selectedTab); + } + + gBrowser.selectedTab = gBrowser.tabs[2]; + const selectedTab = gBrowser.selectedTab; + + await openGlanceOnTab(async (glanceTab) => { + await gZenGlanceManager.fullyOpenGlance(); + Assert.equal( + glanceTab._tPos, + selectedTab._tPos + 1, + 'The glance tab should be on the right of the selected tab' + ); + BrowserTestUtils.removeTab(glanceTab); + }, false); + + for (const tab of tabsToRemove) { + await BrowserTestUtils.removeTab(tab); + } +}); + +add_task(async function test_Glance_Basic_Open() { + const tabsToRemove = []; + for (let i = 0; i < 3; i++) { + await BrowserTestUtils.openNewForegroundTab(window.gBrowser, 'https://example.com/', true); + gBrowser.pinTab(gBrowser.selectedTab); + tabsToRemove.push(gBrowser.selectedTab); + } + + gBrowser.selectedTab = gBrowser.tabs.find((tab) => tab.pinned); + + await openGlanceOnTab(async (glanceTab) => { + await gZenGlanceManager.fullyOpenGlance(); + Assert.equal( + glanceTab._tPos, + 3, + 'The glance tab should be the first normal tab (Ignoring empty tabs)' + ); + BrowserTestUtils.removeTab(glanceTab); + }, false); + + for (const tab of tabsToRemove) { + await BrowserTestUtils.removeTab(tab); + } +}); + +add_task(async function test_Glance_New_From_essential() { + await BrowserTestUtils.withNewTab({ gBrowser, url: 'https://example.com/' }, async (browser) => { + const selectedTab = gBrowser.selectedTab; + gZenPinnedTabManager.addToEssentials(selectedTab); + await openGlanceOnTab(async (glanceTab) => { + await gZenGlanceManager.fullyOpenGlance(); + await BrowserTestUtils.openNewForegroundTab(window.gBrowser, 'https://example.com/', true, { + skipAnimation: true, + }); + Assert.equal( + gBrowser.selectedTab._tPos, + 1, + 'The new tab should be the first normal tab (Ignoring empty tabs)' + ); + Assert.equal( + glanceTab._tPos, + 2, + 'The glance tab should be the second normal tab (Ignoring empty tabs)' + ); + await BrowserTestUtils.removeTab(gBrowser.selectedTab); + BrowserTestUtils.removeTab(glanceTab); + }, false); + }); +}); diff --git a/src/zen/tests/glance/head.js b/src/zen/tests/glance/head.js new file mode 100644 index 000000000..ec872e017 --- /dev/null +++ b/src/zen/tests/glance/head.js @@ -0,0 +1,33 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +'use strict'; + +function openGlanceOnTab(callback, close = true) { + return new Promise(async (resolve) => { + setTimeout(() => { + gZenGlanceManager + .openGlance({ + url: 'https://example.com', + x: 0, + y: 0, + width: 0, + height: 0, + }) + .then(async (glanceTab) => { + await callback(glanceTab); + if (close) { + gZenGlanceManager + .closeGlance({ + onTabClose: true, + }) + .then(() => { + resolve(); + }); + } else { + resolve(); + } + }); + }); + }); +} diff --git a/src/zen/tests/moz.build b/src/zen/tests/moz.build index 5649f6bf3..26051fed0 100644 --- a/src/zen/tests/moz.build +++ b/src/zen/tests/moz.build @@ -2,6 +2,7 @@ BROWSER_CHROME_MANIFESTS += [ "compact_mode/browser.toml", "container_essentials/browser.toml", + "glance/browser.toml", "pinned/browser.toml", "urlbar/browser.toml", "workspaces/browser.toml", diff --git a/src/zen/tests/pinned/browser_issue_7654.js b/src/zen/tests/pinned/browser_issue_7654.js index 5ac6d2df0..38dc659f0 100644 --- a/src/zen/tests/pinned/browser_issue_7654.js +++ b/src/zen/tests/pinned/browser_issue_7654.js @@ -7,7 +7,7 @@ ChromeUtils.defineESModuleGetters(this, { UrlbarTestUtils: 'resource://testing-common/UrlbarTestUtils.sys.mjs', }); -add_task(async function test_Create_Pinned() { +add_task(async function test_Search_Pinned_Title() { let resolvePromise; const promise = new Promise((resolve) => { resolvePromise = resolve;