Bug 1436720 use sessionstore to track controlling extension, r?Gijs,rpl
MozReview-Commit-ID: ACzGfNDVFXm
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -3886,37 +3886,42 @@
this.tabContainer._updateCloseButtons();
this.tabContainer._setPositionalAttributes();
let event = document.createEvent("Events");
event.initEvent("TabShow", true, false);
aTab.dispatchEvent(event);
+ SessionStore.deleteTabValue(aTab, "hiddenBy");
}
]]>
</body>
</method>
<method name="hideTab">
<parameter name="aTab"/>
+ <parameter name="aSource"/>
<body>
<![CDATA[
if (!aTab.hidden && !aTab.pinned && !aTab.selected &&
!aTab.closing && !aTab._sharingState) {
aTab.setAttribute("hidden", "true");
this._visibleTabs = null; // invalidate cache
this.tabContainer._updateCloseButtons();
this.tabContainer._setPositionalAttributes();
let event = document.createEvent("Events");
event.initEvent("TabHide", true, false);
aTab.dispatchEvent(event);
+ if (aSource) {
+ SessionStore.setTabValue(aTab, "hiddenBy", aSource);
+ }
}
]]>
</body>
</method>
<method name="selectTabAtIndex">
<parameter name="aIndex"/>
<parameter name="aEvent"/>
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -6,30 +6,29 @@
/* import-globals-from ext-browser.js */
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
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");
});
var {
ExtensionError,
} = ExtensionUtils;
const TABHIDE_PREFNAME = "extensions.webextensions.tabhide.enabled";
-// WeakMap[Tab -> ExtensionID]
-let hiddenTabs = new WeakMap();
-
let tabListener = {
tabReadyInitialized: false,
tabReadyPromises: new WeakMap(),
initializingTabs: new WeakSet(),
initTabReady() {
if (!this.tabReadyInitialized) {
windowTracker.addListener("progress", this);
@@ -88,21 +87,19 @@ this.tabs = class extends ExtensionAPI {
}
if (reason == "ADDON_DISABLE" ||
reason == "ADDON_UNINSTALL") {
// Show all hidden tabs if a tab managing extension is uninstalled or
// disabled. If a user has more than one, the extensions will need to
// 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);
}
}
}
}
getAPI(context) {
let {extension} = context;
@@ -296,18 +293,16 @@ this.tabs = class extends ExtensionAPI {
needed.push("pinned");
} else if (event.type == "TabBrowserInserted" &&
!event.detail.insertedOnTabCreation) {
needed.push("discarded");
} else if (event.type == "TabBrowserDiscarded") {
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");
}
let tab = tabManager.getWrapper(event.originalTarget);
let changeInfo = {};
for (let prop of needed) {
changeInfo[prop] = tab[prop];
@@ -1059,17 +1054,16 @@ this.tabs = class extends ExtensionAPI {
if (!Array.isArray(tabIds)) {
tabIds = [tabIds];
}
for (let tabId of tabIds) {
let tab = tabTracker.getTab(tabId);
if (tab.ownerGlobal) {
- hiddenTabs.delete(tab);
tab.ownerGlobal.gBrowser.showTab(tab);
}
}
},
hide(tabIds) {
if (!Services.prefs.getBoolPref(TABHIDE_PREFNAME, false)) {
throw new ExtensionError(`tabs.hide is currently experimental and must be enabled with the ${TABHIDE_PREFNAME} preference.`);
@@ -1078,19 +1072,18 @@ this.tabs = class extends ExtensionAPI {
if (!Array.isArray(tabIds)) {
tabIds = [tabIds];
}
let hidden = [];
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));
}
}
}
return hidden;
},
},
};
--- a/browser/components/extensions/test/browser/browser_ext_tabs_hide.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_hide.js
@@ -1,10 +1,14 @@
"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;
// Ensure the pref prevents API use when the extension has the tabHide permission.
add_task(async function test_pref_disabled() {
async function background() {
let tabs = await browser.tabs.query({hidden: false});
let ids = tabs.map(tab => tab.id);
@@ -111,19 +115,36 @@ add_task(async function test_tabs_showhi
for (let win of BrowserWindowIterator()) {
if (win != window) {
otherwin = win;
}
let tabs = Array.from(win.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");
+ 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");
await BrowserTestUtils.removeTab(otherwin.gBrowser.selectedTab);
ok(!otherwin.gBrowser.selectedTab.hidden, "tab was unhidden");
// Showall will unhide any remaining hidden tabs.
extension.sendMessage("showall");
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -3431,17 +3431,17 @@ var SessionStoreInternal = {
tabbrowser.selectedTab = tab;
tabbrowser.removeTab(leftoverTab);
}
}
tabs.push(tab);
if (tabData.hidden) {
- tabbrowser.hideTab(tab);
+ tabbrowser.hideTab(tab, tabData.extData && tabData.extData.hiddenBy);
}
if (tabData.pinned) {
tabbrowser.pinTab(tab);
}
}
// Move the originally open tabs to the end.