diff --git a/mozilla/browser/base/content/browser.js b/mozilla/browser/base/content/browser.js index a7afc202e81..d1a6273a158 100644 --- a/mozilla/browser/base/content/browser.js +++ b/mozilla/browser/base/content/browser.js @@ -1124,6 +1124,9 @@ function delayedStartup() } } } + + // browser-specific tab augmentation + AugmentTabs.init(); } function BrowserShutdown() @@ -6642,3 +6645,57 @@ window.controllers.appendController(BrowserController); #include ../../../toolkit/content/debug.js #endif + +/** + * This object is for augmenting tabs + */ +var AugmentTabs = { + /** + * Called in delayedStartup + */ + init: function at_init() { + // add the tab context menu for undo-close-tab (bz254021) + var ssEnabled = true; + var prefBranch = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefBranch); + try { + ssEnabled = prefBranch.getBoolPref("browser.sessionstore.enabled"); + } catch (ex) {} + + if (ssEnabled) + this._addUndoCloseTabContextMenu(); + }, + + /** + * Add undo-close-tab to tab context menu + */ + _addUndoCloseTabContextMenu: function at_addUndoCloseTabContextMenu() { + // get tab context menu + var tabbrowser = getBrowser(); + var tabMenu = document.getAnonymousElementByAttribute(tabbrowser,"anonid","tabContextMenu"); + + // get strings + var menuLabel = gNavigatorBundle.getString("tabContext.undoCloseTab"); + var menuAccessKey = gNavigatorBundle.getString("tabContext.undoCloseTabAccessKey"); + + // create new menu item + var undoCloseTabItem = document.createElement("menuitem"); + undoCloseTabItem.setAttribute("label", menuLabel); + undoCloseTabItem.setAttribute("accesskey", menuAccessKey); + undoCloseTabItem.addEventListener("command", this.undoCloseTab, false); + + // add to tab context menu + var insertPos = tabMenu.lastChild.previousSibling; + tabMenu.insertBefore(undoCloseTabItem, insertPos); + }, + + /** + * Re-open the most-recently-closed tab + */ + undoCloseTab: function at_undoCloseTab() { + // get session-store service + var ss = Cc["@mozilla.org/browser/sessionstore;1"]. + getService(Ci.nsISessionStore); + ss.undoCloseTab(window, 0); + } +}; diff --git a/mozilla/browser/components/sessionstore/nsISessionStore.idl b/mozilla/browser/components/sessionstore/nsISessionStore.idl index 8343eb127fe..384a9b2c3b9 100644 --- a/mozilla/browser/components/sessionstore/nsISessionStore.idl +++ b/mozilla/browser/components/sessionstore/nsISessionStore.idl @@ -53,4 +53,12 @@ interface nsISessionStore : nsISupports * Initialize the service */ void init(in nsIDOMWindow aWindow); + + /** + * @param aWindow + * The window to reopen a closed tab in. + * @param aIndex + * Indicates the window to be restored (FIFO ordered). + */ + void undoCloseTab(in nsIDOMWindow aWindow, in unsigned long aIndex); }; diff --git a/mozilla/browser/components/sessionstore/src/nsSessionStore.js b/mozilla/browser/components/sessionstore/src/nsSessionStore.js index 3429e3eeeb7..357bd717f6d 100644 --- a/mozilla/browser/components/sessionstore/src/nsSessionStore.js +++ b/mozilla/browser/components/sessionstore/src/nsSessionStore.js @@ -422,7 +422,7 @@ SessionStoreService.prototype = { tabpanels.addEventListener("select", function(aEvent) { _this.onTabSelect(aEvent.currentTarget.ownerDocument.defaultView, aEvent.currentTarget); }, false); - tabbrowser.addEventListener("TabClose", function(aEvent) { + tabbrowser.addEventListener("DOMNodeRemoved", function(aEvent) { _this.onTabClose(aEvent.currentTarget.ownerDocument.defaultView, aEvent.originalTarget); }, false); }, @@ -539,12 +539,17 @@ SessionStoreService.prototype = { this._saveWindowHistory(aWindow); this._updateTextAndScrollData(aWindow); - this._windows[aWindow.__SSi]._closedTabs.unshift({ - state: this._windows[aWindow.__SSi].Tab[aTab._tPos], - title: aTab.getAttribute("label"), - pos: aTab._tPos - }); - this._windows[aWindow.__SSi]._closedTabs.splice(this._getPref("sessionstore.max_tabs_undo", DEFAULT_MAX_TABS_UNDO)); + // DOMNodeRemoved is received *twice* after closing a tab, only take the first + var tabState = this._windows[aWindow.__SSi].Tab[aTab._tPos]; + if (tabState) { + this._windows[aWindow.__SSi]._closedTabs.unshift({ + state: tabState, + title: aTab.getAttribute("label"), + pos: aTab._tPos + }); + var maxTabsUndo = this._getPref("sessionstore.max_tabs_undo", DEFAULT_MAX_TABS_UNDO); + this._windows[aWindow.__SSi]._closedTabs.splice(maxTabsUndo); + } }, /** @@ -685,22 +690,30 @@ SessionStoreService.prototype = { this._windows[aWindow.__SSi]._closedTabs = IniObjectSerializer.decode(aData); }, - undoCloseTab: function sss_undoCloseWindow(aWindow, aIx) { - var tabs = this._windows[aWindow.__SSi]._closedTabs; + undoCloseTab: function sss_undoCloseTab(aWindow, aIndex) { + var closedTabs = this._windows[aWindow.__SSi]._closedTabs; + + // default to the most-recently closed tab + aIndex = aIndex || 0; - if (aIx in tabs) { + if (aIndex in closedTabs) { var browser = aWindow.getBrowser(); - var tabData = tabs.splice(aIx, 1); - tabData._tab = browser.addTab(); + + // fetch the data of closed tab, while removing it from the array + var closedTab = closedTabs.splice(aIndex, 1).shift(); + var closedTabState = closedTab.state; + + // create a new tab + closedTabState._tab = browser.addTab(); // restore the tab's position - browser.moveTabTo(tabData._tab, tabData[0].pos); + browser.moveTabTo(closedTabState._tab, closedTab.pos); - // restore the tab's state - aWindow.setTimeout(this.restoreHistory_proxy, 0, tabData, 1, 0, 0); + // restore tab content + this.restoreHistoryPrecursor(aWindow, [closedTabState], 1, 0, 0); } else { - Components.returnCode = -1; //zeniko: or should we rather fail silently? + Components.returnCode = Cr.NS_ERROR_INVALID_ARG; } }, @@ -1195,11 +1208,8 @@ SessionStoreService.prototype = { } } - var restoreHistoryFunc = function(self) { - self.restoreHistory_proxy(aWindow, winData.Tab, (aOverwriteTabs ? + this.restoreHistoryPrecursor(aWindow, winData.Tab, (aOverwriteTabs ? (parseInt(winData.selected) || 1) : 0), 0, 0); - }; - aWindow.setTimeout(restoreHistoryFunc, 0, this); }, /** @@ -1213,7 +1223,7 @@ SessionStoreService.prototype = { * @param aCount * Counter for number of times delaying b/c browser or history aren't ready */ - restoreHistory_proxy: function sss_restoreHistory_proxy(aWindow, aTabs, aSelectTab, aIx, aCount) { + restoreHistoryPrecursor: function sss_restoreHistoryPrecursor(aWindow, aTabs, aSelectTab, aIx, aCount) { var tabbrowser = aWindow.getBrowser(); // make sure that all browsers and their histories are available @@ -1227,7 +1237,7 @@ SessionStoreService.prototype = { catch (ex) { // in case browser or history aren't ready yet if (aCount < 10) { var restoreHistoryFunc = function(self) { - self.restoreHistory_proxy(aWindow, aTabs, aSelectTab, aIx, aCount + 1); + self.restoreHistoryPrecursor(aWindow, aTabs, aSelectTab, aIx, aCount + 1); } aWindow.setTimeout(restoreHistoryFunc, 100, this); return; diff --git a/mozilla/browser/locales/en-US/chrome/browser/browser.properties b/mozilla/browser/locales/en-US/chrome/browser/browser.properties index d533ef7af2f..07a805dcb07 100644 --- a/mozilla/browser/locales/en-US/chrome/browser/browser.properties +++ b/mozilla/browser/locales/en-US/chrome/browser/browser.properties @@ -119,3 +119,7 @@ feedHasFeeds=Add Live Bookmark... feedNoFeeds=Page has no feeds feedShowFeedNew=Subscribe to '%S'... feedHasFeedsNew=Subscribe to this page... + +# tab context menu additions +tabContext.undoCloseTab=Undo Close Tab +tabContext.undoCloseTabAccessKey=U