diff options
Diffstat (limited to 'chrome/utility/importer/edge_importer_win.cc')
-rw-r--r-- | chrome/utility/importer/edge_importer_win.cc | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/chrome/utility/importer/edge_importer_win.cc b/chrome/utility/importer/edge_importer_win.cc new file mode 100644 index 0000000..b84f06b --- /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(); + return; + } + + scoped_ptr<EdgeDatabaseTableEnumerator> enumerator = + database.OpenTableEnumerator(L"Favorites"); + if (!enumerator) { + DVLOG(1) << "Error opening database table " << database.GetErrorMessage(); + 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); +} |