Bug 459550 - Port Bug 448976 (turn the Session Restore prompt into an error page) to SeaMonkey. r+sr=Neil, a=wanted2+.
This commit is contained in:
Родитель
c8f621edc5
Коммит
1158298990
|
@ -196,7 +196,6 @@ pref("browser.tabs.opentabfor.middleclick", false);
|
|||
pref("browser.tabs.opentabfor.urlbar", false);
|
||||
pref("browser.tabs.tooltippreview.enable", true);
|
||||
pref("browser.tabs.tooltippreview.width", 300);
|
||||
pref("browser.tabs.undoclose.depth", 3);
|
||||
pref("browser.tabs.autoHide", true);
|
||||
pref("browser.tabs.forceHide", false);
|
||||
pref("browser.tabs.warnOnClose", true);
|
||||
|
@ -321,6 +320,11 @@ pref("browser.sessionstore.postdata", 0);
|
|||
// on which sites to save text data, POSTDATA and cookies
|
||||
// 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
|
||||
pref("browser.sessionstore.privacy_level", 1);
|
||||
// number of crashes that can occur before the about:sessionrestore page is displayed
|
||||
// (this pref has no effect if more than 6 hours have passed since the last crash)
|
||||
pref("browser.sessionstore.max_resumed_crashes", 1);
|
||||
// how many tabs can be reopened (per window)
|
||||
pref("browser.sessionstore.max_tabs_undo", 10);
|
||||
|
||||
pref("shell.checkDefaultClient", true);
|
||||
// We want to check if we are the default client for browser and mail. See
|
||||
|
|
|
@ -744,7 +744,7 @@
|
|||
|
||||
var undoItem = document.getAnonymousElementByAttribute(this, "tbattr", "tabbrowser-undoclosetab");
|
||||
undoItem.setAttribute("disabled", this.savedBrowsers.length == 0);
|
||||
undoItem.hidden = this.mPrefs.getIntPref("browser.tabs.undoclose.depth") <= 0;
|
||||
undoItem.hidden = this.mPrefs.getIntPref("browser.sessionstore.max_tabs_undo") <= 0;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -1425,7 +1425,7 @@
|
|||
// Save the tab for undo.
|
||||
// Even though we navigate to about:blank, it costs more RAM than
|
||||
// really closing the tab. The pref controls how far you can undo
|
||||
var maxUndoDepth = this.mPrefs.getIntPref("browser.tabs.undoclose.depth");
|
||||
var maxUndoDepth = this.mPrefs.getIntPref("browser.sessionstore.max_tabs_undo");
|
||||
var oldSH = oldBrowser.webNavigation.sessionHistory;
|
||||
var inOnLoad = oldBrowser.docShell.isExecutingOnLoadHandler;
|
||||
if (maxUndoDepth <= 0 || oldSH.count == 0 || aDisableUndo || inOnLoad) {
|
||||
|
|
|
@ -0,0 +1,328 @@
|
|||
/* ***** 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 nsSessionStore component.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Simon Bünzli <zeniko@gmail.com>
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
var gStateObject;
|
||||
var gTreeData;
|
||||
|
||||
// Page initialization
|
||||
|
||||
window.onload = function() {
|
||||
// the crashed session state is kept inside a textbox so that SessionStore picks it up
|
||||
// (for when the tab is closed or the session crashes right again)
|
||||
var sessionData = document.getElementById("sessionData");
|
||||
if (!sessionData.value) {
|
||||
var ss = Components.classes["@mozilla.org/suite/sessionstartup;1"].getService(Components.interfaces.nsISessionStartup);
|
||||
sessionData.value = ss.state;
|
||||
if (!sessionData.value) {
|
||||
document.getElementById("errorTryAgain").disabled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// make sure the data is tracked to be restored in case of a subsequent crash
|
||||
var event = document.createEvent("UIEvents");
|
||||
event.initUIEvent("input", true, true, window, 0);
|
||||
sessionData.dispatchEvent(event);
|
||||
|
||||
var s = new Components.utils.Sandbox("about:blank");
|
||||
gStateObject = Components.utils.evalInSandbox("(" + sessionData.value + ")", s);
|
||||
|
||||
initTreeView();
|
||||
|
||||
document.getElementById("errorTryAgain").focus();
|
||||
};
|
||||
|
||||
function initTreeView() {
|
||||
var tabList = document.getElementById("tabList");
|
||||
var winLabel = tabList.getAttribute("_window_label");
|
||||
|
||||
gTreeData = [];
|
||||
gStateObject.windows.forEach(function(aWinData, aIx) {
|
||||
var winState = {
|
||||
label: winLabel.replace("%S", (aIx + 1)),
|
||||
open: true,
|
||||
checked: true,
|
||||
ix: aIx
|
||||
};
|
||||
winState.tabs = aWinData.tabs.map(function(aTabData) {
|
||||
var entry = aTabData.entries[aTabData.index - 1] || { url: "about:blank" };
|
||||
var iconURL = aTabData.attributes && aTabData.attributes.image || null;
|
||||
// don't initiate a connection just to fetch a favicon (see bug 462863)
|
||||
if (/^https?:/.test(iconURL))
|
||||
iconURL = "moz-anno:favicon:" + iconURL;
|
||||
return {
|
||||
label: entry.title || entry.url,
|
||||
checked: true,
|
||||
src: iconURL,
|
||||
parent: winState
|
||||
};
|
||||
});
|
||||
gTreeData.push(winState);
|
||||
for each (var tab in winState.tabs)
|
||||
gTreeData.push(tab);
|
||||
}, this);
|
||||
|
||||
tabList.view = treeView;
|
||||
tabList.view.selection.select(0);
|
||||
}
|
||||
|
||||
// User actions
|
||||
|
||||
function restoreSession() {
|
||||
document.getElementById("errorTryAgain").disabled = true;
|
||||
|
||||
// remove all unselected tabs from the state before restoring it
|
||||
var ix = gStateObject.windows.length - 1;
|
||||
for (var t = gTreeData.length - 1; t >= 0; t--) {
|
||||
if (treeView.isContainer(t)) {
|
||||
if (gTreeData[t].checked === 0)
|
||||
// this window will be restored partially
|
||||
gStateObject.windows[ix].tabs =
|
||||
gStateObject.windows[ix].tabs.filter(function(aTabData, aIx)
|
||||
gTreeData[t].tabs[aIx].checked);
|
||||
else if (!gTreeData[t].checked)
|
||||
// this window won't be restored at all
|
||||
gStateObject.windows.splice(ix, 1);
|
||||
ix--;
|
||||
}
|
||||
}
|
||||
var stateString = gStateObject.toSource();
|
||||
|
||||
var ss = Components.classes["@mozilla.org/suite/sessionstore;1"].getService(Components.interfaces.nsISessionStore);
|
||||
var top = getBrowserWindow();
|
||||
|
||||
// if there's only this page open, reuse the window for restoring the session
|
||||
if (top.gBrowser.tabContainer.childNodes.length == 1) {
|
||||
ss.setWindowState(top, stateString, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// restore the session into a new window and close the current tab
|
||||
var newWindow = top.openDialog(top.location, "_blank", "chrome,dialog=no,all", "about:blank");
|
||||
var tab = top.gBrowser.selectedTab;
|
||||
newWindow.addEventListener("load", function() {
|
||||
newWindow.removeEventListener("load", arguments.callee, true);
|
||||
ss.setWindowState(newWindow, stateString, true);
|
||||
|
||||
top.gBrowser.removeTab(tab);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function startNewSession() {
|
||||
getBrowserWindow().BrowserHome();
|
||||
}
|
||||
|
||||
function onListClick(aEvent) {
|
||||
// don't react to right-clicks
|
||||
if (aEvent.button == 2)
|
||||
return;
|
||||
|
||||
var row = {}, col = {};
|
||||
treeView.treeBox.getCellAt(aEvent.clientX, aEvent.clientY, row, col, {});
|
||||
if (col.value) {
|
||||
// restore this specific tab in the same window for middle-clicking
|
||||
// or Ctrl+clicking on a tab's title
|
||||
if ((aEvent.button == 1 || aEvent.ctrlKey) && col.value.id == "title" &&
|
||||
!treeView.isContainer(row.value))
|
||||
restoreSingleTab(row.value, aEvent.shiftKey);
|
||||
else if (col.value.id == "restore")
|
||||
toggleRowChecked(row.value);
|
||||
}
|
||||
}
|
||||
|
||||
function onListKeyDown(aEvent) {
|
||||
switch (aEvent.keyCode)
|
||||
{
|
||||
case KeyEvent.DOM_VK_SPACE:
|
||||
toggleRowChecked(document.getElementById("tabList").currentIndex);
|
||||
break;
|
||||
case KeyEvent.DOM_VK_RETURN:
|
||||
var ix = document.getElementById("tabList").currentIndex;
|
||||
if (aEvent.ctrlKey && !treeView.isContainer(ix))
|
||||
restoreSingleTab(ix, aEvent.shiftKey);
|
||||
break;
|
||||
case KeyEvent.DOM_VK_UP:
|
||||
case KeyEvent.DOM_VK_DOWN:
|
||||
case KeyEvent.DOM_VK_PAGE_UP:
|
||||
case KeyEvent.DOM_VK_PAGE_DOWN:
|
||||
case KeyEvent.DOM_VK_HOME:
|
||||
case KeyEvent.DOM_VK_END:
|
||||
aEvent.preventDefault(); // else the page scrolls unwantedly
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
function getBrowserWindow() {
|
||||
return window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebNavigation)
|
||||
.QueryInterface(Components.interfaces.nsIDocShellTreeItem).rootTreeItem
|
||||
.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindow);
|
||||
}
|
||||
|
||||
function toggleRowChecked(aIx) {
|
||||
var item = gTreeData[aIx];
|
||||
item.checked = !item.checked;
|
||||
treeView.treeBox.invalidateRow(aIx);
|
||||
|
||||
function isChecked(aItem) aItem.checked;
|
||||
|
||||
if (treeView.isContainer(aIx)) {
|
||||
// (un)check all tabs of this window as well
|
||||
for each (var tab in item.tabs) {
|
||||
tab.checked = item.checked;
|
||||
treeView.treeBox.invalidateRow(gTreeData.indexOf(tab));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// update the window's checkmark as well (0 means "partially checked")
|
||||
item.parent.checked = item.parent.tabs.every(isChecked) ? true :
|
||||
item.parent.tabs.some(isChecked) ? 0 : false;
|
||||
treeView.treeBox.invalidateRow(gTreeData.indexOf(item.parent));
|
||||
}
|
||||
|
||||
document.getElementById("errorTryAgain").disabled = !gTreeData.some(isChecked);
|
||||
}
|
||||
|
||||
function restoreSingleTab(aIx, aShifted) {
|
||||
var tabbrowser = getBrowserWindow().gBrowser;
|
||||
var newTab = tabbrowser.addTab();
|
||||
var item = gTreeData[aIx];
|
||||
|
||||
var ss = Components.classes["@mozilla.org/suite/sessionstore;1"].getService(Components.interfaces.nsISessionStore);
|
||||
var tabState = gStateObject.windows[item.parent.ix]
|
||||
.tabs[aIx - gTreeData.indexOf(item.parent) - 1];
|
||||
ss.setTabState(newTab, tabState.toSource());
|
||||
|
||||
// respect the preference as to whether to select the tab (the Shift key inverses)
|
||||
var prefBranch = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
|
||||
if (prefBranch.getBoolPref("browser.tabs.loadInBackground") != !aShifted)
|
||||
tabbrowser.selectedTab = newTab;
|
||||
}
|
||||
|
||||
// Tree controller
|
||||
|
||||
var treeView = {
|
||||
_atoms: {},
|
||||
_getAtom: function(aName)
|
||||
{
|
||||
if (!this._atoms[aName]) {
|
||||
var as = Components.classes["@mozilla.org/atom-service;1"].getService(Components.interfaces.nsIAtomService);
|
||||
this._atoms[aName] = as.getAtom(aName);
|
||||
}
|
||||
return this._atoms[aName];
|
||||
},
|
||||
|
||||
treeBox: null,
|
||||
selection: null,
|
||||
|
||||
get rowCount() { return gTreeData.length; },
|
||||
setTree: function(treeBox) { this.treeBox = treeBox; },
|
||||
getCellText: function(idx, column) { return gTreeData[idx].label; },
|
||||
isContainer: function(idx) { return "open" in gTreeData[idx]; },
|
||||
getCellValue: function(idx, column){ return gTreeData[idx].checked; },
|
||||
isContainerOpen: function(idx) { return gTreeData[idx].open; },
|
||||
isContainerEmpty: function(idx) { return false; },
|
||||
isSeparator: function(idx) { return false; },
|
||||
isSorted: function() { return false; },
|
||||
isEditable: function(idx, column) { return false; },
|
||||
getLevel: function(idx) { return this.isContainer(idx) ? 0 : 1; },
|
||||
|
||||
getParentIndex: function(idx) {
|
||||
if (!this.isContainer(idx))
|
||||
for (var t = idx - 1; t >= 0 ; t--)
|
||||
if (this.isContainer(t))
|
||||
return t;
|
||||
return -1;
|
||||
},
|
||||
|
||||
hasNextSibling: function(idx, after) {
|
||||
var thisLevel = this.getLevel(idx);
|
||||
for (var t = after + 1; t < gTreeData.length; t++)
|
||||
if (this.getLevel(t) <= thisLevel)
|
||||
return this.getLevel(t) == thisLevel;
|
||||
return false;
|
||||
},
|
||||
|
||||
toggleOpenState: function(idx) {
|
||||
if (!this.isContainer(idx))
|
||||
return;
|
||||
var item = gTreeData[idx];
|
||||
if (item.open) {
|
||||
// remove this window's tab rows from the view
|
||||
var thisLevel = this.getLevel(idx);
|
||||
for (var t = idx + 1; t < gTreeData.length && this.getLevel(t) > thisLevel; t++);
|
||||
var deletecount = t - idx - 1;
|
||||
gTreeData.splice(idx + 1, deletecount);
|
||||
this.treeBox.rowCountChanged(idx + 1, -deletecount);
|
||||
}
|
||||
else {
|
||||
// add this window's tab rows to the view
|
||||
var toinsert = gTreeData[idx].tabs;
|
||||
for (var i = 0; i < toinsert.length; i++)
|
||||
gTreeData.splice(idx + i + 1, 0, toinsert[i]);
|
||||
this.treeBox.rowCountChanged(idx + 1, toinsert.length);
|
||||
}
|
||||
item.open = !item.open;
|
||||
this.treeBox.invalidateRow(idx);
|
||||
},
|
||||
|
||||
getCellProperties: function(idx, column, prop) {
|
||||
if (column.id == "restore" && this.isContainer(idx) && gTreeData[idx].checked === 0)
|
||||
prop.AppendElement(this._getAtom("partial"));
|
||||
if (column.id == "title")
|
||||
prop.AppendElement(this._getAtom(this.getImageSrc(idx, column) ? "icon" : "noicon"));
|
||||
},
|
||||
|
||||
getRowProperties: function(idx, prop) {
|
||||
var winState = gTreeData[idx].parent || gTreeData[idx];
|
||||
if (winState.ix % 2 != 0)
|
||||
prop.AppendElement(this._getAtom("alternate"));
|
||||
},
|
||||
|
||||
getImageSrc: function(idx, column) {
|
||||
if (column.id == "title")
|
||||
return gTreeData[idx].src || null;
|
||||
return null;
|
||||
},
|
||||
|
||||
getProgressMode : function(idx, column) { },
|
||||
cycleHeader: function(column) { },
|
||||
cycleCell: function(idx, column) { },
|
||||
selectionChanged: function() { },
|
||||
performAction: function(action) { },
|
||||
performActionOnCell: function(action, index, column) { },
|
||||
getColumnProperties: function(column, prop) { }
|
||||
};
|
|
@ -0,0 +1,119 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
# ***** 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 nsSessionStore component.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Simon Bünzli <zeniko@gmail.com>
|
||||
# Portions created by the Initial Developer are Copyright (C) 2008
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# 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 *****
|
||||
-->
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
|
||||
%brandDTD;
|
||||
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
|
||||
%htmlDTD;
|
||||
<!ENTITY % netErrorDTD SYSTEM "chrome://global/locale/netError.dtd">
|
||||
%netErrorDTD;
|
||||
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
|
||||
%globalDTD;
|
||||
<!ENTITY % restorepageDTD SYSTEM "chrome://communicator/locale/aboutSessionRestore.dtd">
|
||||
%restorepageDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>&restorepage.tabtitle;</title>
|
||||
<link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all"/>
|
||||
<link rel="stylesheet" href="chrome://communicator/skin/aboutSessionRestore.css" type="text/css" media="all"/>
|
||||
<link rel="icon" type="image/png" href="chrome://global/skin/icons/question-16.png"/>
|
||||
|
||||
<script type="application/javascript" src="chrome://communicator/content/aboutSessionRestore.js"/>
|
||||
</head>
|
||||
|
||||
<body dir="&locale.dir;">
|
||||
|
||||
<!-- PAGE CONTAINER (for styling purposes only) -->
|
||||
<div id="errorPageContainer">
|
||||
|
||||
<!-- Error Title -->
|
||||
<div id="errorTitle">
|
||||
<h1 id="errorTitleText">&restorepage.pagetitle;</h1>
|
||||
</div>
|
||||
|
||||
<!-- LONG CONTENT (the section most likely to require scrolling) -->
|
||||
<div id="errorLongContent">
|
||||
|
||||
<!-- Short Description -->
|
||||
<div id="errorShortDesc">
|
||||
<p id="errorShortDescText">&restorepage.issueDesc;</p>
|
||||
</div>
|
||||
|
||||
<!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
|
||||
<div id="errorLongDesc">
|
||||
<p>&restorepage.remedies;</p>
|
||||
<ul>
|
||||
<li>&restorepage.dueToChrome;</li>
|
||||
<li>&restorepage.dueToContent;</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Short Description -->
|
||||
<div id="errorTrailerDesc">
|
||||
<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
id="tabList" flex="1" seltype="single" hidecolumnpicker="true"
|
||||
onclick="onListClick(event);" onkeydown="onListKeyDown(event);"
|
||||
_window_label="&restorepage.windowLabel;">
|
||||
<treecols>
|
||||
<treecol id="restore" type="checkbox" label="&restorepage.restoreHeader;"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol primary="true" id="title" label="&restorepage.listHeader;" flex="1"/>
|
||||
</treecols>
|
||||
<treechildren flex="1"/>
|
||||
</tree>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Buttons -->
|
||||
<hbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="buttons">
|
||||
<button id="errorTryAgain" label="&restorepage.restoreButton;"
|
||||
accesskey="&restorepage.restore.access;"
|
||||
oncommand="restoreSession();"/>
|
||||
<button id="errorCancel" label="&restorepage.cancelButton;"
|
||||
accesskey="&restorepage.cancel.access;"
|
||||
oncommand="startNewSession();"/>
|
||||
</hbox>
|
||||
<!-- holds the session data for when the tab is closed -->
|
||||
<input type="text" id="sessionData" style="display: none;"/>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -46,6 +46,8 @@ comm.jar:
|
|||
% overlay chrome://messenger-smime/content/msgReadSecurityInfo.xul chrome://communicator/content/helpMessengerOverlay.xul
|
||||
% content communicator-platform %content/communicator-platform/ platform xpcnativewrappers=yes
|
||||
% content communicator-region %content/communicator-region/ xpcnativewrappers=yes
|
||||
content/communicator/aboutSessionRestore.js
|
||||
content/communicator/aboutSessionRestore.xhtml
|
||||
content/communicator/askViewZoom.xul
|
||||
content/communicator/askViewZoom.js
|
||||
content/communicator/browserBindings.xul
|
||||
|
|
|
@ -46,6 +46,7 @@ MODULE = suitecommon
|
|||
|
||||
EXTRA_COMPONENTS = \
|
||||
nsAboutCertError.js \
|
||||
nsAboutSessionRestore.js \
|
||||
nsSessionStartup.js \
|
||||
nsSessionStore.js \
|
||||
nsSuiteGlue.js \
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/* ***** 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 nsSessionStore component.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Simon Bünzli <zeniko@gmail.com>
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
function AboutSessionRestore() { }
|
||||
AboutSessionRestore.prototype = {
|
||||
classDescription: "about:sessionrestore",
|
||||
contractID: "@mozilla.org/network/protocol/about;1?what=sessionrestore",
|
||||
classID: Components.ID("{a03c813e-abe8-41de-8d0c-5aa85f877696}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIAboutModule]),
|
||||
|
||||
getURIFlags: function(aURI) {
|
||||
return Components.interfaces.nsIAboutModule.ALLOW_SCRIPT;
|
||||
},
|
||||
|
||||
newChannel: function(aURI) {
|
||||
let ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
|
||||
let channel = ios.newChannel("chrome://communicator/content/aboutSessionRestore.xhtml",
|
||||
null, null);
|
||||
channel.originalURI = aURI;
|
||||
return channel;
|
||||
}
|
||||
};
|
||||
|
||||
function NSGetModule(compMgr, fileSpec) {
|
||||
return XPCOMUtils.generateModule([AboutSessionRestore]);
|
||||
}
|
|
@ -90,9 +90,9 @@ SessionStartup.prototype = {
|
|||
* Initialize the component
|
||||
*/
|
||||
init: function sss_init() {
|
||||
this._prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
|
||||
.getService(Components.interfaces.nsIPrefService)
|
||||
.getBranch("browser.");
|
||||
let prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
|
||||
.getService(Components.interfaces.nsIPrefService)
|
||||
.getBranch("browser.");
|
||||
|
||||
// get file references
|
||||
var dirService = Components.classes["@mozilla.org/file/directory_service;1"]
|
||||
|
@ -100,11 +100,11 @@ SessionStartup.prototype = {
|
|||
let sessionFile = dirService.get("ProfD", Components.interfaces.nsILocalFile);
|
||||
sessionFile.append("sessionstore.js");
|
||||
|
||||
let doResumeSession = this._prefBranch.getBoolPref("sessionstore.resume_session_once") ||
|
||||
this._prefBranch.getIntPref("startup.page") == 3;
|
||||
let doResumeSession = prefBranch.getBoolPref("sessionstore.resume_session_once") ||
|
||||
prefBranch.getIntPref("startup.page") == 3;
|
||||
|
||||
// only read the session file if config allows possibility of restoring
|
||||
var resumeFromCrash = this._prefBranch.getBoolPref("sessionstore.resume_from_crash");
|
||||
var resumeFromCrash = prefBranch.getBoolPref("sessionstore.resume_from_crash");
|
||||
if ((!resumeFromCrash && !doResumeSession) || !sessionFile.exists())
|
||||
return;
|
||||
|
||||
|
@ -130,7 +130,7 @@ SessionStartup.prototype = {
|
|||
initialState.session.state == STATE_RUNNING_STR;
|
||||
|
||||
// set the startup type
|
||||
if (lastSessionCrashed && resumeFromCrash && this._doRecoverSession())
|
||||
if (lastSessionCrashed && resumeFromCrash)
|
||||
this._sessionType = Components.interfaces.nsISessionStartup.RECOVER_SESSION;
|
||||
else if (!lastSessionCrashed && doResumeSession)
|
||||
this._sessionType = Components.interfaces.nsISessionStartup.RESUME_SESSION;
|
||||
|
@ -201,78 +201,6 @@ SessionStartup.prototype = {
|
|||
return this._sessionType;
|
||||
},
|
||||
|
||||
/* ........ Auxiliary Functions .............. */
|
||||
|
||||
/**
|
||||
* prompt user whether or not to restore the previous session,
|
||||
* if the browser crashed
|
||||
* @returns bool
|
||||
*/
|
||||
_doRecoverSession: function sss_doRecoverSession() {
|
||||
// if the prompt fails, recover anyway
|
||||
var recover = true;
|
||||
|
||||
// allow extensions to hook in a more elaborate restore prompt
|
||||
// XXXzeniko drop this when we're using our own dialog instead of a standard prompt
|
||||
var dialogURI = null;
|
||||
try {
|
||||
dialogURI = this._prefBranch.getCharPref("sessionstore.restore_prompt_uri");
|
||||
}
|
||||
catch (ex) { }
|
||||
|
||||
try {
|
||||
if (dialogURI) { // extension provided dialog
|
||||
var params = Components.classes["@mozilla.org/embedcomp/dialogparam;1"]
|
||||
.createInstance(Components.interfaces.nsIDialogParamBlock);
|
||||
// default to recovering
|
||||
params.SetInt(0, 0);
|
||||
Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
|
||||
.getService(Components.interfaces.nsIWindowWatcher)
|
||||
.openWindow(null, dialogURI, "_blank", "chrome,modal,centerscreen,titlebar", params);
|
||||
recover = params.GetInt(0) == 0;
|
||||
}
|
||||
else { // basic prompt with no options
|
||||
// get app name from branding properties
|
||||
var brandStringBundle = this._getStringBundle("chrome://branding/locale/brand.properties");
|
||||
var brandShortName = brandStringBundle.GetStringFromName("brandShortName");
|
||||
|
||||
// create prompt strings
|
||||
var ssStringBundle = this._getStringBundle("chrome://communicator/locale/sessionstore.properties");
|
||||
var restoreTitle = ssStringBundle.formatStringFromName("restoredTitle", [brandShortName], 1);
|
||||
var restoreText = ssStringBundle.formatStringFromName("restoredMsg", [brandShortName], 1);
|
||||
var okTitle = ssStringBundle.GetStringFromName("okTitle");
|
||||
var cancelTitle = ssStringBundle.GetStringFromName("cancelTitle");
|
||||
|
||||
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||||
.getService(Components.interfaces.nsIPromptService);
|
||||
|
||||
// set the buttons that will appear on the dialog
|
||||
var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 +
|
||||
promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 +
|
||||
promptService.BUTTON_POS_0_DEFAULT;
|
||||
var buttonChoice = promptService.confirmEx(null, restoreTitle, restoreText,
|
||||
flags, okTitle, cancelTitle, null,
|
||||
null, {});
|
||||
recover = (buttonChoice == 0);
|
||||
}
|
||||
}
|
||||
catch (ex) { dump(ex + "\n"); } // if the prompt fails, recover anyway
|
||||
return recover;
|
||||
},
|
||||
|
||||
/**
|
||||
* Convenience method to get localized string bundles
|
||||
* @param aURI
|
||||
* @returns nsIStringBundle
|
||||
*/
|
||||
_getStringBundle: function sss_getStringBundle(aURI) {
|
||||
var bundleService = Components.classes["@mozilla.org/intl/stringbundle;1"]
|
||||
.getService(Components.interfaces.nsIStringBundleService);
|
||||
var appLocale = Components.classes["@mozilla.org/intl/nslocaleservice;1"]
|
||||
.getService(Components.interfaces.nsILocaleService).getApplicationLocale();
|
||||
return bundleService.createBundle(aURI, appLocale);
|
||||
},
|
||||
|
||||
/* ........ Storage API .............. */
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Dietrich Ayala <dietrich@mozilla.com>
|
||||
* Ehsan Akhgari <ehsan.akhgari@gmail.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
|
||||
|
@ -93,8 +94,6 @@ const CAPABILITIES = [
|
|||
"Subframes", "Plugins", "Javascript", "MetaRedirects", "Images"
|
||||
];
|
||||
|
||||
// module for JSON conversion (needed for the nsISessionStore API)
|
||||
Components.utils.import("resource://gre/modules/JSON.jsm");
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
function debug(aMsg) {
|
||||
|
@ -117,8 +116,9 @@ SessionStoreService.prototype = {
|
|||
Components.interfaces.nsIObserver,
|
||||
Components.interfaces.nsISupportsWeakReference]),
|
||||
|
||||
// xul:tab attributes to (re)store (extensions might want to hook in here)
|
||||
xulAttributes: [],
|
||||
// xul:tab attributes to (re)store (extensions might want to hook in here);
|
||||
// the favicon is always saved for the about:sessionrestore page
|
||||
xulAttributes: ["image"],
|
||||
|
||||
// set default load state
|
||||
_loadState: STATE_STOPPED,
|
||||
|
@ -145,6 +145,12 @@ SessionStoreService.prototype = {
|
|||
// not-"dirty" windows usually don't need to have their data updated
|
||||
_dirtyWindows: {},
|
||||
|
||||
// collection of session states yet to be restored
|
||||
_statesToRestore: {},
|
||||
|
||||
// counts the number of crashes since the last clean start
|
||||
_recentCrashes: 0,
|
||||
|
||||
/* ........ Global Event Handlers .............. */
|
||||
|
||||
/**
|
||||
|
@ -179,9 +185,9 @@ SessionStoreService.prototype = {
|
|||
this._resume_from_crash = this._prefBranch.getBoolPref("sessionstore.resume_from_crash");
|
||||
this._prefBranch.addObserver("sessionstore.resume_from_crash", this, true);
|
||||
|
||||
// observe prefs changes so we can modify stored data to match
|
||||
this._prefBranch.addObserver("tabs.undoclose.depth", this, true);
|
||||
this._prefBranch.addObserver("sessionstore.max_tabs_undo", this, true);
|
||||
// this pref is only read at startup, so no need to observe it
|
||||
this._sessionhistory_max_entries =
|
||||
this._prefBranch.getIntPref("sessionhistory.max_entries");
|
||||
|
||||
// get file references
|
||||
var dirService = Components.classes["@mozilla.org/file/directory_service;1"]
|
||||
|
@ -211,6 +217,20 @@ SessionStoreService.prototype = {
|
|||
let lastSessionCrashed =
|
||||
this._initialState && this._initialState.session && this._initialState.session.state &&
|
||||
this._initialState.session.state == STATE_RUNNING_STR;
|
||||
// if last session crashed, backup the session
|
||||
if (lastSessionCrashed) {
|
||||
this._recentCrashes = (this._initialState.session &&
|
||||
this._initialState.session.recentCrashes || 0) + 1;
|
||||
|
||||
if (this._needsRestorePage(this._initialState, this._recentCrashes)) {
|
||||
// replace the crashed session with a restore-page-only session
|
||||
let pageData = {
|
||||
url: "about:sessionrestore",
|
||||
formdata: { "#sessionData": iniString }
|
||||
};
|
||||
this._initialState = { windows: [{ tabs: [{ entries: [pageData] }] }] };
|
||||
}
|
||||
}
|
||||
// make sure that at least the first window doesn't have anything hidden
|
||||
if (this._initialState.windows[0])
|
||||
delete this._initialState.windows[0].hidden;
|
||||
|
@ -324,18 +344,6 @@ SessionStoreService.prototype = {
|
|||
break;
|
||||
case "nsPref:changed": // catch pref changes
|
||||
switch (aData) {
|
||||
// if the user decreases the max number of closed tabs they want
|
||||
// preserved update our internal states to match that max
|
||||
// misak: this shouldn't be needed, will remove in final patch
|
||||
//case "tabs.undoclose.depth":
|
||||
// var ix;
|
||||
// for (ix in this._windows) {
|
||||
// this._windows[ix]._closedTabs.splice(this._prefBranch.getIntPref("tabs.undoclose.depth"));
|
||||
// }
|
||||
// break;
|
||||
case "sessionstore.max_tabs_undo":
|
||||
//todo: we need some way to syncronise sessionstore.max_tabs_undo and tabs.undoclose.depth
|
||||
break;
|
||||
case "sessionstore.interval":
|
||||
this._interval = this._prefBranch.getIntPref("sessionstore.interval");
|
||||
// reset timer and save
|
||||
|
@ -433,10 +441,6 @@ SessionStoreService.prototype = {
|
|||
this._loadState = STATE_RUNNING;
|
||||
this._lastSaveTime = Date.now();
|
||||
|
||||
// don't save during the first ten seconds
|
||||
// (until most of the pages have been restored)
|
||||
this.saveStateDelayed(aWindow, 10000);
|
||||
|
||||
// restore a crashed session resp. resume the last session if requested
|
||||
if (this._initialState) {
|
||||
// make sure that the restored tabs are first in the window
|
||||
|
@ -444,14 +448,25 @@ SessionStoreService.prototype = {
|
|||
this._restoreCount = this._initialState.windows ? this._initialState.windows.length : 0;
|
||||
this.restoreWindow(aWindow, this._initialState, this._isCmdLineEmpty(aWindow));
|
||||
delete this._initialState;
|
||||
|
||||
// mark ourselves as running
|
||||
this.saveState(true);
|
||||
}
|
||||
else {
|
||||
// Nothing to restore, notify observers things are complete.
|
||||
var observerService = Components.classes["@mozilla.org/observer-service;1"]
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
observerService.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, "");
|
||||
|
||||
// the next delayed save request should execute immediately
|
||||
this._lastSaveTime -= this._interval;
|
||||
}
|
||||
}
|
||||
// this window was opened by _openWindowWithState
|
||||
else if (!this._isWindowLoaded(aWindow)) {
|
||||
let followUp = this._statesToRestore[aWindow.__SS_restoreID].windows.length == 1;
|
||||
this.restoreWindow(aWindow, this._statesToRestore[aWindow.__SS_restoreID], true, followUp);
|
||||
}
|
||||
|
||||
var tabbrowser = aWindow.getBrowser();
|
||||
var tabpanels = tabbrowser.mPanelContainer;
|
||||
|
@ -474,6 +489,16 @@ SessionStoreService.prototype = {
|
|||
* Window reference
|
||||
*/
|
||||
onClose: function sss_onClose(aWindow) {
|
||||
// this window was about to be restored - conserve its original data, if any
|
||||
let isFullyLoaded = this._isWindowLoaded(aWindow);
|
||||
if (!isFullyLoaded) {
|
||||
if (!aWindow.__SSi)
|
||||
aWindow.__SSi = "window" + Date.now();
|
||||
this._window[aWindow.__SSi] = this._statesToRestore[aWindow.__SS_restoreID];
|
||||
delete this._statesToRestore[aWindow.__SS_restoreID];
|
||||
delete aWindow.__SS_restoreID;
|
||||
}
|
||||
|
||||
// ignore windows not tracked by SessionStore
|
||||
if (!aWindow.__SSi || !this._windows[aWindow.__SSi]) {
|
||||
return;
|
||||
|
@ -490,22 +515,23 @@ SessionStoreService.prototype = {
|
|||
tabbrowser.removeEventListener("TabClose", this, true);
|
||||
tabbrowser.removeEventListener("TabSelect", this, true);
|
||||
|
||||
let winData = this._windows[aWindow.__SSi];
|
||||
windata._closedTabs = tabbrowser.savedBrowsers.map(function(e) { return e.tabData; });
|
||||
if (this._loadState == STATE_RUNNING) { // window not closed during a regular shut-down
|
||||
// update all window data for a last time
|
||||
this._collectWindowData(aWindow);
|
||||
|
||||
// preserve this window's data (in case it was the last navigator:browser)
|
||||
var winData = this._windows[aWindow.__SSi];
|
||||
winData.title = aWindow.content.document.title;
|
||||
winData._closedTabs = tabbrowser.savedBrowsers.map(function(e) { return e.tabData; });
|
||||
|
||||
// if this is a popup window, append it to what we've already got (cf. bug 368677)
|
||||
if (!this._lastClosedWindows || !winData.isPopup)
|
||||
this._lastClosedWindows = [winData];
|
||||
else
|
||||
this._lastClosedWindows.push(winData);
|
||||
|
||||
this._updateCookies(this._lastClosedWindows);
|
||||
if (isFullyLoaded) {
|
||||
winData.title = aWindow.content.document.title;
|
||||
this._updateCookies(this._lastClosedWindows);
|
||||
}
|
||||
|
||||
// clear this window from the list
|
||||
delete this._windows[aWindow.__SSi];
|
||||
|
@ -519,14 +545,7 @@ SessionStoreService.prototype = {
|
|||
}
|
||||
|
||||
// cache the window state until the window is completely gone
|
||||
aWindow.__SS_dyingCache = this._windows[aWindow.__SSi] || winData;
|
||||
|
||||
// reset the _tab property to avoid keeping the tab's XUL element alive
|
||||
// longer than we need it
|
||||
var tabCount = aWindow.__SS_dyingCache.tabs.length;
|
||||
for (var t = 0; t < tabCount; t++) {
|
||||
delete aWindow.__SS_dyingCache.tabs[t]._tab;
|
||||
}
|
||||
aWindow.__SS_dyingCache = winData;
|
||||
|
||||
delete aWindow.__SSi;
|
||||
},
|
||||
|
@ -591,7 +610,7 @@ SessionStoreService.prototype = {
|
|||
event.initEvent("SSTabClosing", true, false);
|
||||
aTab.dispatchEvent(event);
|
||||
|
||||
var maxTabsUndo = this._prefBranch.getIntPref("tabs.undoclose.depth");
|
||||
var maxTabsUndo = this._prefBranch.getIntPref("browser.sessionstore.max_tabs_undo");
|
||||
// don't update our internal state if we don't have to
|
||||
if (maxTabsUndo == 0) {
|
||||
return;
|
||||
|
@ -601,10 +620,6 @@ SessionStoreService.prototype = {
|
|||
var tabState = this._collectTabData(aTab);
|
||||
this._updateTextAndScrollDataForTab(aWindow, aTab.linkedBrowser, tabState);
|
||||
|
||||
// reset the _tab property to avoid keeping the tab's XUL element alive
|
||||
// longer than we need it
|
||||
delete tabState._tab;
|
||||
|
||||
// store closed-tab data for undo
|
||||
if (tabState.entries.length > 0) {
|
||||
let tabTitle = aTab.label;
|
||||
|
@ -695,9 +710,16 @@ SessionStoreService.prototype = {
|
|||
},
|
||||
|
||||
setBrowserState: function sss_setBrowserState(aState) {
|
||||
try {
|
||||
var state = this._safeEval("(" + aState + ")");
|
||||
}
|
||||
catch (ex) { /* invalid state object - don't restore anything */ }
|
||||
if (!state || !state.windows)
|
||||
throw (Components.returnCode = Components.results.NS_ERROR_INVALID_ARG);
|
||||
|
||||
var window = this._getMostRecentBrowserWindow();
|
||||
if (!window) {
|
||||
this._openWindowWithState("(" + aState + ")");
|
||||
this._openWindowWithState(state);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -709,7 +731,7 @@ SessionStoreService.prototype = {
|
|||
});
|
||||
|
||||
// restore to the given state
|
||||
this.restoreWindow(window, "(" + aState + ")", true);
|
||||
this.restoreWindow(window, state, true);
|
||||
},
|
||||
|
||||
getWindowState: function sss_getWindowState(aWindow) {
|
||||
|
@ -745,10 +767,8 @@ SessionStoreService.prototype = {
|
|||
if (!tabState.entries || !aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi)
|
||||
throw (Components.returnCode = Components.results.NS_ERROR_INVALID_ARG);
|
||||
|
||||
tabState._tab = aTab;
|
||||
|
||||
var window = aTab.ownerDocument.defaultView;
|
||||
this.restoreHistoryPrecursor(window, [tabState], 0, 0, 0);
|
||||
this.restoreHistoryPrecursor(window, [aTab], [tabState], 0, 0, 0);
|
||||
},
|
||||
|
||||
duplicateTab: function sss_duplicateTab(aWindow, aTab) {
|
||||
|
@ -761,14 +781,12 @@ SessionStoreService.prototype = {
|
|||
this._updateTextAndScrollDataForTab(sourceWindow, aTab.linkedBrowser, tabState, true);
|
||||
|
||||
var newTab = aWindow.getBrowser().addTab();
|
||||
tabState._tab = newTab;
|
||||
this.restoreHistoryPrecursor(aWindow, [tabState], 0, 0, 0);
|
||||
this.restoreHistoryPrecursor(aWindow, [newTab], [tabState], 0, 0, 0);
|
||||
|
||||
return newTab;
|
||||
},
|
||||
|
||||
getClosedTabCount: function sss_getClosedTabCount(aWindow) {
|
||||
//misak: 2 lines uncommented
|
||||
if (!aWindow.__SSi && aWindow.__SS_dyingCache)
|
||||
return aWindow.__SS_dyingCache._closedTabs.length;
|
||||
|
||||
|
@ -776,8 +794,8 @@ SessionStoreService.prototype = {
|
|||
// XXXzeniko shouldn't we throw here?
|
||||
return 0; // not a browser window, or not otherwise tracked by SS.
|
||||
|
||||
return aWindow.getBrowser().savedBrowsers.length;
|
||||
//return this._windows[aWindow.__SSi]._closedTabs.length;
|
||||
let closedTabs = aWindow.getBrowser().savedBrowsers.map(function(e) { return e.tabData; });
|
||||
return closedTabs.length;
|
||||
},
|
||||
|
||||
getClosedTabData: function sss_getClosedTabDataAt(aWindow) {
|
||||
|
@ -786,7 +804,8 @@ SessionStoreService.prototype = {
|
|||
|
||||
if (!aWindow.__SSi)
|
||||
return this._toJSONString(aWindow.__SS_dyingCache._closedTabs);
|
||||
return this._toJSONString(aWindow.getBrowser().savedBrowsers.map(function(e) { return e.tabData; }))
|
||||
let closedTabs = aWindow.getBrowser().savedBrowsers.map(function(e) { return e.tabData; });
|
||||
return this._toJSONString(closedTabs);
|
||||
},
|
||||
|
||||
undoCloseTab: function sss_undoCloseTab(aWindow, aIndex) {
|
||||
|
@ -804,9 +823,9 @@ SessionStoreService.prototype = {
|
|||
let browser = aWindow.gBrowser;
|
||||
|
||||
// Seamonkey has it's own undoclosetab functionality
|
||||
tab = browser.restoreTab(aIndex);
|
||||
var newTab = browser.restoreTab(aIndex);
|
||||
|
||||
return tab;
|
||||
return newTab;
|
||||
},
|
||||
|
||||
getWindowValue: function sss_getWindowValue(aWindow, aKey) {
|
||||
|
@ -903,7 +922,7 @@ SessionStoreService.prototype = {
|
|||
if (!browser || !browser.currentURI)
|
||||
// can happen when calling this function right after .addTab()
|
||||
return tabData;
|
||||
else if (browser.parentNode.__SS_data && browser.parentNode.__SS_data._tab)
|
||||
else if (browser.parentNode.__SS_data && browser.parentNode.__SS_data._tabStillLoading)
|
||||
// use the data to be restored when the tab hasn't been completely loaded
|
||||
return browser.parentNode.__SS_data;
|
||||
|
||||
|
@ -913,8 +932,11 @@ SessionStoreService.prototype = {
|
|||
}
|
||||
catch (ex) { } // this could happen if we catch a tab during (de)initialization
|
||||
|
||||
// XXXzeniko anchor navigation doesn't reset __SS_data, so we could reuse
|
||||
// data even when we shouldn't (e.g. Back, different anchor)
|
||||
if (history && browser.parentNode.__SS_data &&
|
||||
browser.parentNode.__SS_data.entries[history.index] && !aFullData) {
|
||||
browser.parentNode.__SS_data.entries[history.index] &&
|
||||
history.index < this._sessionhistory_max_entries - 1 && !aFullData) {
|
||||
tabData = browser.parentNode.__SS_data;
|
||||
tabData.index = history.index + 1;
|
||||
}
|
||||
|
@ -1141,7 +1163,8 @@ SessionStoreService.prototype = {
|
|||
for (var i = 0; i < browsers.length; i++) {
|
||||
try {
|
||||
var tabData = this._windows[aWindow.__SSi].tabs[i];
|
||||
if (browsers[i].parentNode.__SS_data && browsers[i].parentNode.__SS_data._tab)
|
||||
if (browsers[i].parentNode.__SS_data &&
|
||||
browsers[i].parentNode.__SS_data._tabStillLoading)
|
||||
continue; // ignore incompletely initialized tabs
|
||||
this._updateTextAndScrollDataForTab(aWindow, browsers[i], tabData);
|
||||
}
|
||||
|
@ -1209,7 +1232,8 @@ SessionStoreService.prototype = {
|
|||
}
|
||||
var isHTTPS = this._getURIFromString((aContent.parent || aContent).
|
||||
document.location.href).schemeIs("https");
|
||||
if (aFullData || this._checkPrivacyLevel(isHTTPS)) {
|
||||
if (aFullData || this._checkPrivacyLevel(isHTTPS) ||
|
||||
aContent.top.document.location.href == "about:sessionrestore") {
|
||||
if (aFullData || aUpdateFormData) {
|
||||
let formData = this._collectFormDataForFrame(aContent.document);
|
||||
if (formData)
|
||||
|
@ -1271,8 +1295,12 @@ SessionStoreService.prototype = {
|
|||
let data = {};
|
||||
do {
|
||||
let id = node.id ? "#" + node.id : XPathHelper.generate(node);
|
||||
if (node instanceof Components.interfaces.nsIDOMHTMLInputElement)
|
||||
data[id] = node.type == "checkbox" || node.type == "radio" ? node.checked : node.value;
|
||||
if (node instanceof Components.interfaces.nsIDOMHTMLInputElement) {
|
||||
if (node.type != "file")
|
||||
data[id] = node.type == "checkbox" || node.type == "radio" ? node.checked : node.value;
|
||||
else
|
||||
data[id] = { type: "file", value: node.value };
|
||||
}
|
||||
else if (node instanceof Components.interfaces.nsIDOMHTMLTextAreaElement)
|
||||
data[id] = node.value;
|
||||
else if (!node.multiple)
|
||||
|
@ -1398,6 +1426,8 @@ SessionStoreService.prototype = {
|
|||
if (this._loadState == STATE_RUNNING) {
|
||||
// update the data for all windows with activities since the last save operation
|
||||
this._forEachBrowserWindow(function(aWindow) {
|
||||
if (!this._isWindowLoaded(aWindow)) // window data is still in _statesToRestore
|
||||
return;
|
||||
if (aUpdateAll || this._dirtyWindows[aWindow.__SSi] || aWindow == activeWindow) {
|
||||
this._collectWindowData(aWindow);
|
||||
}
|
||||
|
@ -1419,6 +1449,16 @@ SessionStoreService.prototype = {
|
|||
nonPopupCount++;
|
||||
}
|
||||
this._updateCookies(total);
|
||||
|
||||
// collect the data for all windows yet to be restored
|
||||
for (ix in this._statesToRestore) {
|
||||
for each (let winData in this._statesToRestore[ix].windows) {
|
||||
total.push(winData);
|
||||
if (!winData.isPopup)
|
||||
nonPopupCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// if no non-popup browser window remains open, return the state of the last closed window(s)
|
||||
if (nonPopupCount == 0 && this._lastClosedWindows) {
|
||||
// prepend the last non-popup browser window, so that if the user loads more tabs
|
||||
|
@ -1439,6 +1479,9 @@ SessionStoreService.prototype = {
|
|||
* @returns string
|
||||
*/
|
||||
_getWindowState: function sss_getWindowState(aWindow) {
|
||||
if (!this._isWindowLoaded(aWindow))
|
||||
return this._statesToRestore[aWindow.__SS_restoreID];
|
||||
|
||||
if (this._loadState == STATE_RUNNING) {
|
||||
this._collectWindowData(aWindow);
|
||||
}
|
||||
|
@ -1451,6 +1494,9 @@ SessionStoreService.prototype = {
|
|||
},
|
||||
|
||||
_collectWindowData: function sss_collectWindowData(aWindow) {
|
||||
if (!this._isWindowLoaded(aWindow))
|
||||
return;
|
||||
|
||||
// update the internal state data for this window
|
||||
this._saveWindowHistory(aWindow);
|
||||
this._updateTextAndScrollData(aWindow);
|
||||
|
@ -1525,12 +1571,13 @@ SessionStoreService.prototype = {
|
|||
var tabbrowser = aWindow.getBrowser();
|
||||
var openTabCount = aOverwriteTabs ? tabbrowser.browsers.length : -1;
|
||||
var newTabCount = winData.tabs.length;
|
||||
let tabs = [];
|
||||
|
||||
for (var t = 0; t < newTabCount; t++) {
|
||||
winData.tabs[t]._tab = t < openTabCount ? tabbrowser.mTabs[t] : tabbrowser.addTab();
|
||||
tabs.push(t < openTabCount ? tabbrowser.mTabs[t] : tabbrowser.addTab());
|
||||
// when resuming at startup: add additionally requested pages to the end
|
||||
if (!aOverwriteTabs && root._firstTabs) {
|
||||
tabbrowser.moveTabTo(winData.tabs[t]._tab, t);
|
||||
tabbrowser.moveTabTo(tabs[t], t);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1546,8 +1593,8 @@ SessionStoreService.prototype = {
|
|||
this.restoreCookies(winData.cookies);
|
||||
}
|
||||
if (winData.extData) {
|
||||
if (!this._windows[aWindow.__SSi].extData) {
|
||||
this._windows[aWindow.__SSi].extData = {}
|
||||
if (aOverwriteTabs || !this._windows[aWindow.__SSi].extData) {
|
||||
this._windows[aWindow.__SSi].extData = {};
|
||||
}
|
||||
for (var key in winData.extData) {
|
||||
this._windows[aWindow.__SSi].extData[key] = winData.extData[key];
|
||||
|
@ -1569,18 +1616,20 @@ SessionStoreService.prototype = {
|
|||
// }
|
||||
//}
|
||||
|
||||
this.restoreHistoryPrecursor(aWindow, winData.tabs, (aOverwriteTabs ?
|
||||
(parseInt(winData.selected) || 1) : 0), 0, 0);
|
||||
this.restoreHistoryPrecursor(aWindow, tabs, winData.tabs,
|
||||
(aOverwriteTabs ? (parseInt(winData.selected) || 1) : 0), 0, 0);
|
||||
|
||||
this._notifyIfAllWindowsRestored();
|
||||
},
|
||||
|
||||
/**
|
||||
* Manage history restoration for a window
|
||||
* @param aWindow
|
||||
* Window to restore the tabs into
|
||||
* @param aTabs
|
||||
* Array of tab data
|
||||
* @param aCurrentTabs
|
||||
* Array of tab references
|
||||
* @param aTabData
|
||||
* Array of tab data
|
||||
* @param aSelectTab
|
||||
* Index of selected tab
|
||||
* @param aIx
|
||||
|
@ -1588,21 +1637,22 @@ SessionStoreService.prototype = {
|
|||
* @param aCount
|
||||
* Counter for number of times delaying b/c browser or history aren't ready
|
||||
*/
|
||||
restoreHistoryPrecursor: function sss_restoreHistoryPrecursor(aWindow, aTabs, aSelectTab, aIx, aCount) {
|
||||
restoreHistoryPrecursor:
|
||||
function sss_restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab, aIx, aCount) {
|
||||
var tabbrowser = aWindow.getBrowser();
|
||||
|
||||
// make sure that all browsers and their histories are available
|
||||
// - if one's not, resume this check in 100ms (repeat at most 10 times)
|
||||
for (var t = aIx; t < aTabs.length; t++) {
|
||||
try {
|
||||
if (!tabbrowser.getBrowserForTab(aTabs[t]._tab).webNavigation.sessionHistory) {
|
||||
if (!tabbrowser.getBrowserForTab(aTabs[t]).webNavigation.sessionHistory) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
catch (ex) { // in case browser or history aren't ready yet
|
||||
if (aCount < 10) {
|
||||
var restoreHistoryFunc = function(self) {
|
||||
self.restoreHistoryPrecursor(aWindow, aTabs, aSelectTab, aIx, aCount + 1);
|
||||
self.restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab, aIx, aCount + 1);
|
||||
}
|
||||
aWindow.setTimeout(restoreHistoryFunc, 100, this);
|
||||
return;
|
||||
|
@ -1612,10 +1662,11 @@ SessionStoreService.prototype = {
|
|||
|
||||
// mark the tabs as loading
|
||||
for (t = 0; t < aTabs.length; t++) {
|
||||
var tab = aTabs[t]._tab;
|
||||
var tab = aTabs[t];
|
||||
var browser = tabbrowser.getBrowserForTab(tab);
|
||||
|
||||
if (!aTabs[t].entries || aTabs[t].entries.length == 0) {
|
||||
aTabData[t]._tabStillLoading = true;
|
||||
if (!aTabData[t].entries || aTabData[t].entries.length == 0) {
|
||||
// make sure to blank out this tab's content
|
||||
// (just purging the tab's history won't be enough)
|
||||
browser.contentDocument.location = "about:blank";
|
||||
|
@ -1632,24 +1683,31 @@ SessionStoreService.prototype = {
|
|||
|
||||
// wall-paper fix for bug 439675: make sure that the URL to be loaded
|
||||
// is always visible in the address bar
|
||||
let activeIndex = (aTabs[t].index || aTabs[t].entries.length) - 1;
|
||||
let activePageData = aTabs[t].entries[activeIndex] || null;
|
||||
let activeIndex = (aTabData[t].index || aTabData[t].entries.length) - 1;
|
||||
let activePageData = aTabData[t].entries[activeIndex] || null;
|
||||
browser.userTypedValue = activePageData ? activePageData.url || null : null;
|
||||
|
||||
// keep the data around to prevent dataloss in case
|
||||
// a tab gets closed before it's been properly restored
|
||||
browser.parentNode.__SS_data = aTabs[t];
|
||||
browser.parentNode.__SS_data = aTabData[t];
|
||||
}
|
||||
|
||||
// make sure to restore the selected tab first (if any)
|
||||
if (aSelectTab-- && aTabs[aSelectTab]) {
|
||||
aTabs.unshift(aTabs.splice(aSelectTab, 1)[0]);
|
||||
tabbrowser.selectedTab = aTabs[0]._tab;
|
||||
aTabData.unshift(aTabData.splice(aSelectTab, 1)[0]);
|
||||
tabbrowser.selectedTab = aTabs[0];
|
||||
}
|
||||
|
||||
if (!this._isWindowLoaded(aWindow)) {
|
||||
// from now on, the data will come from the actual window
|
||||
delete this._statesToRestore[aWindow.__SS_restoreID];
|
||||
delete aWindow.__SS_restoreID;
|
||||
}
|
||||
|
||||
// helper hash for ensuring unique frame IDs
|
||||
var idMap = { used: {} };
|
||||
this.restoreHistory(aWindow, aTabs, idMap);
|
||||
this.restoreHistory(aWindow, aTabs, aTabData, idMap);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1657,22 +1715,25 @@ SessionStoreService.prototype = {
|
|||
* @param aWindow
|
||||
* Window reference
|
||||
* @param aTabs
|
||||
* Array of tab references
|
||||
* @param aTabData
|
||||
* Array of tab data
|
||||
* @param aIdMap
|
||||
* Hash for ensuring unique frame IDs
|
||||
*/
|
||||
restoreHistory: function sss_restoreHistory(aWindow, aTabs, aIdMap) {
|
||||
restoreHistory: function sss_restoreHistory(aWindow, aTabs, aTabData, aIdMap) {
|
||||
var _this = this;
|
||||
while (aTabs.length > 0 && (!aTabs[0]._tab || !aTabs[0]._tab.parentNode)) {
|
||||
while (aTabs.length > 0 && (!aTabData[0]._tabStillLoading || !aTabs[0].parentNode)) {
|
||||
aTabs.shift(); // this tab got removed before being completely restored
|
||||
aTabData.shift();
|
||||
}
|
||||
if (aTabs.length == 0) {
|
||||
return; // no more tabs to restore
|
||||
}
|
||||
|
||||
var tabData = aTabs.shift();
|
||||
var tab = aTabs.shift();
|
||||
var tabData = aTabData.shift();
|
||||
|
||||
var tab = tabData._tab;
|
||||
var browser = aWindow.getBrowser().getBrowserForTab(tab);
|
||||
var history = browser.webNavigation.sessionHistory;
|
||||
|
||||
|
@ -1685,8 +1746,12 @@ SessionStoreService.prototype = {
|
|||
tabData.entries = [];
|
||||
}
|
||||
if (tabData.extData) {
|
||||
tab.__SS_extdata = tabData.extData;
|
||||
tab.__SS_extdata = {};
|
||||
for (let key in tabData.extData)
|
||||
tab.__SS_extdata[key] = tabData.extData[key];
|
||||
}
|
||||
else
|
||||
delete tab.__SS_extdata;
|
||||
|
||||
for (var i = 0; i < tabData.entries.length; i++) {
|
||||
history.addEntry(this._deserializeHistoryEntry(tabData.entries[i], aIdMap), true);
|
||||
|
@ -1743,7 +1808,7 @@ SessionStoreService.prototype = {
|
|||
browser.addEventListener("load", browser.__SS_restore, true);
|
||||
}
|
||||
|
||||
aWindow.setTimeout(function(){ _this.restoreHistory(aWindow, aTabs, aIdMap); }, 0);
|
||||
aWindow.setTimeout(function(){ _this.restoreHistory(aWindow, aTabs, aTabData, aIdMap); }, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1842,7 +1907,7 @@ SessionStoreService.prototype = {
|
|||
* A tab's docshell (containing the sessionStorage)
|
||||
*/
|
||||
_deserializeSessionStorage: function sss_deserializeSessionStorage(aStorageData, aDocShell) {
|
||||
let ioService = Cc["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
|
||||
let ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
|
||||
for (let url in aStorageData) {
|
||||
let uri = ioService.newURI(url, null, null);
|
||||
let storage = aDocShell.getSessionStorageForURI(uri);
|
||||
|
@ -1878,7 +1943,7 @@ SessionStoreService.prototype = {
|
|||
RegExp.$1 == aPrefix && hasExpectedURL(aContent.document, aURL)) {
|
||||
var document = aContent.document;
|
||||
var node = RegExp.$2 ? document.getElementById(RegExp.$3) : document.getElementsByName(RegExp.$3)[0] || null;
|
||||
if (node && "value" in node) {
|
||||
if (node && "value" in node && node.type != "file") {
|
||||
node.value = decodeURI(RegExp.$4);
|
||||
|
||||
var event = document.createEvent("UIEvents");
|
||||
|
@ -1900,7 +1965,10 @@ SessionStoreService.prototype = {
|
|||
continue;
|
||||
|
||||
let value = aData[key];
|
||||
if (typeof value == "string") {
|
||||
if (typeof value == "string" && node.type != "file") {
|
||||
if (node.value == value)
|
||||
continue; // don't dispatch an input event for no change
|
||||
|
||||
node.value = value;
|
||||
|
||||
let event = aDocument.createEvent("UIEvents");
|
||||
|
@ -1913,6 +1981,8 @@ SessionStoreService.prototype = {
|
|||
try {
|
||||
node.selectedIndex = value;
|
||||
} catch (ex) { /* throws for invalid indices */ }
|
||||
else if (value && value.type && value.type == node.type)
|
||||
node.value = value.value;
|
||||
else if (value && typeof value.indexOf == "function" && node.options) {
|
||||
Array.forEach(node.options, function(aOpt, aIx) {
|
||||
aOpt.selected = value.indexOf(aIx) > -1;
|
||||
|
@ -2031,11 +2101,20 @@ SessionStoreService.prototype = {
|
|||
if (!isNaN(aLeft) && !isNaN(aTop) && (aLeft != win_("screenX") || aTop != win_("screenY"))) {
|
||||
aWindow.moveTo(aLeft, aTop);
|
||||
}
|
||||
if (aSizeMode == "maximized" && win_("sizemode") != "maximized") {
|
||||
aWindow.maximize();
|
||||
}
|
||||
else if (aSizeMode && aSizeMode != "maximized" && win_("sizemode") != "normal") {
|
||||
aWindow.restore();
|
||||
switch (aSizeMode)
|
||||
{
|
||||
case "maximized":
|
||||
if (win_("sizemode") != "maximized")
|
||||
aWindow.maximize();
|
||||
break
|
||||
case "minimized":
|
||||
if (win_("sizemode") != "minimized")
|
||||
aWindow.minimize();
|
||||
break
|
||||
default:
|
||||
if (win_("sizemode") != "normal")
|
||||
aWindow.restore();
|
||||
break
|
||||
}
|
||||
var sidebar = aWindow.document.getElementById("sidebar-box");
|
||||
if (sidebar.getAttribute("sidebarcommand") != aSidebar) {
|
||||
|
@ -2044,7 +2123,7 @@ SessionStoreService.prototype = {
|
|||
// since resizing/moving a window brings it to the foreground,
|
||||
// we might want to re-focus the last focused window
|
||||
if (this.windowToFocus) {
|
||||
this.windowToFocus.focus();
|
||||
this.windowToFocus.content.focus();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -2124,11 +2203,24 @@ SessionStoreService.prototype = {
|
|||
return;
|
||||
|
||||
var oState = this._getCurrentState(aUpdateAll);
|
||||
oState.session = { state: ((this._loadState == STATE_RUNNING) ? STATE_RUNNING_STR : STATE_STOPPED_STR) };
|
||||
oState.session = {
|
||||
state: this._loadState == STATE_RUNNING ? STATE_RUNNING_STR : STATE_STOPPED_STR,
|
||||
lastUpdate: Date.now()
|
||||
};
|
||||
if (this._recentCrashes)
|
||||
oState.session.recentCrashes = this._recentCrashes;
|
||||
|
||||
this._saveStateObject(oState);
|
||||
},
|
||||
|
||||
/**
|
||||
* write a state object to disk
|
||||
*/
|
||||
_saveStateObject: function sss_saveStateObject(aStateObj) {
|
||||
var stateString = Components.classes["@mozilla.org/supports-string;1"]
|
||||
.createInstance(Components.interfaces.nsISupportsString);
|
||||
stateString.data = oState.toSource();
|
||||
// parentheses are for backwards compatibility with older sessionstore files
|
||||
stateString.data = "(" + this._toJSONString(aStateObj) + ")";
|
||||
|
||||
var observerService = Components.classes["@mozilla.org/observer-service;1"]
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
|
@ -2207,13 +2299,10 @@ SessionStoreService.prototype = {
|
|||
.openWindow(null, this._prefBranch.getCharPref("chromeURL"), "_blank",
|
||||
"chrome,dialog=no,all", argString);
|
||||
|
||||
window.__SS_state = aState;
|
||||
var _this = this;
|
||||
window.addEventListener("load", function(aEvent) {
|
||||
aEvent.currentTarget.removeEventListener("load", arguments.callee, true);
|
||||
_this.restoreWindow(aEvent.currentTarget, aEvent.currentTarget.__SS_state, true, true);
|
||||
delete aEvent.currentTarget.__SS_state;
|
||||
}, true);
|
||||
do {
|
||||
var ID = "window" + Math.random();
|
||||
} while (ID in this._statesToRestore);
|
||||
this._statesToRestore[(window.__SS_restoreID = ID)] = aState;
|
||||
|
||||
return window;
|
||||
},
|
||||
|
@ -2342,6 +2431,37 @@ SessionStoreService.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param aState is a session state
|
||||
* @param aRecentCrashes is the number of consecutive crashes
|
||||
* @returns whether a restore page will be needed for the session state
|
||||
*/
|
||||
_needsRestorePage: function sss_needsRestorePage(aState, aRecentCrashes) {
|
||||
const SIX_HOURS_IN_MS = 6 * 60 * 60 * 1000;
|
||||
|
||||
// don't wrap a single about:sessionrestore page
|
||||
let winData = aState.windows || null;
|
||||
if (winData && winData.length == 1 && winData[0].tabs &&
|
||||
winData[0].tabs.length == 1 && winData[0].tabs[0].entries &&
|
||||
winData[0].tabs[0].entries.length == 1 &&
|
||||
winData[0].tabs[0].entries[0].url == "about:sessionrestore")
|
||||
return false;
|
||||
|
||||
// don't automatically restore in Safe Mode
|
||||
let XRE = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULRuntime);
|
||||
if (XRE.inSafeMode)
|
||||
return true;
|
||||
|
||||
let max_resumed_crashes =
|
||||
this._prefBranch.getIntPref("sessionstore.max_resumed_crashes");
|
||||
let sessionAge = aState.session && aState.session.lastUpdate &&
|
||||
(Date.now() - aState.session.lastUpdate);
|
||||
|
||||
return max_resumed_crashes != -1 &&
|
||||
(aRecentCrashes > max_resumed_crashes ||
|
||||
sessionAge && sessionAge >= SIX_HOURS_IN_MS);
|
||||
},
|
||||
|
||||
/**
|
||||
* safe eval'ing
|
||||
*/
|
||||
|
@ -2353,20 +2473,15 @@ SessionStoreService.prototype = {
|
|||
* Converts a JavaScript object into a JSON string
|
||||
* (see http://www.json.org/ for more information).
|
||||
*
|
||||
* The inverse operation consists of eval("(" + JSON_string + ")");
|
||||
* and should be provably safe.
|
||||
* The inverse operation consists of JSON.parse(JSON_string).
|
||||
*
|
||||
* @param aJSObject is the object to be converted
|
||||
* @return the object's JSON representation
|
||||
* @returns the object's JSON representation
|
||||
*/
|
||||
_toJSONString: function sss_toJSONString(aJSObject) {
|
||||
let str = JSONModule.toString(aJSObject, ["_tab", "_hosts", "_formDataSaved"] /* keys to drop */);
|
||||
|
||||
// sanity check - so that API consumers can just eval this string
|
||||
if (!JSONModule.isMostlyHarmless(str))
|
||||
throw new Error("JSON conversion failed unexpectedly!");
|
||||
|
||||
return str;
|
||||
// XXXzeniko drop the following keys used only for internal bookkeeping:
|
||||
// _tabStillLoading, _hosts, _formDataSaved
|
||||
return JSON.stringify(aJSObject);
|
||||
},
|
||||
|
||||
_notifyIfAllWindowsRestored: function sss_notifyIfAllWindowsRestored() {
|
||||
|
@ -2381,6 +2496,16 @@ SessionStoreService.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param aWindow
|
||||
* Window reference
|
||||
* @returns whether this window's data is still cached in _statesToRestore
|
||||
* because it's not fully loaded yet
|
||||
*/
|
||||
_isWindowLoaded: function sss_isWindowLoaded(aWindow) {
|
||||
return !aWindow.__SS_restoreID;
|
||||
},
|
||||
|
||||
/* ........ Storage API .............. */
|
||||
|
||||
/**
|
||||
|
|
|
@ -242,6 +242,7 @@ bin/components/libzipwriter.so
|
|||
bin/components/nsSessionStartup.js
|
||||
bin/components/nsSessionStore.js
|
||||
bin/components/sessionstore.xpt
|
||||
bin/components/aboutSessionRestore.js
|
||||
|
||||
; JavaScript components
|
||||
bin/components/FeedProcessor.js
|
||||
|
|
|
@ -246,6 +246,7 @@ bin\sqlite3.dll
|
|||
bin\components\nsSessionStartup.js
|
||||
bin\components\nsSessionStore.js
|
||||
bin\components\sessionstore.xpt
|
||||
bin\components\aboutSessionRestore.js
|
||||
|
||||
; JavaScript components
|
||||
bin\components\FeedProcessor.js
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<!ENTITY restorepage.tabtitle "Restore Session">
|
||||
<!ENTITY restorepage.pagetitle "Would you like to restore your session?">
|
||||
<!-- LOCALIZATION NOTE: If "closed unexpectedly" sounds too awkward in the translation,
|
||||
you may translate "crash" instead (even though it's IT-speak) -->
|
||||
<!ENTITY restorepage.issueDesc "Your previous &brandShortName; session closed unexpectedly. We sincerely apologize for the inconvenience. You can restore the tabs and windows from your previous session, or start a new session if they are no longer needed.">
|
||||
<!ENTITY restorepage.remedies "If &brandShortName; closes repeatedly:">
|
||||
<!ENTITY restorepage.dueToChrome "Try disabling any recently added extensions in the Add-ons Manager.">
|
||||
<!ENTITY restorepage.dueToContent "Try restoring your session without any Web pages you suspect might be causing the problem:">
|
||||
|
||||
<!ENTITY restorepage.restoreButton "Restore Previous Session">
|
||||
<!ENTITY restorepage.restore.access "R">
|
||||
<!ENTITY restorepage.cancelButton "Start New Session">
|
||||
<!ENTITY restorepage.cancel.access "S">
|
||||
|
||||
<!ENTITY restorepage.restoreHeader "Restore">
|
||||
<!ENTITY restorepage.listHeader "Windows and Tabs">
|
||||
<!-- LOCALIZATION NOTE: %S will be replaced with a number. -->
|
||||
<!ENTITY restorepage.windowLabel "Window %S">
|
|
@ -1,7 +0,0 @@
|
|||
restoredTitle= %S - Restore Previous Session
|
||||
restoredMsg=Your last %S session closed unexpectedly. You can restore the tabs and windows from your previous session, or start a new session if you think the problem was related to a page you were viewing.
|
||||
|
||||
# Localization note: It is recommended that okTitle be longer than cancelTitle
|
||||
# so that hitting the more prominent button doesn't lead to dataloss (see bug 346264).
|
||||
okTitle=Restore Previous Session
|
||||
cancelTitle=Start New Session
|
|
@ -29,7 +29,6 @@
|
|||
locale/@AB_CD@/communicator/openLocation.properties (%chrome/common/openLocation.properties)
|
||||
locale/@AB_CD@/communicator/passwordManager.dtd (%chrome/common/passwordManager.dtd)
|
||||
locale/@AB_CD@/communicator/printPreview.dtd (%chrome/common/printPreview.dtd)
|
||||
locale/@AB_CD@/communicator/sessionstore.properties (%chrome/common/sessionstore.properties)
|
||||
locale/@AB_CD@/communicator/shellservice.properties (%chrome/common/shellservice.properties)
|
||||
locale/@AB_CD@/communicator/sanitize.dtd (%chrome/common/sanitize.dtd)
|
||||
locale/@AB_CD@/communicator/tasksOverlay.dtd (%chrome/common/tasksOverlay.dtd)
|
||||
|
@ -38,6 +37,7 @@
|
|||
locale/@AB_CD@/communicator/utilityOverlay.properties (%chrome/common/utilityOverlay.properties)
|
||||
locale/@AB_CD@/communicator/viewZoomOverlay.dtd (%chrome/common/viewZoomOverlay.dtd)
|
||||
locale/@AB_CD@/communicator/viewZoomOverlay.properties (%chrome/common/viewZoomOverlay.properties)
|
||||
locale/@AB_CD@/communicator/aboutSessionRestore.dtd (%chrome/common/aboutSessionRestore.dtd)
|
||||
locale/@AB_CD@/communicator/bookmarks/addBookmark.dtd (%chrome/common/bookmarks/addBookmark.dtd)
|
||||
locale/@AB_CD@/communicator/bookmarks/bm-props.dtd (%chrome/common/bookmarks/bm-props.dtd)
|
||||
locale/@AB_CD@/communicator/bookmarks/bookmarks.dtd (%chrome/common/bookmarks/bookmarks.dtd)
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/* ***** 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 nsSessionStore component.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Simon Bünzli <zeniko@gmail.com>
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Michael Ventnor <m.ventnor@gmail.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 ***** */
|
||||
|
||||
#tabList {
|
||||
width: 100%;
|
||||
height: 12em;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(icon),
|
||||
treechildren::-moz-tree-image(noicon) {
|
||||
padding-right: 2px;
|
||||
margin: 0px 2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(noicon) {
|
||||
list-style-image: url("chrome://communicator/skin/bookmarks/bookmark-item.png");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(container, noicon) {
|
||||
list-style-image: url("chrome://communicator/skin/bookmarks/bookmark-folder-closed.png");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(container, noicon, open) {
|
||||
list-style-image: url("chrome://communicator/skin/bookmarks/bookmark-folder-open.png");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-checkbox(checked) {
|
||||
list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-checkbox(partial) {
|
||||
list-style-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-row(alternate) {
|
||||
background-color: -moz-oddtreerow;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-row(alternate, selected) {
|
||||
background-color: Highlight;
|
||||
}
|
||||
|
||||
#buttons {
|
||||
-moz-margin-start: 80px;
|
||||
}
|
||||
#buttons > button {
|
||||
margin-top: 2em;
|
||||
-moz-margin-start: 5px;
|
||||
}
|
|
@ -21,6 +21,7 @@ classic.jar:
|
|||
skin/classic/communicator/preferences.css (communicator/preferences.css)
|
||||
skin/classic/communicator/prefpanels.css (communicator/prefpanels.css)
|
||||
skin/classic/communicator/smileys.css (communicator/smileys.css)
|
||||
skin/classic/communicator/aboutSessionRestore.css (communicator/aboutSessionRestore.css)
|
||||
skin/classic/communicator/bookmarks/bookmark-folder-button.gif (communicator/bookmarks/bookmark-folder-button.gif)
|
||||
skin/classic/communicator/bookmarks/bookmark-folder-closed.png (communicator/bookmarks/bookmark-folder-closed.png)
|
||||
skin/classic/communicator/bookmarks/bookmark-folder-dis.png (communicator/bookmarks/bookmark-folder-dis.png)
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/* ***** 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 nsSessionStore component.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Simon Bünzli <zeniko@gmail.com>
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Michael Ventnor <m.ventnor@gmail.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 ***** */
|
||||
|
||||
#tabList {
|
||||
width: 100%;
|
||||
height: 12em;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(icon),
|
||||
treechildren::-moz-tree-image(noicon) {
|
||||
padding-right: 2px;
|
||||
margin: 0px 2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(noicon) {
|
||||
list-style-image: url("chrome://communicator/skin/bookmarks/bookmark-item.png");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(container, noicon) {
|
||||
list-style-image: url("chrome://communicator/skin/bookmarks/bookmark-folder-closed.gif");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(container, noicon, open) {
|
||||
list-style-image: url("chrome://communicator/skin/bookmarks/bookmark-folder-open.gif");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-checkbox {
|
||||
list-style-image: url("chrome://global/skin/checkbox/cbox.gif");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-checkbox(checked) {
|
||||
list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-checkbox(partial) {
|
||||
list-style-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
|
||||
}
|
|
@ -18,6 +18,7 @@ modern.jar:
|
|||
skin/modern/communicator/helpOverlay.css (communicator/helpOverlay.css)
|
||||
skin/modern/communicator/smileys.css (communicator/smileys.css)
|
||||
skin/modern/communicator/communicator.css (communicator/communicator.css)
|
||||
skin/modern/communicator/aboutSessionRestore.css (communicator/aboutSessionRestore.css)
|
||||
skin/modern/communicator/bookmarks/bookmark-folder-closed.gif (communicator/bookmarks/bookmark-folder-closed.gif)
|
||||
skin/modern/communicator/bookmarks/bookmark-folder-dis.gif (communicator/bookmarks/bookmark-folder-dis.gif)
|
||||
skin/modern/communicator/bookmarks/bookmark-folder-open.gif (communicator/bookmarks/bookmark-folder-open.gif)
|
||||
|
|
Загрузка…
Ссылка в новой задаче