Commit 79797da3 authored by Jonathan Kingston's avatar Jonathan Kingston Committed by Commit Bot

Support for importing non default Firefox profiles.

Bug: 1011830
Change-Id: If411450952c0e4018933eba636d168d98ece90cd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2070201
Commit-Queue: Ilya Sherman <isherman@chromium.org>
Reviewed-by: default avatardpapad <dpapad@chromium.org>
Reviewed-by: default avatarIlya Sherman <isherman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#749019}
parent 6e7583fd
...@@ -474,6 +474,7 @@ Johnson Lin <johnson.lin@intel.com> ...@@ -474,6 +474,7 @@ Johnson Lin <johnson.lin@intel.com>
Jonathan Frazer <listedegarde@gmail.com> Jonathan Frazer <listedegarde@gmail.com>
Jonathan Garbee <jonathan@garbee.me> Jonathan Garbee <jonathan@garbee.me>
Jonathan Hacker <jhacker@arcanefour.com> Jonathan Hacker <jhacker@arcanefour.com>
Jonathan Kingston <kingstonmailbox@gmail.com>
Jongdeok Kim <jongdeok.kim@navercorp.com> Jongdeok Kim <jongdeok.kim@navercorp.com>
Jongheon Kim <sapzape@gmail.com> Jongheon Kim <sapzape@gmail.com>
JongKwon Lee <jongkwon.lee@navercorp.com> JongKwon Lee <jongkwon.lee@navercorp.com>
......
...@@ -101,41 +101,45 @@ void DetectFirefoxProfiles(const std::string locale, ...@@ -101,41 +101,45 @@ void DetectFirefoxProfiles(const std::string locale,
#else #else
const std::string firefox_install_id; const std::string firefox_install_id;
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
base::FilePath profile_path = GetFirefoxProfilePath(firefox_install_id); std::vector<FirefoxDetail> details = GetFirefoxDetails(firefox_install_id);
if (profile_path.empty()) if (details.empty())
return; return;
// Detects which version of Firefox is installed. for (auto detail = details.begin(); detail != details.end(); ++detail) {
importer::ImporterType firefox_type;
base::FilePath app_path; base::FilePath app_path;
if (detail->path.empty())
continue;
int version = 0; int version = 0;
#if defined(OS_WIN) #if defined(OS_WIN)
version = GetCurrentFirefoxMajorVersionFromRegistry(); version = GetCurrentFirefoxMajorVersionFromRegistry();
#endif #endif
if (version < 2)
GetFirefoxVersionAndPathFromProfile(profile_path, &version, &app_path);
if (version >= 3) { if (version < 2) {
firefox_type = importer::TYPE_FIREFOX; GetFirefoxVersionAndPathFromProfile(detail->path, &version, &app_path);
} else { // Note that |version| is re-assigned above.
if (version < 2) {
// Ignores old versions of firefox. // Ignores old versions of firefox.
return; continue;
}
} }
importer::SourceProfile firefox; importer::SourceProfile firefox;
firefox.importer_name = GetFirefoxImporterName(app_path); firefox.importer_name = GetFirefoxImporterName(app_path);
firefox.importer_type = firefox_type; firefox.profile = detail->name;
firefox.source_path = profile_path; firefox.importer_type = importer::TYPE_FIREFOX;
firefox.source_path = detail->path;
#if defined(OS_WIN) #if defined(OS_WIN)
firefox.app_path = GetFirefoxInstallPathFromRegistry(); firefox.app_path = GetFirefoxInstallPathFromRegistry();
#endif #endif
if (firefox.app_path.empty()) if (firefox.app_path.empty())
firefox.app_path = app_path; firefox.app_path = app_path;
firefox.services_supported = importer::HISTORY | importer::FAVORITES | firefox.services_supported =
importer::PASSWORDS | importer::SEARCH_ENGINES | importer::HISTORY | importer::FAVORITES | importer::PASSWORDS |
importer::AUTOFILL_FORM_DATA; importer::SEARCH_ENGINES | importer::AUTOFILL_FORM_DATA;
firefox.locale = locale; firefox.locale = locale;
profiles->push_back(firefox); profiles->push_back(firefox);
}
} }
std::vector<importer::SourceProfile> DetectSourceProfilesWorker( std::vector<importer::SourceProfile> DetectSourceProfilesWorker(
......
...@@ -9,6 +9,7 @@ cr.define('settings', function() { ...@@ -9,6 +9,7 @@ cr.define('settings', function() {
* @typedef {{ * @typedef {{
* name: string, * name: string,
* index: number, * index: number,
* profileName: string,
* history: boolean, * history: boolean,
* favorites: boolean, * favorites: boolean,
* passwords: boolean, * passwords: boolean,
......
...@@ -64,7 +64,12 @@ ...@@ -64,7 +64,12 @@
aria-label="$i18n{importFromLabel}" aria-label="$i18n{importFromLabel}"
on-change="onBrowserProfileSelectionChange_"> on-change="onBrowserProfileSelectionChange_">
<template is="dom-repeat" items="[[browserProfiles_]]"> <template is="dom-repeat" items="[[browserProfiles_]]">
<option value="[[item.index]]">[[item.name]]</option> <option value="[[item.index]]">
[[item.name]]
<span hidden$="[[!item.profileName]]">
- [[item.profileName]]
</span>
</option>
</template> </template>
</select> </select>
<div class="description">$i18n{importDescription}</div> <div class="description">$i18n{importDescription}</div>
......
...@@ -191,6 +191,7 @@ void ImportDataHandler::SendBrowserProfileData(const std::string& callback_id) { ...@@ -191,6 +191,7 @@ void ImportDataHandler::SendBrowserProfileData(const std::string& callback_id) {
new base::DictionaryValue()); new base::DictionaryValue());
browser_profile->SetString("name", source_profile.importer_name); browser_profile->SetString("name", source_profile.importer_name);
browser_profile->SetInteger("index", i); browser_profile->SetInteger("index", i);
browser_profile->SetString("profileName", source_profile.profile);
browser_profile->SetBoolean("history", browser_profile->SetBoolean("history",
(browser_services & importer::HISTORY) != 0); (browser_services & importer::HISTORY) != 0);
browser_profile->SetBoolean("favorites", browser_profile->SetBoolean("favorites",
......
...@@ -50,74 +50,22 @@ base::FilePath GetProfilePath(const base::DictionaryValue& root, ...@@ -50,74 +50,22 @@ base::FilePath GetProfilePath(const base::DictionaryValue& root,
return path; return path;
} }
// Returns a map from Firefox profiles to their corresponding installation ids.
// The keys are file system paths for Firefox profiles that are the default
// profile in their installation. The values are the registry keys for the
// corresponding installation.
std::map<std::string, std::string> GetDefaultProfilesPerInstall(
const base::DictionaryValue& root) {
std::map<std::string, std::string> default_profile_to_install_id;
static constexpr base::StringPiece kInstallPrefix("Install");
// Find the default profiles for each Firefox installation.
for (const auto& data : root) {
const std::string& dict_key = data.first;
if (base::StartsWith(dict_key, kInstallPrefix,
base::CompareCase::SENSITIVE)) {
std::string path;
if (root.GetStringASCII(dict_key + ".Default", &path)) {
default_profile_to_install_id.emplace(
std::move(path), dict_key.substr(kInstallPrefix.size()));
}
}
}
return default_profile_to_install_id;
}
base::FilePath GetLegacyDefaultProfilePath(
const base::DictionaryValue& root,
const std::vector<std::string>& profile_names) {
if (profile_names.empty())
return base::FilePath();
// When multiple profiles exist, the path to the default profile is returned.
for (const auto& profile_name : profile_names) {
// Checks if the named profile is the default profile using the legacy
// format of profiles.ini (Firefox version < 67).
std::string is_default;
if (root.GetStringASCII(profile_name + ".Default", &is_default) &&
is_default == "1") {
return GetProfilePath(root, profile_name);
}
}
// If no default profile is found, the path to Profile0 will be returned.
return GetProfilePath(root, profile_names.front());
}
} // namespace } // namespace
base::FilePath GetFirefoxProfilePath(const std::string& firefox_install_id) { std::vector<FirefoxDetail> GetFirefoxDetails(
const std::string& firefox_install_id) {
base::FilePath ini_file = GetProfilesINI(); base::FilePath ini_file = GetProfilesINI();
std::string content; std::string content;
base::ReadFileToString(ini_file, &content); base::ReadFileToString(ini_file, &content);
DictionaryValueINIParser ini_parser; DictionaryValueINIParser ini_parser;
ini_parser.Parse(content); ini_parser.Parse(content);
return GetFirefoxProfilePathFromDictionary(ini_parser.root(), return GetFirefoxDetailsFromDictionary(ini_parser.root(), firefox_install_id);
firefox_install_id);
} }
base::FilePath GetFirefoxProfilePathFromDictionary( std::vector<FirefoxDetail> GetFirefoxDetailsFromDictionary(
const base::DictionaryValue& root, const base::DictionaryValue& root,
const std::string& firefox_install_id) { const std::string& firefox_install_id) {
// List of profiles linked to a Firefox installation. This will be empty for std::vector<FirefoxDetail> profile_details;
// Firefox versions older than 67.
std::map<std::string, std::string> default_profile_to_install_id =
GetDefaultProfilesPerInstall(root);
// First profile linked to a Firefox installation (version >= 67).
base::Optional<std::string> first_modern_profile;
// Profiles not linked to a Firefox installation (version < 67).
std::vector<std::string> legacy_profiles;
for (int i = 0; ; ++i) { for (int i = 0; ; ++i) {
std::string current_profile = base::StringPrintf("Profile%d", i); std::string current_profile = base::StringPrintf("Profile%d", i);
...@@ -131,26 +79,24 @@ base::FilePath GetFirefoxProfilePathFromDictionary( ...@@ -131,26 +79,24 @@ base::FilePath GetFirefoxProfilePathFromDictionary(
if (!root.GetStringASCII(current_profile + ".Path", &path)) if (!root.GetStringASCII(current_profile + ".Path", &path))
continue; continue;
auto install_id_it = default_profile_to_install_id.find(path); FirefoxDetail details;
if (install_id_it != default_profile_to_install_id.end()) { details.path = GetProfilePath(root, current_profile);
// If this installation is the default browser, use the associated std::string name;
// profile as default profile. root.GetStringASCII(current_profile + ".Name", &name);
if (install_id_it->second == firefox_install_id) // Make the profile name more presentable by replacing dashes with spaces.
return GetProfilePath(root, current_profile); base::ReplaceChars(name, "-", " ", &name);
if (!first_modern_profile) details.name = name;
first_modern_profile.emplace(std::move(current_profile)); profile_details.push_back(details);
} else {
// If no Firefox installation found in profiles.ini, legacy profiles
// (Firefox version < 67) are being used.
legacy_profiles.push_back(std::move(current_profile));
}
} }
// Take the first install found as the default install. // If there is only one profile, set the name as a blank string.
if (first_modern_profile) // The name is only used to disambiguate profiles in the profile selection UI,
return GetProfilePath(root, *first_modern_profile); // which is only useful when there are multiple profiles.
if (profile_details.size() == 1) {
profile_details[0].name = "";
}
return GetLegacyDefaultProfilePath(root, legacy_profiles); return profile_details;
} }
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/files/file_util.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "build/build_config.h" #include "build/build_config.h"
...@@ -38,15 +39,32 @@ base::FilePath GetFirefoxInstallPathFromRegistry(); ...@@ -38,15 +39,32 @@ base::FilePath GetFirefoxInstallPathFromRegistry();
base::FilePath GetFirefoxDylibPath(); base::FilePath GetFirefoxDylibPath();
#endif // OS_MACOSX #endif // OS_MACOSX
// Returns the path to the default profile of the Firefox installation with id struct FirefoxDetail {
// |firefox_install_id|. // |path| represents the Path field in Profiles.ini.
base::FilePath GetFirefoxProfilePath(const std::string& firefox_install_id); // This path is the directory name where all the profile information
// in stored.
base::FilePath path;
// The user specified name of the profile.
std::string name;
};
inline bool operator==(const FirefoxDetail& a1, const FirefoxDetail& a2) {
return a1.name == a2.name && a1.path == a2.path;
}
inline bool operator!=(const FirefoxDetail& a1, const FirefoxDetail& a2) {
return !(a1 == a2);
}
// Returns a vector of FirefoxDetail for available profiles.
std::vector<FirefoxDetail> GetFirefoxDetails(
const std::string& firefox_install_id);
// Returns the path to the Firefox profile, using a custom dictionary. // Returns the path to the Firefox profile, using a custom dictionary.
// If |firefox_install_id| is not empty returns the default profile associated // If |firefox_install_id| is not empty returns the default profile associated
// with that id. // with that id.
// Exposed for testing. // Exposed for testing.
base::FilePath GetFirefoxProfilePathFromDictionary( std::vector<FirefoxDetail> GetFirefoxDetailsFromDictionary(
const base::DictionaryValue& root, const base::DictionaryValue& root,
const std::string& firefox_install_id); const std::string& firefox_install_id);
......
...@@ -11,9 +11,12 @@ ...@@ -11,9 +11,12 @@
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
using testing::UnorderedElementsAre;
namespace { namespace {
struct GetPrefsJsValueCase { struct GetPrefsJsValueCase {
...@@ -124,46 +127,71 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxImporterName) { ...@@ -124,46 +127,71 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxImporterName) {
TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) { TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) {
base::DictionaryValue no_profiles; base::DictionaryValue no_profiles;
EXPECT_EQ(std::string(), EXPECT_EQ(0u,
GetFirefoxProfilePathFromDictionary(no_profiles, std::string()) GetFirefoxDetailsFromDictionary(no_profiles, std::string()).size());
.MaybeAsASCII());
base::DictionaryValue single_profile; base::DictionaryValue single_profile;
single_profile.SetString("Profile0.Path", "first"); single_profile.SetString("Profile0.Path", "first");
// Ensure that when there is only one profile the profile name shown in the UI
// is empty, since there's no need to disambiguate among multiple profiles
single_profile.SetString("Profile0.Name", "namey");
single_profile.SetString("Profile0.IsRelative", "0"); single_profile.SetString("Profile0.IsRelative", "0");
single_profile.SetString("Profile0.Default", "1"); single_profile.SetString("Profile0.Default", "1");
EXPECT_EQ("first",
GetFirefoxProfilePathFromDictionary(single_profile, std::string()) std::vector<FirefoxDetail> details =
.MaybeAsASCII()); GetFirefoxDetailsFromDictionary(single_profile, std::string());
EXPECT_THAT(details, UnorderedElementsAre(FirefoxDetail{
base::FilePath(FILE_PATH_LITERAL("first")), ""}));
base::DictionaryValue no_default; base::DictionaryValue no_default;
no_default.SetString("Profile0.Path", "first"); no_default.SetString("Profile0.Path", "first");
no_default.SetString("Profile0.Name", "namey");
no_default.SetString("Profile0.IsRelative", "0"); no_default.SetString("Profile0.IsRelative", "0");
no_default.SetString("Profile1.Path", "second"); no_default.SetString("Profile1.Path", "second");
no_default.SetString("Profile1.Name", "namey-name");
no_default.SetString("Profile1.IsRelative", "0"); no_default.SetString("Profile1.IsRelative", "0");
EXPECT_EQ("first", std::vector<FirefoxDetail> no_default_details =
GetFirefoxProfilePathFromDictionary(no_default, std::string()) GetFirefoxDetailsFromDictionary(no_default, std::string());
.MaybeAsASCII()); EXPECT_THAT(
no_default_details,
UnorderedElementsAre(
FirefoxDetail{base::FilePath(FILE_PATH_LITERAL("first")), "namey"},
FirefoxDetail{base::FilePath(FILE_PATH_LITERAL("second")),
"namey name"}));
base::DictionaryValue default_first; base::DictionaryValue default_first;
default_first.SetString("Profile0.Path", "first"); default_first.SetString("Profile0.Path", "first");
default_first.SetString("Profile0.Name", "namey");
default_first.SetString("Profile0.IsRelative", "0"); default_first.SetString("Profile0.IsRelative", "0");
default_first.SetString("Profile0.Default", "1"); default_first.SetString("Profile0.Default", "1");
default_first.SetString("Profile1.Path", "second"); default_first.SetString("Profile1.Path", "second");
default_first.SetString("Profile1.Name", "namey-name");
default_first.SetString("Profile1.IsRelative", "0"); default_first.SetString("Profile1.IsRelative", "0");
EXPECT_EQ("first", std::vector<FirefoxDetail> default_first_details =
GetFirefoxProfilePathFromDictionary(default_first, std::string()) GetFirefoxDetailsFromDictionary(default_first, std::string());
.MaybeAsASCII()); EXPECT_THAT(
default_first_details,
UnorderedElementsAre(
FirefoxDetail{base::FilePath(FILE_PATH_LITERAL("first")), "namey"},
FirefoxDetail{base::FilePath(FILE_PATH_LITERAL("second")),
"namey name"}));
base::DictionaryValue default_second; base::DictionaryValue default_second;
default_second.SetString("Profile0.Path", "first"); default_second.SetString("Profile0.Path", "first");
default_second.SetString("Profile0.Name", "namey");
default_second.SetString("Profile0.IsRelative", "0"); default_second.SetString("Profile0.IsRelative", "0");
default_second.SetString("Profile1.Path", "second"); default_second.SetString("Profile1.Path", "second");
default_second.SetString("Profile1.Name", "namey-name");
default_second.SetString("Profile1.IsRelative", "0"); default_second.SetString("Profile1.IsRelative", "0");
default_second.SetString("Profile1.Default", "1"); default_second.SetString("Profile1.Default", "1");
EXPECT_EQ("second", std::vector<FirefoxDetail> default_second_details =
GetFirefoxProfilePathFromDictionary(default_second, std::string()) GetFirefoxDetailsFromDictionary(default_second, std::string());
.MaybeAsASCII()); EXPECT_THAT(
default_second_details,
UnorderedElementsAre(
FirefoxDetail{base::FilePath(FILE_PATH_LITERAL("first")), "namey"},
FirefoxDetail{base::FilePath(FILE_PATH_LITERAL("second")),
"namey name"}));
// Firefox format from version 67 // Firefox format from version 67
base::DictionaryValue default_single_install; base::DictionaryValue default_single_install;
...@@ -172,9 +200,9 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) { ...@@ -172,9 +200,9 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) {
default_single_install.SetString("Profile0.Default", "1"); default_single_install.SetString("Profile0.Default", "1");
default_single_install.SetString("Profile1.Path", "second"); default_single_install.SetString("Profile1.Path", "second");
default_single_install.SetString("Profile1.IsRelative", "0"); default_single_install.SetString("Profile1.IsRelative", "0");
EXPECT_EQ("second", GetFirefoxProfilePathFromDictionary( std::vector<FirefoxDetail> default_single_install_details =
default_single_install, std::string()) GetFirefoxDetailsFromDictionary(default_single_install, std::string());
.MaybeAsASCII()); EXPECT_EQ("second", default_single_install_details[0].path.MaybeAsASCII());
base::DictionaryValue default_single_install_unknown_profile; base::DictionaryValue default_single_install_unknown_profile;
default_single_install_unknown_profile.SetString("Install01.Default", default_single_install_unknown_profile.SetString("Install01.Default",
...@@ -184,11 +212,15 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) { ...@@ -184,11 +212,15 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) {
default_single_install_unknown_profile.SetString("Profile0.Default", "1"); default_single_install_unknown_profile.SetString("Profile0.Default", "1");
default_single_install_unknown_profile.SetString("Profile1.Path", "second"); default_single_install_unknown_profile.SetString("Profile1.Path", "second");
default_single_install_unknown_profile.SetString("Profile1.IsRelative", "0"); default_single_install_unknown_profile.SetString("Profile1.IsRelative", "0");
EXPECT_EQ("first", GetFirefoxProfilePathFromDictionary( std::vector<FirefoxDetail> default_single_install_unknown_profile_details =
default_single_install_unknown_profile, std::string()) GetFirefoxDetailsFromDictionary(default_single_install_unknown_profile,
.MaybeAsASCII()); std::string());
EXPECT_THAT(
default_single_install_unknown_profile_details,
UnorderedElementsAre(
FirefoxDetail{base::FilePath(FILE_PATH_LITERAL("first")), ""},
FirefoxDetail{base::FilePath(FILE_PATH_LITERAL("second")), ""}));
base::DictionaryValue default_multiple_install;
default_single_install_unknown_profile.SetString("Install01.Default", default_single_install_unknown_profile.SetString("Install01.Default",
"first"); "first");
default_single_install_unknown_profile.SetString("Install02.Default", default_single_install_unknown_profile.SetString("Install02.Default",
...@@ -198,7 +230,12 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) { ...@@ -198,7 +230,12 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) {
default_single_install_unknown_profile.SetString("Profile0.Default", "1"); default_single_install_unknown_profile.SetString("Profile0.Default", "1");
default_single_install_unknown_profile.SetString("Profile1.Path", "second"); default_single_install_unknown_profile.SetString("Profile1.Path", "second");
default_single_install_unknown_profile.SetString("Profile1.IsRelative", "0"); default_single_install_unknown_profile.SetString("Profile1.IsRelative", "0");
EXPECT_EQ("second", GetFirefoxProfilePathFromDictionary( std::vector<FirefoxDetail> default_multiple_install_details =
default_single_install_unknown_profile, "02") GetFirefoxDetailsFromDictionary(default_single_install_unknown_profile,
.MaybeAsASCII()); std::string());
EXPECT_THAT(
default_multiple_install_details,
UnorderedElementsAre(
FirefoxDetail{base::FilePath(FILE_PATH_LITERAL("first")), ""},
FirefoxDetail{base::FilePath(FILE_PATH_LITERAL("second")), ""}));
} }
...@@ -48,6 +48,7 @@ struct SourceProfile { ...@@ -48,6 +48,7 @@ struct SourceProfile {
// The application locale. Stored because we can only access it from the UI // The application locale. Stored because we can only access it from the UI
// thread on the browser process. This is only used by the Firefox importer. // thread on the browser process. This is only used by the Firefox importer.
std::string locale; std::string locale;
std::string profile;
}; };
// Contains information needed for importing search engine urls. // Contains information needed for importing search engine urls.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment