diff -r 000000000000 -r 6474c204b198 browser/components/sessionstore/src/SessionHistory.jsm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/browser/components/sessionstore/src/SessionHistory.jsm Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,434 @@ +/* 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/. */ + +"use strict"; + +this.EXPORTED_SYMBOLS = ["SessionHistory"]; + +const Cu = Components.utils; +const Cc = Components.classes; +const Ci = Components.interfaces; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "Utils", + "resource:///modules/sessionstore/Utils.jsm"); + +function debug(msg) { + Services.console.logStringMessage("SessionHistory: " + msg); +} + +/** + * The external API exported by this module. + */ +this.SessionHistory = Object.freeze({ + isEmpty: function (docShell) { + return SessionHistoryInternal.isEmpty(docShell); + }, + + collect: function (docShell) { + return SessionHistoryInternal.collect(docShell); + }, + + restore: function (docShell, tabData) { + SessionHistoryInternal.restore(docShell, tabData); + } +}); + +/** + * The internal API for the SessionHistory module. + */ +let SessionHistoryInternal = { + /** + * Returns whether the given docShell's session history is empty. + * + * @param docShell + * The docShell that owns the session history. + */ + isEmpty: function (docShell) { + let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation); + let history = webNavigation.sessionHistory; + if (!webNavigation.currentURI) { + return true; + } + let uri = webNavigation.currentURI.spec; + return uri == "about:blank" && history.count == 0; + }, + + /** + * Collects session history data for a given docShell. + * + * @param docShell + * The docShell that owns the session history. + */ + collect: function (docShell) { + let data = {entries: []}; + let isPinned = docShell.isAppTab; + let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation); + let history = webNavigation.sessionHistory; + + if (history && history.count > 0) { + let oldest; + let maxSerializeBack = + Services.prefs.getIntPref("browser.sessionstore.max_serialize_back"); + if (maxSerializeBack >= 0) { + oldest = Math.max(0, history.index - maxSerializeBack); + } else { // History.getEntryAtIndex(0, ...) is the oldest. + oldest = 0; + } + + let newest; + let maxSerializeFwd = + Services.prefs.getIntPref("browser.sessionstore.max_serialize_forward"); + if (maxSerializeFwd >= 0) { + newest = Math.min(history.count - 1, history.index + maxSerializeFwd); + } else { // History.getEntryAtIndex(history.count - 1, ...) is the newest. + newest = history.count - 1; + } + + try { + for (let i = oldest; i <= newest; i++) { + let shEntry = history.getEntryAtIndex(i, false); + let entry = this.serializeEntry(shEntry, isPinned); + data.entries.push(entry); + } + } catch (ex) { + // In some cases, getEntryAtIndex will throw. This seems to be due to + // history.count being higher than it should be. By doing this in a + // try-catch, we'll update history to where it breaks, print an error + // message, and still save sessionstore.js. + debug("SessionStore failed gathering complete history " + + "for the focused window/tab. See bug 669196."); + } + + // Set the one-based index of the currently active tab, + // ensuring it isn't out of bounds if an exception was thrown above. + data.index = Math.min(history.index - oldest + 1, data.entries.length); + } + + // If either the session history isn't available yet or doesn't have any + // valid entries, make sure we at least include the current page. + if (data.entries.length == 0) { + let uri = webNavigation.currentURI.spec; + let body = webNavigation.document.body; + // We landed here because the history is inaccessible or there are no + // history entries. In that case we should at least record the docShell's + // current URL as a single history entry. If the URL is not about:blank + // or it's a blank tab that was modified (like a custom newtab page), + // record it. For about:blank we explicitly want an empty array without + // an 'index' property to denote that there are no history entries. + if (uri != "about:blank" || (body && body.hasChildNodes())) { + data.entries.push({ url: uri }); + data.index = 1; + } + } + + return data; + }, + + /** + * Determines whether a given session history entry has been added dynamically. + * + * @param shEntry + * The session history entry. + * @return bool + */ + isDynamic: function (shEntry) { + // shEntry.isDynamicallyAdded() is true for dynamically added + //