Mozilla Home
Privacy
Cookies
Legal
Bugzilla
Browse
Advanced Search
New Bug
Reports
Documentation
Log In
Log In with GitHub
or
Remember me
Browse
Advanced Search
New Bug
Reports
Documentation
Attachment 542940 Details for
Bug 587873
[patch]
Patch v0.2.1 (WIP)
bug-587873-app-tabs (text/plain), 26.93 KB, created by
Paul O'Shannessy [:zpao] (not bugmail, email directly)
(
hide
)
Description:
Patch v0.2.1 (WIP)
Filename:
MIME Type:
Creator:
Paul O'Shannessy [:zpao] (not bugmail, email directly)
Size:
26.93 KB
patch
obsolete
># vim: se ft=diff : ># HG changeset patch ># Parent d173bf22c9bb311c6e1f49b6a34f5d47899f0890 ># User Paul OâShannessy <paul@oshannessy.com> >import WIP patch from bugzilla > >diff --git a/browser/base/Makefile.in b/browser/base/Makefile.in >--- a/browser/base/Makefile.in >+++ b/browser/base/Makefile.in >@@ -51,16 +51,17 @@ CHROME_DEPS += $(abs_srcdir)/content/ove > > ifdef ENABLE_TESTS > DIRS += content/test > endif > > EXTRA_JS_MODULES = \ > content/openLocationLastURL.jsm \ > content/NetworkPrioritizer.jsm \ >+ content/AppTabs.jsm \ > content/domplate.jsm \ > $(NULL) > > include $(topsrcdir)/config/rules.mk > > PRE_RELEASE_SUFFIX := "" > > DEFINES += \ >diff --git a/browser/base/content/AppTabs.jsm b/browser/base/content/AppTabs.jsm >new file mode 100644 >--- /dev/null >+++ b/browser/base/content/AppTabs.jsm >@@ -0,0 +1,422 @@ >+/* ***** BEGIN LICENSE BLOCK ***** >+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 >+ * >+ * The contents of this file are subject to the Mozilla Public License Version >+ * 1.1 (the "License"); you may not use this file except in compliance with >+ * the License. You may obtain a copy of the License at >+ * http://www.mozilla.org/MPL/ >+ * >+ * Software distributed under the License is distributed on an "AS IS" basis, >+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License >+ * for the specific language governing rights and limitations under the >+ * License. >+ * >+ * The Original Code is mozilla.org code. >+ * >+ * The Initial Developer of the Original Code is Mozilla Foundation. >+ * Portions created by the Initial Developer are Copyright (C) 2010 >+ * the Initial Developer. All Rights Reserved. >+ * >+ * Contributor(s): >+ * Paul OâShannessy <paul@oshannessy.com> (original author) >+ * >+ * Alternatively, the contents of this file may be used under the terms of >+ * either the GNU General Public License Version 2 or later (the "GPL"), or >+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), >+ * in which case the provisions of the GPL or the LGPL are applicable instead >+ * of those above. If you wish to allow use of your version of this file only >+ * under the terms of either the GPL or the LGPL, and not to allow others to >+ * use your version of this file under the terms of the MPL, indicate your >+ * decision by deleting the provisions above and replace them with the notice >+ * and other provisions required by the GPL or the LGPL. If you do not delete >+ * the provisions above, a recipient may use your version of this file under >+ * the terms of any one of the MPL, the GPL or the LGPL. >+ * >+ * ***** END LICENSE BLOCK ***** */ >+ >+let EXPORTED_SYMBOLS = ["trackBrowserWindow"]; >+ >+const Ci = Components.interfaces; >+ >+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); >+Components.utils.import("resource://gre/modules/Services.jsm"); >+ >+ >+// Constants >+const TAB_EVENTS = ["TabSelect", "TabPinned", "TabUnpinned", "TabClose"]; >+const OBSERVER_TOPICS = ["domwindowclosed"]; >+const WINDOW_EVENTS = ["activate", "unload"]; >+const DATA_URI = "data:text/html,<html><head><title>Inactive App Tab</title></head><body><h1>Inactive App Tab</h1><p>This app tab is active in a different window. Focusing this window will restore its contents here.</p></body></html>"; >+ >+ >+// Variables >+let _windows = []; >+let _activeWindow = null; >+let _appTabs = {}; >+// appTabs will look like... >+// { >+// __pinID: { >+// tabs: [tab, tab, tab] >+// } >+// ] >+ >+function d(s) { >+ dump("\n\n" + s + "\n\n"); >+} >+ >+function da() { >+ for (let i in _appTabs) { >+ d(i + " -- " + _appTabs[i].tabs.join("+")); >+ } >+} >+ >+// Exported symbol >+function trackBrowserWindow(aWindow) { >+ onWindowLoad(aWindow); >+} >+ >+ >+// Global methods >+function handleEvent(aEvent) { >+ d(aEvent.type); >+ //da(); >+ switch (aEvent.type) { >+ case "TabSelect": >+ // We only care if you select an app tab AND that app tab isn't already the focused version >+ if (!aEvent.target.pinned || aEvent.target.activePinned) >+ return; >+ onTabSelect(aEvent.target); >+ break; >+ case "TabPinned": >+ onTabPinned(aEvent.target); >+ break; >+ case "TabUnpinned": >+ onTabUnpinned(aEvent.target); >+ break; >+ case "TabClose": >+ // We only care about TabClose on pinned tabs (ones that we haven't forced closed ourselves); >+ if (aEvent.target.pinned && aEvent.target.__pinID) >+ onTabClose(aEvent.target); >+ break; >+ case "activate": >+ // Update _activeWindow for when handling unload (since activate will be fired first) >+ _activeWindow = aEvent.target; >+ // We only want to do something if the selected tab in the window is an >+ // inactive pinned tab. Otherwise, we don't care. >+ let tab = aEvent.target.gBrowser.selectedTab; >+ if (tab.pinned && !tab.__pinActive) >+ onTabSelect(tab); >+ break; >+ case "unload": >+ // Use currentTarget because originalTarget is a XULDocument. >+ onWindowUnload(aEvent.currentTarget); >+ break; >+ } >+ //da(); >+} >+ >+ >+function onTabSelect(aTab) { >+ d("onTabSelect"); >+ let window = aTab.ownerDocument.defaultView; >+ let browser = aTab.linkedBrowser; >+ >+ d(aTab.__pinID); >+ d(aTab.__pinActive); >+ >+ // find the active docshell for this app tab... >+ let apptab = _appTabs[aTab.__pinID]; >+ for each (let tab in apptab.tabs) { >+ if (tab == aTab) >+ continue; >+ >+ d("looking..." + tab); >+ if (tab.__pinActive) { >+ d("found you..." + tab); >+ tab.__pinActive = false; >+ // browser.swapDocShells(tab.linkedBrowser); >+ // window.gBrowser.swapBrowsers(aTab, tab, false); >+ swapTabs(aTab, tab); >+ break; >+ } >+ } >+ aTab.__pinActive = true; >+ //XXXzpao err, is it that easy? >+} >+ >+function onTabPinned(aTab) { >+ let window = aTab.ownerDocument.defaultView; >+ let browser = aTab.linkedBrowser; >+ >+ // Assign a unique id... >+ //XXXzpao for now assume that this will be restored by session restore >+ let pinID = Date.now(); >+ aTab.__pinID = pinID; >+ aTab.__pinActive = true; >+ d(aTab.__pinID); >+ >+ _appTabs[pinID] = { tabs: [aTab] }; >+ // open a new tab in each window >+ // pin it >+ // set __pinID >+ // reorder pinned tabs in each window >+ >+ _windows.forEach(function(win) { >+ if (win === window) >+ return; >+ >+ addInactivePinnedTab(win, pinID, aTab); >+ }); >+} >+ >+ >+function onTabUnpinned(aTab) { >+ // make sure it is the active copy >+ // remove tabs from other windows >+ // delete refs to tabs >+ // remove __pinID and __pinActive >+ // >+ >+ let apptab = _appTabs[aTab.__pinID]; >+ >+ // If this tab isn't the selected one, make it so >+ if (!aTab.__pinActive) >+ onTabSelect(aTab); >+ >+ // Remove the corresponding app tabs in the other windows >+ for each (let tab in apptab.tabs) { >+ if (tab == aTab) >+ continue; >+ >+ // remove __pinID from the tab first so that we don't get caught looping back in here >+ delete tab.__pinID; >+ >+ //XXXzpao Could this close the window before we're ready for that? >+ tab.ownerDocument.defaultView.gBrowser.removeTab(tab); >+ } >+ >+ // Delete our references to those other tabs >+ delete _appTabs[aTab.__pinID]; >+ >+ // Clear out attributes on the tab that we set >+ delete aTab.__pinID; >+ delete aTab.__pinActive; >+} >+ >+ >+function onTabClose(aTab) { >+ // much like onTabUnpinned... >+ >+ let apptab = _appTabs[aTab.__pinID]; >+ >+ // Remove the corresponding app tabs in the other windows >+ for each (let tab in apptab.tabs) { >+ if (tab == aTab) >+ continue; >+ >+ // remove __pinID from the tab first so that we don't get caught looping back in here >+ delete tab.__pinID; >+ >+ //XXXzpao Could this close the window before we're ready for that? >+ tab.ownerDocument.defaultView.gBrowser.removeTab(tab); >+ } >+ >+ // Delete our reference to this app tab >+ delete _appTabs[aTab.__pinID]; >+} >+ >+ >+function onWindowLoad(aWindow) { >+ // Add event listeners... >+ TAB_EVENTS.forEach(function(e) { >+ aWindow.gBrowser.tabContainer.addEventListener(e, handleEvent, false); >+ }); >+ >+ WINDOW_EVENTS.forEach(function(e) { >+ aWindow.addEventListener(e, handleEvent, false); >+ }); >+ >+ if (_activeWindow) { >+ // Now we need to add the pinned tabs... >+ // In theory, any given window will have the right order... >+ let apptabs = Array.filter(_activeWindow.gBrowser.tabs, function(tab) { >+ return tab.pinned; >+ }); >+ apptabs.forEach(function(tab) addInactivePinnedTab(aWindow, tab.__pinID, tab)); >+ } >+ >+ _windows.push(aWindow); >+ // This is called after activate would have been fired, so set this to the active window >+ //XXXzpao should use focusmanager to actually check... >+ _activeWindow = aWindow; >+} >+ >+ >+function onWindowUnload(aWindow) { >+ // remove event listeners >+ // look for active pinned tabs >+ // swap into active window >+ // remove refs to tabs from this window >+ // remove ref to window >+ // if last window, do something special??? >+ // >+ >+ // Remove event listeners >+ TAB_EVENTS.forEach(function(event) { >+ aWindow.gBrowser.tabContainer.removeEventListener(event, handleEvent, false); >+ }); >+ WINDOW_EVENTS.forEach(function(event) { >+ aWindow.removeEventListener(event, handleEvent, false); >+ }); >+ >+ //XXXzpao hmm, we might need to do something special for the last window closing >+ if (aWindow == _activeWindow) { >+ _activeWindow = null; >+ _appTabs = {}; >+ _windows = []; >+ return; >+ } >+ >+ // Find App Tabs from this window >+ let winAppTabs = Array.filter(aWindow.gBrowser.tabs, function(tab) { >+ return tab.pinned; >+ }); >+ d(winAppTabs); >+ winAppTabs.forEach(function(tab) { >+ // If the app tab is active in the closing window, move it >+ let tabIndex = _appTabs[tab.__pinID].tabs.indexOf(tab); >+ if (tab.__pinActive) { >+ let newActiveTab = >+ Array.filter(_activeWindow.gBrowser.tabs, function(t) t.__pinID == tab.__pinID)[0]; >+ onTabSelect(newActiveTab); >+ } >+ >+ // Now make sure we remove the ref to this tab >+ _appTabs[tab.__pinID].tabs.splice(tabIndex, 1); >+ }); >+ >+ _windows.splice(_windows.indexOf(aWindow), 1); >+ >+} >+ >+ >+ >+function addInactivePinnedTab(aWindow, aPinID, aActiveTab) { >+ let tab = aWindow.gBrowser.addTab(DATA_URI); >+ >+ // Set attributes we're going to use >+ tab.__pinID = aPinID; >+ tab.__pinActive = false; >+ >+ // Update the favicon >+ tab.linkedBrowser.addEventListener("load", function() { >+ tab.linkedBrowser.removeEventListener("pageshow", arguments.callee, true); >+ aWindow.gBrowser.setIcon(tab, aActiveTab.linkedBrowser.mIconURL); >+ }, true); >+ >+ aWindow.gBrowser.pinTab(tab, true); >+ _appTabs[aPinID].tabs.push(tab); >+ return tab; >+} >+ >+// This is strikingly similar to tabbrowser.xml#swapBrowsersAndCloseOther but >+// without butchering that, it was more work to re-purpose it for our needs. >+// This swaps aOtherTab into aTab (the direction is important to make sure updates >+// to urlbar, favicon, etc are transferred correctly >+function swapTabs(aTab, aOtherTab) { >+ d("begin swapTabs"); >+ let myWindow = aTab.ownerDocument.defaultView; >+ let myTabBrowser = myWindow.gBrowser; >+ let myBrowser = aTab.linkedBrowser; >+ >+ let otherWindow = aOtherTab.ownerDocument.defaultView; >+ let otherTabBrowser = otherWindow.gBrowser; >+ let otherBrowser = aOtherTab.linkedBrowser; >+ >+ let myIndex = aTab._tPos; >+ let myFilter = myTabBrowser.mTabFilters[myIndex]; >+ let myTabListener = myTabBrowser.mTabListeners[myIndex]; >+ myTabBrowser.webProgress.removeProgressListener(myFilter); >+ myFilter.removeProgressListener(myTabListener); >+ let myTabListenerBlank = myTabListener.mBlank; >+ >+ let otherIndex = aOtherTab._tPos; >+ let otherFilter = otherTabBrowser.mTabFilters[otherIndex]; >+ let otherTabListener = otherTabBrowser.mTabListeners[otherIndex]; >+ otherTabBrowser.webProgress.removeProgressListener(otherFilter); >+ otherFilter.removeProgressListener(otherTabListener); >+ let otherTabListenerBlank = otherTabListener.mBlank; >+ >+ d("swapTabs checkpoint 1"); >+ // Update the favicon from the other tab into this one. We don't need to update >+ // in the other direction because we want the favicon to stay. >+ if (otherBrowser.mIconURL) >+ myTabBrowser.setIcon(aTab, otherBrowser.mIconURL); >+ >+ // Update the busy state. >+ let isBusy = aOtherTab.hasAttribute("busy"); >+ if (isBusy) { >+ aTab.setAttribute("busy", "true"); >+ myTabBrowser._tabAttrModified(aTab); >+ if (aTab == myTabBrowser.selectedTab) >+ myTabBrowser.mIsBusy = true; >+ // Remove the busy state from the other window >+ aOtherTab.removeAttribute("busy"); >+ otherTabBrowser._tabAttrModified(aTab); >+ if (aTab == otherTabBrowser.selectedTab) >+ otherTabBrowser.mIsBusy = true; >+ } >+ d("swapTabs checkpoint 2"); >+ >+ // Swap the DocShells >+ myBrowser.swapDocShells(otherBrowser); >+ d("swapTabs checkpoint 3"); >+ >+ // Restore Progress Listeners >+ myTabListener = myTabBrowser.mTabProgressListener(aTab, myBrowser, >+ myTabListenerBlank); >+ d("swapTabs checkpoint 4"); >+ myTabBrowser.mTabListeners[myIndex] = myTabListener; >+ d("swapTabs checkpoint 5"); >+ myFilter.addProgressListener(myTabListener, Ci.nsIWebProgress.NOTIFY_ALL); >+ d("swapTabs checkpoint 6"); >+ myBrowser.webProgress.addProgressListener(myFilter, Ci.nsIWebProgress.NOTIFY_ALL); >+ d("swapTabs checkpoint 7"); >+ >+ otherTabListener = >+ otherTabBrowser.mTabProgressListener(aTab, otherBrowser, otherTabListenerBlank); >+ otherTabBrowser.mTabListeners[otherIndex] = otherTabListener; >+ otherFilter.addProgressListener(otherTabListener, Ci.nsIWebProgress.NOTIFY_ALL); >+ otherBrowser.webProgress.addProgressListener(otherFilter, Ci.nsIWebProgress.NOTIFY_ALL); >+ d("swapTabs checkpoint 8"); >+ >+ if (isBusy) >+ myTabBrowser.setTabTitleLoading(aTab); >+ else >+ myTabBrowser.setTabTitle(aTab); >+ //myTabBrowser.updateIcon(aTab); >+ d("swapTabs checkpoint 9"); >+ >+ // Make sure the _fastFind ref in otherBrowser points to the right docshell; >+ // if (aOtherTab == otherTabBrowser.selectedTab) { >+ // otherTabBrowser._fastFind.setDocShell(otherBrowser.docShell); >+ // otherTabBrowser.updateTitlebar(); >+ // } >+ d("my docshell - " + myBrowser.docShell + "\nother docshell - " + otherBrowser.docShell); >+ d("my docshell - " + myBrowser.docShell.currentURI.spec + "\nother docshell - " + otherBrowser.docShell.currentURI.spec); >+ >+ // This triggers another "TabSelect" event, which isn't ideal, but oh well >+ if (aTab == myTabBrowser.selectedTab) { >+ // We don't want some things to happen, so pretend we're in preview mode and do a little extra work ourselves >+ // myTabBrowser._previewMode = true; >+ myTabBrowser.updateCurrentBrowser(true); >+ // myTabBrowser.updateTitlebar(); >+ // myTabBrowser._previewMode = false; >+ } >+ >+ // if (aOtherTab == otherTabBrowser.selectedTab) >+ // otherTabBrowser.updateCurrentBrowser(true); >+ >+ >+ d("end swapTabs"); >+} >diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js >--- a/browser/base/content/browser.js >+++ b/browser/base/content/browser.js >@@ -1582,16 +1582,20 @@ function delayedStartup(isLoadingBlank, > catch(ex) { > Components.utils.reportError("Failed to init content pref service:\n" + ex); > } > > let NP = {}; > Cu.import("resource:///modules/NetworkPrioritizer.jsm", NP); > NP.trackBrowserWindow(window); > >+ let AppTabs = {}; >+ Cu.import("resource:///modules/AppTabs.jsm", AppTabs); >+ AppTabs.trackBrowserWindow(window); >+ > // initialize the session-restore service (in case it's not already running) > try { > Cc["@mozilla.org/browser/sessionstore;1"] > .getService(Ci.nsISessionStore) > .init(window); > } catch (ex) { > dump("nsSessionStore could not be initialized: " + ex + "\n"); > } >diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml >--- a/browser/base/content/tabbrowser.xml >+++ b/browser/base/content/tabbrowser.xml >@@ -222,16 +222,17 @@ > clonedItem.removeAttribute("key"); > > parentPopup.insertBefore(clonedItem, nextItem); > ]]></body> > </method> > > <method name="pinTab"> > <parameter name="aTab"/> >+ <parameter name="aSecondary"/> > <body><![CDATA[ > if (aTab.pinned) > return; > > if (aTab.hidden) > this.showTab(aTab); > > this.moveTabTo(aTab, this._numPinnedTabs); >@@ -240,24 +241,27 @@ > this.tabContainer._positionPinnedTabs(); > this.tabContainer.adjustTabstrip(); > > this.getBrowserForTab(aTab).docShell.isAppTab = true; > > if (aTab.selected) > this._setCloseKeyState(false); > >- let event = document.createEvent("Events"); >- event.initEvent("TabPinned", true, false); >- aTab.dispatchEvent(event); >+ if (!aSecondary) { >+ let event = document.createEvent("Events"); >+ event.initEvent("TabPinned", true, false); >+ aTab.dispatchEvent(event); >+ } > ]]></body> > </method> > > <method name="unpinTab"> > <parameter name="aTab"/> >+ <parameter name="aSecondary"/> > <body><![CDATA[ > if (!aTab.pinned) > return; > > this.moveTabTo(aTab, this._numPinnedTabs - 1); > aTab.setAttribute("fadein", "true"); > aTab.removeAttribute("pinned"); > aTab.style.MozMarginStart = ""; >@@ -265,19 +269,21 @@ > this.tabContainer._positionPinnedTabs(); > this.tabContainer.adjustTabstrip(); > > this.getBrowserForTab(aTab).docShell.isAppTab = false; > > if (aTab.selected) > this._setCloseKeyState(true); > >- let event = document.createEvent("Events"); >- event.initEvent("TabUnpinned", true, false); >- aTab.dispatchEvent(event); >+ if (!aSecondary) { >+ let event = document.createEvent("Events"); >+ event.initEvent("TabUnpinned", true, false); >+ aTab.dispatchEvent(event); >+ } > ]]></body> > </method> > > <method name="previewTab"> > <parameter name="aTab"/> > <parameter name="aCallback"/> > <body> > <![CDATA[ >@@ -896,17 +902,19 @@ > this._callProgressListeners(null, "onUpdateCurrentBrowser", > [listener.mStateFlags, listener.mStatus, > listener.mMessage, listener.mTotalProgress], > true, false); > } > > // Don't switch the fast find or update the titlebar (bug 540248) - this tab switch is temporary > if (!this._previewMode) { >+ dump("\n\nHOMEBOY DID WHAT TO _fastFind\n\n"); > this._fastFind.setDocShell(this.mCurrentBrowser.docShell); >+ dump("\n\nHOMEBOY TRIED TO SETDOCSHELL TO _fastFind\n\n"); > > this.updateTitlebar(); > > this.mCurrentTab.removeAttribute("titlechanged"); > } > > // If the new tab is busy, and our current state is not busy, then > // we need to fire a start to all progress listeners. >diff --git a/browser/components/sessionstore/src/nsSessionStore.js b/browser/components/sessionstore/src/nsSessionStore.js >--- a/browser/components/sessionstore/src/nsSessionStore.js >+++ b/browser/components/sessionstore/src/nsSessionStore.js >@@ -111,17 +111,17 @@ XXX keep these in sync with all the attr > const CAPABILITIES = [ > "Subframes", "Plugins", "Javascript", "MetaRedirects", "Images", > "DNSPrefetch", "Auth", "WindowControl" > ]; > > // These keys are for internal use only - they shouldn't be part of the JSON > // that gets saved to disk nor part of the strings returned by the API. > const INTERNAL_KEYS = ["_tabStillLoading", "_hosts", "_formDataSaved", >- "_shouldRestore"]; >+ "_shouldRestore", "__pinActive", "__pinID"]; > > // These are tab events that we listen to. > const TAB_EVENTS = ["TabOpen", "TabClose", "TabSelect", "TabShow", "TabHide", > "TabPinned", "TabUnpinned"]; > > #ifndef XP_WIN > #define BROKEN_WM_Z_ORDER > #endif >@@ -197,16 +197,19 @@ SessionStoreService.prototype = { > > // time in milliseconds (Date.now()) when the session was last written to file > _lastSaveTime: 0, > > // time in milliseconds when the session was started (saved across sessions), > // defaults to now if no session was restored or timestamp doesn't exist > _sessionStartTime: Date.now(), > >+ // States for app tabs >+ _apptabs: [], >+ > // states for all currently opened windows > _windows: {}, > > // states for all recently closed windows > _closedWindows: [], > > // not-"dirty" windows usually don't need to have their data updated > _dirtyWindows: {}, >@@ -696,33 +699,39 @@ SessionStoreService.prototype = { > case "input": > case "DOMAutoComplete": > this.onTabInput(win, aEvent.currentTarget); > break; > case "TabOpen": > this.onTabAdd(win, aEvent.originalTarget); > break; > case "TabClose": >+ // If this was a closing app tab, then we only care about it if it was active >+ if (aEvent.target.pinned && !aEvent.target.__pinActive) >+ return; >+ > // aEvent.detail determines if the tab was closed by moving to a different window > if (!aEvent.detail) > this.onTabClose(win, aEvent.originalTarget); > this.onTabRemove(win, aEvent.originalTarget); > break; > case "TabSelect": > this.onTabSelect(win); > break; > case "TabShow": > this.onTabShow(win, aEvent.originalTarget); > break; > case "TabHide": > this.onTabHide(win, aEvent.originalTarget); > break; > case "TabPinned": >+ this.onTabPinned(win, aEvent.originalTarget); >+ break; > case "TabUnpinned": >- this.saveStateDelayed(win); >+ this.onTabUnpinned(win, aEvent.originalTarget); > break; > } > > this._clearRestoringWindows(); > }, > > /** > * If it's the first window load since app start... >@@ -937,16 +946,17 @@ SessionStoreService.prototype = { > > // clear this window from the list > delete this._windows[aWindow.__SSi]; > > // save the state without this window to disk > this.saveStateDelayed(); > } > >+ //XXXzpao need to adjust this for app tabs > for (let i = 0; i < tabbrowser.tabs.length; i++) { > this.onTabRemove(aWindow, tabbrowser.tabs[i], true); > } > > // cache the window state until the window is completely gone > aWindow.__SS_dyingCache = winData; > > delete aWindow.__SSi; >@@ -1142,29 +1152,51 @@ SessionStoreService.prototype = { > this._tabsToRestore.hidden.push(aTab); > } > > // Default delay of 2 seconds gives enough time to catch multiple TabHide > // events due to changing groups in Panorama. > this.saveStateDelayed(aWindow); > }, > >+ /* >+ * When a tab gets pinned (becomes an "app tab") then we want to make sure >+ * that tab isn't represented in the state for that window, but is instead >+ * added to this._apptabs. >+ */ >+ onTabPinned: function sss_onTabPinned(aWindow) { >+ this.saveStateDelayed(aWindow); >+ }, >+ >+ onTabUnpinned: function sss_onTabUnpinned(aWindow, aTab) { >+ // find matching app tab >+ // remove it from _apptabs >+ // update window data >+ this._appTabs = this._appTabs.filter(function(apptabData) { >+ return apptabData.__pinID == aTab.__pinID; >+ }); >+ this.saveStateDelayed(aWindow); >+ }, >+ >+ > /* ........ nsISessionStore API .............. */ > > getBrowserState: function sss_getBrowserState() { > return this._toJSONString(this._getCurrentState()); > }, > > setBrowserState: function sss_setBrowserState(aState) { > this._handleClosedWindows(); > > try { > var state = JSON.parse(aState); > } > catch (ex) { /* invalid state object - don't restore anything */ } >+ >+ //XXXzpao well technically you could set state with just { apptabs: [] } > if (!state || !state.windows) > throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); > > this._browserSetState = true; > > // Make sure _tabsToRestore is emptied out > this._resetRestoringState(); > >@@ -1695,20 +1727,23 @@ SessionStoreService.prototype = { > if (browser.userTypedValue) { > tabData.userTypedValue = browser.userTypedValue; > tabData.userTypedClear = browser.userTypedClear; > } else { > delete tabData.userTypedValue; > delete tabData.userTypedClear; > } > >- if (aTab.pinned) >- tabData.pinned = true; >- else >+ if (aTab.pinned) { >+ tabData.__pinActive = aTab.__pinActive; >+ tabData.__pinID = aTab.__pinID; >+ } >+ else { > delete tabData.pinned; >+ } > tabData.hidden = aTab.hidden; > > var disallow = []; > for (var i = 0; i < CAPABILITIES.length; i++) > if (!browser.docShell["allow" + CAPABILITIES[i]]) > disallow.push(CAPABILITIES[i]); > if (disallow.length > 0) > tabData.disallow = disallow.join(","); >@@ -2375,17 +2410,17 @@ SessionStoreService.prototype = { > this.activeWindowSSiCache = activeWindow.__SSi || ""; > } > ix = windows.indexOf(this.activeWindowSSiCache); > // We don't want to restore focus to a minimized window or a window which had all its > // tabs stripped out (doesn't exist). > if (ix != -1 && total[ix] && total[ix].sizemode == "minimized") > ix = -1; > >- return { windows: total, selectedWindow: ix + 1, _closedWindows: lastClosedWindowsCopy }; >+ return { windows: total, selectedWindow: ix + 1, _closedWindows: lastClosedWindowsCopy, apptabs: this._apptabs }; > }, > > /** > * serialize session data for a window > * @param aWindow > * Window reference > * @returns string > */ >@@ -2415,16 +2450,18 @@ SessionStoreService.prototype = { > > // Make sure we keep __SS_lastSessionWindowID around for cases like entering > // or leaving PB mode. > if (aWindow.__SS_lastSessionWindowID) > this._windows[aWindow.__SSi].__lastSessionWindowID = > aWindow.__SS_lastSessionWindowID; > > this._dirtyWindows[aWindow.__SSi] = false; >+ //XXXzpao we might want to do something here to prevent tabless windows >+ // if (this._windows[aWindow.__SSi].isOnlyAppTabs > }, > > /* ........ Restoring Functionality .............. */ > > /** > * restore features to a single window > * @param aWindow > * Window reference >@@ -3605,19 +3642,18 @@ SessionStoreService.prototype = { > > /** > * whether the user wants to load any other page at startup > * (except the homepage) - needed for determining whether to overwrite the current tabs > * C.f.: nsBrowserContentHandler's defaultArgs implementation. > * @returns bool > */ > _isCmdLineEmpty: function sss_isCmdLineEmpty(aWindow, aState) { >- var pinnedOnly = aState.windows && >- aState.windows.every(function (win) >- win.tabs.every(function (tab) tab.pinned)); >+ var pinnedOnly = aState.windows && aState.windows.length == 1 && >+ aState.windows[0].tabs.every(function (tab) tab.pinned); > > if (!pinnedOnly) { > let defaultArgs = Cc["@mozilla.org/browser/clh;1"]. > getService(Ci.nsIBrowserHandler).defaultArgs; > if (aWindow.arguments && > aWindow.arguments[0] && > aWindow.arguments[0] == defaultArgs) > aWindow.arguments[0] = null;
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
Actions:
View
|
Diff
|
Review
Attachments on
bug 587873
:
467976
|
469553
| 542940