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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/utility/importer/edge_importer_win.h"
6
7 #include <Shlobj.h>
8
9 #include <algorithm>
10 #include <map>
11 #include <string>
12 #include <tuple>
13 #include <vector>
14
15 #include "base/files/file_enumerator.h"
16 #include "base/files/file_path.h"
17 #include "base/files/file_util.h"
18 #include "base/memory/ref_counted.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/strings/string16.h"
21 #include "base/strings/string_util.h"
22 #include "base/time/time.h"
23 #include "base/win/windows_version.h"
24 #include "chrome/common/importer/edge_importer_utils_win.h"
25 #include "chrome/common/importer/imported_bookmark_entry.h"
26 #include "chrome/common/importer/importer_bridge.h"
27 #include "chrome/grit/generated_resources.h"
28 #include "chrome/utility/importer/edge_database_reader_win.h"
29 #include "chrome/utility/importer/favicon_reencode.h"
30 #include "ui/base/l10n/l10n_util.h"
31 #include "url/gurl.h"
32
33 namespace {
34
35 // Toolbar favorites are placed under this special folder name.
36 const base::char16 kFavoritesBarTitle[] = L"_Favorites_Bar_";
37 const base::char16 kSpartanDatabaseFile[] = L"spartan.edb";
38
39 struct EdgeFavoriteEntry {
40 EdgeFavoriteEntry()
41 : is_folder(false),
42 order_number(0),
43 item_id(GUID_NULL),
44 parent_id(GUID_NULL) {}
45
46 base::string16 title;
47 GURL url;
48 base::FilePath favicon_file;
49 bool is_folder;
50 int64_t order_number;
51 base::Time date_updated;
52 GUID item_id;
53 GUID parent_id;
54
55 std::vector<const EdgeFavoriteEntry*> children;
56
57 ImportedBookmarkEntry ToBookmarkEntry(
58 bool in_toolbar,
59 const std::vector<base::string16>& path) const {
60 ImportedBookmarkEntry entry;
61 entry.in_toolbar = in_toolbar;
62 entry.is_folder = is_folder;
63 entry.url = url;
64 entry.path = path;
65 entry.title = title;
66 entry.creation_time = date_updated;
67 return entry;
68 }
69 };
70
71 struct EdgeFavoriteEntryComparator {
72 bool operator()(const EdgeFavoriteEntry* lhs,
73 const EdgeFavoriteEntry* rhs) const {
74 return std::tie(lhs->order_number, lhs->title) <
75 std::tie(rhs->order_number, rhs->title);
76 }
77 };
78
79 // The name of the database file is spartan.edb, however it isn't clear how
80 // the intermediate path between the DataStore and the database is generated.
81 // Therefore we just do a simple recursive search until we find a matching name.
82 base::FilePath FindSpartanDatabase(const base::FilePath& profile_path) {
83 base::FilePath data_path =
84 profile_path.empty() ? importer::GetEdgeDataFilePath() : profile_path;
85 if (data_path.empty())
86 return base::FilePath();
87
88 base::FileEnumerator enumerator(data_path.Append(L"DataStore\\Data"), true,
89 base::FileEnumerator::FILES);
90 base::FilePath path = enumerator.Next();
91 while (!path.empty()) {
92 if (base::EqualsCaseInsensitiveASCII(path.BaseName().value(),
93 kSpartanDatabaseFile))
94 return path;
95 path = enumerator.Next();
96 }
97 return base::FilePath();
98 }
99
100 struct GuidComparator {
101 bool operator()(const GUID& a, const GUID& b) const {
102 return memcmp(&a, &b, sizeof(a)) < 0;
103 }
104 };
105
106 bool ReadFaviconData(const base::FilePath& file,
107 std::vector<unsigned char>* data) {
108 std::string image_data;
109 if (!base::ReadFileToString(file, &image_data))
110 return false;
111
112 const unsigned char* ptr =
113 reinterpret_cast<const unsigned char*>(image_data.c_str());
114 return importer::ReencodeFavicon(ptr, image_data.size(), data);
115 }
116
117 void BuildBookmarkEntries(const EdgeFavoriteEntry& current_entry,
118 bool is_toolbar,
119 std::vector<ImportedBookmarkEntry>* bookmarks,
120 favicon_base::FaviconUsageDataList* favicons,
121 std::vector<base::string16>* path) {
122 for (const EdgeFavoriteEntry* entry : current_entry.children) {
123 if (entry->is_folder) {
124 // If the favorites bar then load all children as toolbar items.
125 if (base::EqualsCaseInsensitiveASCII(entry->title, kFavoritesBarTitle)) {
126 // Replace name with Links similar to IE.
127 path->push_back(L"Links");
128 BuildBookmarkEntries(*entry, true, bookmarks, favicons, path);
129 path->pop_back();
130 } else {
131 path->push_back(entry->title);
132 BuildBookmarkEntries(*entry, is_toolbar, bookmarks, favicons, path);
133 path->pop_back();
134 }
135 } else {
136 bookmarks->push_back(entry->ToBookmarkEntry(is_toolbar, *path));
137 favicon_base::FaviconUsageData favicon;
138 if (entry->url.is_valid() && !entry->favicon_file.empty() &&
139 ReadFaviconData(entry->favicon_file, &favicon.png_data)) {
140 // As the database doesn't provide us a favicon URL we'll fake one.
141 GURL::Replacements path_replace;
142 path_replace.SetPathStr("/favicon.ico");
143 favicon.favicon_url =
144 entry->url.GetWithEmptyPath().ReplaceComponents(path_replace);
145 favicon.urls.insert(entry->url);
146 favicons->push_back(favicon);
147 }
148 }
149 }
150 }
151
152 } // namespace
153
154 EdgeImporter::EdgeImporter() {}
155
156 void EdgeImporter::StartImport(const importer::SourceProfile& source_profile,
157 uint16 items,
158 ImporterBridge* bridge) {
159 bridge_ = bridge;
160 bridge_->NotifyStarted();
161 source_path_ = source_profile.source_path;
162
163 if ((items & importer::FAVORITES) && !cancelled()) {
164 bridge_->NotifyItemStarted(importer::FAVORITES);
165 ImportFavorites();
166 bridge_->NotifyItemEnded(importer::FAVORITES);
167 }
168 bridge_->NotifyEnded();
169 }
170
171 EdgeImporter::~EdgeImporter() {}
172
173 void EdgeImporter::ImportFavorites() {
174 std::vector<ImportedBookmarkEntry> bookmarks;
175 favicon_base::FaviconUsageDataList favicons;
176 ParseFavoritesDatabase(&bookmarks, &favicons);
177
178 if (!bookmarks.empty() && !cancelled()) {
179 const base::string16& first_folder_name =
180 l10n_util::GetStringUTF16(IDS_BOOKMARK_GROUP_FROM_EDGE);
181 bridge_->AddBookmarks(bookmarks, first_folder_name);
182 }
183 if (!favicons.empty() && !cancelled())
184 bridge_->SetFavicons(favicons);
185 }
186
187 // From Edge 13 (released with Windows 10 TH2), Favorites are stored in a JET
188 // database within the Edge local storage. The import uses the ESE library to
189 // open and read the data file. The data is stored in a Favorites table with
190 // the following schema.
191 // Column Name Column Type
192 // ------------------------------------------
193 // DateUpdated LongLong - FILETIME
194 // FaviconFile LongText - Relative path
195 // HashedUrl ULong
196 // IsDeleted Bit
197 // IsFolder Bit
198 // ItemId Guid
199 // OrderNumber LongLong
200 // ParentId Guid
201 // RoamDisabled Bit
202 // RowId Long
203 // Title LongText
204 // URL LongText
205 void EdgeImporter::ParseFavoritesDatabase(
206 std::vector<ImportedBookmarkEntry>* bookmarks,
207 favicon_base::FaviconUsageDataList* favicons) {
208 base::FilePath database_path = FindSpartanDatabase(source_path_);
209 if (database_path.empty())
210 return;
211
212 EdgeDatabaseReader database;
213 if (!database.OpenDatabase(database_path.value())) {
214 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
215 return;
216 }
217
218 scoped_ptr<EdgeDatabaseTableEnumerator> enumerator =
219 database.OpenTableEnumerator(L"Favorites");
220 if (!enumerator) {
221 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
222 return;
223 }
224
225 if (!enumerator->Reset())
226 return;
227
228 std::map<GUID, EdgeFavoriteEntry, GuidComparator> database_entries;
229 base::FilePath favicon_base =
230 source_path_.empty() ? importer::GetEdgeDataFilePath() : source_path_;
231 favicon_base = favicon_base.Append(L"DataStore");
232
233 do {
234 EdgeFavoriteEntry entry;
235 bool is_deleted = false;
236 if (!enumerator->RetrieveColumn(L"IsDeleted", &is_deleted))
237 continue;
238 if (is_deleted)
239 continue;
240 if (!enumerator->RetrieveColumn(L"IsFolder", &entry.is_folder))
241 continue;
242 base::string16 url;
243 if (!enumerator->RetrieveColumn(L"URL", &url))
244 continue;
245 entry.url = GURL(url);
246 if (!entry.is_folder && !entry.url.is_valid())
247 continue;
248 if (!enumerator->RetrieveColumn(L"Title", &entry.title))
249 continue;
250 base::string16 favicon_file;
251 if (!enumerator->RetrieveColumn(L"FaviconFile", &favicon_file))
252 continue;
253 if (!favicon_file.empty())
254 entry.favicon_file = favicon_base.Append(favicon_file);
255 if (!enumerator->RetrieveColumn(L"ParentId", &entry.parent_id))
256 continue;
257 if (!enumerator->RetrieveColumn(L"ItemId", &entry.item_id))
258 continue;
259 if (!enumerator->RetrieveColumn(L"OrderNumber", &entry.order_number))
260 continue;
261 FILETIME data_updated;
262 if (!enumerator->RetrieveColumn(L"DateUpdated", &data_updated))
263 continue;
264 entry.date_updated = base::Time::FromFileTime(data_updated);
265 database_entries[entry.item_id] = entry;
266 } while (enumerator->Next() && !cancelled());
267
268 // Build simple tree.
269 EdgeFavoriteEntry root_entry;
270 for (auto& entry : database_entries) {
271 auto found_parent = database_entries.find(entry.second.parent_id);
272 if (found_parent == database_entries.end() ||
273 !found_parent->second.is_folder) {
274 root_entry.children.push_back(&entry.second);
275 } else {
276 found_parent->second.children.push_back(&entry.second);
277 }
278 }
279 // With tree built sort the children of each node including the root.
280 std::sort(root_entry.children.begin(), root_entry.children.end(),
281 EdgeFavoriteEntryComparator());
282 for (auto& entry : database_entries) {
283 std::sort(entry.second.children.begin(), entry.second.children.end(),
284 EdgeFavoriteEntryComparator());
285 }
286 std::vector<base::string16> path;
287 BuildBookmarkEntries(root_entry, false, bookmarks, favicons, &path);
288 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698