firefox_importer_browsertest.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/file_util.h" 6#include "base/files/file_path.h" 7#include "base/files/scoped_temp_dir.h" 8#include "base/message_loop.h" 9#include "base/path_service.h" 10#include "base/stl_util.h" 11#include "base/string16.h" 12#include "base/string_util.h" 13#include "base/utf_string_conversions.h" 14#include "chrome/browser/bookmarks/imported_bookmark_entry.h" 15#include "chrome/browser/favicon/imported_favicon_usage.h" 16#include "chrome/browser/importer/external_process_importer_host.h" 17#include "chrome/browser/importer/firefox_importer_unittest_utils.h" 18#include "chrome/browser/importer/importer_data_types.h" 19#include "chrome/browser/importer/importer_host.h" 20#include "chrome/browser/importer/importer_progress_observer.h" 21#include "chrome/browser/importer/importer_unittest_utils.h" 22#include "chrome/browser/search_engines/template_url.h" 23#include "chrome/browser/ui/browser.h" 24#include "chrome/common/chrome_paths.h" 25#include "chrome/test/base/in_process_browser_test.h" 26#include "content/public/common/password_form.h" 27#include "testing/gtest/include/gtest/gtest.h" 28 29// TODO(estade): some of these are disabled on mac. http://crbug.com/48007 30// TODO(jschuh): Disabled on Win64 build. http://crbug.com/179688 31#if defined(OS_MACOSX) || (defined(OS_WIN) && defined(ARCH_CPU_X86_64)) 32#define MAYBE_IMPORTER(x) DISABLED_##x 33#else 34#define MAYBE_IMPORTER(x) x 35#endif 36 37namespace { 38 39const BookmarkInfo kFirefox2Bookmarks[] = { 40 {true, 2, {L"Bookmarks Toolbar Folder", L"Folder"}, 41 L"On Toolbar's Subfolder", 42 "http://on.toolbar/bookmark/folder"}, 43 {true, 1, {L"Bookmarks Toolbar Folder"}, 44 L"On Bookmark Toolbar", 45 "http://on.toolbar/bookmark"}, 46 {false, 1, {L"Folder"}, 47 L"New Bookmark", 48 "http://domain/"}, 49 {false, 0, {}, 50 L"<Name>", 51 "http://domain.com/q?a=%22er%22&b=%3C%20%20%3E"}, 52 {false, 0, {}, 53 L"Google Home Page", 54 "http://www.google.com/"}, 55 {false, 0, {}, 56 L"\x4E2D\x6587", 57 "http://chinese.site.cn/path?query=1#ref"}, 58 {false, 1, {L"< > & \" ' \\ /"}, 59 L"< > & \" ' \\ /", 60 "http://g.cn/"}, 61 {false, 0, {}, 62 L"mail", 63 "mailto:username@host"}, 64}; 65 66struct PasswordInfo { 67 const char* origin; 68 const char* action; 69 const char* realm; 70 const wchar_t* username_element; 71 const wchar_t* username; 72 const wchar_t* password_element; 73 const wchar_t* password; 74 bool blacklisted; 75}; 76 77const PasswordInfo kFirefox2Passwords[] = { 78 {"https://www.google.com/", "", "https://www.google.com/", 79 L"", L"", L"", L"", true}, 80 {"http://localhost:8080/", "", "http://localhost:8080/corp.google.com", 81 L"", L"http", L"", L"Http1+1abcdefg", false}, 82 {"http://localhost:8080/", "http://localhost:8080/", "http://localhost:8080/", 83 L"loginuser", L"usr", L"loginpass", L"pwd", false}, 84 {"http://localhost:8080/", "http://localhost:8080/", "http://localhost:8080/", 85 L"loginuser", L"firefox", L"loginpass", L"firefox", false}, 86 {"http://localhost/", "", "http://localhost/", 87 L"loginuser", L"hello", L"", L"world", false}, 88}; 89 90struct KeywordInfo { 91 const wchar_t* keyword; 92 const char* url; 93}; 94 95const KeywordInfo kFirefox2Keywords[] = { 96 // Searh plugins 97 { L"amazon.com", 98 "http://www.amazon.com/exec/obidos/external-search/?field-keywords=" 99 "{searchTerms}&mode=blended" }, 100 { L"answers.com", 101 "http://www.answers.com/main/ntquery?s={searchTerms}&gwp=13" }, 102 { L"search.creativecommons.org", 103 "http://search.creativecommons.org/?q={searchTerms}" }, 104 { L"search.ebay.com", 105 "http://search.ebay.com/search/search.dll?query={searchTerms}&" 106 "MfcISAPICommand=GetResult&ht=1&ebaytag1=ebayreg&srchdesc=n&" 107 "maxRecordsReturned=300&maxRecordsPerPage=50&SortProperty=MetaEndSort" }, 108 { L"google.com", 109 "http://www.google.com/search?q={searchTerms}&ie=utf-8&oe=utf-8&aq=t" }, 110 { L"search.yahoo.com", 111 "http://search.yahoo.com/search?p={searchTerms}&ei=UTF-8" }, 112 { L"flickr.com", 113 "http://www.flickr.com/photos/tags/?q={searchTerms}" }, 114 { L"imdb.com", 115 "http://www.imdb.com/find?q={searchTerms}" }, 116 { L"webster.com", 117 "http://www.webster.com/cgi-bin/dictionary?va={searchTerms}" }, 118 // Search keywords. 119 { L"google", "http://www.google.com/" }, 120 { L"< > & \" ' \\ /", "http://g.cn/"}, 121}; 122 123const int kDefaultFirefox2KeywordIndex = 8; 124 125class FirefoxObserver : public ProfileWriter, 126 public importer::ImporterProgressObserver { 127 public: 128 FirefoxObserver() : ProfileWriter(NULL) { 129 bookmark_count_ = 0; 130 history_count_ = 0; 131 password_count_ = 0; 132 keyword_count_ = 0; 133 } 134 135 // importer::ImporterProgressObserver: 136 virtual void ImportStarted() OVERRIDE {} 137 virtual void ImportItemStarted(importer::ImportItem item) OVERRIDE {} 138 virtual void ImportItemEnded(importer::ImportItem item) OVERRIDE {} 139 virtual void ImportEnded() OVERRIDE { 140 MessageLoop::current()->Quit(); 141 EXPECT_EQ(arraysize(kFirefox2Bookmarks), bookmark_count_); 142 EXPECT_EQ(1U, history_count_); 143 EXPECT_EQ(arraysize(kFirefox2Passwords), password_count_); 144 EXPECT_EQ(arraysize(kFirefox2Keywords), keyword_count_); 145 } 146 147 virtual bool BookmarkModelIsLoaded() const OVERRIDE { 148 // Profile is ready for writing. 149 return true; 150 } 151 152 virtual bool TemplateURLServiceIsLoaded() const OVERRIDE { 153 return true; 154 } 155 156 virtual void AddPasswordForm(const content::PasswordForm& form) OVERRIDE { 157 PasswordInfo p = kFirefox2Passwords[password_count_]; 158 EXPECT_EQ(p.origin, form.origin.spec()); 159 EXPECT_EQ(p.realm, form.signon_realm); 160 EXPECT_EQ(p.action, form.action.spec()); 161 EXPECT_EQ(WideToUTF16(p.username_element), form.username_element); 162 EXPECT_EQ(WideToUTF16(p.username), form.username_value); 163 EXPECT_EQ(WideToUTF16(p.password_element), form.password_element); 164 EXPECT_EQ(WideToUTF16(p.password), form.password_value); 165 EXPECT_EQ(p.blacklisted, form.blacklisted_by_user); 166 ++password_count_; 167 } 168 169 virtual void AddHistoryPage(const history::URLRows& page, 170 history::VisitSource visit_source) OVERRIDE { 171 ASSERT_EQ(1U, page.size()); 172 EXPECT_EQ("http://en-us.www.mozilla.com/", page[0].url().spec()); 173 EXPECT_EQ(ASCIIToUTF16("Firefox Updated"), page[0].title()); 174 EXPECT_EQ(history::SOURCE_FIREFOX_IMPORTED, visit_source); 175 ++history_count_; 176 } 177 178 virtual void AddBookmarks(const std::vector<ImportedBookmarkEntry>& bookmarks, 179 const string16& top_level_folder_name) OVERRIDE { 180 ASSERT_LE(bookmark_count_ + bookmarks.size(), 181 arraysize(kFirefox2Bookmarks)); 182 // Importer should import the FF favorites the same as the list, in the same 183 // order. 184 for (size_t i = 0; i < bookmarks.size(); ++i) { 185 EXPECT_NO_FATAL_FAILURE( 186 TestEqualBookmarkEntry(bookmarks[i], 187 kFirefox2Bookmarks[bookmark_count_])) << i; 188 ++bookmark_count_; 189 } 190 } 191 192 virtual void AddKeywords(ScopedVector<TemplateURL> template_urls, 193 bool unique_on_host_and_path) OVERRIDE { 194 for (size_t i = 0; i < template_urls.size(); ++i) { 195 // The order might not be deterministic, look in the expected list for 196 // that template URL. 197 bool found = false; 198 string16 keyword = template_urls[i]->keyword(); 199 for (size_t j = 0; j < arraysize(kFirefox2Keywords); ++j) { 200 if (template_urls[i]->keyword() == 201 WideToUTF16Hack(kFirefox2Keywords[j].keyword)) { 202 EXPECT_EQ(kFirefox2Keywords[j].url, template_urls[i]->url()); 203 found = true; 204 break; 205 } 206 } 207 EXPECT_TRUE(found); 208 ++keyword_count_; 209 } 210 } 211 212 virtual void AddFavicons( 213 const std::vector<ImportedFaviconUsage>& favicons) OVERRIDE { 214 } 215 216 private: 217 virtual ~FirefoxObserver() {} 218 219 size_t bookmark_count_; 220 size_t history_count_; 221 size_t password_count_; 222 size_t keyword_count_; 223}; 224 225const BookmarkInfo kFirefox3Bookmarks[] = { 226 {true, 1, {L"Bookmarks Toolbar"}, 227 L"Toolbar", 228 "http://site/"}, 229 {false, 0, {}, 230 L"Title", 231 "http://www.google.com/"}, 232}; 233 234const PasswordInfo kFirefox3Passwords[] = { 235 {"http://localhost:8080/", "http://localhost:8080/", "http://localhost:8080/", 236 L"loginuser", L"abc", L"loginpass", L"123", false}, 237 {"http://localhost:8080/", "", "http://localhost:8080/localhost", 238 L"", L"http", L"", L"Http1+1abcdefg", false}, 239}; 240 241const KeywordInfo kFirefox3Keywords[] = { 242 { L"amazon.com", 243 "http://www.amazon.com/exec/obidos/external-search/?field-keywords=" 244 "{searchTerms}&mode=blended" }, 245 { L"answers.com", 246 "http://www.answers.com/main/ntquery?s={searchTerms}&gwp=13" }, 247 { L"search.creativecommons.org", 248 "http://search.creativecommons.org/?q={searchTerms}" }, 249 { L"search.ebay.com", 250 "http://search.ebay.com/search/search.dll?query={searchTerms}&" 251 "MfcISAPICommand=GetResult&ht=1&ebaytag1=ebayreg&srchdesc=n&" 252 "maxRecordsReturned=300&maxRecordsPerPage=50&SortProperty=MetaEndSort" }, 253 { L"google.com", 254 "http://www.google.com/search?q={searchTerms}&ie=utf-8&oe=utf-8&aq=t" }, 255 { L"en.wikipedia.org", 256 "http://en.wikipedia.org/wiki/Special:Search?search={searchTerms}" }, 257 { L"search.yahoo.com", 258 "http://search.yahoo.com/search?p={searchTerms}&ei=UTF-8" }, 259 { L"flickr.com", 260 "http://www.flickr.com/photos/tags/?q={searchTerms}" }, 261 { L"imdb.com", 262 "http://www.imdb.com/find?q={searchTerms}" }, 263 { L"webster.com", 264 "http://www.webster.com/cgi-bin/dictionary?va={searchTerms}" }, 265 // Search keywords. 266 { L"\x4E2D\x6587", "http://www.google.com/" }, 267}; 268 269const int kDefaultFirefox3KeywordIndex = 8; 270 271class Firefox3Observer : public ProfileWriter, 272 public importer::ImporterProgressObserver { 273 public: 274 Firefox3Observer() 275 : ProfileWriter(NULL), bookmark_count_(0), history_count_(0), 276 password_count_(0), keyword_count_(0), import_search_engines_(true) { 277 } 278 279 explicit Firefox3Observer(bool import_search_engines) 280 : ProfileWriter(NULL), bookmark_count_(0), history_count_(0), 281 password_count_(0), keyword_count_(0), 282 import_search_engines_(import_search_engines) { 283 } 284 285 // importer::ImporterProgressObserver: 286 virtual void ImportStarted() OVERRIDE {} 287 virtual void ImportItemStarted(importer::ImportItem item) OVERRIDE {} 288 virtual void ImportItemEnded(importer::ImportItem item) OVERRIDE {} 289 virtual void ImportEnded() OVERRIDE { 290 MessageLoop::current()->Quit(); 291 EXPECT_EQ(arraysize(kFirefox3Bookmarks), bookmark_count_); 292 EXPECT_EQ(1U, history_count_); 293 EXPECT_EQ(arraysize(kFirefox3Passwords), password_count_); 294 if (import_search_engines_) 295 EXPECT_EQ(arraysize(kFirefox3Keywords), keyword_count_); 296 } 297 298 virtual bool BookmarkModelIsLoaded() const OVERRIDE { 299 // Profile is ready for writing. 300 return true; 301 } 302 303 virtual bool TemplateURLServiceIsLoaded() const OVERRIDE { 304 return true; 305 } 306 307 virtual void AddPasswordForm(const content::PasswordForm& form) OVERRIDE { 308 PasswordInfo p = kFirefox3Passwords[password_count_]; 309 EXPECT_EQ(p.origin, form.origin.spec()); 310 EXPECT_EQ(p.realm, form.signon_realm); 311 EXPECT_EQ(p.action, form.action.spec()); 312 EXPECT_EQ(WideToUTF16(p.username_element), form.username_element); 313 EXPECT_EQ(WideToUTF16(p.username), form.username_value); 314 EXPECT_EQ(WideToUTF16(p.password_element), form.password_element); 315 EXPECT_EQ(WideToUTF16(p.password), form.password_value); 316 EXPECT_EQ(p.blacklisted, form.blacklisted_by_user); 317 ++password_count_; 318 } 319 320 virtual void AddHistoryPage(const history::URLRows& page, 321 history::VisitSource visit_source) OVERRIDE { 322 ASSERT_EQ(3U, page.size()); 323 EXPECT_EQ("http://www.google.com/", page[0].url().spec()); 324 EXPECT_EQ(ASCIIToUTF16("Google"), page[0].title()); 325 EXPECT_EQ("http://www.google.com/", page[1].url().spec()); 326 EXPECT_EQ(ASCIIToUTF16("Google"), page[1].title()); 327 EXPECT_EQ("http://www.cs.unc.edu/~jbs/resources/perl/perl-cgi/programs/" 328 "form1-POST.html", page[2].url().spec()); 329 EXPECT_EQ(ASCIIToUTF16("example form (POST)"), page[2].title()); 330 EXPECT_EQ(history::SOURCE_FIREFOX_IMPORTED, visit_source); 331 ++history_count_; 332 } 333 334 virtual void AddBookmarks(const std::vector<ImportedBookmarkEntry>& bookmarks, 335 const string16& top_level_folder_name) OVERRIDE { 336 337 ASSERT_LE(bookmark_count_ + bookmarks.size(), 338 arraysize(kFirefox3Bookmarks)); 339 // Importer should import the FF favorites the same as the list, in the same 340 // order. 341 for (size_t i = 0; i < bookmarks.size(); ++i) { 342 EXPECT_NO_FATAL_FAILURE( 343 TestEqualBookmarkEntry(bookmarks[i], 344 kFirefox3Bookmarks[bookmark_count_])) << i; 345 ++bookmark_count_; 346 } 347 } 348 349 virtual void AddKeywords(ScopedVector<TemplateURL> template_urls, 350 bool unique_on_host_and_path) OVERRIDE { 351 for (size_t i = 0; i < template_urls.size(); ++i) { 352 // The order might not be deterministic, look in the expected list for 353 // that template URL. 354 bool found = false; 355 string16 keyword = template_urls[i]->keyword(); 356 for (size_t j = 0; j < arraysize(kFirefox3Keywords); ++j) { 357 if (template_urls[i]->keyword() == 358 WideToUTF16Hack(kFirefox3Keywords[j].keyword)) { 359 EXPECT_EQ(kFirefox3Keywords[j].url, template_urls[i]->url()); 360 found = true; 361 break; 362 } 363 } 364 EXPECT_TRUE(found); 365 ++keyword_count_; 366 } 367 } 368 369 virtual void AddFavicons( 370 const std::vector<ImportedFaviconUsage>& favicons) OVERRIDE { 371 } 372 373 private: 374 virtual ~Firefox3Observer() {} 375 376 size_t bookmark_count_; 377 size_t history_count_; 378 size_t password_count_; 379 size_t keyword_count_; 380 bool import_search_engines_; 381}; 382 383} // namespace 384 385// These tests need to be browser tests in order to be able to run the OOP 386// import (via ExternalProcessImporterHost) which launches a utility process on 387// supported platforms. 388class FirefoxProfileImporterBrowserTest : public InProcessBrowserTest { 389 protected: 390 virtual void SetUp() OVERRIDE { 391 // Creates a new profile in a new subdirectory in the temp directory. 392 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 393 base::FilePath test_path = temp_dir_.path().AppendASCII("ImporterTest"); 394 file_util::Delete(test_path, true); 395 file_util::CreateDirectory(test_path); 396 profile_path_ = test_path.AppendASCII("profile"); 397 app_path_ = test_path.AppendASCII("app"); 398 file_util::CreateDirectory(app_path_); 399 400 // This will launch the browser test and thus needs to happen last. 401 InProcessBrowserTest::SetUp(); 402 } 403 404 void Firefox3xImporterBrowserTest( 405 std::string profile_dir, 406 importer::ImporterProgressObserver* observer, 407 ProfileWriter* writer, 408 bool import_search_plugins) { 409 base::FilePath data_path; 410 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 411 data_path = data_path.AppendASCII(profile_dir); 412 ASSERT_TRUE(file_util::CopyDirectory(data_path, profile_path_, true)); 413 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 414 data_path = data_path.AppendASCII("firefox3_nss"); 415 ASSERT_TRUE(file_util::CopyDirectory(data_path, profile_path_, false)); 416 417 base::FilePath search_engine_path = app_path_; 418 search_engine_path = search_engine_path.AppendASCII("searchplugins"); 419 file_util::CreateDirectory(search_engine_path); 420 if (import_search_plugins) { 421 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 422 data_path = data_path.AppendASCII("firefox3_searchplugins"); 423 if (!file_util::PathExists(data_path)) { 424 // TODO(maruel): Create search test data that we can open source! 425 LOG(ERROR) << L"Missing internal test data"; 426 return; 427 } 428 ASSERT_TRUE(file_util::CopyDirectory(data_path, 429 search_engine_path, false)); 430 } 431 432 importer::SourceProfile source_profile; 433 source_profile.importer_type = importer::TYPE_FIREFOX3; 434 source_profile.app_path = app_path_; 435 source_profile.source_path = profile_path_; 436 437 int items = importer::HISTORY | importer::PASSWORDS | importer::FAVORITES; 438 if (import_search_plugins) 439 items = items | importer::SEARCH_ENGINES; 440 441 // Deletes itself. 442 // TODO(gab): Use ExternalProcessImporterHost on both Windows and Linux. 443 ImporterHost* host; 444#if defined(OS_MACOSX) 445 host = new ExternalProcessImporterHost; 446#else 447 host = new ImporterHost; 448#endif 449 host->SetObserver(observer); 450 host->StartImportSettings(source_profile, browser()->profile(), 451 items, make_scoped_refptr(writer)); 452 MessageLoop::current()->Run(); 453 } 454 455 base::ScopedTempDir temp_dir_; 456 base::FilePath profile_path_; 457 base::FilePath app_path_; 458}; 459 460IN_PROC_BROWSER_TEST_F(FirefoxProfileImporterBrowserTest, 461 MAYBE_IMPORTER(Firefox2Importer)) { 462 base::FilePath data_path; 463 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 464 data_path = data_path.AppendASCII("firefox2_profile"); 465 ASSERT_TRUE(file_util::CopyDirectory(data_path, profile_path_, true)); 466 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 467 data_path = data_path.AppendASCII("firefox2_nss"); 468 ASSERT_TRUE(file_util::CopyDirectory(data_path, profile_path_, false)); 469 470 base::FilePath search_engine_path = app_path_; 471 search_engine_path = search_engine_path.AppendASCII("searchplugins"); 472 file_util::CreateDirectory(search_engine_path); 473 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 474 data_path = data_path.AppendASCII("firefox2_searchplugins"); 475 if (!file_util::PathExists(data_path)) { 476 // TODO(maruel): Create test data that we can open source! 477 LOG(ERROR) << L"Missing internal test data"; 478 return; 479 } 480 ASSERT_TRUE(file_util::CopyDirectory(data_path, search_engine_path, false)); 481 482 importer::SourceProfile source_profile; 483 source_profile.importer_type = importer::TYPE_FIREFOX2; 484 source_profile.app_path = app_path_; 485 source_profile.source_path = profile_path_; 486 487 // Deletes itself. 488 // TODO(gab): Use ExternalProcessImporterHost on both Windows and Linux. 489 ImporterHost* host; 490#if defined(OS_MACOSX) 491 host = new ExternalProcessImporterHost; 492#else 493 host = new ImporterHost; 494#endif 495 496 FirefoxObserver* observer = new FirefoxObserver(); 497 host->SetObserver(observer); 498 host->StartImportSettings( 499 source_profile, 500 browser()->profile(), 501 importer::HISTORY | importer::PASSWORDS | 502 importer::FAVORITES | importer::SEARCH_ENGINES, 503 make_scoped_refptr(observer)); 504 MessageLoop::current()->Run(); 505} 506 507IN_PROC_BROWSER_TEST_F(FirefoxProfileImporterBrowserTest, 508 MAYBE_IMPORTER(Firefox30Importer)) { 509 scoped_refptr<Firefox3Observer> observer(new Firefox3Observer()); 510 Firefox3xImporterBrowserTest("firefox3_profile", observer.get(), 511 observer.get(), true); 512} 513 514IN_PROC_BROWSER_TEST_F(FirefoxProfileImporterBrowserTest, 515 MAYBE_IMPORTER(Firefox35Importer)) { 516 bool import_search_engines = false; 517 scoped_refptr<Firefox3Observer> observer( 518 new Firefox3Observer(import_search_engines)); 519 Firefox3xImporterBrowserTest("firefox35_profile", observer.get(), 520 observer.get(), import_search_engines); 521} 522