/*
To run this script in newer versions of Firefox, you need to use the "Scratchpad" tool.

First time setup

Enable Chrome debugging using one of these methods:

(1) Developer Tools Settings

  * "3-bar" menu button > Developer > Web Console
  * Click the "gear" icon at the left end of the bar to open the settings panel
  * In the lower right of the settings panel, check the box for "Enable chrome debugging"
  * You can close the web console now.

(2) about:config preferences editor

  * In a new tab, type or paste about:config in the address bar and press Enter. Click the button promising to be careful.
  * Type dev in the search box above the list and pause while Firefox filters the list
  * Doubleclick devtools.chrome.enabled to switch it from false to true
  * You can close about:config now.

Modified from: http://forums.mozillazine.org/viewtopic.php?p=12098147#p12098147

*/

/*
To run the script:

  * Select this script and copy it (if JavaScript is enabled, just click to select)
  * Open the scratchpad: "3-bar" menu button > Developer > Scratchpad
  * On the scratchpad menu bar, find the Environment menu and change from Content to Browser
  * Paste this page into the scratchpad area
  * Click the Run button on the toolbar, and a file picker should pop up
  * To extract from sessionstore.js (current live session) or sessionstore.bak (previous session) cancel the file picker  

*/  
function scrub(t){
  t = converter.ConvertFromUnicode(t);
  t = t.replace(/&/g, "&");
  t = t.replace(/>/g, ">");
  t = t.replace(/</g, "&lt;");
  t = t.replace(/"/g, "&quot;");
  t = t.replace(/'/g, "&apos;");
  return t;
}
var win;
if (typeof gBrowser == "undefined") {
  win = top.opener; /* Error Console */
} else {
  win = window; /* Scratchpad */
}
var fp = Components.classes["@mozilla.org/filepicker;1"].
  createInstance(Components.interfaces.nsIFilePicker);
fp.init(win, "Open File", Components.interfaces.nsIFilePicker.modeOpen);
fp.appendFilter("Session Store Files", "*.bak; *.js; *.json");
fp.appendFilter("JSON Files", "*.json");
fp.appendFilter("JS Files", "*.js");
fp.appendFilter("BAK Files", "*.bak");
fp.appendFilter("OLD Files", "*.old");
var file;
if (fp.show() == Components.interfaces.nsIFilePicker.returnOK) {
  file = fp.file;
} else {
  var info = Components.classes["@mozilla.org/xre/app-info;1"].
    getService(Components.interfaces.nsIXULAppInfo);
  var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
    getService(Components.interfaces.nsIPromptService);
  var fileExtension = info.name == "SeaMonkey" ? "json" : "js";
  var parentWindow = null;
  var title = "Open From Current Profile Folder";
  var description = "Open sessionstore." + fileExtension + " or sessionstore.bak from the currenctly active profile folder:";
  var flags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING +
    ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING +
    ps.BUTTON_POS_2 * ps.BUTTON_TITLE_IS_STRING;
  var btn0 = fileExtension.toUpperCase();
  var btn1 = "Cancel";
  var btn2 = "BAK";
  var message = null;
  var state = {value: false};
  var choice = ps.confirmEx(parentWindow, title, description, flags, btn0, btn1, btn2, message, state);
  var dirService = Components.classes["@mozilla.org/file/directory_service;1"].
    getService(Components.interfaces.nsIProperties);
  file = dirService.get("ProfD", Components.interfaces.nsILocalFile);
  if (choice == 0) file.append("sessionstore." + fileExtension);
  if (choice == 2) file.append("sessionstore.bak");
}
if (file.exists() && file.isFile() && file.isReadable()) {
  var fis = Components.classes["@mozilla.org/network/file-input-stream;1"].
    createInstance(Components.interfaces.nsIFileInputStream);
  fis.init(file, 0x01, 0, 0);
  var fileSize = fis.available();
  var cis = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
    createInstance(Components.interfaces.nsIConverterInputStream);
  cis.init(fis, "UTF-8", fileSize, Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
  var data = new Object();
  cis.readString(fileSize, data);
  cis.close();
  var jsonObject = JSON.parse(data.value);
  var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
    createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  converter.charset = "UTF-8";
  var out = new Array();
  for (var i = 0, windows = jsonObject.windows; i < windows.length; i++) {
    out.push('<h2 style="border-bottom:1px solid #000;cursor:pointer" onclick="tog(this);">Window ' + (i + 1) + '</h2><div style="margin-left:2.25em">');
    for (var j = 0, tabs = windows[i].tabs; j < tabs.length; j++) {
      out.push('<h3>Tab ' + (j + 1) + '</h3>\n<div class="urllist">');
      for (var entries = tabs[j].entries, k = entries.length - 1; k >= 0; k--) {
        if (k == entries.length - 2) out.push('<p>&larr; Back <em>(earlier pages visited in this tab)</em>:</p>');
        out.push('<p><strong>' + scrub((entries[k].title || "[Title Not Available]")) + '</strong><br><a href="' + entries[k].url + '" target="_blank">' + scrub(entries[k].url) + '</a></p>');
      }
      out.push("</div>");
    }
    out.push("</div>");
  }
  var data = '<!DOCTYPE html>';
  data += "<html><head><title>session contents</title>";
  data += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><style type="text/css">.urllist>p{margin-left:2.25em}.urllist>p:nth-of-type(1){margin-left:0}</style>';
  data += "</head><body><h1>Session Contents</h1>\n" + out.join("\n");
  data += '\n<script type="text/javascript">function tog(h){var d=h.nextElementSibling;if(!d)return;if(d.style.display=="none")d.style.display="";else d.style.display="none";}</script></body></html>';
  var browser = win.gBrowser.getBrowserForTab(
    win.gBrowser.selectedTab = win.gBrowser.addTab("data:text/html;charset=utf-8;base64," + btoa(data)));
}

/* Updated 7/20/2014 */