Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Fix #3227: Implement History Sync #3703

Merged
merged 43 commits into from
Aug 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
0d8a4c3
History Sync Core History object migration and logic for success
soner-yuksel Apr 19, 2021
be7977f
Add history interface change and delete fix
soner-yuksel Apr 20, 2021
e3c1839
Adding Historyv2 Core Type History and fetchresults
soner-yuksel Apr 27, 2021
9250127
Adding Listing History Feature using Brave-Core
soner-yuksel May 4, 2021
e7c1b0e
Adding Add Remove All from History Data Core
soner-yuksel May 4, 2021
9088a43
Change Object Traverse Algorithm
soner-yuksel May 4, 2021
e09812d
Adding HistoryServiceLoaded Observer to migrate existing data
soner-yuksel May 10, 2021
99a1c9c
Adding History Observer method interface and loaded function
soner-yuksel May 10, 2021
1e67cfe
Adding Service Listener Observer
soner-yuksel May 12, 2021
dd0cd8e
Adding Loaded Observer for setting up types of sync
soner-yuksel May 17, 2021
cc2af9e
Adding Sync Profile Toggles and logic
soner-yuksel May 21, 2021
061b2e0
Adding Clear All Functionality to History View Controller
soner-yuksel May 21, 2021
4da2582
Removing Local Resolved File For Brave Core
soner-yuksel May 21, 2021
bf80b0a
Adding State Empty to to overlay in History Controller
soner-yuksel May 25, 2021
2e6d454
Error while fetching details is fixed
soner-yuksel May 25, 2021
eeb8f41
Adding Visit Type Back to Browser
soner-yuksel May 27, 2021
fd0ff77
Adding distinction between typed and local history add
soner-yuksel May 27, 2021
3ec5240
Making changes on typed navigation logic
soner-yuksel Jun 2, 2021
4bff85e
Fixing bookmark frequency query and history frequency query
soner-yuksel Jun 4, 2021
e9d778e
Renaming Frequency typo on query
soner-yuksel Jun 4, 2021
bdfbf75
Deleting removed files
soner-yuksel Jun 4, 2021
c7d0e74
Fixing merge conflict Strings Brave
soner-yuksel Jun 4, 2021
83359b5
Concurrency is removed for core fetched objects
soner-yuksel Jun 4, 2021
392ca92
Instance share for API
soner-yuksel Jun 4, 2021
df615f5
Fixing Changes in VisitType after rebase
soner-yuksel Jun 14, 2021
8ca1158
Making changes for integrate Brace Core API changes
soner-yuksel Jun 18, 2021
7d410c4
Bump Brave Core library to 1.26.x
soner-yuksel Jun 29, 2021
e930a4e
Changing Bookmark Frequency Fetch (POST-TASK is not uplifted to 1.26)
soner-yuksel Jun 29, 2021
41828f0
Changes related to API usage
soner-yuksel Jun 30, 2021
6019d86
Fixing string key localization problems
soner-yuksel Jul 9, 2021
6767403
Reverting reportSponsoredImageBackgroundEvent change
soner-yuksel Jul 9, 2021
143fe4d
Request Fixes inside Migration Class
soner-yuksel Jul 9, 2021
b76e107
Code Syntax-Style changes and fixes + Removing unused code
soner-yuksel Jul 9, 2021
7fc37b1
OrderedDictionary is adde as a library for SPMLibraries
soner-yuksel Jul 9, 2021
2f760e7
Documentation and test around typedDisplayString is included
soner-yuksel Jul 12, 2021
3f51f6f
Notification Initializer Change and Observer loaded state tracked in …
soner-yuksel Jul 12, 2021
8b9e027
Migration Types removed for Sync and Error with description is added
soner-yuksel Jul 12, 2021
6f98d05
Removing unnecessary blank spaces
soner-yuksel Jul 12, 2021
bbe5c73
Changing typedSiplayString implementation using schemelessAbsoluteURL…
soner-yuksel Jul 12, 2021
2546b2e
PR Comments with styling and nits
soner-yuksel Jul 15, 2021
9c10dad
Fixing Rebase problems
soner-yuksel Jul 21, 2021
2f67853
Pull Request Comments are addressed
soner-yuksel Jul 27, 2021
1779d08
History Fetch updated method name and added a documentation comment …
soner-yuksel Jul 27, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions BraveShared/BraveStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2189,6 +2189,41 @@ extension Strings {
NSLocalizedString("sync.v2MigrationErrorMessage", tableName: "BraveShared", bundle: .braveShared,
value: "Failed to migrate bookmarks. Please try again later.",
comment: "Message for popup when the bookmark migration fails")
/// History Migration localization text
public static let historyMigrationErrorMessage =
NSLocalizedString("sync.historyMigrationErrorMessage", tableName: "BraveShared", bundle: .braveShared,
value: "Failed to migrate hitory. Please try again later.",
comment: "Message for popup when the history migration fails")
public static let syncConfigurationInformationText =
NSLocalizedString("sync.syncConfigurationInformationText", tableName: "BraveShared", bundle: .braveShared,
value: "Manage what information you would like to sync between devices. These settings only affect this device.",
comment: "Information Text underneath the toggles for enable/disable different sync types for the device")
public static let syncSettingsTitle =
NSLocalizedString("sync.syncSettingsTitle", tableName: "BraveShared", bundle: .braveShared,
value: "Sync Settings",
comment: "Title for Sync Settings Toggle Header")
}
}

extension Strings {
public struct History {
public static let historyClearAlertTitle =
NSLocalizedString("history.historyClearAlertTitle", tableName: "BraveShared", bundle: .braveShared,
value: "Clear Browsing History",
comment: "Title for Clear All History Alert Title")
public static let historyClearAlertDescription =
NSLocalizedString("history.historyClearAlertDescription", tableName: "BraveShared", bundle: .braveShared,
value: "This will clear all browsing history.",
comment: "Description for Clear All History Alert Description")
public static let historyClearActionTitle =
NSLocalizedString("history.historyClearActionTitle", tableName: "BraveShared", bundle: .braveShared,
value: "Clear History",
comment: "Title for History Clear All Action")

public static let historyEmptyStateTitle =
NSLocalizedString("history.historyEmptyStateTitle", tableName: "BraveShared", bundle: .braveShared,
value: "History will show up here.",
comment: "Title which is displayed when History screen is empty.")
}
}

Expand Down
89 changes: 80 additions & 9 deletions Client.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@
"version": "5.0.1"
}
},
{
"package": "swift-collections",
"repositoryURL": "https://github.com/apple/swift-collections",
"state": {
"branch": null,
"revision": "3426dba9ee5c9f8e4981b0fc9d39a818d36eec28",
"version": "0.0.4"
}
},
{
"package": "SwiftKeychainWrapper",
"repositoryURL": "https://github.com/jrendel/SwiftKeychainWrapper",
Expand Down
12 changes: 11 additions & 1 deletion Client/Application/ClientPreferences.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,19 @@ extension Preferences {
}

final class Chromium {
/// The boolean determine Bookmark Migration is finished on client side
static let syncV2BookmarksMigrationCompleted = Option<Bool>(key: "chromium.migration.bookmarks", default: false)
static let syncV2BookmarksMigrationCount = Option<Int>(key: "chromium.migration.bookmarks.count", default: 0)
/// The boolean determine History Migration is finished on client side
static let syncV2HistoryMigrationCompleted = Option<Bool>(key: "chromium.migration.history", default: false)
/// The count of how many times migration is performed on client side - the value increases with every fail attempt and after 3 tries migration marked as successful
static let syncV2ObjectMigrationCount = Option<Int>(key: "chromium.migration.attempt.count", default: 0)
/// Whether the device is in sync chain
static let syncEnabled = Option<Bool>(key: "chromium.sync.enabled", default: false)
/// The sync type bookmarks enabled for the device in sync chain
static let syncBookmarksEnabled = Option<Bool>(key: "chromium.sync.syncBookmarksEnabled", default: true)
/// The sync type history enabled for the device in sync chain
static let syncHistoryEnabled = Option<Bool>(key: "chromium.sync.syncHistoryEnabled", default: false)
/// Node Id for last bookmark folder
static let lastBookmarksFolderNodeId = Option<Int?>(key: "chromium.last.bookmark.folder.node.id", default: nil)
}

Expand Down
31 changes: 28 additions & 3 deletions Client/Application/Migration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@ import Shared
import BraveShared
import SwiftKeychainWrapper
import Data
import BraveRewards

private let log = Logger.browserLogger

class Migration {
private(set) public static var braveCoreBookmarksMigrator: BraveCoreMigrator?

private(set) public static var braveCoreSyncObjectsMigrator: BraveCoreMigrator?

public static var isChromiumMigrationCompleted: Bool {
return Preferences.Chromium.syncV2BookmarksMigrationCompleted.value &&
Preferences.Chromium.syncV2HistoryMigrationCompleted.value
}

static func launchMigrations(keyPrefix: String) {
Preferences.migratePreferences(keyPrefix: keyPrefix)
Expand All @@ -22,8 +29,8 @@ class Migration {
}

// `.migrate` is called in `BrowserViewController.viewDidLoad()`
if !Preferences.Chromium.syncV2BookmarksMigrationCompleted.value {
braveCoreBookmarksMigrator = BraveCoreMigrator()
if !isChromiumMigrationCompleted {
braveCoreSyncObjectsMigrator = BraveCoreMigrator()
}

if !Preferences.Migration.playlistV1FileSettingsLocationCompleted.value {
Expand All @@ -34,6 +41,24 @@ class Migration {
FaviconMO.clearTooLargeFavicons()
Preferences.Migration.removeLargeFaviconsMigrationCompleted.value = true
}

// Adding Observer to enable sync types

NotificationCenter.default.addObserver(
self,
selector: #selector(enableUserSelectedTypesForSync),
name: BraveServiceStateObserver.coreServiceLoadedNotification,
object: nil
)
}

@objc private func enableUserSelectedTypesForSync() {
guard BraveSyncAPI.shared.isInSyncGroup else {
log.info("Sync is not active")
return
}

BraveSyncAPI.shared.enableSyncTypes()
}

static func moveDatabaseToApplicationDirectory() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "historyEmpty@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
105 changes: 34 additions & 71 deletions Client/Frontend/Browser/BrowserViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class BrowserViewController: UIViewController {
// Tracking navigation items to record history types.
// TODO: weak references?
var ignoredNavigation = Set<WKNavigation>()
var typedNavigation = [URL: VisitType]()
var navigationToolbar: ToolbarProtocol {
return toolbar ?? topToolbar
}
Expand Down Expand Up @@ -711,24 +712,8 @@ class BrowserViewController: UIViewController {

showWalletTransferExpiryPanelIfNeeded()

// We stop ever attempting migration after 3 times.
if Preferences.Chromium.syncV2BookmarksMigrationCount.value < 3 {
self.migrateToChromiumBookmarks { success in
if !success {
DispatchQueue.main.async {
let alert = UIAlertController(title: Strings.Sync.v2MigrationErrorTitle,
message: Strings.Sync.v2MigrationErrorMessage,
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: Strings.OKString, style: .default, handler: nil))
self.present(alert, animated: true)
}
}
}
} else {
// After 3 tries, we mark Migration as successful.
// There is nothing more we can do for the user other than to let them export/import bookmarks.
Preferences.Chromium.syncV2BookmarksMigrationCompleted.value = true
}
/// Perform migration to brave-core sync objects
doSyncMigration()

if #available(iOS 14, *), !Preferences.DefaultBrowserIntro.defaultBrowserNotificationScheduled.value {
scheduleDefaultBrowserNotification()
Expand All @@ -746,38 +731,6 @@ class BrowserViewController: UIViewController {
})
}

private func migrateToChromiumBookmarks(_ completion: @escaping (_ success: Bool) -> Void) {
let showInterstitialPage = { (url: URL?) -> Bool in
guard let url = url else {
log.error("Cannot open bookmarks page in new tab")
return false
}

return BookmarksInterstitialPageHandler.showBookmarksPage(tabManager: self.tabManager, url: url)
}

Migration.braveCoreBookmarksMigrator?.migrate({ success in
Preferences.Chromium.syncV2BookmarksMigrationCount.value += 1

if !success {
guard let url = BraveCoreMigrator.datedBookmarksURL else {
completion(showInterstitialPage(BraveCoreMigrator.bookmarksURL))
return
}

Migration.braveCoreBookmarksMigrator?.exportBookmarks(to: url) { success in
if success {
completion(showInterstitialPage(url))
} else {
completion(showInterstitialPage(BraveCoreMigrator.bookmarksURL))
}
}
} else {
completion(true)
}
})
}

fileprivate let defaultBrowserNotificationId = "defaultBrowserNotification"

private func scheduleDefaultBrowserNotification() {
Expand Down Expand Up @@ -1265,23 +1218,23 @@ class BrowserViewController: UIViewController {
}
}

func finishEditingAndSubmit(_ url: URL, isBookmark: Bool) {
if url.isBookmarklet, isBookmark {
func finishEditingAndSubmit(_ url: URL, visitType: VisitType) {
if url.isBookmarklet {
topToolbar.leaveOverlayMode()

guard let tab = tabManager.selectedTab,
let webView = tab.webView,
let code = url.bookmarkletCodeComponent else {
guard let tab = tabManager.selectedTab else {
return
}

// Another Fix for: https://github.com/brave/brave-ios/pull/2296
// Disable any sort of privileged execution contexts
// IE: The user must explicitly tap a bookmark they have saved.
// Block all other contexts such as redirects, downloads, embed, linked, etc..
webView.evaluateSafeJavaScript(functionName: code, sandboxed: false, asFunction: false) { _, error in
if let error = error {
log.error(error)
if visitType == .bookmark, let webView = tab.webView, let code = url.bookmarkletCodeComponent {
webView.evaluateSafeJavaScript(functionName: code, sandboxed: false, asFunction: false) { _, error in
if let error = error {
log.error(error)
}
}
}
} else {
Expand All @@ -1293,6 +1246,8 @@ class BrowserViewController: UIViewController {
}

tab.loadRequest(URLRequest(url: url))

recordNavigationInTab(url, visitType: visitType)
}
}

Expand Down Expand Up @@ -1891,7 +1846,15 @@ class BrowserViewController: UIViewController {

// Only add history of a url which is not a localhost url
if !tab.isPrivate {
History.add(tab.title ?? "", url: url)
// The visitType is checked If it is "typed" or not to determine the History object we are adding
// should be synced or not. This limitation exists on browser side so we are aligning with this
if let visitType =
typedNavigation.first(where: { $0.key.typedDisplayString == url.typedDisplayString })?.value,
visitType == .typed {
Historyv2.add(url: url, title: tab.title ?? "", dateAdded: Date())
} else {
Historyv2.add(url: url, title: tab.title ?? "", dateAdded: Date(), isURLTyped: false)
}
}
}

Expand Down Expand Up @@ -1937,8 +1900,8 @@ extension BrowserViewController: ClipboardBarDisplayHandlerDelegate {
extension BrowserViewController: QRCodeViewControllerDelegate {
func didScanQRCodeWithURL(_ url: URL) {
popToBVC()
finishEditingAndSubmit(url, isBookmark: false)
finishEditingAndSubmit(url, visitType: .typed)

if !url.isBookmarklet && !PrivateBrowsingManager.shared.isPrivateBrowsing {
RecentSearch.addItem(type: .qrCode, text: nil, websiteUrl: url.absoluteString)
}
Expand Down Expand Up @@ -2116,11 +2079,11 @@ extension BrowserViewController: TabDelegate {
extension BrowserViewController: SearchViewControllerDelegate {
func searchViewController(_ searchViewController: SearchViewController, didSubmit query: String) {
topToolbar.leaveOverlayMode()
processAddressBar(text: query)
processAddressBar(text: query, visitType: .typed)
}

func searchViewController(_ searchViewController: SearchViewController, didSelectURL url: URL) {
finishEditingAndSubmit(url, isBookmark: false)
finishEditingAndSubmit(url, visitType: .typed)
}

func searchViewController(_ searchViewController: SearchViewController, didLongPressSuggestion suggestion: String) {
Expand Down Expand Up @@ -2693,30 +2656,30 @@ extension BrowserViewController: ToolbarUrlActionsDelegate {

func openInNewTab(_ url: URL, isPrivate: Bool) {
topToolbar.leaveOverlayMode()
select(url, isBookmark: false, action: .openInNewTab(isPrivate: isPrivate))
select(url, visitType: .unknown, action: .openInNewTab(isPrivate: isPrivate))
}

func copy(_ url: URL) {
select(url, isBookmark: false, action: .copy)
select(url, visitType: .unknown, action: .copy)
}

func share(_ url: URL) {
select(url, isBookmark: false, action: .share)
select(url, visitType: .unknown, action: .share)
}

func batchOpen(_ urls: [URL]) {
let tabIsPrivate = TabType.of(tabManager.selectedTab).isPrivate
self.tabManager.addTabsForURLs(urls, zombie: false, isPrivate: tabIsPrivate)
}

func select(url: URL, isBookmark: Bool) {
select(url, isBookmark: isBookmark, action: .openInCurrentTab)
func select(url: URL, visitType: VisitType) {
select(url, visitType: visitType, action: .openInCurrentTab)
}

private func select(_ url: URL, isBookmark: Bool, action: ToolbarURLAction) {
private func select(_ url: URL, visitType: VisitType, action: ToolbarURLAction) {
switch action {
case .openInCurrentTab:
finishEditingAndSubmit(url, isBookmark: isBookmark)
finishEditingAndSubmit(url, visitType: visitType)
case .openInNewTab(let isPrivate):
let tab = tabManager.addTab(PrivilegedRequest(url: url) as URLRequest, afterTab: tabManager.selectedTab, isPrivate: isPrivate)
if isPrivate && !PrivateBrowsingManager.shared.isPrivateBrowsing {
Expand Down Expand Up @@ -2754,7 +2717,7 @@ extension BrowserViewController: NewTabPageDelegate {
if inNewTab {
tabManager.addTabAndSelect(isPrivate: isPrivate)
}
processAddressBar(text: input)
processAddressBar(text: input, visitType: .bookmark)
}

func handleFavoriteAction(favorite: Favorite, action: BookmarksAction) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ import Shared
import WebKit
import Storage

// MARK: VisitType

enum VisitType: Int {
case unknown
/// Transition type where user followed a link and got a new top-level window
case link
/// Transition type where user typed the page's URL in the URL bar or selected it from URL bar autocomplete results.
case typed

/// Transition type where user opened a link from bookmarks.
case bookmark
case download
}

// MARK: - ReaderModeDelegate

extension BrowserViewController: ReaderModeDelegate {
Expand Down Expand Up @@ -189,6 +203,10 @@ extension BrowserViewController {
}

func ignoreNavigationInTab(_ tab: Tab, navigation: WKNavigation) {
self.ignoredNavigation.insert(navigation)
ignoredNavigation.insert(navigation)
}

func recordNavigationInTab(_ url: URL, visitType: VisitType) {
typedNavigation[url] = visitType
}
}
Loading