Commit 5e3cc061 authored by Yann Dago's avatar Yann Dago Committed by Commit Bot

Fixes data import from Firefox versions 67 and newer.

Bug: 1003284
Change-Id: If2b2a4dbfacc3849cdf58e92b94c420ad126baf2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1824675
Commit-Queue: Nico Weber <thakis@chromium.org>
Reviewed-by: default avatarNico Weber <thakis@chromium.org>
Reviewed-by: default avatarGreg Thompson <grt@chromium.org>
Reviewed-by: default avatarGabriel Charette <gab@chromium.org>
Reviewed-by: default avatarIlya Sherman <isherman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#708358}
parent f937e8b4
...@@ -94,8 +94,13 @@ void DetectFirefoxProfiles(const std::string locale, ...@@ -94,8 +94,13 @@ void DetectFirefoxProfiles(const std::string locale,
std::vector<importer::SourceProfile>* profiles) { std::vector<importer::SourceProfile>* profiles) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK); base::BlockingType::MAY_BLOCK);
#if defined(OS_WIN)
base::FilePath profile_path = GetFirefoxProfilePath(); const std::string firefox_install_id =
shell_integration::GetFirefoxProgIdSuffix();
#else
const std::string firefox_install_id;
#endif // defined(OS_WIN)
base::FilePath profile_path = GetFirefoxProfilePath(firefox_install_id);
if (profile_path.empty()) if (profile_path.empty())
return; return;
......
...@@ -96,6 +96,13 @@ bool IsFirefoxDefaultBrowser(); ...@@ -96,6 +96,13 @@ bool IsFirefoxDefaultBrowser();
// Returns true if IE is likely to be the default browser for the current // Returns true if IE is likely to be the default browser for the current
// user. This method is very fast so it can be invoked in the UI thread. // user. This method is very fast so it can be invoked in the UI thread.
bool IsIEDefaultBrowser(); bool IsIEDefaultBrowser();
// Returns the install id of the installation set as default browser. If no
// installation of Firefox is set as the default browser, returns an empty
// string.
// TODO(crbug/1011830): This should return the install id of the stable
// version if no version of Firefox is set as default browser.
std::string GetFirefoxProgIdSuffix();
#endif #endif
// Attempt to determine if this instance of Chrome is the default client // Attempt to determine if this instance of Chrome is the default client
......
...@@ -502,6 +502,15 @@ void MigrateChromeAndChromeProxyShortcuts( ...@@ -502,6 +502,15 @@ void MigrateChromeAndChromeProxyShortcuts(
win::MigrateShortcutsInPathInternal(chrome_proxy_path, shortcut_path); win::MigrateShortcutsInPathInternal(chrome_proxy_path, shortcut_path);
} }
base::string16 GetHttpProtocolUserChoiceProgId() {
base::string16 prog_id;
base::win::RegKey key(HKEY_CURRENT_USER, ShellUtil::kRegVistaUrlPrefs,
KEY_QUERY_VALUE);
if (key.Valid())
key.ReadValue(L"ProgId", &prog_id);
return prog_id;
}
} // namespace } // namespace
bool SetAsDefaultBrowser() { bool SetAsDefaultBrowser() {
...@@ -579,31 +588,28 @@ DefaultWebClientState GetDefaultBrowser() { ...@@ -579,31 +588,28 @@ DefaultWebClientState GetDefaultBrowser() {
ShellUtil::GetChromeDefaultState()); ShellUtil::GetChromeDefaultState());
} }
// There is no reliable way to say which browser is default on a machine (each // This method checks if Firefox is default browser by checking for the default
// browser can have some of the protocols/shortcuts). So we look for only HTTP // HTTP protocol handler. Returns false in case of error or if Firefox is not
// protocol handler. Even this handler is located at different places in // the user's default http protocol client.
// registry on XP and Vista:
// - HKCR\http\shell\open\command (XP)
// - HKCU\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\
// http\UserChoice (Vista)
// This method checks if Firefox is default browser by checking these
// locations and returns true if Firefox traces are found there. In case of
// error (or if Firefox is not found)it returns the default value which
// is false.
bool IsFirefoxDefaultBrowser() { bool IsFirefoxDefaultBrowser() {
base::string16 app_cmd; return base::StartsWith(GetHttpProtocolUserChoiceProgId(), L"FirefoxURL",
base::win::RegKey key(HKEY_CURRENT_USER, ShellUtil::kRegVistaUrlPrefs, base::CompareCase::SENSITIVE);
KEY_READ); }
return key.Valid() && key.ReadValue(L"Progid", &app_cmd) == ERROR_SUCCESS &&
app_cmd == L"FirefoxURL"; std::string GetFirefoxProgIdSuffix() {
const base::string16 app_cmd = GetHttpProtocolUserChoiceProgId();
static constexpr base::StringPiece16 kFirefoxProgIdPrefix(L"FirefoxURL-");
if (base::StartsWith(app_cmd, kFirefoxProgIdPrefix,
base::CompareCase::SENSITIVE)) {
// Returns the id that appears after the prefix "FirefoxURL-".
return std::string(app_cmd.begin() + kFirefoxProgIdPrefix.size(),
app_cmd.end());
}
return std::string();
} }
bool IsIEDefaultBrowser() { bool IsIEDefaultBrowser() {
base::string16 app_cmd; return GetHttpProtocolUserChoiceProgId() == L"IE.HTTP";
base::win::RegKey key(HKEY_CURRENT_USER, ShellUtil::kRegVistaUrlPrefs,
KEY_READ);
return key.Valid() && key.ReadValue(L"Progid", &app_cmd) == ERROR_SUCCESS &&
app_cmd == L"IE.HTTP";
} }
DefaultWebClientState IsDefaultProtocolClient(const std::string& protocol) { DefaultWebClientState IsDefaultProtocolClient(const std::string& protocol) {
......
...@@ -50,51 +50,107 @@ base::FilePath GetProfilePath(const base::DictionaryValue& root, ...@@ -50,51 +50,107 @@ base::FilePath GetProfilePath(const base::DictionaryValue& root,
return path; return path;
} }
// Checks if the named profile is the default profile. // Returns a map from Firefox profiles to their corresponding installation ids.
bool IsDefaultProfile(const base::DictionaryValue& root, // The keys are file system paths for Firefox profiles that are the default
const std::string& profile_name) { // profile in their installation. The values are the registry keys for the
std::string is_default; // corresponding installation.
root.GetStringASCII(profile_name + ".Default", &is_default); std::map<std::string, std::string> GetDefaultProfilesPerInstall(
return is_default == "1"; 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() { base::FilePath GetFirefoxProfilePath(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 GetFirefoxProfilePathFromDictionary(ini_parser.root(),
firefox_install_id);
} }
base::FilePath GetFirefoxProfilePathFromDictionary( base::FilePath GetFirefoxProfilePathFromDictionary(
const base::DictionaryValue& root) { const base::DictionaryValue& root,
std::vector<std::string> profiles; const std::string& firefox_install_id) {
// List of profiles linked to a Firefox installation. This will be empty for
// 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);
if (root.HasKey(current_profile)) { if (!root.HasKey(current_profile)) {
profiles.push_back(current_profile); // Profiles are contiguously numbered. So we exit when we can't
} else {
// Profiles are continuously numbered. So we exit when we can't
// find the i-th one. // find the i-th one.
break; break;
} }
}
if (profiles.empty()) std::string path;
return base::FilePath(); if (!root.GetStringASCII(current_profile + ".Path", &path))
continue;
// When multiple profiles exist, the path to the default profile is returned, auto install_id_it = default_profile_to_install_id.find(path);
// since the other profiles are used mostly by developers for testing. if (install_id_it != default_profile_to_install_id.end()) {
for (std::vector<std::string>::const_iterator it = profiles.begin(); // If this installation is the default browser, use the associated
it != profiles.end(); ++it) // profile as default profile.
if (IsDefaultProfile(root, *it)) if (install_id_it->second == firefox_install_id)
return GetProfilePath(root, *it); return GetProfilePath(root, current_profile);
if (!first_modern_profile)
first_modern_profile.emplace(std::move(current_profile));
} 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));
}
}
// If no default profile is found, the path to Profile0 will be returned. // Take the first install found as the default install.
return GetProfilePath(root, profiles.front()); if (first_modern_profile)
return GetProfilePath(root, *first_modern_profile);
return GetLegacyDefaultProfilePath(root, legacy_profiles);
} }
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
......
...@@ -38,13 +38,17 @@ base::FilePath GetFirefoxInstallPathFromRegistry(); ...@@ -38,13 +38,17 @@ base::FilePath GetFirefoxInstallPathFromRegistry();
base::FilePath GetFirefoxDylibPath(); base::FilePath GetFirefoxDylibPath();
#endif // OS_MACOSX #endif // OS_MACOSX
// Returns the path to the Firefox profile. // Returns the path to the default profile of the Firefox installation with id
base::FilePath GetFirefoxProfilePath(); // |firefox_install_id|.
base::FilePath GetFirefoxProfilePath(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
// with that id.
// Exposed for testing. // Exposed for testing.
base::FilePath GetFirefoxProfilePathFromDictionary( base::FilePath GetFirefoxProfilePathFromDictionary(
const base::DictionaryValue& root); const base::DictionaryValue& root,
const std::string& firefox_install_id);
// Detects version of Firefox and installation path for the given Firefox // Detects version of Firefox and installation path for the given Firefox
// profile. // profile.
......
...@@ -21,77 +21,76 @@ struct GetPrefsJsValueCase { ...@@ -21,77 +21,76 @@ struct GetPrefsJsValueCase {
std::string pref_name; std::string pref_name;
std::string pref_value; std::string pref_value;
} GetPrefsJsValueCases[] = { } GetPrefsJsValueCases[] = {
// Basic case. Single pref, unquoted value. // Basic case. Single pref, unquoted value.
{ "user_pref(\"foo.bar\", 1);", "foo.bar", "1" }, {"user_pref(\"foo.bar\", 1);", "foo.bar", "1"},
// Value is quoted. Quotes should be stripped. // Value is quoted. Quotes should be stripped.
{ "user_pref(\"foo.bar\", \"1\");", "foo.bar", "1" }, {"user_pref(\"foo.bar\", \"1\");", "foo.bar", "1"},
// Value has parens. // Value has parens.
{ "user_pref(\"foo.bar\", \"Value (detail)\");", {"user_pref(\"foo.bar\", \"Value (detail)\");", "foo.bar",
"foo.bar", "Value (detail)" }, "Value (detail)"},
// Multi-line case. // Multi-line case.
{ "user_pref(\"foo.bar\", 1);\n" {"user_pref(\"foo.bar\", 1);\n"
"user_pref(\"foo.baz\", 2);\n" "user_pref(\"foo.baz\", 2);\n"
"user_pref(\"foo.bag\", 3);", "user_pref(\"foo.bag\", 3);",
"foo.baz", "2" }, "foo.baz", "2"},
// Malformed content. // Malformed content.
{ "user_pref(\"foo.bar\", 1);\n" {"user_pref(\"foo.bar\", 1);\n"
"user_pref(\"foo.baz\", 2;\n" "user_pref(\"foo.baz\", 2;\n"
"user_pref(\"foo.bag\", 3);", "user_pref(\"foo.bag\", 3);",
"foo.baz", "" }, "foo.baz", std::string()},
// Malformed content. // Malformed content.
{ "uesr_pref(\"foo.bar\", 1);", "foo.bar", "" }, {"uesr_pref(\"foo.bar\", 1);", "foo.bar", std::string()},
}; };
struct GetFirefoxImporterNameCase { struct GetFirefoxImporterNameCase {
std::string app_ini_content; std::string app_ini_content;
int resource_id; int resource_id;
} GetFirefoxImporterNameCases[] = { } GetFirefoxImporterNameCases[] = {
// Basic case // Basic case
{ "[App]\n" {"[App]\n"
"Vendor=Mozilla\n" "Vendor=Mozilla\n"
"Name=iceweasel\n" "Name=iceweasel\n"
"Version=10.0.6\n" "Version=10.0.6\n"
"BuildID=20120717115048\n" "BuildID=20120717115048\n"
"ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}", "ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
IDS_IMPORT_FROM_ICEWEASEL }, IDS_IMPORT_FROM_ICEWEASEL},
// Whitespace // Whitespace
{ " \t[App] \n" {" \t[App] \n"
"Vendor=Mozilla\n" "Vendor=Mozilla\n"
" Name=Firefox\t \r\n" " Name=Firefox\t \r\n"
"Version=10.0.6\n", "Version=10.0.6\n",
IDS_IMPORT_FROM_FIREFOX }, IDS_IMPORT_FROM_FIREFOX},
// No Name setting // No Name setting
{ "[App]\n" {"[App]\n"
"Vendor=Mozilla\n" "Vendor=Mozilla\n"
"Version=10.0.6\n" "Version=10.0.6\n"
"BuildID=20120717115048\n" "BuildID=20120717115048\n"
"ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}", "ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
IDS_IMPORT_FROM_FIREFOX }, IDS_IMPORT_FROM_FIREFOX},
// No [App] section // No [App] section
{ "[Foo]\n" {"[Foo]\n"
"Vendor=Mozilla\n" "Vendor=Mozilla\n"
"Name=Foo\n", "Name=Foo\n",
IDS_IMPORT_FROM_FIREFOX }, IDS_IMPORT_FROM_FIREFOX},
// Multiple Name settings in different sections // Multiple Name settings in different sections
{ "[Foo]\n" {"[Foo]\n"
"Vendor=Mozilla\n" "Vendor=Mozilla\n"
"Name=Firefox\n" "Name=Firefox\n"
"[App]\n" "[App]\n"
"Profile=mozilla/firefox\n" "Profile=mozilla/firefox\n"
"Name=iceweasel\n" "Name=iceweasel\n"
"[Bar]\n" "[Bar]\n"
"Name=Bar\n" "Name=Bar\n"
"ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}", "ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
IDS_IMPORT_FROM_ICEWEASEL }, IDS_IMPORT_FROM_ICEWEASEL},
// Case-insensitivity // Case-insensitivity
{ "[App]\n" {"[App]\n"
"Vendor=Mozilla\n" "Vendor=Mozilla\n"
"Name=IceWeasel\n" "Name=IceWeasel\n"
"Version=10.0.6\n", "Version=10.0.6\n",
IDS_IMPORT_FROM_ICEWEASEL }, IDS_IMPORT_FROM_ICEWEASEL},
// Empty file // Empty file
{ "", IDS_IMPORT_FROM_FIREFOX } {std::string(), IDS_IMPORT_FROM_FIREFOX}};
};
} // anonymous namespace } // anonymous namespace
...@@ -125,15 +124,17 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxImporterName) { ...@@ -125,15 +124,17 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxImporterName) {
TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) { TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) {
base::DictionaryValue no_profiles; base::DictionaryValue no_profiles;
EXPECT_EQ("", EXPECT_EQ(std::string(),
GetFirefoxProfilePathFromDictionary(no_profiles).MaybeAsASCII()); GetFirefoxProfilePathFromDictionary(no_profiles, std::string())
.MaybeAsASCII());
base::DictionaryValue single_profile; base::DictionaryValue single_profile;
single_profile.SetString("Profile0.Path", "first"); single_profile.SetString("Profile0.Path", "first");
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", EXPECT_EQ("first",
GetFirefoxProfilePathFromDictionary(single_profile).MaybeAsASCII()); GetFirefoxProfilePathFromDictionary(single_profile, std::string())
.MaybeAsASCII());
base::DictionaryValue no_default; base::DictionaryValue no_default;
no_default.SetString("Profile0.Path", "first"); no_default.SetString("Profile0.Path", "first");
...@@ -141,7 +142,8 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) { ...@@ -141,7 +142,8 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) {
no_default.SetString("Profile1.Path", "second"); no_default.SetString("Profile1.Path", "second");
no_default.SetString("Profile1.IsRelative", "0"); no_default.SetString("Profile1.IsRelative", "0");
EXPECT_EQ("first", EXPECT_EQ("first",
GetFirefoxProfilePathFromDictionary(no_default).MaybeAsASCII()); GetFirefoxProfilePathFromDictionary(no_default, std::string())
.MaybeAsASCII());
base::DictionaryValue default_first; base::DictionaryValue default_first;
default_first.SetString("Profile0.Path", "first"); default_first.SetString("Profile0.Path", "first");
...@@ -150,7 +152,8 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) { ...@@ -150,7 +152,8 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) {
default_first.SetString("Profile1.Path", "second"); default_first.SetString("Profile1.Path", "second");
default_first.SetString("Profile1.IsRelative", "0"); default_first.SetString("Profile1.IsRelative", "0");
EXPECT_EQ("first", EXPECT_EQ("first",
GetFirefoxProfilePathFromDictionary(default_first).MaybeAsASCII()); GetFirefoxProfilePathFromDictionary(default_first, std::string())
.MaybeAsASCII());
base::DictionaryValue default_second; base::DictionaryValue default_second;
default_second.SetString("Profile0.Path", "first"); default_second.SetString("Profile0.Path", "first");
...@@ -159,5 +162,43 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) { ...@@ -159,5 +162,43 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) {
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", EXPECT_EQ("second",
GetFirefoxProfilePathFromDictionary(default_second).MaybeAsASCII()); GetFirefoxProfilePathFromDictionary(default_second, std::string())
.MaybeAsASCII());
// Firefox format from version 67
base::DictionaryValue default_single_install;
default_single_install.SetString("Install01.Default", "second");
default_single_install.SetString("Profile0.IsRelative", "0");
default_single_install.SetString("Profile0.Default", "1");
default_single_install.SetString("Profile1.Path", "second");
default_single_install.SetString("Profile1.IsRelative", "0");
EXPECT_EQ("second", GetFirefoxProfilePathFromDictionary(
default_single_install, std::string())
.MaybeAsASCII());
base::DictionaryValue default_single_install_unknown_profile;
default_single_install_unknown_profile.SetString("Install01.Default",
"wrong");
default_single_install_unknown_profile.SetString("Profile0.Path", "first");
default_single_install_unknown_profile.SetString("Profile0.IsRelative", "0");
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.IsRelative", "0");
EXPECT_EQ("first", GetFirefoxProfilePathFromDictionary(
default_single_install_unknown_profile, std::string())
.MaybeAsASCII());
base::DictionaryValue default_multiple_install;
default_single_install_unknown_profile.SetString("Install01.Default",
"first");
default_single_install_unknown_profile.SetString("Install02.Default",
"second");
default_single_install_unknown_profile.SetString("Profile0.Path", "first");
default_single_install_unknown_profile.SetString("Profile0.IsRelative", "0");
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.IsRelative", "0");
EXPECT_EQ("second", GetFirefoxProfilePathFromDictionary(
default_single_install_unknown_profile, "02")
.MaybeAsASCII());
} }
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