Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(482)

Side by Side Diff: chrome/browser/importer/bookmarks_file_importer.cc

Issue 14575004: Extract BookmarksFileImporter from Firefox2Importer. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: extraneous include Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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))
gab 2013/05/07 17:47:54 #include "content/public/common/url_constants.h" f
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
gab 2013/05/07 17:47:54 Add a section comment: //////////////////////////
tfarina 2013/05/07 18:02:55 I wouldn't encourage the use of this kind of comme
Avi (use Gerrit) 2013/05/07 18:29:39 Most code doesn't use comments like that; not goin
gab 2013/05/08 16:24:34 Ok, I'd seen it around and thought it was useful w
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;
gab 2013/05/07 17:47:54 can be inline on line 122
58 (source_profile.importer_type == importer::TYPE_BOOKMARKS_FILE); 112 base::FilePath app_path = source_profile.app_path;
gab 2013/05/07 17:47:54 unused.
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.
gab 2013/05/07 17:47:54 // Send bookmarks over the bridge. ? (although th
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;
gab 2013/05/07 17:47:54 Took me some time to understand what this variable
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
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.
gab 2013/05/07 17:47:54 Why remove support for keywords?
Avi (use Gerrit) 2013/05/07 18:29:39 Because it's a feature of the FF2 importer, not of
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 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
281 265
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
gab 2013/05/07 17:47:54 Add a section comment: //////////////////////////
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,
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";
gab 2013/05/07 17:47:54 optional: The compiler should optimize this by put
tfarina 2013/05/07 18:05:52 There was a thread about this: https://groups.goo
Avi (use Gerrit) 2013/05/07 18:29:39 gab: Does that article refer to declarations _insi
gab 2013/05/08 16:24:34 Ah right, it refers to file scope, not local metho
Avi (use Gerrit) 2013/05/08 18:30:27 I'd like to take a pass on this for now. It would
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";
gab 2013/05/07 17:47:54 optional: static (see comment above).
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("&lt;"), ASCIIToUTF16("<"));
612 ReplaceSubstringsAfterOffset(
613 &text16, 0, ASCIIToUTF16("&gt;"), ASCIIToUTF16(">"));
614 ReplaceSubstringsAfterOffset(
615 &text16, 0, ASCIIToUTF16("&amp;"), ASCIIToUTF16("&"));
616 ReplaceSubstringsAfterOffset(
617 &text16, 0, ASCIIToUTF16("&quot;"), ASCIIToUTF16("\""));
618 ReplaceSubstringsAfterOffset(
619 &text16, 0, ASCIIToUTF16("&#39;"), 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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698