Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 "chrome/browser/importer/firefox2_importer.h" | 5 #include "chrome/browser/importer/bookmarks_file_importer.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 #include <vector> | |
| 9 | 8 |
| 9 #include "base/bind.h" | |
| 10 #include "base/callback.h" | |
| 10 #include "base/file_util.h" | 11 #include "base/file_util.h" |
| 11 #include "base/files/file_path.h" | 12 #include "base/files/file_path.h" |
| 12 #include "base/i18n/icu_string_conversions.h" | 13 #include "base/i18n/icu_string_conversions.h" |
| 13 #include "base/message_loop.h" | |
| 14 #include "base/path_service.h" | |
| 15 #include "base/stl_util.h" | |
| 16 #include "base/string_util.h" | 14 #include "base/string_util.h" |
| 17 #include "base/strings/string_number_conversions.h" | 15 #include "base/strings/string_number_conversions.h" |
| 18 #include "base/strings/string_split.h" | 16 #include "base/strings/string_split.h" |
| 19 #include "base/utf_string_conversions.h" | |
| 20 #include "chrome/browser/history/history_types.h" | 17 #include "chrome/browser/history/history_types.h" |
| 21 #include "chrome/browser/importer/firefox_importer_utils.h" | 18 #include "chrome/browser/importer/firefox_importer_utils.h" |
| 22 #include "chrome/browser/importer/importer_bridge.h" | 19 #include "chrome/browser/importer/importer_bridge.h" |
| 23 #include "chrome/browser/importer/importer_util.h" | 20 #include "chrome/browser/importer/importer_util.h" |
| 24 #include "chrome/browser/importer/mork_reader.h" | |
| 25 #include "chrome/browser/importer/nss_decryptor.h" | |
| 26 #include "chrome/browser/search_engines/template_url.h" | 21 #include "chrome/browser/search_engines/template_url.h" |
| 27 #include "chrome/common/time_format.h" | 22 #include "chrome/common/time_format.h" |
| 28 #include "chrome/common/url_constants.h" | 23 #include "chrome/common/url_constants.h" |
| 29 #include "content/public/common/password_form.h" | |
| 30 #include "googleurl/src/gurl.h" | 24 #include "googleurl/src/gurl.h" |
| 31 #include "grit/generated_resources.h" | 25 #include "grit/generated_resources.h" |
| 32 #include "net/base/data_url.h" | 26 #include "net/base/data_url.h" |
| 27 #include "net/base/escape.h" | |
| 33 | 28 |
| 34 namespace { | 29 namespace { |
| 35 const char kItemOpen[] = "<DT><A"; | 30 |
| 36 const char kItemClose[] = "</A>"; | 31 // Fetches the given |attribute| value from the |attribute_list|. Returns true |
| 37 const char kFeedURLAttribute[] = "FEEDURL"; | 32 // if successful, and |value| will contain the value. |
| 38 const char kHrefAttribute[] = "HREF"; | 33 bool GetAttribute(const std::string& attribute_list, |
| 39 const char kIconAttribute[] = "ICON"; | 34 const std::string& attribute, |
| 40 const char kShortcutURLAttribute[] = "SHORTCUTURL"; | 35 std::string* value) { |
| 41 const char kAddDateAttribute[] = "ADD_DATE"; | 36 const char kQuote[] = "\""; |
| 42 const char kPostDataAttribute[] = "POST_DATA"; | 37 |
| 38 size_t begin = attribute_list.find(attribute + "=" + kQuote); | |
| 39 if (begin == std::string::npos) | |
| 40 return false; // Can't find the attribute. | |
| 41 | |
| 42 begin = attribute_list.find(kQuote, begin) + 1; | |
| 43 | |
| 44 size_t end = begin + 1; | |
| 45 while (end < attribute_list.size()) { | |
| 46 if (attribute_list[end] == '"' && | |
| 47 attribute_list[end - 1] != '\\') { | |
| 48 break; | |
| 49 } | |
| 50 end++; | |
| 51 } | |
| 52 | |
| 53 if (end == attribute_list.size()) | |
| 54 return false; // The value is not quoted. | |
| 55 | |
| 56 *value = attribute_list.substr(begin, end - begin); | |
| 57 return true; | |
| 43 } | 58 } |
| 44 | 59 |
| 45 Firefox2Importer::Firefox2Importer() : parsing_bookmarks_html_file_(false) {} | 60 // Given the URL of a page and a favicon data URL, adds an appropriate record |
| 61 // to the given favicon usage vector. Will do nothing if the favicon is not | |
| 62 // valid. | |
| 63 void DataURLToFaviconUsage( | |
| 64 const GURL& link_url, | |
| 65 const GURL& favicon_data, | |
| 66 std::vector<history::ImportedFaviconUsage>* favicons) { | |
| 67 if (!link_url.is_valid() || !favicon_data.is_valid() || | |
| 68 !favicon_data.SchemeIs(chrome::kDataScheme)) | |
| 69 return; | |
| 46 | 70 |
| 47 Firefox2Importer::~Firefox2Importer() {} | 71 // Parse the data URL. |
| 72 std::string mime_type, char_set, data; | |
| 73 if (!net::DataURL::Parse(favicon_data, &mime_type, &char_set, &data) || | |
| 74 data.empty()) | |
| 75 return; | |
| 48 | 76 |
| 49 void Firefox2Importer::StartImport( | 77 history::ImportedFaviconUsage usage; |
| 78 if (!importer::ReencodeFavicon( | |
| 79 reinterpret_cast<const unsigned char*>(&data[0]), | |
| 80 data.size(), &usage.png_data)) | |
| 81 return; // Unable to decode. | |
| 82 | |
| 83 // We need to make up a URL for the favicon. We use a version of the page's | |
| 84 // URL so that we can be sure it will not collide. | |
| 85 usage.favicon_url = GURL(std::string("made-up-favicon:") + link_url.spec()); | |
| 86 | |
| 87 // We only have one URL per favicon for Firefox 2 bookmarks. | |
| 88 usage.urls.insert(link_url); | |
| 89 | |
| 90 favicons->push_back(usage); | |
| 91 } | |
| 92 | |
| 93 bool ImporterCancelBridge(Importer* importer) { | |
| 94 return importer->cancelled(); | |
| 95 } | |
| 96 | |
| 97 } // namespace | |
| 98 | |
| 99 BookmarksFileImporter::BookmarksFileImporter() {} | |
| 100 | |
| 101 BookmarksFileImporter::~BookmarksFileImporter() {} | |
| 102 | |
| 103 void BookmarksFileImporter::StartImport( | |
| 50 const importer::SourceProfile& source_profile, | 104 const importer::SourceProfile& source_profile, |
| 51 uint16 items, | 105 uint16 items, |
| 52 ImporterBridge* bridge) { | 106 ImporterBridge* bridge) { |
| 53 bridge_ = bridge; | 107 // The only thing this importer can import is a bookmarks file, aka |
| 54 source_path_ = source_profile.source_path; | 108 // "favorites". |
| 55 app_path_ = source_profile.app_path; | 109 DCHECK_EQ(importer::FAVORITES, items); |
| 56 | 110 |
| 57 parsing_bookmarks_html_file_ = | 111 base::FilePath source_path = source_profile.source_path; |
| 58 (source_profile.importer_type == importer::TYPE_BOOKMARKS_FILE); | 112 base::FilePath app_path = source_profile.app_path; |
| 59 | 113 |
| 60 // The order here is important! | 114 bridge->NotifyStarted(); |
| 61 bridge_->NotifyStarted(); | 115 bridge->NotifyItemStarted(importer::FAVORITES); |
| 62 if ((items & importer::HOME_PAGE) && !cancelled()) | |
| 63 ImportHomepage(); // Doesn't have a UI item. | |
| 64 | 116 |
| 65 // Note history should be imported before bookmarks because bookmark import | 117 std::vector<ProfileWriter::BookmarkEntry> bookmarks; |
| 66 // will also import favicons and we store favicon for a URL only if the URL | 118 std::vector<history::ImportedFaviconUsage> favicons; |
| 67 // exist in history or bookmarks. | 119 base::Callback<bool(void)> cancellation_callback = |
| 68 if ((items & importer::HISTORY) && !cancelled()) { | 120 base::Bind(ImporterCancelBridge, base::Unretained(this)); |
| 69 bridge_->NotifyItemStarted(importer::HISTORY); | 121 |
| 70 ImportHistory(); | 122 ImportBookmarksFile(source_path, |
| 71 bridge_->NotifyItemEnded(importer::HISTORY); | 123 &cancellation_callback, |
| 124 &bookmarks, | |
| 125 &favicons); | |
| 126 | |
| 127 // Write data into profile. | |
| 128 if (!bookmarks.empty() && !cancelled()) { | |
| 129 string16 first_folder_name = bridge->GetLocalizedString(IDS_BOOKMARK_GROUP); | |
| 130 bridge->AddBookmarks(bookmarks, first_folder_name); | |
| 72 } | 131 } |
| 132 if (!favicons.empty()) | |
| 133 bridge->SetFavicons(favicons); | |
| 73 | 134 |
| 74 if ((items & importer::FAVORITES) && !cancelled()) { | 135 bridge->NotifyItemEnded(importer::FAVORITES); |
| 75 bridge_->NotifyItemStarted(importer::FAVORITES); | 136 bridge->NotifyEnded(); |
| 76 ImportBookmarks(); | |
| 77 bridge_->NotifyItemEnded(importer::FAVORITES); | |
| 78 } | |
| 79 if ((items & importer::SEARCH_ENGINES) && !cancelled()) { | |
| 80 bridge_->NotifyItemStarted(importer::SEARCH_ENGINES); | |
| 81 ImportSearchEngines(); | |
| 82 bridge_->NotifyItemEnded(importer::SEARCH_ENGINES); | |
| 83 } | |
| 84 if ((items & importer::PASSWORDS) && !cancelled()) { | |
| 85 bridge_->NotifyItemStarted(importer::PASSWORDS); | |
| 86 ImportPasswords(); | |
| 87 bridge_->NotifyItemEnded(importer::PASSWORDS); | |
| 88 } | |
| 89 bridge_->NotifyEnded(); | |
| 90 } | 137 } |
| 91 | 138 |
| 92 // static | 139 // static |
| 93 void Firefox2Importer::LoadDefaultBookmarks(const base::FilePath& app_path, | 140 void BookmarksFileImporter::ImportBookmarksFile( |
| 94 std::set<GURL> *urls) { | |
| 95 base::FilePath file = app_path.AppendASCII("defaults") | |
| 96 .AppendASCII("profile") | |
| 97 .AppendASCII("bookmarks.html"); | |
| 98 | |
| 99 urls->clear(); | |
| 100 | |
| 101 // Read the whole file. | |
| 102 std::string content; | |
| 103 file_util::ReadFileToString(file, &content); | |
| 104 std::vector<std::string> lines; | |
| 105 base::SplitString(content, '\n', &lines); | |
| 106 | |
| 107 std::string charset; | |
| 108 for (size_t i = 0; i < lines.size(); ++i) { | |
| 109 std::string line; | |
| 110 TrimString(lines[i], " ", &line); | |
| 111 | |
| 112 // Get the encoding of the bookmark file. | |
| 113 if (ParseCharsetFromLine(line, &charset)) | |
| 114 continue; | |
| 115 | |
| 116 // Get the bookmark. | |
| 117 string16 title; | |
| 118 GURL url, favicon; | |
| 119 string16 shortcut; | |
| 120 base::Time add_date; | |
| 121 string16 post_data; | |
| 122 if (ParseBookmarkFromLine(line, charset, &title, &url, | |
| 123 &favicon, &shortcut, &add_date, | |
| 124 &post_data)) | |
| 125 urls->insert(url); | |
| 126 } | |
| 127 } | |
| 128 | |
| 129 // static | |
| 130 TemplateURL* Firefox2Importer::CreateTemplateURL(const string16& title, | |
| 131 const string16& keyword, | |
| 132 const GURL& url) { | |
| 133 // Skip if the keyword or url is invalid. | |
| 134 if (keyword.empty() || !url.is_valid()) | |
| 135 return NULL; | |
| 136 | |
| 137 TemplateURLData data; | |
| 138 // We set short name by using the title if it exists. | |
| 139 // Otherwise, we use the shortcut. | |
| 140 data.short_name = title.empty() ? keyword : title; | |
| 141 data.SetKeyword(keyword); | |
| 142 data.SetURL(TemplateURLRef::DisplayURLToURLRef(UTF8ToUTF16(url.spec()))); | |
| 143 return new TemplateURL(NULL, data); | |
| 144 } | |
| 145 | |
| 146 // static | |
| 147 void Firefox2Importer::ImportBookmarksFile( | |
| 148 const base::FilePath& file_path, | 141 const base::FilePath& file_path, |
| 149 const std::set<GURL>& default_urls, | 142 base::Callback<bool(void)>* cancellation_callback, |
| 150 Importer* importer, | |
| 151 std::vector<ProfileWriter::BookmarkEntry>* bookmarks, | 143 std::vector<ProfileWriter::BookmarkEntry>* bookmarks, |
| 152 std::vector<TemplateURL*>* template_urls, | |
| 153 std::vector<history::ImportedFaviconUsage>* favicons) { | 144 std::vector<history::ImportedFaviconUsage>* favicons) { |
| 154 std::string content; | 145 std::string content; |
| 155 file_util::ReadFileToString(file_path, &content); | 146 file_util::ReadFileToString(file_path, &content); |
| 156 std::vector<std::string> lines; | 147 std::vector<std::string> lines; |
| 157 base::SplitString(content, '\n', &lines); | 148 base::SplitString(content, '\n', &lines); |
| 158 | 149 |
| 159 string16 last_folder; | 150 string16 last_folder; |
| 160 bool last_folder_on_toolbar = false; | 151 bool last_folder_on_toolbar = false; |
| 161 bool last_folder_is_empty = true; | 152 bool last_folder_is_empty = true; |
| 162 bool has_subfolder = false; | 153 bool has_subfolder = false; |
| 163 base::Time last_folder_add_date; | 154 base::Time last_folder_add_date; |
| 164 std::vector<string16> path; | 155 std::vector<string16> path; |
| 165 size_t toolbar_folder = 0; | 156 size_t toolbar_folder = 0; |
| 166 std::string charset; | 157 std::string charset; |
| 167 for (size_t i = 0; i < lines.size() && (!importer || !importer->cancelled()); | 158 for (size_t i = 0; |
| 159 i < lines.size() && | |
| 160 (!cancellation_callback || !cancellation_callback->Run()); | |
| 168 ++i) { | 161 ++i) { |
| 169 std::string line; | 162 std::string line; |
| 170 TrimString(lines[i], " ", &line); | 163 TrimString(lines[i], " ", &line); |
| 171 | 164 |
| 172 // Get the encoding of the bookmark file. | 165 // Get the encoding of the bookmark file. |
| 173 if (ParseCharsetFromLine(line, &charset)) | 166 if (ParseCharsetFromLine(line, &charset)) |
| 174 continue; | 167 continue; |
| 175 | 168 |
| 176 // Get the folder name. | 169 // Get the folder name. |
| 177 if (ParseFolderNameFromLine(line, charset, &last_folder, | 170 if (ParseFolderNameFromLine(line, charset, &last_folder, |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 191 is_bookmark = ParseBookmarkFromLine(line, charset, &title, | 184 is_bookmark = ParseBookmarkFromLine(line, charset, &title, |
| 192 &url, &favicon, &shortcut, &add_date, | 185 &url, &favicon, &shortcut, &add_date, |
| 193 &post_data) || | 186 &post_data) || |
| 194 ParseMinimumBookmarkFromLine(line, charset, &title, &url); | 187 ParseMinimumBookmarkFromLine(line, charset, &title, &url); |
| 195 | 188 |
| 196 if (is_bookmark) | 189 if (is_bookmark) |
| 197 last_folder_is_empty = false; | 190 last_folder_is_empty = false; |
| 198 | 191 |
| 199 if (is_bookmark && | 192 if (is_bookmark && |
| 200 post_data.empty() && | 193 post_data.empty() && |
| 201 CanImportURL(GURL(url)) && | 194 CanImportURL(GURL(url))) { |
| 202 default_urls.find(url) == default_urls.end()) { | |
| 203 if (toolbar_folder > path.size() && !path.empty()) { | 195 if (toolbar_folder > path.size() && !path.empty()) { |
| 204 NOTREACHED(); // error in parsing. | 196 NOTREACHED(); // error in parsing. |
| 205 break; | 197 break; |
| 206 } | 198 } |
| 207 | 199 |
| 208 ProfileWriter::BookmarkEntry entry; | 200 ProfileWriter::BookmarkEntry entry; |
| 209 entry.creation_time = add_date; | 201 entry.creation_time = add_date; |
| 210 entry.url = url; | 202 entry.url = url; |
| 211 entry.title = title; | 203 entry.title = title; |
| 212 | 204 |
| 213 if (toolbar_folder) { | 205 if (toolbar_folder) { |
| 214 // The toolbar folder should be at the top level. | 206 // The toolbar folder should be at the top level. |
| 215 entry.in_toolbar = true; | 207 entry.in_toolbar = true; |
| 216 entry.path.assign(path.begin() + toolbar_folder - 1, path.end()); | 208 entry.path.assign(path.begin() + toolbar_folder - 1, path.end()); |
| 217 } else { | 209 } else { |
| 218 // Add this bookmark to the list of |bookmarks|. | 210 // Add this bookmark to the list of |bookmarks|. |
| 219 if (!has_subfolder && !last_folder.empty()) { | 211 if (!has_subfolder && !last_folder.empty()) { |
| 220 path.push_back(last_folder); | 212 path.push_back(last_folder); |
| 221 last_folder.clear(); | 213 last_folder.clear(); |
| 222 } | 214 } |
| 223 entry.path.assign(path.begin(), path.end()); | 215 entry.path.assign(path.begin(), path.end()); |
| 224 } | 216 } |
| 225 bookmarks->push_back(entry); | 217 bookmarks->push_back(entry); |
| 226 | 218 |
| 227 // Save the favicon. DataURLToFaviconUsage will handle the case where | 219 // Save the favicon. DataURLToFaviconUsage will handle the case where |
| 228 // there is no favicon. | 220 // there is no favicon. |
| 229 if (favicons) | 221 if (favicons) |
| 230 DataURLToFaviconUsage(url, favicon, favicons); | 222 DataURLToFaviconUsage(url, favicon, favicons); |
| 231 | 223 |
| 232 if (template_urls) { | |
| 233 // If there is a SHORTCUT attribute for this bookmark, we | |
| 234 // add it as our keywords. | |
| 235 TemplateURL* t_url = CreateTemplateURL(title, shortcut, url); | |
| 236 if (t_url) | |
| 237 template_urls->push_back(t_url); | |
| 238 } | |
| 239 | |
| 240 continue; | 224 continue; |
| 241 } | 225 } |
| 242 | 226 |
| 243 // Bookmarks in sub-folder are encapsulated with <DL> tag. | 227 // Bookmarks in sub-folder are encapsulated with <DL> tag. |
| 244 if (StartsWithASCII(line, "<DL>", false)) { | 228 if (StartsWithASCII(line, "<DL>", false)) { |
| 245 has_subfolder = true; | 229 has_subfolder = true; |
| 246 if (!last_folder.empty()) { | 230 if (!last_folder.empty()) { |
| 247 path.push_back(last_folder); | 231 path.push_back(last_folder); |
| 248 last_folder.clear(); | 232 last_folder.clear(); |
| 249 } | 233 } |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 282 // Parent folder include current one, so it's not empty. | 266 // Parent folder include current one, so it's not empty. |
| 283 last_folder_is_empty = false; | 267 last_folder_is_empty = false; |
| 284 } | 268 } |
| 285 | 269 |
| 286 if (toolbar_folder > path.size()) | 270 if (toolbar_folder > path.size()) |
| 287 toolbar_folder = 0; | 271 toolbar_folder = 0; |
| 288 } | 272 } |
| 289 } | 273 } |
| 290 } | 274 } |
| 291 | 275 |
| 292 void Firefox2Importer::ImportBookmarks() { | |
| 293 // Load the default bookmarks. | |
| 294 std::set<GURL> default_urls; | |
| 295 if (!parsing_bookmarks_html_file_) | |
| 296 LoadDefaultBookmarks(app_path_, &default_urls); | |
| 297 | |
| 298 // Parse the bookmarks.html file. | |
| 299 std::vector<ProfileWriter::BookmarkEntry> bookmarks, toolbar_bookmarks; | |
| 300 std::vector<TemplateURL*> template_urls; | |
| 301 std::vector<history::ImportedFaviconUsage> favicons; | |
| 302 base::FilePath file = source_path_; | |
| 303 if (!parsing_bookmarks_html_file_) | |
| 304 file = file.AppendASCII("bookmarks.html"); | |
| 305 | |
| 306 ImportBookmarksFile(file, default_urls, this, &bookmarks, &template_urls, | |
| 307 &favicons); | |
| 308 | |
| 309 // Write data into profile. | |
| 310 if (!bookmarks.empty() && !cancelled()) { | |
| 311 string16 first_folder_name = bridge_->GetLocalizedString( | |
| 312 parsing_bookmarks_html_file_ ? IDS_BOOKMARK_GROUP : | |
| 313 IDS_BOOKMARK_GROUP_FROM_FIREFOX); | |
| 314 bridge_->AddBookmarks(bookmarks, first_folder_name); | |
| 315 } | |
| 316 if (!parsing_bookmarks_html_file_ && !template_urls.empty() && !cancelled()) | |
| 317 bridge_->SetKeywords(template_urls, false); | |
| 318 else | |
| 319 STLDeleteElements(&template_urls); | |
| 320 if (!favicons.empty()) | |
| 321 bridge_->SetFavicons(favicons); | |
| 322 } | |
| 323 | |
| 324 void Firefox2Importer::ImportPasswords() { | |
| 325 // Initializes NSS3. | |
| 326 NSSDecryptor decryptor; | |
| 327 if (!decryptor.Init(source_path_, source_path_) && | |
| 328 !decryptor.Init(app_path_, source_path_)) { | |
| 329 return; | |
| 330 } | |
| 331 | |
| 332 // Firefox 2 uses signons2.txt to store the pssswords. If it doesn't | |
| 333 // exist, we try to find its older version. | |
| 334 base::FilePath file = source_path_.AppendASCII("signons2.txt"); | |
| 335 if (!file_util::PathExists(file)) { | |
| 336 file = source_path_.AppendASCII("signons.txt"); | |
| 337 } | |
| 338 | |
| 339 std::string content; | |
| 340 file_util::ReadFileToString(file, &content); | |
| 341 std::vector<content::PasswordForm> forms; | |
| 342 decryptor.ParseSignons(content, &forms); | |
| 343 | |
| 344 if (!cancelled()) { | |
| 345 for (size_t i = 0; i < forms.size(); ++i) { | |
| 346 bridge_->SetPasswordForm(forms[i]); | |
| 347 } | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 void Firefox2Importer::ImportHistory() { | |
| 352 base::FilePath file = source_path_.AppendASCII("history.dat"); | |
| 353 ImportHistoryFromFirefox2(file, bridge_); | |
| 354 } | |
| 355 | |
| 356 void Firefox2Importer::ImportSearchEngines() { | |
| 357 std::vector<base::FilePath> files; | |
| 358 GetSearchEnginesXMLFiles(&files); | |
| 359 | |
| 360 std::vector<TemplateURL*> search_engines; | |
| 361 ParseSearchEnginesFromXMLFiles(files, &search_engines); | |
| 362 | |
| 363 bridge_->SetKeywords(search_engines, true); | |
| 364 } | |
| 365 | |
| 366 void Firefox2Importer::ImportHomepage() { | |
| 367 GURL home_page = GetHomepage(source_path_); | |
| 368 if (home_page.is_valid() && !IsDefaultHomepage(home_page, app_path_)) { | |
| 369 bridge_->AddHomePage(home_page); | |
| 370 } | |
| 371 } | |
| 372 | |
| 373 void Firefox2Importer::GetSearchEnginesXMLFiles( | |
| 374 std::vector<base::FilePath>* files) { | |
| 375 // Search engines are contained in XML files in a searchplugins directory that | |
| 376 // can be found in 2 locations: | |
| 377 // - Firefox install dir (default search engines) | |
| 378 // - the profile dir (user added search engines) | |
| 379 base::FilePath dir = app_path_.AppendASCII("searchplugins"); | |
| 380 FindXMLFilesInDir(dir, files); | |
| 381 | |
| 382 base::FilePath profile_dir = source_path_.AppendASCII("searchplugins"); | |
| 383 FindXMLFilesInDir(profile_dir, files); | |
| 384 } | |
| 385 | |
| 386 // static | 276 // static |
| 387 bool Firefox2Importer::ParseCharsetFromLine(const std::string& line, | 277 bool BookmarksFileImporter::ParseCharsetFromLine(const std::string& line, |
|
tfarina
2013/05/07 02:15:55
tiny nit: since these are private static functions
Avi (use Gerrit)
2013/05/07 03:11:28
No, they cannot be. These functions are used by th
| |
| 388 std::string* charset) { | 278 std::string* charset) { |
| 389 const char kCharset[] = "charset="; | 279 const char kCharset[] = "charset="; |
| 390 if (StartsWithASCII(line, "<META", false) && | 280 if (StartsWithASCII(line, "<META", false) && |
| 391 (line.find("CONTENT=\"") != std::string::npos || | 281 (line.find("CONTENT=\"") != std::string::npos || |
| 392 line.find("content=\"") != std::string::npos)) { | 282 line.find("content=\"") != std::string::npos)) { |
| 393 size_t begin = line.find(kCharset); | 283 size_t begin = line.find(kCharset); |
| 394 if (begin == std::string::npos) | 284 if (begin == std::string::npos) |
| 395 return false; | 285 return false; |
| 396 begin += std::string(kCharset).size(); | 286 begin += std::string(kCharset).size(); |
| 397 size_t end = line.find_first_of('\"', begin); | 287 size_t end = line.find_first_of('\"', begin); |
| 398 *charset = line.substr(begin, end - begin); | 288 *charset = line.substr(begin, end - begin); |
| 399 return true; | 289 return true; |
| 400 } | 290 } |
| 401 return false; | 291 return false; |
| 402 } | 292 } |
| 403 | 293 |
| 404 // static | 294 // static |
| 405 bool Firefox2Importer::ParseFolderNameFromLine(const std::string& line, | 295 bool BookmarksFileImporter::ParseFolderNameFromLine(const std::string& line, |
| 406 const std::string& charset, | 296 const std::string& charset, |
| 407 string16* folder_name, | 297 string16* folder_name, |
| 408 bool* is_toolbar_folder, | 298 bool* is_toolbar_folder, |
| 409 base::Time* add_date) { | 299 base::Time* add_date) { |
| 410 const char kFolderOpen[] = "<DT><H3"; | 300 const char kFolderOpen[] = "<DT><H3"; |
| 411 const char kFolderClose[] = "</H3>"; | 301 const char kFolderClose[] = "</H3>"; |
| 412 const char kToolbarFolderAttribute[] = "PERSONAL_TOOLBAR_FOLDER"; | 302 const char kToolbarFolderAttribute[] = "PERSONAL_TOOLBAR_FOLDER"; |
| 413 const char kAddDateAttribute[] = "ADD_DATE"; | 303 const char kAddDateAttribute[] = "ADD_DATE"; |
| 414 | 304 |
| 415 if (!StartsWithASCII(line, kFolderOpen, true)) | 305 if (!StartsWithASCII(line, kFolderOpen, true)) |
| 416 return false; | 306 return false; |
| 417 | 307 |
| 418 size_t end = line.find(kFolderClose); | 308 size_t end = line.find(kFolderClose); |
| 419 size_t tag_end = line.rfind('>', end) + 1; | 309 size_t tag_end = line.rfind('>', end) + 1; |
| 420 // If no end tag or start tag is broken, we skip to find the folder name. | 310 // If no end tag or start tag is broken, we skip to find the folder name. |
| 421 if (end == std::string::npos || tag_end < arraysize(kFolderOpen)) | 311 if (end == std::string::npos || tag_end < arraysize(kFolderOpen)) |
| 422 return false; | 312 return false; |
| 423 | 313 |
| 424 base::CodepageToUTF16(line.substr(tag_end, end - tag_end), charset.c_str(), | 314 base::CodepageToUTF16(line.substr(tag_end, end - tag_end), charset.c_str(), |
| 425 base::OnStringConversionError::SKIP, folder_name); | 315 base::OnStringConversionError::SKIP, folder_name); |
| 426 HTMLUnescape(folder_name); | 316 *folder_name = net::UnescapeForHTML(*folder_name); |
| 427 | 317 |
| 428 std::string attribute_list = line.substr(arraysize(kFolderOpen), | 318 std::string attribute_list = line.substr(arraysize(kFolderOpen), |
| 429 tag_end - arraysize(kFolderOpen) - 1); | 319 tag_end - arraysize(kFolderOpen) - 1); |
| 430 std::string value; | 320 std::string value; |
| 431 | 321 |
| 432 // Add date | 322 // Add date |
| 433 if (GetAttribute(attribute_list, kAddDateAttribute, &value)) { | 323 if (GetAttribute(attribute_list, kAddDateAttribute, &value)) { |
| 434 int64 time; | 324 int64 time; |
| 435 base::StringToInt64(value, &time); | 325 base::StringToInt64(value, &time); |
| 436 // Upper bound it at 32 bits. | 326 // Upper bound it at 32 bits. |
| 437 if (0 < time && time < (1LL << 32)) | 327 if (0 < time && time < (1LL << 32)) |
| 438 *add_date = base::Time::FromTimeT(time); | 328 *add_date = base::Time::FromTimeT(time); |
| 439 } | 329 } |
| 440 | 330 |
| 441 if (GetAttribute(attribute_list, kToolbarFolderAttribute, &value) && | 331 if (GetAttribute(attribute_list, kToolbarFolderAttribute, &value) && |
| 442 LowerCaseEqualsASCII(value, "true")) | 332 LowerCaseEqualsASCII(value, "true")) |
| 443 *is_toolbar_folder = true; | 333 *is_toolbar_folder = true; |
| 444 else | 334 else |
| 445 *is_toolbar_folder = false; | 335 *is_toolbar_folder = false; |
| 446 | 336 |
| 447 return true; | 337 return true; |
| 448 } | 338 } |
| 449 | 339 |
| 450 // static | 340 // static |
| 451 bool Firefox2Importer::ParseBookmarkFromLine(const std::string& line, | 341 bool BookmarksFileImporter::ParseBookmarkFromLine(const std::string& line, |
| 452 const std::string& charset, | 342 const std::string& charset, |
| 453 string16* title, | 343 string16* title, |
| 454 GURL* url, | 344 GURL* url, |
| 455 GURL* favicon, | 345 GURL* favicon, |
| 456 string16* shortcut, | 346 string16* shortcut, |
| 457 base::Time* add_date, | 347 base::Time* add_date, |
| 458 string16* post_data) { | 348 string16* post_data) { |
| 349 const char kItemOpen[] = "<DT><A"; | |
| 350 const char kItemClose[] = "</A>"; | |
| 351 const char kFeedURLAttribute[] = "FEEDURL"; | |
| 352 const char kHrefAttribute[] = "HREF"; | |
| 353 const char kIconAttribute[] = "ICON"; | |
| 354 const char kShortcutURLAttribute[] = "SHORTCUTURL"; | |
| 355 const char kAddDateAttribute[] = "ADD_DATE"; | |
| 356 const char kPostDataAttribute[] = "POST_DATA"; | |
| 357 | |
| 459 title->clear(); | 358 title->clear(); |
| 460 *url = GURL(); | 359 *url = GURL(); |
| 461 *favicon = GURL(); | 360 *favicon = GURL(); |
| 462 shortcut->clear(); | 361 shortcut->clear(); |
| 463 post_data->clear(); | 362 post_data->clear(); |
| 464 *add_date = base::Time(); | 363 *add_date = base::Time(); |
| 465 | 364 |
| 466 if (!StartsWithASCII(line, kItemOpen, true)) | 365 if (!StartsWithASCII(line, kItemOpen, true)) |
| 467 return false; | 366 return false; |
| 468 | 367 |
| 469 size_t end = line.find(kItemClose); | 368 size_t end = line.find(kItemClose); |
| 470 size_t tag_end = line.rfind('>', end) + 1; | 369 size_t tag_end = line.rfind('>', end) + 1; |
| 471 if (end == std::string::npos || tag_end < arraysize(kItemOpen)) | 370 if (end == std::string::npos || tag_end < arraysize(kItemOpen)) |
| 472 return false; // No end tag or start tag is broken. | 371 return false; // No end tag or start tag is broken. |
| 473 | 372 |
| 474 std::string attribute_list = line.substr(arraysize(kItemOpen), | 373 std::string attribute_list = line.substr(arraysize(kItemOpen), |
| 475 tag_end - arraysize(kItemOpen) - 1); | 374 tag_end - arraysize(kItemOpen) - 1); |
| 476 | 375 |
| 477 // We don't import Live Bookmark folders, which is Firefox's RSS reading | 376 // We don't import Live Bookmark folders, which is Firefox's RSS reading |
| 478 // feature, since the user never necessarily bookmarked them and we don't | 377 // feature, since the user never necessarily bookmarked them and we don't |
| 479 // have this feature to update their contents. | 378 // have this feature to update their contents. |
| 480 std::string value; | 379 std::string value; |
| 481 if (GetAttribute(attribute_list, kFeedURLAttribute, &value)) | 380 if (GetAttribute(attribute_list, kFeedURLAttribute, &value)) |
| 482 return false; | 381 return false; |
| 483 | 382 |
| 484 // Title | 383 // Title |
| 485 base::CodepageToUTF16(line.substr(tag_end, end - tag_end), charset.c_str(), | 384 base::CodepageToUTF16(line.substr(tag_end, end - tag_end), charset.c_str(), |
| 486 base::OnStringConversionError::SKIP, title); | 385 base::OnStringConversionError::SKIP, title); |
| 487 HTMLUnescape(title); | 386 *title = net::UnescapeForHTML(*title); |
| 488 | 387 |
| 489 // URL | 388 // URL |
| 490 if (GetAttribute(attribute_list, kHrefAttribute, &value)) { | 389 if (GetAttribute(attribute_list, kHrefAttribute, &value)) { |
| 491 string16 url16; | 390 string16 url16; |
| 492 base::CodepageToUTF16(value, charset.c_str(), | 391 base::CodepageToUTF16(value, charset.c_str(), |
| 493 base::OnStringConversionError::SKIP, &url16); | 392 base::OnStringConversionError::SKIP, &url16); |
| 494 HTMLUnescape(&url16); | 393 url16 = net::UnescapeForHTML(url16); |
| 495 | 394 |
| 496 *url = GURL(url16); | 395 *url = GURL(url16); |
| 497 } | 396 } |
| 498 | 397 |
| 499 // Favicon | 398 // Favicon |
| 500 if (GetAttribute(attribute_list, kIconAttribute, &value)) | 399 if (GetAttribute(attribute_list, kIconAttribute, &value)) |
| 501 *favicon = GURL(value); | 400 *favicon = GURL(value); |
| 502 | 401 |
| 503 // Keyword | 402 // Keyword |
| 504 if (GetAttribute(attribute_list, kShortcutURLAttribute, &value)) { | 403 if (GetAttribute(attribute_list, kShortcutURLAttribute, &value)) { |
| 505 base::CodepageToUTF16(value, charset.c_str(), | 404 base::CodepageToUTF16(value, charset.c_str(), |
| 506 base::OnStringConversionError::SKIP, shortcut); | 405 base::OnStringConversionError::SKIP, shortcut); |
| 507 HTMLUnescape(shortcut); | 406 *shortcut = net::UnescapeForHTML(*shortcut); |
| 508 } | 407 } |
| 509 | 408 |
| 510 // Add date | 409 // Add date |
| 511 if (GetAttribute(attribute_list, kAddDateAttribute, &value)) { | 410 if (GetAttribute(attribute_list, kAddDateAttribute, &value)) { |
| 512 int64 time; | 411 int64 time; |
| 513 base::StringToInt64(value, &time); | 412 base::StringToInt64(value, &time); |
| 514 // Upper bound it at 32 bits. | 413 // Upper bound it at 32 bits. |
| 515 if (0 < time && time < (1LL << 32)) | 414 if (0 < time && time < (1LL << 32)) |
| 516 *add_date = base::Time::FromTimeT(time); | 415 *add_date = base::Time::FromTimeT(time); |
| 517 } | 416 } |
| 518 | 417 |
| 519 // Post data. | 418 // Post data. |
| 520 if (GetAttribute(attribute_list, kPostDataAttribute, &value)) { | 419 if (GetAttribute(attribute_list, kPostDataAttribute, &value)) { |
| 521 base::CodepageToUTF16(value, charset.c_str(), | 420 base::CodepageToUTF16(value, charset.c_str(), |
| 522 base::OnStringConversionError::SKIP, post_data); | 421 base::OnStringConversionError::SKIP, post_data); |
| 523 HTMLUnescape(post_data); | 422 *post_data = net::UnescapeForHTML(*post_data); |
| 524 } | 423 } |
| 525 | 424 |
| 526 return true; | 425 return true; |
| 527 } | 426 } |
| 528 | 427 |
| 529 // static | 428 // static |
| 530 bool Firefox2Importer::ParseMinimumBookmarkFromLine(const std::string& line, | 429 bool BookmarksFileImporter::ParseMinimumBookmarkFromLine( |
| 531 const std::string& charset, | 430 const std::string& line, |
| 532 string16* title, | 431 const std::string& charset, |
| 533 GURL* url) { | 432 string16* title, |
| 433 GURL* url) { | |
| 534 const char kItemOpen[] = "<DT><A"; | 434 const char kItemOpen[] = "<DT><A"; |
| 535 const char kItemClose[] = "</"; | 435 const char kItemClose[] = "</"; |
| 536 const char kHrefAttributeUpper[] = "HREF"; | 436 const char kHrefAttributeUpper[] = "HREF"; |
| 537 const char kHrefAttributeLower[] = "href"; | 437 const char kHrefAttributeLower[] = "href"; |
| 538 | 438 |
| 539 title->clear(); | 439 title->clear(); |
| 540 *url = GURL(); | 440 *url = GURL(); |
| 541 | 441 |
| 542 // Case-insensitive check of open tag. | 442 // Case-insensitive check of open tag. |
| 543 if (!StartsWithASCII(line, kItemOpen, false)) | 443 if (!StartsWithASCII(line, kItemOpen, false)) |
| 544 return false; | 444 return false; |
| 545 | 445 |
| 546 // Find any close tag. | 446 // Find any close tag. |
| 547 size_t end = line.find(kItemClose); | 447 size_t end = line.find(kItemClose); |
| 548 size_t tag_end = line.rfind('>', end) + 1; | 448 size_t tag_end = line.rfind('>', end) + 1; |
| 549 if (end == std::string::npos || tag_end < arraysize(kItemOpen)) | 449 if (end == std::string::npos || tag_end < arraysize(kItemOpen)) |
| 550 return false; // No end tag or start tag is broken. | 450 return false; // No end tag or start tag is broken. |
| 551 | 451 |
| 552 std::string attribute_list = line.substr(arraysize(kItemOpen), | 452 std::string attribute_list = line.substr(arraysize(kItemOpen), |
| 553 tag_end - arraysize(kItemOpen) - 1); | 453 tag_end - arraysize(kItemOpen) - 1); |
| 554 | 454 |
| 555 // Title | 455 // Title |
| 556 base::CodepageToUTF16(line.substr(tag_end, end - tag_end), charset.c_str(), | 456 base::CodepageToUTF16(line.substr(tag_end, end - tag_end), charset.c_str(), |
| 557 base::OnStringConversionError::SKIP, title); | 457 base::OnStringConversionError::SKIP, title); |
| 558 HTMLUnescape(title); | 458 *title = net::UnescapeForHTML(*title); |
| 559 | 459 |
| 560 // URL | 460 // URL |
| 561 std::string value; | 461 std::string value; |
| 562 if (GetAttribute(attribute_list, kHrefAttributeUpper, &value) || | 462 if (GetAttribute(attribute_list, kHrefAttributeUpper, &value) || |
| 563 GetAttribute(attribute_list, kHrefAttributeLower, &value)) { | 463 GetAttribute(attribute_list, kHrefAttributeLower, &value)) { |
| 564 if (charset.length() != 0) { | 464 if (charset.length() != 0) { |
| 565 string16 url16; | 465 string16 url16; |
| 566 base::CodepageToUTF16(value, charset.c_str(), | 466 base::CodepageToUTF16(value, charset.c_str(), |
| 567 base::OnStringConversionError::SKIP, &url16); | 467 base::OnStringConversionError::SKIP, &url16); |
| 568 HTMLUnescape(&url16); | 468 url16 = net::UnescapeForHTML(url16); |
| 569 | 469 |
| 570 *url = GURL(url16); | 470 *url = GURL(url16); |
| 571 } else { | 471 } else { |
| 572 *url = GURL(value); | 472 *url = GURL(value); |
| 573 } | 473 } |
| 574 } | 474 } |
| 575 | 475 |
| 576 return true; | 476 return true; |
| 577 } | 477 } |
| 578 | |
| 579 // static | |
| 580 bool Firefox2Importer::GetAttribute(const std::string& attribute_list, | |
| 581 const std::string& attribute, | |
| 582 std::string* value) { | |
| 583 const char kQuote[] = "\""; | |
| 584 | |
| 585 size_t begin = attribute_list.find(attribute + "=" + kQuote); | |
| 586 if (begin == std::string::npos) | |
| 587 return false; // Can't find the attribute. | |
| 588 | |
| 589 begin = attribute_list.find(kQuote, begin) + 1; | |
| 590 | |
| 591 size_t end = begin + 1; | |
| 592 while (end < attribute_list.size()) { | |
| 593 if (attribute_list[end] == '"' && | |
| 594 attribute_list[end - 1] != '\\') { | |
| 595 break; | |
| 596 } | |
| 597 end++; | |
| 598 } | |
| 599 | |
| 600 if (end == attribute_list.size()) | |
| 601 return false; // The value is not quoted. | |
| 602 | |
| 603 *value = attribute_list.substr(begin, end - begin); | |
| 604 return true; | |
| 605 } | |
| 606 | |
| 607 // static | |
| 608 void Firefox2Importer::HTMLUnescape(string16* text) { | |
| 609 string16 text16 = *text; | |
| 610 ReplaceSubstringsAfterOffset( | |
| 611 &text16, 0, ASCIIToUTF16("<"), ASCIIToUTF16("<")); | |
| 612 ReplaceSubstringsAfterOffset( | |
| 613 &text16, 0, ASCIIToUTF16(">"), ASCIIToUTF16(">")); | |
| 614 ReplaceSubstringsAfterOffset( | |
| 615 &text16, 0, ASCIIToUTF16("&"), ASCIIToUTF16("&")); | |
| 616 ReplaceSubstringsAfterOffset( | |
| 617 &text16, 0, ASCIIToUTF16("""), ASCIIToUTF16("\"")); | |
| 618 ReplaceSubstringsAfterOffset( | |
| 619 &text16, 0, ASCIIToUTF16("'"), ASCIIToUTF16("\'")); | |
| 620 text->assign(text16); | |
| 621 } | |
| 622 | |
| 623 // static | |
| 624 void Firefox2Importer::FindXMLFilesInDir( | |
| 625 const base::FilePath& dir, | |
| 626 std::vector<base::FilePath>* xml_files) { | |
| 627 file_util::FileEnumerator file_enum(dir, false, | |
| 628 file_util::FileEnumerator::FILES, | |
| 629 FILE_PATH_LITERAL("*.xml")); | |
| 630 base::FilePath file(file_enum.Next()); | |
| 631 while (!file.empty()) { | |
| 632 xml_files->push_back(file); | |
| 633 file = file_enum.Next(); | |
| 634 } | |
| 635 } | |
| 636 | |
| 637 // static | |
| 638 void Firefox2Importer::DataURLToFaviconUsage( | |
| 639 const GURL& link_url, | |
| 640 const GURL& favicon_data, | |
| 641 std::vector<history::ImportedFaviconUsage>* favicons) { | |
| 642 if (!link_url.is_valid() || !favicon_data.is_valid() || | |
| 643 !favicon_data.SchemeIs(chrome::kDataScheme)) | |
| 644 return; | |
| 645 | |
| 646 // Parse the data URL. | |
| 647 std::string mime_type, char_set, data; | |
| 648 if (!net::DataURL::Parse(favicon_data, &mime_type, &char_set, &data) || | |
| 649 data.empty()) | |
| 650 return; | |
| 651 | |
| 652 history::ImportedFaviconUsage usage; | |
| 653 if (!importer::ReencodeFavicon( | |
| 654 reinterpret_cast<const unsigned char*>(&data[0]), | |
| 655 data.size(), &usage.png_data)) | |
| 656 return; // Unable to decode. | |
| 657 | |
| 658 // We need to make up a URL for the favicon. We use a version of the page's | |
| 659 // URL so that we can be sure it will not collide. | |
| 660 usage.favicon_url = GURL(std::string("made-up-favicon:") + link_url.spec()); | |
| 661 | |
| 662 // We only have one URL per favicon for Firefox 2 bookmarks. | |
| 663 usage.urls.insert(link_url); | |
| 664 | |
| 665 favicons->push_back(usage); | |
| 666 } | |
| OLD | NEW |