1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
/* 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);
XPCOMUtils.defineLazyModuleGetter(this, "Utils",
"resource://gre/modules/sessionstore/Utils.jsm");
// An encoder to UTF-8.
XPCOMUtils.defineLazyGetter(this, "gEncoder", function () {
return new TextEncoder();
});
// A decoder.
XPCOMUtils.defineLazyGetter(this, "gDecoder", function () {
return new TextDecoder();
});
var 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, triggeringPrincipal
* - with pinned state
* - with tab group info (hidden + group id)
* - with selected tab info
* - with selected window 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, urls and triggeringPrincipals for history entries
tab.entries = oldTab.entries.map(function(entry) {
return { url: entry.url,
triggeringPrincipal_base64: entry.triggeringPrincipal_base64,
title: entry.title };
});
tab.index = oldTab.index;
tab.hidden = oldTab.hidden;
tab.pinned = oldTab.pinned;
return tab;
});
win.selected = oldWin.selected;
win._closedTabs = [];
return win;
});
let url = "about:welcomeback";
let formdata = {id: {sessionData: state}, url};
let entry = { url, triggeringPrincipal_base64: Utils.SERIALIZED_SYSTEMPRINCIPAL };
return { windows: [{ tabs: [{ entries: [ entry ], formdata}]}]};
},
/**
* 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"});
}
}
var 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);
});
}
};
|