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/message_loop.h" 9 #include "base/path_service.h" 10 #include "base/stl_util.h" 11 #include "base/strings/string16.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "chrome/browser/importer/external_process_importer_host.h" 15 #include "chrome/browser/importer/importer_progress_observer.h" 16 #include "chrome/browser/importer/importer_unittest_utils.h" 17 #include "chrome/browser/search_engines/template_url.h" 18 #include "chrome/browser/ui/browser.h" 19 #include "chrome/common/chrome_paths.h" 20 #include "chrome/common/importer/imported_bookmark_entry.h" 21 #include "chrome/common/importer/imported_favicon_usage.h" 22 #include "chrome/common/importer/importer_data_types.h" 23 #include "chrome/test/base/in_process_browser_test.h" 24 #include "content/public/common/password_form.h" 25 #include "testing/gtest/include/gtest/gtest.h" 26 27 // TODO(estade): some of these are disabled on mac. http://crbug.com/48007 28 // TODO(jschuh): Disabled on Win64 build. http://crbug.com/179688 29 #if defined(OS_MACOSX) || (defined(OS_WIN) && defined(ARCH_CPU_X86_64)) 30 #define MAYBE_IMPORTER(x) DISABLED_##x 31 #else 32 #define MAYBE_IMPORTER(x) x 33 #endif 34 35 namespace { 36 37 struct PasswordInfo { 38 const char* origin; 39 const char* action; 40 const char* realm; 41 const wchar_t* username_element; 42 const wchar_t* username; 43 const wchar_t* password_element; 44 const wchar_t* password; 45 bool blacklisted; 46 }; 47 48 struct KeywordInfo { 49 const wchar_t* keyword; 50 const char* url; 51 }; 52 53 const BookmarkInfo kFirefoxBookmarks[] = { 54 {true, 1, {L"Bookmarks Toolbar"}, 55 L"Toolbar", 56 "http://site/"}, 57 {false, 0, {}, 58 L"Title", 59 "http://www.google.com/"}, 60 }; 61 62 const PasswordInfo kFirefoxPasswords[] = { 63 {"http://localhost:8080/", "http://localhost:8080/", "http://localhost:8080/", 64 L"loginuser", L"abc", L"loginpass", L"123", false}, 65 {"http://localhost:8080/", "", "http://localhost:8080/localhost", 66 L"", L"http", L"", L"Http1+1abcdefg", false}, 67 }; 68 69 const KeywordInfo kFirefoxKeywords[] = { 70 { L"amazon.com", 71 "http://www.amazon.com/exec/obidos/external-search/?field-keywords=" 72 "{searchTerms}&mode=blended" }, 73 { L"answers.com", 74 "http://www.answers.com/main/ntquery?s={searchTerms}&gwp=13" }, 75 { L"search.creativecommons.org", 76 "http://search.creativecommons.org/?q={searchTerms}" }, 77 { L"search.ebay.com", 78 "http://search.ebay.com/search/search.dll?query={searchTerms}&" 79 "MfcISAPICommand=GetResult&ht=1&ebaytag1=ebayreg&srchdesc=n&" 80 "maxRecordsReturned=300&maxRecordsPerPage=50&SortProperty=MetaEndSort" }, 81 { L"google.com", 82 "http://www.google.com/search?q={searchTerms}&ie=utf-8&oe=utf-8&aq=t" }, 83 { L"en.wikipedia.org", 84 "http://en.wikipedia.org/wiki/Special:Search?search={searchTerms}" }, 85 { L"search.yahoo.com", 86 "http://search.yahoo.com/search?p={searchTerms}&ei=UTF-8" }, 87 { L"flickr.com", 88 "http://www.flickr.com/photos/tags/?q={searchTerms}" }, 89 { L"imdb.com", 90 "http://www.imdb.com/find?q={searchTerms}" }, 91 { L"webster.com", 92 "http://www.webster.com/cgi-bin/dictionary?va={searchTerms}" }, 93 // Search keywords. 94 { L"\x4E2D\x6587", "http://www.google.com/" }, 95 }; 96 97 const int kDefaultFirefoxKeywordIndex = 8; 98 99 class FirefoxObserver : public ProfileWriter, 100 public importer::ImporterProgressObserver { 101 public: 102 FirefoxObserver() 103 : ProfileWriter(NULL), bookmark_count_(0), history_count_(0), 104 password_count_(0), keyword_count_(0), import_search_engines_(true) { 105 } 106 107 explicit FirefoxObserver(bool import_search_engines) 108 : ProfileWriter(NULL), bookmark_count_(0), history_count_(0), 109 password_count_(0), keyword_count_(0), 110 import_search_engines_(import_search_engines) { 111 } 112 113 // importer::ImporterProgressObserver: 114 virtual void ImportStarted() OVERRIDE {} 115 virtual void ImportItemStarted(importer::ImportItem item) OVERRIDE {} 116 virtual void ImportItemEnded(importer::ImportItem item) OVERRIDE {} 117 virtual void ImportEnded() OVERRIDE { 118 base::MessageLoop::current()->Quit(); 119 EXPECT_EQ(arraysize(kFirefoxBookmarks), bookmark_count_); 120 EXPECT_EQ(1U, history_count_); 121 EXPECT_EQ(arraysize(kFirefoxPasswords), password_count_); 122 if (import_search_engines_) 123 EXPECT_EQ(arraysize(kFirefoxKeywords), keyword_count_); 124 } 125 126 virtual bool BookmarkModelIsLoaded() const OVERRIDE { 127 // Profile is ready for writing. 128 return true; 129 } 130 131 virtual bool TemplateURLServiceIsLoaded() const OVERRIDE { 132 return true; 133 } 134 135 virtual void AddPasswordForm(const content::PasswordForm& form) OVERRIDE { 136 PasswordInfo p = kFirefoxPasswords[password_count_]; 137 EXPECT_EQ(p.origin, form.origin.spec()); 138 EXPECT_EQ(p.realm, form.signon_realm); 139 EXPECT_EQ(p.action, form.action.spec()); 140 EXPECT_EQ(WideToUTF16(p.username_element), form.username_element); 141 EXPECT_EQ(WideToUTF16(p.username), form.username_value); 142 EXPECT_EQ(WideToUTF16(p.password_element), form.password_element); 143 EXPECT_EQ(WideToUTF16(p.password), form.password_value); 144 EXPECT_EQ(p.blacklisted, form.blacklisted_by_user); 145 ++password_count_; 146 } 147 148 virtual void AddHistoryPage(const history::URLRows& page, 149 history::VisitSource visit_source) OVERRIDE { 150 ASSERT_EQ(3U, page.size()); 151 EXPECT_EQ("http://www.google.com/", page[0].url().spec()); 152 EXPECT_EQ(ASCIIToUTF16("Google"), page[0].title()); 153 EXPECT_EQ("http://www.google.com/", page[1].url().spec()); 154 EXPECT_EQ(ASCIIToUTF16("Google"), page[1].title()); 155 EXPECT_EQ("http://www.cs.unc.edu/~jbs/resources/perl/perl-cgi/programs/" 156 "form1-POST.html", page[2].url().spec()); 157 EXPECT_EQ(ASCIIToUTF16("example form (POST)"), page[2].title()); 158 EXPECT_EQ(history::SOURCE_FIREFOX_IMPORTED, visit_source); 159 ++history_count_; 160 } 161 162 virtual void AddBookmarks(const std::vector<ImportedBookmarkEntry>& bookmarks, 163 const string16& top_level_folder_name) OVERRIDE { 164 ASSERT_LE(bookmark_count_ + bookmarks.size(), arraysize(kFirefoxBookmarks)); 165 // Importer should import the FF favorites the same as the list, in the same 166 // order. 167 for (size_t i = 0; i < bookmarks.size(); ++i) { 168 EXPECT_NO_FATAL_FAILURE( 169 TestEqualBookmarkEntry(bookmarks[i], 170 kFirefoxBookmarks[bookmark_count_])) << i; 171 ++bookmark_count_; 172 } 173 } 174 175 virtual void AddKeywords(ScopedVector<TemplateURL> template_urls, 176 bool unique_on_host_and_path) OVERRIDE { 177 for (size_t i = 0; i < template_urls.size(); ++i) { 178 // The order might not be deterministic, look in the expected list for 179 // that template URL. 180 bool found = false; 181 string16 keyword = template_urls[i]->keyword(); 182 for (size_t j = 0; j < arraysize(kFirefoxKeywords); ++j) { 183 if (template_urls[i]->keyword() == 184 WideToUTF16Hack(kFirefoxKeywords[j].keyword)) { 185 EXPECT_EQ(kFirefoxKeywords[j].url, template_urls[i]->url()); 186 found = true; 187 break; 188 } 189 } 190 EXPECT_TRUE(found); 191 ++keyword_count_; 192 } 193 } 194 195 virtual void AddFavicons( 196 const std::vector<ImportedFaviconUsage>& favicons) OVERRIDE { 197 } 198 199 private: 200 virtual ~FirefoxObserver() {} 201 202 size_t bookmark_count_; 203 size_t history_count_; 204 size_t password_count_; 205 size_t keyword_count_; 206 bool import_search_engines_; 207 }; 208 209 } // namespace 210 211 // These tests need to be browser tests in order to be able to run the OOP 212 // import (via ExternalProcessImporterHost) which launches a utility process on 213 // supported platforms. 214 class FirefoxProfileImporterBrowserTest : public InProcessBrowserTest { 215 protected: 216 virtual void SetUp() OVERRIDE { 217 // Creates a new profile in a new subdirectory in the temp directory. 218 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 219 base::FilePath test_path = temp_dir_.path().AppendASCII("ImporterTest"); 220 base::DeleteFile(test_path, true); 221 file_util::CreateDirectory(test_path); 222 profile_path_ = test_path.AppendASCII("profile"); 223 app_path_ = test_path.AppendASCII("app"); 224 file_util::CreateDirectory(app_path_); 225 226 // This will launch the browser test and thus needs to happen last. 227 InProcessBrowserTest::SetUp(); 228 } 229 230 void Firefox3xImporterBrowserTest( 231 std::string profile_dir, 232 importer::ImporterProgressObserver* observer, 233 ProfileWriter* writer, 234 bool import_search_plugins) { 235 base::FilePath data_path; 236 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 237 data_path = data_path.AppendASCII(profile_dir); 238 ASSERT_TRUE(base::CopyDirectory(data_path, profile_path_, true)); 239 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 240 data_path = data_path.AppendASCII("firefox3_nss"); 241 ASSERT_TRUE(base::CopyDirectory(data_path, profile_path_, false)); 242 243 base::FilePath search_engine_path = app_path_; 244 search_engine_path = search_engine_path.AppendASCII("searchplugins"); 245 file_util::CreateDirectory(search_engine_path); 246 if (import_search_plugins) { 247 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); 248 data_path = data_path.AppendASCII("firefox3_searchplugins"); 249 if (!base::PathExists(data_path)) { 250 // TODO(maruel): Create search test data that we can open source! 251 LOG(ERROR) << L"Missing internal test data"; 252 return; 253 } 254 ASSERT_TRUE(base::CopyDirectory(data_path, search_engine_path, false)); 255 } 256 257 importer::SourceProfile source_profile; 258 source_profile.importer_type = importer::TYPE_FIREFOX; 259 source_profile.app_path = app_path_; 260 source_profile.source_path = profile_path_; 261 source_profile.locale = "en-US"; 262 263 int items = importer::HISTORY | importer::PASSWORDS | importer::FAVORITES; 264 if (import_search_plugins) 265 items = items | importer::SEARCH_ENGINES; 266 267 // Deletes itself. 268 ExternalProcessImporterHost* host = new ExternalProcessImporterHost; 269 host->set_observer(observer); 270 host->StartImportSettings(source_profile, 271 browser()->profile(), 272 items, 273 make_scoped_refptr(writer).get()); 274 base::MessageLoop::current()->Run(); 275 } 276 277 base::ScopedTempDir temp_dir_; 278 base::FilePath profile_path_; 279 base::FilePath app_path_; 280 }; 281 282 IN_PROC_BROWSER_TEST_F(FirefoxProfileImporterBrowserTest, 283 MAYBE_IMPORTER(Firefox30Importer)) { 284 scoped_refptr<FirefoxObserver> observer(new FirefoxObserver()); 285 Firefox3xImporterBrowserTest("firefox3_profile", observer.get(), 286 observer.get(), true); 287 } 288 289 IN_PROC_BROWSER_TEST_F(FirefoxProfileImporterBrowserTest, 290 MAYBE_IMPORTER(Firefox35Importer)) { 291 bool import_search_engines = false; 292 scoped_refptr<FirefoxObserver> observer( 293 new FirefoxObserver(import_search_engines)); 294 Firefox3xImporterBrowserTest("firefox35_profile", observer.get(), 295 observer.get(), import_search_engines); 296 } 297