blob: f2f9dcc5a3f3f148485b0082ce8943362dd07d65 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/importer/external_process_importer_host.h"
#include "chrome/browser/importer/importer_progress_observer.h"
#include "chrome/browser/importer/importer_unittest_utils.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/importer/imported_bookmark_entry.h"
#include "chrome/common/importer/importer_data_types.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/autofill/core/browser/webdata/autofill_entry.h"
#include "components/autofill/core/common/password_form.h"
#include "components/favicon_base/favicon_usage_data.h"
#include "components/search_engines/template_url.h"
#include "testing/gtest/include/gtest/gtest.h"
// TODO(estade): some of these are disabled on mac. http://crbug.com/48007
// TODO(jschuh): Disabled on Win64 build. http://crbug.com/179688
#if defined(OS_MACOSX) || (defined(OS_WIN) && defined(ARCH_CPU_X86_64))
#define MAYBE_IMPORTER(x) DISABLED_##x
#else
#define MAYBE_IMPORTER(x) x
#endif
// TODO(kszatan): Disabled all tests on old profiles. http://crbug.com/592239
#undef MAYBE_IMPORTER
#define MAYBE_IMPORTER(x) DISABLED_##x
namespace {
struct PasswordInfo {
const char* origin;
const char* action;
const char* realm;
const char* username_element;
const char* username;
const char* password_element;
const char* password;
bool blacklisted;
};
struct KeywordInfo {
const wchar_t* keyword_in_sqlite;
const wchar_t* keyword_in_json;
const char* url;
};
struct AutofillFormDataInfo {
const char* name;
const char* value;
};
const BookmarkInfo kFirefoxBookmarks[] = {
{true, 1, {"Bookmarks Toolbar"},
L"Toolbar",
"http://site/"},
{false, 0, {},
L"Title",
"http://www.google.com/"},
};
const PasswordInfo kFirefoxPasswords[] = {
{"http://blacklist.com/", "", "http://blacklist.com/",
"", "", "", "", true},
{"http://localhost:8080/", "http://localhost:8080/", "http://localhost:8080/",
"loginuser", "abc", "loginpass", "123", false},
{"http://localhost:8080/", "", "http://localhost:8080/localhost",
"", "http", "", "Http1+1abcdefg", false},
{"http://server.com:1234/", "", "http://server.com:1234/http_realm",
"loginuser", "user", "loginpass", "password", false},
{"http://server.com:4321/", "", "http://server.com:4321/http_realm",
"loginuser", "user", "loginpass", "", false},
{"http://server.com:4321/", "", "http://server.com:4321/http_realm",
"loginuser", "", "loginpass", "password", false},
};
const KeywordInfo kFirefoxKeywords[] = {
{L"amazon.com", L"amazon.com",
"http://www.amazon.com/exec/obidos/external-search/?field-keywords="
"{searchTerms}&mode=blended"},
{L"answers.com", L"answers.com",
"http://www.answers.com/main/ntquery?s={searchTerms}&gwp=13"},
{L"search.creativecommons.org", L"search.creativecommons.org",
"http://search.creativecommons.org/?q={searchTerms}"},
{L"search.ebay.com", L"search.ebay.com",
"http://search.ebay.com/search/search.dll?query={searchTerms}&"
"MfcISAPICommand=GetResult&ht=1&ebaytag1=ebayreg&srchdesc=n&"
"maxRecordsReturned=300&maxRecordsPerPage=50&SortProperty=MetaEndSort"},
{L"google.com", L"google.com",
"http://www.google.com/search?q={searchTerms}&ie=utf-8&oe=utf-8&aq=t"},
{L"en.wikipedia.org", L"wiki",
"http://en.wikipedia.org/wiki/Special:Search?search={searchTerms}"},
{L"search.yahoo.com", L"search.yahoo.com",
"http://search.yahoo.com/search?p={searchTerms}&ei=UTF-8"},
{L"flickr.com", L"flickr.com",
"http://www.flickr.com/photos/tags/?q={searchTerms}"},
{L"imdb.com", L"imdb.com", "http://www.imdb.com/find?q={searchTerms}"},
{L"webster.com", L"webster.com",
"http://www.webster.com/cgi-bin/dictionary?va={searchTerms}"},
// Search keywords.
{L"\x4E2D\x6587", L"\x4E2D\x6587", "http://www.google.com/"},
{L"keyword", L"keyword", "http://example.{searchTerms}.com/"},
// in_process_importer_bridge.cc:CreateTemplateURL() will return NULL for
// the following bookmark. Consequently, it won't be imported as a search
// engine.
{L"", L"", "http://%x.example.{searchTerms}.com/"},
};
const AutofillFormDataInfo kFirefoxAutofillEntries[] = {
{"name", "John"},
{"address", "#123 Cherry Ave"},
{"city", "Mountain View"},
{"zip", "94043"},
{"n300", "+1 (408) 871-4567"},
{"name", "john"},
{"name", "aguantó"},
{"address", "télévision@example.com"},
{"city", "&$%$$$ TESTO *&*&^&^& MOKO"},
{"zip", "WOHOOOO$$$$$$$$****"},
{"n300", "\xe0\xa4\x9f\xe2\x97\x8c\xe0\xa4\xbe\xe0\xa4\xaf\xe0\xa4\xb0"},
{"n300", "\xe4\xbb\xa5\xe7\x8e\xa9\xe4\xb8\xba\xe4\xb8\xbb"}
};
class FirefoxObserver : public ProfileWriter,
public importer::ImporterProgressObserver {
public:
explicit FirefoxObserver(bool use_keyword_in_json)
: ProfileWriter(NULL),
bookmark_count_(0),
history_count_(0),
password_count_(0),
keyword_count_(0),
use_keyword_in_json_(use_keyword_in_json) {}
// importer::ImporterProgressObserver:
void ImportStarted() override {}
void ImportItemStarted(importer::ImportItem item) override {}
void ImportItemEnded(importer::ImportItem item) override {}
void ImportEnded() override {
base::RunLoop::QuitCurrentWhenIdleDeprecated();
EXPECT_EQ(arraysize(kFirefoxBookmarks), bookmark_count_);
EXPECT_EQ(1U, history_count_);
EXPECT_EQ(arraysize(kFirefoxPasswords), password_count_);
// The following test case from |kFirefoxKeywords| won't be imported:
// "http://%x.example.{searchTerms}.com/"}.
// Hence, value of |keyword_count_| should be lower than size of
// |kFirefoxKeywords| by 1.
EXPECT_EQ(arraysize(kFirefoxKeywords) - 1, keyword_count_);
}
bool BookmarkModelIsLoaded() const override {
// Profile is ready for writing.
return true;
}
bool TemplateURLServiceIsLoaded() const override { return true; }
void AddPasswordForm(const autofill::PasswordForm& form) override {
PasswordInfo p = kFirefoxPasswords[password_count_];
EXPECT_EQ(p.origin, form.origin.spec());
EXPECT_EQ(p.realm, form.signon_realm);
EXPECT_EQ(p.action, form.action.spec());
EXPECT_EQ(base::ASCIIToUTF16(p.username_element), form.username_element);
EXPECT_EQ(base::ASCIIToUTF16(p.username), form.username_value);
EXPECT_EQ(base::ASCIIToUTF16(p.password_element), form.password_element);
EXPECT_EQ(base::ASCIIToUTF16(p.password), form.password_value);
EXPECT_EQ(p.blacklisted, form.blacklisted_by_user);
++password_count_;
}
void AddHistoryPage(const history::URLRows& page,
history::VisitSource visit_source) override {
ASSERT_EQ(3U, page.size());
EXPECT_EQ("http://www.google.com/", page[0].url().spec());
EXPECT_EQ(base::ASCIIToUTF16("Google"), page[0].title());
EXPECT_EQ("http://www.google.com/", page[1].url().spec());
EXPECT_EQ(base::ASCIIToUTF16("Google"), page[1].title());
EXPECT_EQ("http://www.cs.unc.edu/~jbs/resources/perl/perl-cgi/programs/"
"form1-POST.html", page[2].url().spec());
EXPECT_EQ(base::ASCIIToUTF16("example form (POST)"), page[2].title());
EXPECT_EQ(history::SOURCE_FIREFOX_IMPORTED, visit_source);
++history_count_;
}
void AddBookmarks(const std::vector<ImportedBookmarkEntry>& bookmarks,
const base::string16& top_level_folder_name) override {
ASSERT_LE(bookmark_count_ + bookmarks.size(), arraysize(kFirefoxBookmarks));
// Importer should import the FF favorites the same as the list, in the same
// order.
for (size_t i = 0; i < bookmarks.size(); ++i) {
EXPECT_NO_FATAL_FAILURE(
TestEqualBookmarkEntry(bookmarks[i],
kFirefoxBookmarks[bookmark_count_])) << i;
++bookmark_count_;
}
}
void AddAutofillFormDataEntries(
const std::vector<autofill::AutofillEntry>& autofill_entries) override {
EXPECT_EQ(arraysize(kFirefoxAutofillEntries), autofill_entries.size());
for (size_t i = 0; i < arraysize(kFirefoxAutofillEntries); ++i) {
EXPECT_EQ(kFirefoxAutofillEntries[i].name,
base::UTF16ToUTF8(autofill_entries[i].key().name()));
EXPECT_EQ(kFirefoxAutofillEntries[i].value,
base::UTF16ToUTF8(autofill_entries[i].key().value()));
}
}
void AddKeywords(TemplateURLService::OwnedTemplateURLVector template_urls,
bool unique_on_host_and_path) override {
for (const auto& turl : template_urls) {
// The order might not be deterministic, look in the expected list for
// that template URL.
bool found = false;
const base::string16& imported_keyword = turl->keyword();
for (const auto& keyword : kFirefoxKeywords) {
const base::string16 expected_keyword =
base::WideToUTF16(use_keyword_in_json_ ? keyword.keyword_in_json
: keyword.keyword_in_sqlite);
if (imported_keyword == expected_keyword) {
EXPECT_EQ(keyword.url, turl->url());
found = true;
break;
}
}
EXPECT_TRUE(found);
++keyword_count_;
}
}
void AddFavicons(
const favicon_base::FaviconUsageDataList& favicons) override {}
private:
~FirefoxObserver() override {}
size_t bookmark_count_;
size_t history_count_;
size_t password_count_;
size_t keyword_count_;
// Newer versions of Firefox can store custom keyword names in json, which
// override the sqlite values. To be able to test both older and newer
// versions, tests set this variable to indicate whether to expect the
// |keyword_in_sqlite| or |keyword_in_json| values from the reference data.
bool use_keyword_in_json_;
};
} // namespace
// These tests need to be browser tests in order to be able to run the OOP
// import (via ExternalProcessImporterHost) which launches a utility process on
// supported platforms.
class FirefoxProfileImporterBrowserTest : public InProcessBrowserTest {
protected:
void SetUp() override {
// Creates a new profile in a new subdirectory in the temp directory.
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
base::FilePath test_path = temp_dir_.GetPath().AppendASCII("ImporterTest");
base::DeleteFile(test_path, true);
base::CreateDirectory(test_path);
profile_path_ = test_path.AppendASCII("profile");
app_path_ = test_path.AppendASCII("app");
base::CreateDirectory(app_path_);
// This will launch the browser test and thus needs to happen last.
InProcessBrowserTest::SetUp();
}
void FirefoxImporterBrowserTest(std::string profile_dir,
importer::ImporterProgressObserver* observer,
ProfileWriter* writer) {
base::FilePath data_path;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path));
data_path = data_path.AppendASCII(profile_dir);
ASSERT_TRUE(base::CopyDirectory(data_path, profile_path_, true));
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path));
data_path = data_path.AppendASCII("firefox3_nss");
ASSERT_TRUE(base::CopyDirectory(data_path, profile_path_, false));
// Create a directory to house default search engines.
base::FilePath default_search_engine_path =
app_path_.AppendASCII("searchplugins");
base::CreateDirectory(default_search_engine_path);
// Create a directory to house custom/installed search engines.
base::FilePath custom_search_engine_path =
profile_path_.AppendASCII("searchplugins");
base::CreateDirectory(custom_search_engine_path);
// Copy over search engines.
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path));
data_path = data_path.AppendASCII("firefox_searchplugins");
base::FilePath default_search_engine_source_path =
data_path.AppendASCII("default");
base::FilePath custom_search_engine_source_path =
data_path.AppendASCII("custom");
ASSERT_TRUE(base::CopyDirectory(
default_search_engine_source_path, default_search_engine_path, false));
ASSERT_TRUE(base::CopyDirectory(
custom_search_engine_source_path, custom_search_engine_path, false));
importer::SourceProfile source_profile;
source_profile.importer_type = importer::TYPE_FIREFOX;
source_profile.app_path = app_path_;
source_profile.source_path = profile_path_;
source_profile.locale = "en-US";
int items = importer::HISTORY | importer::PASSWORDS | importer::FAVORITES |
importer::SEARCH_ENGINES | importer::AUTOFILL_FORM_DATA;
// Deletes itself.
ExternalProcessImporterHost* host = new ExternalProcessImporterHost;
host->set_observer(observer);
host->StartImportSettings(
source_profile, browser()->profile(), items, writer);
base::RunLoop().Run();
}
base::ScopedTempDir temp_dir_;
base::FilePath profile_path_;
base::FilePath app_path_;
};
IN_PROC_BROWSER_TEST_F(FirefoxProfileImporterBrowserTest,
MAYBE_IMPORTER(Firefox30Importer)) {
scoped_refptr<FirefoxObserver> observer(new FirefoxObserver(false));
FirefoxImporterBrowserTest(
"firefox3_profile", observer.get(), observer.get());
}
IN_PROC_BROWSER_TEST_F(FirefoxProfileImporterBrowserTest,
MAYBE_IMPORTER(Firefox35Importer)) {
scoped_refptr<FirefoxObserver> observer(new FirefoxObserver(false));
FirefoxImporterBrowserTest(
"firefox35_profile", observer.get(), observer.get());
}
IN_PROC_BROWSER_TEST_F(FirefoxProfileImporterBrowserTest,
MAYBE_IMPORTER(FirefoxImporter)) {
scoped_refptr<FirefoxObserver> observer(new FirefoxObserver(true));
FirefoxImporterBrowserTest("firefox_profile", observer.get(), observer.get());
}
IN_PROC_BROWSER_TEST_F(FirefoxProfileImporterBrowserTest,
MAYBE_IMPORTER(Firefox320Importer)) {
scoped_refptr<FirefoxObserver> observer(new FirefoxObserver(true));
FirefoxImporterBrowserTest("firefox320_profile", observer.get(),
observer.get());
}