Index: chrome/utility/importer/firefox_places_factory.cc |
diff --git a/chrome/utility/importer/firefox_places_factory.cc b/chrome/utility/importer/firefox_places_factory.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e06093949d5e4af46e65da7d9ee09d3d6a48b5cc |
--- /dev/null |
+++ b/chrome/utility/importer/firefox_places_factory.cc |
@@ -0,0 +1,310 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/utility/importer/favicon_reencode.h" |
+#include "chrome/utility/importer/firefox_places_factory.h" |
+#include "firefox_places_factory.h" |
+ |
+FirefoxPlaces::FirefoxPlaces(const base::FilePath& places_path) |
+ : places_path_(places_path) { |
+ db_.Open(places_path); |
+} |
+ |
+bool FirefoxPlaces::LoadBookmarks(BookmarkList* bookmarks) { |
+ // Get the bookmark folders that we are interested in. |
+ int toolbar_folder_id = GetFolderId(FolderType::TOOLBAR); |
+ int menu_folder_id = GetFolderId(FolderType::MENU); |
+ int unsorted_folder_id = GetFolderId(FolderType::UNSORTED); |
+ |
+ GetTopBookmarkFolder(toolbar_folder_id, bookmarks); |
+ GetTopBookmarkFolder(menu_folder_id, bookmarks); |
+ GetTopBookmarkFolder(unsorted_folder_id, bookmarks); |
+ auto num_folders = bookmarks->size(); |
+ for (size_t i = 0; i < num_folders; ++i) |
+ GetWholeBookmarkFolder(bookmarks, i, NULL); |
+ return true; |
+} |
+ |
+bool FirefoxPlaces::LoadHistory(std::vector<ImporterURLRow>* rows) { |
+ // |visit_type| represent the transition type of URLs (typed, click, |
+ // redirect, bookmark, etc.) We eliminate some URLs like sub-frames and |
+ // redirects, since we don't want them to appear in history. |
+ // Firefox transition types are defined in: |
+ // toolkit/components/places/public/nsINavHistoryService.idl |
+ const char query[] = |
+ "SELECT h.url, h.title, h.visit_count, " |
+ "h.hidden, h.typed, v.visit_date " |
+ "FROM moz_places h JOIN moz_historyvisits v " |
+ "ON h.id = v.place_id " |
+ "WHERE v.visit_type <= 3"; |
+ |
+ sql::Statement s(db_.GetUniqueStatement(query)); |
+ |
+ while (s.Step()) { |
+ GURL url(s.ColumnString(0)); |
+ |
+ ImporterURLRow row(url); |
+ row.title = s.ColumnString16(1); |
+ row.visit_count = s.ColumnInt(2); |
+ row.hidden = s.ColumnInt(3) == 1; |
+ row.typed_count = s.ColumnInt(4); |
+ row.last_visit = base::Time::FromTimeT(s.ColumnInt64(5) / 1000000); |
+ |
+ rows->push_back(row); |
+ } |
+ return true; |
+} |
+ |
+bool FirefoxPlaces::LoadPostKeywordIds(PostKeywordIds* post_keyword_ids) { |
+ const char query[] = |
+ "SELECT b.id FROM moz_bookmarks b " |
+ "INNER JOIN moz_items_annos ia ON ia.item_id = b.id " |
+ "INNER JOIN moz_anno_attributes aa ON ia.anno_attribute_id = aa.id " |
+ "WHERE aa.name = 'bookmarkProperties/POSTData'"; |
+ sql::Statement s(db_.GetUniqueStatement(query)); |
+ |
+ if (!s.is_valid()) |
+ return false; |
+ |
+ while (s.Step()) |
+ post_keyword_ids->insert(s.ColumnInt(0)); |
+ return true; |
+} |
+ |
+bool FirefoxPlaces::LoadLivemarkIds(LivemarkIds* livemark_ids) { |
+ static const char kFeedAnnotation[] = "livemark/feedURI"; |
+ livemark_ids->clear(); |
+ |
+ const char query[] = |
+ "SELECT b.item_id " |
+ "FROM moz_anno_attributes a " |
+ "JOIN moz_items_annos b ON a.id = b.anno_attribute_id " |
+ "WHERE a.name = ? "; |
+ sql::Statement s(db_.GetUniqueStatement(query)); |
+ s.BindString(0, kFeedAnnotation); |
+ |
+ while (s.Step()) |
+ livemark_ids->insert(s.ColumnInt(0)); |
+ return true; |
+} |
+ |
+int FirefoxPlaces::GetFolderId(FolderType folder_type) { |
+ return -1; |
+} |
+ |
+bool FirefoxPlaces::LoadFavicons(const FaviconMap& favicon_map, |
+ favicon_base::FaviconUsageDataList* favicons) { |
+ const char query[] = "SELECT url, data FROM moz_favicons WHERE id=?"; |
+ sql::Statement s(db_.GetUniqueStatement(query)); |
+ |
+ if (!s.is_valid()) |
+ return false; |
+ |
+ for (FaviconMap::const_iterator i = favicon_map.begin(); |
+ i != favicon_map.end(); ++i) { |
+ s.BindInt64(0, i->first); |
+ if (s.Step()) { |
+ favicon_base::FaviconUsageData usage; |
+ |
+ usage.favicon_url = GURL(s.ColumnString(0)); |
+ if (!usage.favicon_url.is_valid()) |
+ continue; // Don't bother importing favicons with invalid URLs. |
+ |
+ std::vector<unsigned char> data; |
+ s.ColumnBlobAsVector(1, &data); |
+ if (data.empty()) |
+ continue; // Data definitely invalid. |
+ |
+ if (!importer::ReencodeFavicon(&data[0], data.size(), &usage.png_data)) |
+ continue; // Unable to decode. |
+ |
+ usage.urls = i->second; |
+ favicons->push_back(usage); |
+ } |
+ s.Reset(true); |
+ } |
+ return true; |
+} |
+ |
+void FirefoxPlaces::GetTopBookmarkFolder(int folder_id, BookmarkList* list) { |
+ const char query[] = |
+ "SELECT b.title " |
+ "FROM moz_bookmarks b " |
+ "WHERE b.type = 2 AND b.id = ? " |
+ "ORDER BY b.position"; |
+ sql::Statement s(db_.GetUniqueStatement(query)); |
+ s.BindInt(0, folder_id); |
+ |
+ if (s.Step()) { |
+ BookmarkItem* item = new BookmarkItem; |
+ item->parent = -1; // The top level folder has no parent. |
+ item->id = folder_id; |
+ item->title = s.ColumnString16(0); |
+ item->type = TYPE_FOLDER; |
+ item->favicon = 0; |
+ item->empty_folder = true; |
+ list->push_back(item); |
+ } |
+} |
+ |
+void FirefoxPlaces::GetWholeBookmarkFolder(BookmarkList* list, |
+ size_t position, |
+ bool* empty_folder) { |
+ if (position >= list->size()) { |
+ NOTREACHED(); |
+ return; |
+ } |
+ |
+ const char query[] = |
+ "SELECT b.id, h.url, COALESCE(b.title, h.title), " |
+ "b.type, k.keyword, b.dateAdded, h.favicon_id " |
+ "FROM moz_bookmarks b " |
+ "LEFT JOIN moz_places h ON b.fk = h.id " |
+ "LEFT JOIN moz_keywords k ON k.id = b.keyword_id " |
+ "WHERE b.type IN (1,2) AND b.parent = ? " |
+ "ORDER BY b.position"; |
+ sql::Statement s(db_.GetUniqueStatement(query)); |
+ s.BindInt(0, (*list)[position]->id); |
+ |
+ BookmarkList temp_list; |
+ while (s.Step()) { |
+ BookmarkItem* item = new BookmarkItem; |
+ item->parent = static_cast<int>(position); |
+ item->id = s.ColumnInt(0); |
+ item->url = GURL(s.ColumnString(1)); |
+ item->title = s.ColumnString16(2); |
+ item->type = static_cast<BookmarkItemType>(s.ColumnInt(3)); |
+ item->keyword = s.ColumnString(4); |
+ item->date_added = base::Time::FromTimeT(s.ColumnInt64(5) / 1000000); |
+ item->favicon = s.ColumnInt64(6); |
+ item->empty_folder = true; |
+ |
+ temp_list.push_back(item); |
+ if (empty_folder != NULL) |
+ *empty_folder = false; |
+ } |
+ |
+ // Appends all items to the list. |
+ for (BookmarkList::iterator i = temp_list.begin(); i != temp_list.end(); |
+ ++i) { |
+ list->push_back(*i); |
+ // Recursive add bookmarks in sub-folders. |
+ if ((*i)->type == TYPE_FOLDER) |
+ GetWholeBookmarkFolder(list, list->size() - 1, &(*i)->empty_folder); |
+ } |
+} |
+ |
+// FirefoxPlacesV11 |
+FirefoxPlacesV11::FirefoxPlacesV11(const base::FilePath& places_path) |
+ : FirefoxPlaces(places_path) {} |
+ |
+int FirefoxPlacesV11::GetFolderId(FolderType folder_type) { |
+ int folder_id = -1; |
+ switch (folder_type) { |
+ case FolderType::ROOT: |
+ folder_id = LoadNodeIDByName("root"); |
+ break; |
+ case FolderType::MENU: |
+ folder_id = LoadNodeIDByName("menu"); |
+ break; |
+ case FolderType::TOOLBAR: |
+ folder_id = LoadNodeIDByName("toolbar"); |
+ break; |
+ case FolderType::TAGS: |
+ folder_id = LoadNodeIDByName("tags"); |
+ break; |
+ case FolderType::UNSORTED: |
+ folder_id = LoadNodeIDByName("unfiled"); |
+ break; |
+ default: |
+ folder_id = -1; |
+ break; |
+ } |
+ return folder_id; |
+} |
+ |
+int FirefoxPlacesV11::LoadNodeIDByName(const std::string& name) { |
+ const char query[] = |
+ "SELECT folder_id " |
+ "FROM moz_bookmarks_roots " |
+ "WHERE root_name = ?"; |
+ sql::Statement s(db_.GetUniqueStatement(query)); |
+ s.BindString(0, name); |
+ if (!s.Step()) |
+ return -1; |
+ return s.ColumnInt(0); |
+} |
+ |
+// FirefoxPlacesV25 |
+FirefoxPlacesV25::FirefoxPlacesV25(const base::FilePath& places_path) |
+ : FirefoxPlaces(places_path) {} |
+ |
+int FirefoxPlacesV25::LoadNodeIDByGUID(const std::string& GUID) { |
+ const char query[] = |
+ "SELECT id " |
+ "FROM moz_bookmarks " |
+ "WHERE guid = ?"; |
+ sql::Statement s(db_.GetUniqueStatement(query)); |
+ s.BindString(0, GUID); |
+ |
+ if (!s.Step()) |
+ return -1; |
+ return s.ColumnInt(0); |
+} |
+ |
+int FirefoxPlacesV25::GetFolderId(FolderType folder_type) { |
+ int folder_id = -1; |
+ switch (folder_type) { |
+ case FolderType::ROOT: |
+ folder_id = LoadNodeIDByGUID("root________"); |
+ break; |
+ case FolderType::MENU: |
+ folder_id = LoadNodeIDByGUID("menu________"); |
+ break; |
+ case FolderType::TOOLBAR: |
+ folder_id = LoadNodeIDByGUID("toolbar_____"); |
+ break; |
+ case FolderType::TAGS: |
+ folder_id = LoadNodeIDByGUID("tags________"); |
+ break; |
+ case FolderType::UNSORTED: |
+ folder_id = LoadNodeIDByGUID("unfiled_____"); |
+ break; |
+ default: |
+ folder_id = -1; |
+ break; |
+ } |
+ return folder_id; |
+} |
+ |
+std::unique_ptr<FirefoxPlaces> FirefoxPlacesFactory::GetForProfile( |
+ const base::FilePath& profile_path) { |
+ base::FilePath places_path = profile_path.AppendASCII("places.sqlite"); |
+ if (!base::PathExists(places_path)) |
+ return std::unique_ptr<FirefoxPlaces>(); |
+ |
+ sql::Connection db; |
+ if (!db.Open(places_path)) |
+ return std::unique_ptr<FirefoxPlaces>(); |
+ |
+ const std::string schema_version_query{"PRAGMA user_version"}; |
+ sql::Statement s(db.GetUniqueStatement(schema_version_query.c_str())); |
+ if (!s.is_valid()) |
+ return std::unique_ptr<FirefoxPlaces>(); |
+ |
+ s.Step(); |
+ int schema_version = s.ColumnInt(0); |
+ s.Clear(); |
+ db.Close(); |
+ |
+ if (schema_version >= 25) { |
+ return std::unique_ptr<FirefoxPlaces>(new FirefoxPlacesV25(places_path)); |
+ } else if (schema_version >= 11) { |
+ return std::unique_ptr<FirefoxPlaces>(new FirefoxPlacesV11(places_path)); |
+ } else { |
+ return std::unique_ptr<FirefoxPlaces>(); // Same as Firefox, we don't |
+ // support schemas earlier than |
+ // 11. |
+ } |
+} |