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

Commit

Permalink
Add persistent private browsing
Browse files Browse the repository at this point in the history
  • Loading branch information
Brandon-T committed Jun 9, 2023
1 parent fd32054 commit 740f911
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 39 deletions.
25 changes: 12 additions & 13 deletions Sources/Brave/Frontend/Browser/BrowserViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1130,10 +1130,9 @@ public class BrowserViewController: UIViewController {
} else {
userActivity?.targetContentIdentifier = windowId.uuidString
userActivity?.addUserInfoEntries(from: ["WindowID": windowId.uuidString,
"isPrivate": isPrivateBrowsing])
"isPrivate": isPrivateBrowsing])
}


view.window?.windowScene?.userActivity = userActivity
view.window?.windowScene?.session.userInfo = ["WindowID": windowId.uuidString,
"isPrivate": isPrivateBrowsing]
Expand Down Expand Up @@ -2235,21 +2234,21 @@ public class BrowserViewController: UIViewController {
webView.evaluateSafeJavaScript(functionName: "\(ReaderModeNamespace).checkReadability", contentWorld: ReaderModeScriptHandler.scriptSandbox)

// Only add history of a url which is not a localhost url
if !tab.isPrivate, !url.isReaderModeURL {
if !url.isReaderModeURL {
// 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 {
braveCore.historyAPI.add(url: url, title: tab.title, dateAdded: Date())
} else {
braveCore.historyAPI.add(url: url, title: tab.title, dateAdded: Date(), isURLTyped: false)
}

// Saving Tab. Private Mode - not supported yet.
if !tab.isPrivate {
tabManager.saveTab(tab)
if let visitType = typedNavigation.first(where: {
$0.key.typedDisplayString == url.typedDisplayString
})?.value, visitType == .typed {
braveCore.historyAPI.add(url: url, title: tab.title, dateAdded: Date())
} else {
braveCore.historyAPI.add(url: url, title: tab.title, dateAdded: Date(), isURLTyped: false)
}
}

// Saving Tab.
tabManager.saveTab(tab)
}
}

Expand Down
51 changes: 31 additions & 20 deletions Sources/Brave/Frontend/Browser/TabManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ class TabManager: NSObject {
privateBrowsingManager.isPrivateBrowsing = true
}
// Make sure to wipe the private tabs if the user has the pref turned on
if !TabType.of(tab).isPrivate {
if !TabType.of(tab).isPrivate && (Preferences.Privacy.privateBrowsingOnly.value || !Preferences.Privacy.persistentPrivateBrowsing.value) {
removeAllPrivateTabs()
}

Expand Down Expand Up @@ -381,7 +381,9 @@ class TabManager: NSObject {
// we only want to remove all private tabs when leaving PBM and not when entering.
func willSwitchTabMode(leavingPBM: Bool) {
if leavingPBM {
removeAllPrivateTabs()
if Preferences.Privacy.privateBrowsingOnly.value || !Preferences.Privacy.persistentPrivateBrowsing.value {
removeAllPrivateTabs()
}
}
}

Expand Down Expand Up @@ -498,7 +500,7 @@ class TabManager: NSObject {
}

private func saveTabOrder() {
if privateBrowsingManager.isPrivateBrowsing { return }
if Preferences.Privacy.privateBrowsingOnly.value || (privateBrowsingManager.isPrivateBrowsing && !Preferences.Privacy.persistentPrivateBrowsing.value) { return }
let allTabIds = allTabs.compactMap { $0.id }
SessionTab.saveTabOrder(tabIds: allTabIds)
}
Expand All @@ -507,11 +509,14 @@ class TabManager: NSObject {
assert(Thread.isMainThread)

let isPrivate = tab.type == .private
if !isPrivate {
let isPersistentTab = !isPrivate || (isPrivate && !Preferences.Privacy.privateBrowsingOnly.value && Preferences.Privacy.persistentPrivateBrowsing.value)

if isPersistentTab {
SessionTab.createIfNeeded(windowId: windowId,
tabId: tab.id,
title: Strings.newTab,
tabURL: request?.url ?? TabManager.ntpInteralURL)
tabURL: request?.url ?? TabManager.ntpInteralURL,
isPrivate: isPrivate)
}

delegates.forEach { $0.get()?.tabManager(self, willAddTab: tab) }
Expand Down Expand Up @@ -543,7 +548,7 @@ class TabManager: NSObject {
}

// Ignore on restore.
if flushToDisk && !zombie && !isPrivate {
if flushToDisk && !zombie && isPersistentTab {
saveTab(tab, saveOrder: true)
}

Expand All @@ -558,11 +563,13 @@ class TabManager: NSObject {
tab.webStateDebounceTimer?.invalidate()

if state == .complete || state == .loaded || state == .pushstate || state == .popstate || state == .replacestate {
// Saving Tab Private Mode - not supported yet.
if !tab.isPrivate {
self.preserveScreenshots()
self.saveTab(tab)

if Preferences.Privacy.privateBrowsingOnly.value || (tab.isPrivate && !Preferences.Privacy.persistentPrivateBrowsing.value) {
return
}

self.preserveScreenshots()
self.saveTab(tab)
}
}
}
Expand All @@ -583,8 +590,10 @@ class TabManager: NSObject {
}

func saveAllTabs() {
if privateBrowsingManager.isPrivateBrowsing { return }
SessionTab.updateAll(tabs: tabs(withType: .regular).compactMap({
if Preferences.Privacy.privateBrowsingOnly.value || (privateBrowsingManager.isPrivateBrowsing && !Preferences.Privacy.persistentPrivateBrowsing.value) { return }

let tabs = Preferences.Privacy.persistentPrivateBrowsing.value ? allTabs : tabs(withType: .regular)
SessionTab.updateAll(tabs: tabs.compactMap({
if let sessionData = $0.webView?.sessionData {
return ($0.id, sessionData, $0.title, $0.url ?? TabManager.ntpInteralURL)
}
Expand All @@ -593,7 +602,7 @@ class TabManager: NSObject {
}

func saveTab(_ tab: Tab, saveOrder: Bool = false) {
if privateBrowsingManager.isPrivateBrowsing { return }
if Preferences.Privacy.privateBrowsingOnly.value || (tab.isPrivate && !Preferences.Privacy.persistentPrivateBrowsing.value) { return }
SessionTab.update(tabId: tab.id, interactionState: tab.webView?.sessionData ?? Data(), title: tab.title, url: tab.url ?? TabManager.ntpInteralURL)
if saveOrder {
saveTabOrder()
Expand Down Expand Up @@ -922,20 +931,19 @@ class TabManager: NSObject {
flushToDisk: false,
zombie: true,
id: savedTab.tabId,
isPrivate: false)

let isPrivateBrowsing = privateBrowsingManager.isPrivateBrowsing
isPrivate: savedTab.isPrivate)

tab.lastTitle = savedTab.title
tab.favicon = FaviconFetcher.getIconFromCache(for: tabURL) ?? Favicon.default
tab.setScreenshot(savedTab.screenshot)

Task { @MainActor in
tab.favicon = try await FaviconFetcher.loadIcon(url: tabURL, kind: .smallIcon, persistent: !isPrivateBrowsing)
tab.favicon = try await FaviconFetcher.loadIcon(url: tabURL, kind: .smallIcon, persistent: tab.isPrivate)
tab.setScreenshot(savedTab.screenshot)
}

if savedTab.isSelected {
// Do not select the private tab since we always restore to regular mode!
if savedTab.isSelected && !savedTab.isPrivate {
tabToSelect = tab
}
}
Expand Down Expand Up @@ -1151,8 +1159,11 @@ extension TabManager: WKNavigationDelegate {
return
}

// Saving Tab Private Mode - not supported yet.
if let tab = tabForWebView(webView), !tab.isPrivate {
if let tab = tabForWebView(webView) {
if Preferences.Privacy.privateBrowsingOnly.value || (tab.isPrivate && !Preferences.Privacy.persistentPrivateBrowsing.value) {
return
}

preserveScreenshots()
saveTab(tab)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -603,10 +603,19 @@ class TabTrayController: LoadingViewController {
tabTypeSelector.selectedSegmentIndex = 0
tabTypeSelector.sendActions(for: UIControl.Event.valueChanged)

tabTrayView.showPrivateModeInfo()
// New private tab is created immediately to reflect changes on NTP.
// If user drags the modal down or dismisses it, a new private tab will be ready.
tabManager.addTabAndSelect(isPrivate: true)
if !Preferences.Privacy.persistentPrivateBrowsing.value {
tabTrayView.showPrivateModeInfo()
// New private tab is created immediately to reflect changes on NTP.
// If user drags the modal down or dismisses it, a new private tab will be ready.
tabManager.addTabAndSelect(isPrivate: true)
} else {
if tabManager.tabsForCurrentMode.isEmpty {
tabManager.addTabAndSelect(isPrivate: true)
}

tabTrayView.hidePrivateModeInfo()
tabTrayView.collectionView.reloadData()
}
} else {
tabTrayView.hidePrivateModeInfo()

Expand Down
2 changes: 2 additions & 0 deletions Sources/Brave/Frontend/ClientPreferences.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ extension Preferences {
static let lockWithPasscode = Option<Bool>(key: "privacy.lock-with-passcode", default: false)
/// Forces all private tabs
public static let privateBrowsingOnly = Option<Bool>(key: "privacy.private-only", default: false)
/// Whether or not private browsing tabs can be session restored (persistent private browsing)
public static let persistentPrivateBrowsing = Option<Bool>(key: "privacy.private-browsing-persistence", default: false)
/// Blocks all cookies and access to local storage
static let blockAllCookies = Option<Bool>(key: "privacy.block-all-cookies", default: false)
/// The toggles states for clear private data screen
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import SwiftUI
import Preferences
import Strings
import BraveShared
import Data

struct OtherPrivacySettingsSectionView: View {
@State private var showPrivateBrowsingConfirmation = false
Expand Down Expand Up @@ -34,6 +35,7 @@ struct OtherPrivacySettingsSectionView: View {
Task { @MainActor in
try await Task.sleep(nanoseconds: NSEC_PER_MSEC * 100)

Preferences.Privacy.persistentPrivateBrowsing.value = false
await settings.clearPrivateData([CookiesAndCacheClearable()])

// First remove all tabs so that only a blank tab exists.
Expand All @@ -52,6 +54,25 @@ struct OtherPrivacySettingsSectionView: View {
)
})

if !Preferences.Privacy.privateBrowsingOnly.value {
OptionToggleView(title: Strings.persistentPrivateBrowsing,
subtitle: nil,
option: Preferences.Privacy.persistentPrivateBrowsing) { newValue in
Task { @MainActor in
if newValue {
settings.tabManager.saveAllTabs()
} else {
let tabs = settings.tabManager.allTabs.filter({ $0.isPrivate })
if settings.tabManager.privateBrowsingManager.isPrivateBrowsing {
SessionTab.deleteAll(tabIds: tabs.map({ $0.id }))
} else {
settings.tabManager.removeTabs(tabs)
}
}
}
}
}

ShieldToggleView(
title: Strings.blockMobileAnnoyances,
subtitle: nil,
Expand Down
1 change: 1 addition & 0 deletions Sources/BraveStrings/BraveStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,7 @@ extension Strings {
public static let mediaAutoBackgrounding = NSLocalizedString("MediaAutoBackgrounding", tableName: "BraveShared", bundle: .module, value: "Enable Background Audio", comment: "Setting to allow media to play in the background")
public static let showTabsBar = NSLocalizedString("ShowTabsBar", tableName: "BraveShared", bundle: .module, value: "Tabs Bar", comment: "Setting to show/hide the tabs bar")
public static let privateBrowsingOnly = NSLocalizedString("PrivateBrowsingOnly", tableName: "BraveShared", bundle: .module, value: "Private Browsing Only", comment: "Setting to keep app in private mode")
public static let persistentPrivateBrowsing = NSLocalizedString("PersistentPrivateBrowsing", tableName: "BraveShared", bundle: .module, value: "Persistent Private Browsing", comment: "Setting to allow the app to restore private browsing tabs")
public static let shieldsDefaults = NSLocalizedString("ShieldsDefaults", tableName: "BraveShared", bundle: .module, value: "Brave Shields Global Defaults", comment: "Section title for adbblock, tracking protection, HTTPS-E, and cookies")
public static let shieldsDefaultsFooter = NSLocalizedString("ShieldsDefaultsFooter", tableName: "BraveShared", bundle: .module, value: "These are the default Shields settings for new sites. Changing these won't affect your existing per-site settings.", comment: "Section footer for global shields defaults")
public static let HTTPSEverywhere = NSLocalizedString("HTTPSEverywhere", tableName: "BraveShared", bundle: .module, value: "Upgrade Connections to HTTPS", comment: "")
Expand Down
9 changes: 7 additions & 2 deletions Sources/Data/models/SessionTab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ extension SessionTab {
deleteAll(context: .new(inMemory: false))
}

public static func deleteAll(tabIds: [UUID]) {
let predicate = NSPredicate(format: "\(#keyPath(SessionTab.tabId)) IN %@", tabIds)
deleteAll(predicate: predicate, context: .new(inMemory: false))
}

public static func deleteAll(olderThan timeInterval: TimeInterval) {
let lastUpdatedKeyPath = #keyPath(SessionTab.lastUpdated)
let date = Date().advanced(by: -timeInterval) as NSDate
Expand Down Expand Up @@ -207,7 +212,7 @@ extension SessionTab {
}
}

public static func createIfNeeded(windowId: UUID, tabId: UUID, title: String, tabURL: URL) {
public static func createIfNeeded(windowId: UUID, tabId: UUID, title: String, tabURL: URL, isPrivate: Bool) {
DataController.perform { context in
guard !SessionTab.exists(tabId: tabId, in: context),
let window = SessionWindow.from(windowId: windowId, in: context) else { return }
Expand All @@ -217,7 +222,7 @@ extension SessionTab {
sessionTabGroup: nil,
index: Int32(window.sessionTabs?.count ?? 0),
interactionState: Data(),
isPrivate: false,
isPrivate: isPrivate,
isSelected: false,
lastUpdated: .now,
screenshotData: Data(),
Expand Down

0 comments on commit 740f911

Please sign in to comment.