diff --git a/App/Client.xcodeproj/project.pbxproj b/App/Client.xcodeproj/project.pbxproj index 38fb6f7f048..94bde10100b 100644 --- a/App/Client.xcodeproj/project.pbxproj +++ b/App/Client.xcodeproj/project.pbxproj @@ -75,6 +75,7 @@ CA986FB4298C1254000C6DD8 /* BraveWidgetsModels in Frameworks */ = {isa = PBXBuildFile; productRef = CA986FB3298C1254000C6DD8 /* BraveWidgetsModels */; }; CAADEFE026E2707F0020DC4C /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAADEFDF26E2707F0020DC4C /* SceneDelegate.swift */; }; CAAE653D287C9FCF00FA44A3 /* CPTemplateApplicationSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAAE653C287C9FCF00FA44A3 /* CPTemplateApplicationSceneDelegate.swift */; }; + CABDE77F2A55DD1C00A388A4 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABDE77E2A55DD1C00A388A4 /* AppState.swift */; }; E6231C011B90A44F005ABB0D /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E6231C001B90A44F005ABB0D /* libz.tbd */; }; E6231C051B90A472005ABB0D /* libxml2.2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E6231C041B90A472005ABB0D /* libxml2.2.tbd */; }; F84B22041A0910F600AAB793 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F84B21E51A0910F600AAB793 /* AppDelegate.swift */; }; @@ -315,6 +316,7 @@ CA0391F7271E143F000EB13C /* SingleStatWidget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleStatWidget.swift; sourceTree = ""; }; CAADEFDF26E2707F0020DC4C /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; CAAE653C287C9FCF00FA44A3 /* CPTemplateApplicationSceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CPTemplateApplicationSceneDelegate.swift; sourceTree = ""; }; + CABDE77E2A55DD1C00A388A4 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; }; E6231C001B90A44F005ABB0D /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; E6231C041B90A472005ABB0D /* libxml2.2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.2.tbd; path = usr/lib/libxml2.2.tbd; sourceTree = SDKROOT; }; E62AC15F1E956AFC00843532 /* Dev.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Dev.entitlements; sourceTree = ""; }; @@ -606,6 +608,7 @@ children = ( CAAE653C287C9FCF00FA44A3 /* CPTemplateApplicationSceneDelegate.swift */, F84B21E51A0910F600AAB793 /* AppDelegate.swift */, + CABDE77E2A55DD1C00A388A4 /* AppState.swift */, CAADEFDF26E2707F0020DC4C /* SceneDelegate.swift */, ); path = Delegates; @@ -1040,6 +1043,7 @@ 2F4A572429E608C8003454F8 /* BrowserIntents.intentdefinition in Sources */, F84B22041A0910F600AAB793 /* AppDelegate.swift in Sources */, 27BEDCCC28AD37CE0073425E /* BraveWidgets.intentdefinition in Sources */, + CABDE77F2A55DD1C00A388A4 /* AppState.swift in Sources */, CAADEFE026E2707F0020DC4C /* SceneDelegate.swift in Sources */, CAAE653D287C9FCF00FA44A3 /* CPTemplateApplicationSceneDelegate.swift in Sources */, ); diff --git a/App/iOS/Delegates/AppDelegate.swift b/App/iOS/Delegates/AppDelegate.swift index 0e71961dcb6..ca3126b527b 100644 --- a/App/iOS/Delegates/AppDelegate.swift +++ b/App/iOS/Delegates/AppDelegate.swift @@ -176,6 +176,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { if isFirstLaunch { Preferences.PrivacyReports.ntpOnboardingCompleted.value = false + Preferences.Privacy.shouldShowPersistentPrivateBrowsingAlert.value = false + } else { + Preferences.Privacy.shouldShowPersistentPrivateBrowsingAlert.value = false } Preferences.General.isFirstLaunch.value = false diff --git a/Sources/Brave/States/AppState.swift b/App/iOS/Delegates/AppState.swift similarity index 93% rename from Sources/Brave/States/AppState.swift rename to App/iOS/Delegates/AppState.swift index 53055fc9af6..ca17be7eb82 100644 --- a/Sources/Brave/States/AppState.swift +++ b/App/iOS/Delegates/AppState.swift @@ -6,6 +6,7 @@ import Foundation import UIKit import BraveCore +import Brave import Data import RuntimeWarnings import Shared @@ -15,17 +16,21 @@ import Storage import BraveNews import os.log +private let adsRewardsLog = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "ads-rewards") + /// Class that does startup initialization /// Everything in this class can only be execute ONCE /// IE: BraveCore initialization, BuildChannel, Migrations, etc. public class AppState { + private let log = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "app-state") + public static let shared = AppState() public let braveCore: BraveCoreMain public let dau: DAU public let migration: Migration public let profile: Profile - public let rewards: BraveRewards + public let rewards: Brave.BraveRewards public let newsFeedDataSource: FeedDataSource private var didBecomeActive = false @@ -63,7 +68,7 @@ public class AppState { namespace: "TabManagerScreenshots", quality: UIConstants.screenshotQuality) } catch { - Logger.module.error("Failed to create an image store for files: \(self.profile.files.rootPath) and namespace: \"TabManagerScreenshots\": \(error.localizedDescription)") + log.error("Failed to create an image store for files: \(self.profile.files.rootPath) and namespace: \"TabManagerScreenshots\": \(error.localizedDescription)") } return nil }() @@ -193,14 +198,14 @@ public class AppState { } } - private static func migrateAdsConfirmations(for configruation: BraveRewards.Configuration) { + private static func migrateAdsConfirmations(for configuruation: Brave.BraveRewards.Configuration) { // To ensure after a user launches 1.21 that their ads confirmations, viewed count and // estimated payout remain correct. // // This hack is unfortunately neccessary due to a missed migration path when moving // confirmations from ledger to ads, we must extract `confirmations.json` out of ledger's // state file and save it as a new file under the ads directory. - let base = configruation.storageURL + let base = configuruation.storageURL let ledgerStateContainer = base.appendingPathComponent("ledger/random_state.plist") let adsConfirmations = base.appendingPathComponent("ads/confirmations.json") let fm = FileManager.default diff --git a/App/iOS/Delegates/SceneDelegate.swift b/App/iOS/Delegates/SceneDelegate.swift index 58cbe4baa59..d8223ac24fc 100644 --- a/App/iOS/Delegates/SceneDelegate.swift +++ b/App/iOS/Delegates/SceneDelegate.swift @@ -402,7 +402,7 @@ extension SceneDelegate { // Store the scene's activities let windowId: UUID let isPrivate: Bool - var userActivity = userActivity + let urlToOpen: URL? if let userActivity = userActivity { // Restore the scene with the WindowID from the User-Activity @@ -411,6 +411,7 @@ extension SceneDelegate { windowId = UUID(uuidString: windowIdString) ?? UUID() isPrivate = userActivity.userInfo?["isPrivate"] as? Bool == true privateBrowsingManager.isPrivateBrowsing = isPrivate + urlToOpen = userActivity.userInfo?["OpenURL"] as? URL // Create a new session window SessionWindow.createWindow(isPrivate: isPrivate, isSelected: true, uuid: windowId) @@ -427,6 +428,7 @@ extension SceneDelegate { windowId = windowUUID isPrivate = sceneIsPrivate privateBrowsingManager.isPrivateBrowsing = sceneIsPrivate + urlToOpen = scene.session.userInfo?["OpenURL"] as? URL scene.userActivity = BrowserState.userActivity(for: windowId, isPrivate: isPrivate) } else { @@ -461,6 +463,7 @@ extension SceneDelegate { isPrivate = false privateBrowsingManager.isPrivateBrowsing = false + urlToOpen = nil scene.userActivity = BrowserState.userActivity(for: windowId, isPrivate: false) scene.session.userInfo = ["WindowID": windowId.uuidString, @@ -476,7 +479,6 @@ extension SceneDelegate { rewards: rewards, migration: migration, crashedLastSession: crashedLastSession, - rewards: rewards, newsFeedDataSource: newsFeedDataSource, privateBrowsingManager: privateBrowsingManager ) @@ -509,6 +511,11 @@ extension SceneDelegate { currentTabSceneBrowser.moveTab(tabId: tabId, to: browserViewController) } } + + if let urlToOpen = urlToOpen { + browserViewController.loadViewIfNeeded() + browserViewController.switchToTabForURLOrOpen(urlToOpen, isPrivileged: false) + } return browserViewController } diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController.swift b/Sources/Brave/Frontend/Browser/BrowserViewController.swift index 4f7aff68d03..a95e16c980c 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController.swift @@ -281,7 +281,6 @@ public class BrowserViewController: UIViewController { rewards: BraveRewards, migration: Migration?, crashedLastSession: Bool, - rewards: BraveRewards, newsFeedDataSource: FeedDataSource, privateBrowsingManager: PrivateBrowsingManager ) { @@ -292,7 +291,6 @@ public class BrowserViewController: UIViewController { self.rewards = rewards self.migration = migration self.crashedLastSession = crashedLastSession - self.rewards = rewards self.privateBrowsingManager = privateBrowsingManager self.feedDataSource = newsFeedDataSource feedDataSource.historyAPI = braveCore.historyAPI @@ -1952,6 +1950,25 @@ public class BrowserViewController: UIViewController { } } } + + func openInNewWindow(url: URL?, isPrivate: Bool) { + let activity = BrowserState.userActivity(for: UUID(), isPrivate: isPrivate) + + if let url = url { + activity.addUserInfoEntries(from: ["OpenURL": url]) + } + + let options = UIScene.ActivationRequestOptions().then { + $0.requestingScene = view.window?.windowScene + } + + UIApplication.shared.requestSceneSessionActivation(nil, + userActivity: activity, + options: options, + errorHandler: { error in + Logger.module.error("Error creating new window: \(error)") + }) + } func clearHistoryAndOpenNewTab() { // When PB Only mode is enabled @@ -2496,21 +2513,6 @@ extension BrowserViewController: TabsBarViewControllerDelegate { func tabsBarDidSelectAddNewTab(_ isPrivate: Bool) { openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: isPrivate) } - - func tabsBarDidSelectAddNewWindow(_ isPrivate: Bool) { - let activity = BrowserState.userActivity(for: UUID(), isPrivate: false) - - let options = UIScene.ActivationRequestOptions().then { - $0.requestingScene = view.window?.windowScene - } - - UIApplication.shared.requestSceneSessionActivation(nil, - userActivity: activity, - options: options, - errorHandler: { error in - Logger.module.error("Error creating new window: \(error)") - }) - } func tabsBarDidSelectTab(_ tabsBarController: TabsBarViewController, _ tab: Tab) { if tab == tabManager.selectedTab { return } diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift index 758f6ad15bd..1d7e1018487 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift @@ -242,10 +242,6 @@ extension BrowserViewController: TabManagerDelegate { newTabMenuChildren.append(openNewTab) } addTabMenuChildren.append(openNewTab) - - addTabMenuChildren.append(UIAction(title: "New Window", image: UIImage(systemName: "window.horizontal.closed"), handler: UIAction.deferredActionHandler { [unowned self] _ in - self.tabsBarDidSelectAddNewWindow(privateBrowsingManager.isPrivateBrowsing) - })) var bookmarkMenuChildren: [UIAction] = [] diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift index 307e074b482..bde445a8e04 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift @@ -990,7 +990,6 @@ extension BrowserViewController: WKUIDelegate { } openNewTabAction.accessibilityLabel = "linkContextMenu.openInNewTab" - actions.append(openNewTabAction) } @@ -1001,8 +1000,31 @@ extension BrowserViewController: WKUIDelegate { self.addTab(url: url, inPrivateMode: true, currentTab: currentTab) } openNewPrivateTabAction.accessibilityLabel = "linkContextMenu.openInNewPrivateTab" - actions.append(openNewPrivateTabAction) + + if UIApplication.shared.supportsMultipleScenes { + if !tabType.isPrivate { + let openNewWindowAction = UIAction( + title: Strings.openInNewWindowTitle, + image: UIImage(braveSystemNamed: "leo.window") + ) { _ in + self.openInNewWindow(url: url, isPrivate: false) + } + + openNewWindowAction.accessibilityLabel = "linkContextMenu.openInNewWindow" + actions.append(openNewWindowAction) + } + + let openNewPrivateWindowAction = UIAction( + title: Strings.openInNewPrivateWindowTitle, + image: UIImage(braveSystemNamed: "leo.window.tab-private") + ) { _ in + self.openInNewWindow(url: url, isPrivate: true) + } + + openNewPrivateWindowAction.accessibilityLabel = "linkContextMenu.openInNewPrivateWindow" + actions.append(openNewPrivateWindowAction) + } let copyAction = UIAction( title: Strings.copyLinkActionTitle, diff --git a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistListViewController+TableViewDelegate.swift b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistListViewController+TableViewDelegate.swift index c0f6d3af14e..f5b0fe7f207 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistListViewController+TableViewDelegate.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistListViewController+TableViewDelegate.swift @@ -149,7 +149,7 @@ extension PlaylistListViewController: UITableViewDelegate { handler: { [weak self] (action, view, completionHandler) in guard let self = self else { return } - let isPrivateBrowsing = self.privateBrowsingManager?.isPrivateBrowsing == true + let isPrivateBrowsing = self.isPrivateBrowsing let style: UIAlertController.Style = UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet let alert = UIAlertController( @@ -244,7 +244,7 @@ extension PlaylistListViewController: UITableViewDelegate { // In Private-Browsing, we do not show "Open in New Tab", // we only show "Open in Private Tab" - let isPrivateBrowsing = self.privateBrowsingManager?.isPrivateBrowsing == true + let isPrivateBrowsing = self.isPrivateBrowsing if !isPrivateBrowsing { actions.append( UIAction( diff --git a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistListViewController.swift b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistListViewController.swift index ccf63578aa3..f28860a6bbc 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistListViewController.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistListViewController.swift @@ -48,7 +48,7 @@ class PlaylistListViewController: UIViewController { weak var delegate: PlaylistViewControllerDelegate? private let playerView: VideoView - let privateBrowsingManager: PrivateBrowsingManager? + let isPrivateBrowsing: Bool private var observers = Set() private var folderObserver: AnyCancellable? @@ -74,9 +74,9 @@ class PlaylistListViewController: UIViewController { $0.allowsSelectionDuringEditing = true } - init(playerView: VideoView, privateBrowsingManager: PrivateBrowsingManager?) { + init(playerView: VideoView, isPrivateBrowsing: Bool) { self.playerView = playerView - self.privateBrowsingManager = privateBrowsingManager + self.isPrivateBrowsing = isPrivateBrowsing super.init(nibName: nil, bundle: nil) } @@ -641,7 +641,7 @@ extension PlaylistListViewController { activityIndicator.isHidden = false let selectedCell = tableView.cellForRow(at: indexPath) as? PlaylistCell - playerView.setVideoInfo(videoDomain: item.pageSrc, videoTitle: item.pageTitle, isPrivateBrowsing: privateBrowsingManager?.isPrivateBrowsing == true) + playerView.setVideoInfo(videoDomain: item.pageSrc, videoTitle: item.pageTitle, isPrivateBrowsing: isPrivateBrowsing) PlaylistMediaStreamer.setNowPlayingMediaArtwork(image: selectedCell?.iconView.image) completion?(item) } @@ -756,7 +756,7 @@ extension PlaylistListViewController { if let url = URL(string: item.pageSrc) { self.dismiss(animated: true, completion: nil) - let isPrivateBrowsing = self.privateBrowsingManager?.isPrivateBrowsing == true + let isPrivateBrowsing = self.isPrivateBrowsing self.delegate?.openURLInNewTab( url, isPrivate: isPrivateBrowsing, @@ -829,7 +829,7 @@ extension PlaylistListViewController { let pageURL = URL(string: currentItem.pageSrc) { self.dismiss(animated: true) { - let isPrivateBrowsing = self.privateBrowsingManager?.isPrivateBrowsing == true + let isPrivateBrowsing = self.isPrivateBrowsing browser.tabManager.addTabAndSelect( URLRequest(url: pageURL), isPrivate: isPrivateBrowsing) diff --git a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift index 7649791dea1..bb7effae2ec 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift @@ -49,12 +49,12 @@ class PlaylistViewController: UIViewController { private let player: MediaPlayer private let playerView = VideoView() private let mediaStreamer: PlaylistMediaStreamer - private let privateBrowsingManager: PrivateBrowsingManager? + private let isPrivateBrowsing: Bool private let splitController = UISplitViewController() private let folderController = PlaylistFolderController() - private lazy var listController = PlaylistListViewController(playerView: playerView, privateBrowsingManager: privateBrowsingManager) - private lazy var detailController = PlaylistDetailViewController(isPrivateBrowsing: privateBrowsingManager?.isPrivateBrowsing == true) + private lazy var listController = PlaylistListViewController(playerView: playerView, isPrivateBrowsing: isPrivateBrowsing) + private lazy var detailController = PlaylistDetailViewController(isPrivateBrowsing: isPrivateBrowsing) private var folderObserver: AnyCancellable? private var playerStateObservers = Set() @@ -70,14 +70,14 @@ class PlaylistViewController: UIViewController { mediaPlayer: MediaPlayer, initialItem: PlaylistInfo?, initialItemPlaybackOffset: Double, - privateBrowsingManager: PrivateBrowsingManager? + isPrivateBrowsing: Bool ) { self.openInNewTab = openInNewTab self.openPlaylistSettingsMenu = openPlaylistSettingsMenu self.player = mediaPlayer self.mediaStreamer = PlaylistMediaStreamer(playerView: playerView) - self.privateBrowsingManager = privateBrowsingManager + self.isPrivateBrowsing = isPrivateBrowsing self.folderSharingUrl = nil super.init(nibName: nil, bundle: nil) @@ -315,7 +315,7 @@ class PlaylistViewController: UIViewController { } if let item = PlaylistCarplayManager.shared.currentPlaylistItem { - playerView.setVideoInfo(videoDomain: item.pageSrc, videoTitle: item.pageTitle, isPrivateBrowsing: privateBrowsingManager?.isPrivateBrowsing == true) + playerView.setVideoInfo(videoDomain: item.pageSrc, videoTitle: item.pageTitle, isPrivateBrowsing: isPrivateBrowsing) } else { playerView.resetVideoInfo() } @@ -333,7 +333,7 @@ class PlaylistViewController: UIViewController { self.playerView.setVideoInfo( videoDomain: item.pageSrc, videoTitle: item.pageTitle, - isPrivateBrowsing: self.privateBrowsingManager?.isPrivateBrowsing == true) + isPrivateBrowsing: self.isPrivateBrowsing) } self.listController.highlightActiveItem() @@ -916,7 +916,7 @@ extension PlaylistViewController: VideoViewDelegate { self.playerView.setVideoInfo( videoDomain: item.pageSrc, videoTitle: item.pageTitle, - isPrivateBrowsing: self.privateBrowsingManager?.isPrivateBrowsing == true) + isPrivateBrowsing: self.isPrivateBrowsing) PlaylistMediaStreamer.setNowPlayingInfo(item, withPlayer: self.player) } catch { PlaylistMediaStreamer.clearNowPlayingInfo() @@ -964,7 +964,7 @@ extension PlaylistViewController: VideoViewDelegate { playerView.setVideoInfo( videoDomain: item.pageSrc, videoTitle: item.pageTitle, - isPrivateBrowsing: privateBrowsingManager?.isPrivateBrowsing == true) + isPrivateBrowsing: isPrivateBrowsing) PlaylistMediaStreamer.setNowPlayingInfo(item, withPlayer: self.player) return item diff --git a/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift b/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift index 07c8f1c27d4..ba490f69e0b 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift @@ -117,7 +117,7 @@ public class PlaylistCarplayManager: NSObject { mediaPlayer: mediaPlayer, initialItem: initialItem, initialItemPlaybackOffset: initialItemPlaybackOffset, - privateBrowsingManager: browserController?.privateBrowsingManager) + isPrivateBrowsing: browserController?.privateBrowsingManager.isPrivateBrowsing == true) self.mediaPlayer = mediaPlayer return playlistController } diff --git a/Sources/Brave/Frontend/Browser/Tab.swift b/Sources/Brave/Frontend/Browser/Tab.swift index f4c5ffdf967..1eafd4c6421 100644 --- a/Sources/Brave/Frontend/Browser/Tab.swift +++ b/Sources/Brave/Frontend/Browser/Tab.swift @@ -74,7 +74,6 @@ class Tab: NSObject { private(set) var type: TabType = .regular - var redirectURLs = [URL]() var isPrivate: Bool { diff --git a/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift b/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift index ca43f95b353..8eaa815557d 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift @@ -13,7 +13,6 @@ protocol TabsBarViewControllerDelegate: AnyObject { func tabsBarDidSelectTab(_ tabsBarController: TabsBarViewController, _ tab: Tab) func tabsBarDidLongPressAddTab(_ tabsBarController: TabsBarViewController, button: UIButton) func tabsBarDidSelectAddNewTab(_ isPrivate: Bool) - func tabsBarDidSelectAddNewWindow(_ isPrivate: Bool) func tabsBarDidChangeReaderModeVisibility(_ isHidden: Bool) } @@ -124,10 +123,6 @@ class TabsBarViewController: UIViewController { }) newTabMenu.append(openNewTab) - - newTabMenu.append(UIAction(title: "New Window", image: UIImage(systemName: "window.horizontal.closed"), handler: UIAction.deferredActionHandler { [unowned self] _ in - self.delegate?.tabsBarDidSelectAddNewWindow(isPrivateBrowsing) - })) plusButton.menu = UIMenu(title: "", identifier: nil, children: newTabMenu) privateModeCancellable = tabManager?.privateBrowsingManager diff --git a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift index e194a3b9d43..0139e32059f 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift @@ -618,6 +618,22 @@ class TabTrayController: LoadingViewController { tabTrayView.collectionView.reloadData() navigationController?.setNavigationBarHidden(false, animated: false) } + + if Preferences.Privacy.shouldShowPersistentPrivateBrowsingAlert.value { + Preferences.Privacy.shouldShowPersistentPrivateBrowsingAlert.value = false + + let alert = UIAlertController(title: Strings.persistentPrivateBrowsingAlertTitle, message: Strings.persistentPrivateBrowsingAlertMessage, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: Strings.yes, style: .default) { [unowned self] _ in + Preferences.Privacy.persistentPrivateBrowsing.value = true + self.tabManager.saveAllTabs() + }) + + alert.addAction(UIAlertAction(title: Strings.no, style: .cancel) { _ in + Preferences.Privacy.persistentPrivateBrowsing.value = false + }) + + self.present(alert, animated: true) + } } else { tabTrayView.hidePrivateModeInfo() diff --git a/Sources/Brave/Frontend/ClientPreferences.swift b/Sources/Brave/Frontend/ClientPreferences.swift index 20ce2cbe033..56828a01c4b 100644 --- a/Sources/Brave/Frontend/ClientPreferences.swift +++ b/Sources/Brave/Frontend/ClientPreferences.swift @@ -126,7 +126,9 @@ extension Preferences { /// Forces all private tabs public static let privateBrowsingOnly = Option(key: "privacy.private-only", default: false) /// Whether or not private browsing tabs can be session restored (persistent private browsing) - public static let persistentPrivateBrowsing = Option(key: "privacy.private-browsing-persistence", default: false) + public static let persistentPrivateBrowsing = Option(key: "privacy.private-browsing-persistence", default: true) + /// Whether or not a persistent private browsing alert was shown to existing users + public static let shouldShowPersistentPrivateBrowsingAlert = Option(key: "privacy.private-browsing-persistence-alert", default: false) /// Blocks all cookies and access to local storage static let blockAllCookies = Option(key: "privacy.block-all-cookies", default: false) /// The toggles states for clear private data screen diff --git a/Sources/Brave/Frontend/Rewards/BraveRewards.swift b/Sources/Brave/Frontend/Rewards/BraveRewards.swift index e70e29f0e88..a7a00c9bbd3 100644 --- a/Sources/Brave/Frontend/Rewards/BraveRewards.swift +++ b/Sources/Brave/Frontend/Rewards/BraveRewards.swift @@ -306,7 +306,7 @@ extension BraveRewards { case staging } - var storageURL: URL + public var storageURL: URL public var environment: Environment public var adsBuildChannel: BraveAds.BuildChannelInfo = .init() public var isDebug: Bool? diff --git a/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift b/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift index f214e228fe4..99f8c1017e1 100644 --- a/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift +++ b/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift @@ -58,15 +58,17 @@ struct OtherPrivacySettingsSectionView: View { 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 }) - SessionTab.deleteAll(tabIds: tabs.map({ $0.id })) - - if !settings.tabManager.privateBrowsingManager.isPrivateBrowsing { - settings.tabManager.willSwitchTabMode(leavingPBM: true) + if !Preferences.Privacy.shouldShowPersistentPrivateBrowsingAlert.value { + Task { @MainActor in + if newValue { + settings.tabManager.saveAllTabs() + } else { + let tabs = settings.tabManager.allTabs.filter({ $0.isPrivate }) + SessionTab.deleteAll(tabIds: tabs.map({ $0.id })) + + if !settings.tabManager.privateBrowsingManager.isPrivateBrowsing { + settings.tabManager.willSwitchTabMode(leavingPBM: true) + } } } } diff --git a/Sources/BraveStrings/BraveStrings.swift b/Sources/BraveStrings/BraveStrings.swift index 1ac7a85c3aa..34836c8b4b5 100644 --- a/Sources/BraveStrings/BraveStrings.swift +++ b/Sources/BraveStrings/BraveStrings.swift @@ -141,6 +141,8 @@ extension Strings { bundle: .module, value: "An unknown error occurred while opening the Downloads folder in the Files app.", comment: "Error description when there is an error while navigating to Files App") + public static let openInNewWindowTitle = NSLocalizedString("OpenInNewWindowTitle", tableName: "BraveShared", bundle: .module, value: "Open in New Window", comment: "Context menu item for opening a link in a new window") + public static let openInNewPrivateWindowTitle = NSLocalizedString("OpenInNewPrivateWindowTitle", tableName: "BraveShared", bundle: .module, value: "Open in New Private Window", comment: "Context menu item for opening a link in a new private browsing window") } // MARK:- DefaultBrowserIntroCalloutViewController.swift @@ -1050,6 +1052,8 @@ extension Strings { public static let youtubeMediaQualityOn = NSLocalizedString("YoutubeMediaQualityOn", tableName: "BraveShared", bundle: .module, value: "Always enabled", comment: "Setting that enables high quality playback always") 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 persistentPrivateBrowsingAlertTitle = NSLocalizedString("PersistentPrivateBrowsingAlertTitle", tableName: "BraveShared", bundle: .module, value: "Restore Private Tabs", comment: "Persistent private browsing alert title to existing users") + public static let persistentPrivateBrowsingAlertMessage = NSLocalizedString("PersistentPrivateBrowsingAlertMessage", tableName: "BraveShared", bundle: .module, value: "Allows Brave to restore private browsing tabs, even if you close / re-open the app", comment: "Persistent private browsing alert message to existing users") 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") diff --git a/Sources/DesignSystem/Icons/Symbols.xcassets/leo.window.symbolset/Contents.json b/Sources/DesignSystem/Icons/Symbols.xcassets/leo.window.symbolset/Contents.json new file mode 100644 index 00000000000..2f415ce6e4c --- /dev/null +++ b/Sources/DesignSystem/Icons/Symbols.xcassets/leo.window.symbolset/Contents.json @@ -0,0 +1,11 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "symbols" : [ + { + "idiom" : "universal" + } + ] +} diff --git a/Sources/DesignSystem/Icons/Symbols.xcassets/leo.window.tab-private.symbolset/Contents.json b/Sources/DesignSystem/Icons/Symbols.xcassets/leo.window.tab-private.symbolset/Contents.json new file mode 100644 index 00000000000..2f415ce6e4c --- /dev/null +++ b/Sources/DesignSystem/Icons/Symbols.xcassets/leo.window.tab-private.symbolset/Contents.json @@ -0,0 +1,11 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "symbols" : [ + { + "idiom" : "universal" + } + ] +} diff --git a/Sources/Onboarding/ProductNotifications/CookieNotificationBlockingConsentView.swift b/Sources/Onboarding/ProductNotifications/CookieNotificationBlockingConsentView.swift index 93478e880b5..d5ace344006 100644 --- a/Sources/Onboarding/ProductNotifications/CookieNotificationBlockingConsentView.swift +++ b/Sources/Onboarding/ProductNotifications/CookieNotificationBlockingConsentView.swift @@ -148,8 +148,6 @@ public struct CookieNotificationBlockingConsentView: View { } } - - #if DEBUG struct CookieNotificationBlockingConsentView_Previews: PreviewProvider { static var previews: some View { diff --git a/Tests/ClientTests/NavigationRouterTests.swift b/Tests/ClientTests/NavigationRouterTests.swift index 3bd2315d979..193046acb00 100644 --- a/Tests/ClientTests/NavigationRouterTests.swift +++ b/Tests/ClientTests/NavigationRouterTests.swift @@ -9,16 +9,18 @@ import Preferences import XCTest class NavigationRouterTests: XCTestCase { + + private let privateBrowsingManager = PrivateBrowsingManager() override func setUp() { super.setUp() - PrivateBrowsingManager.shared.isPrivateBrowsing = false + privateBrowsingManager.isPrivateBrowsing = false Preferences.Privacy.privateBrowsingOnly.reset() } override func tearDown() { super.tearDown() - PrivateBrowsingManager.shared.isPrivateBrowsing = false + privateBrowsingManager.isPrivateBrowsing = false Preferences.Privacy.privateBrowsingOnly.reset() } @@ -38,7 +40,7 @@ class NavigationRouterTests: XCTestCase { return false } - guard let navItem = NavigationPath(url: appURL) else { + guard let navItem = NavigationPath(url: appURL, isPrivateBrowsing: self.privateBrowsingManager.isPrivateBrowsing) else { XCTFail("Invalid Navigation Path") return false } @@ -55,17 +57,17 @@ class NavigationRouterTests: XCTestCase { // Test URL with double escaped encoded characters (URL was encoded twice) XCTAssertTrue(testURLEncoding("http://google.com%3Fa%3D1%26b%3D2%26c%3Dfoo%2520bar")) - let emptyNav = NavigationPath(url: URL(string: "\(appScheme)://open-url?private=true")!) + let emptyNav = NavigationPath(url: URL(string: "\(appScheme)://open-url?private=true")!, isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing) XCTAssertEqual(emptyNav, NavigationPath.url(webURL: nil, isPrivate: true)) - let badNav = NavigationPath(url: URL(string: "\(appScheme)://open-url?url=blah")!) + let badNav = NavigationPath(url: URL(string: "\(appScheme)://open-url?url=blah")!, isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing) XCTAssertEqual(badNav, NavigationPath.url(webURL: URL(string: "blah"), isPrivate: false)) } func testSearchScheme() { let query = "Foo Bar".addingPercentEncoding(withAllowedCharacters: .alphanumerics)! let appURL = "\(appScheme)://search?q=" + query - let navItem = NavigationPath(url: URL(string: appURL)!)! + let navItem = NavigationPath(url: URL(string: appURL)!, isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing)! XCTAssertEqual(navItem, NavigationPath.text("Foo Bar")) } @@ -73,7 +75,7 @@ class NavigationRouterTests: XCTestCase { func testDefaultNavigationPath() { let url = URL(string: "https://duckduckgo.com")! let appURL = URL(string: "\(self.appScheme)://open-url?url=\(url.absoluteString.escape()!)")! - let path = NavigationPath(url: appURL)! + let path = NavigationPath(url: appURL, isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing)! XCTAssertEqual(path, NavigationPath.url(webURL: url, isPrivate: false)) } @@ -83,29 +85,29 @@ class NavigationRouterTests: XCTestCase { let url = URL(string: "https://duckduckgo.com")! let appURL = URL(string: "\(self.appScheme)://open-url?url=\(url.absoluteString.escape()!)")! - let path = NavigationPath(url: appURL)! + let path = NavigationPath(url: appURL, isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing)! // Should inheritely be private by default XCTAssertEqual(path, NavigationPath.url(webURL: url, isPrivate: true)) } func testNavigationPathAlreadyInPrivateBrowsingMode() { - PrivateBrowsingManager.shared.isPrivateBrowsing = true + privateBrowsingManager.isPrivateBrowsing = true let url = URL(string: "https://duckduckgo.com")! let appURL = URL(string: "\(self.appScheme)://open-url?url=\(url.absoluteString.escape()!)")! - let path = NavigationPath(url: appURL)! + let path = NavigationPath(url: appURL, isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing)! // Should inheritely be private by default XCTAssertEqual(path, NavigationPath.url(webURL: url, isPrivate: true)) } func testNavigationPathForcedRegularMode() { - PrivateBrowsingManager.shared.isPrivateBrowsing = true + privateBrowsingManager.isPrivateBrowsing = true let url = URL(string: "https://duckduckgo.com")! let appURL = URL(string: "\(self.appScheme)://open-url?url=\(url.absoluteString.escape()!)&private=false")! - let path = NavigationPath(url: appURL)! + let path = NavigationPath(url: appURL, isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing)! // Should be regular due to specified argument in the URL XCTAssertEqual(path, NavigationPath.url(webURL: url, isPrivate: false)) diff --git a/Tests/ClientTests/SearchEnginesTests.swift b/Tests/ClientTests/SearchEnginesTests.swift index be470b2502c..6a75ea6159e 100644 --- a/Tests/ClientTests/SearchEnginesTests.swift +++ b/Tests/ClientTests/SearchEnginesTests.swift @@ -26,8 +26,6 @@ class SearchEnginesTests: XCTestCase { Preferences.Search.defaultEngineName.reset() Preferences.Search.defaultPrivateEngineName.reset() Preferences.Search.yahooEngineMigrationCompleted.reset() - - PrivateBrowsingManager.shared.isPrivateBrowsing = false } func testIncludesExpectedEngines() { diff --git a/Tests/ClientTests/TabManagerTests.swift b/Tests/ClientTests/TabManagerTests.swift index 7f8493b5905..7b2c34f4727 100644 --- a/Tests/ClientTests/TabManagerTests.swift +++ b/Tests/ClientTests/TabManagerTests.swift @@ -107,18 +107,19 @@ open class MockTabManagerDelegate: TabManagerDelegate { let didAdd = MethodSpy(functionName: "tabManager(_:didAddTab:)") var manager: TabManager! + private let privateBrowsingManager = PrivateBrowsingManager() override func setUp() { super.setUp() DataController.shared.initializeOnce() let profile = MockProfile() - manager = TabManager(prefs: profile.prefs, rewards: nil, tabGeneratorAPI: nil) - PrivateBrowsingManager.shared.isPrivateBrowsing = false + manager = TabManager(windowId: UUID(), prefs: profile.prefs, rewards: nil, tabGeneratorAPI: nil, privateBrowsingManager: privateBrowsingManager) + privateBrowsingManager.isPrivateBrowsing = false } override func tearDown() { - PrivateBrowsingManager.shared.isPrivateBrowsing = false + privateBrowsingManager.isPrivateBrowsing = false super.tearDown() } diff --git a/Tests/ClientTests/TabSessionTests.swift b/Tests/ClientTests/TabSessionTests.swift index 3dbd52d6e92..b3b30b391ee 100644 --- a/Tests/ClientTests/TabSessionTests.swift +++ b/Tests/ClientTests/TabSessionTests.swift @@ -77,6 +77,7 @@ private class WebViewNavigationAdapter: NSObject, WKNavigationDelegate { @MainActor class TabSessionTests: XCTestCase { private var tabManager: TabManager! private let maxTimeout = 60.0 + private let privateBrowsingManager = PrivateBrowsingManager() override class func setUp() { super.setUp() @@ -89,7 +90,7 @@ private class WebViewNavigationAdapter: NSObject, WKNavigationDelegate { DataController.shared.initializeOnce() tabManager = { () -> TabManager in let profile = BrowserProfile(localName: "profile") - return TabManager(prefs: profile.prefs, rewards: nil, tabGeneratorAPI: nil) + return TabManager(windowId: UUID(), prefs: profile.prefs, rewards: nil, tabGeneratorAPI: nil, privateBrowsingManager: privateBrowsingManager) }() } diff --git a/Tests/ClientTests/TestFavicons.swift b/Tests/ClientTests/TestFavicons.swift index d70be03b8ec..fee90987000 100644 --- a/Tests/ClientTests/TestFavicons.swift +++ b/Tests/ClientTests/TestFavicons.swift @@ -28,7 +28,7 @@ import Favicon func testImageViewLoad() { let expectation = XCTestExpectation(description: "favicon.load") let imageView = UIImageView() - imageView.loadFavicon(for: URL(string: "http://www.google.de")!, monogramFallbackCharacter: nil) { _ in + imageView.loadFavicon(for: URL(string: "http://www.google.de")!, isPrivateBrowsing: false, monogramFallbackCharacter: nil) { _ in // Should be a default icon therefore not truly async XCTAssertNotNil(imageView.image) expectation.fulfill() diff --git a/package-lock.json b/package-lock.json index a50ff466451..caf22c88817 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@mozilla/readability": "^0.4.2", "brave-core-ios": "https://github.com/brave/brave-browser/releases/download/v1.57.13/brave-core-ios-1.57.13.tgz", - "leo-sf-symbols": "github:brave/leo-sf-symbols#60a41d77d4e58bd48284848a04ad3f9b79bf7daa", + "leo-sf-symbols": "github:brave/leo-sf-symbols#9d272b2fdccdfced7785ee3b527b33df5a33a989", "page-metadata-parser": "^1.1.3", "webpack-cli": "^4.8.0" }, @@ -783,9 +783,9 @@ } }, "node_modules/leo-sf-symbols": { - "version": "1.0.7", - "resolved": "git+ssh://git@github.com/brave/leo-sf-symbols.git#60a41d77d4e58bd48284848a04ad3f9b79bf7daa", - "integrity": "sha512-234URdy8AbdEYRQC+YybQ6JGQiKMztPgBTt6lILS64udhr/3nj0d6pT3xa4mqRhX6TYmSyFRTWxKUjZ2ZmI2SQ==", + "version": "1.0.9", + "resolved": "git+ssh://git@github.com/brave/leo-sf-symbols.git#9d272b2fdccdfced7785ee3b527b33df5a33a989", + "integrity": "sha512-pH1vv7MeTBfORv7rIX8lVBC+7Cs3xk8IRcVVcfZWGsDcza9l2AegWr56xYSGk2HyiTwBzsdMIBkEyOf64QXrlw==", "license": "MPL-2.0" }, "node_modules/loader-runner": { @@ -2055,9 +2055,9 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, "leo-sf-symbols": { - "version": "git+ssh://git@github.com/brave/leo-sf-symbols.git#60a41d77d4e58bd48284848a04ad3f9b79bf7daa", - "integrity": "sha512-234URdy8AbdEYRQC+YybQ6JGQiKMztPgBTt6lILS64udhr/3nj0d6pT3xa4mqRhX6TYmSyFRTWxKUjZ2ZmI2SQ==", - "from": "leo-sf-symbols@github:brave/leo-sf-symbols#60a41d77d4e58bd48284848a04ad3f9b79bf7daa" + "version": "git+ssh://git@github.com/brave/leo-sf-symbols.git#9d272b2fdccdfced7785ee3b527b33df5a33a989", + "integrity": "sha512-pH1vv7MeTBfORv7rIX8lVBC+7Cs3xk8IRcVVcfZWGsDcza9l2AegWr56xYSGk2HyiTwBzsdMIBkEyOf64QXrlw==", + "from": "leo-sf-symbols@github:brave/leo-sf-symbols#9d272b2fdccdfced7785ee3b527b33df5a33a989" }, "loader-runner": { "version": "4.2.0", diff --git a/package.json b/package.json index 4725d50cb23..8cbadc54525 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "dependencies": { "@mozilla/readability": "^0.4.2", "brave-core-ios": "https://github.com/brave/brave-browser/releases/download/v1.57.13/brave-core-ios-1.57.13.tgz", - "leo-sf-symbols": "github:brave/leo-sf-symbols#60a41d77d4e58bd48284848a04ad3f9b79bf7daa", + "leo-sf-symbols": "github:brave/leo-sf-symbols#9d272b2fdccdfced7785ee3b527b33df5a33a989", "page-metadata-parser": "^1.1.3", "webpack-cli": "^4.8.0" },