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

Side by Side Diff: chrome/utility/importer/firefox_importer.cc

Issue 2451223004: Improve Firefox importer to handle all Firefox profiles. (Closed)
Patch Set: Created 4 years, 1 month 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
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 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/utility/importer/firefox_importer.h" 5 #include "chrome/utility/importer/firefox_importer.h"
6 6
7 #include <memory> 7 #include <memory>
8 #include <set> 8 #include <set>
9 9
10 #include "base/files/file_enumerator.h" 10 #include "base/files/file_enumerator.h"
11 #include "base/files/file_util.h" 11 #include "base/files/file_util.h"
12 #include "base/json/json_file_value_serializer.h" 12 #include "base/json/json_file_value_serializer.h"
13 #include "base/macros.h" 13 #include "base/macros.h"
14 #include "base/message_loop/message_loop.h" 14 #include "base/message_loop/message_loop.h"
15 #include "base/stl_util.h" 15 #include "base/stl_util.h"
16 #include "base/strings/string_util.h" 16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h" 17 #include "base/strings/utf_string_conversions.h"
18 #include "build/build_config.h" 18 #include "build/build_config.h"
19 #include "chrome/common/importer/firefox_importer_utils.h" 19 #include "chrome/common/importer/firefox_importer_utils.h"
20 #include "chrome/common/importer/imported_bookmark_entry.h" 20 #include "chrome/common/importer/imported_bookmark_entry.h"
21 #include "chrome/common/importer/importer_autofill_form_data_entry.h" 21 #include "chrome/common/importer/importer_autofill_form_data_entry.h"
22 #include "chrome/common/importer/importer_bridge.h" 22 #include "chrome/common/importer/importer_bridge.h"
23 #include "chrome/common/importer/importer_url_row.h" 23 #include "chrome/common/importer/importer_url_row.h"
24 #include "chrome/grit/generated_resources.h" 24 #include "chrome/grit/generated_resources.h"
25 #include "chrome/utility/importer/bookmark_html_reader.h" 25 #include "chrome/utility/importer/bookmark_html_reader.h"
26 #include "chrome/utility/importer/favicon_reencode.h" 26 #include "chrome/utility/importer/firefox_places_factory.h"
27 #include "chrome/utility/importer/nss_decryptor.h" 27 #include "chrome/utility/importer/nss_decryptor.h"
28 #include "components/autofill/core/common/password_form.h" 28 #include "components/autofill/core/common/password_form.h"
29 #include "sql/connection.h" 29 #include "sql/connection.h"
30 #include "sql/statement.h" 30 #include "sql/statement.h"
31 #include "url/gurl.h" 31 #include "url/gurl.h"
32 32
33 namespace { 33 namespace {
34 34
35 // Original definition is in http://mxr.mozilla.org/firefox/source/toolkit/
36 // components/places/public/nsINavBookmarksService.idl
37 enum BookmarkItemType {
38 TYPE_BOOKMARK = 1,
39 TYPE_FOLDER = 2,
40 TYPE_SEPARATOR = 3,
41 TYPE_DYNAMIC_CONTAINER = 4
42 };
43
44 // Loads the default bookmarks in the Firefox installed at |app_path|, 35 // Loads the default bookmarks in the Firefox installed at |app_path|,
45 // and stores their locations in |urls|. 36 // and stores their locations in |urls|.
46 void LoadDefaultBookmarks(const base::FilePath& app_path, 37 void LoadDefaultBookmarks(const base::FilePath& app_path,
47 std::set<GURL>* urls) { 38 std::set<GURL>* urls) {
48 base::FilePath file = app_path.AppendASCII("defaults") 39 base::FilePath file = app_path.AppendASCII("defaults")
49 .AppendASCII("profile") 40 .AppendASCII("profile")
50 .AppendASCII("bookmarks.html"); 41 .AppendASCII("bookmarks.html");
51 urls->clear(); 42 urls->clear();
52 43
53 std::vector<ImportedBookmarkEntry> bookmarks; 44 std::vector<ImportedBookmarkEntry> bookmarks;
(...skipping 20 matching lines...) Expand all
74 for (size_t i = 0; i < arraysize(kInvalidSchemes); ++i) { 65 for (size_t i = 0; i < arraysize(kInvalidSchemes); ++i) {
75 if (url.SchemeIs(kInvalidSchemes[i])) 66 if (url.SchemeIs(kInvalidSchemes[i]))
76 return false; 67 return false;
77 } 68 }
78 69
79 return true; 70 return true;
80 } 71 }
81 72
82 } // namespace 73 } // namespace
83 74
84 struct FirefoxImporter::BookmarkItem {
85 int parent;
86 int id;
87 GURL url;
88 base::string16 title;
89 BookmarkItemType type;
90 std::string keyword;
91 base::Time date_added;
92 int64_t favicon;
93 bool empty_folder;
94 };
95
96 FirefoxImporter::FirefoxImporter() { 75 FirefoxImporter::FirefoxImporter() {
97 } 76 }
98 77
99 FirefoxImporter::~FirefoxImporter() { 78 FirefoxImporter::~FirefoxImporter() {
100 } 79 }
101 80
102 void FirefoxImporter::StartImport(const importer::SourceProfile& source_profile, 81 void FirefoxImporter::StartImport(const importer::SourceProfile& source_profile,
103 uint16_t items, 82 uint16_t items,
104 ImporterBridge* bridge) { 83 ImporterBridge* bridge) {
105 bridge_ = bridge; 84 bridge_ = bridge;
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 } 123 }
145 if ((items & importer::AUTOFILL_FORM_DATA) && !cancelled()) { 124 if ((items & importer::AUTOFILL_FORM_DATA) && !cancelled()) {
146 bridge_->NotifyItemStarted(importer::AUTOFILL_FORM_DATA); 125 bridge_->NotifyItemStarted(importer::AUTOFILL_FORM_DATA);
147 ImportAutofillFormData(); 126 ImportAutofillFormData();
148 bridge_->NotifyItemEnded(importer::AUTOFILL_FORM_DATA); 127 bridge_->NotifyItemEnded(importer::AUTOFILL_FORM_DATA);
149 } 128 }
150 bridge_->NotifyEnded(); 129 bridge_->NotifyEnded();
151 } 130 }
152 131
153 void FirefoxImporter::ImportHistory() { 132 void FirefoxImporter::ImportHistory() {
154 base::FilePath file = source_path_.AppendASCII("places.sqlite"); 133 auto places = FirefoxPlacesFactory::GetForProfile(source_path_);
155 if (!base::PathExists(file)) 134 if (!places)
156 return; 135 return;
157 136
158 sql::Connection db;
159 if (!db.Open(file))
160 return;
161
162 // |visit_type| represent the transition type of URLs (typed, click,
163 // redirect, bookmark, etc.) We eliminate some URLs like sub-frames and
164 // redirects, since we don't want them to appear in history.
165 // Firefox transition types are defined in:
166 // toolkit/components/places/public/nsINavHistoryService.idl
167 const char query[] =
168 "SELECT h.url, h.title, h.visit_count, "
169 "h.hidden, h.typed, v.visit_date "
170 "FROM moz_places h JOIN moz_historyvisits v "
171 "ON h.id = v.place_id "
172 "WHERE v.visit_type <= 3";
173
174 sql::Statement s(db.GetUniqueStatement(query));
175
176 std::vector<ImporterURLRow> rows; 137 std::vector<ImporterURLRow> rows;
177 while (s.Step() && !cancelled()) { 138 places->LoadHistory(&rows);
178 GURL url(s.ColumnString(0)); 139 rows.erase(begin(rows),
179 140 std::remove_if(begin(rows), end(rows), [](const ImporterURLRow& row ) {
180 // Filter out unwanted URLs. 141 return CanImportURL(row.url);
181 if (!CanImportURL(url)) 142 }));
182 continue;
183
184 ImporterURLRow row(url);
185 row.title = s.ColumnString16(1);
186 row.visit_count = s.ColumnInt(2);
187 row.hidden = s.ColumnInt(3) == 1;
188 row.typed_count = s.ColumnInt(4);
189 row.last_visit = base::Time::FromTimeT(s.ColumnInt64(5)/1000000);
190
191 rows.push_back(row);
192 }
193
194 if (!rows.empty() && !cancelled()) 143 if (!rows.empty() && !cancelled())
195 bridge_->SetHistoryItems(rows, importer::VISIT_SOURCE_FIREFOX_IMPORTED); 144 bridge_->SetHistoryItems(rows, importer::VISIT_SOURCE_FIREFOX_IMPORTED);
196 } 145 }
197 146
198 void FirefoxImporter::ImportBookmarks() { 147 void FirefoxImporter::ImportBookmarks() {
199 base::FilePath file = source_path_.AppendASCII("places.sqlite"); 148 auto places = FirefoxPlacesFactory::GetForProfile(source_path_);
200 if (!base::PathExists(file)) 149 if (!places)
150 return;
151 BookmarkList list;
152 if (!places->LoadBookmarks(&list))
153 return;
154 // TODO(jcampan): http://b/issue?id=1196285 we do not support POST based
155 // keywords yet. We won't include them in the list.
156 PostKeywordIds post_keyword_ids;
157 if (!places->LoadPostKeywordIds(&post_keyword_ids))
201 return; 158 return;
202 159
203 sql::Connection db; 160 // Load livemark IDs.
204 if (!db.Open(file)) 161 LivemarkIds livemark_ids;
162 if (!places->LoadLivemarkIds(&livemark_ids))
205 return; 163 return;
206 164
207 // Get the bookmark folders that we are interested in.
208 int toolbar_folder_id = -1;
209 int menu_folder_id = -1;
210 int unsorted_folder_id = -1;
211 LoadRootNodeID(&db, &toolbar_folder_id, &menu_folder_id, &unsorted_folder_id);
212
213 // Load livemark IDs.
214 std::set<int> livemark_id;
215 LoadLivemarkIDs(&db, &livemark_id);
216
217 // Load the default bookmarks. 165 // Load the default bookmarks.
218 std::set<GURL> default_urls; 166 std::set<GURL> default_urls;
219 LoadDefaultBookmarks(app_path_, &default_urls); 167 LoadDefaultBookmarks(app_path_, &default_urls);
220 168
221 BookmarkList list; 169 auto toolbar_folder_id = places->GetFolderId(FirefoxPlaces::FolderType::TOOLBA R);
222 GetTopBookmarkFolder(&db, toolbar_folder_id, &list); 170 auto menu_folder_id = places->GetFolderId(FirefoxPlaces::FolderType::MENU);
223 GetTopBookmarkFolder(&db, menu_folder_id, &list); 171 auto unsorted_folder_id = places->GetFolderId(FirefoxPlaces::FolderType::UNSOR TED);
224 GetTopBookmarkFolder(&db, unsorted_folder_id, &list);
225 size_t count = list.size();
226 for (size_t i = 0; i < count; ++i)
227 GetWholeBookmarkFolder(&db, &list, i, NULL);
228 172
229 std::vector<ImportedBookmarkEntry> bookmarks; 173 std::vector<ImportedBookmarkEntry> bookmarks;
230 std::vector<importer::SearchEngineInfo> search_engines; 174 std::vector<importer::SearchEngineInfo> search_engines;
231 FaviconMap favicon_map; 175 FaviconMap favicon_map;
232 176
233 // TODO(jcampan): http://b/issue?id=1196285 we do not support POST based
234 // keywords yet. We won't include them in the list.
235 std::set<int> post_keyword_ids;
236 const char query[] =
237 "SELECT b.id FROM moz_bookmarks b "
238 "INNER JOIN moz_items_annos ia ON ia.item_id = b.id "
239 "INNER JOIN moz_anno_attributes aa ON ia.anno_attribute_id = aa.id "
240 "WHERE aa.name = 'bookmarkProperties/POSTData'";
241 sql::Statement s(db.GetUniqueStatement(query));
242
243 if (!s.is_valid())
244 return;
245
246 while (s.Step() && !cancelled())
247 post_keyword_ids.insert(s.ColumnInt(0));
248
249 for (size_t i = 0; i < list.size(); ++i) { 177 for (size_t i = 0; i < list.size(); ++i) {
250 BookmarkItem* item = list[i]; 178 BookmarkItem* item = list[i];
251
252 // Folders are added implicitly on adding children, so we only explicitly 179 // Folders are added implicitly on adding children, so we only explicitly
253 // add empty folders. 180 // add empty folders.
254 if (item->type != TYPE_BOOKMARK && 181 if (item->type != TYPE_BOOKMARK &&
255 ((item->type != TYPE_FOLDER) || !item->empty_folder)) 182 ((item->type != TYPE_FOLDER) || !item->empty_folder))
256 continue; 183 continue;
257 184
258 if (CanImportURL(item->url)) { 185 if (CanImportURL(item->url)) {
259 // Skip the default bookmarks and unwanted URLs. 186 // Skip the default bookmarks and unwanted URLs.
260 if (default_urls.find(item->url) != default_urls.end() || 187 if (default_urls.find(item->url) != default_urls.end() ||
261 post_keyword_ids.find(item->id) != post_keyword_ids.end()) 188 post_keyword_ids.find(item->id) != post_keyword_ids.end())
262 continue; 189 continue;
263 190
264 // Find the bookmark path by tracing their links to parent folders. 191 // Find the bookmark path by tracing their links to parent folders.
265 std::vector<base::string16> path; 192 std::vector<base::string16> path;
266 BookmarkItem* child = item; 193 BookmarkItem* child = item;
267 bool found_path = false; 194 bool found_path = false;
268 bool is_in_toolbar = false; 195 bool is_in_toolbar = false;
269 while (child->parent >= 0) { 196 while (child->parent >= 0) {
270 BookmarkItem* parent = list[child->parent]; 197 BookmarkItem* parent = list[child->parent];
271 if (livemark_id.find(parent->id) != livemark_id.end()) { 198 if (livemark_ids.find(parent->id) != livemark_ids.end()) {
272 // Don't import live bookmarks. 199 // Don't import live bookmarks.
273 break; 200 break;
274 } 201 }
275 202
276 if (parent->id != menu_folder_id) { 203 if (parent->id != menu_folder_id) {
277 // To avoid excessive nesting, omit the name for the bookmarks menu 204 // To avoid excessive nesting, omit the name for the bookmarks menu
278 // folder. 205 // folder.
279 path.insert(path.begin(), parent->title); 206 path.insert(path.begin(), parent->title);
280 } 207 }
281 208
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
339 if (!bookmarks.empty() && !cancelled()) { 266 if (!bookmarks.empty() && !cancelled()) {
340 const base::string16& first_folder_name = 267 const base::string16& first_folder_name =
341 bridge_->GetLocalizedString(IDS_BOOKMARK_GROUP_FROM_FIREFOX); 268 bridge_->GetLocalizedString(IDS_BOOKMARK_GROUP_FROM_FIREFOX);
342 bridge_->AddBookmarks(bookmarks, first_folder_name); 269 bridge_->AddBookmarks(bookmarks, first_folder_name);
343 } 270 }
344 if (!search_engines.empty() && !cancelled()) { 271 if (!search_engines.empty() && !cancelled()) {
345 bridge_->SetKeywords(search_engines, false); 272 bridge_->SetKeywords(search_engines, false);
346 } 273 }
347 if (!favicon_map.empty() && !cancelled()) { 274 if (!favicon_map.empty() && !cancelled()) {
348 favicon_base::FaviconUsageDataList favicons; 275 favicon_base::FaviconUsageDataList favicons;
349 LoadFavicons(&db, favicon_map, &favicons); 276 if (places->LoadFavicons(favicon_map, &favicons))
350 bridge_->SetFavicons(favicons); 277 bridge_->SetFavicons(favicons);
351 } 278 }
352 } 279 }
353 280
354 void FirefoxImporter::ImportPasswords() { 281 void FirefoxImporter::ImportPasswords() {
355 // Initializes NSS3. 282 // Initializes NSS3.
356 NSSDecryptor decryptor; 283 NSSDecryptor decryptor;
357 if (!decryptor.Init(source_path_, source_path_) && 284 if (!decryptor.Init(source_path_, source_path_) &&
358 !decryptor.Init(app_path_, source_path_)) { 285 !decryptor.Init(app_path_, source_path_)) {
359 return; 286 return;
360 } 287 }
(...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after
639 // Add <Alias> element as the last child element. 566 // Add <Alias> element as the last child element.
640 size_t end_of_parent = file_data.find("</SearchPlugin>"); 567 size_t end_of_parent = file_data.find("</SearchPlugin>");
641 if (end_of_parent != std::string::npos && !alias.empty()) 568 if (end_of_parent != std::string::npos && !alias.empty())
642 file_data.insert(end_of_parent, "<Alias>" + alias + "</Alias> \n"); 569 file_data.insert(end_of_parent, "<Alias>" + alias + "</Alias> \n");
643 } 570 }
644 search_engine_data->push_back(file_data); 571 search_engine_data->push_back(file_data);
645 } 572 }
646 } 573 }
647 } 574 }
648 } 575 }
649
650 void FirefoxImporter::LoadRootNodeID(sql::Connection* db,
651 int* toolbar_folder_id,
652 int* menu_folder_id,
653 int* unsorted_folder_id) {
654 static const char kToolbarFolderName[] = "toolbar";
655 static const char kMenuFolderName[] = "menu";
656 static const char kUnsortedFolderName[] = "unfiled";
657
658 const char query[] = "SELECT root_name, folder_id FROM moz_bookmarks_roots";
659 sql::Statement s(db->GetUniqueStatement(query));
660
661 while (s.Step()) {
662 std::string folder = s.ColumnString(0);
663 int id = s.ColumnInt(1);
664 if (folder == kToolbarFolderName)
665 *toolbar_folder_id = id;
666 else if (folder == kMenuFolderName)
667 *menu_folder_id = id;
668 else if (folder == kUnsortedFolderName)
669 *unsorted_folder_id = id;
670 }
671 }
672
673 void FirefoxImporter::LoadLivemarkIDs(sql::Connection* db,
674 std::set<int>* livemark) {
675 static const char kFeedAnnotation[] = "livemark/feedURI";
676 livemark->clear();
677
678 const char query[] =
679 "SELECT b.item_id "
680 "FROM moz_anno_attributes a "
681 "JOIN moz_items_annos b ON a.id = b.anno_attribute_id "
682 "WHERE a.name = ? ";
683 sql::Statement s(db->GetUniqueStatement(query));
684 s.BindString(0, kFeedAnnotation);
685
686 while (s.Step() && !cancelled())
687 livemark->insert(s.ColumnInt(0));
688 }
689
690 void FirefoxImporter::GetTopBookmarkFolder(sql::Connection* db,
691 int folder_id,
692 BookmarkList* list) {
693 const char query[] =
694 "SELECT b.title "
695 "FROM moz_bookmarks b "
696 "WHERE b.type = 2 AND b.id = ? "
697 "ORDER BY b.position";
698 sql::Statement s(db->GetUniqueStatement(query));
699 s.BindInt(0, folder_id);
700
701 if (s.Step()) {
702 BookmarkItem* item = new BookmarkItem;
703 item->parent = -1; // The top level folder has no parent.
704 item->id = folder_id;
705 item->title = s.ColumnString16(0);
706 item->type = TYPE_FOLDER;
707 item->favicon = 0;
708 item->empty_folder = true;
709 list->push_back(item);
710 }
711 }
712
713 void FirefoxImporter::GetWholeBookmarkFolder(sql::Connection* db,
714 BookmarkList* list,
715 size_t position,
716 bool* empty_folder) {
717 if (position >= list->size()) {
718 NOTREACHED();
719 return;
720 }
721
722 const char query[] =
723 "SELECT b.id, h.url, COALESCE(b.title, h.title), "
724 "b.type, k.keyword, b.dateAdded, h.favicon_id "
725 "FROM moz_bookmarks b "
726 "LEFT JOIN moz_places h ON b.fk = h.id "
727 "LEFT JOIN moz_keywords k ON k.id = b.keyword_id "
728 "WHERE b.type IN (1,2) AND b.parent = ? "
729 "ORDER BY b.position";
730 sql::Statement s(db->GetUniqueStatement(query));
731 s.BindInt(0, (*list)[position]->id);
732
733 BookmarkList temp_list;
734 while (s.Step()) {
735 BookmarkItem* item = new BookmarkItem;
736 item->parent = static_cast<int>(position);
737 item->id = s.ColumnInt(0);
738 item->url = GURL(s.ColumnString(1));
739 item->title = s.ColumnString16(2);
740 item->type = static_cast<BookmarkItemType>(s.ColumnInt(3));
741 item->keyword = s.ColumnString(4);
742 item->date_added = base::Time::FromTimeT(s.ColumnInt64(5)/1000000);
743 item->favicon = s.ColumnInt64(6);
744 item->empty_folder = true;
745
746 temp_list.push_back(item);
747 if (empty_folder != NULL)
748 *empty_folder = false;
749 }
750
751 // Appends all items to the list.
752 for (BookmarkList::iterator i = temp_list.begin();
753 i != temp_list.end(); ++i) {
754 list->push_back(*i);
755 // Recursive add bookmarks in sub-folders.
756 if ((*i)->type == TYPE_FOLDER)
757 GetWholeBookmarkFolder(db, list, list->size() - 1, &(*i)->empty_folder);
758 }
759 }
760
761 void FirefoxImporter::LoadFavicons(
762 sql::Connection* db,
763 const FaviconMap& favicon_map,
764 favicon_base::FaviconUsageDataList* favicons) {
765 const char query[] = "SELECT url, data FROM moz_favicons WHERE id=?";
766 sql::Statement s(db->GetUniqueStatement(query));
767
768 if (!s.is_valid())
769 return;
770
771 for (FaviconMap::const_iterator i = favicon_map.begin();
772 i != favicon_map.end(); ++i) {
773 s.BindInt64(0, i->first);
774 if (s.Step()) {
775 favicon_base::FaviconUsageData usage;
776
777 usage.favicon_url = GURL(s.ColumnString(0));
778 if (!usage.favicon_url.is_valid())
779 continue; // Don't bother importing favicons with invalid URLs.
780
781 std::vector<unsigned char> data;
782 s.ColumnBlobAsVector(1, &data);
783 if (data.empty())
784 continue; // Data definitely invalid.
785
786 if (!importer::ReencodeFavicon(&data[0], data.size(), &usage.png_data))
787 continue; // Unable to decode.
788
789 usage.urls = i->second;
790 favicons->push_back(usage);
791 }
792 s.Reset(true);
793 }
794 }
OLDNEW
« no previous file with comments | « chrome/utility/importer/firefox_importer.h ('k') | chrome/utility/importer/firefox_importer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698