summaryrefslogtreecommitdiffstats
path: root/chrome/utility
diff options
context:
space:
mode:
authorforshaw <forshaw@chromium.org>2015-12-03 06:08:12 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-03 14:09:17 +0000
commit57e648081288644895bf6f5fe184597a63282336 (patch)
tree76f51cb9e552c318ec91ce13e9524e490c32bc58 /chrome/utility
parentcbe916974f8558032775f461b03b33358911f7c2 (diff)
downloadchromium_src-57e648081288644895bf6f5fe184597a63282336.zip
chromium_src-57e648081288644895bf6f5fe184597a63282336.tar.gz
chromium_src-57e648081288644895bf6f5fe184597a63282336.tar.bz2
Implement support for importing favorites from Edge on Windows 10.
This CL adds support for importing favorties from the Edge browser on Windows 10. It supports the old style importation from internet shortcuts (without supporting order due to structure changes) as well as importing from Edge 13 database format. BUG=539746 Review URL: https://codereview.chromium.org/1465853002 Cr-Commit-Position: refs/heads/master@{#362977}
Diffstat (limited to 'chrome/utility')
-rw-r--r--chrome/utility/BUILD.gn4
-rw-r--r--chrome/utility/importer/DEPS1
-rw-r--r--chrome/utility/importer/edge_database_reader_unittest_win.cc448
-rw-r--r--chrome/utility/importer/edge_database_reader_win.cc257
-rw-r--r--chrome/utility/importer/edge_database_reader_win.h95
-rw-r--r--chrome/utility/importer/edge_importer_win.cc288
-rw-r--r--chrome/utility/importer/edge_importer_win.h43
-rw-r--r--chrome/utility/importer/ie_importer_win.cc25
-rw-r--r--chrome/utility/importer/ie_importer_win.h5
-rw-r--r--chrome/utility/importer/importer_creator.cc7
10 files changed, 1166 insertions, 7 deletions
diff --git a/chrome/utility/BUILD.gn b/chrome/utility/BUILD.gn
index ace1569..62a69d1 100644
--- a/chrome/utility/BUILD.gn
+++ b/chrome/utility/BUILD.gn
@@ -66,6 +66,10 @@ static_library("utility") {
# Prevent wininet from loading in the renderer. http://crbug.com/460679
if (is_win) {
ldflags = [ "/DELAYLOAD:wininet.dll" ]
+
+ # Add ESE library for Edge Import support.
+ libs = [ "esent.lib" ]
+ ldflags += [ "/DELAYLOAD:esent.dll" ]
}
if (is_win || is_mac) {
diff --git a/chrome/utility/importer/DEPS b/chrome/utility/importer/DEPS
index e25c5f2..100c7d6 100644
--- a/chrome/utility/importer/DEPS
+++ b/chrome/utility/importer/DEPS
@@ -1,5 +1,6 @@
include_rules = [
"+components/autofill/core/common", # For PasswordForm.
+ "+components/compression", # For edge database reader unittests.
"+components/favicon_base",
"+components/search_engines",
"+components/strings/grit",
diff --git a/chrome/utility/importer/edge_database_reader_unittest_win.cc b/chrome/utility/importer/edge_database_reader_unittest_win.cc
new file mode 100644
index 0000000..d0a0b77
--- /dev/null
+++ b/chrome/utility/importer/edge_database_reader_unittest_win.cc
@@ -0,0 +1,448 @@
+// 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_database_reader_win.h"
+
+#include <windows.h>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/windows_version.h"
+#include "chrome/common/chrome_paths.h"
+#include "components/compression/compression_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class EdgeDatabaseReaderTest : public ::testing::Test {
+ protected:
+ bool CopyTestDatabase(const base::string16& database_name,
+ base::FilePath* copied_path) {
+ base::FilePath input_path;
+ input_path = test_data_path_.AppendASCII("edge_database_reader")
+ .Append(database_name)
+ .AddExtension(L".gz");
+ base::FilePath output_path = temp_dir_.path().Append(database_name);
+
+ if (DecompressDatabase(input_path, output_path)) {
+ *copied_path = output_path;
+ return true;
+ }
+ return false;
+ }
+
+ bool WriteFile(const base::string16& name,
+ const std::string& contents,
+ base::FilePath* output_path) {
+ *output_path = temp_dir_.path().Append(name);
+ return base::WriteFile(*output_path, contents.c_str(), contents.size()) >=
+ 0;
+ }
+
+ void SetUp() override {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_path_));
+ }
+
+ private:
+ bool DecompressDatabase(const base::FilePath& gzip_file,
+ const base::FilePath& output_file) {
+ std::string gzip_data;
+ if (!base::ReadFileToString(gzip_file, &gzip_data))
+ return false;
+ if (!compression::GzipUncompress(gzip_data, &gzip_data))
+ return false;
+ return base::WriteFile(output_file, gzip_data.c_str(), gzip_data.size()) >=
+ 0;
+ }
+
+ base::ScopedTempDir temp_dir_;
+ base::FilePath test_data_path_;
+};
+
+} // namespace
+
+TEST_F(EdgeDatabaseReaderTest, OpenFileTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(CopyTestDatabase(L"testdata.edb", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_TRUE(reader.OpenDatabase(database_path.value()));
+}
+
+TEST_F(EdgeDatabaseReaderTest, NoFileTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ EdgeDatabaseReader reader;
+ EXPECT_FALSE(reader.OpenDatabase(L"ThisIsntARealFileName.edb"));
+}
+
+TEST_F(EdgeDatabaseReaderTest, RandomGarbageDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(CopyTestDatabase(L"random.edb", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_FALSE(reader.OpenDatabase(database_path.value()));
+}
+
+TEST_F(EdgeDatabaseReaderTest, ZerosDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ std::string zeros(0x10000, '\0');
+ ASSERT_TRUE(WriteFile(L"zeros.edb", zeros, &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_FALSE(reader.OpenDatabase(database_path.value()));
+}
+
+TEST_F(EdgeDatabaseReaderTest, EmptyDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(WriteFile(L"empty.edb", "", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_FALSE(reader.OpenDatabase(database_path.value()));
+}
+
+TEST_F(EdgeDatabaseReaderTest, OpenTableDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(CopyTestDatabase(L"testdata.edb", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_TRUE(reader.OpenDatabase(database_path.value()));
+ scoped_ptr<EdgeDatabaseTableEnumerator> table_enum =
+ reader.OpenTableEnumerator(L"TestTable");
+ EXPECT_NE(table_enum, nullptr);
+}
+
+TEST_F(EdgeDatabaseReaderTest, InvalidTableDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(CopyTestDatabase(L"testdata.edb", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_TRUE(reader.OpenDatabase(database_path.value()));
+ scoped_ptr<EdgeDatabaseTableEnumerator> table_enum =
+ reader.OpenTableEnumerator(L"NotARealTableName");
+ EXPECT_EQ(table_enum, nullptr);
+}
+
+TEST_F(EdgeDatabaseReaderTest, NotOpenDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ EdgeDatabaseReader reader;
+ scoped_ptr<EdgeDatabaseTableEnumerator> table_enum =
+ reader.OpenTableEnumerator(L"TestTable");
+ EXPECT_EQ(table_enum, nullptr);
+ EXPECT_EQ(reader.last_error(), JET_errDatabaseNotFound);
+}
+
+TEST_F(EdgeDatabaseReaderTest, AlreadyOpenDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(CopyTestDatabase(L"testdata.edb", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_TRUE(reader.OpenDatabase(database_path.value()));
+ EXPECT_FALSE(reader.OpenDatabase(database_path.value()));
+ EXPECT_EQ(reader.last_error(), JET_errOneDatabasePerSession);
+}
+
+TEST_F(EdgeDatabaseReaderTest, OpenTableAndReadDataDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(CopyTestDatabase(L"testdata.edb", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_TRUE(reader.OpenDatabase(database_path.value()));
+ scoped_ptr<EdgeDatabaseTableEnumerator> table_enum =
+ reader.OpenTableEnumerator(L"TestTable");
+ EXPECT_NE(table_enum, nullptr);
+ int row_count = 0;
+ do {
+ int32_t int_col_value = 0;
+ EXPECT_TRUE(table_enum->RetrieveColumn(L"IntCol", &int_col_value));
+ EXPECT_EQ(int_col_value, -row_count);
+
+ uint32_t uint_col_value = 0;
+ EXPECT_TRUE(table_enum->RetrieveColumn(L"UIntCol", &uint_col_value));
+ EXPECT_EQ(uint_col_value, row_count);
+
+ int64_t longlong_col_value = 0;
+ EXPECT_TRUE(
+ table_enum->RetrieveColumn(L"LongLongCol", &longlong_col_value));
+ EXPECT_EQ(longlong_col_value, row_count);
+
+ GUID guid_col_value = {};
+ GUID expected_guid_col_value = {};
+ EXPECT_TRUE(table_enum->RetrieveColumn(L"GuidCol", &guid_col_value));
+ memset(&expected_guid_col_value, row_count,
+ sizeof(expected_guid_col_value));
+ EXPECT_EQ(guid_col_value, expected_guid_col_value);
+
+ FILETIME filetime_col_value = {};
+ FILETIME expected_filetime_col_value = {};
+ SYSTEMTIME system_time = {};
+ // Expected time value is row_count+1/January/1970.
+ system_time.wYear = 1970;
+ system_time.wMonth = 1;
+ system_time.wDay = row_count + 1;
+ EXPECT_TRUE(
+ SystemTimeToFileTime(&system_time, &expected_filetime_col_value));
+ EXPECT_TRUE(table_enum->RetrieveColumn(L"DateCol", &filetime_col_value));
+ EXPECT_EQ(filetime_col_value.dwLowDateTime,
+ expected_filetime_col_value.dwLowDateTime);
+ EXPECT_EQ(filetime_col_value.dwHighDateTime,
+ expected_filetime_col_value.dwHighDateTime);
+
+ std::wstring row_string = base::StringPrintf(L"String: %d", row_count);
+ base::string16 str_col_value;
+ EXPECT_TRUE(table_enum->RetrieveColumn(L"StrCol", &str_col_value));
+ EXPECT_EQ(str_col_value, row_string);
+
+ bool bool_col_value;
+ EXPECT_TRUE(table_enum->RetrieveColumn(L"BoolCol", &bool_col_value));
+ EXPECT_EQ(bool_col_value, (row_count % 2) == 0);
+
+ row_count++;
+ } while (table_enum->Next());
+ EXPECT_EQ(row_count, 16);
+}
+
+TEST_F(EdgeDatabaseReaderTest, CheckEnumResetDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(CopyTestDatabase(L"testdata.edb", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_TRUE(reader.OpenDatabase(database_path.value()));
+ scoped_ptr<EdgeDatabaseTableEnumerator> table_enum =
+ reader.OpenTableEnumerator(L"TestTable");
+ EXPECT_NE(table_enum, nullptr);
+ int row_count = 0;
+ do {
+ row_count++;
+ } while (table_enum->Next());
+ EXPECT_NE(row_count, 0);
+ EXPECT_TRUE(table_enum->Reset());
+ do {
+ row_count--;
+ } while (table_enum->Next());
+ EXPECT_EQ(row_count, 0);
+}
+
+TEST_F(EdgeDatabaseReaderTest, InvalidColumnDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(CopyTestDatabase(L"testdata.edb", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_TRUE(reader.OpenDatabase(database_path.value()));
+ scoped_ptr<EdgeDatabaseTableEnumerator> table_enum =
+ reader.OpenTableEnumerator(L"TestTable");
+ EXPECT_NE(table_enum, nullptr);
+ int32_t int_col_value = 0;
+ EXPECT_FALSE(table_enum->RetrieveColumn(L"NotARealNameCol", &int_col_value));
+ EXPECT_EQ(table_enum->last_error(), JET_errColumnNotFound);
+}
+
+TEST_F(EdgeDatabaseReaderTest, NoColumnDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(CopyTestDatabase(L"testdata.edb", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_TRUE(reader.OpenDatabase(database_path.value()));
+ scoped_ptr<EdgeDatabaseTableEnumerator> table_enum =
+ reader.OpenTableEnumerator(L"NoColsTable");
+ EXPECT_NE(table_enum, nullptr);
+ int32_t int_col_value = 0;
+ EXPECT_FALSE(table_enum->RetrieveColumn(L"IntCol", &int_col_value));
+ EXPECT_EQ(table_enum->last_error(), JET_errColumnNotFound);
+}
+
+TEST_F(EdgeDatabaseReaderTest, EmptyTableDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(CopyTestDatabase(L"testdata.edb", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_TRUE(reader.OpenDatabase(database_path.value()));
+ scoped_ptr<EdgeDatabaseTableEnumerator> table_enum =
+ reader.OpenTableEnumerator(L"EmptyTable");
+ EXPECT_NE(table_enum, nullptr);
+ int32_t int_col_value = 0;
+ EXPECT_FALSE(table_enum->RetrieveColumn(L"IntCol", &int_col_value));
+ EXPECT_NE(table_enum->last_error(), JET_errColumnNotFound);
+ EXPECT_FALSE(table_enum->Reset());
+ EXPECT_FALSE(table_enum->Next());
+}
+
+TEST_F(EdgeDatabaseReaderTest, UnicodeStringsDatabaseTest) {
+ const char* utf8_strings[] = {
+ "\xE3\x81\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF",
+ "\xE4\xBD\xA0\xE5\xA5\xBD",
+ "\xD0\x97\xD0\xB4\xD1\x80\xD0\xB0\xD0\xB2\xD1\x81\xD1\x82\xD0\xB2\xD1\x83"
+ "\xD0\xB9\xD1\x82\xD0\xB5",
+ "\x48\x65\x6C\x6C\x6F",
+ "\xEC\x95\x88\xEB\x85\x95\xED\x95\x98\xEC\x84\xB8\xEC\x9A\x94",
+ };
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(CopyTestDatabase(L"testdata.edb", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_TRUE(reader.OpenDatabase(database_path.value()));
+ scoped_ptr<EdgeDatabaseTableEnumerator> table_enum =
+ reader.OpenTableEnumerator(L"UnicodeTable");
+ EXPECT_NE(table_enum, nullptr);
+ size_t utf8_strings_count = arraysize(utf8_strings);
+ for (size_t row_count = 0; row_count < utf8_strings_count; ++row_count) {
+ std::wstring row_string = base::UTF8ToWide(utf8_strings[row_count]);
+ base::string16 str_col_value;
+ EXPECT_TRUE(table_enum->RetrieveColumn(L"StrCol", &str_col_value));
+ EXPECT_EQ(str_col_value, row_string);
+ if (row_count < utf8_strings_count - 1)
+ EXPECT_TRUE(table_enum->Next());
+ else
+ EXPECT_FALSE(table_enum->Next());
+ }
+}
+
+TEST_F(EdgeDatabaseReaderTest, NonUnicodeStringsDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(CopyTestDatabase(L"testdata.edb", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_TRUE(reader.OpenDatabase(database_path.value()));
+ scoped_ptr<EdgeDatabaseTableEnumerator> table_enum =
+ reader.OpenTableEnumerator(L"NonUnicodeTable");
+ EXPECT_NE(table_enum, nullptr);
+ base::string16 str_col_value;
+ EXPECT_FALSE(table_enum->RetrieveColumn(L"StrCol", &str_col_value));
+ EXPECT_EQ(table_enum->last_error(), JET_errInvalidColumnType);
+}
+
+TEST_F(EdgeDatabaseReaderTest, CheckNullColumnDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(CopyTestDatabase(L"testdata.edb", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_TRUE(reader.OpenDatabase(database_path.value()));
+ scoped_ptr<EdgeDatabaseTableEnumerator> table_enum =
+ reader.OpenTableEnumerator(L"NullTable");
+ EXPECT_NE(table_enum, nullptr);
+
+ // We expect to successfully open a column value but get the default value
+ // back.
+ int32_t int_col_value = 1;
+ EXPECT_TRUE(table_enum->RetrieveColumn(L"IntCol", &int_col_value));
+ EXPECT_EQ(int_col_value, 0);
+
+ uint32_t uint_col_value = 1;
+ EXPECT_TRUE(table_enum->RetrieveColumn(L"UIntCol", &uint_col_value));
+ EXPECT_EQ(uint_col_value, 0);
+
+ int64_t longlong_col_value = 1;
+ EXPECT_TRUE(table_enum->RetrieveColumn(L"LongLongCol", &longlong_col_value));
+ EXPECT_EQ(longlong_col_value, 0);
+
+ GUID guid_col_value = {};
+ GUID expected_guid_col_value = {};
+ memset(&guid_col_value, 0x1, sizeof(guid_col_value));
+ EXPECT_TRUE(table_enum->RetrieveColumn(L"GuidCol", &guid_col_value));
+ memset(&expected_guid_col_value, 0, sizeof(expected_guid_col_value));
+ EXPECT_EQ(guid_col_value, expected_guid_col_value);
+
+ FILETIME filetime_col_value = {1, 1};
+ FILETIME expected_filetime_col_value = {};
+ EXPECT_TRUE(table_enum->RetrieveColumn(L"DateCol", &filetime_col_value));
+ EXPECT_EQ(filetime_col_value.dwLowDateTime,
+ expected_filetime_col_value.dwLowDateTime);
+ EXPECT_EQ(filetime_col_value.dwHighDateTime,
+ expected_filetime_col_value.dwHighDateTime);
+
+ base::string16 str_col_value;
+ EXPECT_TRUE(table_enum->RetrieveColumn(L"StrCol", &str_col_value));
+ EXPECT_TRUE(str_col_value.empty());
+
+ bool bool_col_value;
+ EXPECT_TRUE(table_enum->RetrieveColumn(L"BoolCol", &bool_col_value));
+ EXPECT_EQ(bool_col_value, false);
+}
+
+TEST_F(EdgeDatabaseReaderTest, CheckInvalidColumnTypeDatabaseTest) {
+ // Only verified to work with ESE library on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::FilePath database_path;
+ ASSERT_TRUE(CopyTestDatabase(L"testdata.edb", &database_path));
+ EdgeDatabaseReader reader;
+ EXPECT_TRUE(reader.OpenDatabase(database_path.value()));
+ scoped_ptr<EdgeDatabaseTableEnumerator> table_enum =
+ reader.OpenTableEnumerator(L"TestTable");
+ EXPECT_NE(table_enum, nullptr);
+
+ uint32_t uint_col_value = 0;
+ EXPECT_FALSE(table_enum->RetrieveColumn(L"IntCol", &uint_col_value));
+ EXPECT_EQ(table_enum->last_error(), JET_errInvalidColumnType);
+ // Check unsigned int with a signed int.
+ int32_t int_col_value = 0;
+ EXPECT_FALSE(table_enum->RetrieveColumn(L"UIntCol", &int_col_value));
+ EXPECT_EQ(table_enum->last_error(), JET_errInvalidColumnType);
+ EXPECT_FALSE(table_enum->RetrieveColumn(L"LongLongCol", &uint_col_value));
+ EXPECT_EQ(table_enum->last_error(), JET_errInvalidColumnType);
+ EXPECT_FALSE(table_enum->RetrieveColumn(L"GuidCol", &uint_col_value));
+ EXPECT_EQ(table_enum->last_error(), JET_errInvalidColumnType);
+ EXPECT_FALSE(table_enum->RetrieveColumn(L"DateCol", &uint_col_value));
+ EXPECT_EQ(table_enum->last_error(), JET_errInvalidColumnType);
+ EXPECT_FALSE(table_enum->RetrieveColumn(L"StrCol", &uint_col_value));
+ EXPECT_EQ(table_enum->last_error(), JET_errInvalidColumnType);
+ EXPECT_FALSE(table_enum->RetrieveColumn(L"BoolCol", &uint_col_value));
+ EXPECT_EQ(table_enum->last_error(), JET_errInvalidColumnType);
+}
diff --git a/chrome/utility/importer/edge_database_reader_win.cc b/chrome/utility/importer/edge_database_reader_win.cc
new file mode 100644
index 0000000..58f4b20
--- /dev/null
+++ b/chrome/utility/importer/edge_database_reader_win.cc
@@ -0,0 +1,257 @@
+// 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_database_reader_win.h"
+
+#include <windows.h>
+
+#include <vector>
+
+namespace {
+
+// This is an arbitary size chosen for the database error message buffer.
+const size_t kErrorMessageSize = 1024;
+// This is the page size of the Edge data. It's unlikely to change.
+const JET_API_PTR kEdgeDatabasePageSize = 8192;
+// This is the code page value for a Unicode (UCS-2) column.
+const unsigned short kJetUnicodeCodePage = 1200;
+
+template <typename T>
+bool ValidateAndConvertValueGeneric(const JET_COLTYP match_column_type,
+ const JET_COLTYP column_type,
+ const std::vector<uint8_t>& column_data,
+ T* value) {
+ if ((column_type == match_column_type) && (column_data.size() == sizeof(T))) {
+ memcpy(value, &column_data[0], sizeof(T));
+ return true;
+ }
+ return false;
+}
+
+bool ValidateAndConvertValue(const JET_COLTYP column_type,
+ const std::vector<uint8_t>& column_data,
+ bool* value) {
+ if ((column_type == JET_coltypBit) && (column_data.size() == 1)) {
+ *value = (column_data[0] & 1) == 1;
+ return true;
+ }
+ return false;
+}
+
+bool ValidateAndConvertValue(const JET_COLTYP column_type,
+ const std::vector<uint8_t>& column_data,
+ base::string16* value) {
+ if ((column_type == JET_coltypLongText) &&
+ ((column_data.size() % sizeof(base::char16)) == 0)) {
+ base::string16& value_ref = *value;
+ size_t char_length = column_data.size() / sizeof(base::char16);
+ value_ref.resize(char_length);
+ memcpy(&value_ref[0], &column_data[0], column_data.size());
+ // Remove any trailing NUL characters.
+ while (char_length > 0) {
+ if (value_ref[char_length - 1])
+ break;
+ char_length--;
+ }
+ value_ref.resize(char_length);
+ return true;
+ }
+ return false;
+}
+
+bool ValidateAndConvertValue(const JET_COLTYP column_type,
+ const std::vector<uint8_t>& column_data,
+ GUID* value) {
+ return ValidateAndConvertValueGeneric(JET_coltypGUID, column_type,
+ column_data, value);
+}
+
+bool ValidateAndConvertValue(const JET_COLTYP column_type,
+ const std::vector<uint8_t>& column_data,
+ int32_t* value) {
+ return ValidateAndConvertValueGeneric(JET_coltypLong, column_type,
+ column_data, value);
+}
+
+bool ValidateAndConvertValue(const JET_COLTYP column_type,
+ const std::vector<uint8_t>& column_data,
+ int64_t* value) {
+ return ValidateAndConvertValueGeneric(JET_coltypLongLong, column_type,
+ column_data, value);
+}
+
+bool ValidateAndConvertValue(const JET_COLTYP column_type,
+ const std::vector<uint8_t>& column_data,
+ FILETIME* value) {
+ return ValidateAndConvertValueGeneric(JET_coltypLongLong, column_type,
+ column_data, value);
+}
+
+bool ValidateAndConvertValue(const JET_COLTYP column_type,
+ const std::vector<uint8_t>& column_data,
+ uint32_t* value) {
+ return ValidateAndConvertValueGeneric(JET_coltypUnsignedLong, column_type,
+ column_data, value);
+}
+
+} // namespace
+
+base::string16 EdgeErrorObject::GetErrorMessage() const {
+ WCHAR error_message[kErrorMessageSize] = {};
+ JET_API_PTR err = last_error_;
+ JET_ERR result = JetGetSystemParameter(JET_instanceNil, JET_sesidNil,
+ JET_paramErrorToString, &err,
+ error_message, sizeof(error_message));
+ if (result != JET_errSuccess)
+ return L"";
+
+ return error_message;
+}
+
+bool EdgeErrorObject::SetLastError(JET_ERR error) {
+ last_error_ = error;
+ return error == JET_errSuccess;
+}
+
+EdgeDatabaseTableEnumerator::EdgeDatabaseTableEnumerator(
+ const base::string16& table_name,
+ JET_SESID session_id,
+ JET_TABLEID table_id)
+ : table_id_(table_id), table_name_(table_name), session_id_(session_id) {}
+
+EdgeDatabaseTableEnumerator::~EdgeDatabaseTableEnumerator() {
+ if (table_id_ != JET_tableidNil)
+ JetCloseTable(session_id_, table_id_);
+}
+
+bool EdgeDatabaseTableEnumerator::Reset() {
+ return SetLastError(JetMove(session_id_, table_id_, JET_MoveFirst, 0));
+}
+
+bool EdgeDatabaseTableEnumerator::Next() {
+ return SetLastError(JetMove(session_id_, table_id_, JET_MoveNext, 0));
+}
+
+template <typename T>
+bool EdgeDatabaseTableEnumerator::RetrieveColumn(
+ const base::string16& column_name,
+ T* value) {
+ const JET_COLUMNBASE& column_base = GetColumnByName(column_name);
+ if (column_base.cbMax == 0) {
+ SetLastError(JET_errColumnNotFound);
+ return false;
+ }
+ if (column_base.coltyp == JET_coltypLongText &&
+ column_base.cp != kJetUnicodeCodePage) {
+ SetLastError(JET_errInvalidColumnType);
+ return false;
+ }
+ std::vector<uint8_t> column_data(column_base.cbMax);
+ unsigned long actual_size = 0;
+ JET_ERR err = JetRetrieveColumn(session_id_, table_id_, column_base.columnid,
+ &column_data[0], column_data.size(),
+ &actual_size, 0, nullptr);
+ SetLastError(err);
+ if (err != JET_errSuccess && err != JET_wrnColumnNull) {
+ return false;
+ }
+
+ if (err == JET_errSuccess) {
+ column_data.resize(actual_size);
+ if (!ValidateAndConvertValue(column_base.coltyp, column_data, value)) {
+ SetLastError(JET_errInvalidColumnType);
+ return false;
+ }
+ } else {
+ *value = T();
+ }
+
+ return true;
+}
+
+// Explicitly instantiate implementations of RetrieveColumn for various types.
+template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&,
+ bool*);
+template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&,
+ FILETIME*);
+template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&,
+ GUID*);
+template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&,
+ int32_t*);
+template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&,
+ int64_t*);
+template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&,
+ base::string16*);
+template bool EdgeDatabaseTableEnumerator::RetrieveColumn(const base::string16&,
+ uint32_t*);
+
+const JET_COLUMNBASE& EdgeDatabaseTableEnumerator::GetColumnByName(
+ const base::string16& column_name) {
+ auto found_col = columns_by_name_.find(column_name);
+ if (found_col == columns_by_name_.end()) {
+ JET_COLUMNBASE column_base = {};
+ column_base.cbStruct = sizeof(JET_COLUMNBASE);
+ if (!SetLastError(JetGetTableColumnInfo(
+ session_id_, table_id_, column_name.c_str(), &column_base,
+ sizeof(column_base), JET_ColInfoBase))) {
+ // 0 indicates an invalid column.
+ column_base.cbMax = 0;
+ }
+ columns_by_name_[column_name] = column_base;
+ found_col = columns_by_name_.find(column_name);
+ }
+ return found_col->second;
+}
+
+EdgeDatabaseReader::~EdgeDatabaseReader() {
+ // We don't need to collect other ID handles, terminating instance
+ // is enough to shut the entire session down.
+ if (instance_id_ != JET_instanceNil)
+ JetTerm(instance_id_);
+}
+
+bool EdgeDatabaseReader::OpenDatabase(const base::string16& database_file) {
+ if (IsOpen()) {
+ SetLastError(JET_errOneDatabasePerSession);
+ return false;
+ }
+ if (!SetLastError(JetSetSystemParameter(nullptr, JET_sesidNil,
+ JET_paramDatabasePageSize,
+ kEdgeDatabasePageSize, nullptr)))
+ return false;
+ if (!SetLastError(JetCreateInstance(&instance_id_, L"EdgeDataImporter")))
+ return false;
+ if (!SetLastError(JetSetSystemParameter(&instance_id_, JET_sesidNil,
+ JET_paramRecovery, 0, L"Off")))
+ return false;
+ if (!SetLastError(JetInit(&instance_id_)))
+ return false;
+ if (!SetLastError(
+ JetBeginSession(instance_id_, &session_id_, nullptr, nullptr)))
+ return false;
+ if (!SetLastError(JetAttachDatabase2(session_id_, database_file.c_str(), 0,
+ JET_bitDbReadOnly)))
+ return false;
+ if (!SetLastError(JetOpenDatabase(session_id_, database_file.c_str(), nullptr,
+ &db_id_, JET_bitDbReadOnly)))
+ return false;
+ return true;
+}
+
+scoped_ptr<EdgeDatabaseTableEnumerator> EdgeDatabaseReader::OpenTableEnumerator(
+ const base::string16& table_name) {
+ JET_TABLEID table_id;
+
+ if (!IsOpen()) {
+ SetLastError(JET_errDatabaseNotFound);
+ return nullptr;
+ }
+
+ if (!SetLastError(JetOpenTable(session_id_, db_id_, table_name.c_str(),
+ nullptr, 0, JET_bitTableReadOnly, &table_id)))
+ return nullptr;
+
+ return make_scoped_ptr(
+ new EdgeDatabaseTableEnumerator(table_name, session_id_, table_id));
+}
diff --git a/chrome/utility/importer/edge_database_reader_win.h b/chrome/utility/importer/edge_database_reader_win.h
new file mode 100644
index 0000000..1b87801
--- /dev/null
+++ b/chrome/utility/importer/edge_database_reader_win.h
@@ -0,0 +1,95 @@
+// 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.
+
+#ifndef CHROME_UTILITY_IMPORTER_EDGE_DATABASE_READER_WIN_H_
+#define CHROME_UTILITY_IMPORTER_EDGE_DATABASE_READER_WIN_H_
+
+#define JET_UNICODE
+#include <esent.h>
+#undef JET_UNICODE
+
+#include <map>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+
+class EdgeErrorObject {
+ public:
+ EdgeErrorObject() : last_error_(JET_errSuccess) {}
+
+ // Get the last error converted to a descriptive string.
+ base::string16 GetErrorMessage() const;
+ // Get the last error value.
+ JET_ERR last_error() const { return last_error_; }
+
+ protected:
+ // This function returns true if the passed error parameter is equal
+ // to JET_errSuccess
+ bool SetLastError(JET_ERR error);
+
+ private:
+ JET_ERR last_error_;
+
+ DISALLOW_COPY_AND_ASSIGN(EdgeErrorObject);
+};
+
+class EdgeDatabaseTableEnumerator : public EdgeErrorObject {
+ public:
+ EdgeDatabaseTableEnumerator(const base::string16& table_name,
+ JET_SESID session_id,
+ JET_TABLEID table_id);
+
+ ~EdgeDatabaseTableEnumerator();
+
+ const base::string16& table_name() { return table_name_; }
+
+ // Reset the enumerator to the start of the table. Returns true if successful.
+ bool Reset();
+ // Move to the next row in the table. Returns false on error or no more rows.
+ bool Next();
+
+ // Retrieve a column's data value. If a NULL is encountered in the column the
+ // default value for the template type is placed in |value|.
+ template <typename T>
+ bool RetrieveColumn(const base::string16& column_name, T* value);
+
+ private:
+ const JET_COLUMNBASE& GetColumnByName(const base::string16& column_name);
+
+ std::map<const base::string16, JET_COLUMNBASE> columns_by_name_;
+ JET_TABLEID table_id_;
+ base::string16 table_name_;
+ JET_SESID session_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(EdgeDatabaseTableEnumerator);
+};
+
+class EdgeDatabaseReader : public EdgeErrorObject {
+ public:
+ EdgeDatabaseReader()
+ : db_id_(JET_dbidNil),
+ instance_id_(JET_instanceNil),
+ session_id_(JET_sesidNil) {}
+
+ ~EdgeDatabaseReader();
+
+ // Open the database from a file path. Returns true on success.
+ bool OpenDatabase(const base::string16& database_file);
+
+ // Open a row enumerator for a specified table. Returns a nullptr on error.
+ scoped_ptr<EdgeDatabaseTableEnumerator> OpenTableEnumerator(
+ const base::string16& table_name);
+
+ private:
+ bool IsOpen() { return instance_id_ != JET_instanceNil; }
+
+ JET_DBID db_id_;
+ JET_INSTANCE instance_id_;
+ JET_SESID session_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(EdgeDatabaseReader);
+};
+
+#endif // CHROME_UTILITY_IMPORTER_EDGE_DATABASE_READER_WIN_H_
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);
+}
diff --git a/chrome/utility/importer/edge_importer_win.h b/chrome/utility/importer/edge_importer_win.h
new file mode 100644
index 0000000..79e1f7a
--- /dev/null
+++ b/chrome/utility/importer/edge_importer_win.h
@@ -0,0 +1,43 @@
+// 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.
+
+#ifndef CHROME_UTILITY_IMPORTER_EDGE_IMPORTER_WIN_H_
+#define CHROME_UTILITY_IMPORTER_EDGE_IMPORTER_WIN_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "chrome/utility/importer/importer.h"
+#include "components/favicon_base/favicon_usage_data.h"
+
+struct ImportedBookmarkEntry;
+
+class EdgeImporter : public Importer {
+ public:
+ EdgeImporter();
+
+ // Importer:
+ void StartImport(const importer::SourceProfile& source_profile,
+ uint16 items,
+ ImporterBridge* bridge) override;
+
+ private:
+ ~EdgeImporter() override;
+
+ void ImportFavorites();
+ // This function will read the favorites from the spartan database storing
+ // the bookmark items in |bookmarks| and favicon information in |favicons|.
+ void ParseFavoritesDatabase(std::vector<ImportedBookmarkEntry>* bookmarks,
+ favicon_base::FaviconUsageDataList* favicons);
+
+ // Edge does not have source path. It's used in unit tests only for providing
+ // a fake source for the spartan database location.
+ base::FilePath source_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(EdgeImporter);
+};
+
+#endif // CHROME_UTILITY_IMPORTER_EDGE_IMPORTER_WIN_H_
diff --git a/chrome/utility/importer/ie_importer_win.cc b/chrome/utility/importer/ie_importer_win.cc
index bc017ac..f2fceb0 100644
--- a/chrome/utility/importer/ie_importer_win.cc
+++ b/chrome/utility/importer/ie_importer_win.cc
@@ -29,6 +29,7 @@
#include "base/win/scoped_handle.h"
#include "base/win/scoped_propvariant.h"
#include "base/win/windows_version.h"
+#include "chrome/common/importer/edge_importer_utils_win.h"
#include "chrome/common/importer/ie_importer_utils_win.h"
#include "chrome/common/importer/imported_bookmark_entry.h"
#include "chrome/common/importer/importer_bridge.h"
@@ -420,13 +421,20 @@ const GUID IEImporter::kUnittestGUID = {
{ 0xb8, 0x7, 0x3d, 0x46, 0xab, 0x15, 0x45, 0xdf }
};
-IEImporter::IEImporter() {
-}
+IEImporter::IEImporter() : edge_import_mode_(false) {}
void IEImporter::StartImport(const importer::SourceProfile& source_profile,
uint16 items,
ImporterBridge* bridge) {
+ edge_import_mode_ = source_profile.importer_type == importer::TYPE_EDGE;
bridge_ = bridge;
+
+ if (edge_import_mode_) {
+ // When using for Edge imports we only support Favorites.
+ DCHECK_EQ(items, importer::FAVORITES);
+ // As coming from untrusted source ensure items is correct.
+ items = importer::FAVORITES;
+ }
source_path_ = source_profile.source_path;
bridge_->NotifyStarted();
@@ -478,7 +486,10 @@ void IEImporter::ImportFavorites() {
if (!bookmarks.empty() && !cancelled()) {
const base::string16& first_folder_name =
- l10n_util::GetStringUTF16(IDS_BOOKMARK_GROUP_FROM_IE);
+ edge_import_mode_
+ ? l10n_util::GetStringUTF16(IDS_BOOKMARK_GROUP_FROM_EDGE)
+ : l10n_util::GetStringUTF16(IDS_BOOKMARK_GROUP_FROM_IE);
+
bridge_->AddBookmarks(bookmarks, first_folder_name);
}
if (!favicons.empty() && !cancelled())
@@ -782,7 +793,7 @@ void IEImporter::ImportHomepage() {
bool IEImporter::GetFavoritesInfo(IEImporter::FavoritesInfo* info) {
if (!source_path_.empty()) {
- // Source path exists during testing.
+ // Source path exists during testing as well as when importing from Edge.
info->path = source_path_;
info->path = info->path.AppendASCII("Favorites");
info->links_folder = L"Links";
@@ -888,8 +899,10 @@ void IEImporter::ParseFavoritesFolder(
bookmarks->push_back(entry);
}
- // Reflect the menu order in IE.
- SortBookmarksInIEOrder(this, bookmarks);
+ if (!edge_import_mode_) {
+ // Reflect the menu order in IE.
+ SortBookmarksInIEOrder(this, bookmarks);
+ }
// Record favicon data.
for (FaviconMap::iterator iter = favicon_map.begin();
diff --git a/chrome/utility/importer/ie_importer_win.h b/chrome/utility/importer/ie_importer_win.h
index 1d38034..acf5527 100644
--- a/chrome/utility/importer/ie_importer_win.h
+++ b/chrome/utility/importer/ie_importer_win.h
@@ -76,8 +76,11 @@ class IEImporter : public Importer {
// Determines which version of IE is in use.
int CurrentIEVersion() const;
+ // Set to true when importing favorites from old Edge on Windows 10.
+ bool edge_import_mode_;
+
// IE does not have source path. It's used in unit tests only for providing a
- // fake source.
+ // fake source and it's used if importing old Edge favorites on Windows 10.
base::FilePath source_path_;
DISALLOW_COPY_AND_ASSIGN(IEImporter);
diff --git a/chrome/utility/importer/importer_creator.cc b/chrome/utility/importer/importer_creator.cc
index d1ebe1a..f260f8c 100644
--- a/chrome/utility/importer/importer_creator.cc
+++ b/chrome/utility/importer/importer_creator.cc
@@ -9,6 +9,8 @@
#include "chrome/utility/importer/firefox_importer.h"
#if defined(OS_WIN)
+#include "chrome/common/importer/edge_importer_utils_win.h"
+#include "chrome/utility/importer/edge_importer_win.h"
#include "chrome/utility/importer/ie_importer_win.h"
#endif
@@ -26,6 +28,11 @@ Importer* CreateImporterByType(ImporterType type) {
#if defined(OS_WIN)
case TYPE_IE:
return new IEImporter();
+ case TYPE_EDGE:
+ // If legacy mode we pass back an IE importer.
+ if (IsEdgeFavoritesLegacyMode())
+ return new IEImporter();
+ return new EdgeImporter();
#endif
case TYPE_BOOKMARKS_FILE:
return new BookmarksFileImporter();