diff --git a/app/brave_generated_resources.grd b/app/brave_generated_resources.grd
index bf9054084fb5..52e32720cfdc 100644
--- a/app/brave_generated_resources.grd
+++ b/app/brave_generated_resources.grd
@@ -938,7 +938,7 @@ By installing this extension, you are agreeing to the Google Widevine Terms of U
Full Disk Access required
- Brave needs Full Disk Access to import your Bookmarks from Safari.
+ Brave needs Full Disk Access to import your Bookmarks and History from Safari.
Learn how to grant Full Disk Access from your System Preferences.
diff --git a/browser/ui/webui/settings/brave_import_data_handler_mac.mm b/browser/ui/webui/settings/brave_import_data_handler_mac.mm
index 15bf7f367b40..9a44093a94d2 100644
--- a/browser/ui/webui/settings/brave_import_data_handler_mac.mm
+++ b/browser/ui/webui/settings/brave_import_data_handler_mac.mm
@@ -32,6 +32,26 @@
using content::BrowserThread;
+class ScopedOpenFile {
+ public:
+ explicit ScopedOpenFile(const base::FilePath& file_path) {
+ file_ = base::OpenFile(file_path, "r");
+ }
+
+ ~ScopedOpenFile() {
+ if (file_)
+ base::CloseFile(file_);
+ }
+
+ bool CanRead() const { return !!file_; }
+
+ ScopedOpenFile(const ScopedOpenFile&) = delete;
+ ScopedOpenFile& operator=(const ScopedOpenFile&) = delete;
+
+ private:
+ FILE* file_ = nullptr;
+};
+
class FullDiskAccessConfirmDialogDelegate
: public TabModalConfirmDialogDelegate {
public:
@@ -104,7 +124,8 @@ bool HasProperDiskAccessPermission(uint16_t imported_items) {
if (imported_items & importer::FAVORITES) {
const base::FilePath bookmarks_path = safari_dir.Append("Bookmarks.plist");
- if(!PathIsWritable(bookmarks_path)) {
+ ScopedOpenFile open_file(bookmarks_path);
+ if(!open_file.CanRead()) {
LOG(ERROR) << __func__ << " " << bookmarks_path << " is not accessible."
<< " Please check full disk access permission.";
return false;
@@ -112,8 +133,15 @@ bool HasProperDiskAccessPermission(uint16_t imported_items) {
}
if (imported_items & importer::HISTORY) {
- const base::FilePath history_path = safari_dir.Append("History.plist");
- if(!PathIsWritable(history_path)) {
+ // HISTORY is set if plist or db exists.
+ base::FilePath history_path = safari_dir.Append("History.plist");
+ if (!base::PathExists(history_path)) {
+ history_path = safari_dir.Append("History.db");
+ DCHECK(base::PathExists(history_path));
+ }
+
+ ScopedOpenFile open_file(history_path);
+ if(!open_file.CanRead()) {
LOG(ERROR) << __func__ << " " << history_path << " is not accessible."
<< " Please check full disk access permission.";
return false;
diff --git a/chromium_src/chrome/common/importer/safari_importer_utils.mm b/chromium_src/chrome/common/importer/safari_importer_utils.mm
new file mode 100644
index 000000000000..cb7abf98e67e
--- /dev/null
+++ b/chromium_src/chrome/common/importer/safari_importer_utils.mm
@@ -0,0 +1,20 @@
+/* Copyright (c) 2020 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#define SafariImporterCanImport SafariImporterCanImport_ChromiumImpl
+
+#include "../../../../../chrome/common/importer/safari_importer_utils.mm"
+
+#undef SafariImporterCanImport
+
+bool SafariImporterCanImport(const base::FilePath& library_dir,
+ uint16_t* services_supported) {
+ SafariImporterCanImport_ChromiumImpl(library_dir, services_supported);
+
+ if (base::PathExists(library_dir.Append("Safari").Append("History.db")))
+ *services_supported |= importer::HISTORY;
+
+ return *services_supported != importer::NONE;
+}
diff --git a/chromium_src/chrome/utility/importer/importer_creator.cc b/chromium_src/chrome/utility/importer/importer_creator.cc
new file mode 100644
index 000000000000..e75af9eefb45
--- /dev/null
+++ b/chromium_src/chrome/utility/importer/importer_creator.cc
@@ -0,0 +1,10 @@
+/* Copyright 2019 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "brave/utility/importer/brave_safari_importer.h"
+
+#define SafariImporter BraveSafariImporter
+#include "../../../../../chrome/utility/importer/importer_creator.cc"
+#undef SafariImporter
diff --git a/chromium_src/chrome/utility/importer/safari_importer.h b/chromium_src/chrome/utility/importer/safari_importer.h
new file mode 100644
index 000000000000..b7f3d11d75ff
--- /dev/null
+++ b/chromium_src/chrome/utility/importer/safari_importer.h
@@ -0,0 +1,19 @@
+/* Copyright 2020 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef BRAVE_CHROMIUM_SRC_CHROME_UTILITY_IMPORTER_SAFARI_IMPORTER_H_
+#define BRAVE_CHROMIUM_SRC_CHROME_UTILITY_IMPORTER_SAFARI_IMPORTER_H_
+
+#define BRAVE_SAFARI_IMPORTER_H \
+ friend class BraveSafariImporter;
+
+#define ImportHistory virtual ImportHistory
+
+#include "../../../../../chrome/utility/importer/safari_importer.h"
+
+#undef BRAVE_SAFARI_IMPORTER_H
+#undef ImportHistory
+
+#endif // BRAVE_CHROMIUM_SRC_CHROME_UTILITY_IMPORTER_SAFARI_IMPORTER_H_
diff --git a/patches/chrome-utility-importer-safari_importer.h.patch b/patches/chrome-utility-importer-safari_importer.h.patch
new file mode 100644
index 000000000000..0a1f82fd3010
--- /dev/null
+++ b/patches/chrome-utility-importer-safari_importer.h.patch
@@ -0,0 +1,12 @@
+diff --git a/chrome/utility/importer/safari_importer.h b/chrome/utility/importer/safari_importer.h
+index 1891fa3eff16f4e7b8f0821bc9f793c5e00514f7..89bce81bbc3d2f682adb3aacd888ac29f6d06e74 100644
+--- a/chrome/utility/importer/safari_importer.h
++++ b/chrome/utility/importer/safari_importer.h
+@@ -46,6 +46,7 @@ class SafariImporter : public Importer {
+ uint16_t items,
+ ImporterBridge* bridge) override;
+
++ BRAVE_SAFARI_IMPORTER_H
+ private:
+ FRIEND_TEST_ALL_PREFIXES(SafariImporterTest, BookmarkImport);
+ FRIEND_TEST_ALL_PREFIXES(SafariImporterTest,
diff --git a/utility/BUILD.gn b/utility/BUILD.gn
index c72c9ed75952..c06a8c38cdac 100644
--- a/utility/BUILD.gn
+++ b/utility/BUILD.gn
@@ -43,6 +43,19 @@ source_set("utility") {
]
}
+ if (is_mac) {
+ sources += [
+ "importer/brave_safari_importer.mm",
+ "importer/brave_safari_importer.h",
+ ]
+
+ deps += [
+ "//base",
+ "//sql",
+ "//url",
+ ]
+ }
+
if (enable_tor) {
deps += [ "//brave/components/services/tor" ]
}
diff --git a/utility/importer/brave_safari_importer.h b/utility/importer/brave_safari_importer.h
new file mode 100644
index 000000000000..9b74c6bbbd55
--- /dev/null
+++ b/utility/importer/brave_safari_importer.h
@@ -0,0 +1,25 @@
+/* Copyright 2020 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef BRAVE_UTILITY_IMPORTER_BRAVE_SAFARI_IMPORTER_H_
+#define BRAVE_UTILITY_IMPORTER_BRAVE_SAFARI_IMPORTER_H_
+
+#include "chrome/utility/importer/safari_importer.h"
+
+class BraveSafariImporter : public SafariImporter {
+ public:
+ using SafariImporter::SafariImporter;
+
+ BraveSafariImporter(const BraveSafariImporter&) = delete;
+ BraveSafariImporter& operator=(const BraveSafariImporter&) = delete;
+
+ private:
+ // SafariImporter overrides:
+ void ImportHistory() override;
+
+ ~BraveSafariImporter() override;
+};
+
+#endif // BRAVE_UTILITY_IMPORTER_BRAVE_SAFARI_IMPORTER_H_
diff --git a/utility/importer/brave_safari_importer.mm b/utility/importer/brave_safari_importer.mm
new file mode 100644
index 000000000000..a4ac51a6fe34
--- /dev/null
+++ b/utility/importer/brave_safari_importer.mm
@@ -0,0 +1,71 @@
+/* Copyright 2020 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include
+
+#include "brave/utility/importer/brave_safari_importer.h"
+
+#include
+#include
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "chrome/common/importer/importer_bridge.h"
+#include "chrome/common/importer/importer_url_row.h"
+#include "sql/statement.h"
+#include "url/gurl.h"
+
+BraveSafariImporter::~BraveSafariImporter() = default;
+
+void BraveSafariImporter::ImportHistory() {
+ // For importing history from History.plist.
+ SafariImporter::ImportHistory();
+
+ // From now, try to import history from History.db.
+ NSString* library_dir = [NSString
+ stringWithUTF8String:library_dir_.value().c_str()];
+ NSString* safari_dir = [library_dir
+ stringByAppendingPathComponent:@"Safari"];
+ NSString* history_db = [safari_dir
+ stringByAppendingPathComponent:@"History.db"];
+
+ // Import favicons.
+ sql::Database db;
+ const char* db_path = [history_db fileSystemRepresentation];
+ if (!db.Open(base::FilePath(db_path)))
+ return;
+
+ std::vector rows;
+ const char query[] = "SELECT hi.url, hi.visit_count, hv.visit_time, hv.title "
+ "FROM history_items as hi "
+ "JOIN history_visits as hv ON hi.id == hv.history_item";
+ sql::Statement s(db.GetUniqueStatement(query));
+ while (s.Step() && !cancelled()) {
+ const GURL url = GURL(s.ColumnString(0));
+ if (!url.is_valid())
+ continue;
+
+ ImporterURLRow row(url);
+ row.visit_count = s.ColumnInt(1);
+ double visit_time = s.ColumnDouble(2);
+ if (!visit_time)
+ continue;
+ row.last_visit =
+ base::Time::FromDoubleT(visit_time + kCFAbsoluteTimeIntervalSince1970);
+ std::string title = s.ColumnString(3);
+ if (title.empty())
+ title = url.spec();
+ row.title = base::UTF8ToUTF16(title);
+ row.hidden = 0;
+ row.typed_count = 0;
+ rows.push_back(row);
+ }
+
+ if (!rows.empty() && !cancelled()) {
+ bridge_->SetHistoryItems(rows, importer::VISIT_SOURCE_SAFARI_IMPORTED);
+ }
+}