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

Unified Diff: chrome/utility/importer/edge_importer_win.cc

Issue 1465853002: Implement support for importing favorites from Edge on Windows 10. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Added missing include files Created 5 years 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 side-by-side diff with in-line comments
Download patch
Index: chrome/utility/importer/edge_importer_win.cc
diff --git a/chrome/utility/importer/edge_importer_win.cc b/chrome/utility/importer/edge_importer_win.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b84f06bd6f1be1e9ebc25f9ae90734787e2be156
--- /dev/null
+++ b/chrome/utility/importer/edge_importer_win.cc
@@ -0,0 +1,288 @@
+// Copyright 2015 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/edge_importer_win.h"
+
+#include <Shlobj.h>
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/time/time.h"
+#include "base/win/windows_version.h"
+#include "chrome/common/importer/edge_importer_utils_win.h"
+#include "chrome/common/importer/imported_bookmark_entry.h"
+#include "chrome/common/importer/importer_bridge.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/utility/importer/edge_database_reader_win.h"
+#include "chrome/utility/importer/favicon_reencode.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Toolbar favorites are placed under this special folder name.
+const base::char16 kFavoritesBarTitle[] = L"_Favorites_Bar_";
+const base::char16 kSpartanDatabaseFile[] = L"spartan.edb";
+
+struct EdgeFavoriteEntry {
+ EdgeFavoriteEntry()
+ : is_folder(false),
+ order_number(0),
+ item_id(GUID_NULL),
+ parent_id(GUID_NULL) {}
+
+ base::string16 title;
+ GURL url;
+ base::FilePath favicon_file;
+ bool is_folder;
+ int64_t order_number;
+ base::Time date_updated;
+ GUID item_id;
+ GUID parent_id;
+
+ std::vector<const EdgeFavoriteEntry*> children;
+
+ ImportedBookmarkEntry ToBookmarkEntry(
+ bool in_toolbar,
+ const std::vector<base::string16>& path) const {
+ ImportedBookmarkEntry entry;
+ entry.in_toolbar = in_toolbar;
+ entry.is_folder = is_folder;
+ entry.url = url;
+ entry.path = path;
+ entry.title = title;
+ entry.creation_time = date_updated;
+ return entry;
+ }
+};
+
+struct EdgeFavoriteEntryComparator {
+ bool operator()(const EdgeFavoriteEntry* lhs,
+ const EdgeFavoriteEntry* rhs) const {
+ return std::tie(lhs->order_number, lhs->title) <
+ std::tie(rhs->order_number, rhs->title);
+ }
+};
+
+// The name of the database file is spartan.edb, however it isn't clear how
+// the intermediate path between the DataStore and the database is generated.
+// Therefore we just do a simple recursive search until we find a matching name.
+base::FilePath FindSpartanDatabase(const base::FilePath& profile_path) {
+ base::FilePath data_path =
+ profile_path.empty() ? importer::GetEdgeDataFilePath() : profile_path;
+ if (data_path.empty())
+ return base::FilePath();
+
+ base::FileEnumerator enumerator(data_path.Append(L"DataStore\\Data"), true,
+ base::FileEnumerator::FILES);
+ base::FilePath path = enumerator.Next();
+ while (!path.empty()) {
+ if (base::EqualsCaseInsensitiveASCII(path.BaseName().value(),
+ kSpartanDatabaseFile))
+ return path;
+ path = enumerator.Next();
+ }
+ return base::FilePath();
+}
+
+struct GuidComparator {
+ bool operator()(const GUID& a, const GUID& b) const {
+ return memcmp(&a, &b, sizeof(a)) < 0;
+ }
+};
+
+bool ReadFaviconData(const base::FilePath& file,
+ std::vector<unsigned char>* data) {
+ std::string image_data;
+ if (!base::ReadFileToString(file, &image_data))
+ return false;
+
+ const unsigned char* ptr =
+ reinterpret_cast<const unsigned char*>(image_data.c_str());
+ return importer::ReencodeFavicon(ptr, image_data.size(), data);
+}
+
+void BuildBookmarkEntries(const EdgeFavoriteEntry& current_entry,
+ bool is_toolbar,
+ std::vector<ImportedBookmarkEntry>* bookmarks,
+ favicon_base::FaviconUsageDataList* favicons,
+ std::vector<base::string16>* path) {
+ for (const EdgeFavoriteEntry* entry : current_entry.children) {
+ if (entry->is_folder) {
+ // If the favorites bar then load all children as toolbar items.
+ if (base::EqualsCaseInsensitiveASCII(entry->title, kFavoritesBarTitle)) {
+ // Replace name with Links similar to IE.
+ path->push_back(L"Links");
+ BuildBookmarkEntries(*entry, true, bookmarks, favicons, path);
+ path->pop_back();
+ } else {
+ path->push_back(entry->title);
+ BuildBookmarkEntries(*entry, is_toolbar, bookmarks, favicons, path);
+ path->pop_back();
+ }
+ } else {
+ bookmarks->push_back(entry->ToBookmarkEntry(is_toolbar, *path));
+ favicon_base::FaviconUsageData favicon;
+ if (entry->url.is_valid() && !entry->favicon_file.empty() &&
+ ReadFaviconData(entry->favicon_file, &favicon.png_data)) {
+ // As the database doesn't provide us a favicon URL we'll fake one.
+ GURL::Replacements path_replace;
+ path_replace.SetPathStr("/favicon.ico");
+ favicon.favicon_url =
+ entry->url.GetWithEmptyPath().ReplaceComponents(path_replace);
+ favicon.urls.insert(entry->url);
+ favicons->push_back(favicon);
+ }
+ }
+ }
+}
+
+} // namespace
+
+EdgeImporter::EdgeImporter() {}
+
+void EdgeImporter::StartImport(const importer::SourceProfile& source_profile,
+ uint16 items,
+ ImporterBridge* bridge) {
+ bridge_ = bridge;
+ bridge_->NotifyStarted();
+ source_path_ = source_profile.source_path;
+
+ if ((items & importer::FAVORITES) && !cancelled()) {
+ bridge_->NotifyItemStarted(importer::FAVORITES);
+ ImportFavorites();
+ bridge_->NotifyItemEnded(importer::FAVORITES);
+ }
+ bridge_->NotifyEnded();
+}
+
+EdgeImporter::~EdgeImporter() {}
+
+void EdgeImporter::ImportFavorites() {
+ std::vector<ImportedBookmarkEntry> bookmarks;
+ favicon_base::FaviconUsageDataList favicons;
+ ParseFavoritesDatabase(&bookmarks, &favicons);
+
+ if (!bookmarks.empty() && !cancelled()) {
+ const base::string16& first_folder_name =
+ l10n_util::GetStringUTF16(IDS_BOOKMARK_GROUP_FROM_EDGE);
+ bridge_->AddBookmarks(bookmarks, first_folder_name);
+ }
+ if (!favicons.empty() && !cancelled())
+ bridge_->SetFavicons(favicons);
+}
+
+// From Edge 13 (released with Windows 10 TH2), Favorites are stored in a JET
+// database within the Edge local storage. The import uses the ESE library to
+// open and read the data file. The data is stored in a Favorites table with
+// the following schema.
+// Column Name Column Type
+// ------------------------------------------
+// DateUpdated LongLong - FILETIME
+// FaviconFile LongText - Relative path
+// HashedUrl ULong
+// IsDeleted Bit
+// IsFolder Bit
+// ItemId Guid
+// OrderNumber LongLong
+// ParentId Guid
+// RoamDisabled Bit
+// RowId Long
+// Title LongText
+// URL LongText
+void EdgeImporter::ParseFavoritesDatabase(
+ std::vector<ImportedBookmarkEntry>* bookmarks,
+ favicon_base::FaviconUsageDataList* favicons) {
+ base::FilePath database_path = FindSpartanDatabase(source_path_);
+ if (database_path.empty())
+ return;
+
+ EdgeDatabaseReader database;
+ if (!database.OpenDatabase(database_path.value())) {
+ DVLOG(1) << "Error opening database " << database.GetErrorMessage();
ananta 2015/12/02 21:33:11 Should this be a regular log error or a check perh
forshaw 2015/12/02 21:56:11 I did have this originally as a normal LOG but was
Ilya Sherman 2015/12/02 21:59:32 FWIW, you previously had DLOG, which isn't going t
+ return;
+ }
+
+ scoped_ptr<EdgeDatabaseTableEnumerator> enumerator =
+ database.OpenTableEnumerator(L"Favorites");
+ if (!enumerator) {
+ DVLOG(1) << "Error opening database table " << database.GetErrorMessage();
ananta 2015/12/02 21:33:11 Ditto for replacing this with a log error and or a
+ return;
+ }
+
+ if (!enumerator->Reset())
+ return;
+
+ std::map<GUID, EdgeFavoriteEntry, GuidComparator> database_entries;
+ base::FilePath favicon_base =
+ source_path_.empty() ? importer::GetEdgeDataFilePath() : source_path_;
+ favicon_base = favicon_base.Append(L"DataStore");
+
+ do {
+ EdgeFavoriteEntry entry;
+ bool is_deleted = false;
+ if (!enumerator->RetrieveColumn(L"IsDeleted", &is_deleted))
+ continue;
+ if (is_deleted)
+ continue;
+ if (!enumerator->RetrieveColumn(L"IsFolder", &entry.is_folder))
+ continue;
+ base::string16 url;
+ if (!enumerator->RetrieveColumn(L"URL", &url))
+ continue;
+ entry.url = GURL(url);
+ if (!entry.is_folder && !entry.url.is_valid())
+ continue;
+ if (!enumerator->RetrieveColumn(L"Title", &entry.title))
+ continue;
+ base::string16 favicon_file;
+ if (!enumerator->RetrieveColumn(L"FaviconFile", &favicon_file))
+ continue;
+ if (!favicon_file.empty())
+ entry.favicon_file = favicon_base.Append(favicon_file);
+ if (!enumerator->RetrieveColumn(L"ParentId", &entry.parent_id))
+ continue;
+ if (!enumerator->RetrieveColumn(L"ItemId", &entry.item_id))
+ continue;
+ if (!enumerator->RetrieveColumn(L"OrderNumber", &entry.order_number))
+ continue;
+ FILETIME data_updated;
+ if (!enumerator->RetrieveColumn(L"DateUpdated", &data_updated))
+ continue;
+ entry.date_updated = base::Time::FromFileTime(data_updated);
+ database_entries[entry.item_id] = entry;
+ } while (enumerator->Next() && !cancelled());
+
+ // Build simple tree.
+ EdgeFavoriteEntry root_entry;
+ for (auto& entry : database_entries) {
+ auto found_parent = database_entries.find(entry.second.parent_id);
+ if (found_parent == database_entries.end() ||
+ !found_parent->second.is_folder) {
+ root_entry.children.push_back(&entry.second);
+ } else {
+ found_parent->second.children.push_back(&entry.second);
+ }
+ }
+ // With tree built sort the children of each node including the root.
+ std::sort(root_entry.children.begin(), root_entry.children.end(),
+ EdgeFavoriteEntryComparator());
+ for (auto& entry : database_entries) {
+ std::sort(entry.second.children.begin(), entry.second.children.end(),
+ EdgeFavoriteEntryComparator());
+ }
+ std::vector<base::string16> path;
+ BuildBookmarkEntries(root_entry, false, bookmarks, favicons, &path);
+}

Powered by Google App Engine
This is Rietveld 408576698