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,
std::vector<importer::SourceProfile>* profiles) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
base::FilePath profile_path = GetFirefoxProfilePath();
#if defined(OS_WIN)
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())
return;
......
......@@ -96,6 +96,13 @@ bool IsFirefoxDefaultBrowser();
// 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.
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
// Attempt to determine if this instance of Chrome is the default client
......
......@@ -502,6 +502,15 @@ void MigrateChromeAndChromeProxyShortcuts(
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
bool SetAsDefaultBrowser() {
......@@ -579,31 +588,28 @@ DefaultWebClientState GetDefaultBrowser() {
ShellUtil::GetChromeDefaultState());
}
// There is no reliable way to say which browser is default on a machine (each
// browser can have some of the protocols/shortcuts). So we look for only HTTP
// protocol handler. Even this handler is located at different places in
// 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.
// This method checks if Firefox is default browser by checking for the default
// HTTP protocol handler. Returns false in case of error or if Firefox is not
// the user's default http protocol client.
bool IsFirefoxDefaultBrowser() {
base::string16 app_cmd;
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"FirefoxURL";
return base::StartsWith(GetHttpProtocolUserChoiceProgId(), L"FirefoxURL",
base::CompareCase::SENSITIVE);
}
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() {
base::string16 app_cmd;
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";
return GetHttpProtocolUserChoiceProgId() == L"IE.HTTP";
}
DefaultWebClientState IsDefaultProtocolClient(const std::string& protocol) {
......
......@@ -50,51 +50,107 @@ base::FilePath GetProfilePath(const base::DictionaryValue& root,
return path;
}
// Checks if the named profile is the default profile.
bool IsDefaultProfile(const base::DictionaryValue& root,
const std::string& profile_name) {
std::string is_default;
root.GetStringASCII(profile_name + ".Default", &is_default);
return is_default == "1";
// 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
base::FilePath GetFirefoxProfilePath() {
base::FilePath GetFirefoxProfilePath(const std::string& firefox_install_id) {
base::FilePath ini_file = GetProfilesINI();
std::string content;
base::ReadFileToString(ini_file, &content);
DictionaryValueINIParser ini_parser;
ini_parser.Parse(content);
return GetFirefoxProfilePathFromDictionary(ini_parser.root());
return GetFirefoxProfilePathFromDictionary(ini_parser.root(),
firefox_install_id);
}
base::FilePath GetFirefoxProfilePathFromDictionary(
const base::DictionaryValue& root) {
std::vector<std::string> profiles;
const base::DictionaryValue& root,
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) {
std::string current_profile = base::StringPrintf("Profile%d", i);
if (root.HasKey(current_profile)) {
profiles.push_back(current_profile);
} else {
// Profiles are continuously numbered. So we exit when we can't
if (!root.HasKey(current_profile)) {
// Profiles are contiguously numbered. So we exit when we can't
// find the i-th one.
break;
}
}
if (profiles.empty())
return base::FilePath();
std::string path;
if (!root.GetStringASCII(current_profile + ".Path", &path))
continue;
// When multiple profiles exist, the path to the default profile is returned,
// since the other profiles are used mostly by developers for testing.
for (std::vector<std::string>::const_iterator it = profiles.begin();
it != profiles.end(); ++it)
if (IsDefaultProfile(root, *it))
return GetProfilePath(root, *it);
auto install_id_it = default_profile_to_install_id.find(path);
if (install_id_it != default_profile_to_install_id.end()) {
// If this installation is the default browser, use the associated
// profile as default profile.
if (install_id_it->second == firefox_install_id)
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.
return GetProfilePath(root, profiles.front());
// Take the first install found as the default install.
if (first_modern_profile)
return GetProfilePath(root, *first_modern_profile);
return GetLegacyDefaultProfilePath(root, legacy_profiles);
}
#if defined(OS_MACOSX)
......
......@@ -38,13 +38,17 @@ base::FilePath GetFirefoxInstallPathFromRegistry();
base::FilePath GetFirefoxDylibPath();
#endif // OS_MACOSX
// Returns the path to the Firefox profile.
base::FilePath GetFirefoxProfilePath();
// Returns the path to the default profile of the Firefox installation with id
// |firefox_install_id|.
base::FilePath GetFirefoxProfilePath(const std::string& firefox_install_id);
// 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.
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
// profile.
......
......@@ -21,77 +21,76 @@ struct GetPrefsJsValueCase {
std::string pref_name;
std::string pref_value;
} GetPrefsJsValueCases[] = {
// Basic case. Single pref, unquoted value.
{ "user_pref(\"foo.bar\", 1);", "foo.bar", "1" },
// Value is quoted. Quotes should be stripped.
{ "user_pref(\"foo.bar\", \"1\");", "foo.bar", "1" },
// Value has parens.
{ "user_pref(\"foo.bar\", \"Value (detail)\");",
"foo.bar", "Value (detail)" },
// Multi-line case.
{ "user_pref(\"foo.bar\", 1);\n"
"user_pref(\"foo.baz\", 2);\n"
"user_pref(\"foo.bag\", 3);",
"foo.baz", "2" },
// Malformed content.
{ "user_pref(\"foo.bar\", 1);\n"
"user_pref(\"foo.baz\", 2;\n"
"user_pref(\"foo.bag\", 3);",
"foo.baz", "" },
// Malformed content.
{ "uesr_pref(\"foo.bar\", 1);", "foo.bar", "" },
// Basic case. Single pref, unquoted value.
{"user_pref(\"foo.bar\", 1);", "foo.bar", "1"},
// Value is quoted. Quotes should be stripped.
{"user_pref(\"foo.bar\", \"1\");", "foo.bar", "1"},
// Value has parens.
{"user_pref(\"foo.bar\", \"Value (detail)\");", "foo.bar",
"Value (detail)"},
// Multi-line case.
{"user_pref(\"foo.bar\", 1);\n"
"user_pref(\"foo.baz\", 2);\n"
"user_pref(\"foo.bag\", 3);",
"foo.baz", "2"},
// Malformed content.
{"user_pref(\"foo.bar\", 1);\n"
"user_pref(\"foo.baz\", 2;\n"
"user_pref(\"foo.bag\", 3);",
"foo.baz", std::string()},
// Malformed content.
{"uesr_pref(\"foo.bar\", 1);", "foo.bar", std::string()},
};
struct GetFirefoxImporterNameCase {
std::string app_ini_content;
int resource_id;
} GetFirefoxImporterNameCases[] = {
// Basic case
{ "[App]\n"
"Vendor=Mozilla\n"
"Name=iceweasel\n"
"Version=10.0.6\n"
"BuildID=20120717115048\n"
"ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
IDS_IMPORT_FROM_ICEWEASEL },
// Whitespace
{ " \t[App] \n"
"Vendor=Mozilla\n"
" Name=Firefox\t \r\n"
"Version=10.0.6\n",
IDS_IMPORT_FROM_FIREFOX },
// No Name setting
{ "[App]\n"
"Vendor=Mozilla\n"
"Version=10.0.6\n"
"BuildID=20120717115048\n"
"ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
IDS_IMPORT_FROM_FIREFOX },
// No [App] section
{ "[Foo]\n"
"Vendor=Mozilla\n"
"Name=Foo\n",
IDS_IMPORT_FROM_FIREFOX },
// Multiple Name settings in different sections
{ "[Foo]\n"
"Vendor=Mozilla\n"
"Name=Firefox\n"
"[App]\n"
"Profile=mozilla/firefox\n"
"Name=iceweasel\n"
"[Bar]\n"
"Name=Bar\n"
"ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
IDS_IMPORT_FROM_ICEWEASEL },
// Case-insensitivity
{ "[App]\n"
"Vendor=Mozilla\n"
"Name=IceWeasel\n"
"Version=10.0.6\n",
IDS_IMPORT_FROM_ICEWEASEL },
// Empty file
{ "", IDS_IMPORT_FROM_FIREFOX }
};
// Basic case
{"[App]\n"
"Vendor=Mozilla\n"
"Name=iceweasel\n"
"Version=10.0.6\n"
"BuildID=20120717115048\n"
"ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
IDS_IMPORT_FROM_ICEWEASEL},
// Whitespace
{" \t[App] \n"
"Vendor=Mozilla\n"
" Name=Firefox\t \r\n"
"Version=10.0.6\n",
IDS_IMPORT_FROM_FIREFOX},
// No Name setting
{"[App]\n"
"Vendor=Mozilla\n"
"Version=10.0.6\n"
"BuildID=20120717115048\n"
"ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
IDS_IMPORT_FROM_FIREFOX},
// No [App] section
{"[Foo]\n"
"Vendor=Mozilla\n"
"Name=Foo\n",
IDS_IMPORT_FROM_FIREFOX},
// Multiple Name settings in different sections
{"[Foo]\n"
"Vendor=Mozilla\n"
"Name=Firefox\n"
"[App]\n"
"Profile=mozilla/firefox\n"
"Name=iceweasel\n"
"[Bar]\n"
"Name=Bar\n"
"ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
IDS_IMPORT_FROM_ICEWEASEL},
// Case-insensitivity
{"[App]\n"
"Vendor=Mozilla\n"
"Name=IceWeasel\n"
"Version=10.0.6\n",
IDS_IMPORT_FROM_ICEWEASEL},
// Empty file
{std::string(), IDS_IMPORT_FROM_FIREFOX}};
} // anonymous namespace
......@@ -125,15 +124,17 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxImporterName) {
TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) {
base::DictionaryValue no_profiles;
EXPECT_EQ("",
GetFirefoxProfilePathFromDictionary(no_profiles).MaybeAsASCII());
EXPECT_EQ(std::string(),
GetFirefoxProfilePathFromDictionary(no_profiles, std::string())
.MaybeAsASCII());
base::DictionaryValue single_profile;
single_profile.SetString("Profile0.Path", "first");
single_profile.SetString("Profile0.IsRelative", "0");
single_profile.SetString("Profile0.Default", "1");
EXPECT_EQ("first",
GetFirefoxProfilePathFromDictionary(single_profile).MaybeAsASCII());
GetFirefoxProfilePathFromDictionary(single_profile, std::string())
.MaybeAsASCII());
base::DictionaryValue no_default;
no_default.SetString("Profile0.Path", "first");
......@@ -141,7 +142,8 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) {
no_default.SetString("Profile1.Path", "second");
no_default.SetString("Profile1.IsRelative", "0");
EXPECT_EQ("first",
GetFirefoxProfilePathFromDictionary(no_default).MaybeAsASCII());
GetFirefoxProfilePathFromDictionary(no_default, std::string())
.MaybeAsASCII());
base::DictionaryValue default_first;
default_first.SetString("Profile0.Path", "first");
......@@ -150,7 +152,8 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) {
default_first.SetString("Profile1.Path", "second");
default_first.SetString("Profile1.IsRelative", "0");
EXPECT_EQ("first",
GetFirefoxProfilePathFromDictionary(default_first).MaybeAsASCII());
GetFirefoxProfilePathFromDictionary(default_first, std::string())
.MaybeAsASCII());
base::DictionaryValue default_second;
default_second.SetString("Profile0.Path", "first");
......@@ -159,5 +162,43 @@ TEST(FirefoxImporterUtilsTest, GetFirefoxProfilePath) {
default_second.SetString("Profile1.IsRelative", "0");
default_second.SetString("Profile1.Default", "1");
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