diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 307c979ca9e4..4c45decd5422 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3897,6 +3897,7 @@ let event = document.createEvent("Events"); event.initEvent("TabShow", true, false); aTab.dispatchEvent(event); + SessionStore.deleteTabValue(aTab, "hiddenBy"); } ]]> @@ -3904,6 +3905,7 @@ + diff --git a/browser/components/extensions/ext-tabs.js b/browser/components/extensions/ext-tabs.js index 03f42f12222f..cbc1d64fd552 100644 --- a/browser/components/extensions/ext-tabs.js +++ b/browser/components/extensions/ext-tabs.js @@ -11,6 +11,8 @@ ChromeUtils.defineModuleGetter(this, "PromiseUtils", "resource://gre/modules/PromiseUtils.jsm"); ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); +ChromeUtils.defineModuleGetter(this, "SessionStore", + "resource:///modules/sessionstore/SessionStore.jsm"); XPCOMUtils.defineLazyGetter(this, "strBundle", function() { return Services.strings.createBundle("chrome://global/locale/extensions.properties"); @@ -22,9 +24,6 @@ var { const TABHIDE_PREFNAME = "extensions.webextensions.tabhide.enabled"; -// WeakMap[Tab -> ExtensionID] -let hiddenTabs = new WeakMap(); - let tabListener = { tabReadyInitialized: false, tabReadyPromises: new WeakMap(), @@ -93,11 +92,9 @@ this.tabs = class extends ExtensionAPI { // self-manage re-hiding tabs. for (let tab of this.extension.tabManager.query()) { let nativeTab = tabTracker.getTab(tab.id); - if (hiddenTabs.get(nativeTab) === this.extension.id) { - hiddenTabs.delete(nativeTab); - if (nativeTab.ownerGlobal) { - nativeTab.ownerGlobal.gBrowser.showTab(nativeTab); - } + if (nativeTab.hidden && nativeTab.ownerGlobal && + SessionStore.getTabValue(nativeTab, "hiddenBy") === this.extension.id) { + nativeTab.ownerGlobal.gBrowser.showTab(nativeTab); } } } @@ -301,8 +298,6 @@ this.tabs = class extends ExtensionAPI { needed.push("discarded"); } else if (event.type == "TabShow") { needed.push("hidden"); - // Always remove the tab from the hiddenTabs map. - hiddenTabs.delete(event.originalTarget); } else if (event.type == "TabHide") { needed.push("hidden"); } @@ -1064,7 +1059,6 @@ this.tabs = class extends ExtensionAPI { for (let tabId of tabIds) { let tab = tabTracker.getTab(tabId); if (tab.ownerGlobal) { - hiddenTabs.delete(tab); tab.ownerGlobal.gBrowser.showTab(tab); } } @@ -1083,9 +1077,8 @@ this.tabs = class extends ExtensionAPI { let tabs = tabIds.map(tabId => tabTracker.getTab(tabId)); for (let tab of tabs) { if (tab.ownerGlobal && !tab.hidden) { - tab.ownerGlobal.gBrowser.hideTab(tab); + tab.ownerGlobal.gBrowser.hideTab(tab, extension.id); if (tab.hidden) { - hiddenTabs.set(tab, extension.id); hidden.push(tabTracker.getId(tab)); } } diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_hide.js b/browser/components/extensions/test/browser/browser_ext_tabs_hide.js index a4ed2765295b..95eb99c21a4e 100644 --- a/browser/components/extensions/test/browser/browser_ext_tabs_hide.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_hide.js @@ -1,5 +1,9 @@ "use strict"; +ChromeUtils.defineModuleGetter(this, "SessionStore", + "resource:///modules/sessionstore/SessionStore.jsm"); +ChromeUtils.defineModuleGetter(this, "TabStateFlusher", + "resource:///modules/sessionstore/TabStateFlusher.jsm"); const {Utils} = ChromeUtils.import("resource://gre/modules/sessionstore/Utils.jsm", {}); const triggeringPrincipal_base64 = Utils.SERIALIZED_SYSTEMPRINCIPAL; @@ -116,9 +120,26 @@ add_task(async function test_tabs_showhide() { ok(!tabs[0].hidden, "first tab not hidden"); for (let i = 1; i < tabs.length; i++) { ok(tabs[i].hidden, "tab hidden value is correct"); + let id = SessionStore.getTabValue(tabs[i], "hiddenBy"); + is(id, extension.id, "tab hiddenBy value is correct"); + await TabStateFlusher.flush(tabs[i].linkedBrowser); } } + // Close the other window then restore it to test that the tabs are + // restored with proper hidden state, and the correct extension id. + await BrowserTestUtils.closeWindow(otherwin); + + otherwin = SessionStore.undoCloseWindow(0); + await BrowserTestUtils.waitForEvent(otherwin, "load"); + let tabs = Array.from(otherwin.gBrowser.tabs.values()); + ok(!tabs[0].hidden, "first tab not hidden"); + for (let i = 1; i < tabs.length; i++) { + ok(tabs[i].hidden, "tab hidden value is correct"); + let id = SessionStore.getTabValue(tabs[i], "hiddenBy"); + is(id, extension.id, "tab hiddenBy value is correct"); + } + // Test closing the last visible tab, the next tab which is hidden should become // the selectedTab and will be visible. ok(!otherwin.gBrowser.selectedTab.hidden, "selected tab is not hidden"); diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index 0fa2e29d1a2f..15d719593a6b 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -3436,7 +3436,7 @@ var SessionStoreInternal = { tabs.push(tab); if (tabData.hidden) { - tabbrowser.hideTab(tab); + tabbrowser.hideTab(tab, tabData.extData && tabData.extData.hiddenBy); } if (tabData.pinned) {