зеркало из https://github.com/mozilla/gecko-dev.git
Bug 887515 - Restore multiple tab closings using Undo Close Tab(s) after a multiple-tab operation. r=fluent-reviewers,flod,Gijs
Differential Revision: https://phabricator.services.mozilla.com/D76087
This commit is contained in:
Родитель
882614bb0f
Коммит
803b52c5dd
|
@ -7,9 +7,9 @@
|
|||
<vbox class="panel-subview-body">
|
||||
<toolbarbutton id="allTabsMenu-undoCloseTab"
|
||||
class="subviewbutton subviewbutton-iconic"
|
||||
data-l10n-id="all-tabs-menu-undo-close-tab"
|
||||
data-l10n-id="all-tabs-menu-undo-close-tabs"
|
||||
key="key_undoCloseTab"
|
||||
command="History:UndoCloseTab"/>
|
||||
observes="History:UndoCloseTab"/>
|
||||
<toolbarbutton id="allTabsMenu-searchTabs"
|
||||
class="subviewbutton subviewbutton-iconic"
|
||||
oncommand="gTabsPanel.searchTabs();"
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
oncommand="OpenBrowserWindow({fission: false});"
|
||||
hidden="true"/>
|
||||
#endif
|
||||
<command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
|
||||
<command id="History:UndoCloseTab" oncommand="undoCloseTab();" data-l10n-args='{"tabCount": 1}'/>
|
||||
<command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
|
||||
|
||||
<command id="wrCaptureCmd" oncommand="gGfxUtils.webrenderCapture();" disabled="true"/>
|
||||
|
|
|
@ -8039,19 +8039,27 @@ function AddKeywordForSearchField() {
|
|||
*/
|
||||
function undoCloseTab(aIndex) {
|
||||
// wallpaper patch to prevent an unnecessary blank tab (bug 343895)
|
||||
var blankTabToRemove = null;
|
||||
let blankTabToRemove = null;
|
||||
if (gBrowser.tabs.length == 1 && gBrowser.selectedTab.isEmpty) {
|
||||
blankTabToRemove = gBrowser.selectedTab;
|
||||
}
|
||||
|
||||
var tab = null;
|
||||
if (SessionStore.getClosedTabCount(window) > (aIndex || 0)) {
|
||||
tab = SessionStore.undoCloseTab(window, aIndex || 0);
|
||||
let tab = null;
|
||||
// aIndex is undefined if the function is called without a specific tab to restore.
|
||||
let tabsToRemove =
|
||||
aIndex !== undefined
|
||||
? [aIndex]
|
||||
: new Array(SessionStore.getLastClosedTabCount(window)).fill(0);
|
||||
for (let index of tabsToRemove) {
|
||||
if (SessionStore.getClosedTabCount(window) > index) {
|
||||
tab = SessionStore.undoCloseTab(window, index);
|
||||
|
||||
if (blankTabToRemove) {
|
||||
gBrowser.removeTab(blankTabToRemove);
|
||||
if (blankTabToRemove) {
|
||||
gBrowser.removeTab(blankTabToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
SessionStore.setLastClosedTabCount(window, 1);
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
|
|
@ -185,12 +185,18 @@
|
|||
onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, TabContextMenu.contextTab.linkedBrowser.currentURI.spec, TabContextMenu.contextTab.linkedBrowser.contentTitle, TabContextMenu.contextTab.multiselected);"/>
|
||||
</menu>
|
||||
<menuseparator/>
|
||||
<menuitem id="context_closeTabsToTheEnd" data-lazy-l10n-id="close-tabs-to-the-end"
|
||||
oncommand="gBrowser.removeTabsToTheEndFrom(TabContextMenu.contextTab, {animate: true});"/>
|
||||
<menuitem id="context_closeOtherTabs" data-lazy-l10n-id="close-other-tabs"
|
||||
oncommand="gBrowser.removeAllTabsBut(TabContextMenu.contextTab);"/>
|
||||
<menu id="context_closeTabOptions"
|
||||
data-lazy-l10n-id="tab-context-close-multiple-tabs">
|
||||
<menupopup id="closeTabOptions">
|
||||
<menuitem id="context_closeTabsToTheEnd" data-lazy-l10n-id="close-tabs-to-the-end"
|
||||
oncommand="gBrowser.removeTabsToTheEndFrom(TabContextMenu.contextTab, {animate: true});"/>
|
||||
<menuitem id="context_closeOtherTabs" data-lazy-l10n-id="close-other-tabs"
|
||||
oncommand="gBrowser.removeAllTabsBut(TabContextMenu.contextTab);"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menuitem id="context_undoCloseTab"
|
||||
data-lazy-l10n-id="undo-close-tab"
|
||||
data-lazy-l10n-id="tab-context-undo-close-tabs"
|
||||
data-l10n-args='{"tabCount": 1}'
|
||||
observes="History:UndoCloseTab"/>
|
||||
<menuitem id="context_closeTab" data-lazy-l10n-id="close-tab"
|
||||
oncommand="gBrowser.removeTab(TabContextMenu.contextTab, { animate: true });"/>
|
||||
|
|
|
@ -2545,6 +2545,13 @@
|
|||
);
|
||||
}
|
||||
|
||||
// Don't use document.l10n.setAttributes because the FTL file is loaded
|
||||
// lazily and we won't be able to resolve the string.
|
||||
document
|
||||
.getElementById("History:UndoCloseTab")
|
||||
.setAttribute("data-l10n-args", JSON.stringify({ tabCount: 1 }));
|
||||
SessionStore.setLastClosedTabCount(window, 1);
|
||||
|
||||
// if we're adding tabs, we're past interrupt mode, ditch the owner
|
||||
if (this.selectedTab.owner) {
|
||||
this.selectedTab.owner = null;
|
||||
|
@ -3294,6 +3301,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
let initialTabCount = tabs.length;
|
||||
this._clearMultiSelectionLocked = true;
|
||||
|
||||
// Guarantee that _clearMultiSelectionLocked lock gets released.
|
||||
|
@ -3329,6 +3337,17 @@
|
|||
|
||||
this._clearMultiSelectionLocked = false;
|
||||
this.avoidSingleSelectedTab();
|
||||
let closedTabsCount =
|
||||
initialTabCount - tabs.filter(t => !t.closing).length;
|
||||
// Don't use document.l10n.setAttributes because the FTL file is loaded
|
||||
// lazily and we won't be able to resolve the string.
|
||||
document
|
||||
.getElementById("History:UndoCloseTab")
|
||||
.setAttribute(
|
||||
"data-l10n-args",
|
||||
JSON.stringify({ tabCount: closedTabsCount })
|
||||
);
|
||||
SessionStore.setLastClosedTabCount(window, closedTabsCount);
|
||||
},
|
||||
|
||||
removeCurrentTab(aParams) {
|
||||
|
|
|
@ -98,6 +98,7 @@ skip-if = !e10s # Tab spinner is e10s only.
|
|||
[browser_tabSwitchPrintPreview.js]
|
||||
skip-if = os == 'mac'
|
||||
[browser_tabswitch_updatecommands.js]
|
||||
[browser_undo_close_tabs.js]
|
||||
[browser_viewsource_of_data_URI_in_file_process.js]
|
||||
[browser_visibleTabs_bookmarkAllTabs.js]
|
||||
[browser_visibleTabs_contextMenu.js]
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const PREF_WARN_ON_CLOSE = "browser.tabs.warnOnCloseOtherTabs";
|
||||
|
||||
add_task(async function setPref() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [[PREF_WARN_ON_CLOSE, false]],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function withMultiSelectedTabs() {
|
||||
let initialTab = gBrowser.selectedTab;
|
||||
let tab1 = await addTab("https://example.com/1");
|
||||
let tab2 = await addTab("https://example.com/2");
|
||||
let tab3 = await addTab("https://example.com/3");
|
||||
let tab4 = await addTab("https://example.com/4");
|
||||
|
||||
is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
|
||||
|
||||
gBrowser.selectedTab = tab2;
|
||||
await triggerClickOn(tab4, { shiftKey: true });
|
||||
|
||||
ok(!initialTab.multiselected, "InitialTab is not multiselected");
|
||||
ok(!tab1.multiselected, "Tab1 is not multiselected");
|
||||
ok(tab2.multiselected, "Tab2 is multiselected");
|
||||
ok(tab3.multiselected, "Tab3 is multiselected");
|
||||
ok(tab4.multiselected, "Tab4 is multiselected");
|
||||
is(gBrowser.multiSelectedTabsCount, 3, "Two multiselected tabs");
|
||||
|
||||
gBrowser.removeMultiSelectedTabs();
|
||||
await TestUtils.waitForCondition(
|
||||
() => gBrowser.tabs.length == 2,
|
||||
"wait for the multiselected tabs to close"
|
||||
);
|
||||
is(
|
||||
SessionStore.getLastClosedTabCount(window),
|
||||
3,
|
||||
"SessionStore should know how many tabs were just closed"
|
||||
);
|
||||
|
||||
undoCloseTab();
|
||||
await TestUtils.waitForCondition(
|
||||
() => gBrowser.tabs.length == 5,
|
||||
"wait for the tabs to reopen"
|
||||
);
|
||||
info("waiting for the browsers to finish loading");
|
||||
// Check that the tabs are restored in the correct order
|
||||
for (let tabId of [2, 3, 4]) {
|
||||
let browser = gBrowser.tabs[tabId].linkedBrowser;
|
||||
await ContentTask.spawn(browser, tabId, async aTabId => {
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
return (
|
||||
content?.document?.readyState == "complete" &&
|
||||
content?.document?.location.href == "https://example.com/" + aTabId
|
||||
);
|
||||
}, "waiting for tab " + aTabId + " to load");
|
||||
});
|
||||
}
|
||||
|
||||
gBrowser.removeAllTabsBut(initialTab);
|
||||
});
|
||||
|
||||
add_task(async function withCloseTabsToTheRight() {
|
||||
let initialTab = gBrowser.selectedTab;
|
||||
let tab1 = await addTab("https://example.com/1");
|
||||
await addTab("https://example.com/2");
|
||||
await addTab("https://example.com/3");
|
||||
await addTab("https://example.com/4");
|
||||
|
||||
gBrowser.removeTabsToTheEndFrom(tab1);
|
||||
await TestUtils.waitForCondition(
|
||||
() => gBrowser.tabs.length == 2,
|
||||
"wait for the multiselected tabs to close"
|
||||
);
|
||||
is(
|
||||
SessionStore.getLastClosedTabCount(window),
|
||||
3,
|
||||
"SessionStore should know how many tabs were just closed"
|
||||
);
|
||||
|
||||
undoCloseTab();
|
||||
await TestUtils.waitForCondition(
|
||||
() => gBrowser.tabs.length == 5,
|
||||
"wait for the tabs to reopen"
|
||||
);
|
||||
info("waiting for the browsers to finish loading");
|
||||
// Check that the tabs are restored in the correct order
|
||||
for (let tabId of [2, 3, 4]) {
|
||||
let browser = gBrowser.tabs[tabId].linkedBrowser;
|
||||
ContentTask.spawn(browser, tabId, async aTabId => {
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
return (
|
||||
content?.document?.readyState == "complete" &&
|
||||
content?.document?.location.href == "https://example.com/" + aTabId
|
||||
);
|
||||
}, "waiting for tab " + aTabId + " to load");
|
||||
});
|
||||
}
|
||||
|
||||
gBrowser.removeAllTabsBut(initialTab);
|
||||
});
|
|
@ -328,6 +328,13 @@ var SessionStore = {
|
|||
);
|
||||
},
|
||||
|
||||
getLastClosedTabCount(aWindow) {
|
||||
return SessionStoreInternal.getLastClosedTabCount(aWindow);
|
||||
},
|
||||
setLastClosedTabCount(aWindow, aNumber) {
|
||||
return SessionStoreInternal.setLastClosedTabCount(aWindow, aNumber);
|
||||
},
|
||||
|
||||
getClosedTabCount: function ss_getClosedTabCount(aWindow) {
|
||||
return SessionStoreInternal.getClosedTabCount(aWindow);
|
||||
},
|
||||
|
@ -723,6 +730,7 @@ var SessionStoreInternal = {
|
|||
|
||||
this._initPrefs();
|
||||
this._initialized = true;
|
||||
this._closedTabCache = new WeakMap();
|
||||
|
||||
Telemetry.getHistogramById("FX_SESSION_RESTORE_PRIVACY_LEVEL").add(
|
||||
Services.prefs.getIntPref("browser.sessionstore.privacy_level")
|
||||
|
@ -3175,6 +3183,28 @@ var SessionStoreInternal = {
|
|||
return newTab;
|
||||
},
|
||||
|
||||
getLastClosedTabCount(aWindow) {
|
||||
if ("__SSi" in aWindow) {
|
||||
// Blank tabs cannot be undo-closed, so the number returned by
|
||||
// the ClosedTabCache can be greater than the return value of
|
||||
// getClosedTabCount. We won't restore blank tabs, so we return
|
||||
// the minimum of these two values.
|
||||
return Math.min(
|
||||
this._closedTabCache.get(aWindow) || 1,
|
||||
this.getClosedTabCount(aWindow)
|
||||
);
|
||||
}
|
||||
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
},
|
||||
setLastClosedTabCount(aWindow, aNumber) {
|
||||
if ("__SSi" in aWindow) {
|
||||
return this._closedTabCache.set(aWindow, aNumber);
|
||||
}
|
||||
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
},
|
||||
|
||||
getClosedTabCount: function ssi_getClosedTabCount(aWindow) {
|
||||
if ("__SSi" in aWindow) {
|
||||
return this._windows[aWindow.__SSi]._closedTabs.length;
|
||||
|
|
|
@ -2,8 +2,12 @@
|
|||
# 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/.
|
||||
|
||||
all-tabs-menu-undo-close-tab =
|
||||
.label = Undo Close Tab
|
||||
all-tabs-menu-undo-close-tabs =
|
||||
.label =
|
||||
{ $tabCount ->
|
||||
[1] Undo Close Tab
|
||||
*[other] Undo Close Tabs
|
||||
}
|
||||
|
||||
# "Search" is a verb, as in "Search through tabs".
|
||||
all-tabs-menu-search-tabs =
|
||||
|
|
|
@ -53,8 +53,15 @@ move-to-end =
|
|||
move-to-new-window =
|
||||
.label = Move to New Window
|
||||
.accesskey = W
|
||||
undo-close-tab =
|
||||
.label = Undo Close Tab
|
||||
tab-context-close-multiple-tabs =
|
||||
.label = Close Multiple Tabs
|
||||
.accesskey = M
|
||||
tab-context-undo-close-tabs =
|
||||
.label =
|
||||
{ $tabCount ->
|
||||
[1] Undo Close Tab
|
||||
*[other] Undo Close Tabs
|
||||
}
|
||||
.accesskey = U
|
||||
close-tab =
|
||||
.label = Close Tab
|
||||
|
|
|
@ -17,8 +17,12 @@ toolbar-context-menu-bookmark-selected-tabs =
|
|||
toolbar-context-menu-select-all-tabs =
|
||||
.label = Select All Tabs
|
||||
.accesskey = S
|
||||
toolbar-context-menu-undo-close-tab =
|
||||
.label = Undo Close Tab
|
||||
toolbar-context-menu-undo-close-tabs =
|
||||
.label =
|
||||
{ $tabCount ->
|
||||
[1] Undo Close Tab
|
||||
*[other] Undo Close Tabs
|
||||
}
|
||||
.accesskey = U
|
||||
|
||||
toolbar-context-menu-manage-extension =
|
||||
|
|
Загрузка…
Ссылка в новой задаче