diff options
Diffstat (limited to 'comm/suite/components/sessionstore/content')
-rw-r--r-- | comm/suite/components/sessionstore/content/aboutSessionRestore.js | 291 | ||||
-rw-r--r-- | comm/suite/components/sessionstore/content/aboutSessionRestore.xhtml | 84 |
2 files changed, 375 insertions, 0 deletions
diff --git a/comm/suite/components/sessionstore/content/aboutSessionRestore.js b/comm/suite/components/sessionstore/content/aboutSessionRestore.js new file mode 100644 index 0000000000..677bf20adc --- /dev/null +++ b/comm/suite/components/sessionstore/content/aboutSessionRestore.js @@ -0,0 +1,291 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +var gStateObject; +var gTreeData; + +// Page initialization + +window.onload = function() { + // establish the event handlers for <tree> and <button> elements + var tabList = document.getElementById("tabList"); + tabList.addEventListener("click", onListClick); + tabList.addEventListener("keydown", onListKeyDown); + + document.getElementById("errorTryAgain") + .addEventListener("command", restoreSession); + + document.getElementById("errorCancel") + .addEventListener("command", startNewSession); + + // 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 = Cc["@mozilla.org/suite/sessionstartup;1"].getService(Ci.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 + sessionData.dispatchEvent(new UIEvent("input", + { bubbles: true, cancelable: true, view: window, detail: 0 })); + + gStateObject = JSON.parse(sessionData.value); + + 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 (var tab of 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((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 = JSON.stringify(gStateObject); + + var ss = Cc["@mozilla.org/suite/sessionstore;1"].getService(Ci.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 newWindowLoad() { + newWindow.removeEventListener("load", newWindowLoad, true); + ss.setWindowState(newWindow, stateString, true); + + top.gBrowser.removeTab(tab); + }, true); +} + +function startNewSession() { + if (Services.prefs.getIntPref("browser.startup.page") == 1) + getBrowserWindow().BrowserHome(); + else + getBrowserWindow().getBrowser().loadURI("about:blank"); +} + +function onListClick(aEvent) { + // don't react to right-clicks + if (aEvent.button == 2) + return; + + var cell = treeView.treeBox.getCellAt(aEvent.clientX, aEvent.clientY); + if (cell.col) { + // 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) && cell.col.id == "title" && + !treeView.isContainer(cell.row)) + restoreSingleTab(cell.row, aEvent.shiftKey); + else if (cell.col.id == "restore") + toggleRowChecked(cell.row); + } +} + +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; + } +} + +// Helper functions + +function getBrowserWindow() { + return window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow); +} + +function toggleRowChecked(aIx) { + var item = gTreeData[aIx]; + item.checked = !item.checked; + treeView.treeBox.invalidateRow(aIx); + + function isChecked(aItem) { + return aItem.checked; + } + + if (treeView.isContainer(aIx)) { + // (un)check all tabs of this window as well + for (var tab of 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 = Cc["@mozilla.org/suite/sessionstore;1"].getService(Ci.nsISessionStore); + var tabState = gStateObject.windows[item.parent.ix] + .tabs[aIx - gTreeData.indexOf(item.parent) - 1]; + ss.setTabState(newTab, JSON.stringify(tabState)); + + // respect the preference as to whether to select the tab (the Shift key inverses) + if (Services.prefs.getBoolPref("browser.tabs.loadInBackground") != !aShifted) + tabbrowser.selectedTab = newTab; +} + +// Tree controller + +var treeView = { + 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; }, + canDrop: function(idx, orientation, dt) { 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) { + if (column.id == "restore" && this.isContainer(idx) && gTreeData[idx].checked === 0) + return "partial"; + if (column.id == "title") + return this.getImageSrc(idx, column) ? "icon" : "noicon"; + return ""; + }, + + getRowProperties: function(idx) { + var winState = gTreeData[idx].parent || gTreeData[idx]; + return winState.ix % 2 != 0 ? "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() { }, + getColumnProperties: function(column) { return ""; } +}; diff --git a/comm/suite/components/sessionstore/content/aboutSessionRestore.xhtml b/comm/suite/components/sessionstore/content/aboutSessionRestore.xhtml new file mode 100644 index 0000000000..34c9a893ea --- /dev/null +++ b/comm/suite/components/sessionstore/content/aboutSessionRestore.xhtml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +--> +<!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 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" + _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;"/> + <button id="errorCancel" label="&restorepage.cancelButton;" + accesskey="&restorepage.cancel.access;"/> + </hbox> + <!-- holds the session data for when the tab is closed --> + <input type="hidden" id="sessionData"/> + </div> + + </body> +</html> |