• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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