1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/components/sessionstore/src/SessionMigration.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,119 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +this.EXPORTED_SYMBOLS = ["SessionMigration"]; 1.11 + 1.12 +const Cu = Components.utils; 1.13 +Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); 1.14 +Cu.import("resource://gre/modules/Task.jsm", this); 1.15 +Cu.import("resource://gre/modules/osfile.jsm", this); 1.16 + 1.17 +// An encoder to UTF-8. 1.18 +XPCOMUtils.defineLazyGetter(this, "gEncoder", function () { 1.19 + return new TextEncoder(); 1.20 +}); 1.21 + 1.22 +// A decoder. 1.23 +XPCOMUtils.defineLazyGetter(this, "gDecoder", function () { 1.24 + return new TextDecoder(); 1.25 +}); 1.26 + 1.27 +let SessionMigrationInternal = { 1.28 + /** 1.29 + * Convert the original session restore state into a minimal state. It will 1.30 + * only contain: 1.31 + * - open windows 1.32 + * - with tabs 1.33 + * - with history entries with only title, url 1.34 + * - with pinned state 1.35 + * - with tab group info (hidden + group id) 1.36 + * - with selected tab info 1.37 + * - with selected window info 1.38 + * - with tabgroups info 1.39 + * 1.40 + * The complete state is then wrapped into the "about:welcomeback" page as 1.41 + * form field info to be restored when restoring the state. 1.42 + */ 1.43 + convertState: function(aStateObj) { 1.44 + let state = { 1.45 + selectedWindow: aStateObj.selectedWindow, 1.46 + _closedWindows: [] 1.47 + }; 1.48 + state.windows = aStateObj.windows.map(function(oldWin) { 1.49 + var win = {extData: {}}; 1.50 + win.tabs = oldWin.tabs.map(function(oldTab) { 1.51 + var tab = {}; 1.52 + // Keep only titles and urls for history entries 1.53 + tab.entries = oldTab.entries.map(function(entry) { 1.54 + return {url: entry.url, title: entry.title}; 1.55 + }); 1.56 + tab.index = oldTab.index; 1.57 + tab.hidden = oldTab.hidden; 1.58 + tab.pinned = oldTab.pinned; 1.59 + // The tabgroup info is in the extData, so we need to get it out. 1.60 + if (oldTab.extData && "tabview-tab" in oldTab.extData) { 1.61 + tab.extData = {"tabview-tab": oldTab.extData["tabview-tab"]}; 1.62 + } 1.63 + return tab; 1.64 + }); 1.65 + // There are various tabgroup-related attributes that we need to get out 1.66 + // of the session restore data for the window, too. 1.67 + if (oldWin.extData) { 1.68 + for (let k of Object.keys(oldWin.extData)) { 1.69 + if (k.startsWith("tabview-")) { 1.70 + win.extData[k] = oldWin.extData[k]; 1.71 + } 1.72 + } 1.73 + } 1.74 + win.selected = oldWin.selected; 1.75 + win._closedTabs = []; 1.76 + return win; 1.77 + }); 1.78 + let wrappedState = { 1.79 + url: "about:welcomeback", 1.80 + formdata: { 1.81 + id: {"sessionData": state}, 1.82 + xpath: {} 1.83 + } 1.84 + }; 1.85 + return {windows: [{tabs: [{entries: [wrappedState]}]}]}; 1.86 + }, 1.87 + /** 1.88 + * Asynchronously read session restore state (JSON) from a path 1.89 + */ 1.90 + readState: function(aPath) { 1.91 + return Task.spawn(function() { 1.92 + let bytes = yield OS.File.read(aPath); 1.93 + let text = gDecoder.decode(bytes); 1.94 + let state = JSON.parse(text); 1.95 + throw new Task.Result(state); 1.96 + }); 1.97 + }, 1.98 + /** 1.99 + * Asynchronously write session restore state as JSON to a path 1.100 + */ 1.101 + writeState: function(aPath, aState) { 1.102 + let bytes = gEncoder.encode(JSON.stringify(aState)); 1.103 + return OS.File.writeAtomic(aPath, bytes, {tmpPath: aPath + ".tmp"}); 1.104 + } 1.105 +} 1.106 + 1.107 +let SessionMigration = { 1.108 + /** 1.109 + * Migrate a limited set of session data from one path to another. 1.110 + */ 1.111 + migrate: function(aFromPath, aToPath) { 1.112 + return Task.spawn(function() { 1.113 + let inState = yield SessionMigrationInternal.readState(aFromPath); 1.114 + let outState = SessionMigrationInternal.convertState(inState); 1.115 + // Unfortunately, we can't use SessionStore's own SessionFile to 1.116 + // write out the data because it has a dependency on the profile dir 1.117 + // being known. When the migration runs, there is no guarantee that 1.118 + // that's true. 1.119 + yield SessionMigrationInternal.writeState(aToPath, outState); 1.120 + }); 1.121 + } 1.122 +};