| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <Cocoa/Cocoa.h> | 5 #include <Cocoa/Cocoa.h> |
| 6 | 6 |
| 7 #include "chrome/browser/importer/safari_importer.h" | 7 #include "chrome/browser/importer/safari_importer.h" |
| 8 | 8 |
| 9 #include <map> | 9 #include <map> |
| 10 #include <vector> | 10 #include <vector> |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 90 } | 90 } |
| 91 if ((items & importer::PASSWORDS) && !cancelled()) { | 91 if ((items & importer::PASSWORDS) && !cancelled()) { |
| 92 bridge_->NotifyItemStarted(importer::PASSWORDS); | 92 bridge_->NotifyItemStarted(importer::PASSWORDS); |
| 93 ImportPasswords(); | 93 ImportPasswords(); |
| 94 bridge_->NotifyItemEnded(importer::PASSWORDS); | 94 bridge_->NotifyItemEnded(importer::PASSWORDS); |
| 95 } | 95 } |
| 96 bridge_->NotifyEnded(); | 96 bridge_->NotifyEnded(); |
| 97 } | 97 } |
| 98 | 98 |
| 99 void SafariImporter::ImportBookmarks() { | 99 void SafariImporter::ImportBookmarks() { |
| 100 string16 toolbar_name = |
| 101 bridge_->GetLocalizedString(IDS_BOOMARK_BAR_FOLDER_NAME); |
| 100 std::vector<ProfileWriter::BookmarkEntry> bookmarks; | 102 std::vector<ProfileWriter::BookmarkEntry> bookmarks; |
| 101 ParseBookmarks(&bookmarks); | 103 ParseBookmarks(toolbar_name, &bookmarks); |
| 102 | 104 |
| 103 // Write bookmarks into profile. | 105 // Write bookmarks into profile. |
| 104 if (!bookmarks.empty() && !cancelled()) { | 106 if (!bookmarks.empty() && !cancelled()) { |
| 105 const string16& first_folder_name = | 107 const string16& first_folder_name = |
| 106 bridge_->GetLocalizedString(IDS_BOOKMARK_GROUP_FROM_SAFARI); | 108 bridge_->GetLocalizedString(IDS_BOOKMARK_GROUP_FROM_SAFARI); |
| 107 int options = 0; | 109 bridge_->AddBookmarks(bookmarks, first_folder_name); |
| 108 if (import_to_bookmark_bar()) | |
| 109 options = ProfileWriter::IMPORT_TO_BOOKMARK_BAR; | |
| 110 bridge_->AddBookmarks(bookmarks, first_folder_name, options); | |
| 111 } | 110 } |
| 112 | 111 |
| 113 // Import favicons. | 112 // Import favicons. |
| 114 sql::Connection db; | 113 sql::Connection db; |
| 115 if (!OpenDatabase(&db)) | 114 if (!OpenDatabase(&db)) |
| 116 return; | 115 return; |
| 117 | 116 |
| 118 FaviconMap favicon_map; | 117 FaviconMap favicon_map; |
| 119 ImportFaviconURLs(&db, &favicon_map); | 118 ImportFaviconURLs(&db, &favicon_map); |
| 120 // Write favicons into profile. | 119 // Write favicons into profile. |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 186 usage.urls = i->second; | 185 usage.urls = i->second; |
| 187 favicons->push_back(usage); | 186 favicons->push_back(usage); |
| 188 } | 187 } |
| 189 } | 188 } |
| 190 } | 189 } |
| 191 | 190 |
| 192 void SafariImporter::RecursiveReadBookmarksFolder( | 191 void SafariImporter::RecursiveReadBookmarksFolder( |
| 193 NSDictionary* bookmark_folder, | 192 NSDictionary* bookmark_folder, |
| 194 const std::vector<string16>& parent_path_elements, | 193 const std::vector<string16>& parent_path_elements, |
| 195 bool is_in_toolbar, | 194 bool is_in_toolbar, |
| 195 const string16& toolbar_name, |
| 196 std::vector<ProfileWriter::BookmarkEntry>* out_bookmarks) { | 196 std::vector<ProfileWriter::BookmarkEntry>* out_bookmarks) { |
| 197 DCHECK(bookmark_folder); | 197 DCHECK(bookmark_folder); |
| 198 | 198 |
| 199 NSString* type = [bookmark_folder objectForKey:@"WebBookmarkType"]; | 199 NSString* type = [bookmark_folder objectForKey:@"WebBookmarkType"]; |
| 200 NSString* title = [bookmark_folder objectForKey:@"Title"]; | 200 NSString* title = [bookmark_folder objectForKey:@"Title"]; |
| 201 | 201 |
| 202 // Are we the dictionary that contains all other bookmarks? | 202 // Are we the dictionary that contains all other bookmarks? |
| 203 // We need to know this so we don't add it to the path. | 203 // We need to know this so we don't add it to the path. |
| 204 bool is_top_level_bookmarks_container = [bookmark_folder | 204 bool is_top_level_bookmarks_container = [bookmark_folder |
| 205 objectForKey:@"WebBookmarkFileVersion"] != nil; | 205 objectForKey:@"WebBookmarkFileVersion"] != nil; |
| 206 | 206 |
| 207 // We're expecting a list of bookmarks here, if that isn't what we got, fail. | 207 // We're expecting a list of bookmarks here, if that isn't what we got, fail. |
| 208 if (!is_top_level_bookmarks_container) { | 208 if (!is_top_level_bookmarks_container) { |
| 209 // Top level containers sometimes don't have title attributes. | 209 // Top level containers sometimes don't have title attributes. |
| 210 if (![type isEqualToString:@"WebBookmarkTypeList"] || !title) { | 210 if (![type isEqualToString:@"WebBookmarkTypeList"] || !title) { |
| 211 DCHECK(false) << "Type =(" | 211 NOTREACHED() << "Type=(" |
| 212 << (type ? base::SysNSStringToUTF8(type) : "Null Type") | 212 << (type ? base::SysNSStringToUTF8(type) : "Null type") |
| 213 << ") Title=(" << (title ? base::SysNSStringToUTF8(title) : "Null title") | 213 << ") Title=(" |
| 214 << ")"; | 214 << (title ? base::SysNSStringToUTF8(title) : "Null title") |
| 215 << ")"; |
| 215 return; | 216 return; |
| 216 } | 217 } |
| 217 } | 218 } |
| 218 | 219 |
| 220 NSArray* elements = [bookmark_folder objectForKey:@"Children"]; |
| 221 if (!elements && (!parent_path_elements.empty() || !is_in_toolbar)) { |
| 222 // This is an empty folder, so add it explicitly; but don't add the toolbar |
| 223 // folder if it is empty. Note that all non-empty folders are added |
| 224 // implicitly when their children are added. |
| 225 ProfileWriter::BookmarkEntry entry; |
| 226 // Safari doesn't specify a creation time for the folder. |
| 227 entry.creation_time = base::Time::Now(); |
| 228 entry.title = base::SysNSStringToUTF16(title); |
| 229 entry.path = parent_path_elements; |
| 230 entry.in_toolbar = is_in_toolbar; |
| 231 entry.is_folder = true; |
| 232 |
| 233 out_bookmarks->push_back(entry); |
| 234 return; |
| 235 } |
| 236 |
| 219 std::vector<string16> path_elements(parent_path_elements); | 237 std::vector<string16> path_elements(parent_path_elements); |
| 220 // Is this the toolbar folder? | 238 // Create a folder for the toolbar, but not for the bookmarks menu. |
| 221 if ([title isEqualToString:@"BookmarksBar"]) { | 239 if (path_elements.empty() && [title isEqualToString:@"BookmarksBar"]) { |
| 222 // Be defensive, the toolbar items shouldn't have a prepended path. | |
| 223 path_elements.clear(); | |
| 224 is_in_toolbar = true; | 240 is_in_toolbar = true; |
| 225 } else if ([title isEqualToString:@"BookmarksMenu"]) { | 241 path_elements.push_back(toolbar_name); |
| 226 // top level container for normal bookmarks. | 242 } else if (!is_top_level_bookmarks_container && |
| 227 path_elements.clear(); | 243 !(path_elements.empty() && |
| 228 } else if (!is_top_level_bookmarks_container) { | 244 [title isEqualToString:@"BookmarksMenu"])) { |
| 229 if (title) | 245 if (title) |
| 230 path_elements.push_back(base::SysNSStringToUTF16(title)); | 246 path_elements.push_back(base::SysNSStringToUTF16(title)); |
| 231 } | 247 } |
| 232 | 248 |
| 233 NSArray* elements = [bookmark_folder objectForKey:@"Children"]; | |
| 234 // TODO(jeremy) Does Chrome support importing empty folders? | |
| 235 if (!elements) | |
| 236 return; | |
| 237 | |
| 238 // Iterate over individual bookmarks. | 249 // Iterate over individual bookmarks. |
| 239 for (NSDictionary* bookmark in elements) { | 250 for (NSDictionary* bookmark in elements) { |
| 240 NSString* type = [bookmark objectForKey:@"WebBookmarkType"]; | 251 NSString* type = [bookmark objectForKey:@"WebBookmarkType"]; |
| 241 if (!type) | 252 if (!type) |
| 242 continue; | 253 continue; |
| 243 | 254 |
| 244 // If this is a folder, recurse. | 255 // If this is a folder, recurse. |
| 245 if ([type isEqualToString:@"WebBookmarkTypeList"]) { | 256 if ([type isEqualToString:@"WebBookmarkTypeList"]) { |
| 246 RecursiveReadBookmarksFolder(bookmark, | 257 RecursiveReadBookmarksFolder(bookmark, |
| 247 path_elements, | 258 path_elements, |
| 248 is_in_toolbar, | 259 is_in_toolbar, |
| 260 toolbar_name, |
| 249 out_bookmarks); | 261 out_bookmarks); |
| 250 } | 262 } |
| 251 | 263 |
| 252 // If we didn't see a bookmark folder, then we're expecting a bookmark | 264 // If we didn't see a bookmark folder, then we're expecting a bookmark |
| 253 // item, if that's not what we got then ignore it. | 265 // item. If that's not what we got then ignore it. |
| 254 if (![type isEqualToString:@"WebBookmarkTypeLeaf"]) | 266 if (![type isEqualToString:@"WebBookmarkTypeLeaf"]) |
| 255 continue; | 267 continue; |
| 256 | 268 |
| 257 NSString* url = [bookmark objectForKey:@"URLString"]; | 269 NSString* url = [bookmark objectForKey:@"URLString"]; |
| 258 NSString* title = [[bookmark objectForKey:@"URIDictionary"] | 270 NSString* title = [[bookmark objectForKey:@"URIDictionary"] |
| 259 objectForKey:@"title"]; | 271 objectForKey:@"title"]; |
| 260 | 272 |
| 261 if (!url || !title) | 273 if (!url || !title) |
| 262 continue; | 274 continue; |
| 263 | 275 |
| 264 // Output Bookmark. | 276 // Output Bookmark. |
| 265 ProfileWriter::BookmarkEntry entry; | 277 ProfileWriter::BookmarkEntry entry; |
| 266 // Safari doesn't specify a creation time for the bookmark. | 278 // Safari doesn't specify a creation time for the bookmark. |
| 267 entry.creation_time = base::Time::Now(); | 279 entry.creation_time = base::Time::Now(); |
| 268 entry.title = base::SysNSStringToUTF16(title); | 280 entry.title = base::SysNSStringToUTF16(title); |
| 269 entry.url = GURL(base::SysNSStringToUTF8(url)); | 281 entry.url = GURL(base::SysNSStringToUTF8(url)); |
| 270 entry.path = path_elements; | 282 entry.path = path_elements; |
| 271 entry.in_toolbar = is_in_toolbar; | 283 entry.in_toolbar = is_in_toolbar; |
| 272 | 284 |
| 273 out_bookmarks->push_back(entry); | 285 out_bookmarks->push_back(entry); |
| 274 } | 286 } |
| 275 } | 287 } |
| 276 | 288 |
| 277 void SafariImporter::ParseBookmarks( | 289 void SafariImporter::ParseBookmarks( |
| 290 const string16& toolbar_name, |
| 278 std::vector<ProfileWriter::BookmarkEntry>* bookmarks) { | 291 std::vector<ProfileWriter::BookmarkEntry>* bookmarks) { |
| 279 DCHECK(bookmarks); | 292 DCHECK(bookmarks); |
| 280 | 293 |
| 281 // Construct ~/Library/Safari/Bookmarks.plist path | 294 // Construct ~/Library/Safari/Bookmarks.plist path |
| 282 NSString* library_dir = [NSString | 295 NSString* library_dir = [NSString |
| 283 stringWithUTF8String:library_dir_.value().c_str()]; | 296 stringWithUTF8String:library_dir_.value().c_str()]; |
| 284 NSString* safari_dir = [library_dir | 297 NSString* safari_dir = [library_dir |
| 285 stringByAppendingPathComponent:@"Safari"]; | 298 stringByAppendingPathComponent:@"Safari"]; |
| 286 NSString* bookmarks_plist = [safari_dir | 299 NSString* bookmarks_plist = [safari_dir |
| 287 stringByAppendingPathComponent:@"Bookmarks.plist"]; | 300 stringByAppendingPathComponent:@"Bookmarks.plist"]; |
| 288 | 301 |
| 289 // Load the plist file. | 302 // Load the plist file. |
| 290 NSDictionary* bookmarks_dict = [NSDictionary | 303 NSDictionary* bookmarks_dict = [NSDictionary |
| 291 dictionaryWithContentsOfFile:bookmarks_plist]; | 304 dictionaryWithContentsOfFile:bookmarks_plist]; |
| 292 if (!bookmarks_dict) | 305 if (!bookmarks_dict) |
| 293 return; | 306 return; |
| 294 | 307 |
| 295 // Recursively read in bookmarks. | 308 // Recursively read in bookmarks. |
| 296 std::vector<string16> parent_path_elements; | 309 std::vector<string16> parent_path_elements; |
| 297 RecursiveReadBookmarksFolder(bookmarks_dict, parent_path_elements, false, | 310 RecursiveReadBookmarksFolder(bookmarks_dict, parent_path_elements, false, |
| 298 bookmarks); | 311 toolbar_name, bookmarks); |
| 299 } | 312 } |
| 300 | 313 |
| 301 void SafariImporter::ImportPasswords() { | 314 void SafariImporter::ImportPasswords() { |
| 302 // Safari stores it's passwords in the Keychain, same as us so we don't need | 315 // Safari stores it's passwords in the Keychain, same as us so we don't need |
| 303 // to import them. | 316 // to import them. |
| 304 // Note: that we don't automatically pick them up, there is some logic around | 317 // Note: that we don't automatically pick them up, there is some logic around |
| 305 // the user needing to explicitly input his username in a page and blurring | 318 // the user needing to explicitly input his username in a page and blurring |
| 306 // the field before we pick it up, but the details of that are beyond the | 319 // the field before we pick it up, but the details of that are beyond the |
| 307 // scope of this comment. | 320 // scope of this comment. |
| 308 } | 321 } |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 380 if (!last_visit_str) | 393 if (!last_visit_str) |
| 381 continue; | 394 continue; |
| 382 | 395 |
| 383 // Convert Safari's last visit time to Unix Epoch time. | 396 // Convert Safari's last visit time to Unix Epoch time. |
| 384 double seconds_since_unix_epoch = HistoryTimeToEpochTime(last_visit_str); | 397 double seconds_since_unix_epoch = HistoryTimeToEpochTime(last_visit_str); |
| 385 row.set_last_visit(base::Time::FromDoubleT(seconds_since_unix_epoch)); | 398 row.set_last_visit(base::Time::FromDoubleT(seconds_since_unix_epoch)); |
| 386 | 399 |
| 387 history_items->push_back(row); | 400 history_items->push_back(row); |
| 388 } | 401 } |
| 389 } | 402 } |
| OLD | NEW |