browser/components/migration/src/ChromeProfileMigrator.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * vim: sw=2 ts=2 sts=2 et */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 "use strict";
michael@0 8
michael@0 9 const Cc = Components.classes;
michael@0 10 const Ci = Components.interfaces;
michael@0 11 const Cu = Components.utils;
michael@0 12 const Cr = Components.results;
michael@0 13
michael@0 14 const FILE_INPUT_STREAM_CID = "@mozilla.org/network/file-input-stream;1";
michael@0 15
michael@0 16 const S100NS_FROM1601TO1970 = 0x19DB1DED53E8000;
michael@0 17 const S100NS_PER_MS = 10;
michael@0 18
michael@0 19 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 20 Cu.import("resource://gre/modules/Services.jsm");
michael@0 21 Cu.import("resource://gre/modules/NetUtil.jsm");
michael@0 22 Cu.import("resource://gre/modules/FileUtils.jsm");
michael@0 23 Cu.import("resource:///modules/MigrationUtils.jsm");
michael@0 24
michael@0 25 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
michael@0 26 "resource://gre/modules/PlacesUtils.jsm");
michael@0 27
michael@0 28 /**
michael@0 29 * Convert Chrome time format to Date object
michael@0 30 *
michael@0 31 * @param aTime
michael@0 32 * Chrome time
michael@0 33 * @return converted Date object
michael@0 34 * @note Google Chrome uses FILETIME / 10 as time.
michael@0 35 * FILETIME is based on same structure of Windows.
michael@0 36 */
michael@0 37 function chromeTimeToDate(aTime)
michael@0 38 {
michael@0 39 return new Date((aTime * S100NS_PER_MS - S100NS_FROM1601TO1970 ) / 10000);
michael@0 40 }
michael@0 41
michael@0 42 /**
michael@0 43 * Insert bookmark items into specific folder.
michael@0 44 *
michael@0 45 * @param aFolderId
michael@0 46 * id of folder where items will be inserted
michael@0 47 * @param aItems
michael@0 48 * bookmark items to be inserted
michael@0 49 */
michael@0 50 function insertBookmarkItems(aFolderId, aItems)
michael@0 51 {
michael@0 52 for (let i = 0; i < aItems.length; i++) {
michael@0 53 let item = aItems[i];
michael@0 54
michael@0 55 try {
michael@0 56 if (item.type == "url") {
michael@0 57 PlacesUtils.bookmarks.insertBookmark(aFolderId,
michael@0 58 NetUtil.newURI(item.url),
michael@0 59 PlacesUtils.bookmarks.DEFAULT_INDEX,
michael@0 60 item.name);
michael@0 61 } else if (item.type == "folder") {
michael@0 62 let newFolderId =
michael@0 63 PlacesUtils.bookmarks.createFolder(aFolderId,
michael@0 64 item.name,
michael@0 65 PlacesUtils.bookmarks.DEFAULT_INDEX);
michael@0 66
michael@0 67 insertBookmarkItems(newFolderId, item.children);
michael@0 68 }
michael@0 69 } catch (e) {
michael@0 70 Cu.reportError(e);
michael@0 71 }
michael@0 72 }
michael@0 73 }
michael@0 74
michael@0 75
michael@0 76 function ChromeProfileMigrator() {
michael@0 77 let chromeUserDataFolder = FileUtils.getDir(
michael@0 78 #ifdef XP_WIN
michael@0 79 "LocalAppData", ["Google", "Chrome", "User Data"]
michael@0 80 #elifdef XP_MACOSX
michael@0 81 "ULibDir", ["Application Support", "Google", "Chrome"]
michael@0 82 #else
michael@0 83 "Home", [".config", "google-chrome"]
michael@0 84 #endif
michael@0 85 , false);
michael@0 86 this._chromeUserDataFolder = chromeUserDataFolder.exists() ?
michael@0 87 chromeUserDataFolder : null;
michael@0 88 }
michael@0 89
michael@0 90 ChromeProfileMigrator.prototype = Object.create(MigratorPrototype);
michael@0 91
michael@0 92 ChromeProfileMigrator.prototype.getResources =
michael@0 93 function Chrome_getResources(aProfile) {
michael@0 94 if (this._chromeUserDataFolder) {
michael@0 95 let profileFolder = this._chromeUserDataFolder.clone();
michael@0 96 profileFolder.append(aProfile);
michael@0 97 if (profileFolder.exists()) {
michael@0 98 let possibleResources = [GetBookmarksResource(profileFolder),
michael@0 99 GetHistoryResource(profileFolder),
michael@0 100 GetCookiesResource(profileFolder)];
michael@0 101 return [r for each (r in possibleResources) if (r != null)];
michael@0 102 }
michael@0 103 }
michael@0 104 return [];
michael@0 105 };
michael@0 106
michael@0 107 Object.defineProperty(ChromeProfileMigrator.prototype, "sourceProfiles", {
michael@0 108 get: function Chrome_sourceProfiles() {
michael@0 109 if ("__sourceProfiles" in this)
michael@0 110 return this.__sourceProfiles;
michael@0 111
michael@0 112 if (!this._chromeUserDataFolder)
michael@0 113 return [];
michael@0 114
michael@0 115 let profiles;
michael@0 116 try {
michael@0 117 // Local State is a JSON file that contains profile info.
michael@0 118 let localState = this._chromeUserDataFolder.clone();
michael@0 119 localState.append("Local State");
michael@0 120 if (!localState.exists())
michael@0 121 throw new Error("Chrome's 'Local State' file does not exist.");
michael@0 122 if (!localState.isReadable())
michael@0 123 throw new Error("Chrome's 'Local State' file could not be read.");
michael@0 124
michael@0 125 let fstream = Cc[FILE_INPUT_STREAM_CID].createInstance(Ci.nsIFileInputStream);
michael@0 126 fstream.init(localState, -1, 0, 0);
michael@0 127 let inputStream = NetUtil.readInputStreamToString(fstream, fstream.available(),
michael@0 128 { charset: "UTF-8" });
michael@0 129 let info_cache = JSON.parse(inputStream).profile.info_cache;
michael@0 130 if (info_cache)
michael@0 131 profiles = Object.keys(info_cache);
michael@0 132 } catch (e) {
michael@0 133 Cu.reportError("Error detecting Chrome profiles: " + e);
michael@0 134 // If we weren't able to detect any profiles above, fallback to the Default profile.
michael@0 135 let defaultProfileFolder = this._chromeUserDataFolder.clone();
michael@0 136 defaultProfileFolder.append("Default");
michael@0 137 if (defaultProfileFolder.exists())
michael@0 138 profiles = ["Default"];
michael@0 139 }
michael@0 140
michael@0 141 // Only list profiles from which any data can be imported
michael@0 142 return this.__sourceProfiles = profiles.filter(function(profileName) {
michael@0 143 let resources = this.getResources(profileName);
michael@0 144 return resources && resources.length > 0;
michael@0 145 }, this);
michael@0 146 }
michael@0 147 });
michael@0 148
michael@0 149 Object.defineProperty(ChromeProfileMigrator.prototype, "sourceHomePageURL", {
michael@0 150 get: function Chrome_sourceHomePageURL() {
michael@0 151 let prefsFile = this._chromeUserDataFolder.clone();
michael@0 152 prefsFile.append("Preferences");
michael@0 153 if (prefsFile.exists()) {
michael@0 154 // XXX reading and parsing JSON is synchronous.
michael@0 155 let fstream = Cc[FILE_INPUT_STREAM_CID].
michael@0 156 createInstance(Ci.nsIFileInputStream);
michael@0 157 fstream.init(file, -1, 0, 0);
michael@0 158 try {
michael@0 159 return JSON.parse(
michael@0 160 NetUtil.readInputStreamToString(fstream, fstream.available(),
michael@0 161 { charset: "UTF-8" })
michael@0 162 ).homepage;
michael@0 163 }
michael@0 164 catch(e) {
michael@0 165 Cu.reportError("Error parsing Chrome's preferences file: " + e);
michael@0 166 }
michael@0 167 }
michael@0 168 return "";
michael@0 169 }
michael@0 170 });
michael@0 171
michael@0 172 function GetBookmarksResource(aProfileFolder) {
michael@0 173 let bookmarksFile = aProfileFolder.clone();
michael@0 174 bookmarksFile.append("Bookmarks");
michael@0 175 if (!bookmarksFile.exists())
michael@0 176 return null;
michael@0 177
michael@0 178 return {
michael@0 179 type: MigrationUtils.resourceTypes.BOOKMARKS,
michael@0 180
michael@0 181 migrate: function(aCallback) {
michael@0 182 NetUtil.asyncFetch(bookmarksFile, MigrationUtils.wrapMigrateFunction(
michael@0 183 function(aInputStream, aResultCode) {
michael@0 184 if (!Components.isSuccessCode(aResultCode))
michael@0 185 throw new Error("Could not read Bookmarks file");
michael@0 186
michael@0 187 // Parse Chrome bookmark file that is JSON format
michael@0 188 let bookmarkJSON = NetUtil.readInputStreamToString(
michael@0 189 aInputStream, aInputStream.available(), { charset : "UTF-8" });
michael@0 190 let roots = JSON.parse(bookmarkJSON).roots;
michael@0 191 PlacesUtils.bookmarks.runInBatchMode({
michael@0 192 runBatched: function() {
michael@0 193 // Importing bookmark bar items
michael@0 194 if (roots.bookmark_bar.children &&
michael@0 195 roots.bookmark_bar.children.length > 0) {
michael@0 196 // Toolbar
michael@0 197 let parentId = PlacesUtils.toolbarFolderId;
michael@0 198 if (!MigrationUtils.isStartupMigration) {
michael@0 199 parentId = MigrationUtils.createImportedBookmarksFolder(
michael@0 200 "Chrome", parentId);
michael@0 201 }
michael@0 202 insertBookmarkItems(parentId, roots.bookmark_bar.children);
michael@0 203 }
michael@0 204
michael@0 205 // Importing bookmark menu items
michael@0 206 if (roots.other.children &&
michael@0 207 roots.other.children.length > 0) {
michael@0 208 // Bookmark menu
michael@0 209 let parentId = PlacesUtils.bookmarksMenuFolderId;
michael@0 210 if (!MigrationUtils.isStartupMigration) {
michael@0 211 parentId = MigrationUtils.createImportedBookmarksFolder(
michael@0 212 "Chrome", parentId);
michael@0 213 }
michael@0 214 insertBookmarkItems(parentId, roots.other.children);
michael@0 215 }
michael@0 216 }
michael@0 217 }, null);
michael@0 218 }, aCallback));
michael@0 219 }
michael@0 220 };
michael@0 221 }
michael@0 222
michael@0 223 function GetHistoryResource(aProfileFolder) {
michael@0 224 let historyFile = aProfileFolder.clone();
michael@0 225 historyFile.append("History");
michael@0 226 if (!historyFile.exists())
michael@0 227 return null;
michael@0 228
michael@0 229 return {
michael@0 230 type: MigrationUtils.resourceTypes.HISTORY,
michael@0 231
michael@0 232 migrate: function(aCallback) {
michael@0 233 let dbConn = Services.storage.openUnsharedDatabase(historyFile);
michael@0 234 let stmt = dbConn.createAsyncStatement(
michael@0 235 "SELECT url, title, last_visit_time, typed_count FROM urls WHERE hidden = 0");
michael@0 236
michael@0 237 stmt.executeAsync({
michael@0 238 handleResult : function(aResults) {
michael@0 239 let places = [];
michael@0 240 for (let row = aResults.getNextRow(); row; row = aResults.getNextRow()) {
michael@0 241 try {
michael@0 242 // if having typed_count, we changes transition type to typed.
michael@0 243 let transType = PlacesUtils.history.TRANSITION_LINK;
michael@0 244 if (row.getResultByName("typed_count") > 0)
michael@0 245 transType = PlacesUtils.history.TRANSITION_TYPED;
michael@0 246
michael@0 247 places.push({
michael@0 248 uri: NetUtil.newURI(row.getResultByName("url")),
michael@0 249 title: row.getResultByName("title"),
michael@0 250 visits: [{
michael@0 251 transitionType: transType,
michael@0 252 visitDate: chromeTimeToDate(
michael@0 253 row.getResultByName(
michael@0 254 "last_visit_time")) * 1000,
michael@0 255 }],
michael@0 256 });
michael@0 257 } catch (e) {
michael@0 258 Cu.reportError(e);
michael@0 259 }
michael@0 260 }
michael@0 261
michael@0 262 try {
michael@0 263 PlacesUtils.asyncHistory.updatePlaces(places);
michael@0 264 } catch (e) {
michael@0 265 Cu.reportError(e);
michael@0 266 }
michael@0 267 },
michael@0 268
michael@0 269 handleError : function(aError) {
michael@0 270 Cu.reportError("Async statement execution returned with '" +
michael@0 271 aError.result + "', '" + aError.message + "'");
michael@0 272 },
michael@0 273
michael@0 274 handleCompletion : function(aReason) {
michael@0 275 dbConn.asyncClose();
michael@0 276 aCallback(aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED);
michael@0 277 }
michael@0 278 });
michael@0 279 stmt.finalize();
michael@0 280 }
michael@0 281 };
michael@0 282 }
michael@0 283
michael@0 284 function GetCookiesResource(aProfileFolder) {
michael@0 285 let cookiesFile = aProfileFolder.clone();
michael@0 286 cookiesFile.append("Cookies");
michael@0 287 if (!cookiesFile.exists())
michael@0 288 return null;
michael@0 289
michael@0 290 return {
michael@0 291 type: MigrationUtils.resourceTypes.COOKIES,
michael@0 292
michael@0 293 migrate: function(aCallback) {
michael@0 294 let dbConn = Services.storage.openUnsharedDatabase(cookiesFile);
michael@0 295 let stmt = dbConn.createAsyncStatement(
michael@0 296 "SELECT host_key, path, name, value, secure, httponly, expires_utc FROM cookies");
michael@0 297
michael@0 298 stmt.executeAsync({
michael@0 299 handleResult : function(aResults) {
michael@0 300 for (let row = aResults.getNextRow(); row; row = aResults.getNextRow()) {
michael@0 301 let host_key = row.getResultByName("host_key");
michael@0 302 if (host_key.match(/^\./)) {
michael@0 303 // 1st character of host_key may be ".", so we have to remove it
michael@0 304 host_key = host_key.substr(1);
michael@0 305 }
michael@0 306
michael@0 307 try {
michael@0 308 let expiresUtc =
michael@0 309 chromeTimeToDate(row.getResultByName("expires_utc")) / 1000;
michael@0 310 Services.cookies.add(host_key,
michael@0 311 row.getResultByName("path"),
michael@0 312 row.getResultByName("name"),
michael@0 313 row.getResultByName("value"),
michael@0 314 row.getResultByName("secure"),
michael@0 315 row.getResultByName("httponly"),
michael@0 316 false,
michael@0 317 parseInt(expiresUtc));
michael@0 318 } catch (e) {
michael@0 319 Cu.reportError(e);
michael@0 320 }
michael@0 321 }
michael@0 322 },
michael@0 323
michael@0 324 handleError : function(aError) {
michael@0 325 Cu.reportError("Async statement execution returned with '" +
michael@0 326 aError.result + "', '" + aError.message + "'");
michael@0 327 },
michael@0 328
michael@0 329 handleCompletion : function(aReason) {
michael@0 330 dbConn.asyncClose();
michael@0 331 aCallback(aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED);
michael@0 332 },
michael@0 333 });
michael@0 334 stmt.finalize();
michael@0 335 }
michael@0 336 }
michael@0 337 }
michael@0 338
michael@0 339 ChromeProfileMigrator.prototype.classDescription = "Chrome Profile Migrator";
michael@0 340 ChromeProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chrome";
michael@0 341 ChromeProfileMigrator.prototype.classID = Components.ID("{4cec1de4-1671-4fc3-a53e-6c539dc77a26}");
michael@0 342
michael@0 343 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ChromeProfileMigrator]);

mercurial