browser/components/sessionstore/content/aboutSessionRestore.js

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 const Cc = Components.classes;
michael@0 6 const Ci = Components.interfaces;
michael@0 7 const Cu = Components.utils;
michael@0 8
michael@0 9 var gStateObject;
michael@0 10 var gTreeData;
michael@0 11
michael@0 12 // Page initialization
michael@0 13
michael@0 14 window.onload = function() {
michael@0 15 // the crashed session state is kept inside a textbox so that SessionStore picks it up
michael@0 16 // (for when the tab is closed or the session crashes right again)
michael@0 17 var sessionData = document.getElementById("sessionData");
michael@0 18 if (!sessionData.value) {
michael@0 19 document.getElementById("errorTryAgain").disabled = true;
michael@0 20 return;
michael@0 21 }
michael@0 22
michael@0 23 // remove unneeded braces (added for compatibility with Firefox 2.0 and 3.0)
michael@0 24 if (sessionData.value.charAt(0) == '(')
michael@0 25 sessionData.value = sessionData.value.slice(1, -1);
michael@0 26 try {
michael@0 27 gStateObject = JSON.parse(sessionData.value);
michael@0 28 }
michael@0 29 catch (exJSON) {
michael@0 30 var s = new Cu.Sandbox("about:blank", {sandboxName: 'aboutSessionRestore'});
michael@0 31 gStateObject = Cu.evalInSandbox("(" + sessionData.value + ")", s);
michael@0 32 // If we couldn't parse the string with JSON.parse originally, make sure
michael@0 33 // that the value in the textbox will be parsable.
michael@0 34 sessionData.value = JSON.stringify(gStateObject);
michael@0 35 }
michael@0 36
michael@0 37 // make sure the data is tracked to be restored in case of a subsequent crash
michael@0 38 var event = document.createEvent("UIEvents");
michael@0 39 event.initUIEvent("input", true, true, window, 0);
michael@0 40 sessionData.dispatchEvent(event);
michael@0 41
michael@0 42 initTreeView();
michael@0 43
michael@0 44 document.getElementById("errorTryAgain").focus();
michael@0 45 };
michael@0 46
michael@0 47 function initTreeView() {
michael@0 48 var tabList = document.getElementById("tabList");
michael@0 49 var winLabel = tabList.getAttribute("_window_label");
michael@0 50
michael@0 51 gTreeData = [];
michael@0 52 gStateObject.windows.forEach(function(aWinData, aIx) {
michael@0 53 var winState = {
michael@0 54 label: winLabel.replace("%S", (aIx + 1)),
michael@0 55 open: true,
michael@0 56 checked: true,
michael@0 57 ix: aIx
michael@0 58 };
michael@0 59 winState.tabs = aWinData.tabs.map(function(aTabData) {
michael@0 60 var entry = aTabData.entries[aTabData.index - 1] || { url: "about:blank" };
michael@0 61 var iconURL = aTabData.attributes && aTabData.attributes.image || null;
michael@0 62 // don't initiate a connection just to fetch a favicon (see bug 462863)
michael@0 63 if (/^https?:/.test(iconURL))
michael@0 64 iconURL = "moz-anno:favicon:" + iconURL;
michael@0 65 return {
michael@0 66 label: entry.title || entry.url,
michael@0 67 checked: true,
michael@0 68 src: iconURL,
michael@0 69 parent: winState
michael@0 70 };
michael@0 71 });
michael@0 72 gTreeData.push(winState);
michael@0 73 for each (var tab in winState.tabs)
michael@0 74 gTreeData.push(tab);
michael@0 75 }, this);
michael@0 76
michael@0 77 tabList.view = treeView;
michael@0 78 tabList.view.selection.select(0);
michael@0 79 }
michael@0 80
michael@0 81 // User actions
michael@0 82
michael@0 83 function restoreSession() {
michael@0 84 document.getElementById("errorTryAgain").disabled = true;
michael@0 85
michael@0 86 // remove all unselected tabs from the state before restoring it
michael@0 87 var ix = gStateObject.windows.length - 1;
michael@0 88 for (var t = gTreeData.length - 1; t >= 0; t--) {
michael@0 89 if (treeView.isContainer(t)) {
michael@0 90 if (gTreeData[t].checked === 0)
michael@0 91 // this window will be restored partially
michael@0 92 gStateObject.windows[ix].tabs =
michael@0 93 gStateObject.windows[ix].tabs.filter(function(aTabData, aIx)
michael@0 94 gTreeData[t].tabs[aIx].checked);
michael@0 95 else if (!gTreeData[t].checked)
michael@0 96 // this window won't be restored at all
michael@0 97 gStateObject.windows.splice(ix, 1);
michael@0 98 ix--;
michael@0 99 }
michael@0 100 }
michael@0 101 var stateString = JSON.stringify(gStateObject);
michael@0 102
michael@0 103 var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
michael@0 104 var top = getBrowserWindow();
michael@0 105
michael@0 106 // if there's only this page open, reuse the window for restoring the session
michael@0 107 if (top.gBrowser.tabs.length == 1) {
michael@0 108 ss.setWindowState(top, stateString, true);
michael@0 109 return;
michael@0 110 }
michael@0 111
michael@0 112 // restore the session into a new window and close the current tab
michael@0 113 var newWindow = top.openDialog(top.location, "_blank", "chrome,dialog=no,all");
michael@0 114 newWindow.addEventListener("load", function() {
michael@0 115 newWindow.removeEventListener("load", arguments.callee, true);
michael@0 116 ss.setWindowState(newWindow, stateString, true);
michael@0 117
michael@0 118 var tabbrowser = top.gBrowser;
michael@0 119 var tabIndex = tabbrowser.getBrowserIndexForDocument(document);
michael@0 120 tabbrowser.removeTab(tabbrowser.tabs[tabIndex]);
michael@0 121 }, true);
michael@0 122 }
michael@0 123
michael@0 124 function startNewSession() {
michael@0 125 var prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
michael@0 126 if (prefBranch.getIntPref("browser.startup.page") == 0)
michael@0 127 getBrowserWindow().gBrowser.loadURI("about:blank");
michael@0 128 else
michael@0 129 getBrowserWindow().BrowserHome();
michael@0 130 }
michael@0 131
michael@0 132 function onListClick(aEvent) {
michael@0 133 // don't react to right-clicks
michael@0 134 if (aEvent.button == 2)
michael@0 135 return;
michael@0 136
michael@0 137 var row = {}, col = {};
michael@0 138 treeView.treeBox.getCellAt(aEvent.clientX, aEvent.clientY, row, col, {});
michael@0 139 if (col.value) {
michael@0 140 // Restore this specific tab in the same window for middle/double/accel clicking
michael@0 141 // on a tab's title.
michael@0 142 #ifdef XP_MACOSX
michael@0 143 let accelKey = aEvent.metaKey;
michael@0 144 #else
michael@0 145 let accelKey = aEvent.ctrlKey;
michael@0 146 #endif
michael@0 147 if ((aEvent.button == 1 || aEvent.button == 0 && aEvent.detail == 2 || accelKey) &&
michael@0 148 col.value.id == "title" &&
michael@0 149 !treeView.isContainer(row.value)) {
michael@0 150 restoreSingleTab(row.value, aEvent.shiftKey);
michael@0 151 aEvent.stopPropagation();
michael@0 152 }
michael@0 153 else if (col.value.id == "restore")
michael@0 154 toggleRowChecked(row.value);
michael@0 155 }
michael@0 156 }
michael@0 157
michael@0 158 function onListKeyDown(aEvent) {
michael@0 159 switch (aEvent.keyCode)
michael@0 160 {
michael@0 161 case KeyEvent.DOM_VK_SPACE:
michael@0 162 toggleRowChecked(document.getElementById("tabList").currentIndex);
michael@0 163 break;
michael@0 164 case KeyEvent.DOM_VK_RETURN:
michael@0 165 var ix = document.getElementById("tabList").currentIndex;
michael@0 166 if (aEvent.ctrlKey && !treeView.isContainer(ix))
michael@0 167 restoreSingleTab(ix, aEvent.shiftKey);
michael@0 168 break;
michael@0 169 }
michael@0 170 }
michael@0 171
michael@0 172 // Helper functions
michael@0 173
michael@0 174 function getBrowserWindow() {
michael@0 175 return window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
michael@0 176 .QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem
michael@0 177 .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
michael@0 178 }
michael@0 179
michael@0 180 function toggleRowChecked(aIx) {
michael@0 181 var item = gTreeData[aIx];
michael@0 182 item.checked = !item.checked;
michael@0 183 treeView.treeBox.invalidateRow(aIx);
michael@0 184
michael@0 185 function isChecked(aItem) aItem.checked;
michael@0 186
michael@0 187 if (treeView.isContainer(aIx)) {
michael@0 188 // (un)check all tabs of this window as well
michael@0 189 for each (var tab in item.tabs) {
michael@0 190 tab.checked = item.checked;
michael@0 191 treeView.treeBox.invalidateRow(gTreeData.indexOf(tab));
michael@0 192 }
michael@0 193 }
michael@0 194 else {
michael@0 195 // update the window's checkmark as well (0 means "partially checked")
michael@0 196 item.parent.checked = item.parent.tabs.every(isChecked) ? true :
michael@0 197 item.parent.tabs.some(isChecked) ? 0 : false;
michael@0 198 treeView.treeBox.invalidateRow(gTreeData.indexOf(item.parent));
michael@0 199 }
michael@0 200
michael@0 201 document.getElementById("errorTryAgain").disabled = !gTreeData.some(isChecked);
michael@0 202 }
michael@0 203
michael@0 204 function restoreSingleTab(aIx, aShifted) {
michael@0 205 var tabbrowser = getBrowserWindow().gBrowser;
michael@0 206 var newTab = tabbrowser.addTab();
michael@0 207 var item = gTreeData[aIx];
michael@0 208
michael@0 209 var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
michael@0 210 var tabState = gStateObject.windows[item.parent.ix]
michael@0 211 .tabs[aIx - gTreeData.indexOf(item.parent) - 1];
michael@0 212 // ensure tab would be visible on the tabstrip.
michael@0 213 tabState.hidden = false;
michael@0 214 ss.setTabState(newTab, JSON.stringify(tabState));
michael@0 215
michael@0 216 // respect the preference as to whether to select the tab (the Shift key inverses)
michael@0 217 var prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
michael@0 218 if (prefBranch.getBoolPref("browser.tabs.loadInBackground") != !aShifted)
michael@0 219 tabbrowser.selectedTab = newTab;
michael@0 220 }
michael@0 221
michael@0 222 // Tree controller
michael@0 223
michael@0 224 var treeView = {
michael@0 225 treeBox: null,
michael@0 226 selection: null,
michael@0 227
michael@0 228 get rowCount() { return gTreeData.length; },
michael@0 229 setTree: function(treeBox) { this.treeBox = treeBox; },
michael@0 230 getCellText: function(idx, column) { return gTreeData[idx].label; },
michael@0 231 isContainer: function(idx) { return "open" in gTreeData[idx]; },
michael@0 232 getCellValue: function(idx, column){ return gTreeData[idx].checked; },
michael@0 233 isContainerOpen: function(idx) { return gTreeData[idx].open; },
michael@0 234 isContainerEmpty: function(idx) { return false; },
michael@0 235 isSeparator: function(idx) { return false; },
michael@0 236 isSorted: function() { return false; },
michael@0 237 isEditable: function(idx, column) { return false; },
michael@0 238 canDrop: function(idx, orientation, dt) { return false; },
michael@0 239 getLevel: function(idx) { return this.isContainer(idx) ? 0 : 1; },
michael@0 240
michael@0 241 getParentIndex: function(idx) {
michael@0 242 if (!this.isContainer(idx))
michael@0 243 for (var t = idx - 1; t >= 0 ; t--)
michael@0 244 if (this.isContainer(t))
michael@0 245 return t;
michael@0 246 return -1;
michael@0 247 },
michael@0 248
michael@0 249 hasNextSibling: function(idx, after) {
michael@0 250 var thisLevel = this.getLevel(idx);
michael@0 251 for (var t = after + 1; t < gTreeData.length; t++)
michael@0 252 if (this.getLevel(t) <= thisLevel)
michael@0 253 return this.getLevel(t) == thisLevel;
michael@0 254 return false;
michael@0 255 },
michael@0 256
michael@0 257 toggleOpenState: function(idx) {
michael@0 258 if (!this.isContainer(idx))
michael@0 259 return;
michael@0 260 var item = gTreeData[idx];
michael@0 261 if (item.open) {
michael@0 262 // remove this window's tab rows from the view
michael@0 263 var thisLevel = this.getLevel(idx);
michael@0 264 for (var t = idx + 1; t < gTreeData.length && this.getLevel(t) > thisLevel; t++);
michael@0 265 var deletecount = t - idx - 1;
michael@0 266 gTreeData.splice(idx + 1, deletecount);
michael@0 267 this.treeBox.rowCountChanged(idx + 1, -deletecount);
michael@0 268 }
michael@0 269 else {
michael@0 270 // add this window's tab rows to the view
michael@0 271 var toinsert = gTreeData[idx].tabs;
michael@0 272 for (var i = 0; i < toinsert.length; i++)
michael@0 273 gTreeData.splice(idx + i + 1, 0, toinsert[i]);
michael@0 274 this.treeBox.rowCountChanged(idx + 1, toinsert.length);
michael@0 275 }
michael@0 276 item.open = !item.open;
michael@0 277 this.treeBox.invalidateRow(idx);
michael@0 278 },
michael@0 279
michael@0 280 getCellProperties: function(idx, column) {
michael@0 281 if (column.id == "restore" && this.isContainer(idx) && gTreeData[idx].checked === 0)
michael@0 282 return "partial";
michael@0 283 if (column.id == "title")
michael@0 284 return this.getImageSrc(idx, column) ? "icon" : "noicon";
michael@0 285
michael@0 286 return "";
michael@0 287 },
michael@0 288
michael@0 289 getRowProperties: function(idx) {
michael@0 290 var winState = gTreeData[idx].parent || gTreeData[idx];
michael@0 291 if (winState.ix % 2 != 0)
michael@0 292 return "alternate";
michael@0 293
michael@0 294 return "";
michael@0 295 },
michael@0 296
michael@0 297 getImageSrc: function(idx, column) {
michael@0 298 if (column.id == "title")
michael@0 299 return gTreeData[idx].src || null;
michael@0 300 return null;
michael@0 301 },
michael@0 302
michael@0 303 getProgressMode : function(idx, column) { },
michael@0 304 cycleHeader: function(column) { },
michael@0 305 cycleCell: function(idx, column) { },
michael@0 306 selectionChanged: function() { },
michael@0 307 performAction: function(action) { },
michael@0 308 performActionOnCell: function(action, index, column) { },
michael@0 309 getColumnProperties: function(column) { return ""; }
michael@0 310 };

mercurial