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