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