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.

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

mercurial