1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
9 ChromeUtils.defineESModuleGetters(lazy, {
10 PlacesUIUtils: "resource:///modules/PlacesUIUtils.sys.mjs",
11 PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
12 SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
15 ChromeUtils.defineLazyGetter(lazy, "l10n", () => {
16 return new Localization(["browser/recentlyClosed.ftl"], true);
19 XPCOMUtils.defineLazyPreferenceGetter(
21 "closedTabsFromAllWindowsEnabled",
22 "browser.sessionstore.closedTabsFromAllWindows"
25 XPCOMUtils.defineLazyPreferenceGetter(
27 "closedTabsFromClosedWindowsEnabled",
28 "browser.sessionstore.closedTabsFromClosedWindows"
31 export var RecentlyClosedTabsAndWindowsMenuUtils = {
33 * Builds up a document fragment of UI items for the recently closed tabs.
35 * The window that the tabs were closed in.
37 * The tag name that will be used when creating the UI items.
38 * @param aPrefixRestoreAll (defaults to false)
39 * Whether the 'restore all tabs' item is suffixed or prefixed to the list.
40 * If suffixed (the default) a separator will be inserted before it.
41 * @returns A document fragment with UI items for each recently closed tab.
43 getTabsFragment(aWindow, aTagName, aPrefixRestoreAll = false) {
44 let doc = aWindow.document;
45 const isPrivate = lazy.PrivateBrowsingUtils.isWindowPrivate(aWindow);
46 const fragment = doc.createDocumentFragment();
50 lazy.SessionStore.getClosedTabCount({
51 sourceWindow: aWindow,
52 closedTabsFromClosedWindows: false,
56 const browserWindows = lazy.closedTabsFromAllWindowsEnabled
57 ? lazy.SessionStore.getWindows(aWindow)
60 for (const win of browserWindows) {
61 const closedTabs = lazy.SessionStore.getClosedTabDataForWindow(win);
62 for (let i = 0; i < closedTabs.length; i++) {
78 lazy.closedTabsFromClosedWindowsEnabled &&
79 lazy.SessionStore.getClosedTabCountFromClosedWindows()
82 const closedTabs = lazy.SessionStore.getClosedTabDataFromClosedWindows();
83 for (let i = 0; i < closedTabs.length; i++) {
97 createRestoreAllEntry(
102 aTagName == "menuitem"
103 ? "recently-closed-menu-reopen-all-tabs"
104 : "recently-closed-panel-reopen-all-tabs",
112 * Builds up a document fragment of UI items for the recently closed windows.
114 * A window that can be used to create the elements and document fragment.
116 * The tag name that will be used when creating the UI items.
117 * @param aPrefixRestoreAll (defaults to false)
118 * Whether the 'restore all windows' item is suffixed or prefixed to the list.
119 * If suffixed (the default) a separator will be inserted before it.
120 * @returns A document fragment with UI items for each recently closed window.
122 getWindowsFragment(aWindow, aTagName, aPrefixRestoreAll = false) {
123 let closedWindowData = lazy.SessionStore.getClosedWindowData();
124 let doc = aWindow.document;
125 let fragment = doc.createDocumentFragment();
126 if (closedWindowData.length) {
127 for (let i = 0; i < closedWindowData.length; i++) {
128 const { selected, tabs, title } = closedWindowData[i];
129 const selectedTab = tabs[selected - 1];
131 const menuLabel = lazy.l10n.formatValueSync(
132 "recently-closed-undo-close-window-label",
133 { tabCount: tabs.length - 1, winTitle: title }
135 createEntry(aTagName, true, i, selectedTab, doc, menuLabel, fragment);
139 createRestoreAllEntry(
144 aTagName == "menuitem"
145 ? "recently-closed-menu-reopen-all-windows"
146 : "recently-closed-panel-reopen-all-windows",
154 * Handle a command event to re-open all closed tabs
156 * The command event when the user clicks the restore all menu item
158 onRestoreAllTabsCommand(aEvent) {
159 const currentWindow = aEvent.target.ownerGlobal;
160 const browserWindows = lazy.closedTabsFromAllWindowsEnabled
161 ? lazy.SessionStore.getWindows(currentWindow)
163 for (const sourceWindow of browserWindows) {
164 let count = lazy.SessionStore.getClosedTabCountForWindow(sourceWindow);
165 while (--count >= 0) {
166 lazy.SessionStore.undoCloseTab(sourceWindow, 0, currentWindow);
169 if (lazy.closedTabsFromClosedWindowsEnabled) {
170 for (let tabData of lazy.SessionStore.getClosedTabDataFromClosedWindows()) {
171 lazy.SessionStore.undoClosedTabFromClosedWindow(
172 { sourceClosedId: tabData.sourceClosedId },
181 * Handle a command event to re-open all closed windows
183 * The command event when the user clicks the restore all menu item
185 onRestoreAllWindowsCommand(aEvent) {
186 const count = lazy.SessionStore.getClosedWindowCount();
187 for (let index = 0; index < count; index++) {
188 lazy.SessionStore.undoCloseWindow(index);
193 * Re-open a closed tab and put it to the end of the tab strip.
194 * Used for a middle click.
196 * The event when the user clicks the menu item
198 _undoCloseMiddleClick(aEvent) {
199 if (aEvent.button != 1) {
202 if (aEvent.originalTarget.hasAttribute("source-closed-id")) {
203 lazy.SessionStore.undoClosedTabFromClosedWindow(
206 aEvent.originalTarget.getAttribute("source-closed-id"),
208 aEvent.originalTarget.getAttribute("value")
211 aEvent.view.undoCloseTab(
212 aEvent.originalTarget.getAttribute("value"),
213 aEvent.originalTarget.getAttribute("source-window-id")
216 aEvent.view.gBrowser.moveTabToEnd();
217 let ancestorPanel = aEvent.target.closest("panel");
219 ancestorPanel.hidePopup();
225 * Create a UI entry for a recently closed tab or window.
227 * the tag name that will be used when creating the UI entry
228 * @param aIsWindowsFragment
229 * whether or not this entry will represent a closed window
231 * the index of the closed tab
235 * a document that can be used to create the entry
237 * the label the created entry will have
239 * the fragment the created entry will be in
241 function createEntry(
250 let element = aDocument.createXULElement(aTagName);
252 element.setAttribute("label", aMenuLabel);
253 if (aClosedTab.image) {
254 const iconURL = lazy.PlacesUIUtils.getImageURL(aClosedTab.image);
255 element.setAttribute("image", iconURL);
258 if (aIsWindowsFragment) {
259 element.setAttribute("oncommand", `undoCloseWindow("${aIndex}");`);
260 } else if (typeof aClosedTab.sourceClosedId == "number") {
261 // sourceClosedId is used to look up the closed window to remove it when the tab is restored
262 let sourceClosedId = aClosedTab.sourceClosedId;
263 element.setAttribute("source-closed-id", sourceClosedId);
264 element.setAttribute("value", aClosedTab.closedId);
265 element.removeAttribute("oncommand");
266 element.addEventListener(
269 lazy.SessionStore.undoClosedTabFromClosedWindow(
277 // sourceWindowId is used to look up the closed tab entry to remove it when it is restored
278 let sourceWindowId = aClosedTab.sourceWindowId;
279 element.setAttribute("value", aIndex);
280 element.setAttribute("source-window-id", sourceWindowId);
281 element.setAttribute(
283 `undoCloseTab(${aIndex}, "${sourceWindowId}");`
287 if (aTagName == "menuitem") {
288 element.setAttribute(
290 "menuitem-iconic bookmark-item menuitem-with-favicon"
294 // Set the targetURI attribute so it will be shown in tooltip.
295 // SessionStore uses one-based indexes, so we need to normalize them.
297 tabData = aIsWindowsFragment ? aClosedTab : aClosedTab.state;
298 let activeIndex = (tabData.index || tabData.entries.length) - 1;
299 if (activeIndex >= 0 && tabData.entries[activeIndex]) {
300 element.setAttribute("targetURI", tabData.entries[activeIndex].url);
303 // Windows don't open in new tabs and menuitems dispatch command events on
304 // middle click, so we only need to manually handle middle clicks for
306 if (!aIsWindowsFragment && aTagName != "menuitem") {
307 element.addEventListener(
309 RecentlyClosedTabsAndWindowsMenuUtils._undoCloseMiddleClick
314 element.setAttribute(
316 "key_undoClose" + (aIsWindowsFragment ? "Window" : "Tab")
320 aFragment.appendChild(element);
324 * Create an entry to restore all closed windows or tabs.
326 * a document that can be used to create the entry
328 * the fragment the created entry will be in
329 * @param aPrefixRestoreAll
330 * whether the 'restore all windows' item is suffixed or prefixed to the list
331 * If suffixed a separator will be inserted before it.
332 * @param aIsWindowsFragment
333 * whether or not this entry will represent a closed window
334 * @param aRestoreAllLabel
335 * which localizable string to use for the entry
337 * the number of elements to be restored by this entry
339 * the tag name that will be used when creating the UI entry
341 function createRestoreAllEntry(
349 let restoreAllElements = aDocument.createXULElement(aTagName);
350 restoreAllElements.classList.add("restoreallitem");
352 // We cannot use aDocument.l10n.setAttributes because the menubar label is not
353 // updated in time and displays a blank string (see Bug 1691553).
354 restoreAllElements.setAttribute(
356 lazy.l10n.formatValueSync(aRestoreAllLabel)
359 restoreAllElements.addEventListener(
362 ? RecentlyClosedTabsAndWindowsMenuUtils.onRestoreAllWindowsCommand
363 : RecentlyClosedTabsAndWindowsMenuUtils.onRestoreAllTabsCommand
366 if (aPrefixRestoreAll) {
367 aFragment.insertBefore(restoreAllElements, aFragment.firstChild);
369 aFragment.appendChild(aDocument.createXULElement("menuseparator"));
370 aFragment.appendChild(restoreAllElements);