aboutSessionRestore.js
Go to the documentation of this file.
1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Original Code is the nsSessionStore component.
15  *
16  * The Initial Developer of the Original Code is
17  * Simon Bünzli <zeniko@gmail.com>
18  * Portions created by the Initial Developer are Copyright (C) 2008
19  * the Initial Developer. All Rights Reserved.
20  *
21  * Contributor(s):
22  *
23  * Alternatively, the contents of this file may be used under the terms of
24  * either the GNU General Public License Version 2 or later (the "GPL"), or
25  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26  * in which case the provisions of the GPL or the LGPL are applicable instead
27  * of those above. If you wish to allow use of your version of this file only
28  * under the terms of either the GPL or the LGPL, and not to allow others to
29  * use your version of this file under the terms of the MPL, indicate your
30  * decision by deleting the provisions above and replace them with the notice
31  * and other provisions required by the GPL or the LGPL. If you do not delete
32  * the provisions above, a recipient may use your version of this file under
33  * the terms of any one of the MPL, the GPL or the LGPL.
34  *
35  * ***** END LICENSE BLOCK ***** */
36 
37 const Cc = Components.classes;
38 const Ci = Components.interfaces;
39 
42 
43 // Page initialization
44 
45 window.onload = function() {
46  // the crashed session state is kept inside a textbox so that SessionStore picks it up
47  // (for when the tab is closed or the session crashes right again)
48  var sessionData = document.getElementById("sessionData");
49  if (!sessionData.value) {
50  var ss = Cc["@mozilla.org/browser/sessionstartup;1"].getService(Ci.nsISessionStartup);
51  sessionData.value = ss.state;
52  if (!sessionData.value) {
53  document.getElementById("errorTryAgain").disabled = true;
54  return;
55  }
56  }
57  // make sure the data is tracked to be restored in case of a subsequent crash
58  var event = document.createEvent("UIEvents");
59  event.initUIEvent("input", true, true, window, 0);
60  sessionData.dispatchEvent(event);
61 
62  var s = new Components.utils.Sandbox("about:blank");
63  gStateObject = Components.utils.evalInSandbox("(" + sessionData.value + ")", s);
64 
65  initTreeView();
66 
67  document.getElementById("errorTryAgain").focus();
68 };
69 
70 function initTreeView() {
71  var tabList = document.getElementById("tabList");
72  var winLabel = tabList.getAttribute("_window_label");
73 
74  gTreeData = [];
75  gStateObject.windows.forEach(function(aWinData, aIx) {
76  var winState = {
77  label: winLabel.replace("%S", (aIx + 1)),
78  open: true,
79  checked: true,
80  ix: aIx
81  };
82  winState.tabs = aWinData.tabs.map(function(aTabData) {
83  var entry = aTabData.entries[aTabData.index - 1] || { url: "about:blank" };
84  var iconURL = aTabData.attributes && aTabData.attributes.image || null;
85  // don't initiate a connection just to fetch a favicon (see bug 462863)
86  if (/^https?:/.test(iconURL))
87  iconURL = "moz-anno:favicon:" + iconURL;
88  return {
89  label: entry.title || entry.url,
90  checked: true,
91  src: iconURL,
92  parent: winState
93  };
94  });
95  gTreeData.push(winState);
96  for each (var tab in winState.tabs)
97  gTreeData.push(tab);
98  }, this);
99 
100  tabList.view = treeView;
101  tabList.view.selection.select(0);
102 }
103 
104 // User actions
105 
106 function restoreSession() {
107  document.getElementById("errorTryAgain").disabled = true;
108 
109  // remove all unselected tabs from the state before restoring it
110  var ix = gStateObject.windows.length - 1;
111  for (var t = gTreeData.length - 1; t >= 0; t--) {
112  if (treeView.isContainer(t)) {
113  if (gTreeData[t].checked === 0)
114  // this window will be restored partially
115  gStateObject.windows[ix].tabs =
116  gStateObject.windows[ix].tabs.filter(function(aTabData, aIx)
117  gTreeData[t].tabs[aIx].checked);
118  else if (!gTreeData[t].checked)
119  // this window won't be restored at all
120  gStateObject.windows.splice(ix, 1);
121  ix--;
122  }
123  }
124  var stateString = gStateObject.toSource();
125 
126  var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
127  var top = getBrowserWindow();
128 
129  // if there's only this page open, reuse the window for restoring the session
130  if (top.gBrowser.tabContainer.childNodes.length == 1) {
131  ss.setWindowState(top, stateString, true);
132  return;
133  }
134 
135  // restore the session into a new window and close the current tab
136  var newWindow = top.openDialog(top.location, "_blank", "chrome,dialog=no,all");
137  newWindow.addEventListener("load", function() {
138  newWindow.removeEventListener("load", arguments.callee, true);
139  ss.setWindowState(newWindow, stateString, true);
140 
141  var tabbrowser = top.gBrowser;
142  var tabIndex = tabbrowser.getBrowserIndexForDocument(document);
143  tabbrowser.removeTab(tabbrowser.tabContainer.childNodes[tabIndex]);
144  }, true);
145 }
146 
147 function startNewSession() {
148  var prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
149  if (prefBranch.getIntPref("browser.startup.page") == 0)
150  getBrowserWindow().gBrowser.loadURI("about:blank");
151  else
152  getBrowserWindow().BrowserHome();
153 }
154 
155 function onListClick(aEvent) {
156  // don't react to right-clicks
157  if (aEvent.button == 2)
158  return;
159 
160  var row = {}, col = {};
161  treeView.treeBox.getCellAt(aEvent.clientX, aEvent.clientY, row, col, {});
162  if (col.value) {
163  // restore this specific tab in the same window for middle-clicking
164  // or Ctrl+clicking on a tab's title
165  if ((aEvent.button == 1 || aEvent.ctrlKey) && col.value.id == "title" &&
166  !treeView.isContainer(row.value))
167  restoreSingleTab(row.value, aEvent.shiftKey);
168  else if (col.value.id == "restore")
169  toggleRowChecked(row.value);
170  }
171 }
172 
173 function onListKeyDown(aEvent) {
174  switch (aEvent.keyCode)
175  {
176  case KeyEvent.DOM_VK_SPACE:
177  toggleRowChecked(document.getElementById("tabList").currentIndex);
178  break;
179  case KeyEvent.DOM_VK_RETURN:
180  var ix = document.getElementById("tabList").currentIndex;
181  if (aEvent.ctrlKey && !treeView.isContainer(ix))
182  restoreSingleTab(ix, aEvent.shiftKey);
183  break;
184  case KeyEvent.DOM_VK_UP:
185  case KeyEvent.DOM_VK_DOWN:
186  case KeyEvent.DOM_VK_PAGE_UP:
187  case KeyEvent.DOM_VK_PAGE_DOWN:
188  case KeyEvent.DOM_VK_HOME:
189  case KeyEvent.DOM_VK_END:
190  aEvent.preventDefault(); // else the page scrolls unwantedly
191  break;
192  }
193 }
194 
195 // Helper functions
196 
197 function getBrowserWindow() {
198  return window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
199  .QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem
200  .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
201 }
202 
204  var item = gTreeData[aIx];
205  item.checked = !item.checked;
206  treeView.treeBox.invalidateRow(aIx);
207 
208  function isChecked(aItem) aItem.checked;
209 
210  if (treeView.isContainer(aIx)) {
211  // (un)check all tabs of this window as well
212  for each (var tab in item.tabs) {
213  tab.checked = item.checked;
214  treeView.treeBox.invalidateRow(gTreeData.indexOf(tab));
215  }
216  }
217  else {
218  // update the window's checkmark as well (0 means "partially checked")
219  item.parent.checked = item.parent.tabs.every(isChecked) ? true :
220  item.parent.tabs.some(isChecked) ? 0 : false;
221  treeView.treeBox.invalidateRow(gTreeData.indexOf(item.parent));
222  }
223 
224  document.getElementById("errorTryAgain").disabled = !gTreeData.some(isChecked);
225 }
226 
227 function restoreSingleTab(aIx, aShifted) {
228  var tabbrowser = getBrowserWindow().gBrowser;
229  var newTab = tabbrowser.addTab();
230  var item = gTreeData[aIx];
231 
232  var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
233  var tabState = gStateObject.windows[item.parent.ix]
234  .tabs[aIx - gTreeData.indexOf(item.parent) - 1];
235  ss.setTabState(newTab, tabState.toSource());
236 
237  // respect the preference as to whether to select the tab (the Shift key inverses)
238  var prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
239  if (prefBranch.getBoolPref("browser.tabs.loadInBackground") != !aShifted)
240  tabbrowser.selectedTab = newTab;
241 }
242 
243 // Tree controller
244 
245 var treeView = {
246  _atoms: {},
247  _getAtom: function(aName)
248  {
249  if (!this._atoms[aName]) {
250  var as = Cc["@mozilla.org/atom-service;1"].getService(Ci.nsIAtomService);
251  this._atoms[aName] = as.getAtom(aName);
252  }
253  return this._atoms[aName];
254  },
255 
256  treeBox: null,
257  selection: null,
258 
259  get rowCount() { return gTreeData.length; },
260  setTree: function(treeBox) { this.treeBox = treeBox; },
261  getCellText: function(idx, column) { return gTreeData[idx].label; },
262  isContainer: function(idx) { return "open" in gTreeData[idx]; },
263  getCellValue: function(idx, column){ return gTreeData[idx].checked; },
264  isContainerOpen: function(idx) { return gTreeData[idx].open; },
265  isContainerEmpty: function(idx) { return false; },
266  isSeparator: function(idx) { return false; },
267  isSorted: function() { return false; },
268  isEditable: function(idx, column) { return false; },
269  getLevel: function(idx) { return this.isContainer(idx) ? 0 : 1; },
270 
271  getParentIndex: function(idx) {
272  if (!this.isContainer(idx))
273  for (var t = idx - 1; t >= 0 ; t--)
274  if (this.isContainer(t))
275  return t;
276  return -1;
277  },
278 
279  hasNextSibling: function(idx, after) {
280  var thisLevel = this.getLevel(idx);
281  for (var t = after + 1; t < gTreeData.length; t++)
282  if (this.getLevel(t) <= thisLevel)
283  return this.getLevel(t) == thisLevel;
284  return false;
285  },
286 
287  toggleOpenState: function(idx) {
288  if (!this.isContainer(idx))
289  return;
290  var item = gTreeData[idx];
291  if (item.open) {
292  // remove this window's tab rows from the view
293  var thisLevel = this.getLevel(idx);
294  for (var t = idx + 1; t < gTreeData.length && this.getLevel(t) > thisLevel; t++);
295  var deletecount = t - idx - 1;
296  gTreeData.splice(idx + 1, deletecount);
297  this.treeBox.rowCountChanged(idx + 1, -deletecount);
298  }
299  else {
300  // add this window's tab rows to the view
301  var toinsert = gTreeData[idx].tabs;
302  for (var i = 0; i < toinsert.length; i++)
303  gTreeData.splice(idx + i + 1, 0, toinsert[i]);
304  this.treeBox.rowCountChanged(idx + 1, toinsert.length);
305  }
306  item.open = !item.open;
307  this.treeBox.invalidateRow(idx);
308  },
309 
310  getCellProperties: function(idx, column, prop) {
311  if (column.id == "restore" && this.isContainer(idx) && gTreeData[idx].checked === 0)
312  prop.AppendElement(this._getAtom("partial"));
313  if (column.id == "title")
314  prop.AppendElement(this._getAtom(this.getImageSrc(idx, column) ? "icon" : "noicon"));
315  },
316 
317  getRowProperties: function(idx, prop) {
318  var winState = gTreeData[idx].parent || gTreeData[idx];
319  if (winState.ix % 2 != 0)
320  prop.AppendElement(this._getAtom("alternate"));
321  },
322 
323  getImageSrc: function(idx, column) {
324  if (column.id == "title")
325  return gTreeData[idx].src || null;
326  return null;
327  },
328 
329  getProgressMode : function(idx, column) { },
330  cycleHeader: function(column) { },
331  cycleCell: function(idx, column) { },
332  selectionChanged: function() { },
333  performAction: function(action) { },
334  performActionOnCell: function(action, index, column) { },
335  getColumnProperties: function(column, prop) { }
336 };
classDescription entry
Definition: FeedWriter.js:1427
function restoreSingleTab(aIx, aShifted)
function getBrowserWindow()
var ix
var event
var gStateObject
function restoreSession()
var tab
let window
gImageView getCellProperties
Definition: pageInfo.js:171
var gTreeData
const Ci
this _contentSandbox label
Definition: FeedWriter.js:814
var t
function onListKeyDown(aEvent)
_updateTextAndScrollDataForTab aTabData
BogusChannel prototype open
var tabbrowser
var tabs
return null
Definition: FeedWriter.js:1143
function onListClick(aEvent)
function startNewSession()
restoreHistoryPrecursor aIx
return!aWindow arguments!aWindow arguments[0]
function initTreeView()
_updateCookies aName
function url(spec)
var tabIndex
const Cc
function toggleRowChecked(aIx)
_getSelectedPageStyle s i
var treeView
restoreWindowFeatures aWinData