From d804c06d7f30809716a1c37e690e550df84a00a5 Mon Sep 17 00:00:00 2001 From: Ivan Efremov Date: Fri, 1 Nov 2019 00:17:47 +0700 Subject: [PATCH] Issue 6251: Backport firefox importer fix. Fix https://github.com/brave/brave-browser/issues/6521 See https://chromium-review.googlesource.com/#/q/If2b2a4dbfacc3849cdf58e92b94c420ad126baf2 --- ...me-browser-importer-importer_list.cc.patch | 26 +- .../chrome-browser-shell_integration.h.patch | 11 +- ...ome-browser-shell_integration_win.cc.patch | 66 ++++- ...n-importer-firefox_importer_utils.cc.patch | 137 +++++++++++ ...on-importer-firefox_importer_utils.h.patch | 25 ++ ...r-firefox_importer_utils_unittest.cc.patch | 232 ++++++++++++++++++ 6 files changed, 480 insertions(+), 17 deletions(-) create mode 100644 patches/chrome-common-importer-firefox_importer_utils.cc.patch create mode 100644 patches/chrome-common-importer-firefox_importer_utils.h.patch create mode 100644 patches/chrome-common-importer-firefox_importer_utils_unittest.cc.patch diff --git a/patches/chrome-browser-importer-importer_list.cc.patch b/patches/chrome-browser-importer-importer_list.cc.patch index 7b1e67577d23..4977bc35bb23 100644 --- a/patches/chrome-browser-importer-importer_list.cc.patch +++ b/patches/chrome-browser-importer-importer_list.cc.patch @@ -1,5 +1,5 @@ diff --git a/chrome/browser/importer/importer_list.cc b/chrome/browser/importer/importer_list.cc -index e557e55bdacf3164232e329389da9631cade8e93..613cd4fd78321310eed78cbb336c9eb1bd2f82c9 100644 +index e557e55bdacf3164232e329389da9631cade8e93..f613af5207e550b91fb878d7fe56204fbdaecbc7 100644 --- a/chrome/browser/importer/importer_list.cc +++ b/chrome/browser/importer/importer_list.cc @@ -29,6 +29,9 @@ @@ -29,16 +29,32 @@ index e557e55bdacf3164232e329389da9631cade8e93..613cd4fd78321310eed78cbb336c9eb1 - if (importer::EdgeImporterCanImport()) + if (shell_integration::IsIEDefaultBrowser()) { + DetectIEProfiles(profiles); ++ DetectEdgeProfiles(profiles); ++ } else { DetectEdgeProfiles(profiles); - DetectIEProfiles(profiles); -+ } else { -+ DetectEdgeProfiles(profiles); + DetectIEProfiles(profiles); + } } #endif // defined(OS_WIN) -@@ -122,6 +130,7 @@ void DetectFirefoxProfiles(const std::string locale, +@@ -89,8 +97,13 @@ void DetectFirefoxProfiles(const std::string locale, + std::vector* 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; + +@@ -122,6 +135,7 @@ void DetectFirefoxProfiles(const std::string locale, firefox.app_path = app_path; firefox.services_supported = importer::HISTORY | importer::FAVORITES | importer::PASSWORDS | importer::SEARCH_ENGINES | @@ -46,7 +62,7 @@ index e557e55bdacf3164232e329389da9631cade8e93..613cd4fd78321310eed78cbb336c9eb1 importer::AUTOFILL_FORM_DATA; firefox.locale = locale; profiles->push_back(firefox); -@@ -140,21 +149,37 @@ std::vector DetectSourceProfilesWorker( +@@ -140,21 +154,37 @@ std::vector DetectSourceProfilesWorker( #if defined(OS_WIN) if (shell_integration::IsFirefoxDefaultBrowser()) { DetectFirefoxProfiles(locale, &profiles); diff --git a/patches/chrome-browser-shell_integration.h.patch b/patches/chrome-browser-shell_integration.h.patch index b711f96f34ab..7ba207491c23 100644 --- a/patches/chrome-browser-shell_integration.h.patch +++ b/patches/chrome-browser-shell_integration.h.patch @@ -1,8 +1,8 @@ diff --git a/chrome/browser/shell_integration.h b/chrome/browser/shell_integration.h -index a7bd5219a9b9a433036db3a82e579c53158ce798..fdb4c9d1d59009577d4521ba32374984b109e864 100644 +index a7bd5219a9b9a433036db3a82e579c53158ce798..c4e668791cf82b6269634c5a97381aae3b58034e 100644 --- a/chrome/browser/shell_integration.h +++ b/chrome/browser/shell_integration.h -@@ -92,6 +92,12 @@ DefaultWebClientState GetDefaultBrowser(); +@@ -92,6 +92,19 @@ DefaultWebClientState GetDefaultBrowser(); // user. This method is very fast so it can be invoked in the UI thread. bool IsFirefoxDefaultBrowser(); @@ -10,6 +10,13 @@ index a7bd5219a9b9a433036db3a82e579c53158ce798..fdb4c9d1d59009577d4521ba32374984 +// 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 diff --git a/patches/chrome-browser-shell_integration_win.cc.patch b/patches/chrome-browser-shell_integration_win.cc.patch index b2d9a7b34a62..d54daacfbecc 100644 --- a/patches/chrome-browser-shell_integration_win.cc.patch +++ b/patches/chrome-browser-shell_integration_win.cc.patch @@ -1,19 +1,65 @@ diff --git a/chrome/browser/shell_integration_win.cc b/chrome/browser/shell_integration_win.cc -index 9b05880c7b94700e2cbfdddcd8e0b95cd11f7c4e..08e519ba345854aa29dc5927b58fe6325228e55d 100644 +index 9b05880c7b94700e2cbfdddcd8e0b95cd11f7c4e..f6b4919378cc72bffe1e95d261566364e3a06e4b 100644 --- a/chrome/browser/shell_integration_win.cc +++ b/chrome/browser/shell_integration_win.cc -@@ -588,6 +588,14 @@ bool IsFirefoxDefaultBrowser() { - app_cmd == L"FirefoxURL"; +@@ -492,6 +492,15 @@ void IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult( + delete this; } -+bool IsIEDefaultBrowser() { -+ base::string16 app_cmd; ++base::string16 GetHttpProtocolUserChoiceProgId() { ++ base::string16 prog_id; + 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"; ++ KEY_QUERY_VALUE); ++ if (key.Valid()) ++ key.ReadValue(L"ProgId", &prog_id); ++ return prog_id; ++} ++ + } // namespace + + bool SetAsDefaultBrowser() { +@@ -569,23 +578,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() { ++ return GetHttpProtocolUserChoiceProgId() == L"IE.HTTP"; + } + DefaultWebClientState IsDefaultProtocolClient(const std::string& protocol) { - return GetDefaultWebClientStateFromShellUtilDefaultState( - ShellUtil::GetChromeDefaultProtocolClientState( diff --git a/patches/chrome-common-importer-firefox_importer_utils.cc.patch b/patches/chrome-common-importer-firefox_importer_utils.cc.patch new file mode 100644 index 000000000000..e2a4d5d8c2c1 --- /dev/null +++ b/patches/chrome-common-importer-firefox_importer_utils.cc.patch @@ -0,0 +1,137 @@ +diff --git a/chrome/common/importer/firefox_importer_utils.cc b/chrome/common/importer/firefox_importer_utils.cc +index e1f5ada77718755f5a59d78e63a14e500330e002..7360ca488a158b70c99f55c4ba170777a19d5181 100644 +--- a/chrome/common/importer/firefox_importer_utils.cc ++++ b/chrome/common/importer/firefox_importer_utils.cc +@@ -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 GetDefaultProfilesPerInstall( ++ const base::DictionaryValue& root) { ++ std::map 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& 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 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 default_profile_to_install_id = ++ GetDefaultProfilesPerInstall(root); ++ // First profile linked to a Firefox installation (version >= 67). ++ base::Optional first_modern_profile; ++ ++ // Profiles not linked to a Firefox installation (version < 67). ++ std::vector 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::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) diff --git a/patches/chrome-common-importer-firefox_importer_utils.h.patch b/patches/chrome-common-importer-firefox_importer_utils.h.patch new file mode 100644 index 000000000000..0ce9d98a882f --- /dev/null +++ b/patches/chrome-common-importer-firefox_importer_utils.h.patch @@ -0,0 +1,25 @@ +diff --git a/chrome/common/importer/firefox_importer_utils.h b/chrome/common/importer/firefox_importer_utils.h +index ec57c17927173691d98db394d726da41d1f43475..e9feec4eae2b73cea2b1c722095b4c121968aced 100644 +--- a/chrome/common/importer/firefox_importer_utils.h ++++ b/chrome/common/importer/firefox_importer_utils.h +@@ -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. diff --git a/patches/chrome-common-importer-firefox_importer_utils_unittest.cc.patch b/patches/chrome-common-importer-firefox_importer_utils_unittest.cc.patch new file mode 100644 index 000000000000..f7406f29d651 --- /dev/null +++ b/patches/chrome-common-importer-firefox_importer_utils_unittest.cc.patch @@ -0,0 +1,232 @@ +diff --git a/chrome/common/importer/firefox_importer_utils_unittest.cc b/chrome/common/importer/firefox_importer_utils_unittest.cc +index 6fdf99de260850a18eb796fa1c96400482863e8e..16d51771c469763bc5fffe3b8c61c8a0b10f76d8 100644 +--- a/chrome/common/importer/firefox_importer_utils_unittest.cc ++++ b/chrome/common/importer/firefox_importer_utils_unittest.cc +@@ -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()); + }