|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 this.EXPORTED_SYMBOLS = ["SessionMigration"]; |
|
8 |
|
9 const Cu = Components.utils; |
|
10 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); |
|
11 Cu.import("resource://gre/modules/Task.jsm", this); |
|
12 Cu.import("resource://gre/modules/osfile.jsm", this); |
|
13 |
|
14 // An encoder to UTF-8. |
|
15 XPCOMUtils.defineLazyGetter(this, "gEncoder", function () { |
|
16 return new TextEncoder(); |
|
17 }); |
|
18 |
|
19 // A decoder. |
|
20 XPCOMUtils.defineLazyGetter(this, "gDecoder", function () { |
|
21 return new TextDecoder(); |
|
22 }); |
|
23 |
|
24 let SessionMigrationInternal = { |
|
25 /** |
|
26 * Convert the original session restore state into a minimal state. It will |
|
27 * only contain: |
|
28 * - open windows |
|
29 * - with tabs |
|
30 * - with history entries with only title, url |
|
31 * - with pinned state |
|
32 * - with tab group info (hidden + group id) |
|
33 * - with selected tab info |
|
34 * - with selected window info |
|
35 * - with tabgroups info |
|
36 * |
|
37 * The complete state is then wrapped into the "about:welcomeback" page as |
|
38 * form field info to be restored when restoring the state. |
|
39 */ |
|
40 convertState: function(aStateObj) { |
|
41 let state = { |
|
42 selectedWindow: aStateObj.selectedWindow, |
|
43 _closedWindows: [] |
|
44 }; |
|
45 state.windows = aStateObj.windows.map(function(oldWin) { |
|
46 var win = {extData: {}}; |
|
47 win.tabs = oldWin.tabs.map(function(oldTab) { |
|
48 var tab = {}; |
|
49 // Keep only titles and urls for history entries |
|
50 tab.entries = oldTab.entries.map(function(entry) { |
|
51 return {url: entry.url, title: entry.title}; |
|
52 }); |
|
53 tab.index = oldTab.index; |
|
54 tab.hidden = oldTab.hidden; |
|
55 tab.pinned = oldTab.pinned; |
|
56 // The tabgroup info is in the extData, so we need to get it out. |
|
57 if (oldTab.extData && "tabview-tab" in oldTab.extData) { |
|
58 tab.extData = {"tabview-tab": oldTab.extData["tabview-tab"]}; |
|
59 } |
|
60 return tab; |
|
61 }); |
|
62 // There are various tabgroup-related attributes that we need to get out |
|
63 // of the session restore data for the window, too. |
|
64 if (oldWin.extData) { |
|
65 for (let k of Object.keys(oldWin.extData)) { |
|
66 if (k.startsWith("tabview-")) { |
|
67 win.extData[k] = oldWin.extData[k]; |
|
68 } |
|
69 } |
|
70 } |
|
71 win.selected = oldWin.selected; |
|
72 win._closedTabs = []; |
|
73 return win; |
|
74 }); |
|
75 let wrappedState = { |
|
76 url: "about:welcomeback", |
|
77 formdata: { |
|
78 id: {"sessionData": state}, |
|
79 xpath: {} |
|
80 } |
|
81 }; |
|
82 return {windows: [{tabs: [{entries: [wrappedState]}]}]}; |
|
83 }, |
|
84 /** |
|
85 * Asynchronously read session restore state (JSON) from a path |
|
86 */ |
|
87 readState: function(aPath) { |
|
88 return Task.spawn(function() { |
|
89 let bytes = yield OS.File.read(aPath); |
|
90 let text = gDecoder.decode(bytes); |
|
91 let state = JSON.parse(text); |
|
92 throw new Task.Result(state); |
|
93 }); |
|
94 }, |
|
95 /** |
|
96 * Asynchronously write session restore state as JSON to a path |
|
97 */ |
|
98 writeState: function(aPath, aState) { |
|
99 let bytes = gEncoder.encode(JSON.stringify(aState)); |
|
100 return OS.File.writeAtomic(aPath, bytes, {tmpPath: aPath + ".tmp"}); |
|
101 } |
|
102 } |
|
103 |
|
104 let SessionMigration = { |
|
105 /** |
|
106 * Migrate a limited set of session data from one path to another. |
|
107 */ |
|
108 migrate: function(aFromPath, aToPath) { |
|
109 return Task.spawn(function() { |
|
110 let inState = yield SessionMigrationInternal.readState(aFromPath); |
|
111 let outState = SessionMigrationInternal.convertState(inState); |
|
112 // Unfortunately, we can't use SessionStore's own SessionFile to |
|
113 // write out the data because it has a dependency on the profile dir |
|
114 // being known. When the migration runs, there is no guarantee that |
|
115 // that's true. |
|
116 yield SessionMigrationInternal.writeState(aToPath, outState); |
|
117 }); |
|
118 } |
|
119 }; |