diff options
author | jeremy@chromium.org <jeremy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-05 23:50:35 +0000 |
---|---|---|
committer | jeremy@chromium.org <jeremy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-05 23:50:35 +0000 |
commit | 06a793f89522301fe42fbaaffba20a301f809d72 (patch) | |
tree | a82f89826a1e5baf4c456f0ba60c5d0b846e5df0 /chrome/browser | |
parent | 6b1c25b4adfcd0ce330c10a65d3eed22182f59f8 (diff) | |
download | chromium_src-06a793f89522301fe42fbaaffba20a301f809d72.zip chromium_src-06a793f89522301fe42fbaaffba20a301f809d72.tar.gz chromium_src-06a793f89522301fe42fbaaffba20a301f809d72.tar.bz2 |
Safari Bookmark/Favicon import.
Review URL: http://codereview.chromium.org/159750
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@22556 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/first_run_mac.mm | 16 | ||||
-rw-r--r-- | chrome/browser/importer/firefox3_importer.cc | 20 | ||||
-rw-r--r-- | chrome/browser/importer/safari_importer.h | 46 | ||||
-rw-r--r-- | chrome/browser/importer/safari_importer.mm | 247 | ||||
-rw-r--r-- | chrome/browser/importer/safari_importer_unittest.mm | 104 |
5 files changed, 344 insertions, 89 deletions
diff --git a/chrome/browser/first_run_mac.mm b/chrome/browser/first_run_mac.mm index d839bf4..7242f94 100644 --- a/chrome/browser/first_run_mac.mm +++ b/chrome/browser/first_run_mac.mm @@ -91,13 +91,15 @@ bool OpenFirstRunDialog(Profile* profile, ProcessSingleton* process_singleton) { } // Import bookmarks. - const ProfileInfo& source_profile = importer_host->GetSourceProfileInfoAt( - [dialog.get() browserImportSelectedIndex]); - int items = SEARCH_ENGINES + HISTORY + FAVORITES + HOME_PAGE + PASSWORDS; - // TODO(port): Call StartImportingWithUI here instead and launch - // a new process that does the actual import. - importer_host->StartImportSettings(source_profile, profile, items, - new ProfileWriter(profile), true); + if ([dialog.get() importBookmarks]) { + const ProfileInfo& source_profile = importer_host->GetSourceProfileInfoAt( + [dialog.get() browserImportSelectedIndex]); + int items = SEARCH_ENGINES + HISTORY + FAVORITES + HOME_PAGE + PASSWORDS; + // TODO(port): Call StartImportingWithUI here instead and launch + // a new process that does the actual import. + importer_host->StartImportSettings(source_profile, profile, items, + new ProfileWriter(profile), true); + } #endif // defined(GOOGLE_CHROME_BUILD) return true; diff --git a/chrome/browser/importer/firefox3_importer.cc b/chrome/browser/importer/firefox3_importer.cc index ffdf407..8ee342c 100644 --- a/chrome/browser/importer/firefox3_importer.cc +++ b/chrome/browser/importer/firefox3_importer.cc @@ -24,20 +24,6 @@ using base::Time; using webkit_glue::PasswordForm; -// Wraps the function sqlite3_close() in a class that is -// used in scoped_ptr_malloc. - -namespace { - -class DBClose { - public: - inline void operator()(sqlite3* x) const { - sqlite3_close(x); - } -}; - -} // namespace - void Firefox3Importer::StartImport(ProfileInfo profile_info, uint16 items, ProfileWriter* writer, MessageLoop* delagate_loop, @@ -84,7 +70,7 @@ void Firefox3Importer::ImportHistory() { sqlite3* sqlite; if (sqlite3_open(WideToUTF8(file).c_str(), &sqlite) != SQLITE_OK) return; - scoped_ptr_malloc<sqlite3, DBClose> db(sqlite); + sqlite_utils::scoped_sqlite_db_ptr db(sqlite); SQLStatement s; // |visit_type| represent the transition type of URLs (typed, click, @@ -133,7 +119,7 @@ void Firefox3Importer::ImportBookmarks() { sqlite3* sqlite; if (sqlite3_open(WideToUTF8(file).c_str(), &sqlite) != SQLITE_OK) return; - scoped_ptr_malloc<sqlite3, DBClose> db(sqlite); + sqlite_utils::scoped_sqlite_db_ptr db(sqlite); // Get the bookmark folders that we are interested in. int toolbar_folder_id = -1; @@ -329,7 +315,7 @@ void Firefox3Importer::GetSearchEnginesXMLFiles( sqlite3* sqlite; if (sqlite3_open(WideToUTF8(file).c_str(), &sqlite) != SQLITE_OK) return; - scoped_ptr_malloc<sqlite3, DBClose> db(sqlite); + sqlite_utils::scoped_sqlite_db_ptr db(sqlite); SQLStatement s; const char* stmt = "SELECT engineid FROM engine_data " diff --git a/chrome/browser/importer/safari_importer.h b/chrome/browser/importer/safari_importer.h index 28b939f..75f2a65 100644 --- a/chrome/browser/importer/safari_importer.h +++ b/chrome/browser/importer/safari_importer.h @@ -7,6 +7,12 @@ #include "chrome/browser/importer/importer.h" +#include <map> +#include <set> +#include <vector> + +#include "chrome/common/sqlite_utils.h" + #if __OBJC__ @class NSDictionary; @class NSString; @@ -31,14 +37,35 @@ class SafariImporter : public Importer { ImporterHost* host); private: + FRIEND_TEST(SafariImporterTest, BookmarkImport); + FRIEND_TEST(SafariImporterTest, FavIconImport); FRIEND_TEST(SafariImporterTest, HistoryImport); + // Multiple URLs can share the same FavIcon, this is a map + // of URLs -> IconIDs that we load as a temporary step before + // actually loading the icons. + typedef std::map<int64, std::set<GURL> > FaviconMap; + void ImportBookmarks(); - void ImportSearchEngines(); void ImportPasswords(); void ImportHistory(); void ImportHomepage(); + // Parse Safari's stored bookmarks. + void ParseBookmarks(std::vector<ProfileWriter::BookmarkEntry>* bookmarks); + + // Function to recursively read Bookmarks out of Safari plist. + // |bookmark_folder| The dictionary containing a folder to parse. + // |parent_path_elements| Path elements up to this point. + // |is_in_toolbar| Is this folder in the toolbar. + // |out_bookmarks| BookMark element array to write into. + void RecursiveReadBookmarksFolder( + NSDictionary* bookmark_folder, + const std::vector<std::wstring>& parent_path_elements, + bool is_in_toolbar, + std::vector<ProfileWriter::BookmarkEntry>* out_bookmarks); + + // Converts history time stored by Safari as a double serialized as a string, // to seconds-since-UNIX-Ephoch-format used by Chrome. double HistoryTimeToEpochTime(NSString* history_time); @@ -46,13 +73,16 @@ class SafariImporter : public Importer { // Parses Safari's history and loads it into the input array. void ParseHistoryItems(std::vector<history::URLRow>* history_items); - // Given the URL of a page and a favicon data URL, adds an appropriate record - // to the given favicon usage vector. Will do nothing if the favicon is not - // valid. - static void DataURLToFaviconUsage( - const GURL& link_url, - const GURL& favicon_data, - std::vector<history::ImportedFavIconUsage>* favicons); + // Loads the favicon Database file, returns NULL on failure. + sqlite3* OpenFavIconDB(); + + // Loads the urls associated with the favicons into favicon_map; + void ImportFavIconURLs(sqlite3* db, FaviconMap* favicon_map); + + // Loads and reencodes the individual favicons. + void LoadFaviconData(sqlite3* db, + const FaviconMap& favicon_map, + std::vector<history::ImportedFavIconUsage>* favicons); ProfileWriter* writer_; FilePath library_dir_; diff --git a/chrome/browser/importer/safari_importer.mm b/chrome/browser/importer/safari_importer.mm index 78e1111..0057aef 100644 --- a/chrome/browser/importer/safari_importer.mm +++ b/chrome/browser/importer/safari_importer.mm @@ -6,13 +6,16 @@ #include "chrome/browser/importer/safari_importer.h" +#include <map> #include <vector> +#include "app/l10n_util.h" #include "base/message_loop.h" #include "base/scoped_nsobject.h" #include "base/string16.h" #include "base/sys_string_conversions.h" #include "base/time.h" +#include "chrome/common/sqlite_utils.h" #include "chrome/common/url_constants.h" #include "googleurl/src/gurl.h" #include "grit/generated_resources.h" @@ -57,11 +60,6 @@ void SafariImporter::StartImport(ProfileInfo profile_info, ImportBookmarks(); NotifyItemEnded(FAVORITES); } - if ((items & SEARCH_ENGINES) && !cancelled()) { - NotifyItemStarted(SEARCH_ENGINES); - ImportSearchEngines(); - NotifyItemEnded(SEARCH_ENGINES); - } if ((items & PASSWORDS) && !cancelled()) { NotifyItemStarted(PASSWORDS); ImportPasswords(); @@ -76,11 +74,199 @@ void SafariImporter::StartImport(ProfileInfo profile_info, } void SafariImporter::ImportBookmarks() { - NOTIMPLEMENTED(); + std::vector<ProfileWriter::BookmarkEntry> bookmarks; + ParseBookmarks(&bookmarks); + + // Write bookmarks into profile. + if (!bookmarks.empty() && !cancelled()) { + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddBookmarkEntry, bookmarks, + l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_SAFARI), + import_to_bookmark_bar() ? ProfileWriter::IMPORT_TO_BOOKMARK_BAR : 0)); + } + + // Import favicons. + sqlite_utils::scoped_sqlite_db_ptr db(OpenFavIconDB()); + FaviconMap favicon_map; + ImportFavIconURLs(db.get(), &favicon_map); + // Write favicons into profile. + if (!favicon_map.empty() && !cancelled()) { + std::vector<history::ImportedFavIconUsage> favicons; + LoadFaviconData(db.get(), favicon_map, &favicons); + main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, + &ProfileWriter::AddFavicons, favicons)); + } +} + +sqlite3* SafariImporter::OpenFavIconDB() { + // Construct ~/Library/Safari/WebIcons.db path + NSString* library_dir = [NSString + stringWithUTF8String:library_dir_.value().c_str()]; + NSString* safari_dir = [library_dir + stringByAppendingPathComponent:@"Safari"]; + NSString* favicons_db_path = [safari_dir + stringByAppendingPathComponent:@"WebpageIcons.db"]; + + sqlite3* favicons_db; + const char* safariicons_dbname = [favicons_db_path fileSystemRepresentation]; + if (sqlite3_open(safariicons_dbname, &favicons_db) != SQLITE_OK) + return NULL; + + return favicons_db; +} + +void SafariImporter::ImportFavIconURLs(sqlite3* db, FaviconMap* favicon_map) { + SQLStatement s; + const char* stmt = "SELECT iconID, url FROM PageURL;"; + if (s.prepare(db, stmt) != SQLITE_OK) + return; + + while (s.step() == SQLITE_ROW && !cancelled()) { + int64 icon_id = s.column_int(0); + GURL url = GURL(s.column_string(1)); + (*favicon_map)[icon_id].insert(url); + } } -void SafariImporter::ImportSearchEngines() { - NOTIMPLEMENTED(); +void SafariImporter::LoadFaviconData(sqlite3* db, + const FaviconMap& favicon_map, + std::vector<history::ImportedFavIconUsage>* favicons) { + SQLStatement s; + const char* stmt = "SELECT i.url, d.data " + "FROM IconInfo i JOIN IconData d " + "ON i.iconID = d.iconID " + "WHERE i.iconID = ?;"; + if (s.prepare(db, stmt) != SQLITE_OK) + return; + + for (FaviconMap::const_iterator i = favicon_map.begin(); + i != favicon_map.end(); ++i) { + s.bind_int64(0, i->first); + if (s.step() == SQLITE_ROW) { + history::ImportedFavIconUsage usage; + + usage.favicon_url = GURL(s.column_string(0)); + if (!usage.favicon_url.is_valid()) + continue; // Don't bother importing favicons with invalid URLs. + + std::vector<unsigned char> data; + if (!s.column_blob_as_vector(1, &data) || data.empty()) + continue; // Data definitely invalid. + + if (!ReencodeFavicon(&data[0], data.size(), &usage.png_data)) + continue; // Unable to decode. + + usage.urls = i->second; + favicons->push_back(usage); + } + s.reset(); + } +} + +void SafariImporter::RecursiveReadBookmarksFolder( + NSDictionary* bookmark_folder, + const std::vector<std::wstring>& parent_path_elements, + bool is_in_toolbar, + std::vector<ProfileWriter::BookmarkEntry>* out_bookmarks) { + DCHECK(bookmark_folder); + + NSString* type = [bookmark_folder objectForKey:@"WebBookmarkType"]; + NSString* title = [bookmark_folder objectForKey:@"Title"]; + + // Are we the dictionary that contains all other bookmarks? + // We need to know this so we don't add it to the path. + bool is_top_level_bookmarks_container = [bookmark_folder + objectForKey:@"WebBookmarkFileVersion"] != nil; + + // We're expecting a list of bookmarks here, if that isn't what we got, fail. + if (![type isEqualToString:@"WebBookmarkTypeList"] || !title) { + DCHECK(false) << "Type =(" + << (type ? base::SysNSStringToUTF8(type) : "Null Type") + << ") Title=(" << (title ? base::SysNSStringToUTF8(title) : "Null title") + << ")"; + return; + } + + std::vector<std::wstring> path_elements(parent_path_elements); + // Is this the toolbar folder? + if ([title isEqualToString:@"BookmarksBar"]) { + // Be defensive, the toolbar items shouldn't have a prepended path. + path_elements.clear(); + is_in_toolbar = true; + } else if ([title isEqualToString:@"BookmarksMenu"]) { + // top level container for normal bookmarks. + path_elements.clear(); + } else if (!is_top_level_bookmarks_container) { + if (title) + path_elements.push_back(base::SysNSStringToWide(title)); + } + + NSArray* elements = [bookmark_folder objectForKey:@"Children"]; + // TODO(jeremy) Does Chrome support importing empty folders? + if (!elements) + return; + + // Iterate over individual bookmarks. + for (NSDictionary* bookmark in elements) { + NSString* type = [bookmark objectForKey:@"WebBookmarkType"]; + if (!type) + continue; + + // If this is a folder, recurse. + if ([type isEqualToString:@"WebBookmarkTypeList"]) { + RecursiveReadBookmarksFolder(bookmark, + path_elements, + is_in_toolbar, + out_bookmarks); + } + + // If we didn't see a bookmark folder, then we're expecting a bookmark + // item, if that's not what we got then ignore it. + if (![type isEqualToString:@"WebBookmarkTypeLeaf"]) + continue; + + NSString* url = [bookmark objectForKey:@"URLString"]; + NSString* title = [[bookmark objectForKey:@"URIDictionary"] + objectForKey:@"title"]; + + if (!url || !title) + continue; + + // Output Bookmark. + ProfileWriter::BookmarkEntry entry; + // Safari doesn't specify a creation time for the bookmark. + entry.creation_time = base::Time::Now(); + entry.title = base::SysNSStringToWide(title); + entry.url = GURL(base::SysNSStringToUTF8(url)); + entry.path = path_elements; + entry.in_toolbar = is_in_toolbar; + + out_bookmarks->push_back(entry); + } +} + +void SafariImporter::ParseBookmarks( + std::vector<ProfileWriter::BookmarkEntry>* bookmarks) { + DCHECK(bookmarks); + + // Construct ~/Library/Safari/Bookmarks.plist path + NSString* library_dir = [NSString + stringWithUTF8String:library_dir_.value().c_str()]; + NSString* safari_dir = [library_dir + stringByAppendingPathComponent:@"Safari"]; + NSString* bookmarks_plist = [safari_dir + stringByAppendingPathComponent:@"Bookmarks.plist"]; + + // Load the plist file. + NSDictionary* bookmarks_dict = [NSDictionary + dictionaryWithContentsOfFile:bookmarks_plist]; + if (!bookmarks_dict) + return; + + // Recursively read in bookmarks. + std::vector<std::wstring> parent_path_elements; + RecursiveReadBookmarksFolder(bookmarks_dict, parent_path_elements, false, + bookmarks); } void SafariImporter::ImportPasswords() { @@ -126,13 +312,15 @@ void SafariImporter::ParseHistoryItems( // Load the plist file. NSDictionary* history_dict = [NSDictionary dictionaryWithContentsOfFile:history_plist]; + if (!history_dict) + return; - NSArray* safari_history_items = [history_dict valueForKey:@"WebHistoryDates"]; + NSArray* safari_history_items = [history_dict objectForKey:@"WebHistoryDates"]; for (NSDictionary* history_item in safari_history_items) { using base::SysNSStringToUTF8; using base::SysNSStringToWide; - NSString* url_ns = [history_item valueForKey:@""]; + NSString* url_ns = [history_item objectForKey:@""]; if (!url_ns) continue; @@ -142,7 +330,7 @@ void SafariImporter::ParseHistoryItems( continue; history::URLRow row(url); - NSString* title_ns = [history_item valueForKey:@"title"]; + NSString* title_ns = [history_item objectForKey:@"title"]; // Sometimes items don't have a title, in which case we just substitue // the url. @@ -150,7 +338,7 @@ void SafariImporter::ParseHistoryItems( title_ns = url_ns; row.set_title(SysNSStringToWide(title_ns)); - int visit_count = [[history_item valueForKey:@"visitCount"] + int visit_count = [[history_item objectForKey:@"visitCount"] intValue]; row.set_visit_count(visit_count); // Include imported URLs in autocompletion - don't hide them. @@ -158,7 +346,7 @@ void SafariImporter::ParseHistoryItems( // Item was never typed before in the omnibox. row.set_typed_count(0); - NSString* last_visit_str = [history_item valueForKey:@"lastVisitedDate"]; + NSString* last_visit_str = [history_item objectForKey:@"lastVisitedDate"]; // The last visit time should always be in the history item, but if not /// just continue without this item. DCHECK(last_visit_str); @@ -188,36 +376,3 @@ void SafariImporter::ImportHomepage() { &ProfileWriter::AddHomepage, homepage)); } } - -// TODO(jeremy): This is temporary, just copied from the FF import code in case -// we need it, clean this up when writing favicon import code. -// static -void SafariImporter::DataURLToFaviconUsage( - const GURL& link_url, - const GURL& favicon_data, - std::vector<history::ImportedFavIconUsage>* favicons) { - if (!link_url.is_valid() || !favicon_data.is_valid() || - !favicon_data.SchemeIs(chrome::kDataScheme)) - return; - - // Parse the data URL. - std::string mime_type, char_set, data; - if (!net::DataURL::Parse(favicon_data, &mime_type, &char_set, &data) || - data.empty()) - return; - - history::ImportedFavIconUsage usage; - if (!ReencodeFavicon(reinterpret_cast<const unsigned char*>(&data[0]), - data.size(), &usage.png_data)) - return; // Unable to decode. - - // We need to make up a URL for the favicon. We use a version of the page's - // URL so that we can be sure it will not collide. - usage.favicon_url = GURL(std::string("made-up-favicon:") + link_url.spec()); - - // We only have one URL per favicon for Firefox 2 bookmarks. - usage.urls.insert(link_url); - - favicons->push_back(usage); -} - diff --git a/chrome/browser/importer/safari_importer_unittest.mm b/chrome/browser/importer/safari_importer_unittest.mm index af0ae9e..78b4f65 100644 --- a/chrome/browser/importer/safari_importer_unittest.mm +++ b/chrome/browser/importer/safari_importer_unittest.mm @@ -4,6 +4,7 @@ #include "chrome/browser/importer/safari_importer.h" +#include "base/basictypes.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/path_service.h" @@ -19,28 +20,32 @@ FilePath GetTestSafariLibraryPath() { std::wstring test_dir_wstring; PathService::Get(chrome::DIR_TEST_DATA, &test_dir_wstring); FilePath test_dir = FilePath::FromWStringHack(test_dir_wstring); - + // Our simulated ~/Library directory test_dir = test_dir.Append("safari_import"); return test_dir; } -class SafariImporterTest : public PlatformTest {}; +class SafariImporterTest : public PlatformTest { + public: + SafariImporter* GetSafariImporter() { + FilePath test_library_dir = GetTestSafariLibraryPath(); + CHECK(file_util::PathExists(test_library_dir)) << + "Missing test data directory"; + + return new SafariImporter(test_library_dir); + } +}; TEST_F(SafariImporterTest, HistoryImport) { - FilePath test_library_dir = GetTestSafariLibraryPath(); - ASSERT_TRUE(file_util::PathExists(test_library_dir)) << - "Missing test data directory"; + scoped_refptr<SafariImporter> importer(GetSafariImporter()); - scoped_refptr<SafariImporter> importer( - new SafariImporter(test_library_dir)); - std::vector<history::URLRow> history_items; importer->ParseHistoryItems(&history_items); - + // Should be 2 history items. ASSERT_EQ(history_items.size(), 2U); - + history::URLRow& it1 = history_items[0]; EXPECT_EQ(it1.url(), GURL("http://www.firsthistoryitem.com/")); EXPECT_EQ(it1.title(), L"First History Item Title"); @@ -49,7 +54,7 @@ TEST_F(SafariImporterTest, HistoryImport) { EXPECT_EQ(it1.typed_count(), 0); EXPECT_EQ(it1.last_visit().ToDoubleT(), importer->HistoryTimeToEpochTime(@"270598264.4")); - + history::URLRow& it2 = history_items[1]; std::string second_item_title("http://www.secondhistoryitem.com/"); EXPECT_EQ(it2.url(), GURL(second_item_title)); @@ -61,3 +66,80 @@ TEST_F(SafariImporterTest, HistoryImport) { EXPECT_EQ(it2.last_visit().ToDoubleT(), importer->HistoryTimeToEpochTime(@"270598231.4")); } + +TEST_F(SafariImporterTest, BookmarkImport) { + // Expected results + const struct { + bool in_toolbar; + GURL url; + // If the path array for an element is entry set this to true. + bool path_is_empty; + // We ony support one level of nesting in paths, this makes testing a little + // easier. + std::wstring path; + std::wstring title; + } kImportedBookmarksData[] = { + {true, GURL("http://www.apple.com/"), true, L"", L"Apple"}, + {true, GURL("http://www.yahoo.com/"), true, L"", L"Yahoo!"}, + {true, GURL("http://www.cnn.com/"), false, L"News", L"CNN"}, + {true, GURL("http://www.nytimes.com/"), false, L"News", + L"The New York Times"}, + {false, GURL("http://www.reddit.com/"), true, L"", + L"reddit.com: what's new online!"}, + }; + + scoped_refptr<SafariImporter> importer(GetSafariImporter()); + std::vector<ProfileWriter::BookmarkEntry> bookmarks; + importer->ParseBookmarks(&bookmarks); + size_t num_bookmarks = bookmarks.size(); + EXPECT_EQ(num_bookmarks, ARRAYSIZE_UNSAFE(kImportedBookmarksData)); + + for (size_t i = 0; i < num_bookmarks; ++i) { + ProfileWriter::BookmarkEntry& entry = bookmarks[i]; + EXPECT_EQ(entry.in_toolbar, kImportedBookmarksData[i].in_toolbar); + EXPECT_EQ(entry.url, kImportedBookmarksData[i].url); + if (kImportedBookmarksData[i].path_is_empty) { + EXPECT_EQ(entry.path.size(), 0U); + } else { + EXPECT_EQ(entry.path.size(), 1U); + EXPECT_EQ(entry.path[0], kImportedBookmarksData[i].path); + EXPECT_EQ(entry.title, kImportedBookmarksData[i].title); + } + } + +} + +TEST_F(SafariImporterTest, FavIconImport) { + scoped_refptr<SafariImporter> importer(GetSafariImporter()); + sqlite_utils::scoped_sqlite_db_ptr db(importer->OpenFavIconDB()); + ASSERT_TRUE(db.get() != NULL); + + SafariImporter::FaviconMap favicon_map; + importer->ImportFavIconURLs(db.get(), &favicon_map); + + std::vector<history::ImportedFavIconUsage> favicons; + importer->LoadFaviconData(db.get(), favicon_map, &favicons); + + size_t num_favicons = favicons.size(); + ASSERT_EQ(num_favicons, 2U); + + history::ImportedFavIconUsage &fav0 = favicons[0]; + EXPECT_EQ("http://s.ytimg.com/yt/favicon-vfl86270.ico", + fav0.favicon_url.spec()); + EXPECT_GT(fav0.png_data.size(), 0U); + EXPECT_EQ(fav0.urls.size(), 1U); + EXPECT_TRUE(fav0.urls.find(GURL("http://www.youtube.com/")) + != fav0.urls.end()); + + history::ImportedFavIconUsage &fav1 = favicons[1]; + EXPECT_EQ("http://www.opensearch.org/favicon.ico", + fav1.favicon_url.spec()); + EXPECT_GT(fav1.png_data.size(), 0U); + EXPECT_EQ(fav1.urls.size(), 2U); + EXPECT_TRUE(fav1.urls.find(GURL("http://www.opensearch.org/Home")) + != fav1.urls.end()); + + EXPECT_TRUE(fav1.urls.find( + GURL("http://www.opensearch.org/Special:Search?search=lalala&go=Search")) + != fav1.urls.end()); +} |