Commit 90f55907 authored by Robert Sesek's avatar Robert Sesek Committed by Commit Bot

Remove old Firefox search engine importer support.

Firefox has not stored search engine data in XML files in the past five
years (https://bugzilla.mozilla.org/show_bug.cgi?id=1203167). Something
about the search engines importer is causing crashes, but with no tests
and no good way to get an old-style profile to test with, fixing that
bug is difficult. Just remove the code instead, since it has bitrotted
and it's unlikely that anyone is importing a 3+ year-old Firefox profile
that has not been migrated to the new storage scheme.

Firefox now stores search engines in an LZ4-compressed JSON file called
search.json.mozlz4, which would be the thing to read if Chromium wanted
to re-add support for importing Firefox search engines.

Bug: 1073905
Change-Id: Ib1643d64407a550805105908df40075d85cfa118
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2163872Reviewed-by: default avatarGabriel Charette <gab@chromium.org>
Reviewed-by: default avatarIlya Sherman <isherman@chromium.org>
Commit-Queue: Robert Sesek <rsesek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#763336}
parent 81b40ad9
......@@ -220,13 +220,6 @@ void ExternalProcessImporterClient::OnKeywordsImportReady(
bridge_->SetKeywords(search_engines, unique_on_host_and_path);
}
void ExternalProcessImporterClient::OnFirefoxSearchEngineDataReceived(
const std::vector<std::string>& search_engine_data) {
if (cancelled_)
return;
bridge_->SetFirefoxSearchEnginesXMLData(search_engine_data);
}
void ExternalProcessImporterClient::OnAutofillFormDataImportStart(
uint32_t total_autofill_form_data_entry_count) {
if (cancelled_)
......
......@@ -79,8 +79,6 @@ class ExternalProcessImporterClient
void OnKeywordsImportReady(
const std::vector<importer::SearchEngineInfo>& search_engines,
bool unique_on_host_and_path) override;
void OnFirefoxSearchEngineDataReceived(
const std::vector<std::string>& search_engine_data) override;
void OnAutofillFormDataImportStart(
uint32_t total_autofill_form_data_entry_count) override;
void OnAutofillFormDataImportGroup(
......
......@@ -134,9 +134,9 @@ void DetectFirefoxProfiles(const std::string locale,
#endif
if (firefox.app_path.empty())
firefox.app_path = app_path;
firefox.services_supported =
importer::HISTORY | importer::FAVORITES | importer::PASSWORDS |
importer::SEARCH_ENGINES | importer::AUTOFILL_FORM_DATA;
firefox.services_supported = importer::HISTORY | importer::FAVORITES |
importer::PASSWORDS |
importer::AUTOFILL_FORM_DATA;
firefox.locale = locale;
profiles->push_back(firefox);
}
......
......@@ -21,7 +21,6 @@
#include "components/search_engines/template_url.h"
#include "components/search_engines/template_url_parser.h"
#include "components/search_engines/template_url_prepopulate_data.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
#include "ui/base/l10n/l10n_util.h"
#include <iterator>
......@@ -64,19 +63,6 @@ history::VisitSource ConvertImporterVisitSourceToHistoryVisitSource(
namespace {
// FirefoxURLParameterFilter is used to remove parameter mentioning Firefox from
// the search URL when importing search engines.
bool FirefoxURLParameterFilter(const std::string& key,
const std::string& value) {
std::string low_value = base::ToLowerASCII(value);
if (low_value.find("mozilla") != std::string::npos ||
low_value.find("firefox") != std::string::npos ||
low_value.find("moz:") != std::string::npos) {
return false;
}
return true;
}
// Attempts to create a TemplateURL from the provided data. |title| is optional.
// If TemplateURL creation fails, returns null.
std::unique_ptr<TemplateURL> CreateTemplateURL(const base::string16& url,
......@@ -95,94 +81,6 @@ std::unique_ptr<TemplateURL> CreateTemplateURL(const base::string16& url,
} // namespace
// When the Bridge receives the search engines XML data via
// SetFirefoxSearchEnginesXMLData(), this class is responsible for managing the
// asynchronous TemplateURL parsing operations. The Bridge generally operates
// synchronously, so this class manages the state and notifies the bridge when
// parsing is done.
class InProcessImporterBridge::SearchEnginesParser {
public:
// Starts parsing the |search_engines_xml_data| and will notify |bridge|
// upon completion.
SearchEnginesParser(const std::vector<std::string>& search_engines_xml_data,
InProcessImporterBridge* bridge)
: bridge_(bridge), data_decoder_(new data_decoder::DataDecoder()) {
DCHECK(!search_engines_xml_data.empty());
StartParse(search_engines_xml_data);
}
// Returns true if all the data have been parsed, false if the operation
// is still ongoing.
bool is_done() const { return is_done_; }
// If InProcessImporterBridge::NotifyEnded() is called before is_done()
// returns true, NotifyEnded() sets this flag so that it can be called back
// to complete the import.
void set_notify_ended_on_completion() { notify_ended_on_completion_ = true; }
private:
void StartParse(const std::vector<std::string>& search_engines_xml_data) {
const auto& last_item = search_engines_xml_data.end() - 1;
TemplateURLParser::ParameterFilter param_filter =
base::BindRepeating(&FirefoxURLParameterFilter);
for (auto it = search_engines_xml_data.begin();
it != search_engines_xml_data.end(); ++it) {
// Because all TemplateURLParser are handled by the same data_decoder_
// instance, the results will be returned FIFO.
// The SearchEnginesParser is owned by the InProcessImporterBridge,
// which is not deleted until NotifyEnded() is called, so using Unretained
// is safe.
TemplateURLParser::ParseWithDataDecoder(
data_decoder_.get(), &search_terms_data_, *it, param_filter,
base::BindOnce(&SearchEnginesParser::OnURLParsed,
base::Unretained(this), it == last_item));
}
}
void OnURLParsed(bool is_last_item, std::unique_ptr<TemplateURL> url) {
if (url)
parsed_urls_.push_back(std::move(url));
if (is_last_item)
FinishParsing();
}
void FinishParsing() {
is_done_ = true;
// Shut down the DataDecoder.
data_decoder_.reset();
bridge_->WriteSearchEngines(std::move(parsed_urls_));
if (notify_ended_on_completion_)
bridge_->NotifyEnded();
}
// Storage for the URLs. These are stored in the same order as the original
// |search_engines_xml_data|.
TemplateURLService::OwnedTemplateURLVector parsed_urls_;
InProcessImporterBridge* bridge_; // Weak, owns this.
// Set to true if the last search engine has been parsed.
bool is_done_ = false;
// Set to true if the ImporterBridge has been NotifyEnded() already but was
// waiting on this class to finish the import.
bool notify_ended_on_completion_ = false;
// Parameter for TemplateURLParser.
UIThreadSearchTermsData search_terms_data_;
// The DataDecoder instance that is shared amongst all the TemplateURLs being
// parsed.
std::unique_ptr<data_decoder::DataDecoder> data_decoder_;
DISALLOW_COPY_AND_ASSIGN(SearchEnginesParser);
};
InProcessImporterBridge::InProcessImporterBridge(
ProfileWriter* writer,
base::WeakPtr<ExternalProcessImporterHost> host) : writer_(writer),
......@@ -227,14 +125,6 @@ void InProcessImporterBridge::SetKeywords(
writer_->AddKeywords(std::move(owned_template_urls), unique_on_host_and_path);
}
void InProcessImporterBridge::SetFirefoxSearchEnginesXMLData(
const std::vector<std::string>& search_engine_data) {
if (!search_engine_data.empty()) {
// SearchEnginesParser will call back the Bridge back when it is done.
search_engines_.reset(new SearchEnginesParser(search_engine_data, this));
}
}
void InProcessImporterBridge::SetPasswordForm(
const autofill::PasswordForm& form) {
writer_->AddPasswordForm(form);
......@@ -271,13 +161,6 @@ void InProcessImporterBridge::NotifyItemEnded(importer::ImportItem item) {
}
void InProcessImporterBridge::NotifyEnded() {
// If there are search engines to parse but parsing them is not yet complete,
// arrange to be called back when they are done.
if (search_engines_ && !search_engines_->is_done()) {
search_engines_->set_notify_ended_on_completion();
return;
}
host_->NotifyImportEnded();
}
......@@ -286,35 +169,3 @@ base::string16 InProcessImporterBridge::GetLocalizedString(int message_id) {
}
InProcessImporterBridge::~InProcessImporterBridge() {}
void InProcessImporterBridge::WriteSearchEngines(
TemplateURL::OwnedTemplateURLVector template_urls) {
std::map<std::string, std::unique_ptr<TemplateURL>> search_engine_for_url;
for (auto& template_url : template_urls) {
std::string key = template_url->url();
// Give priority to the latest template URL that is found, as
// GetSearchEnginesXMLFiles() returns a vector with first Firefox default
// search engines and then the user's ones. The user ones should take
// precedence.
search_engine_for_url[key] = std::move(template_url);
}
// The first URL represents the default search engine in Firefox 3, so we
// need to keep it on top of the list.
auto default_turl = search_engine_for_url.end();
if (!template_urls.empty())
default_turl = search_engine_for_url.find(template_urls[0]->url());
// Put the results in the |search_engines| vector.
TemplateURLService::OwnedTemplateURLVector search_engines;
for (auto it = search_engine_for_url.begin();
it != search_engine_for_url.end(); ++it) {
if (it == default_turl) {
search_engines.insert(search_engines.begin(),
std::move(default_turl->second));
} else {
search_engines.push_back(std::move(it->second));
}
}
writer_->AddKeywords(std::move(search_engines), true);
}
......@@ -43,9 +43,6 @@ class InProcessImporterBridge : public ImporterBridge {
const std::vector<importer::SearchEngineInfo>& search_engines,
bool unique_on_host_and_path) override;
void SetFirefoxSearchEnginesXMLData(
const std::vector<std::string>& search_engine_data) override;
void SetPasswordForm(const autofill::PasswordForm& form) override;
void SetAutofillFormData(
......@@ -60,19 +57,10 @@ class InProcessImporterBridge : public ImporterBridge {
// End ImporterBridge implementation.
private:
class SearchEnginesParser;
friend class SearchEnginesParser;
~InProcessImporterBridge() override;
// Called by the SearchEnginesParser when all the search engines have been
// parsed. The |template_urls| vector is in the same sort order that was
// passed to SetFirefoxSearchEnginesXMLData().
void WriteSearchEngines(TemplateURL::OwnedTemplateURLVector template_urls);
ProfileWriter* const writer_; // weak
const base::WeakPtr<ExternalProcessImporterHost> host_;
std::unique_ptr<SearchEnginesParser> search_engines_;
DISALLOW_COPY_AND_ASSIGN(InProcessImporterBridge);
};
......
......@@ -48,11 +48,6 @@ class ImporterBridge : public base::RefCountedThreadSafe<ImporterBridge> {
const std::vector<importer::SearchEngineInfo>& search_engines,
bool unique_on_host_and_path) = 0;
// The search_engine_data vector contains XML data retrieved from the Firefox
// profile and its sqlite db.
virtual void SetFirefoxSearchEnginesXMLData(
const std::vector<std::string>& search_engine_data) = 0;
virtual void SetPasswordForm(const autofill::PasswordForm& form) = 0;
virtual void SetAutofillFormData(
......
......@@ -27,8 +27,6 @@ class MockImporterBridge : public ImporterBridge {
void(const std::vector<ImporterURLRow>&, importer::VisitSource));
MOCK_METHOD2(SetKeywords,
void(const std::vector<importer::SearchEngineInfo>&, bool));
MOCK_METHOD1(SetFirefoxSearchEnginesXMLData,
void(const std::vector<std::string>&));
MOCK_METHOD1(SetPasswordForm, void(const autofill::PasswordForm&));
MOCK_METHOD1(SetAutofillFormData,
void(const std::vector<ImporterAutofillFormDataEntry>&));
......
......@@ -58,7 +58,6 @@ interface ProfileImportObserver {
OnKeywordsImportReady(
array<SearchEngineInfo> search_engines,
bool unique_on_host_and_path);
OnFirefoxSearchEngineDataReceived(array<string> search_engine_data);
OnAutofillFormDataImportStart(uint32 total_autofill_form_data_entry_count);
OnAutofillFormDataImportGroup(
array<ImporterAutofillFormDataEntry> autofill_form_data_entry_group);
......
......@@ -109,11 +109,6 @@ void ExternalProcessImporterBridge::SetKeywords(
observer_->OnKeywordsImportReady(search_engines, unique_on_host_and_path);
}
void ExternalProcessImporterBridge::SetFirefoxSearchEnginesXMLData(
const std::vector<std::string>& search_engine_data) {
observer_->OnFirefoxSearchEngineDataReceived(search_engine_data);
}
void ExternalProcessImporterBridge::SetPasswordForm(
const autofill::PasswordForm& form) {
observer_->OnPasswordFormImportReady(form);
......
......@@ -55,9 +55,6 @@ class ExternalProcessImporterBridge : public ImporterBridge {
const std::vector<importer::SearchEngineInfo>& search_engines,
bool unique_on_host_and_path) override;
void SetFirefoxSearchEnginesXMLData(
const std::vector<std::string>& seach_engine_data) override;
void SetPasswordForm(const autofill::PasswordForm& form) override;
void SetAutofillFormData(
......
......@@ -150,11 +150,6 @@ void FirefoxImporter::StartImport(const importer::SourceProfile& source_profile,
ImportBookmarks();
bridge_->NotifyItemEnded(importer::FAVORITES);
}
if ((items & importer::SEARCH_ENGINES) && !cancelled()) {
bridge_->NotifyItemStarted(importer::SEARCH_ENGINES);
ImportSearchEngines();
bridge_->NotifyItemEnded(importer::SEARCH_ENGINES);
}
if ((items & importer::PASSWORDS) && !cancelled()) {
bridge_->NotifyItemStarted(importer::PASSWORDS);
ImportPasswords();
......@@ -418,13 +413,6 @@ void FirefoxImporter::ImportPasswords() {
}
}
void FirefoxImporter::ImportSearchEngines() {
std::vector<std::string> search_engine_data;
GetSearchEnginesXMLData(&search_engine_data);
bridge_->SetFirefoxSearchEnginesXMLData(search_engine_data);
}
void FirefoxImporter::ImportHomepage() {
GURL home_page = GetHomepage(source_path_);
if (home_page.is_valid() && !IsDefaultHomepage(home_page, app_path_)) {
......@@ -467,215 +455,6 @@ void FirefoxImporter::ImportAutofillFormData() {
bridge_->SetAutofillFormData(form_entries);
}
void FirefoxImporter::GetSearchEnginesXMLData(
std::vector<std::string>* search_engine_data) {
base::FilePath file = GetCopiedSourcePath("search.sqlite");
if (!base::PathExists(file)) {
// Since Firefox 3.5, search engines are no longer stored in search.sqlite.
// Instead, search.json is used for storing search engines.
GetSearchEnginesXMLDataFromJSON(search_engine_data);
return;
}
sql::Database db;
if (!db.Open(file))
return;
const char query[] =
"SELECT engineid FROM engine_data "
"WHERE engineid NOT IN "
"(SELECT engineid FROM engine_data "
"WHERE name='hidden') "
"ORDER BY value ASC";
sql::Statement s(db.GetUniqueStatement(query));
if (!s.is_valid())
return;
const base::FilePath searchplugins_path(FILE_PATH_LITERAL("searchplugins"));
// Search engine definitions are XMLs stored in two directories. Default
// engines are in the app directory (app_path_) and custom engines are
// in the profile directory (source_path_).
// Since Firefox 21, app_path_ engines are in 'browser' subdirectory:
base::FilePath app_path =
app_path_.AppendASCII("browser").Append(searchplugins_path);
if (!base::PathExists(app_path)) {
// This might be an older Firefox, try old location without the 'browser'
// path component:
app_path = app_path_.Append(searchplugins_path);
}
base::FilePath profile_path = source_path_.Append(searchplugins_path);
// Firefox doesn't store a search engine in its sqlite database unless the
// user has added a engine. So we get search engines from sqlite db as well
// as from the file system.
if (s.Step()) {
const std::string kAppPrefix("[app]/");
const std::string kProfilePrefix("[profile]/");
do {
base::FilePath file;
std::string engine(s.ColumnString(0));
// The string contains [app]/<name>.xml or [profile]/<name>.xml where
// the [app] and [profile] need to be replaced with the actual app or
// profile path.
size_t index = engine.find(kAppPrefix);
if (index != std::string::npos) {
// Remove '[app]/'.
file = app_path.AppendASCII(engine.substr(index + kAppPrefix.length()));
} else if ((index = engine.find(kProfilePrefix)) != std::string::npos) {
// Remove '[profile]/'.
file = profile_path.AppendASCII(
engine.substr(index + kProfilePrefix.length()));
} else {
// Looks like absolute path to the file.
file = base::FilePath::FromUTF8Unsafe(engine);
}
std::string file_data;
base::ReadFileToString(file, &file_data);
search_engine_data->push_back(file_data);
} while (s.Step() && !cancelled());
}
#if defined(OS_POSIX)
// Ubuntu-flavored Firefox supports locale-specific search engines via
// locale-named subdirectories. They fall back to en-US.
// See http://crbug.com/53899
// TODO(jshin): we need to make sure our locale code matches that of
// Firefox.
DCHECK(!locale_.empty());
base::FilePath locale_app_path = app_path.AppendASCII(locale_);
base::FilePath default_locale_app_path = app_path.AppendASCII("en-US");
if (base::DirectoryExists(locale_app_path))
app_path = locale_app_path;
else if (base::DirectoryExists(default_locale_app_path))
app_path = default_locale_app_path;
#endif
// Get search engine definition from file system.
base::FileEnumerator engines(app_path, false, base::FileEnumerator::FILES);
for (base::FilePath engine_path = engines.Next();
!engine_path.value().empty(); engine_path = engines.Next()) {
std::string file_data;
base::ReadFileToString(file, &file_data);
search_engine_data->push_back(file_data);
}
}
void FirefoxImporter::GetSearchEnginesXMLDataFromJSON(
std::vector<std::string>* search_engine_data) {
// search-metadata.json contains keywords for search engines. This
// file exists only if the user has set keywords for search engines.
base::FilePath search_metadata_json_file =
source_path_.AppendASCII("search-metadata.json");
JSONFileValueDeserializer metadata_deserializer(search_metadata_json_file);
std::unique_ptr<base::Value> metadata_root =
metadata_deserializer.Deserialize(NULL, NULL);
const base::DictionaryValue* search_metadata_root = NULL;
if (metadata_root)
metadata_root->GetAsDictionary(&search_metadata_root);
// search.json contains information about search engines to import.
base::FilePath search_json_file = source_path_.AppendASCII("search.json");
if (!base::PathExists(search_json_file))
return;
JSONFileValueDeserializer deserializer(search_json_file);
std::unique_ptr<base::Value> root = deserializer.Deserialize(NULL, NULL);
const base::DictionaryValue* search_root = NULL;
if (!root || !root->GetAsDictionary(&search_root))
return;
const std::string kDirectories("directories");
const base::DictionaryValue* search_directories = NULL;
if (!search_root->GetDictionary(kDirectories, &search_directories))
return;
// Dictionary |search_directories| contains a list of search engines
// (default and installed). The list can be found from key <engines>
// of the dictionary. Key <engines> is a grandchild of key <directories>.
// However, key <engines> parent's key is dynamic which depends on
// operating systems. For example,
// Ubuntu (for default search engine):
// /usr/lib/firefox/distribution/searchplugins/locale/en-US
// Ubuntu (for installed search engines):
// /home/<username>/.mozilla/firefox/lcd50n4n.default/searchplugins
// Windows (for default search engine):
// C:\\Program Files (x86)\\Mozilla Firefox\\browser\\searchplugins
// Therefore, it needs to be retrieved by searching.
for (base::DictionaryValue::Iterator it(*search_directories); !it.IsAtEnd();
it.Advance()) {
// The key of |it| may contains dot (.) which cannot be used as <key>
// for retrieving <engines>. Hence, it is needed to get |it| as dictionary.
// The resulted dictionary can be used for retrieving <engines>.
const std::string kEngines("engines");
const base::DictionaryValue* search_directory = NULL;
if (!it.value().GetAsDictionary(&search_directory))
continue;
const base::ListValue* search_engines = NULL;
if (!search_directory->GetList(kEngines, &search_engines))
continue;
const std::string kFilePath("filePath");
const std::string kHidden("_hidden");
for (size_t i = 0; i < search_engines->GetSize(); ++i) {
const base::DictionaryValue* engine_info = NULL;
if (!search_engines->GetDictionary(i, &engine_info))
continue;
bool is_hidden = false;
std::string file_path;
if (!engine_info->GetBoolean(kHidden, &is_hidden) ||
!engine_info->GetString(kFilePath, &file_path))
continue;
if (!is_hidden) {
const std::string kAppPrefix("[app]/");
const std::string kProfilePrefix("[profile]/");
base::FilePath xml_file = base::FilePath::FromUTF8Unsafe(file_path);
// If |file_path| contains [app] or [profile] then they need to be
// replaced with the actual app or profile path.
size_t index = file_path.find(kAppPrefix);
if (index != std::string::npos) {
// Replace '[app]/' with actual app path.
xml_file = app_path_.AppendASCII("searchplugins").AppendASCII(
file_path.substr(index + kAppPrefix.length()));
} else if ((index = file_path.find(kProfilePrefix)) !=
std::string::npos) {
// Replace '[profile]/' with actual profile path.
xml_file = source_path_.AppendASCII("searchplugins").AppendASCII(
file_path.substr(index + kProfilePrefix.length()));
}
std::string file_data;
base::ReadFileToString(xml_file, &file_data);
// If a keyword is mentioned for this search engine, then add
// it to the XML string as an <Alias> element and use this updated
// string.
const base::DictionaryValue* search_xml_path = NULL;
if (search_metadata_root && search_metadata_root->HasKey(file_path) &&
search_metadata_root->GetDictionaryWithoutPathExpansion(
file_path, &search_xml_path)) {
std::string alias;
search_xml_path->GetString("alias", &alias);
// Add <Alias> element as the last child element.
size_t end_of_parent = file_data.find("</SearchPlugin>");
if (end_of_parent != std::string::npos && !alias.empty())
file_data.insert(end_of_parent, "<Alias>" + alias + "</Alias> \n");
}
search_engine_data->push_back(file_data);
}
}
}
}
int FirefoxImporter::LoadNodeIDByGUID(sql::Database* db,
const std::string& GUID) {
const char query[] =
......
......@@ -62,14 +62,10 @@ class FirefoxImporter : public Importer {
void ImportBookmarks();
void ImportPasswords();
void ImportHistory();
void ImportSearchEngines();
// Import the user's home page, unless it is set to default home page as
// defined in browserconfig.properties.
void ImportHomepage();
void ImportAutofillFormData();
void GetSearchEnginesXMLData(std::vector<std::string>* search_engine_data);
void GetSearchEnginesXMLDataFromJSON(
std::vector<std::string>* search_engine_data);
// The struct stores the information about a bookmark item.
struct BookmarkItem;
......
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