diff options
Diffstat (limited to 'chrome/browser/importer/safari_importer.mm')
-rw-r--r-- | chrome/browser/importer/safari_importer.mm | 247 |
1 files changed, 201 insertions, 46 deletions
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); -} - |