new file mode 100644
--- /dev/null
+++ b/browser/components/migration/ChromeMigrationUtils.jsm
@@ -0,0 +1,288 @@
+/* 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 = ["ChromeMigrationUtils"];
+
+const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
+const FILE_INPUT_STREAM_CID = "@mozilla.org/network/file-input-stream;1";
+
+Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+this.ChromeMigrationUtils = {
+ _chromeUserDataPath: null,
+
+ _extensionVersionDirectoryNames: {},
+
+ // The cache for the locale strings.
+ // For example, the data could be:
+ // {
+ // "profile-id-1": {
+ // "extension-id-1": {
+ // "name": {
+ // "message": "Fake App 1"
+ // }
+ // },
+ // }
+ _extensionLocaleStrings: {},
+
+ /**
+ * Get all extensions installed in a specific profile.
+ * @param {String} profileId - A Chrome user profile ID. For example, "Profile 1".
+ * @returns {Array} All installed Chrome extensions information.
+ */
+ async getExtensionList(profileId = this.getLastUsedProfileId()) {
+ let path = this.getExtensionPath(profileId);
+ let iterator = new OS.File.DirectoryIterator(path);
+ let extensionList = [];
+ await iterator.forEach(async entry => {
+ if (entry.isDir) {
+ let extensionInformation = await this.getExtensionInformation(entry.name, profileId);
+ if (extensionInformation) {
+ extensionList.push(extensionInformation);
+ }
+ }
+ }).catch(ex => Cu.reportError(ex));
+ return extensionList;
+ },
+
+ /**
+ * Get information of a specific Chrome extension.
+ * @param {String} extensionId - The extension ID.
+ * @param {String} profileId - The user profile's ID.
+ * @retruns {Object} The Chrome extension information.
+ */
+ async getExtensionInformation(extensionId, profileId = this.getLastUsedProfileId()) {
+ let extensionInformation = null;
+ try {
+ let manifestPath = this.getExtensionPath(profileId);
+ manifestPath = OS.Path.join(manifestPath, extensionId);
+ // If there are multiple sub-directories in the extension directory,
+ // read the files in the latest directory.
+ let directories = await this._getSortedByVersionSubDirectoryNames(manifestPath);
+ if (!directories[0]) {
+ return null;
+ }
+
+ manifestPath = OS.Path.join(manifestPath, directories[0], "manifest.json");
+ let manifest = await OS.File.read(manifestPath, { encoding: "utf-8" });
+ manifest = JSON.parse(manifest);
+ // No app attribute means this is a Chrome extension not a Chrome app.
+ if (!manifest.app) {
+ const DEFAULT_LOCALE = manifest.default_locale;
+ let name = await this._getLocaleString(manifest.name, DEFAULT_LOCALE, extensionId, profileId);
+ let description = await this._getLocaleString(manifest.description, DEFAULT_LOCALE, extensionId, profileId);
+ if (name) {
+ extensionInformation = {
+ id: extensionId,
+ name,
+ description,
+ };
+ } else {
+ throw new Error("Cannot read the Chrome extension's name property.");
+ }
+ }
+ } catch (ex) {
+ Cu.reportError(ex);
+ }
+ return extensionInformation;
+ },
+
+ /**
+ * Get the manifest's locale string.
+ * @param {String} key - The key of a locale string, for example __MSG_name__.
+ * @param {String} locale - The specific language of locale string.
+ * @param {String} extensionId - The extension ID.
+ * @param {String} profileId - The user profile's ID.
+ * @retruns {String} The locale string.
+ */
+ async _getLocaleString(key, locale, extensionId, profileId) {
+ // Return the key string if it is not a locale key.
+ // The key string starts with "__MSG_" and ends with "__".
+ // For example, "__MSG_name__".
+ // https://developer.chrome.com/apps/i18n
+ if (!key.startsWith("__MSG_") || !key.endsWith("__")) {
+ return key;
+ }
+
+ let localeString = null;
+ try {
+ let localeFile;
+ if (this._extensionLocaleStrings[profileId] &&
+ this._extensionLocaleStrings[profileId][extensionId]) {
+ localeFile = this._extensionLocaleStrings[profileId][extensionId];
+ } else {
+ if (!this._extensionLocaleStrings[profileId]) {
+ this._extensionLocaleStrings[profileId] = {};
+ }
+ let localeFilePath = this.getExtensionPath(profileId);
+ localeFilePath = OS.Path.join(localeFilePath, extensionId);
+ let directories = await this._getSortedByVersionSubDirectoryNames(localeFilePath);
+ // If there are multiple sub-directories in the extension directory,
+ // read the files in the latest directory.
+ localeFilePath = OS.Path.join(localeFilePath, directories[0], "_locales", locale, "messages.json");
+ localeFile = await OS.File.read(localeFilePath, { encoding: "utf-8" });
+ localeFile = JSON.parse(localeFile);
+ this._extensionLocaleStrings[profileId][extensionId] = localeFile;
+ }
+ const PREFIX_LENGTH = 6;
+ const SUFFIX_LENGTH = 2;
+ // Get the locale key from the string with locale prefix and suffix.
+ // For example, it will get the "name" sub-string from the "__MSG_name__" string.
+ key = key.substring(PREFIX_LENGTH, key.length - SUFFIX_LENGTH);
+ if (localeFile[key] && localeFile[key].message) {
+ localeString = localeFile[key].message;
+ }
+ } catch (ex) {
+ Cu.reportError(ex);
+ }
+ return localeString;
+ },
+
+ /**
+ * Check that a specific extension is installed or not.
+ * @param {String} extensionId - The extension ID.
+ * @param {String} profileId - The user profile's ID.
+ * @returns {Boolean} Return true if the extension is installed otherwise return false.
+ */
+ async isExtensionInstalled(extensionId, profileId = this.getLastUsedProfileId()) {
+ let extensionPath = this.getExtensionPath(profileId);
+ let isInstalled = await OS.File.exists(OS.Path.join(extensionPath, extensionId));
+ return isInstalled;
+ },
+
+ /**
+ * Get the last used user profile's ID.
+ * @returns {String} The last used user profile's ID.
+ */
+ getLastUsedProfileId() {
+ let localState = this.getLocalState();
+ return localState ? localState.profile.last_used : "Default";
+ },
+
+ /**
+ * Get the local state file content.
+ * @returns {Object} The JSON-based content.
+ */
+ getLocalState() {
+ let localStateFile = new FileUtils.File(this.getChromeUserDataPath());
+ localStateFile.append("Local State");
+ if (!localStateFile.exists())
+ throw new Error("Chrome's 'Local State' file does not exist.");
+ if (!localStateFile.isReadable())
+ throw new Error("Chrome's 'Local State' file could not be read.");
+
+ let localState = null;
+ try {
+ let fstream = Cc[FILE_INPUT_STREAM_CID].createInstance(Ci.nsIFileInputStream);
+ fstream.init(localStateFile, -1, 0, 0);
+ let inputStream = NetUtil.readInputStreamToString(fstream, fstream.available(),
+ { charset: "UTF-8" });
+ localState = JSON.parse(inputStream);
+ } catch (ex) {
+ Cu.reportError(ex);
+ throw ex;
+ }
+ return localState;
+ },
+
+ /**
+ * Get the path of Chrome extension directory.
+ * @param {String} profileId - The user profile's ID.
+ * @returns {String} The path of Chrome extension directory.
+ */
+ getExtensionPath(profileId) {
+ return OS.Path.join(this.getChromeUserDataPath(), profileId, "Extensions");
+ },
+
+ /**
+ * Get the path of the Chrome user data directory.
+ * @returns {String} The path of the Chrome user data directory.
+ */
+ getChromeUserDataPath() {
+ if (!this._chromeUserDataPath) {
+ this._chromeUserDataPath = this.getDataPath("Chrome");
+ }
+ return this._chromeUserDataPath;
+ },
+
+ /**
+ * Get the path of an application data directory.
+ * @param {String} chromeProjectName - The Chrome project name, e.g. "Chrome", "Chromium" or "Canary".
+ * @returns {String} The path of application data directory.
+ */
+ getDataPath(chromeProjectName) {
+ const SUB_DIRECTORIES = {
+ win: {
+ Chrome: ["Google", "Chrome"],
+ Chromium: ["Chromium"],
+ Canary: ["Google", "Chrome SxS"],
+ },
+ macosx: {
+ Chrome: ["Google", "Chrome"],
+ Chromium: ["Chromium"],
+ Canary: ["Google", "Chrome Canary"],
+ },
+ linux: {
+ Chrome: ["google-chrome"],
+ Chromium: ["chromium"],
+ // Canary is not available on Linux.
+ },
+ };
+ let dirKey, subfolders;
+ subfolders = SUB_DIRECTORIES[AppConstants.platform][chromeProjectName];
+ if (!subfolders) {
+ return null;
+ }
+
+ if (AppConstants.platform == "win") {
+ dirKey = "winLocalAppDataDir";
+ subfolders = subfolders.concat(["User Data"]);
+ } else if (AppConstants.platform == "macosx") {
+ dirKey = "macUserLibDir";
+ subfolders = ["Application Support"].concat(subfolders);
+ } else {
+ dirKey = "homeDir";
+ subfolders = [".config"].concat(subfolders);
+ }
+ subfolders.unshift(OS.Constants.Path[dirKey]);
+ return OS.Path.join(...subfolders);
+ },
+
+ /**
+ * Get the directory objects sorted by version number.
+ * @param {String} path - The path to the extension directory.
+ * otherwise return all file/directory object.
+ * @returns {Array} The file/directory object array.
+ */
+ async _getSortedByVersionSubDirectoryNames(path) {
+ if (this._extensionVersionDirectoryNames[path]) {
+ return this._extensionVersionDirectoryNames[path];
+ }
+
+ let iterator = new OS.File.DirectoryIterator(path);
+ let entries = [];
+ await iterator.forEach(async entry => {
+ if (entry.isDir) {
+ entries.push(entry.name);
+ }
+ }).catch(ex => {
+ Cu.reportError(ex);
+ entries = [];
+ });
+ // The directory name is the version number string of the extension.
+ // For example, it could be "1.0_0", "1.0_1", "1.0_2", 1.1_0, 1.1_1, or 1.1_2.
+ // The "1.0_1" strings mean that the "1.0_0" directory is existed and you install the version 1.0 again.
+ // https://chromium.googlesource.com/chromium/src/+/0b58a813992b539a6b555ad7959adfad744b095a/chrome/common/extensions/extension_file_util_unittest.cc
+ entries.sort((a, b) => Services.vc.compare(b, a));
+
+ this._extensionVersionDirectoryNames[path] = entries;
+ return entries;
+ },
+};
--- a/browser/components/migration/ChromeProfileMigrator.js
+++ b/browser/components/migration/ChromeProfileMigrator.js
@@ -15,49 +15,28 @@ const S100NS_PER_MS = 10;
const AUTH_TYPE = {
SCHEME_HTML: 0,
SCHEME_BASIC: 1,
SCHEME_DIGEST: 2
};
Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource:///modules/ChromeMigrationUtils.jsm");
Cu.import("resource:///modules/MigrationUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
"resource://gre/modules/OSCrypto.jsm");
-/**
- * Get an nsIFile instance representing the expected location of user data
- * for this copy of Chrome/Chromium/Canary on different OSes.
- * @param subfoldersWin {Array} an array of subfolders to use for Windows
- * @param subfoldersOSX {Array} an array of subfolders to use for OS X
- * @param subfoldersUnix {Array} an array of subfolders to use for *nix systems
- * @returns {nsIFile} the place we expect data to live. Might not actually exist!
- */
-function getDataFolder(subfoldersWin, subfoldersOSX, subfoldersUnix) {
- let dirServiceID, subfolders;
- if (AppConstants.platform == "win") {
- dirServiceID = "LocalAppData";
- subfolders = subfoldersWin.concat(["User Data"]);
- } else if (AppConstants.platform == "macosx") {
- dirServiceID = "ULibDir";
- subfolders = ["Application Support"].concat(subfoldersOSX);
- } else {
- dirServiceID = "Home";
- subfolders = [".config"].concat(subfoldersUnix);
- }
- return FileUtils.getDir(dirServiceID, subfolders, false);
-}
/**
* Convert Chrome time format to Date object
*
* @param aTime
* Chrome time
* @return converted Date object
* @note Google Chrome uses FILETIME / 10 as time.
@@ -108,18 +87,18 @@ function convertBookmarks(items, errorAc
Cu.reportError(ex);
errorAccumulator(ex);
}
}
return itemsToInsert;
}
function ChromeProfileMigrator() {
- let chromeUserDataFolder =
- getDataFolder(["Google", "Chrome"], ["Google", "Chrome"], ["google-chrome"]);
+ let path = ChromeMigrationUtils.getDataPath("Chrome");
+ let chromeUserDataFolder = new FileUtils.File(path);
this._chromeUserDataFolder = chromeUserDataFolder.exists() ?
chromeUserDataFolder : null;
}
ChromeProfileMigrator.prototype = Object.create(MigratorPrototype);
ChromeProfileMigrator.prototype.getResources =
function Chrome_getResources(aProfile) {
@@ -166,29 +145,17 @@ Object.defineProperty(ChromeProfileMigra
if ("__sourceProfiles" in this)
return this.__sourceProfiles;
if (!this._chromeUserDataFolder)
return [];
let profiles = [];
try {
- // Local State is a JSON file that contains profile info.
- let localState = this._chromeUserDataFolder.clone();
- localState.append("Local State");
- if (!localState.exists())
- throw new Error("Chrome's 'Local State' file does not exist.");
- if (!localState.isReadable())
- throw new Error("Chrome's 'Local State' file could not be read.");
-
- let fstream = Cc[FILE_INPUT_STREAM_CID].createInstance(Ci.nsIFileInputStream);
- fstream.init(localState, -1, 0, 0);
- let inputStream = NetUtil.readInputStreamToString(fstream, fstream.available(),
- { charset: "UTF-8" });
- let info_cache = JSON.parse(inputStream).profile.info_cache;
+ let info_cache = ChromeMigrationUtils.getLocalState().profile.info_cache;
for (let profileFolderName in info_cache) {
let profileFolder = this._chromeUserDataFolder.clone();
profileFolder.append(profileFolderName);
profiles.push({
id: profileFolderName,
name: info_cache[profileFolderName].name || profileFolderName,
});
}
@@ -498,33 +465,35 @@ ChromeProfileMigrator.prototype.classDes
ChromeProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chrome";
ChromeProfileMigrator.prototype.classID = Components.ID("{4cec1de4-1671-4fc3-a53e-6c539dc77a26}");
/**
* Chromium migration
**/
function ChromiumProfileMigrator() {
- let chromiumUserDataFolder = getDataFolder(["Chromium"], ["Chromium"], ["chromium"]);
+ let path = ChromeMigrationUtils.getDataPath("Chromium");
+ let chromiumUserDataFolder = new FileUtils.File(path);
this._chromeUserDataFolder = chromiumUserDataFolder.exists() ? chromiumUserDataFolder : null;
}
ChromiumProfileMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
ChromiumProfileMigrator.prototype.classDescription = "Chromium Profile Migrator";
ChromiumProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chromium";
ChromiumProfileMigrator.prototype.classID = Components.ID("{8cece922-9720-42de-b7db-7cef88cb07ca}");
var componentsArray = [ChromeProfileMigrator, ChromiumProfileMigrator];
/**
* Chrome Canary
* Not available on Linux
**/
function CanaryProfileMigrator() {
- let chromeUserDataFolder = getDataFolder(["Google", "Chrome SxS"], ["Google", "Chrome Canary"]);
+ let path = ChromeMigrationUtils.getDataPath("Canary");
+ let chromeUserDataFolder = new FileUtils.File(path);
this._chromeUserDataFolder = chromeUserDataFolder.exists() ? chromeUserDataFolder : null;
}
CanaryProfileMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
CanaryProfileMigrator.prototype.classDescription = "Chrome Canary Profile Migrator";
CanaryProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=canary";
CanaryProfileMigrator.prototype.classID = Components.ID("{4bf85aa5-4e21-46ca-825f-f9c51a5e8c76}");
if (AppConstants.platform == "win" || AppConstants.platform == "macosx") {
--- a/browser/components/migration/moz.build
+++ b/browser/components/migration/moz.build
@@ -25,16 +25,17 @@ EXTRA_COMPONENTS += [
]
EXTRA_PP_COMPONENTS += [
'BrowserProfileMigrators.manifest',
]
EXTRA_JS_MODULES += [
'AutoMigrate.jsm',
+ 'ChromeMigrationUtils.jsm',
'MigrationUtils.jsm',
]
if CONFIG['OS_ARCH'] == 'WINNT':
SOURCES += [
'nsIEHistoryEnumerator.cpp',
]
EXTRA_COMPONENTS += [
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/Library/Application Support/Google/Chrome/Default/Extensions/fake-app-1/1.0_0/_locales/en_US/messages.json
@@ -0,0 +1,9 @@
+{
+ "description": {
+ "description": "Extension description in manifest. Should not exceed 132 characters.",
+ "message": "It is the description of fake app 1."
+ },
+ "name": {
+ "message": "Fake App 1"
+ }
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/Library/Application Support/Google/Chrome/Default/Extensions/fake-app-1/1.0_0/manifest.json
@@ -0,0 +1,10 @@
+{
+ "app": {
+ "launch": {
+ "local_path": "main.html"
+ }
+ },
+ "default_locale": "en_US",
+ "description": "__MSG_description__",
+ "name": "__MSG_name__"
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/Library/Application Support/Google/Chrome/Default/Extensions/fake-extension-1/1.0_0/_locales/en_US/messages.json
@@ -0,0 +1,9 @@
+{
+ "description": {
+ "description": "Extension description in manifest. Should not exceed 132 characters.",
+ "message": "It is the description of fake extension 1."
+ },
+ "name": {
+ "message": "Fake Extension 1"
+ }
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/Library/Application Support/Google/Chrome/Default/Extensions/fake-extension-1/1.0_0/manifest.json
@@ -0,0 +1,5 @@
+{
+ "default_locale": "en_US",
+ "description": "__MSG_description__",
+ "name": "__MSG_name__"
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/Library/Application Support/Google/Chrome/Default/Extensions/fake-extension-2/1.0_0/manifest.json
@@ -0,0 +1,5 @@
+{
+ "default_locale": "en_US",
+ "description": "It is the description of fake extension 2.",
+ "name": "Fake Extension 2"
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/test_ChromeMigrationUtils.js
@@ -0,0 +1,49 @@
+"use strict";
+
+var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
+
+Cu.import("resource:///modules/ChromeMigrationUtils.jsm");
+
+// Setup chrome user data path for all platforms.
+ChromeMigrationUtils.getChromeUserDataPath = () => {
+ return do_get_file("Library/Application Support/Google/Chrome/").path;
+};
+
+add_task(async function test_getExtensionList_function() {
+ let extensionList = await ChromeMigrationUtils.getExtensionList("Default");
+ Assert.equal(extensionList.length, 2, "There should be 2 extensions installed.");
+ Assert.deepEqual(extensionList.find(extension => extension.id == "fake-extension-1"), {
+ id: "fake-extension-1",
+ name: "Fake Extension 1",
+ description: "It is the description of fake extension 1.",
+ }, "First extension should match expectations.");
+ Assert.deepEqual(extensionList.find(extension => extension.id == "fake-extension-2"), {
+ id: "fake-extension-2",
+ name: "Fake Extension 2",
+ description: "It is the description of fake extension 2.",
+ }, "Second extension should match expectations.");
+});
+
+add_task(async function test_getExtensionInformation_function() {
+ let extension = await ChromeMigrationUtils.getExtensionInformation("fake-extension-1", "Default");
+ Assert.deepEqual(extension, {
+ id: "fake-extension-1",
+ name: "Fake Extension 1",
+ description: "It is the description of fake extension 1.",
+ }, "Should get the extension information correctly.");
+});
+
+add_task(async function test_getLocaleString_function() {
+ let name = await ChromeMigrationUtils._getLocaleString("__MSG_name__", "en_US", "fake-extension-1", "Default");
+ Assert.deepEqual(name, "Fake Extension 1", "The value of __MSG_name__ locale key is Fake Extension 1.");
+});
+
+add_task(async function test_isExtensionInstalled_function() {
+ let isInstalled = await ChromeMigrationUtils.isExtensionInstalled("fake-extension-1", "Default");
+ Assert.ok(isInstalled, "The fake-extension-1 extension should be installed.");
+});
+
+add_task(async function test_getLastUsedProfileId_function() {
+ let profileId = ChromeMigrationUtils.getLastUsedProfileId();
+ Assert.equal(profileId, "Default", "The last used profile ID should be Default.");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/test_ChromeMigrationUtils_path.js
@@ -0,0 +1,81 @@
+"use strict";
+
+var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource:///modules/ChromeMigrationUtils.jsm");
+Cu.import("resource://gre/modules/osfile.jsm");
+
+function getRootPath() {
+ let dirKey;
+ if (AppConstants.platform == "win") {
+ dirKey = "winLocalAppDataDir";
+ } else if (AppConstants.platform == "macosx") {
+ dirKey = "macUserLibDir";
+ } else {
+ dirKey = "homeDir";
+ }
+ return OS.Constants.Path[dirKey];
+}
+
+add_task(async function test_getDataPath_function() {
+ let chromeUserDataPath = ChromeMigrationUtils.getDataPath("Chrome");
+ let chromiumUserDataPath = ChromeMigrationUtils.getDataPath("Chromium");
+ let canaryUserDataPath = ChromeMigrationUtils.getDataPath("Canary");
+ if (AppConstants.platform == "win") {
+ Assert.equal(chromeUserDataPath,
+ OS.Path.join(getRootPath(), "Google", "Chrome", "User Data"),
+ "Should get the path of Chrome data directory.");
+ Assert.equal(chromiumUserDataPath,
+ OS.Path.join(getRootPath(), "Chromium", "User Data"),
+ "Should get the path of Chromium data directory.");
+ Assert.equal(canaryUserDataPath,
+ OS.Path.join(getRootPath(), "Google", "Chrome SxS", "User Data"),
+ "Should get the path of Canary data directory.");
+ } else if (AppConstants.platform == "macosx") {
+ Assert.equal(chromeUserDataPath,
+ OS.Path.join(getRootPath(), "Application Support", "Google", "Chrome"),
+ "Should get the path of Chrome data directory.");
+ Assert.equal(chromiumUserDataPath,
+ OS.Path.join(getRootPath(), "Application Support", "Chromium"),
+ "Should get the path of Chromium data directory.");
+ Assert.equal(canaryUserDataPath,
+ OS.Path.join(getRootPath(), "Application Support", "Google", "Chrome Canary"),
+ "Should get the path of Canary data directory.");
+ } else {
+ Assert.equal(chromeUserDataPath,
+ OS.Path.join(getRootPath(), ".config", "google-chrome"),
+ "Should get the path of Chrome data directory.");
+ Assert.equal(chromiumUserDataPath,
+ OS.Path.join(getRootPath(), ".config", "chromium"),
+ "Should get the path of Chromium data directory.");
+ Assert.equal(canaryUserDataPath, null,
+ "Should get null for Canary.");
+ }
+});
+
+add_task(async function test_getChromeUserDataPath_function() {
+ let chromeUserDataPath = ChromeMigrationUtils.getChromeUserDataPath();
+ let expectedPath;
+ if (AppConstants.platform == "win") {
+ expectedPath = OS.Path.join(getRootPath(), "Google", "Chrome", "User Data");
+ } else if (AppConstants.platform == "macosx") {
+ expectedPath = OS.Path.join(getRootPath(), "Application Support", "Google", "Chrome");
+ } else {
+ expectedPath = OS.Path.join(getRootPath(), ".config", "google-chrome");
+ }
+ Assert.equal(chromeUserDataPath, expectedPath, "Should get the path of Chrome user data directory.");
+});
+
+add_task(async function test_getExtensionPath_function() {
+ let extensionPath = ChromeMigrationUtils.getExtensionPath("Default");
+ let expectedPath;
+ if (AppConstants.platform == "win") {
+ expectedPath = OS.Path.join(getRootPath(), "Google", "Chrome", "User Data", "Default", "Extensions");
+ } else if (AppConstants.platform == "macosx") {
+ expectedPath = OS.Path.join(getRootPath(), "Application Support", "Google", "Chrome", "Default", "Extensions");
+ } else {
+ expectedPath = OS.Path.join(getRootPath(), ".config", "google-chrome", "Default", "Extensions");
+ }
+ Assert.equal(extensionPath, expectedPath, "Should get the path of extensions directory.");
+});
--- a/browser/components/migration/tests/unit/xpcshell.ini
+++ b/browser/components/migration/tests/unit/xpcshell.ini
@@ -9,16 +9,18 @@ support-files =
[test_360se_bookmarks.js]
skip-if = os != "win"
[test_automigration.js]
[test_Chrome_bookmarks.js]
[test_Chrome_cookies.js]
skip-if = os != "mac" # Relies on ULibDir
[test_Chrome_passwords.js]
skip-if = os != "win"
+[test_ChromeMigrationUtils.js]
+[test_ChromeMigrationUtils_path.js]
[test_Edge_db_migration.js]
skip-if = os != "win"
[test_fx_telemetry.js]
[test_IE_bookmarks.js]
skip-if = !(os == "win" && bits == 64) # bug 1392396
[test_IE_cookies.js]
skip-if = os != "win"
[test_IE7_passwords.js]
--- a/toolkit/components/osfile/modules/osfile_async_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_async_front.jsm
@@ -83,16 +83,17 @@ function lazyPathGetter(constProp, dirKe
};
}
for (let [constProp, dirKey] of [
["localProfileDir", "ProfLD"],
["profileDir", "ProfD"],
["userApplicationDataDir", "UAppData"],
["winAppDataDir", "AppData"],
+ ["winLocalAppDataDir", "LocalAppData"],
["winStartMenuProgsDir", "Progs"],
]) {
if (constProp in SharedAll.Constants.Path) {
continue;
}
LOG("Installing lazy getter for OS.Constants.Path." + constProp +