| 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.
|
| + }
|
| +}
|
|
|