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:
ImporterTest()55 ImporterTest()
56 : ui_thread_(BrowserThread::UI, &message_loop_),
57 file_thread_(BrowserThread::FILE, &message_loop_) {}
58
59 protected:
SetUp()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
Firefox3xImporterTest(std::string profile_dir,importer::ImporterProgressObserver * observer,ProfileWriter * writer,bool import_search_plugins)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|.
FindBookmarkEntry(const ProfileWriter::BookmarkEntry & entry,const BookmarkList * list,int list_size)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:
TestObserver()187 TestObserver() : ProfileWriter(NULL) {
188 bookmark_count_ = 0;
189 history_count_ = 0;
190 password_count_ = 0;
191 }
192
193 // importer::ImporterProgressObserver:
ImportStarted()194 virtual void ImportStarted() OVERRIDE {}
ImportItemStarted(importer::ImportItem item)195 virtual void ImportItemStarted(importer::ImportItem item) OVERRIDE {}
ImportItemEnded(importer::ImportItem item)196 virtual void ImportItemEnded(importer::ImportItem item) OVERRIDE {}
ImportEnded()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
BookmarkModelIsLoaded() const209 virtual bool BookmarkModelIsLoaded() const {
210 // Profile is ready for writing.
211 return true;
212 }
213
TemplateURLModelIsLoaded() const214 virtual bool TemplateURLModelIsLoaded() const {
215 return true;
216 }
217
AddPasswordForm(const webkit_glue::PasswordForm & form)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
AddHistoryPage(const std::vector<history::URLRow> & page,history::VisitSource visit_source)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
AddBookmarkEntry(const std::vector<BookmarkEntry> & bookmark,const string16 & first_folder_name,int options)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
AddKeyword(std::vector<TemplateURL * > template_url,int default_keyword_index)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:
~TestObserver()261 ~TestObserver() {}
262
263 size_t bookmark_count_;
264 size_t history_count_;
265 size_t password_count_;
266 };
267
CreateUrlFile(std::wstring file,std::wstring url)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
ClearPStoreType(IPStore * pstore,const GUID * type,const GUID * subtype)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
WritePStore(IPStore * pstore,const GUID * type,const GUID * subtype)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
TEST_F(ImporterTest,IEImporter)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
TEST_F(ImporterTest,IE7Importer)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:
FirefoxObserver()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:
ImportStarted()561 virtual void ImportStarted() OVERRIDE {}
ImportItemStarted(importer::ImportItem item)562 virtual void ImportItemStarted(importer::ImportItem item) OVERRIDE {}
ImportItemEnded(importer::ImportItem item)563 virtual void ImportItemEnded(importer::ImportItem item) OVERRIDE {}
ImportEnded()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
BookmarkModelIsLoaded() const576 virtual bool BookmarkModelIsLoaded() const {
577 // Profile is ready for writing.
578 return true;
579 }
580
TemplateURLModelIsLoaded() const581 virtual bool TemplateURLModelIsLoaded() const {
582 return true;
583 }
584
AddPasswordForm(const webkit_glue::PasswordForm & form)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
AddHistoryPage(const std::vector<history::URLRow> & page,history::VisitSource visit_source)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
AddBookmarkEntry(const std::vector<BookmarkEntry> & bookmark,const string16 & first_folder_name,int options)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
AddKeywords(const std::vector<TemplateURL * > & template_urls,int default_keyword_index,bool unique_on_host_and_path)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
AddFavicons(const std::vector<history::ImportedFaviconUsage> & favicons)647 void AddFavicons(const std::vector<history::ImportedFaviconUsage>& favicons) {
648 }
649
650 private:
~FirefoxObserver()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
TEST_F(ImporterTest,MAYBE (Firefox2Importer))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:
Firefox3Observer()752 Firefox3Observer()
753 : ProfileWriter(NULL), bookmark_count_(0), history_count_(0),
754 password_count_(0), keyword_count_(0), import_search_engines_(true) {
755 }
756
Firefox3Observer(bool import_search_engines)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:
ImportStarted()764 virtual void ImportStarted() OVERRIDE {}
ImportItemStarted(importer::ImportItem item)765 virtual void ImportItemStarted(importer::ImportItem item) OVERRIDE {}
ImportItemEnded(importer::ImportItem item)766 virtual void ImportItemEnded(importer::ImportItem item) OVERRIDE {}
ImportEnded()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
BookmarkModelIsLoaded() const781 virtual bool BookmarkModelIsLoaded() const {
782 // Profile is ready for writing.
783 return true;
784 }
785
TemplateURLModelIsLoaded() const786 virtual bool TemplateURLModelIsLoaded() const {
787 return true;
788 }
789
AddPasswordForm(const webkit_glue::PasswordForm & form)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
AddHistoryPage(const std::vector<history::URLRow> & page,history::VisitSource visit_source)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
AddBookmarkEntry(const std::vector<BookmarkEntry> & bookmark,const string16 & first_folder_name,int options)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
AddKeywords(const std::vector<TemplateURL * > & template_urls,int default_keyword_index,bool unique_on_host_and_path)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
AddFavicons(const std::vector<history::ImportedFaviconUsage> & favicons)857 void AddFavicons(const std::vector<history::ImportedFaviconUsage>& favicons) {
858 }
859
860 private:
~Firefox3Observer()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
TEST_F(ImporterTest,MAYBE (Firefox30Importer))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
TEST_F(ImporterTest,MAYBE (Firefox35Importer))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