1 // Copyright (c) 2011 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 "build/build_config.h" 6 7 #if defined(OS_WIN) 8 // The order of these includes is important. 9 #include <windows.h> 10 #include <unknwn.h> 11 #include <intshcut.h> 12 #include <pstore.h> 13 #include <urlhist.h> 14 #include <shlguid.h> 15 #endif 16 17 #include <vector> 18 19 #include "app/win/scoped_com_initializer.h" 20 #include "base/compiler_specific.h" 21 #include "base/file_util.h" 22 #include "base/message_loop.h" 23 #include "base/path_service.h" 24 #include "base/stl_util-inl.h" 25 #include "base/string_util.h" 26 #include "base/utf_string_conversions.h" 27 #include "base/memory/scoped_temp_dir.h" 28 #include "chrome/browser/history/history_types.h" 29 #include "chrome/browser/importer/importer_bridge.h" 30 #include "chrome/browser/importer/importer_data_types.h" 31 #include "chrome/browser/importer/importer_host.h" 32 #include "chrome/browser/importer/importer_progress_observer.h" 33 #include "chrome/browser/search_engines/template_url.h" 34 #include "chrome/common/chrome_paths.h" 35 #include "content/browser/browser_thread.h" 36 #include "testing/gtest/include/gtest/gtest.h" 37 #include "webkit/glue/password_form.h" 38 39 #if defined(OS_WIN) 40 #include "base/win/scoped_comptr.h" 41 #include "base/win/windows_version.h" 42 #include "chrome/browser/importer/ie_importer.h" 43 #include "chrome/browser/password_manager/ie7_password.h" 44 #endif 45 46 // TODO(estade): some of these are disabled on mac. http://crbug.com/48007 47 #if defined(OS_MACOSX) 48 #define MAYBE(x) DISABLED_##x 49 #else 50 #define MAYBE(x) x 51 #endif 52 53 class ImporterTest : public testing::Test { 54 public: 55 ImporterTest() 56 : ui_thread_(BrowserThread::UI, &message_loop_), 57 file_thread_(BrowserThread::FILE, &message_loop_) {} 58 59 protected: 60 virtual void SetUp() { 61 // Creates a new profile in a new subdirectory in the temp directory. 62 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 63 FilePath test_path = temp_dir_.path().AppendASCII("ImporterTest"); 64 file_util::Delete(test_path, true); 65 file_util::CreateDirectory(test_path); 66 profile_path_ = test_path.AppendASCII("profile"); 67 app_path_ = test_path.AppendASCII("app"); 68 file_util::CreateDirectory(app_path_); 69 } 70 71 void Firefox3xImporterTest(std::string profile_dir, 72 importer::ImporterProgressObserver* observer, 73 ProfileWriter* writer, 74 bool import_search_plugins) { 75 FilePath data_path; 76 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 77 data_path = data_path.AppendASCII(profile_dir); 78 ASSERT_TRUE(file_util::CopyDirectory(data_path, profile_path_, true)); 79 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 80 data_path = data_path.AppendASCII("firefox3_nss"); 81 ASSERT_TRUE(file_util::CopyDirectory(data_path, profile_path_, false)); 82 83 FilePath search_engine_path = app_path_; 84 search_engine_path = search_engine_path.AppendASCII("searchplugins"); 85 file_util::CreateDirectory(search_engine_path); 86 if (import_search_plugins) { 87 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 88 data_path = data_path.AppendASCII("firefox3_searchplugins"); 89 if (!file_util::PathExists(data_path)) { 90 // TODO(maruel): Create search test data that we can open source! 91 LOG(ERROR) << L"Missing internal test data"; 92 return; 93 } 94 ASSERT_TRUE(file_util::CopyDirectory(data_path, 95 search_engine_path, false)); 96 } 97 98 MessageLoop* loop = MessageLoop::current(); 99 importer::SourceProfile source_profile; 100 source_profile.importer_type = importer::FIREFOX3; 101 source_profile.app_path = app_path_; 102 source_profile.source_path = profile_path_; 103 scoped_refptr<ImporterHost> host(new ImporterHost); 104 host->SetObserver(observer); 105 int items = importer::HISTORY | importer::PASSWORDS | importer::FAVORITES; 106 if (import_search_plugins) 107 items = items | importer::SEARCH_ENGINES; 108 loop->PostTask(FROM_HERE, NewRunnableMethod(host.get(), 109 &ImporterHost::StartImportSettings, source_profile, 110 static_cast<Profile*>(NULL), items, make_scoped_refptr(writer), true)); 111 loop->Run(); 112 } 113 114 ScopedTempDir temp_dir_; 115 MessageLoopForUI message_loop_; 116 BrowserThread ui_thread_; 117 BrowserThread file_thread_; 118 FilePath profile_path_; 119 FilePath app_path_; 120 }; 121 122 const int kMaxPathSize = 5; 123 124 struct BookmarkList { 125 const bool in_toolbar; 126 const size_t path_size; 127 const wchar_t* path[kMaxPathSize]; 128 const wchar_t* title; 129 const char* url; 130 }; 131 132 // Returns true if the |entry| is in the |list|. 133 bool FindBookmarkEntry(const ProfileWriter::BookmarkEntry& entry, 134 const BookmarkList* list, int list_size) { 135 for (int i = 0; i < list_size; ++i) { 136 if (list[i].in_toolbar == entry.in_toolbar && 137 list[i].path_size == entry.path.size() && 138 list[i].url == entry.url.spec() && 139 WideToUTF16Hack(list[i].title) == entry.title) { 140 bool equal = true; 141 for (size_t k = 0; k < list[i].path_size; ++k) 142 if (WideToUTF16Hack(list[i].path[k]) != entry.path[k]) { 143 equal = false; 144 break; 145 } 146 147 if (equal) 148 return true; 149 } 150 } 151 return false; 152 } 153 154 #if defined(OS_WIN) 155 static const BookmarkList kIEBookmarks[] = { 156 {true, 0, {}, 157 L"TheLink", 158 "http://www.links-thelink.com/"}, 159 {true, 1, {L"SubFolderOfLinks"}, 160 L"SubLink", 161 "http://www.links-sublink.com/"}, 162 {false, 0, {}, 163 L"Google Home Page", 164 "http://www.google.com/"}, 165 {false, 0, {}, 166 L"TheLink", 167 "http://www.links-thelink.com/"}, 168 {false, 1, {L"SubFolder"}, 169 L"Title", 170 "http://www.link.com/"}, 171 {false, 0, {}, 172 L"WithPortAndQuery", 173 "http://host:8080/cgi?q=query"}, 174 {false, 1, {L"a"}, 175 L"\x4E2D\x6587", 176 "http://chinese-title-favorite/"}, 177 }; 178 179 static const wchar_t* kIEIdentifyUrl = 180 L"http://A79029D6-753E-4e27-B807-3D46AB1545DF.com:8080/path?key=value"; 181 static const wchar_t* kIEIdentifyTitle = 182 L"Unittest GUID"; 183 184 class TestObserver : public ProfileWriter, 185 public importer::ImporterProgressObserver { 186 public: 187 TestObserver() : ProfileWriter(NULL) { 188 bookmark_count_ = 0; 189 history_count_ = 0; 190 password_count_ = 0; 191 } 192 193 // importer::ImporterProgressObserver: 194 virtual void ImportStarted() OVERRIDE {} 195 virtual void ImportItemStarted(importer::ImportItem item) OVERRIDE {} 196 virtual void ImportItemEnded(importer::ImportItem item) OVERRIDE {} 197 virtual void ImportEnded() OVERRIDE { 198 MessageLoop::current()->Quit(); 199 EXPECT_EQ(arraysize(kIEBookmarks), bookmark_count_); 200 EXPECT_EQ(1, history_count_); 201 #if 0 // This part of the test is disabled. See bug #2466 202 if (base::win::GetVersion() >= base::win::VERSION_VISTA) 203 EXPECT_EQ(0, password_count_); 204 else 205 EXPECT_EQ(1, password_count_); 206 #endif 207 } 208 209 virtual bool BookmarkModelIsLoaded() const { 210 // Profile is ready for writing. 211 return true; 212 } 213 214 virtual bool TemplateURLModelIsLoaded() const { 215 return true; 216 } 217 218 virtual void AddPasswordForm(const webkit_glue::PasswordForm& form) { 219 // Importer should obtain this password form only. 220 EXPECT_EQ(GURL("http://localhost:8080/security/index.htm"), form.origin); 221 EXPECT_EQ("http://localhost:8080/", form.signon_realm); 222 EXPECT_EQ(L"user", form.username_element); 223 EXPECT_EQ(L"1", form.username_value); 224 EXPECT_EQ(L"", form.password_element); 225 EXPECT_EQ(L"2", form.password_value); 226 EXPECT_EQ("", form.action.spec()); 227 ++password_count_; 228 } 229 230 virtual void AddHistoryPage(const std::vector<history::URLRow>& page, 231 history::VisitSource visit_source) { 232 // Importer should read the specified URL. 233 for (size_t i = 0; i < page.size(); ++i) { 234 if (page[i].title() == kIEIdentifyTitle && 235 page[i].url() == GURL(kIEIdentifyUrl)) 236 ++history_count_; 237 } 238 EXPECT_EQ(history::SOURCE_IE_IMPORTED, visit_source); 239 } 240 241 virtual void AddBookmarkEntry(const std::vector<BookmarkEntry>& bookmark, 242 const string16& first_folder_name, 243 int options) { 244 // Importer should import the IE Favorites folder the same as the list. 245 for (size_t i = 0; i < bookmark.size(); ++i) { 246 if (FindBookmarkEntry(bookmark[i], kIEBookmarks, 247 arraysize(kIEBookmarks))) 248 ++bookmark_count_; 249 } 250 } 251 252 virtual void AddKeyword(std::vector<TemplateURL*> template_url, 253 int default_keyword_index) { 254 // TODO(jcampan): bug 1169230: we should test keyword importing for IE. 255 // In order to do that we'll probably need to mock the Windows registry. 256 NOTREACHED(); 257 STLDeleteContainerPointers(template_url.begin(), template_url.end()); 258 } 259 260 private: 261 ~TestObserver() {} 262 263 size_t bookmark_count_; 264 size_t history_count_; 265 size_t password_count_; 266 }; 267 268 bool CreateUrlFile(std::wstring file, std::wstring url) { 269 base::win::ScopedComPtr<IUniformResourceLocator> locator; 270 HRESULT result = locator.CreateInstance(CLSID_InternetShortcut, NULL, 271 CLSCTX_INPROC_SERVER); 272 if (FAILED(result)) 273 return false; 274 base::win::ScopedComPtr<IPersistFile> persist_file; 275 result = persist_file.QueryFrom(locator); 276 if (FAILED(result)) 277 return false; 278 result = locator->SetURL(url.c_str(), 0); 279 if (FAILED(result)) 280 return false; 281 result = persist_file->Save(file.c_str(), TRUE); 282 if (FAILED(result)) 283 return false; 284 return true; 285 } 286 287 void ClearPStoreType(IPStore* pstore, const GUID* type, const GUID* subtype) { 288 base::win::ScopedComPtr<IEnumPStoreItems, NULL> item; 289 HRESULT result = pstore->EnumItems(0, type, subtype, 0, item.Receive()); 290 if (result == PST_E_OK) { 291 wchar_t* item_name; 292 while (SUCCEEDED(item->Next(1, &item_name, 0))) { 293 pstore->DeleteItem(0, type, subtype, item_name, NULL, 0); 294 CoTaskMemFree(item_name); 295 } 296 } 297 pstore->DeleteSubtype(0, type, subtype, 0); 298 pstore->DeleteType(0, type, 0); 299 } 300 301 void WritePStore(IPStore* pstore, const GUID* type, const GUID* subtype) { 302 struct PStoreItem { 303 wchar_t* name; 304 int data_size; 305 char* data; 306 } items[] = { 307 {L"http://localhost:8080/security/index.htm#ref:StringData", 8, 308 "\x31\x00\x00\x00\x32\x00\x00\x00"}, 309 {L"http://localhost:8080/security/index.htm#ref:StringIndex", 20, 310 "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00" 311 "\x00\x00\x2f\x00\x74\x00\x01\x00\x00\x00"}, 312 {L"user:StringData", 4, 313 "\x31\x00\x00\x00"}, 314 {L"user:StringIndex", 20, 315 "\x57\x49\x43\x4b\x18\x00\x00\x00\x01\x00" 316 "\x00\x00\x2f\x00\x74\x00\x00\x00\x00\x00"}, 317 }; 318 319 for (int i = 0; i < arraysize(items); ++i) { 320 HRESULT res = pstore->WriteItem(0, type, subtype, items[i].name, 321 items[i].data_size, reinterpret_cast<BYTE*>(items[i].data), 322 NULL, 0, 0); 323 ASSERT_TRUE(res == PST_E_OK); 324 } 325 } 326 327 TEST_F(ImporterTest, IEImporter) { 328 // Sets up a favorites folder. 329 app::win::ScopedCOMInitializer com_init; 330 std::wstring path = temp_dir_.path().AppendASCII("Favorites").value(); 331 CreateDirectory(path.c_str(), NULL); 332 CreateDirectory((path + L"\\SubFolder").c_str(), NULL); 333 CreateDirectory((path + L"\\Links").c_str(), NULL); 334 CreateDirectory((path + L"\\Links\\SubFolderOfLinks").c_str(), NULL); 335 CreateDirectory((path + L"\\\x0061").c_str(), NULL); 336 ASSERT_TRUE(CreateUrlFile(path + L"\\Google Home Page.url", 337 L"http://www.google.com/")); 338 ASSERT_TRUE(CreateUrlFile(path + L"\\SubFolder\\Title.url", 339 L"http://www.link.com/")); 340 ASSERT_TRUE(CreateUrlFile(path + L"\\TheLink.url", 341 L"http://www.links-thelink.com/")); 342 ASSERT_TRUE(CreateUrlFile(path + L"\\WithPortAndQuery.url", 343 L"http://host:8080/cgi?q=query")); 344 ASSERT_TRUE(CreateUrlFile(path + L"\\\x0061\\\x4E2D\x6587.url", 345 L"http://chinese-title-favorite/")); 346 ASSERT_TRUE(CreateUrlFile(path + L"\\Links\\TheLink.url", 347 L"http://www.links-thelink.com/")); 348 ASSERT_TRUE(CreateUrlFile(path + L"\\Links\\SubFolderOfLinks\\SubLink.url", 349 L"http://www.links-sublink.com/")); 350 file_util::WriteFile(path + L"\\InvalidUrlFile.url", "x", 1); 351 file_util::WriteFile(path + L"\\PlainTextFile.txt", "x", 1); 352 353 // Sets up dummy password data. 354 HRESULT res; 355 #if 0 // This part of the test is disabled. See bug #2466 356 base::win::ScopedComPtr<IPStore> pstore; 357 HMODULE pstorec_dll; 358 GUID type = IEImporter::kUnittestGUID; 359 GUID subtype = IEImporter::kUnittestGUID; 360 // PStore is read-only in Windows Vista. 361 if (base::win::GetVersion() < base::win::VERSION_VISTA) { 362 typedef HRESULT (WINAPI *PStoreCreateFunc)(IPStore**, DWORD, DWORD, DWORD); 363 pstorec_dll = LoadLibrary(L"pstorec.dll"); 364 PStoreCreateFunc PStoreCreateInstance = 365 (PStoreCreateFunc)GetProcAddress(pstorec_dll, "PStoreCreateInstance"); 366 res = PStoreCreateInstance(pstore.Receive(), 0, 0, 0); 367 ASSERT_TRUE(res == S_OK); 368 ClearPStoreType(pstore, &type, &subtype); 369 PST_TYPEINFO type_info; 370 type_info.szDisplayName = L"TestType"; 371 type_info.cbSize = 8; 372 pstore->CreateType(0, &type, &type_info, 0); 373 pstore->CreateSubtype(0, &type, &subtype, &type_info, NULL, 0); 374 WritePStore(pstore, &type, &subtype); 375 } 376 #endif 377 378 // Sets up a special history link. 379 base::win::ScopedComPtr<IUrlHistoryStg2> url_history_stg2; 380 res = url_history_stg2.CreateInstance(CLSID_CUrlHistory, NULL, 381 CLSCTX_INPROC_SERVER); 382 ASSERT_TRUE(res == S_OK); 383 res = url_history_stg2->AddUrl(kIEIdentifyUrl, kIEIdentifyTitle, 0); 384 ASSERT_TRUE(res == S_OK); 385 386 // Starts to import the above settings. 387 MessageLoop* loop = MessageLoop::current(); 388 scoped_refptr<ImporterHost> host(new ImporterHost); 389 390 TestObserver* observer = new TestObserver(); 391 host->SetObserver(observer); 392 importer::SourceProfile source_profile; 393 source_profile.importer_type = importer::MS_IE; 394 source_profile.source_path = temp_dir_.path(); 395 396 loop->PostTask(FROM_HERE, NewRunnableMethod(host.get(), 397 &ImporterHost::StartImportSettings, 398 source_profile, 399 static_cast<Profile*>(NULL), 400 importer::HISTORY | importer::PASSWORDS | importer::FAVORITES, 401 observer, 402 true)); 403 loop->Run(); 404 405 // Cleans up. 406 url_history_stg2->DeleteUrl(kIEIdentifyUrl, 0); 407 url_history_stg2.Release(); 408 #if 0 // This part of the test is disabled. See bug #2466 409 if (base::win::GetVersion() < base::win::VERSION_VISTA) { 410 ClearPStoreType(pstore, &type, &subtype); 411 // Releases it befor unload the dll. 412 pstore.Release(); 413 FreeLibrary(pstorec_dll); 414 } 415 #endif 416 } 417 418 TEST_F(ImporterTest, IE7Importer) { 419 // This is the unencrypted values of my keys under Storage2. 420 // The passwords have been manually changed to abcdef... but the size remains 421 // the same. 422 unsigned char data1[] = "\x0c\x00\x00\x00\x38\x00\x00\x00\x2c\x00\x00\x00" 423 "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00" 424 "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00" 425 "\x00\x00\x00\x00\x4e\xfa\x67\x76\x22\x94\xc8\x01" 426 "\x08\x00\x00\x00\x12\x00\x00\x00\x4e\xfa\x67\x76" 427 "\x22\x94\xc8\x01\x0c\x00\x00\x00\x61\x00\x62\x00" 428 "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00" 429 "\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00" 430 "\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00" 431 "\x6c\x00\x00\x00"; 432 433 unsigned char data2[] = "\x0c\x00\x00\x00\x38\x00\x00\x00\x24\x00\x00\x00" 434 "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00" 435 "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00" 436 "\x00\x00\x00\x00\xa8\xea\xf4\xe5\x9f\x9a\xc8\x01" 437 "\x09\x00\x00\x00\x14\x00\x00\x00\xa8\xea\xf4\xe5" 438 "\x9f\x9a\xc8\x01\x07\x00\x00\x00\x61\x00\x62\x00" 439 "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00" 440 "\x69\x00\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00" 441 "\x65\x00\x66\x00\x67\x00\x00\x00"; 442 443 444 445 std::vector<unsigned char> decrypted_data1; 446 decrypted_data1.resize(arraysize(data1)); 447 memcpy(&decrypted_data1.front(), data1, sizeof(data1)); 448 449 std::vector<unsigned char> decrypted_data2; 450 decrypted_data2.resize(arraysize(data2)); 451 memcpy(&decrypted_data2.front(), data2, sizeof(data2)); 452 453 std::wstring password; 454 std::wstring username; 455 ASSERT_TRUE(ie7_password::GetUserPassFromData(decrypted_data1, &username, 456 &password)); 457 EXPECT_EQ(L"abcdefgh", username); 458 EXPECT_EQ(L"abcdefghijkl", password); 459 460 ASSERT_TRUE(ie7_password::GetUserPassFromData(decrypted_data2, &username, 461 &password)); 462 EXPECT_EQ(L"abcdefghi", username); 463 EXPECT_EQ(L"abcdefg", password); 464 } 465 #endif // defined(OS_WIN) 466 467 static const BookmarkList kFirefox2Bookmarks[] = { 468 {true, 1, {L"Folder"}, 469 L"On Toolbar's Subfolder", 470 "http://on.toolbar/bookmark/folder"}, 471 {true, 0, {}, 472 L"On Bookmark Toolbar", 473 "http://on.toolbar/bookmark"}, 474 {false, 1, {L"Folder"}, 475 L"New Bookmark", 476 "http://domain/"}, 477 {false, 0, {}, 478 L"<Name>", 479 "http://domain.com/q?a=%22er%22&b=%3C%20%20%3E"}, 480 {false, 0, {}, 481 L"Google Home Page", 482 "http://www.google.com/"}, 483 {false, 0, {}, 484 L"\x4E2D\x6587", 485 "http://chinese.site.cn/path?query=1#ref"}, 486 {false, 0, {}, 487 L"mail", 488 "mailto:username@host"}, 489 }; 490 491 struct PasswordList { 492 const char* origin; 493 const char* action; 494 const char* realm; 495 const wchar_t* username_element; 496 const wchar_t* username; 497 const wchar_t* password_element; 498 const wchar_t* password; 499 bool blacklisted; 500 }; 501 502 static const PasswordList kFirefox2Passwords[] = { 503 {"https://www.google.com/", "", "https://www.google.com/", 504 L"", L"", L"", L"", true}, 505 {"http://localhost:8080/", "", "http://localhost:8080/corp.google.com", 506 L"", L"http", L"", L"Http1+1abcdefg", false}, 507 {"http://localhost:8080/", "http://localhost:8080/", "http://localhost:8080/", 508 L"loginuser", L"usr", L"loginpass", L"pwd", false}, 509 {"http://localhost:8080/", "http://localhost:8080/", "http://localhost:8080/", 510 L"loginuser", L"firefox", L"loginpass", L"firefox", false}, 511 {"http://localhost/", "", "http://localhost/", 512 L"loginuser", L"hello", L"", L"world", false}, 513 }; 514 515 struct KeywordList { 516 const wchar_t* keyword; 517 const char* url; 518 }; 519 520 static const KeywordList kFirefox2Keywords[] = { 521 // Searh plugins 522 { L"amazon.com", 523 "http://www.amazon.com/exec/obidos/external-search/?field-keywords=" 524 "{searchTerms}&mode=blended" }, 525 { L"answers.com", 526 "http://www.answers.com/main/ntquery?s={searchTerms}&gwp=13" }, 527 { L"search.creativecommons.org", 528 "http://search.creativecommons.org/?q={searchTerms}" }, 529 { L"search.ebay.com", 530 "http://search.ebay.com/search/search.dll?query={searchTerms}&" 531 "MfcISAPICommand=GetResult&ht=1&ebaytag1=ebayreg&srchdesc=n&" 532 "maxRecordsReturned=300&maxRecordsPerPage=50&SortProperty=MetaEndSort" }, 533 { L"google.com", 534 "http://www.google.com/search?q={searchTerms}&ie=utf-8&oe=utf-8&aq=t" }, 535 { L"search.yahoo.com", 536 "http://search.yahoo.com/search?p={searchTerms}&ei=UTF-8" }, 537 { L"flickr.com", 538 "http://www.flickr.com/photos/tags/?q={searchTerms}" }, 539 { L"imdb.com", 540 "http://www.imdb.com/find?q={searchTerms}" }, 541 { L"webster.com", 542 "http://www.webster.com/cgi-bin/dictionary?va={searchTerms}" }, 543 // Search keywords. 544 { L"google", "http://www.google.com/" }, 545 { L"< > & \" ' \\ /", "http://g.cn/"}, 546 }; 547 548 static const int kDefaultFirefox2KeywordIndex = 8; 549 550 class FirefoxObserver : public ProfileWriter, 551 public importer::ImporterProgressObserver { 552 public: 553 FirefoxObserver() : ProfileWriter(NULL) { 554 bookmark_count_ = 0; 555 history_count_ = 0; 556 password_count_ = 0; 557 keyword_count_ = 0; 558 } 559 560 // importer::ImporterProgressObserver: 561 virtual void ImportStarted() OVERRIDE {} 562 virtual void ImportItemStarted(importer::ImportItem item) OVERRIDE {} 563 virtual void ImportItemEnded(importer::ImportItem item) OVERRIDE {} 564 virtual void ImportEnded() OVERRIDE { 565 MessageLoop::current()->Quit(); 566 EXPECT_EQ(arraysize(kFirefox2Bookmarks), bookmark_count_); 567 EXPECT_EQ(1U, history_count_); 568 EXPECT_EQ(arraysize(kFirefox2Passwords), password_count_); 569 EXPECT_EQ(arraysize(kFirefox2Keywords), keyword_count_); 570 EXPECT_EQ(kFirefox2Keywords[kDefaultFirefox2KeywordIndex].keyword, 571 default_keyword_); 572 EXPECT_EQ(kFirefox2Keywords[kDefaultFirefox2KeywordIndex].url, 573 default_keyword_url_); 574 } 575 576 virtual bool BookmarkModelIsLoaded() const { 577 // Profile is ready for writing. 578 return true; 579 } 580 581 virtual bool TemplateURLModelIsLoaded() const { 582 return true; 583 } 584 585 virtual void AddPasswordForm(const webkit_glue::PasswordForm& form) { 586 PasswordList p = kFirefox2Passwords[password_count_]; 587 EXPECT_EQ(p.origin, form.origin.spec()); 588 EXPECT_EQ(p.realm, form.signon_realm); 589 EXPECT_EQ(p.action, form.action.spec()); 590 EXPECT_EQ(WideToUTF16(p.username_element), form.username_element); 591 EXPECT_EQ(WideToUTF16(p.username), form.username_value); 592 EXPECT_EQ(WideToUTF16(p.password_element), form.password_element); 593 EXPECT_EQ(WideToUTF16(p.password), form.password_value); 594 EXPECT_EQ(p.blacklisted, form.blacklisted_by_user); 595 ++password_count_; 596 } 597 598 virtual void AddHistoryPage(const std::vector<history::URLRow>& page, 599 history::VisitSource visit_source) { 600 ASSERT_EQ(1U, page.size()); 601 EXPECT_EQ("http://en-us.www.mozilla.com/", page[0].url().spec()); 602 EXPECT_EQ(ASCIIToUTF16("Firefox Updated"), page[0].title()); 603 EXPECT_EQ(history::SOURCE_FIREFOX_IMPORTED, visit_source); 604 ++history_count_; 605 } 606 607 virtual void AddBookmarkEntry(const std::vector<BookmarkEntry>& bookmark, 608 const string16& first_folder_name, 609 int options) { 610 for (size_t i = 0; i < bookmark.size(); ++i) { 611 if (FindBookmarkEntry(bookmark[i], kFirefox2Bookmarks, 612 arraysize(kFirefox2Bookmarks))) 613 ++bookmark_count_; 614 } 615 } 616 617 virtual void AddKeywords(const std::vector<TemplateURL*>& template_urls, 618 int default_keyword_index, 619 bool unique_on_host_and_path) { 620 for (size_t i = 0; i < template_urls.size(); ++i) { 621 // The order might not be deterministic, look in the expected list for 622 // that template URL. 623 bool found = false; 624 string16 keyword = template_urls[i]->keyword(); 625 for (size_t j = 0; j < arraysize(kFirefox2Keywords); ++j) { 626 if (template_urls[i]->keyword() == 627 WideToUTF16Hack(kFirefox2Keywords[j].keyword)) { 628 EXPECT_EQ(kFirefox2Keywords[j].url, template_urls[i]->url()->url()); 629 found = true; 630 break; 631 } 632 } 633 EXPECT_TRUE(found); 634 ++keyword_count_; 635 } 636 637 if (default_keyword_index != -1) { 638 EXPECT_LT(default_keyword_index, static_cast<int>(template_urls.size())); 639 TemplateURL* default_turl = template_urls[default_keyword_index]; 640 default_keyword_ = UTF16ToWideHack(default_turl->keyword()); 641 default_keyword_url_ = default_turl->url()->url(); 642 } 643 644 STLDeleteContainerPointers(template_urls.begin(), template_urls.end()); 645 } 646 647 void AddFavicons(const std::vector<history::ImportedFaviconUsage>& favicons) { 648 } 649 650 private: 651 ~FirefoxObserver() {} 652 653 size_t bookmark_count_; 654 size_t history_count_; 655 size_t password_count_; 656 size_t keyword_count_; 657 std::wstring default_keyword_; 658 std::string default_keyword_url_; 659 }; 660 661 TEST_F(ImporterTest, MAYBE(Firefox2Importer)) { 662 FilePath data_path; 663 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 664 data_path = data_path.AppendASCII("firefox2_profile"); 665 ASSERT_TRUE(file_util::CopyDirectory(data_path, profile_path_, true)); 666 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 667 data_path = data_path.AppendASCII("firefox2_nss"); 668 ASSERT_TRUE(file_util::CopyDirectory(data_path, profile_path_, false)); 669 670 FilePath search_engine_path = app_path_; 671 search_engine_path = search_engine_path.AppendASCII("searchplugins"); 672 file_util::CreateDirectory(search_engine_path); 673 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 674 data_path = data_path.AppendASCII("firefox2_searchplugins"); 675 if (!file_util::PathExists(data_path)) { 676 // TODO(maruel): Create test data that we can open source! 677 LOG(ERROR) << L"Missing internal test data"; 678 return; 679 } 680 ASSERT_TRUE(file_util::CopyDirectory(data_path, search_engine_path, false)); 681 682 MessageLoop* loop = MessageLoop::current(); 683 scoped_refptr<ImporterHost> host(new ImporterHost); 684 FirefoxObserver* observer = new FirefoxObserver(); 685 host->SetObserver(observer); 686 importer::SourceProfile source_profile; 687 source_profile.importer_type = importer::FIREFOX2; 688 source_profile.app_path = app_path_; 689 source_profile.source_path = profile_path_; 690 691 loop->PostTask(FROM_HERE, NewRunnableMethod( 692 host.get(), 693 &ImporterHost::StartImportSettings, 694 source_profile, 695 static_cast<Profile*>(NULL), 696 importer::HISTORY | importer::PASSWORDS | 697 importer::FAVORITES | importer::SEARCH_ENGINES, 698 make_scoped_refptr(observer), 699 true)); 700 loop->Run(); 701 } 702 703 static const BookmarkList kFirefox3Bookmarks[] = { 704 {true, 0, {}, 705 L"Toolbar", 706 "http://site/"}, 707 {false, 0, {}, 708 L"Title", 709 "http://www.google.com/"}, 710 }; 711 712 static const PasswordList kFirefox3Passwords[] = { 713 {"http://localhost:8080/", "http://localhost:8080/", "http://localhost:8080/", 714 L"loginuser", L"abc", L"loginpass", L"123", false}, 715 {"http://localhost:8080/", "", "http://localhost:8080/localhost", 716 L"", L"http", L"", L"Http1+1abcdefg", false}, 717 }; 718 719 static const KeywordList kFirefox3Keywords[] = { 720 { L"amazon.com", 721 "http://www.amazon.com/exec/obidos/external-search/?field-keywords=" 722 "{searchTerms}&mode=blended" }, 723 { L"answers.com", 724 "http://www.answers.com/main/ntquery?s={searchTerms}&gwp=13" }, 725 { L"search.creativecommons.org", 726 "http://search.creativecommons.org/?q={searchTerms}" }, 727 { L"search.ebay.com", 728 "http://search.ebay.com/search/search.dll?query={searchTerms}&" 729 "MfcISAPICommand=GetResult&ht=1&ebaytag1=ebayreg&srchdesc=n&" 730 "maxRecordsReturned=300&maxRecordsPerPage=50&SortProperty=MetaEndSort" }, 731 { L"google.com", 732 "http://www.google.com/search?q={searchTerms}&ie=utf-8&oe=utf-8&aq=t" }, 733 { L"en.wikipedia.org", 734 "http://en.wikipedia.org/wiki/Special:Search?search={searchTerms}" }, 735 { L"search.yahoo.com", 736 "http://search.yahoo.com/search?p={searchTerms}&ei=UTF-8" }, 737 { L"flickr.com", 738 "http://www.flickr.com/photos/tags/?q={searchTerms}" }, 739 { L"imdb.com", 740 "http://www.imdb.com/find?q={searchTerms}" }, 741 { L"webster.com", 742 "http://www.webster.com/cgi-bin/dictionary?va={searchTerms}" }, 743 // Search keywords. 744 { L"\x4E2D\x6587", "http://www.google.com/" }, 745 }; 746 747 static const int kDefaultFirefox3KeywordIndex = 8; 748 749 class Firefox3Observer : public ProfileWriter, 750 public importer::ImporterProgressObserver { 751 public: 752 Firefox3Observer() 753 : ProfileWriter(NULL), bookmark_count_(0), history_count_(0), 754 password_count_(0), keyword_count_(0), import_search_engines_(true) { 755 } 756 757 explicit Firefox3Observer(bool import_search_engines) 758 : ProfileWriter(NULL), bookmark_count_(0), history_count_(0), 759 password_count_(0), keyword_count_(0), 760 import_search_engines_(import_search_engines) { 761 } 762 763 // importer::ImporterProgressObserver: 764 virtual void ImportStarted() OVERRIDE {} 765 virtual void ImportItemStarted(importer::ImportItem item) OVERRIDE {} 766 virtual void ImportItemEnded(importer::ImportItem item) OVERRIDE {} 767 virtual void ImportEnded() OVERRIDE { 768 MessageLoop::current()->Quit(); 769 EXPECT_EQ(arraysize(kFirefox3Bookmarks), bookmark_count_); 770 EXPECT_EQ(1U, history_count_); 771 EXPECT_EQ(arraysize(kFirefox3Passwords), password_count_); 772 if (import_search_engines_) { 773 EXPECT_EQ(arraysize(kFirefox3Keywords), keyword_count_); 774 EXPECT_EQ(kFirefox3Keywords[kDefaultFirefox3KeywordIndex].keyword, 775 default_keyword_); 776 EXPECT_EQ(kFirefox3Keywords[kDefaultFirefox3KeywordIndex].url, 777 default_keyword_url_); 778 } 779 } 780 781 virtual bool BookmarkModelIsLoaded() const { 782 // Profile is ready for writing. 783 return true; 784 } 785 786 virtual bool TemplateURLModelIsLoaded() const { 787 return true; 788 } 789 790 virtual void AddPasswordForm(const webkit_glue::PasswordForm& form) { 791 PasswordList p = kFirefox3Passwords[password_count_]; 792 EXPECT_EQ(p.origin, form.origin.spec()); 793 EXPECT_EQ(p.realm, form.signon_realm); 794 EXPECT_EQ(p.action, form.action.spec()); 795 EXPECT_EQ(WideToUTF16(p.username_element), form.username_element); 796 EXPECT_EQ(WideToUTF16(p.username), form.username_value); 797 EXPECT_EQ(WideToUTF16(p.password_element), form.password_element); 798 EXPECT_EQ(WideToUTF16(p.password), form.password_value); 799 EXPECT_EQ(p.blacklisted, form.blacklisted_by_user); 800 ++password_count_; 801 } 802 803 virtual void AddHistoryPage(const std::vector<history::URLRow>& page, 804 history::VisitSource visit_source) { 805 ASSERT_EQ(3U, page.size()); 806 EXPECT_EQ("http://www.google.com/", page[0].url().spec()); 807 EXPECT_EQ(ASCIIToUTF16("Google"), page[0].title()); 808 EXPECT_EQ("http://www.google.com/", page[1].url().spec()); 809 EXPECT_EQ(ASCIIToUTF16("Google"), page[1].title()); 810 EXPECT_EQ("http://www.cs.unc.edu/~jbs/resources/perl/perl-cgi/programs/form1-POST.html", 811 page[2].url().spec()); 812 EXPECT_EQ(ASCIIToUTF16("example form (POST)"), page[2].title()); 813 EXPECT_EQ(history::SOURCE_FIREFOX_IMPORTED, visit_source); 814 ++history_count_; 815 } 816 817 virtual void AddBookmarkEntry(const std::vector<BookmarkEntry>& bookmark, 818 const string16& first_folder_name, 819 int options) { 820 for (size_t i = 0; i < bookmark.size(); ++i) { 821 if (FindBookmarkEntry(bookmark[i], kFirefox3Bookmarks, 822 arraysize(kFirefox3Bookmarks))) 823 ++bookmark_count_; 824 } 825 } 826 827 void AddKeywords(const std::vector<TemplateURL*>& template_urls, 828 int default_keyword_index, 829 bool unique_on_host_and_path) { 830 for (size_t i = 0; i < template_urls.size(); ++i) { 831 // The order might not be deterministic, look in the expected list for 832 // that template URL. 833 bool found = false; 834 string16 keyword = template_urls[i]->keyword(); 835 for (size_t j = 0; j < arraysize(kFirefox3Keywords); ++j) { 836 if (template_urls[i]->keyword() == 837 WideToUTF16Hack(kFirefox3Keywords[j].keyword)) { 838 EXPECT_EQ(kFirefox3Keywords[j].url, template_urls[i]->url()->url()); 839 found = true; 840 break; 841 } 842 } 843 EXPECT_TRUE(found); 844 ++keyword_count_; 845 } 846 847 if (default_keyword_index != -1) { 848 EXPECT_LT(default_keyword_index, static_cast<int>(template_urls.size())); 849 TemplateURL* default_turl = template_urls[default_keyword_index]; 850 default_keyword_ = UTF16ToWideHack(default_turl->keyword()); 851 default_keyword_url_ = default_turl->url()->url(); 852 } 853 854 STLDeleteContainerPointers(template_urls.begin(), template_urls.end()); 855 } 856 857 void AddFavicons(const std::vector<history::ImportedFaviconUsage>& favicons) { 858 } 859 860 private: 861 ~Firefox3Observer() {} 862 863 size_t bookmark_count_; 864 size_t history_count_; 865 size_t password_count_; 866 size_t keyword_count_; 867 bool import_search_engines_; 868 std::wstring default_keyword_; 869 std::string default_keyword_url_; 870 }; 871 872 TEST_F(ImporterTest, MAYBE(Firefox30Importer)) { 873 scoped_refptr<Firefox3Observer> observer(new Firefox3Observer()); 874 Firefox3xImporterTest("firefox3_profile", observer.get(), observer.get(), 875 true); 876 } 877 878 TEST_F(ImporterTest, MAYBE(Firefox35Importer)) { 879 bool import_search_engines = false; 880 scoped_refptr<Firefox3Observer> observer( 881 new Firefox3Observer(import_search_engines)); 882 Firefox3xImporterTest("firefox35_profile", observer.get(), observer.get(), 883 import_search_engines); 884 } 885