/* 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 //