Bug 579361 - Enhance SessionStore service in Fennec [r=vingtetun]

This commit is contained in:
Mark Finkle 2010-07-19 01:27:52 -04:00
Родитель ab02cbfb32
Коммит ff2b05499a
12 изменённых файлов: 438 добавлений и 81 удалений

Просмотреть файл

@ -1163,11 +1163,8 @@ KeyModule.prototype = {
handleEvent: function handleEvent(evInfo) {
if (evInfo.event.type == "keydown" || evInfo.event.type == "keyup" || evInfo.event.type == "keypress") {
let keyer = this._browserViewContainer.customKeySender;
if (keyer) {
if (keyer)
keyer.dispatchKeyEvent(evInfo.event);
evInfo.event.preventDefault();
evInfo.event.stopPropagation();
}
}
},

Просмотреть файл

@ -84,7 +84,7 @@
</div>
<div id="about">
<img src="chrome://browser/skin/images/mozilla-32.png"/> <a href="http://www.mozilla.com/about/">&aboutHome.aboutMozilla;</a>
<img src="chrome://browser/skin/images/mozilla-32.png"/> <a href="http://www.mozilla.com/about/">&aboutHome.aboutMozilla;</a>
</div>
</div>
@ -162,15 +162,19 @@
let tabs = data.windows[0].tabs;
for (let i=0; i<tabs.length; i++) {
let url = tabs[i].url;
let tabData = tabs[i];
if ("entries" in tabData)
tabData = tabData.entries[0];
let url = tabData.url;
if (url.indexOf("about:") == 0)
continue;
let title = tabs[i].title;
let title = tabData.title;
if (!title)
continue;
let uri = chromeWin.makeURI(url);
let uri = chromeWin.Util.makeURI(url);
let favicon = chromeWin.gFaviconService.getFaviconImageForPage(uri).spec;
let outer = document.createElement("div");

Просмотреть файл

@ -444,7 +444,8 @@
},
set currentURI(aURI) { this.loadURI(aURI.spec, null, null, null); },
referringURI: null,
get sessionHistory() { throw "sessionHistory: Not Remoteable"; },
get sessionHistory() { return null; },
set sessionHistory(aValue) { },
_currentURI: null,
_browser: this,

Просмотреть файл

@ -102,7 +102,7 @@ var BrowserUI = {
}
},
_titleChanged : function(aBrowser) {
_titleChanged: function(aBrowser) {
var browser = Browser.selectedBrowser;
if (browser && aBrowser != browser)
return;
@ -131,7 +131,7 @@ var BrowserUI = {
}
},
_updateButtons : function(aBrowser) {
_updateButtons: function(aBrowser) {
let back = document.getElementById("cmd_back");
let forward = document.getElementById("cmd_forward");
@ -150,7 +150,7 @@ var BrowserUI = {
}
},
_tabSelect : function(aEvent) {
_tabSelect: function(aEvent) {
let browser = Browser.selectedBrowser;
this._titleChanged(browser);
this._updateToolbar();
@ -159,7 +159,7 @@ var BrowserUI = {
this.updateStar();
},
showToolbar : function showToolbar(aEdit) {
showToolbar: function showToolbar(aEdit) {
this.hidePanel();
this._editURI(aEdit);
if (aEdit)
@ -195,7 +195,7 @@ var BrowserUI = {
this._edit.value = aCaption;
},
_editURI : function _editURI(aEdit) {
_editURI: function _editURI(aEdit) {
var icons = document.getElementById("urlbar-icons");
if (aEdit && icons.getAttribute("mode") != "edit") {
icons.setAttribute("mode", "edit");
@ -236,7 +236,7 @@ var BrowserUI = {
return null;
},
pushDialog : function pushDialog(aDialog) {
pushDialog: function pushDialog(aDialog) {
// If we have a dialog push it on the stack and set the attr for CSS
if (aDialog) {
this.lockToolbar();
@ -246,7 +246,7 @@ var BrowserUI = {
}
},
popDialog : function popDialog() {
popDialog: function popDialog() {
if (this._dialogs.length) {
this._dialogs.pop();
this.unlockToolbar();
@ -297,7 +297,7 @@ var BrowserUI = {
return targetNode ? true : false;
},
switchPane : function switchPane(id) {
switchPane: function switchPane(id) {
let button = document.getElementsByAttribute("linkedpanel", id)[0];
if (button)
button.checked = true;
@ -329,7 +329,7 @@ var BrowserUI = {
return this.starButton = document.getElementById("tool-star");
},
sizeControls : function(windowW, windowH) {
sizeControls: function(windowW, windowH) {
// tabs
document.getElementById("tabs").resize();
@ -344,7 +344,7 @@ var BrowserUI = {
formHelper.top = windowH - formHelper.getBoundingClientRect().height;
},
init : function() {
init: function() {
this._edit = document.getElementById("urlbar-edit");
this._throbber = document.getElementById("urlbar-throbber");
this._favicon = document.getElementById("urlbar-favicon");
@ -401,12 +401,12 @@ var BrowserUI = {
FormHelperUI.init();
},
uninit : function() {
uninit: function() {
ExtensionsView.uninit();
ConsoleView.uninit();
},
update : function(aState) {
update: function(aState) {
let icons = document.getElementById("urlbar-icons");
let browser = Browser.selectedBrowser;
@ -430,7 +430,7 @@ var BrowserUI = {
}
},
_updateIcon : function(aIconSrc) {
_updateIcon: function(aIconSrc) {
this._favicon.src = aIconSrc || "";
if (Browser.selectedTab.isLoading()) {
this._throbber.hidden = false;
@ -444,7 +444,7 @@ var BrowserUI = {
}
},
getDisplayURI : function(browser) {
getDisplayURI: function(browser) {
if (!this._URIFixup)
this._URIFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
@ -457,7 +457,7 @@ var BrowserUI = {
},
/* Set the location to the current content */
updateURI : function() {
updateURI: function() {
var browser = Browser.selectedBrowser;
// FIXME: deckbrowser should not fire TabSelect on the initial tab (bug 454028)
@ -477,7 +477,7 @@ var BrowserUI = {
this._setURI(urlString);
},
goToURI : function(aURI) {
goToURI: function(aURI) {
aURI = aURI || this._edit.value;
if (!aURI)
return;
@ -509,7 +509,7 @@ var BrowserUI = {
gHistSvc.markPageAsTyped(uri);
},
showAutoComplete : function showAutoComplete() {
showAutoComplete: function showAutoComplete() {
if (this.isAutoCompleteOpen())
return;
@ -533,7 +533,7 @@ var BrowserUI = {
return this._edit.popup.popupOpen;
},
doButtonSearch : function(button) {
doButtonSearch: function(button) {
if (!("engine" in button) || !button.engine)
return;
@ -561,14 +561,14 @@ var BrowserUI = {
getBrowser().loadURIWithFlags(submission.uri.spec, flags, null, null, submission.postData);
},
updateStar : function() {
updateStar: function() {
if (PlacesUtils.getMostRecentBookmarkForURI(Browser.selectedBrowser.currentURI) != -1)
this.starButton.setAttribute("starred", "true");
else
this.starButton.removeAttribute("starred");
},
newTab : function newTab(aURI, aOwner) {
newTab: function newTab(aURI, aOwner) {
aURI = aURI || "about:blank";
let tab = Browser.addTab(aURI, true, aOwner);
@ -598,15 +598,24 @@ var BrowserUI = {
this.newTab(aURI, aOwner);
},
closeTab : function closeTab(aTab) {
closeTab: function closeTab(aTab) {
// If no tab is passed in, assume the current tab
Browser.closeTab(aTab || Browser.selectedTab);
},
selectTab : function selectTab(aTab) {
selectTab: function selectTab(aTab) {
Browser.selectedTab = aTab;
},
undoCloseTab: function undoCloseTab(aIndex) {
let tab = null;
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
if (ss.getClosedTabCount(window) > (aIndex || 0)) {
tab = ss.undoCloseTab(window, aIndex || 0);
}
return tab;
},
isTabsVisible: function isTabsVisible() {
// The _1, _2 and _3 are to make the js2 emacs mode happy
let [leftvis,_1,_2,_3] = Browser.computeSidebarVisibility();
@ -824,6 +833,7 @@ var BrowserUI = {
case "cmd_menu":
case "cmd_newTab":
case "cmd_closeTab":
case "cmd_undoCloseTab":
case "cmd_actions":
case "cmd_panel":
case "cmd_sanitize":
@ -916,6 +926,9 @@ var BrowserUI = {
case "cmd_closeTab":
this.closeTab();
break;
case "cmd_undoCloseTab":
this.undoCloseTab();
break;
case "cmd_sanitize":
{
// disable the button temporarily to indicate something happened

Просмотреть файл

@ -200,8 +200,8 @@ function onDebugKeyPress(ev) {
const q = 81; // toggle orientation
const r = 82; // reset visible rect
const s = 83;
const t = 84; // debug given list of tiles separated by space
const u = 85;
const t = 84;
const u = 85; // debug given list of tiles separated by space
const v = 86;
const w = 87;
const x = 88;
@ -278,7 +278,7 @@ function onDebugKeyPress(ev) {
case b:
window.tileMapMode = true;
break;
case t:
case u:
let ijstrs = window.prompt('row,col plz').split(' ');
for each (let ijstr in ijstrs) {
let [i, j] = ijstr.split(',').map(function (x) { return parseInt(x); });
@ -2584,6 +2584,7 @@ Tab.prototype = {
// Create the browser using the current width the dynamically size the height
let browser = this._browser = document.createElement("browser");
this._chromeTab.linkedBrowser = browser;
browser.setAttribute("style", "overflow: -moz-hidden-unscrollable; visibility: hidden;");
browser.setAttribute("type", "content");

Просмотреть файл

@ -113,6 +113,7 @@
<command id="cmd_newTab" label="&newtab.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_closeTab" label="&closetab.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_remoteTabs" oncommand="WeaveGlue.openRemoteTabs();"/>
<command id="cmd_undoCloseTab" oncommand="CommandUpdater.doCommand(this.id);"/>
<!-- bookmarking -->
<command id="cmd_star" label="&star.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
@ -185,6 +186,7 @@
<!-- tabs -->
<key id="key_newTab" key="t" modifiers="accel" command="cmd_newTab"/>
<key id="key_closeTab" key="w" modifiers="accel" command="cmd_closeTab"/>
<key id="key_undoCloseTab" key="t" modifiers="accel,shift" command="cmd_undoCloseTab"/>
</keyset>
<stack flex="1" id="stack">

Просмотреть файл

@ -58,6 +58,7 @@ _BROWSER_FILES = \
browser_viewport.js \
browser_navigation.js \
browser_preferences_basic.js \
browser_sessionstore.js \
browser_blank_01.html \
browser_blank_02.html \
browser_select.html \

Просмотреть файл

@ -0,0 +1,99 @@
var testURL = "chrome://mochikit/content/browser/mobile/chrome/browser_blank_01.html";
// A queue to order the tests and a handle for each test
var gTests = [];
var gCurrentTest = null;
function pageLoaded(url) {
return function() {
let tab = gCurrentTest._tab;
return !tab.isLoading() && tab.browser.currentURI.spec == url;
}
}
var ss = null;
//------------------------------------------------------------------------------
// Entry point (must be named "test")
function test() {
// The "runNextTest" approach is async, so we need to call "waitForExplicitFinish()"
// We call "finish()" when the tests are finished
waitForExplicitFinish();
ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
// Start the tests
runNextTest();
}
//------------------------------------------------------------------------------
// Iterating tests by shifting test out one by one as runNextTest is called.
function runNextTest() {
// Run the next test until all tests completed
if (gTests.length > 0) {
gCurrentTest = gTests.shift();
info(gCurrentTest.desc);
gCurrentTest.run();
}
else {
// Cleanup. All tests are completed at this point
try {
// Add any cleanup code here
}
finally {
// We must finialize the tests
finish();
}
}
}
//------------------------------------------------------------------------------
// Case: Loading a page and test setting tab values
gTests.push({
desc: "Loading a page and test setting tab values",
_tab: null,
run: function() {
Browser.addTab("about:blank", true);
this._tab = Browser.addTab(testURL, true);
// Wait for the tab to load, then do the test
waitFor(gCurrentTest.onPageReady, pageLoaded(testURL));
},
onPageReady: function() {
// Add some data
ss.setTabValue(gCurrentTest._tab.chromeTab, "test1", "hello");
is(ss.getTabValue(gCurrentTest._tab.chromeTab, "test1"), "hello", "Set/Get tab value matches");
// Close tab and then undo the close
gCurrentTest.numTabs = Browser.tabs.length;
gCurrentTest.numClosed = ss.getClosedTabCount(window);
Browser.closeTab(gCurrentTest._tab);
is(Browser.tabs.length, gCurrentTest.numTabs - 1, "Tab was closed");
is(ss.getClosedTabCount(window), gCurrentTest.numClosed + 1, "Tab was stored");
// SessionStore works with chrome tab elements, not JS tab objects.
// Map the _tab from chrome to JS
gCurrentTest._tab = Browser.getTabFromChrome(ss.undoCloseTab(window, 0));
// Wait for the tab to load, then do the test
waitFor(gCurrentTest.onPageUndo, pageLoaded(testURL));
},
onPageUndo: function() {
is(Browser.tabs.length, gCurrentTest.numTabs, "Tab was reopened");
is(ss.getClosedTabCount(window), gCurrentTest.numClosed, "Tab was removed from store");
is(ss.getTabValue(gCurrentTest._tab.chromeTab, "test1"), "hello", "Set/Get tab value matches after un-close");
ss.deleteTabValue(gCurrentTest._tab.chromeTab, "test1");
is(ss.getTabValue(gCurrentTest._tab.chromeTab, "test1"), "", "Set/Get tab value matches after removing value");
// Shutdown
Browser.closeTab(gCurrentTest._tab);
runNextTest();
}
});

Просмотреть файл

@ -43,8 +43,12 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = browsercomps
XPIDL_MODULE = browsercompsbase
MODULE = MobileComponents
XPIDL_MODULE = MobileComponents
XPIDLSRCS = \
SessionStore.idl \
$(NULL)
EXTRA_PP_COMPONENTS = \
AboutRedirector.js \

Просмотреть файл

@ -26,14 +26,14 @@ category JavaScript-global-property sidebar @mozilla.org/sidebar;1
category JavaScript-global-property external @mozilla.org/sidebar;1
# SessionStore.js
component {90c3dfaf-4245-46d3-9bc1-1d8251ff8c01} SessionStore.js
contract @mozilla.org/mobile/sessionstore;1 {90c3dfaf-4245-46d3-9bc1-1d8251ff8c01}
category app-startup SessionStore service,@mozilla.org/mobile/sessionstore;1
component {8c1f07d6-cba3-4226-a315-8bd43d67d032} SessionStore.js
contract @mozilla.org/browser/sessionstore;1 {8c1f07d6-cba3-4226-a315-8bd43d67d032}
category app-startup SessionStore service,@mozilla.org/browser/sessionstore;1
# BrowserStartup.js
component {1d542abc-c88b-4636-a4ef-075b49806317} BrowserStartup.js
contract @mozilla.org/mobile/browser-startup;1 {1d542abc-c88b-4636-a4ef-075b49806317}
category app-startup BrowserStartup service,@mozilla.org/mobile/browser-startup;1
contract @mozilla.org/browser/browser-startup;1 {1d542abc-c88b-4636-a4ef-075b49806317}
category app-startup BrowserStartup service,@mozilla.org/browser/browser-startup;1
category agent-style-sheets browser-content-stylesheet chrome://browser/content/content.css
category agent-style-sheets browser-cursor-stylesheet chrome://browser/content/cursor.css
@ -64,8 +64,8 @@ contract @mozilla.org/embedcomp/prompt-service;1 {9a61149b-2276-4a0a-b79c-be994a
# BrowserCLH.js
component {be623d20-d305-11de-8a39-0800200c9a66} BrowserCLH.js
contract @mozilla.org/mobile/browser-clh;1 {be623d20-d305-11de-8a39-0800200c9a66}
category command-line-handler m-browser @mozilla.org/mobile/browser-clh;1
contract @mozilla.org/browser/browser-clh;1 {be623d20-d305-11de-8a39-0800200c9a66}
category command-line-handler m-browser @mozilla.org/browser/browser-clh;1
# ContentDispatchChooser.js
component {5a072a22-1e66-4100-afc1-07aed8b62fc5} ContentDispatchChooser.js

Просмотреть файл

@ -0,0 +1,105 @@
/* ***** 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 the Session Store component.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mark Finkle <mfinkle@mozilla.com>
*
* 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 ***** */
#include "nsISupports.idl"
interface nsIDOMWindow;
interface nsIDOMNode;
/**
* nsISessionStore keeps track of the current browsing state.
*
* The nsISessionStore API operates mostly on browser windows and the browser
* tabs contained in them.
*/
[scriptable, uuid(a0a52a85-4032-41d7-b183-9d21009e0a65)]
interface nsISessionStore : nsISupports
{
/**
* Get the current browsing state.
* @returns a JSON string representing the session state.
*/
AString getBrowserState();
/**
* Get the number of restore-able tabs for a browser window
*/
unsigned long getClosedTabCount(in nsIDOMWindow aWindow);
/**
* Get closed tab data
*
* @param aWindow is the browser window for which to get closed tab data
* @returns a JSON string representing the list of closed tabs.
*/
AString getClosedTabData(in nsIDOMWindow aWindow);
/**
* @param aWindow is the browser window to reopen a closed tab in.
* @param aIndex is the index of the tab to be restored (FIFO ordered).
* @returns a reference to the reopened tab.
*/
nsIDOMNode undoCloseTab(in nsIDOMWindow aWindow, in unsigned long aIndex);
/**
* @param aWindow is the browser window associated with the closed tab.
* @param aIndex is the index of the closed tab to be removed (FIFO ordered).
*/
nsIDOMNode forgetClosedTab(in nsIDOMWindow aWindow, in unsigned long aIndex);
/**
* @param aTab is the browser tab to get the value for.
* @param aKey is the value's name.
*
* @returns A string value or an empty string if none is set.
*/
AString getTabValue(in nsIDOMNode aTab, in AString aKey);
/**
* @param aTab is the browser tab to set the value for.
* @param aKey is the value's name.
* @param aStringValue is the value itself (use JSON.stringify/parse before setting JS objects).
*/
void setTabValue(in nsIDOMNode aTab, in AString aKey, in AString aStringValue);
/**
* @param aTab is the browser tab to get the value for.
* @param aKey is the value's name.
*/
void deleteTabValue(in nsIDOMNode aTab, in AString aKey);
};

Просмотреть файл

@ -37,6 +37,7 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@ -62,13 +63,17 @@ const STATE_QUITTING = -1;
function SessionStore() { }
SessionStore.prototype = {
classID: Components.ID("{90c3dfaf-4245-46d3-9bc1-1d8251ff8c01}"),
classID: Components.ID("{8c1f07d6-cba3-4226-a315-8bd43d67d032}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener, Ci.nsIObserver, Ci.nsISupportsWeakReference]),
QueryInterface: XPCOMUtils.generateQI([Ci.nsISessionStore,
Ci.nsIDOMEventListener,
Ci.nsIObserver,
Ci.nsISupportsWeakReference]),
_windows: {},
_lastSaveTime: 0,
_interval: 15000,
_maxTabsUndo: 5,
init: function ss_init() {
// Get file references
@ -90,6 +95,7 @@ SessionStore.prototype = {
try {
this._interval = Services.prefs.getIntPref("sessionstore.interval");
this._maxTabsUndo = Services.prefs.getIntPref("sessionstore.max_tabs_undo");
} catch (e) {}
},
@ -171,18 +177,30 @@ SessionStore.prototype = {
this.onTabLoad(window, aEvent.currentTarget, aEvent);
break;
case "TabOpen":
case "TabClose":
let browser = window.Browser.getTabFromChrome(aEvent.originalTarget).browser;
case "TabClose": {
let browser = aEvent.originalTarget.linkedBrowser;
if (aEvent.type == "TabOpen") {
this.onTabAdd(window, browser);
}
else {
this.onTabClose(window, aEvent.originalTarget);
this.onTabClose(window, browser);
this.onTabRemove(window, browser);
}
break;
case "TabSelect":
this.onTabSelect(window);
}
case "TabSelect": {
let browser = aEvent.originalTarget.linkedBrowser;
this.onTabSelect(window, browser);
break;
}
}
},
receiveMessage: function ss_receiveMessage(aMessage) {
let window = aMessage.target.ownerDocument.defaultView;
switch (aMessage.name) {
case "pageshow":
this.onTabLoad(window, aMessage.target, aMessage);
break;
}
},
@ -198,7 +216,7 @@ SessionStore.prototype = {
// Assign it a unique identifier (timestamp) and create its data object
aWindow.__SSID = "window" + Date.now();
this._windows[aWindow.__SSID] = { tabs: [], selected: 0 };
this._windows[aWindow.__SSID] = { tabs: [], selected: 0, closedTabs: [] };
// Perform additional initialization when the first window is loading
if (this._loadState == STATE_STOPPED) {
@ -247,46 +265,62 @@ SessionStore.prototype = {
},
onTabAdd: function ss_onTabAdd(aWindow, aBrowser, aNoNotification) {
aBrowser.addEventListener("load", this, true);
aBrowser.addEventListener("pageshow", this, true);
aBrowser.messageManager.addMessageListener("pageshow", this, true);
if (!aNoNotification) {
this.saveStateDelayed(aWindow);
}
if (!aNoNotification)
this.saveStateDelayed();
this._updateCrashReportURL(aWindow);
},
onTabRemove: function ss_onTabRemove(aWindow, aBrowser, aNoNotification) {
aBrowser.removeEventListener("load", this, true);
aBrowser.removeEventListener("pageshow", this, true);
aBrowser.messageManager.removeMessageListener("pageshow", this, true);
delete aBrowser.__SS_data;
if (!aNoNotification) {
this.saveStateDelayed(aWindow);
}
if (!aNoNotification)
this.saveStateDelayed();
},
onTabClose: function ss_onTabClose(aWindow, aTab) {
},
onTabLoad: function ss_onTabLoad(aWindow, aBrowser, aEvent) {
// react on "load" and solitary "pageshow" events (the first "pageshow"
// following "load" is too late for deleting the data caches)
if (aEvent.type != "load" && !aEvent.persisted) {
onTabClose: function ss_onTabClose(aWindow, aBrowser) {
if (this._maxTabsUndo == 0)
return;
if (aWindow.Browser.tabs.length > 0) {
// Bundle this browser's data and extra data and save in the closedTabs
// window property
let data = aBrowser.__SS_data;
data.extraData = aBrowser.__SS_extdata;
this._windows[aWindow.__SSID].closedTabs.unshift(data);
let length = this._windows[aWindow.__SSID].closedTabs.length;
if (length > this._maxTabsUndo)
this._windows[aWindow.__SSID].closedTabs.splice(this._maxTabsUndo, length - this._maxTabsUndo);
}
},
onTabLoad: function ss_onTabLoad(aWindow, aBrowser, aMessage) {
delete aBrowser.__SS_data;
this._collectTabData(aBrowser);
this.saveStateDelayed(aWindow);
this.saveStateDelayed();
this._updateCrashReportURL(aWindow);
},
onTabSelect: function ss_onTabSelect(aWindow) {
onTabSelect: function ss_onTabSelect(aWindow, aBrowser) {
if (this._loadState != STATE_RUNNING)
return;
let index = 0;
let browser = aWindow.Browser;
let tabs = browser.tabs;
for (let i = 0; i < tabs.length; i++) {
if (tabs[i].browser == aBrowser) {
index = i;
break;
}
}
this._windows[aWindow.__SSID].selected = index + 1; // 1-based
this._updateCrashReportURL(aWindow);
},
@ -308,6 +342,13 @@ SessionStore.prototype = {
},
saveState: function ss_saveState() {
let data = this._getCurrentState();
this._writeFile(this._sessionFile, JSON.stringify(data));
this._lastSaveTime = Date.now();
},
_getCurrentState: function ss_getCurrentState() {
let self = this;
this._forEachBrowserWindow(function(aWindow) {
self._collectWindowData(aWindow);
@ -317,14 +358,15 @@ SessionStore.prototype = {
let index;
for (index in this._windows)
data.windows.push(this._windows[index]);
this._writeFile(this._sessionFile, JSON.stringify(data));
this._lastSaveTime = Date.now();
return data;
},
_collectTabData: function ss__collectTabData(aBrowser) {
let tabData = { url: aBrowser.currentURI.spec, title: aBrowser.contentTitle };
let tabData = { entries: [{}] };
tabData.entries[0] = { url: aBrowser.currentURI.spec, title: aBrowser.contentTitle };
tabData.index = 1;
tabData.attributes = { image: aBrowser.mIconURL };
aBrowser.__SS_data = tabData;
},
@ -338,8 +380,13 @@ SessionStore.prototype = {
let tabs = aWindow.Browser.tabs;
for (let i = 0; i < tabs.length; i++) {
if (tabs[i].browser.__SS_data)
winData.tabs.push(tabs[i].browser.__SS_data);
if (tabs[i].browser.__SS_data) {
let browser = tabs[i].browser;
let tabData = browser.__SS_data;
if (browser.__SS_extdata)
tabData.extData = browser.__SS_extdata;
winData.tabs.push(tabData);
}
}
},
@ -396,6 +443,89 @@ SessionStore.prototype = {
Components.utils.reportError("SessionStore:" + ex);
}
#endif
},
getBrowserState: function ss_getBrowserState() {
let data = this._getCurrentState();
return JSON.stringify(data);
},
getClosedTabCount: function ss_getClosedTabCount(aWindow) {
if (!aWindow || !aWindow.__SSID)
return 0; // not a browser window, or not otherwise tracked by SS.
return this._windows[aWindow.__SSID].closedTabs.length;
},
getClosedTabData: function ss_getClosedTabData(aWindow) {
if (!aWindow.__SSID)
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
return JSON.stringify(this._windows[aWindow.__SSID].closedTabs);
},
undoCloseTab: function ss_undoCloseTab(aWindow, aIndex) {
if (!aWindow.__SSID)
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
let closedTabs = this._windows[aWindow.__SSID].closedTabs;
if (!closedTabs)
return null;
// default to the most-recently closed tab
aIndex = aIndex || 0;
if (!(aIndex in closedTabs))
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
// fetch the data of closed tab, while removing it from the array
let closedTab = closedTabs.splice(aIndex, 1).shift();
// create a new tab and bring to front
let tab = aWindow.Browser.addTab(closedTab.entries[0].url, true);
// Put back the extra data
tab.browser.__SS_extdata = closedTab.extraData;
// TODO: save and restore more data (position, field values, etc)
return tab.chromeTab;
},
forgetClosedTab: function ss_forgetClosedTab(aWindow, aIndex) {
if (!aWindow.__SSID)
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
let closedTabs = this._windows[aWindow.__SSID].closedTabs;
// default to the most-recently closed tab
aIndex = aIndex || 0;
if (!(aIndex in closedTabs))
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
// remove closed tab from the array
closedTabs.splice(aIndex, 1);
},
getTabValue: function ss_getTabValue(aTab, aKey) {
let browser = aTab.linkedBrowser;
let data = browser.__SS_extdata || {};
return data[aKey] || "";
},
setTabValue: function ss_setTabValue(aTab, aKey, aStringValue) {
let browser = aTab.linkedBrowser;
if (!browser.__SS_extdata)
browser.__SS_extdata = {};
browser.__SS_extdata[aKey] = aStringValue;
this.saveStateDelayed();
},
deleteTabValue: function ss_deleteTabValue(aTab, aKey) {
let browser = aTab.linkedBrowser;
if (browser.__SS_extdata && browser.__SS_extdata[aKey])
delete browser.__SS_extdata[aKey];
else
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
}
};