diff -r 000000000000 -r 6474c204b198 browser/components/sessionstore/src/SessionMigration.jsm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/browser/components/sessionstore/src/SessionMigration.jsm Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,119 @@ +/* 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 = ["SessionMigration"]; + +const Cu = Components.utils; +Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); +Cu.import("resource://gre/modules/Task.jsm", this); +Cu.import("resource://gre/modules/osfile.jsm", this); + +// An encoder to UTF-8. +XPCOMUtils.defineLazyGetter(this, "gEncoder", function () { + return new TextEncoder(); +}); + +// A decoder. +XPCOMUtils.defineLazyGetter(this, "gDecoder", function () { + return new TextDecoder(); +}); + +let SessionMigrationInternal = { + /** + * Convert the original session restore state into a minimal state. It will + * only contain: + * - open windows + * - with tabs + * - with history entries with only title, url + * - with pinned state + * - with tab group info (hidden + group id) + * - with selected tab info + * - with selected window info + * - with tabgroups info + * + * The complete state is then wrapped into the "about:welcomeback" page as + * form field info to be restored when restoring the state. + */ + convertState: function(aStateObj) { + let state = { + selectedWindow: aStateObj.selectedWindow, + _closedWindows: [] + }; + state.windows = aStateObj.windows.map(function(oldWin) { + var win = {extData: {}}; + win.tabs = oldWin.tabs.map(function(oldTab) { + var tab = {}; + // Keep only titles and urls for history entries + tab.entries = oldTab.entries.map(function(entry) { + return {url: entry.url, title: entry.title}; + }); + tab.index = oldTab.index; + tab.hidden = oldTab.hidden; + tab.pinned = oldTab.pinned; + // The tabgroup info is in the extData, so we need to get it out. + if (oldTab.extData && "tabview-tab" in oldTab.extData) { + tab.extData = {"tabview-tab": oldTab.extData["tabview-tab"]}; + } + return tab; + }); + // There are various tabgroup-related attributes that we need to get out + // of the session restore data for the window, too. + if (oldWin.extData) { + for (let k of Object.keys(oldWin.extData)) { + if (k.startsWith("tabview-")) { + win.extData[k] = oldWin.extData[k]; + } + } + } + win.selected = oldWin.selected; + win._closedTabs = []; + return win; + }); + let wrappedState = { + url: "about:welcomeback", + formdata: { + id: {"sessionData": state}, + xpath: {} + } + }; + return {windows: [{tabs: [{entries: [wrappedState]}]}]}; + }, + /** + * Asynchronously read session restore state (JSON) from a path + */ + readState: function(aPath) { + return Task.spawn(function() { + let bytes = yield OS.File.read(aPath); + let text = gDecoder.decode(bytes); + let state = JSON.parse(text); + throw new Task.Result(state); + }); + }, + /** + * Asynchronously write session restore state as JSON to a path + */ + writeState: function(aPath, aState) { + let bytes = gEncoder.encode(JSON.stringify(aState)); + return OS.File.writeAtomic(aPath, bytes, {tmpPath: aPath + ".tmp"}); + } +} + +let SessionMigration = { + /** + * Migrate a limited set of session data from one path to another. + */ + migrate: function(aFromPath, aToPath) { + return Task.spawn(function() { + let inState = yield SessionMigrationInternal.readState(aFromPath); + let outState = SessionMigrationInternal.convertState(inState); + // Unfortunately, we can't use SessionStore's own SessionFile to + // write out the data because it has a dependency on the profile dir + // being known. When the migration runs, there is no guarantee that + // that's true. + yield SessionMigrationInternal.writeState(aToPath, outState); + }); + } +};