From 05910d6bbca954e2150d21e4ce677fd62c4d5b16 Mon Sep 17 00:00:00 2001 From: Brandon T Date: Thu, 6 Jul 2023 12:42:30 -0400 Subject: [PATCH 01/13] Move private browsing manager to non-singleton. Fix BraveRewards crash when allocating multiple rewards. Add persistent private browsing. Wallet fix for when onboarding is visible in multiple windows. Fix windowIds being the same on tapping the app icon. Set window selected Fix window activation predicate so that tapping the Application Icon does not create new windows. Added documentation to the SceneDelegate when restoring. --- App/iOS/Delegates/AppDelegate.swift | 240 ++++-------------- App/iOS/Delegates/SceneDelegate.swift | 223 ++++++++++------ App/iOS/Supporting Files/Info.plist | 3 +- .../Panel/BraveRewardsViewController.swift | 2 +- .../BackForwardListViewController.swift | 1 + .../Browser/BackForwardTableViewCell.swift | 4 +- .../Browser/BrowserViewController.swift | 141 ++++++---- .../BrowserViewController/BVC+Rewards.swift | 6 +- .../BrowserViewController+Callout.swift | 2 +- .../BrowserViewController+KeyCommands.swift | 4 +- .../BrowserViewController+Menu.swift | 13 +- .../BrowserViewController+Onboarding.swift | 4 +- ...serViewController+TabManagerDelegate.swift | 24 +- ...rowserViewController+ToolbarDelegate.swift | 13 +- ...rViewController+WKNavigationDelegate.swift | 8 +- .../BrowserViewController+Wallet.swift | 8 +- ...rowserViewController+Web3NameService.swift | 4 +- .../Browser/Favicons/LargeFaviconView.swift | 4 +- .../Favicons/UIImageView+Favicon.swift | 4 +- .../Favorites/FavoritesViewController.swift | 6 +- .../Frontend/Browser/FrequencyQuery.swift | 2 +- .../Helpers/BrowserNavigationHelper.swift | 4 +- .../Browser/Helpers/FaviconImage.swift | 11 +- .../Browser/HomePanel/NTPDataSource.swift | 4 +- .../Browser/LinkPreviewViewController.swift | 2 +- .../Frontend/Browser/NavigationRouter.swift | 12 +- .../NewTabPageBackgroundButtonsView.swift | 9 +- .../NewTabPageViewController.swift | 22 +- .../Sections/BraveNewsSectionProvider.swift | 4 +- .../Sections/FavoritesSectionProvider.swift | 10 +- .../Sections/StatsSectionProvider.swift | 99 +++++--- .../Browser/PageZoom/PageZoomView.swift | 16 +- .../PlaylistDetailViewController.swift | 17 +- ...ListViewController+TableViewDelegate.swift | 4 +- .../PlaylistListViewController.swift | 11 +- .../Controllers/PlaylistNewFolderView.swift | 2 +- .../Controllers/PlaylistViewController.swift | 21 +- .../PlaylistCacheLoader.swift | 3 +- .../PlaylistCarplayManager.swift | 29 +-- .../Managers & Cache/PlaylistManager.swift | 2 - .../Utilities/PlaylistThumbnailUtility.swift | 2 +- .../Playlist/VideoPlayer/UI/VideoPlayer.swift | 4 +- .../VideoPlayer/UI/VideoPlayerInfoBar.swift | 4 +- .../PrivacyHub/PrivacyHubAllTimeSection.swift | 5 +- .../PrivacyReportAllTimeListsView.swift | 7 +- .../PrivacyHub/PrivacyReportsManager.swift | 12 +- .../PrivacyHub/PrivacyReportsView.swift | 7 +- .../PrivateBrowsingManager.swift | 4 +- .../Browser/Search/SearchEngines.swift | 14 +- .../Search/SearchSuggestionDataSource.swift | 6 +- .../Browser/Search/SearchViewController.swift | 12 +- Sources/Brave/Frontend/Browser/Tab.swift | 2 +- .../Brave/Frontend/Browser/TabManager.swift | 71 ++++-- .../Browser/TabManagerNavDelegate.swift | 2 +- .../RecentlyClosedTabsView.swift | 2 +- .../Browser/Tabs/TabBar/TabBarCell.swift | 31 ++- .../Tabs/TabBar/TabsBarViewController.swift | 20 +- .../TabTrayController+KeyCommands.swift | 2 +- .../TabTrayController+TableViewDelegate.swift | 2 +- .../Tabs/TabTray/TabTrayController.swift | 27 +- .../Tabs/TabTray/Views/TabTrayCell.swift | 2 +- .../BottomToolbar/BottomToolbarView.swift | 10 +- .../AddEditBookmarkTableViewController.swift | 14 +- .../Menu/Bookmarks/BookmarkDetailsView.swift | 4 +- .../Bookmarks/BookmarksViewController.swift | 6 +- .../Menu/HistoryViewController.swift | 6 +- .../Toolbars/HeaderContainerView.swift | 8 +- .../Toolbars/UrlBar/TabLocationView.swift | 4 +- .../Toolbars/UrlBar/TopToolbarView.swift | 4 +- .../Brave/Frontend/ClientPreferences.swift | 2 + .../Login/LoginListViewController.swift | 2 +- .../SearchCustomEngineViewController.swift | 10 +- .../SearchQuickEnginesViewController.swift | 6 +- .../SearchSettingsTableViewController.swift | 8 +- .../Settings/SettingsViewController.swift | 4 +- .../OtherPrivacySettingsSectionView.swift | 21 ++ .../ShieldsActivityItemSourceProvider.swift | 3 +- .../Shields/ShieldsViewController.swift | 8 +- .../Paged/BraveSearchScriptHandler.swift | 2 +- .../Paged/BraveTalkScriptHandler.swift | 2 +- .../Paged/ContentBlockerScriptHandler.swift | 4 +- .../RequestBlockingContentScriptHandler.swift | 4 +- .../Paged/RewardsReportingScriptHandler.swift | 2 +- Sources/Brave/Migration/Migration.swift | 2 +- .../Shortcuts/ActivityShortcutManager.swift | 7 +- Sources/Brave/States/AppState.swift | 232 +++++++++++++++++ Sources/Brave/States/BrowserState.swift | 32 +++ Sources/BraveStrings/BraveStrings.swift | 1 + .../Crypto/Stores/KeyringStore.swift | 23 ++ Sources/Data/models/SessionTab.swift | 11 +- Sources/Data/models/SessionWindow.swift | 58 ++++- 91 files changed, 1079 insertions(+), 657 deletions(-) create mode 100644 Sources/Brave/States/AppState.swift create mode 100644 Sources/Brave/States/BrowserState.swift diff --git a/App/iOS/Delegates/AppDelegate.swift b/App/iOS/Delegates/AppDelegate.swift index a1aebc47f2c..32c3f2ad7fe 100644 --- a/App/iOS/Delegates/AppDelegate.swift +++ b/App/iOS/Delegates/AppDelegate.swift @@ -32,56 +32,23 @@ import Preferences import BraveShields import PrivateCDN -extension AppDelegate { - // A model that is passed used in every scene - struct SceneInfoModel { - let profile: Profile - let diskImageStore: DiskImageStore? - let migration: Migration? - let rewards: Brave.BraveRewards - } -} - @main class AppDelegate: UIResponder, UIApplicationDelegate { private let log = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "app-delegate") var window: UIWindow? - lazy var braveCore: BraveCoreMain = { - var switches: [BraveCoreSwitch] = [] - if !AppConstants.buildChannel.isPublic { - // Check prefs for additional switches - let activeSwitches = Set(Preferences.BraveCore.activeSwitches.value) - let switchValues = Preferences.BraveCore.switchValues.value - for activeSwitch in activeSwitches { - let key = BraveCoreSwitchKey(rawValue: activeSwitch) - if key.isValueless { - switches.append(.init(key: key)) - } else if let value = switchValues[activeSwitch], !value.isEmpty { - switches.append(.init(key: key, value: value)) - } - } - } - switches.append(.init(key: .rewardsFlags, value: BraveRewards.Configuration.current().flags)) - return BraveCoreMain(userAgent: UserAgent.mobile, additionalSwitches: switches) - }() - - var migration: Migration? - private weak var application: UIApplication? - var launchOptions: [AnyHashable: Any]? - let appVersion = Bundle.main.infoDictionaryString(forKey: "CFBundleShortVersionString") - var receivedURLs: [URL]? - - /// Object used to handle server pings - private(set) lazy var dau = DAU(braveCoreStats: braveCore.braveStats) - - private var cancellables: Set = [] - private var sceneInfo: SceneInfoModel? - override init() { + private var cancellables: Set = [] + + @discardableResult + func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Hold references to willFinishLaunching parameters for delayed app launch + self.application = application + + // Application Constants must be initialized first #if MOZ_CHANNEL_RELEASE AppConstants.buildChannel = .release #elseif MOZ_CHANNEL_BETA @@ -93,62 +60,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { #elseif MOZ_CHANNEL_DEBUG AppConstants.buildChannel = .debug #endif - super.init() - } - - @discardableResult - func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Hold references to willFinishLaunching parameters for delayed app launch - self.application = application - self.launchOptions = launchOptions - - // Brave Core Initialization - BraveCoreMain.setLogHandler { severity, file, line, messageStartIndex, message in - let message = String(message.dropFirst(messageStartIndex).dropLast()) - .trimmingCharacters(in: .whitespacesAndNewlines) - if message.isEmpty { - // Nothing to print - return true - } - - if severity == .fatal { - let filename = URL(fileURLWithPath: file).lastPathComponent -#if DEBUG - // Prints a special runtime warning instead of crashing. - os_log( - .fault, - dso: os_rw.dso, - log: os_rw.log(category: "BraveCore"), - "[%@:%ld] > %@", filename, line, message - ) - return true -#else - fatalError("Fatal BraveCore Error at \(filename):\(line).\n\(message)") -#endif - } - - let level: OSLogType = { - switch severity { - case .fatal: return .fault - case .error: return .error - // No `.warning` level exists for OSLogType. os_Log.warning is an alias for `.error`. - case .warning: return .error - case .info: return .info - default: return .debug - } - }() - - let braveCoreLogger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "brave-core") - if AppConstants.buildChannel.isPublic { - braveCoreLogger.log(level: level, "\(message, privacy: .private)") - } else { - braveCoreLogger.log(level: level, "\(message, privacy: .public)") - } - - return true - } - - migration = Migration(braveCore: braveCore) + + AppState.shared.state = .launching(options: launchOptions ?? [:], active: false) // Passcode checking, must happen on immediate launch if !DataController.shared.storeExists() { @@ -157,14 +70,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // upon reinstall. KeychainWrapper.sharedAppContainerKeychain.setAuthenticationInfo(nil) } - - return startApplication(application, withLaunchOptions: launchOptions) - } - - @discardableResult - fileprivate func startApplication(_ application: UIApplication, withLaunchOptions launchOptions: [AnyHashable: Any]?) -> Bool { - log.info("startApplication begin") - + // Set the Safari UA for browsing. setUserAgent() @@ -185,41 +91,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { SDImageCodersManager.shared.addCoder(PrivateCDNImageCoder()) SDImageCodersManager.shared.addCoder(SDImageSVGNativeCoder.shared) - // Setup Profile - let profile = BrowserProfile(localName: "profile") - - // Setup DiskImageStore for Screenshots - let diskImageStore = { () -> DiskImageStore? in - do { - return try DiskImageStore( - files: profile.files, - namespace: "TabManagerScreenshots", - quality: UIConstants.screenshotQuality) - } catch { - log.error("Failed to create an image store for files: \(profile.files.rootPath) and namespace: \"TabManagerScreenshots\": \(error.localizedDescription)") - assertionFailure() - } - return nil - }() - - // Setup ads - let rewardsConfiguration = BraveRewards.Configuration.current() - Migration.migrateAdsConfirmations(for: rewardsConfiguration) - - // Setup Scene Info - sceneInfo = SceneInfoModel( - profile: profile, - diskImageStore: diskImageStore, - migration: migration, - rewards: BraveRewards(configuration: rewardsConfiguration)) - - // Perform migrations - let profilePrefix = profile.prefs.getBranchPrefix() - migration?.launchMigrations(keyPrefix: profilePrefix, profile: profile) - - // Setup Custom WKWebView Scheme Handlers - setupCustomSchemeHandlers(profile) - // Temporary fix for Bug 1390871 - NSInvalidArgumentException: -[WKContentView menuHelperFindInPage]: unrecognized selector if let clazz = NSClassFromString("WKCont" + "ent" + "View"), let swizzledMethod = class_getInstanceMethod(TabWebViewMenuHelper.self, #selector(TabWebViewMenuHelper.swizzledMenuHelperFindInPage)) { class_addMethod(clazz, MenuHelper.selectorFindInPage, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)) @@ -240,15 +111,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } SystemUtils.onFirstRun() - - // Schedule Brave Core Priority Tasks - braveCore.scheduleLowPriorityStartupTasks() - - log.info("startApplication end") return true } func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + AppState.shared.state = .launching(options: launchOptions ?? [:], active: true) + // IAPs can trigger on the app as soon as it launches, // for example when a previous transaction was not finished and is in pending state. SKPaymentQueue.default().add(BraveVPN.iapObserver) @@ -316,12 +184,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // There was a bug that when you skipped onboarding, default search engine preference // was not set. if Preferences.Search.defaultEngineName.value == nil { - sceneInfo?.profile.searchEngines.searchEngineSetup() + AppState.shared.profile.searchEngines.searchEngineSetup() } // Migration of Yahoo Search Engines if !Preferences.Search.yahooEngineMigrationCompleted.value { - sceneInfo?.profile.searchEngines.migrateDefaultYahooSearchEngines() + AppState.shared.profile.searchEngines.migrateDefaultYahooSearchEngines() } if isFirstLaunch { @@ -359,7 +227,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { if let weekOfInstall = Preferences.DAU.weekOfInstallation.value ?? Preferences.DAU.installationDate.value?.mondayOfCurrentWeekFormatted, AppConstants.buildChannel != .debug { - braveCore.initializeP3AService( + AppState.shared.braveCore.initializeP3AService( forChannel: AppConstants.buildChannel.serverChannelParam, weekOfInstall: weekOfInstall ) @@ -369,6 +237,25 @@ class AppDelegate: UIResponder, UIApplicationDelegate { await self.cleanUpLargeTemporaryDirectory() } + Task(priority: .high) { + // Start preparing the ad-block services right away + // So it's ready a lot faster + await LaunchHelper.shared.prepareAdBlockServices( + adBlockService: AppState.shared.braveCore.adblockService + ) + } + + // Setup Playlist + // This restores the playlist incomplete downloads. So if a download was started + // and interrupted on application death, we restart it on next launch. + Task(priority: .low) { @MainActor in + PlaylistManager.shared.setupPlaylistFolder() + PlaylistManager.shared.restoreSession() + } + + UserDefaults.standard.set(true, forKey: "UIStateRestorationDebugLogging") + UserDefaults.standard.set(true, forKey: "UIStateRestorationDeveloperMode") + return shouldPerformAdditionalDelegateHandling } @@ -383,13 +270,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { #endif func applicationWillTerminate(_ application: UIApplication) { - // We have only five seconds here, so let's hope this doesn't take too long. - sceneInfo?.profile.shutdown() - + AppState.shared.profile.shutdown() + SKPaymentQueue.default().remove(BraveVPN.iapObserver) // Clean up BraveCore - braveCore.syncAPI.removeAllObservers() + AppState.shared.braveCore.syncAPI.removeAllObservers() log.debug("Cleanly Terminated the Application") } @@ -402,45 +288,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } - func syncOnDidEnterBackground(application: UIApplication) { - // BRAVE TODO: Decide whether or not we want to use this for our own sync down the road - - var taskId = UIBackgroundTaskIdentifier(rawValue: 0) - taskId = application.beginBackgroundTask { - print("Running out of background time, but we have a profile shutdown pending.") - self.shutdownProfileWhenNotActive(application) - application.endBackgroundTask(taskId) - } - - sceneInfo?.profile.shutdown() - application.endBackgroundTask(taskId) - } - - fileprivate func shutdownProfileWhenNotActive(_ application: UIApplication) { - // Only shutdown the profile if we are not in the foreground - guard application.applicationState != .active else { - return - } - - sceneInfo?.profile.shutdown() - } - - func setupCustomSchemeHandlers(_ profile: Profile) { - let responders: [(String, InternalSchemeResponse)] = [ - (AboutHomeHandler.path, AboutHomeHandler()), - (AboutLicenseHandler.path, AboutLicenseHandler()), - (SessionRestoreHandler.path, SessionRestoreHandler()), - (ErrorPageHandler.path, ErrorPageHandler()), - (ReaderModeHandler.path, ReaderModeHandler(profile: profile)), - (IPFSSchemeHandler.path, IPFSSchemeHandler()), - (Web3DomainHandler.path, Web3DomainHandler()) - ] - - responders.forEach { (path, responder) in - InternalSchemeHandler.responders[path] = responder - } - } - fileprivate func setUserAgent() { let userAgent = UserAgent.userAgentForDesktopMode @@ -455,10 +302,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Record the user agent for use by search suggestion clients. SearchViewController.userAgent = userAgent } - - func sceneInfo(for sceneSession: UISceneSession) -> SceneInfoModel? { - return sceneInfo - } /// Dumps the temporary directory if the total size of the directory exceeds a size threshold (in bytes) private nonisolated func cleanUpLargeTemporaryDirectory(thresholdInBytes: Int = 100_000_000) async { @@ -494,7 +337,6 @@ extension AppDelegate: MFMailComposeViewControllerDelegate { func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { // Dismiss the view controller and start the app up controller.dismiss(animated: true, completion: nil) - startApplication(application!, withLaunchOptions: self.launchOptions) } } @@ -517,5 +359,11 @@ extension AppDelegate { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + + sceneSessions.forEach { session in + if let windowIdString = session.scene?.userActivity?.userInfo?["WindowID"] as? String, let windowId = UUID(uuidString: windowIdString) { + SessionWindow.delete(windowId: windowId) + } + } } } diff --git a/App/iOS/Delegates/SceneDelegate.swift b/App/iOS/Delegates/SceneDelegate.swift index 4e0b1a92cdf..e6e1e48dfee 100644 --- a/App/iOS/Delegates/SceneDelegate.swift +++ b/App/iOS/Delegates/SceneDelegate.swift @@ -25,72 +25,58 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Chromium force unwraps it and uses it. For this reason, we always set this window property to the scene's main window. internal var window: UIWindow? private var windowProtection: WindowProtection? - private var sceneInfo: AppDelegate.SceneInfoModel? static var shouldHandleUrpLookup = false private var cancellables: Set = [] - private let log = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "scene-delegate") func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } - - // Create a browser instance - // There has to be an application delegate - guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { - fatalError("Failed to create browser instance") - } - - guard let sceneInfo = appDelegate.sceneInfo(for: session) else { - return - } - self.sceneInfo = sceneInfo - - // We have to wait until pre1.12 migration is done until we proceed with database - // initialization. This is because Database container may change. See bugs #3416, #3377. - DataController.shared.initializeOnce() - Migration.postCoreDataInitMigrations() - Migration.migrateTabStateToWebkitState(diskImageStore: sceneInfo.diskImageStore) + let browserViewController = createBrowserWindow( + scene: windowScene, + braveCore: AppState.shared.braveCore, + profile: AppState.shared.profile, + diskImageStore: AppState.shared.diskImageStore, + migration: AppState.shared.migration, + rewards: AppState.shared.rewards, + userActivity: connectionOptions.userActivities.first ?? session.stateRestorationActivity + ) - Task(priority: .high) { - // Start preparing the ad-block services right away - // So it's ready a lot faster - await LaunchHelper.shared.prepareAdBlockServices( - adBlockService: appDelegate.braveCore.adblockService - ) + let conditions = scene.activationConditions + conditions.canActivateForTargetContentIdentifierPredicate = NSPredicate(value: true) + if let windowId = session.userInfo?["WindowID"] as? UUID { + let preferPredicate = NSPredicate(format: "self == %@", windowId.uuidString) + conditions.prefersToActivateForTargetContentIdentifierPredicate = preferPredicate } - + Preferences.General.themeNormalMode.objectWillChange .receive(on: RunLoop.main) - .sink { [weak self] _ in - self?.updateTheme() + .sink { [weak self, weak scene] _ in + guard let self = self, + let scene = scene as? UIWindowScene else { return } + self.updateTheme(for: scene) } .store(in: &cancellables) Preferences.General.nightModeEnabled.objectWillChange .receive(on: RunLoop.main) - .sink { [weak self] _ in - self?.updateTheme() + .sink { [weak self, weak scene] _ in + guard let self = self, + let scene = scene as? UIWindowScene else { return } + self.updateTheme(for: scene) } .store(in: &cancellables) - PrivateBrowsingManager.shared.$isPrivateBrowsing + browserViewController.privateBrowsingManager.$isPrivateBrowsing .removeDuplicates() .receive(on: RunLoop.main) - .sink { [weak self] _ in - self?.updateTheme() + .sink { [weak self, weak scene] _ in + guard let self = self, + let scene = scene as? UIWindowScene else { return } + self.updateTheme(for: scene) } .store(in: &cancellables) - - let browserViewController = createBrowserWindow( - scene: windowScene, - braveCore: appDelegate.braveCore, - profile: sceneInfo.profile, - diskImageStore: sceneInfo.diskImageStore, - migration: sceneInfo.migration, - rewards: sceneInfo.rewards - ) if SceneDelegate.shouldHandleUrpLookup { // TODO: Find a better way to do this when multiple windows are involved. @@ -101,12 +87,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } } - // Setup Playlist - // This restores the playlist incomplete downloads. So if a download was started - // and interrupted on application death, we restart it on next launch. - PlaylistManager.shared.setupPlaylistFolder() - PlaylistManager.shared.restoreSession() - // Setup Playlist Car-Play // TODO: Decide what to do if we have multiple windows // as it is only possible to have a single car-play instance. @@ -124,7 +104,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { PrivacyReportsManager.scheduleNotification(debugMode: !AppConstants.buildChannel.isPublic) PrivacyReportsManager.consolidateData() - PrivacyReportsManager.scheduleProcessingBlockedRequests() + PrivacyReportsManager.scheduleProcessingBlockedRequests(isPrivateBrowsing: browserViewController.privateBrowsingManager.isPrivateBrowsing) PrivacyReportsManager.scheduleVPNAlertsTask() } @@ -138,7 +118,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Assign each browser a window of its own let window = UIWindow(windowScene: windowScene).then { $0.backgroundColor = .black - $0.overrideUserInterfaceStyle = expectedThemeOverride + $0.overrideUserInterfaceStyle = expectedThemeOverride(for: windowScene) $0.tintColor = .braveBlurpleTint $0.rootViewController = navigationController @@ -175,21 +155,26 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func sceneDidDisconnect(_ scene: UIScene) { - + print("SCENE DISCONNECTED") } func sceneDidBecomeActive(_ scene: UIScene) { + scene.userActivity?.becomeCurrent() + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, - let scene = scene as? UIWindowScene, - let profile = sceneInfo?.profile + let scene = scene as? UIWindowScene else { return } - + + if let windowId = (scene.userActivity?.userInfo?["WindowID"] ?? + scene.session.userInfo?["WindowID"]) as? String, + let windowUUID = UUID(uuidString: windowId) { + SessionWindow.setSelected(windowId: windowUUID) + } + Preferences.AppState.backgroundedCleanly.value = false - - profile.reopen() - appDelegate.setupCustomSchemeHandlers(profile) + AppState.shared.profile.reopen() appDelegate.receivedURLs = nil UIApplication.shared.applicationIconBadgeNumber = 0 @@ -211,14 +196,15 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // (offline, bad connection etc.). // Also send the ping only after the URP lookup has processed. if Preferences.URP.referralLookupOutstanding.value == false { - appDelegate.dau.sendPingToServer() + AppState.shared.dau.sendPingToServer() } - BraveSkusManager.refreshSKUCredential(isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing) + BraveSkusManager.refreshSKUCredential(isPrivate: scene.browserViewController?.privateBrowsingManager.isPrivateBrowsing == true) } func sceneWillResignActive(_ scene: UIScene) { Preferences.AppState.backgroundedCleanly.value = true + scene.userActivity?.resignCurrent() } func sceneWillEnterForeground(_ scene: UIScene) { @@ -228,11 +214,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func sceneDidEnterBackground(_ scene: UIScene) { - guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { - return - } - - appDelegate.syncOnDidEnterBackground(application: UIApplication.shared) + AppState.shared.profile.shutdown() BraveVPN.sendVPNWorksInBackgroundNotification() } @@ -243,7 +225,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } URLContexts.forEach({ - guard let routerpath = NavigationPath(url: $0.url) else { + guard let routerpath = NavigationPath(url: $0.url, isPrivateBrowsing: scene.browserViewController?.privateBrowsingManager.isPrivateBrowsing == true) else { log.debug("Invalid Navigation Path: \($0.url)") return } @@ -251,9 +233,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { scene.browserViewController?.handleNavigationPath(path: routerpath) }) } + + func scene(_ scene: UIScene, didUpdate userActivity: NSUserActivity) { + log.debug("Updated User Activity for Scene") + } func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { - guard let scene = scene as? UIWindowScene else { return } @@ -361,7 +346,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { - if let browserViewController = windowScene.browserViewController { QuickActions.sharedInstance.handleShortCutItem(shortcutItem, withBrowserViewController: browserViewController) completionHandler(true) @@ -371,54 +355,127 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? { - return nil + return scene.userActivity } } extension SceneDelegate { - private var expectedThemeOverride: UIUserInterfaceStyle { + private func expectedThemeOverride(for scene: UIWindowScene?) -> UIUserInterfaceStyle { // The expected appearance theme should be dark mode when night mode is enabled for websites let themeValue = Preferences.General.nightModeEnabled.value ? DefaultTheme.dark.rawValue : Preferences.General.themeNormalMode.value let themeOverride = DefaultTheme(rawValue: themeValue)?.userInterfaceStyleOverride ?? .unspecified - let isPrivateBrowsing = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateBrowsing = scene?.browserViewController?.privateBrowsingManager.isPrivateBrowsing == true return isPrivateBrowsing ? .dark : themeOverride } - private func updateTheme() { - guard let window = UIApplication.shared.windows.first(where: { (window) -> Bool in window.isKeyWindow }) else { return } - UIView.transition( - with: window, duration: 0.15, options: [.transitionCrossDissolve], - animations: { - window.overrideUserInterfaceStyle = self.expectedThemeOverride - }, completion: nil) + private func updateTheme(for scene: UIWindowScene) { + scene.windows.forEach { window in + UIView.transition( + with: window, duration: 0.15, options: [.transitionCrossDissolve], + animations: { + window.overrideUserInterfaceStyle = self.expectedThemeOverride(for: scene) + }, completion: nil) + } } } extension SceneDelegate { - private func createBrowserWindow(scene: UIWindowScene, braveCore: BraveCoreMain, profile: Profile, diskImageStore: DiskImageStore?, migration: Migration?, rewards: Brave.BraveRewards) -> BrowserViewController { - // Make sure current private browsing flag respects the private browsing only user preference - PrivateBrowsingManager.shared.isPrivateBrowsing = Preferences.Privacy.privateBrowsingOnly.value + private func createBrowserWindow(scene: UIWindowScene, braveCore: BraveCoreMain, profile: Profile, diskImageStore: DiskImageStore?, migration: Migration?, rewards: Brave.BraveRewards, userActivity: NSUserActivity?) -> BrowserViewController { + + let privateBrowsingManager = PrivateBrowsingManager() // Don't track crashes if we're building the development environment due to the fact that terminating/stopping // the simulator via Xcode will count as a "crash" and lead to restore popups in the subsequent launch let crashedLastSession = !Preferences.AppState.backgroundedCleanly.value && AppConstants.buildChannel != .debug + + // Store the scene's activities + let windowId: UUID + let isPrivate: Bool + var userActivity = userActivity + + if let userActivity = userActivity { + // Restore the scene with the WindowID from the User-Activity + + let windowIdString = userActivity.userInfo?["WindowID"] as? String ?? "" + windowId = UUID(uuidString: windowIdString) ?? UUID() + isPrivate = userActivity.userInfo?["isPrivate"] as? Bool == true + privateBrowsingManager.isPrivateBrowsing = isPrivate + + // Create a new session window + SessionWindow.createWindow(isPrivate: isPrivate, isSelected: true, uuid: windowId) + + scene.userActivity = userActivity + scene.session.userInfo?["WindowID"] = windowId + scene.session.userInfo?["isPrivate"] = isPrivate + } else if let sceneWindowId = scene.session.userInfo?["WindowID"] as? String, + let sceneIsPrivate = scene.session.userInfo?["isPrivate"] as? Bool, + let windowUUID = UUID(uuidString: sceneWindowId) { + + // Restore the scene from the Session's User-Info WindowID + + windowId = windowUUID + isPrivate = sceneIsPrivate + privateBrowsingManager.isPrivateBrowsing = sceneIsPrivate + + scene.userActivity = BrowserState.userActivity(for: windowId, isPrivate: isPrivate) + } else { + // Should NOT be possible to get here. + // However, if a controller is NOT active, and tapping the app-icon opens a New-Window + // Then we need to make sure not to restore that "New" Window + // So we iterate all the windows and if there is no active window, then we need to "Restore" one. + // If a window is already active, we need to create a new blank window. + + if let activeWindowId = SessionWindow.getActiveWindow(context: DataController.swiftUIContext)?.windowId { + let activeSession = UIApplication.shared.openSessions + .compactMap({ $0.userInfo?["WindowID"] as? String }) + .first(where: { $0 == activeWindowId.uuidString }) + + if activeSession != nil { + // An existing window is already active on screen + // So create a new window + let newWindowId = UUID() + SessionWindow.createWindow(isPrivate: false, isSelected: true, uuid: newWindowId) + windowId = newWindowId + } else { + // Restore the active window since none is active on screen + windowId = activeWindowId + } + } else { + // Should be impossible to get here. There must always be an active window. + // However, if for some reason there is none, then we should create one. + let newWindowId = UUID() + SessionWindow.createWindow(isPrivate: false, isSelected: true, uuid: newWindowId) + windowId = newWindowId + } + + isPrivate = false + privateBrowsingManager.isPrivateBrowsing = false + + scene.userActivity = BrowserState.userActivity(for: windowId, isPrivate: false) + scene.session.userInfo = ["WindowID": windowId.uuidString, + "isPrivate": false] + } // Create a browser instance let browserViewController = BrowserViewController( + windowId: windowId, profile: profile, diskImageStore: diskImageStore, braveCore: braveCore, rewards: rewards, migration: migration, - crashedLastSession: crashedLastSession) + crashedLastSession: crashedLastSession, + rewards: rewards, + privateBrowsingManager: privateBrowsingManager + ) browserViewController.do { $0.edgesForExtendedLayout = [] // Add restoration class, the factory that will return the ViewController we will restore with. - $0.restorationIdentifier = NSStringFromClass(BrowserViewController.self) + $0.restorationIdentifier = BrowserState.sceneId $0.restorationClass = SceneDelegate.self // Remove Ad-Grant Reminders @@ -442,7 +499,7 @@ extension BrowserViewController { urp.referralLookup() { referralCode, offerUrl in // Attempting to send ping after first urp lookup. // This way we can grab the referral code if it exists, see issue #2586. - (UIApplication.shared.delegate as? AppDelegate)?.dau.sendPingToServer() + AppState.shared.dau.sendPingToServer() if let code = referralCode { let retryTime = AppConstants.buildChannel.isPublic ? 1.days : 10.minutes let retryDeadline = Date() + retryTime diff --git a/App/iOS/Supporting Files/Info.plist b/App/iOS/Supporting Files/Info.plist index f6c92dfeb7b..cde218b12b9 100644 --- a/App/iOS/Supporting Files/Info.plist +++ b/App/iOS/Supporting Files/Info.plist @@ -89,13 +89,14 @@ ShortcutsConfigurationIntent StatsConfigurationIntent com.brave.ios.browsing + com.brave.ios.browser-scene SERVICES_KEY $(BRAVE_SERVICES_KEY) UIApplicationSceneManifest UIApplicationSupportsMultipleScenes - + UISceneConfigurations CPTemplateApplicationSceneSessionRoleApplication diff --git a/Sources/Brave/Frontend/Brave Rewards/Panel/BraveRewardsViewController.swift b/Sources/Brave/Frontend/Brave Rewards/Panel/BraveRewardsViewController.swift index 9ab7127205a..8935f3c5ccb 100644 --- a/Sources/Brave/Frontend/Brave Rewards/Panel/BraveRewardsViewController.swift +++ b/Sources/Brave/Frontend/Brave Rewards/Panel/BraveRewardsViewController.swift @@ -34,7 +34,7 @@ class BraveRewardsViewController: UIViewController, PopoverContentComponent { } else { if let url = tab.url { rewardsView.publisherView.faviconImageView.contentMode = .scaleAspectFit - rewardsView.publisherView.faviconImageView.loadFavicon(for: url) + rewardsView.publisherView.faviconImageView.loadFavicon(for: url, isPrivateBrowsing: tab.isPrivate) } else { rewardsView.publisherView.faviconImageView.isHidden = true } diff --git a/Sources/Brave/Frontend/Browser/BackForwardListViewController.swift b/Sources/Brave/Frontend/Browser/BackForwardListViewController.swift index fce79c67441..554f6ccad6a 100644 --- a/Sources/Brave/Frontend/Browser/BackForwardListViewController.swift +++ b/Sources/Brave/Frontend/Browser/BackForwardListViewController.swift @@ -216,6 +216,7 @@ class BackForwardListViewController: UIViewController, UITableViewDataSource, UI return extracted.absoluteString }() + cell.isPrivateBrowsing = tabManager.privateBrowsingManager.isPrivateBrowsing cell.isCurrentTab = listData[indexPath.item] == self.currentItem cell.connectingBackwards = indexPath.item != listData.count - 1 cell.connectingForwards = indexPath.item != 0 diff --git a/Sources/Brave/Frontend/Browser/BackForwardTableViewCell.swift b/Sources/Brave/Frontend/Browser/BackForwardTableViewCell.swift index cf24846125c..99ffb39fd52 100644 --- a/Sources/Brave/Frontend/Browser/BackForwardTableViewCell.swift +++ b/Sources/Brave/Frontend/Browser/BackForwardTableViewCell.swift @@ -43,6 +43,8 @@ class BackForwardTableViewCell: UITableViewCell { label.textColor = BackForwardViewCellUX.textColor return label }() + + var isPrivateBrowsing: Bool = false var connectingForwards = true { didSet { @@ -70,7 +72,7 @@ class BackForwardTableViewCell: UITableViewCell { faviconView.backgroundColor = .white faviconView.image = Favicon.defaultImage } else { - faviconView.loadFavicon(for: s.tileURL) + faviconView.loadFavicon(for: s.tileURL, isPrivateBrowsing: isPrivateBrowsing) } var title = s.title if title.isEmpty { diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController.swift b/Sources/Brave/Frontend/Browser/BrowserViewController.swift index 1f875757984..9279f6eaecd 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController.swift @@ -52,7 +52,7 @@ public class BrowserViewController: UIViewController { private(set) lazy var topToolbar: TopToolbarView = { // Setup the URL bar, wrapped in a view to get transparency effect - let topToolbar = TopToolbarView(voiceSearchSupported: speechRecognizer.isVoiceSearchAvailable) + let topToolbar = TopToolbarView(voiceSearchSupported: speechRecognizer.isVoiceSearchAvailable, privateBrowsingManager: privateBrowsingManager) topToolbar.translatesAutoresizingMaskIntoConstraints = false topToolbar.delegate = self topToolbar.tabToolbarDelegate = self @@ -69,7 +69,7 @@ public class BrowserViewController: UIViewController { }() // These views wrap the top and bottom toolbars to provide background effects on them - let header = HeaderContainerView() + private(set) lazy var header = HeaderContainerView(privateBrowsingManager: privateBrowsingManager) private let headerHeightLayoutGuide = UILayoutGuide() let footer: UIView = { @@ -163,11 +163,13 @@ public class BrowserViewController: UIViewController { var displayedPopoverController: UIViewController? var updateDisplayedPopoverProperties: (() -> Void)? + public let windowId: UUID let profile: Profile let braveCore: BraveCoreMain let tabManager: TabManager let migration: Migration? let bookmarkManager: BookmarkManager + public let privateBrowsingManager: PrivateBrowsingManager /// Whether last session was a crash or not private let crashedLastSession: Bool @@ -272,27 +274,36 @@ public class BrowserViewController: UIViewController { var topToolbarDidPressReloadTask: Task<(), Never>? public init( + windowId: UUID, profile: Profile, diskImageStore: DiskImageStore?, braveCore: BraveCoreMain, rewards: BraveRewards, migration: Migration?, - crashedLastSession: Bool + crashedLastSession: Bool, + rewards: BraveRewards, + privateBrowsingManager: PrivateBrowsingManager ) { + self.windowId = windowId self.profile = profile self.braveCore = braveCore self.bookmarkManager = BookmarkManager(bookmarksAPI: braveCore.bookmarksAPI) self.rewards = rewards self.migration = migration self.crashedLastSession = crashedLastSession + self.rewards = rewards + self.privateBrowsingManager = privateBrowsingManager feedDataSource.historyAPI = braveCore.historyAPI backgroundDataSource = .init(service: braveCore.backgroundImagesService) // Initialize TabManager self.tabManager = TabManager( + windowId: windowId, prefs: profile.prefs, rewards: rewards, - tabGeneratorAPI: braveCore.tabGeneratorAPI) + tabGeneratorAPI: braveCore.tabGeneratorAPI, + privateBrowsingManager: privateBrowsingManager + ) // Add Regular tabs to Sync Chain if Preferences.Chromium.syncOpenTabsEnabled.value { @@ -443,7 +454,7 @@ public class BrowserViewController: UIViewController { pageZoomListener = NotificationCenter.default.addObserver(forName: PageZoomView.notificationName, object: nil, queue: .main) { [weak self] _ in self?.tabManager.allTabs.forEach({ guard let url = $0.webView?.url else { return } - let zoomLevel = PrivateBrowsingManager.shared.isPrivateBrowsing ? 1.0 : Domain.getPersistedDomain(for: url)?.zoom_level?.doubleValue ?? Preferences.General.defaultPageZoomLevel.value + let zoomLevel = self?.privateBrowsingManager.isPrivateBrowsing == true ? 1.0 : Domain.getPersistedDomain(for: url)?.zoom_level?.doubleValue ?? Preferences.General.defaultPageZoomLevel.value $0.webView?.setValue(zoomLevel, forKey: PageZoomView.propertyName) }) } @@ -550,7 +561,7 @@ public class BrowserViewController: UIViewController { notificationsPresenter: notificationsPresenter) notificationsHandler?.canShowNotifications = { [weak self] in guard let self = self else { return false } - return !PrivateBrowsingManager.shared.isPrivateBrowsing && !self.topToolbar.inOverlayMode + return !self.privateBrowsingManager.isPrivateBrowsing && !self.topToolbar.inOverlayMode } notificationsHandler?.actionOccured = { [weak self] ad, action in guard let self = self, let ad = ad else { return } @@ -567,7 +578,7 @@ public class BrowserViewController: UIViewController { return } let request = URLRequest(url: targetURL) - self.tabManager.addTabAndSelect(request, isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing) + self.tabManager.addTabAndSelect(request, isPrivate: self.privateBrowsingManager.isPrivateBrowsing) } } } @@ -608,7 +619,7 @@ public class BrowserViewController: UIViewController { bottomTouchArea.isEnabled = showToolbar if showToolbar { - toolbar = BottomToolbarView() + toolbar = BottomToolbarView(privateBrowsingManager: privateBrowsingManager) toolbar?.setSearchButtonState(url: tabManager.selectedTab?.url) footer.addSubview(toolbar!) toolbar?.tabToolbarDelegate = self @@ -872,7 +883,7 @@ public class BrowserViewController: UIViewController { scheduleDefaultBrowserNotification() } - privateModeCancellable = PrivateBrowsingManager.shared + privateModeCancellable = privateBrowsingManager .$isPrivateBrowsing .removeDuplicates() .sink(receiveValue: { [weak self] isPrivateBrowsing in @@ -1081,7 +1092,7 @@ public class BrowserViewController: UIViewController { fileprivate func showRestoreTabsAlert() { guard canRestoreTabs() else { - self.tabManager.addTabAndSelect(isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing) + self.tabManager.addTabAndSelect(isPrivate: self.privateBrowsingManager.isPrivateBrowsing) return } let alert = UIAlertController.restoreTabsAlert( @@ -1090,7 +1101,7 @@ public class BrowserViewController: UIViewController { }, noCallback: { _ in SessionTab.deleteAll() - self.tabManager.addTabAndSelect(isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing) + self.tabManager.addTabAndSelect(isPrivate: self.privateBrowsingManager.isPrivateBrowsing) } ) self.present(alert, animated: true, completion: nil) @@ -1123,6 +1134,24 @@ public class BrowserViewController: UIViewController { show(toast: toast, afterWaiting: ButtonToastUX.toastDelay) } showQueuedAlertIfAvailable() + + let isPrivateBrowsing = SessionWindow.from(windowId: windowId)?.isPrivate == true + var userActivity = view.window?.windowScene?.userActivity + if userActivity == nil { + userActivity = BrowserState.userActivity(for: windowId, isPrivate: isPrivateBrowsing) + } else { + userActivity?.targetContentIdentifier = windowId.uuidString + userActivity?.addUserInfoEntries(from: ["WindowID": windowId.uuidString, + "isPrivate": isPrivateBrowsing]) + } + + view.window?.windowScene?.userActivity = userActivity + view.window?.windowScene?.session.userInfo = ["WindowID": windowId.uuidString, + "isPrivate": isPrivateBrowsing] + + for session in UIApplication.shared.openSessions { + UIApplication.shared.requestSceneSessionRefresh(session) + } } /// Whether or not to show the playlist onboarding callout this session @@ -1141,6 +1170,7 @@ public class BrowserViewController: UIViewController { super.viewWillDisappear(animated) rewards.ledger?.selectedTabId = 0 + view.window?.windowScene?.userActivity = nil } /// A layout guide defining where the favorites and NTP overlay are placed @@ -1299,7 +1329,9 @@ public class BrowserViewController: UIViewController { profile: profile, dataSource: backgroundDataSource, feedDataSource: feedDataSource, - rewards: rewards) + rewards: rewards, + privateBrowsingManager: privateBrowsingManager + ) // Donate NewTabPage Activity For Custom Suggestions let newTabPageActivity = ActivityShortcutManager.shared.createShortcutActivity(type: selectedTab.isPrivate ? .newPrivateTab : .newTab) @@ -1382,7 +1414,7 @@ public class BrowserViewController: UIViewController { public func presentCorrespondingVPNViewController() { if BraveSkusManager.keepShowingSessionExpiredState { let alert = BraveSkusManager.sessionExpiredStateAlert(loginCallback: { [unowned self] _ in - self.openURLInNewTab(.brave.account, isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing, + self.openURLInNewTab(.brave.account, isPrivate: self.privateBrowsingManager.isPrivateBrowsing, isPrivileged: false) }) @@ -1855,7 +1887,7 @@ public class BrowserViewController: UIViewController { } else if let tab = tabManager.getTabForURL(url) { tabManager.selectTab(tab) } else { - openURLInNewTab(url, isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing, isPrivileged: false) + openURLInNewTab(url, isPrivate: privateBrowsingManager.isPrivateBrowsing, isPrivileged: false) } } @@ -1958,7 +1990,7 @@ public class BrowserViewController: UIViewController { var activities = [UIActivity]() // Adding SendTabToSelfActivity conditionally to show device selection screen - if !PrivateBrowsingManager.shared.isPrivateBrowsing, !url.isLocal, !InternalURL.isValid(url: url), !url.isReaderModeURL, + if !privateBrowsingManager.isPrivateBrowsing, !url.isLocal, !InternalURL.isValid(url: url), !url.isReaderModeURL, braveCore.syncAPI.isSendTabToSelfVisible { let sendTabToSelfActivity = SendTabToSelfActivity() { [weak self] in guard let self = self else { return } @@ -2176,7 +2208,7 @@ public class BrowserViewController: UIViewController { } guard let webView = tabManager.selectedTab?.webView else { return } - let pageZoomBar = UIHostingController(rootView: PageZoomView(webView: webView)) + let pageZoomBar = UIHostingController(rootView: PageZoomView(webView: webView, isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing)) pageZoomBar.rootView.dismiss = { [weak self] in guard let self = self else { return } @@ -2213,7 +2245,7 @@ public class BrowserViewController: UIViewController { if let currentURL = tab.url { let domain = Domain.getPersistedDomain(for: currentURL) - let zoomLevel = PrivateBrowsingManager.shared.isPrivateBrowsing ? 1.0 : domain?.zoom_level?.doubleValue ?? Preferences.General.defaultPageZoomLevel.value + let zoomLevel = privateBrowsingManager.isPrivateBrowsing ? 1.0 : domain?.zoom_level?.doubleValue ?? Preferences.General.defaultPageZoomLevel.value tab.webView?.setValue(zoomLevel, forKey: PageZoomView.propertyName) } } @@ -2230,7 +2262,7 @@ public class BrowserViewController: UIViewController { defer { setNeedsStatusBarAppearanceUpdate() } guard isUsingBottomBar, let tab = tabManager.selectedTab, tab.url.map(InternalURL.isValid) == false, let color = tab.webView?.sampledPageTopColor else { - if PrivateBrowsingManager.shared.isPrivateBrowsing { + if privateBrowsingManager.isPrivateBrowsing { statusBarOverlay.backgroundColor = .privateModeBackground } else { statusBarOverlay.backgroundColor = Preferences.General.nightModeEnabled.value ? .nightModeBackground : .urlBarBackground @@ -2260,21 +2292,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) } } @@ -2414,7 +2446,7 @@ extension BrowserViewController { self.topToolbar.enterOverlayMode(overlayText, pasted: false, search: false) } - if !url.isBookmarklet && !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !url.isBookmarklet && !privateBrowsingManager.isPrivateBrowsing { RecentSearch.addItem(type: .qrCode, text: nil, websiteUrl: url.absoluteString) } } @@ -2423,7 +2455,7 @@ extension BrowserViewController { popToBVC() submitSearchText(text) - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !privateBrowsingManager.isPrivateBrowsing { RecentSearch.addItem(type: .qrCode, text: text, websiteUrl: nil) } } @@ -2431,7 +2463,7 @@ extension BrowserViewController { extension BrowserViewController: SettingsDelegate { func settingsOpenURLInNewTab(_ url: URL) { - let forcedPrivate = PrivateBrowsingManager.shared.isPrivateBrowsing + let forcedPrivate = privateBrowsingManager.isPrivateBrowsing self.openURLInNewTab(url, isPrivate: forcedPrivate, isPrivileged: false) } @@ -2451,6 +2483,21 @@ 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 } @@ -2614,7 +2661,7 @@ extension BrowserViewController: TabDelegate { /// Triggered when "Search with Brave" is selected on selected web text func tab(_ tab: Tab, didSelectSearchWithBraveFor selectedText: String) { - let engine = profile.searchEngines.defaultEngine() + let engine = profile.searchEngines.defaultEngine(forType: tab.isPrivate ? .privateMode : .standard) guard let url = engine.searchURLForQuery(selectedText) else { assertionFailure("If this returns nil, investigate why and add proper handling or commenting") @@ -2627,7 +2674,7 @@ extension BrowserViewController: TabDelegate { isPrivate: tab.isPrivate ) - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !privateBrowsingManager.isPrivateBrowsing { RecentSearch.addItem(type: .text, text: selectedText, websiteUrl: url.absoluteString) } } @@ -2666,7 +2713,7 @@ extension BrowserViewController: TabDelegate { vc.linkTapped = { [unowned self] request in tab.rewardsEnabledCallback?(false) self.tabManager - .addTabAndSelect(request, isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing) + .addTabAndSelect(request, isPrivate: privateBrowsingManager.isPrivateBrowsing) } } @@ -2714,7 +2761,7 @@ extension BrowserViewController: TabDelegate { @MainActor private func isPendingRequestAvailable() async -> Bool { - let privateMode = PrivateBrowsingManager.shared.isPrivateBrowsing + let privateMode = privateBrowsingManager.isPrivateBrowsing // If we have an open `WalletStore`, use that so we can assign the pending request if the wallet is open, // which allows us to store the new `PendingRequest` triggering a modal presentation for that request. guard let cryptoStore = self.walletStore?.cryptoStore ?? CryptoStore.from(ipfsApi: braveCore.ipfsAPI, privateMode: privateMode) else { @@ -2751,7 +2798,7 @@ extension BrowserViewController: SearchViewControllerDelegate { } func presentSearchSettingsController() { - let settingsNavigationController = SearchSettingsTableViewController(profile: profile) + let settingsNavigationController = SearchSettingsTableViewController(profile: profile, privateBrowsingManager: privateBrowsingManager) let navController = ModalSettingsNavigationController(rootViewController: settingsNavigationController) self.present(navController, animated: true, completion: nil) @@ -2860,7 +2907,7 @@ extension BrowserViewController: ToolbarUrlActionsDelegate { updateURLBarWalletButton() case .openInNewTab(let isPrivate): let tab = tabManager.addTab(PrivilegedRequest(url: url) as URLRequest, afterTab: tabManager.selectedTab, isPrivate: isPrivate) - if isPrivate && !PrivateBrowsingManager.shared.isPrivateBrowsing { + if isPrivate && !privateBrowsingManager.isPrivateBrowsing { tabManager.selectTab(tab) } else { // If we are showing toptabs a user can just use the top tab bar @@ -2905,7 +2952,7 @@ extension BrowserViewController: ToolbarUrlActionsDelegate { extension BrowserViewController: NewTabPageDelegate { func navigateToInput(_ input: String, inNewTab: Bool, switchingToPrivateMode: Bool) { - let isPrivate = PrivateBrowsingManager.shared.isPrivateBrowsing || switchingToPrivateMode + let isPrivate = privateBrowsingManager.isPrivateBrowsing || switchingToPrivateMode if inNewTab { tabManager.addTabAndSelect(isPrivate: isPrivate) } @@ -2983,7 +3030,7 @@ extension BrowserViewController: PreferencesObserver { case Preferences.General.tabBarVisibility.key: updateTabsBarVisibility() case Preferences.Privacy.privateBrowsingOnly.key: - PrivateBrowsingManager.shared.isPrivateBrowsing = Preferences.Privacy.privateBrowsingOnly.value + privateBrowsingManager.isPrivateBrowsing = Preferences.Privacy.privateBrowsingOnly.value setupTabs() updateTabsBarVisibility() updateApplicationShortcuts() @@ -3001,7 +3048,7 @@ extension BrowserViewController: PreferencesObserver { case Preferences.General.defaultPageZoomLevel.key: tabManager.allTabs.forEach({ guard let url = $0.webView?.url else { return } - let zoomLevel = PrivateBrowsingManager.shared.isPrivateBrowsing ? 1.0 : Domain.getPersistedDomain(for: url)?.zoom_level?.doubleValue ?? Preferences.General.defaultPageZoomLevel.value + let zoomLevel = $0.isPrivate ? 1.0 : Domain.getPersistedDomain(for: url)?.zoom_level?.doubleValue ?? Preferences.General.defaultPageZoomLevel.value $0.webView?.setValue(zoomLevel, forKey: PageZoomView.propertyName) }) case Preferences.Shields.httpsEverywhere.key: @@ -3051,7 +3098,7 @@ extension BrowserViewController: PreferencesObserver { state: selectedTab?.playlistItemState ?? .none, item: selectedTab?.playlistItem) case Preferences.PrivacyReports.captureShieldsData.key: - PrivacyReportsManager.scheduleProcessingBlockedRequests() + PrivacyReportsManager.scheduleProcessingBlockedRequests(isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing) PrivacyReportsManager.scheduleNotification(debugMode: !AppConstants.buildChannel.isPublic) case Preferences.PrivacyReports.captureVPNAlerts.key: PrivacyReportsManager.scheduleVPNAlertsTask() @@ -3061,7 +3108,7 @@ extension BrowserViewController: PreferencesObserver { notificationsPresenter.removeNotification(with: WalletNotification.Constant.id) WalletProviderPermissionRequestsManager.shared.cancelAllPendingRequests(for: [.eth]) WalletProviderAccountCreationRequestManager.shared.cancelAllPendingRequests(coins: [.eth]) - let privateMode = PrivateBrowsingManager.shared.isPrivateBrowsing + let privateMode = privateBrowsingManager.isPrivateBrowsing if let cryptoStore = CryptoStore.from(ipfsApi: braveCore.ipfsAPI, privateMode: privateMode) { cryptoStore.rejectAllPendingWebpageRequests() } @@ -3072,7 +3119,7 @@ extension BrowserViewController: PreferencesObserver { notificationsPresenter.removeNotification(with: WalletNotification.Constant.id) WalletProviderPermissionRequestsManager.shared.cancelAllPendingRequests(for: [.sol]) WalletProviderAccountCreationRequestManager.shared.cancelAllPendingRequests(coins: [.sol]) - let privateMode = PrivateBrowsingManager.shared.isPrivateBrowsing + let privateMode = privateBrowsingManager.isPrivateBrowsing if let cryptoStore = CryptoStore.from(ipfsApi: braveCore.ipfsAPI, privateMode: privateMode) { cryptoStore.rejectAllPendingWebpageRequests() } @@ -3103,7 +3150,7 @@ extension BrowserViewController { extension BrowserViewController { func presentTabReceivedToast(url: URL) { // 'Tab Received' indicator will only be shown in normal browsing - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !privateBrowsingManager.isPrivateBrowsing { let toast = ButtonToast( labelText: Strings.Callout.tabReceivedCalloutTitle, image: UIImage(braveSystemNamed: "leo.smartphone.tablet-portrait"), @@ -3173,11 +3220,11 @@ extension BrowserViewController: UIScreenshotServiceDelegate { // Privacy reports extension BrowserViewController { public func openPrivacyReport() { - if PrivateBrowsingManager.shared.isPrivateBrowsing { + if privateBrowsingManager.isPrivateBrowsing { return } - let host = UIHostingController(rootView: PrivacyReportsManager.prepareView()) + let host = UIHostingController(rootView: PrivacyReportsManager.prepareView(isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing)) host.rootView.openPrivacyReportsUrl = { [weak self] in guard let self = self else { return } diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+Rewards.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+Rewards.swift index 97ad2f85c4e..7aaf0e2c959 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+Rewards.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+Rewards.swift @@ -25,7 +25,7 @@ extension BrowserViewController { self.topToolbar.locationView.rewardsButton.isHidden = true return } - self.topToolbar.locationView.rewardsButton.isHidden = Preferences.Rewards.hideRewardsIcon.value || PrivateBrowsingManager.shared.isPrivateBrowsing + self.topToolbar.locationView.rewardsButton.isHidden = Preferences.Rewards.hideRewardsIcon.value || privateBrowsingManager.isPrivateBrowsing self.topToolbar.locationView.rewardsButton.iconState = Preferences.Rewards.rewardsToggledOnce.value ? (rewards.isEnabled || rewards.isCreatingWallet ? .enabled : .disabled) : .initial } @@ -119,7 +119,7 @@ extension BrowserViewController { tabManager.selectTab(tab) } else { let request = URLRequest(url: url) - let isPrivate = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivate = privateBrowsingManager.isPrivateBrowsing tabManager.addTabAndSelect(request, isPrivate: isPrivate) } } @@ -185,7 +185,7 @@ extension BrowserViewController { extension Tab { func reportPageLoad(to rewards: BraveRewards, redirectionURLs urls: [URL]) { guard let webView = webView, let url = webView.url else { return } - if url.isLocal || PrivateBrowsingManager.shared.isPrivateBrowsing { return } + if url.isLocal || self.isPrivate { return } var htmlBlob: String? var classifierText: String? diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Callout.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Callout.swift index 9517232dbdd..41cae5be3fb 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Callout.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Callout.swift @@ -249,7 +249,7 @@ extension BrowserViewController { var linkReceiptView = VPNLinkReceiptView() linkReceiptView.linkReceiptAction = { - self.openURLInNewTab(.brave.braveVPNLinkReceiptProd, isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing, isPrivileged: false) + self.openURLInNewTab(.brave.braveVPNLinkReceiptProd, isPrivate: self.privateBrowsingManager.isPrivateBrowsing, isPrivileged: false) } let popup = PopupViewController(rootView: linkReceiptView, isDismissable: true) diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+KeyCommands.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+KeyCommands.swift index 327f3ecde3c..37937a06d9e 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+KeyCommands.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+KeyCommands.swift @@ -43,7 +43,7 @@ extension BrowserViewController { } @objc private func newTabKeyCommand() { - openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing) + openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: privateBrowsingManager.isPrivateBrowsing) } @objc private func newPrivateTabKeyCommand() { @@ -211,7 +211,7 @@ extension BrowserViewController { UIKeyCommand(title: Strings.Hotkey.newPrivateTabTitle, action: #selector(newPrivateTabKeyCommand), input: "n", modifierFlags: [.command, .shift]), ] - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !privateBrowsingManager.isPrivateBrowsing { navigationCommands += [ UIKeyCommand(title: Strings.Hotkey.recentlyClosedTabTitle, action: #selector(reopenRecentlyClosedTabCommand), input: "t", modifierFlags: [.command, .shift]) ] diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Menu.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Menu.swift index 0d5b780ba4d..c936c44dbd1 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Menu.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Menu.swift @@ -31,7 +31,8 @@ extension BrowserViewController { }, displayAlert: { [unowned menuController] alert in menuController.present(alert, animated: true) }, openURL: { [weak self] url in - self?.openURLInNewTab(url, isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing, + guard let self = self else { return } + self.openURLInNewTab(url, isPrivate: self.privateBrowsingManager.isPrivateBrowsing, isPrivileged: false) }) @@ -69,7 +70,7 @@ extension BrowserViewController { self.present(alert, animated: true) }, openURL: { [unowned self] url in self.openURLInNewTab(url, - isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing, + isPrivate: self.privateBrowsingManager.isPrivateBrowsing, isPrivileged: false) } ) @@ -89,7 +90,7 @@ extension BrowserViewController { } // Add Brave Talk and News options only in normal browsing - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !privateBrowsingManager.isPrivateBrowsing { // Show Brave News if it is first launch and after first launch If the new is enabled if Preferences.General.isFirstLaunch.value || (!Preferences.General.isFirstLaunch.value && Preferences.BraveNews.isEnabled.value) { MenuItemFactory.button(for: .news) { [weak self] in @@ -124,13 +125,13 @@ extension BrowserViewController { let vc = BookmarksViewController( folder: bookmarkManager.lastVisitedFolder(), bookmarkManager: bookmarkManager, - isPrivateBrowsing: PrivateBrowsingManager.shared.isPrivateBrowsing) + isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing) vc.toolbarUrlActionsDelegate = self menuController.presentInnerMenu(vc) } MenuItemFactory.button(for: .history) { [unowned self, unowned menuController] in let vc = HistoryViewController( - isPrivateBrowsing: PrivateBrowsingManager.shared.isPrivateBrowsing, + isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing, historyAPI: self.braveCore.historyAPI, tabManager: self.tabManager) vc.toolbarUrlActionsDelegate = self @@ -154,7 +155,7 @@ extension BrowserViewController { } } MenuItemFactory.button(for: .settings) { [unowned self, unowned menuController] in - let isPrivateMode = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateMode = privateBrowsingManager.isPrivateBrowsing let keyringService = BraveWallet.KeyringServiceFactory.get(privateMode: isPrivateMode) let walletService = BraveWallet.ServiceFactory.get(privateMode: isPrivateMode) let rpcService = BraveWallet.JsonRpcServiceFactory.get(privateMode: isPrivateMode) diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Onboarding.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Onboarding.swift index 163b3487dec..0ddd471629f 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Onboarding.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Onboarding.swift @@ -49,7 +49,7 @@ extension BrowserViewController { tabManager.addTab( PrivilegedRequest(url: url) as URLRequest, afterTab: self.tabManager.selectedTab, - isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing) + isPrivate: privateBrowsingManager.isPrivateBrowsing) } } @@ -105,7 +105,7 @@ extension BrowserViewController { } private func showPrivacyReportsOnboardingIfNeeded() { - if Preferences.PrivacyReports.ntpOnboardingCompleted.value || PrivateBrowsingManager.shared.isPrivateBrowsing { + if Preferences.PrivacyReports.ntpOnboardingCompleted.value || privateBrowsingManager.isPrivateBrowsing { return } diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift index 64d7dd132fa..f2b30d5d671 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift @@ -167,7 +167,7 @@ extension BrowserViewController: TabManagerDelegate { topToolbar.leaveOverlayMode(didCancel: true) updateTabsBarVisibility() - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !privateBrowsingManager.isPrivateBrowsing { rewards.reportTabClosed(tabId: Int(tab.rewardsId)) } } @@ -200,7 +200,7 @@ extension BrowserViewController: TabManagerDelegate { } func tabManagerDidRemoveAllTabs(_ tabManager: TabManager, toast: ButtonToast?) { - guard let toast = toast, !PrivateBrowsingManager.shared.isPrivateBrowsing else { + guard let toast = toast, !privateBrowsingManager.isPrivateBrowsing else { return } show(toast: toast, afterWaiting: ButtonToastUX.toastDelay) @@ -216,7 +216,7 @@ extension BrowserViewController: TabManagerDelegate { var newTabMenuChildren: [UIAction] = [] var addTabMenuChildren: [UIAction] = [] - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !privateBrowsingManager.isPrivateBrowsing { let openNewPrivateTab = UIAction( title: Strings.Hotkey.newPrivateTabTitle, image: UIImage(systemName: "plus.square.fill.on.square.fill"), @@ -232,16 +232,20 @@ extension BrowserViewController: TabManagerDelegate { } let openNewTab = UIAction( - title: PrivateBrowsingManager.shared.isPrivateBrowsing ? Strings.Hotkey.newPrivateTabTitle : Strings.Hotkey.newTabTitle, - image: PrivateBrowsingManager.shared.isPrivateBrowsing ? UIImage(systemName: "plus.square.fill.on.square.fill") : UIImage(systemName: "plus.square.on.square"), + title: privateBrowsingManager.isPrivateBrowsing ? Strings.Hotkey.newPrivateTabTitle : Strings.Hotkey.newTabTitle, + image: privateBrowsingManager.isPrivateBrowsing ? UIImage(systemName: "plus.square.fill.on.square.fill") : UIImage(systemName: "plus.square.on.square"), handler: UIAction.deferredActionHandler { [unowned self] _ in - self.openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing) + self.openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: privateBrowsingManager.isPrivateBrowsing) }) if (UIDevice.current.userInterfaceIdiom == .pad && tabsBar.view.isHidden) || (UIDevice.current.userInterfaceIdiom == .phone && toolbar == nil) { 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] = [] @@ -264,7 +268,7 @@ extension BrowserViewController: TabManagerDelegate { image: UIImage(systemName: "book"), handler: UIAction.deferredActionHandler { [unowned self] _ in let mode = BookmarkEditMode.addFolderUsingTabs(title: Strings.savedTabsFolderTitle, tabList: tabManager.tabsForCurrentMode) - let addBookMarkController = AddEditBookmarkTableViewController(bookmarkManager: bookmarkManager, mode: mode) + let addBookMarkController = AddEditBookmarkTableViewController(bookmarkManager: bookmarkManager, mode: mode, isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing) presentSettingsNavigation(with: addBookMarkController, cancelEnabled: true) }) @@ -294,14 +298,14 @@ extension BrowserViewController: TabManagerDelegate { var recentlyClosedMenuChildren: [UIAction] = [] // Recently Closed Actions are only in normal mode - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !privateBrowsingManager.isPrivateBrowsing { let viewRecentlyClosedTabs = UIAction( title: Strings.RecentlyClosed.viewRecentlyClosedTab, image: UIImage(braveSystemNamed: "leo.browser.mobile-recent-tabs"), handler: UIAction.deferredActionHandler { [weak self] _ in guard let self = self else { return } - if PrivateBrowsingManager.shared.isPrivateBrowsing { + if privateBrowsingManager.isPrivateBrowsing { return } @@ -369,7 +373,7 @@ extension BrowserViewController: TabManagerDelegate { let cancelAction = UIAlertAction(title: Strings.CancelString, style: .cancel) let closedTabsTitle = String(format: Strings.closeAllTabsTitle, tabManager.tabsForCurrentMode.count) let closeAllAction = UIAlertAction(title: closedTabsTitle, style: .destructive) { _ in - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !privateBrowsingManager.isPrivateBrowsing { // Add the tab information to recently closed before removing tabManager.addAllTabsToRecentlyClosed() } diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+ToolbarDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+ToolbarDelegate.swift index 6752ff0f2f6..2c4002ba24c 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+ToolbarDelegate.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+ToolbarDelegate.swift @@ -260,7 +260,8 @@ extension BrowserViewController: TopToolbarDelegate { if let url = searchURL, InternalURL.isValid(url: url) { searchURL = url } - if let query = profile.searchEngines.queryForSearchURL(searchURL as URL?) { + if let query = profile.searchEngines.queryForSearchURL(searchURL as URL?, + forType: privateBrowsingManager.isPrivateBrowsing ? .privateMode : .standard) { return (query, true) } else { return (topToolbar?.absoluteString, false) @@ -304,7 +305,7 @@ extension BrowserViewController: TopToolbarDelegate { // We couldn't build a URL, so pass it on to the search engine. submitSearchText(text, isBraveSearchPromotion: isBraveSearchPromotion) - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !privateBrowsingManager.isPrivateBrowsing { RecentSearch.addItem(type: .text, text: text, websiteUrl: nil) } } @@ -313,7 +314,7 @@ extension BrowserViewController: TopToolbarDelegate { @discardableResult func handleIPFSSchemeURL(_ url: URL, visitType: VisitType) -> Bool { - guard !PrivateBrowsingManager.shared.isPrivateBrowsing else { + guard !privateBrowsingManager.isPrivateBrowsing else { topToolbar.leaveOverlayMode() if let errorPageHelper = tabManager.selectedTab?.getContentScript(name: ErrorPageHelper.scriptName) as? ErrorPageHelper, let webView = tabManager.selectedTab?.webView { errorPageHelper.loadPage(IPFSErrorPageHandler.privateModeError, forUrl: url, inWebView: webView) @@ -386,7 +387,7 @@ extension BrowserViewController: TopToolbarDelegate { } func submitSearchText(_ text: String, isBraveSearchPromotion: Bool = false) { - var engine = profile.searchEngines.defaultEngine() + var engine = profile.searchEngines.defaultEngine(forType: privateBrowsingManager.isPrivateBrowsing ? .privateMode : .standard) if isBraveSearchPromotion { let braveSearchEngine = profile.searchEngines.orderedEngines.first { @@ -790,7 +791,7 @@ extension BrowserViewController: TopToolbarDelegate { let mode = BookmarkEditMode.addBookmark(title: selectedTab.displayTitle, url: bookmarkUrl.absoluteString) - let addBookMarkController = AddEditBookmarkTableViewController(bookmarkManager: bookmarkManager, mode: mode) + let addBookMarkController = AddEditBookmarkTableViewController(bookmarkManager: bookmarkManager, mode: mode, isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing) presentSettingsNavigation(with: addBookMarkController, cancelEnabled: true) } @@ -881,7 +882,7 @@ extension BrowserViewController: ToolbarDelegate { } func tabToolbarDidPressAddTab(_ tabToolbar: ToolbarProtocol, button: UIButton) { - self.openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing) + self.openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: privateBrowsingManager.isPrivateBrowsing) } func tabToolbarDidLongPressForward(_ tabToolbar: ToolbarProtocol, button: UIButton) { diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift index f1abd6c6c8b..416d2055b1d 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift @@ -241,7 +241,7 @@ extension BrowserViewController: WKNavigationDelegate { } } - let isPrivateBrowsing = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateBrowsing = privateBrowsingManager.isPrivateBrowsing // Website redirection logic if url.isWebPage(includeDataURIs: false), @@ -446,7 +446,7 @@ extension BrowserViewController: WKNavigationDelegate { @MainActor public func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse) async -> WKNavigationResponsePolicy { - let isPrivateBrowsing = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateBrowsing = privateBrowsingManager.isPrivateBrowsing let response = navigationResponse.response let responseURL = response.url let tab = tab(for: webView) @@ -693,7 +693,7 @@ extension BrowserViewController: WKNavigationDelegate { tab: tab, url: url, isSelected: tabManager.selectedTab == tab, - isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing + isPrivate: privateBrowsingManager.isPrivateBrowsing ) } @@ -1091,7 +1091,7 @@ extension BrowserViewController: WKUIDelegate { fileprivate func addTab(url: URL, inPrivateMode: Bool, currentTab: Tab) { let tab = self.tabManager.addTab(URLRequest(url: url), afterTab: currentTab, isPrivate: inPrivateMode) - if inPrivateMode && !PrivateBrowsingManager.shared.isPrivateBrowsing { + if inPrivateMode && !privateBrowsingManager.isPrivateBrowsing { self.tabManager.selectTab(tab) } else { // We're not showing the top tabs; show a toast to quick switch to the fresh new tab. diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Wallet.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Wallet.swift index 2a908d7898a..cb953179257 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Wallet.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Wallet.swift @@ -80,7 +80,7 @@ extension BrowserViewController { /// Initializes a new WalletStore for displaying the wallet, setting up an observer to notify /// when the pending request is updated so we can update the wallet url bar button. func newWalletStore() -> WalletStore? { - let privateMode = PrivateBrowsingManager.shared.isPrivateBrowsing + let privateMode = privateBrowsingManager.isPrivateBrowsing guard let walletStore = WalletStore.from(ipfsApi: braveCore.ipfsAPI, privateMode: privateMode) else { Logger.module.error("Failed to load wallet. One or more services were unavailable") return nil @@ -135,7 +135,7 @@ extension BrowserViewController: BraveWalletDelegate { } else { _ = tabManager.addTabAndSelect( URLRequest(url: destinationURL), - isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing + isPrivate: privateBrowsingManager.isPrivateBrowsing ) } } @@ -194,7 +194,7 @@ extension Tab: BraveWalletProviderDelegate { return } - let isPrivate = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivate = self.isPrivate // Check if eth permissions already exist for this origin and if they don't, ensure the user allows // ethereum/solana provider access @@ -338,7 +338,7 @@ extension Tab: BraveWalletProviderDelegate { } func showAccountCreation(_ coin: BraveWallet.CoinType) { - let privateMode = PrivateBrowsingManager.shared.isPrivateBrowsing + let privateMode = self.isPrivate guard let keyringService = BraveWallet.KeyringServiceFactory.get(privateMode: privateMode) else { return } diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Web3NameService.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Web3NameService.swift index e199d8266f5..ceaf7452087 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Web3NameService.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Web3NameService.swift @@ -11,7 +11,7 @@ import BraveCore extension BrowserViewController: Web3NameServiceScriptHandlerDelegate { /// Returns a `DecentralizedDNSHelper` for the given mode if supported and not in private mode. func decentralizedDNSHelperFor(url: URL?) -> DecentralizedDNSHelper? { - let isPrivateMode = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateMode = privateBrowsingManager.isPrivateBrowsing guard !isPrivateMode, let url, DecentralizedDNSHelper.isSupported(domain: url.domainURL.schemelessAbsoluteDisplayString), @@ -24,7 +24,7 @@ extension BrowserViewController: Web3NameServiceScriptHandlerDelegate { } func web3NameServiceDecisionHandler(_ proceed: Bool, web3Service: Web3Service, originalURL: URL, visitType: VisitType) { - let isPrivateMode = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateMode = privateBrowsingManager.isPrivateBrowsing guard let rpcService = BraveWallet.JsonRpcServiceFactory.get(privateMode: isPrivateMode), let decentralizedDNSHelper = self.decentralizedDNSHelperFor(url: originalURL) else { finishEditingAndSubmit(originalURL, visitType: visitType) diff --git a/Sources/Brave/Frontend/Browser/Favicons/LargeFaviconView.swift b/Sources/Brave/Frontend/Browser/Favicons/LargeFaviconView.swift index 07423220ee3..452f58685de 100644 --- a/Sources/Brave/Frontend/Browser/Favicons/LargeFaviconView.swift +++ b/Sources/Brave/Frontend/Browser/Favicons/LargeFaviconView.swift @@ -16,7 +16,7 @@ struct FaviconUX { /// Displays a large favicon given some favorite class LargeFaviconView: UIView { - func loadFavicon(siteURL: URL, monogramFallbackCharacter: Character? = nil) { + func loadFavicon(siteURL: URL, isPrivateBrowsing: Bool, monogramFallbackCharacter: Character? = nil) { faviconTask?.cancel() if let favicon = FaviconFetcher.getIconFromCache(for: siteURL) { faviconTask = nil @@ -34,7 +34,7 @@ class LargeFaviconView: UIView { } faviconTask = Task { @MainActor in - let isPersistent = !PrivateBrowsingManager.shared.isPrivateBrowsing + let isPersistent = !isPrivateBrowsing do { let favicon = try await FaviconFetcher.loadIcon(url: siteURL, kind: .largeIcon, diff --git a/Sources/Brave/Frontend/Browser/Favicons/UIImageView+Favicon.swift b/Sources/Brave/Frontend/Browser/Favicons/UIImageView+Favicon.swift index f960640a926..777340447de 100644 --- a/Sources/Brave/Frontend/Browser/Favicons/UIImageView+Favicon.swift +++ b/Sources/Brave/Frontend/Browser/Favicons/UIImageView+Favicon.swift @@ -28,7 +28,7 @@ extension UIImageView { /// Load the favicon from a site URL directly into a `UIImageView`. If no /// favicon is found, a monogram will be used where the letter is determined /// based on the site URL. - func loadFavicon(for siteURL: URL, monogramFallbackCharacter: Character? = nil, completion: ((Favicon?) -> Void)? = nil) { + func loadFavicon(for siteURL: URL, isPrivateBrowsing: Bool, monogramFallbackCharacter: Character? = nil, completion: ((Favicon?) -> Void)? = nil) { cancelFaviconLoad() if let icon = FaviconFetcher.getIconFromCache(for: siteURL) { @@ -39,7 +39,7 @@ extension UIImageView { self.image = Favicon.defaultImage faviconTask = Task { @MainActor in do { - let favicon = try await FaviconFetcher.loadIcon(url: siteURL, persistent: !PrivateBrowsingManager.shared.isPrivateBrowsing) + let favicon = try await FaviconFetcher.loadIcon(url: siteURL, persistent: !isPrivateBrowsing) self.image = favicon.image ?? Favicon.defaultImage completion?(favicon) } catch { diff --git a/Sources/Brave/Frontend/Browser/Favorites/FavoritesViewController.swift b/Sources/Brave/Frontend/Browser/Favorites/FavoritesViewController.swift index af957c4525a..bae22c84e12 100644 --- a/Sources/Brave/Frontend/Browser/Favorites/FavoritesViewController.swift +++ b/Sources/Brave/Frontend/Browser/Favorites/FavoritesViewController.swift @@ -94,11 +94,13 @@ class FavoritesViewController: UIViewController { $0.contentView.backgroundColor = UIColor.braveBackground.withAlphaComponent(0.5) } private var hasPasteboardURL = false + private var isPrivateBrowsing: Bool init(tabType: TabType, action: @escaping (Favorite, BookmarksAction) -> Void, recentSearchAction: @escaping (RecentSearch?, Bool) -> Void) { self.tabType = tabType self.action = action self.recentSearchAction = recentSearchAction + self.isPrivateBrowsing = tabType.isPrivate collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) super.init(nibName: nil, bundle: nil) @@ -352,7 +354,7 @@ extension FavoritesViewController: UICollectionViewDelegateFlowLayout { }) var urlChildren: [UIAction] = [openInNewTab] - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !self.isPrivateBrowsing { let openInNewPrivateTab = UIAction( title: Strings.openNewPrivateTabButtonTitle, handler: UIAction.deferredActionHandler { _ in @@ -668,7 +670,7 @@ extension FavoritesViewController: NSFetchedResultsControllerDelegate { cell.textLabel.text = favorite.displayTitle ?? favorite.url if let url = favorite.url?.asURL { - cell.imageView.loadFavicon(siteURL: url) + cell.imageView.loadFavicon(siteURL: url, isPrivateBrowsing: self.isPrivateBrowsing) } cell.accessibilityLabel = cell.textLabel.text diff --git a/Sources/Brave/Frontend/Browser/FrequencyQuery.swift b/Sources/Brave/Frontend/Browser/FrequencyQuery.swift index 66102bd4c41..024aa17e974 100644 --- a/Sources/Brave/Frontend/Browser/FrequencyQuery.swift +++ b/Sources/Brave/Frontend/Browser/FrequencyQuery.swift @@ -79,7 +79,7 @@ class FrequencyQuery { var tabList = [Site]() for tab in tabs { - if PrivateBrowsingManager.shared.isPrivateBrowsing { + if tab.isPrivate { if let url = tab.url, url.isWebPage(), !(InternalURL(url)?.isAboutHomeURL ?? false) { if let selectedTabID = tabManager.selectedTab?.id, selectedTabID == tab.id { continue diff --git a/Sources/Brave/Frontend/Browser/Helpers/BrowserNavigationHelper.swift b/Sources/Brave/Frontend/Browser/Helpers/BrowserNavigationHelper.swift index c4a49aca3cd..8d8e4e9c9ca 100644 --- a/Sources/Brave/Frontend/Browser/Helpers/BrowserNavigationHelper.swift +++ b/Sources/Brave/Frontend/Browser/Helpers/BrowserNavigationHelper.swift @@ -45,7 +45,7 @@ class BrowserNavigationHelper { let vc = BookmarksViewController( folder: bvc.bookmarkManager.lastVisitedFolder(), bookmarkManager: bvc.bookmarkManager, - isPrivateBrowsing: PrivateBrowsingManager.shared.isPrivateBrowsing) + isPrivateBrowsing: bvc.privateBrowsingManager.isPrivateBrowsing) vc.toolbarUrlActionsDelegate = bvc open(vc, doneButton: DoneButton(style: .done, position: .right)) @@ -58,7 +58,7 @@ class BrowserNavigationHelper { func openHistory() { guard let bvc = bvc else { return } let vc = HistoryViewController( - isPrivateBrowsing: PrivateBrowsingManager.shared.isPrivateBrowsing, + isPrivateBrowsing: bvc.privateBrowsingManager.isPrivateBrowsing, historyAPI: bvc.braveCore.historyAPI, tabManager: bvc.tabManager) vc.toolbarUrlActionsDelegate = bvc diff --git a/Sources/Brave/Frontend/Browser/Helpers/FaviconImage.swift b/Sources/Brave/Frontend/Browser/Helpers/FaviconImage.swift index 7b842d320a7..1ac8a31584d 100644 --- a/Sources/Brave/Frontend/Browser/Helpers/FaviconImage.swift +++ b/Sources/Brave/Frontend/Browser/Helpers/FaviconImage.swift @@ -14,10 +14,10 @@ private class FaviconHelper: ObservableObject { @Published var image: UIImage? private var faviconTask: Task? - func load(url: URL) { + func load(url: URL, isPrivateBrowsing: Bool) { faviconTask?.cancel() faviconTask = Task { @MainActor in - let favicon = try await FaviconFetcher.loadIcon(url: url, persistent: !PrivateBrowsingManager.shared.isPrivateBrowsing) + let favicon = try await FaviconFetcher.loadIcon(url: url, persistent: !isPrivateBrowsing) self.image = favicon.image } } @@ -25,9 +25,12 @@ private class FaviconHelper: ObservableObject { struct FaviconImage: View { let url: URL? + private let isPrivateBrowsing: Bool @StateObject private var faviconLoader = FaviconHelper() - init(url: String?) { + init(url: String?, isPrivateBrowsing: Bool) { + self.isPrivateBrowsing = isPrivateBrowsing + if let url = url { self.url = URL(string: url) } else { @@ -43,7 +46,7 @@ struct FaviconImage: View { .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)) .onAppear { if let url = url { - faviconLoader.load(url: url) + faviconLoader.load(url: url, isPrivateBrowsing: isPrivateBrowsing) } } } diff --git a/Sources/Brave/Frontend/Browser/HomePanel/NTPDataSource.swift b/Sources/Brave/Frontend/Browser/HomePanel/NTPDataSource.swift index a0415b3d44a..657c408dd53 100644 --- a/Sources/Brave/Frontend/Browser/HomePanel/NTPDataSource.swift +++ b/Sources/Brave/Frontend/Browser/HomePanel/NTPDataSource.swift @@ -52,6 +52,8 @@ enum NTPWallpaper { } public class NTPDataSource { + + var privateBrowsingManager: PrivateBrowsingManager var initializeFavorites: ((_ sites: [NTPSponsoredImageTopSite]?) -> Void)? @@ -131,7 +133,7 @@ public class NTPDataSource { let attemptSponsored = Preferences.NewTabPage.backgroundSponsoredImages.value && backgroundRotationCounter == NTPDataSource.sponsorshipShowValue - && !PrivateBrowsingManager.shared.isPrivateBrowsing + && !privateBrowsingManager.isPrivateBrowsing if attemptSponsored { // Pick the campaign randomly diff --git a/Sources/Brave/Frontend/Browser/LinkPreviewViewController.swift b/Sources/Brave/Frontend/Browser/LinkPreviewViewController.swift index 4292226662b..aaa1afb1798 100644 --- a/Sources/Brave/Frontend/Browser/LinkPreviewViewController.swift +++ b/Sources/Brave/Frontend/Browser/LinkPreviewViewController.swift @@ -45,7 +45,7 @@ class LinkPreviewViewController: UIViewController { } // Add rule lists for this page - let domain = Domain.getOrCreate(forUrl: url, persistent: !PrivateBrowsingManager.shared.isPrivateBrowsing) + let domain = Domain.getOrCreate(forUrl: url, persistent: !currentTab.isPrivate) Task(priority: .userInitiated) { let ruleLists = await ContentBlockerManager.shared.ruleLists(for: domain) diff --git a/Sources/Brave/Frontend/Browser/NavigationRouter.swift b/Sources/Brave/Frontend/Browser/NavigationRouter.swift index 37e8834dbf9..4ea3edc0f1a 100644 --- a/Sources/Brave/Frontend/Browser/NavigationRouter.swift +++ b/Sources/Brave/Frontend/Browser/NavigationRouter.swift @@ -21,10 +21,10 @@ public enum NavigationPath: Equatable { case text(String) case widgetShortcutURL(WidgetShortcut) - public init?(url: URL) { + public init?(url: URL, isPrivateBrowsing: Bool) { let urlString = url.absoluteString if url.scheme == "http" || url.scheme == "https" || url.isIPFSScheme { - self = .url(webURL: url, isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing) + self = .url(webURL: url, isPrivate: isPrivateBrowsing) return } @@ -48,7 +48,7 @@ public enum NavigationPath: Equatable { } else if urlString.starts(with: "\(scheme)://open-url") { let urlText = components.valueForQuery("url") let url = URIFixup.getURL(urlText ?? "") ?? urlText?.asURL - let forcedPrivate = Preferences.Privacy.privateBrowsingOnly.value || PrivateBrowsingManager.shared.isPrivateBrowsing + let forcedPrivate = Preferences.Privacy.privateBrowsingOnly.value || isPrivateBrowsing let isPrivate = Bool(components.valueForQuery("private") ?? "") ?? forcedPrivate self = .url(webURL: url, isPrivate: isPrivate) } else if urlString.starts(with: "\(scheme)://open-text") { @@ -95,7 +95,7 @@ public enum NavigationPath: Equatable { private static func handleText(text: String, with bvc: BrowserViewController) { bvc.openBlankNewTab( attemptLocationFieldFocus: true, - isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing, + isPrivate: bvc.privateBrowsingManager.isPrivateBrowsing, searchFor: text) } @@ -106,10 +106,10 @@ public enum NavigationPath: Equatable { if let url = bvc.tabManager.selectedTab?.url, InternalURL(url)?.isAboutHomeURL == true { bvc.focusURLBar() } else { - bvc.openBlankNewTab(attemptLocationFieldFocus: true, isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing) + bvc.openBlankNewTab(attemptLocationFieldFocus: true, isPrivate: bvc.privateBrowsingManager.isPrivateBrowsing) } case .newTab: - bvc.openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing) + bvc.openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: bvc.privateBrowsingManager.isPrivateBrowsing) case .newPrivateTab: bvc.openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: true) case .bookmarks: diff --git a/Sources/Brave/Frontend/Browser/New Tab Page/Backgrounds/NewTabPageBackgroundButtonsView.swift b/Sources/Brave/Frontend/Browser/New Tab Page/Backgrounds/NewTabPageBackgroundButtonsView.swift index b4f25a79e33..16767bca7f1 100644 --- a/Sources/Brave/Frontend/Browser/New Tab Page/Backgrounds/NewTabPageBackgroundButtonsView.swift +++ b/Sources/Brave/Frontend/Browser/New Tab Page/Backgrounds/NewTabPageBackgroundButtonsView.swift @@ -77,9 +77,12 @@ class NewTabPageBackgroundButtonsView: UIView, PreferencesObserver { } private var safeAreaInsetsConstraint: Constraint? private let collectionViewSafeAreaLayoutGuide = UILayoutGuide() + private let privateBrowsingManager: PrivateBrowsingManager - override init(frame: CGRect) { - super.init(frame: frame) + init(privateBrowsingManager: PrivateBrowsingManager) { + self.privateBrowsingManager = privateBrowsingManager + + super.init(frame: .zero) Preferences.BraveNews.isEnabled.observe(from: self) @@ -106,7 +109,7 @@ class NewTabPageBackgroundButtonsView: UIView, PreferencesObserver { let isLandscape = frame.width > frame.height let braveNewsVisible = - !PrivateBrowsingManager.shared.isPrivateBrowsing && (Preferences.BraveNews.isEnabled.value || Preferences.BraveNews.isShowingOptIn.value) + !privateBrowsingManager.isPrivateBrowsing && (Preferences.BraveNews.isEnabled.value || Preferences.BraveNews.isShowingOptIn.value) imageCreditButton.snp.remakeConstraints { $0.leading.equalTo(collectionViewSafeAreaLayoutGuide).inset(16) diff --git a/Sources/Brave/Frontend/Browser/New Tab Page/NewTabPageViewController.swift b/Sources/Brave/Frontend/Browser/New Tab Page/NewTabPageViewController.swift index 949b62bb17f..fcb571ea6ce 100644 --- a/Sources/Brave/Frontend/Browser/New Tab Page/NewTabPageViewController.swift +++ b/Sources/Brave/Frontend/Browser/New Tab Page/NewTabPageViewController.swift @@ -107,7 +107,7 @@ class NewTabPageViewController: UIViewController { private var background: NewTabPageBackground private let backgroundView = NewTabPageBackgroundView() - private let backgroundButtonsView = NewTabPageBackgroundButtonsView() + private let backgroundButtonsView: NewTabPageBackgroundButtonsView /// A gradient to display over background images to ensure visibility of /// the NTP contents and sponsored logo /// @@ -129,17 +129,21 @@ class NewTabPageViewController: UIViewController { private let notifications: NewTabPageNotifications private var cancellables: Set = [] + private let privateBrowsingManager: PrivateBrowsingManager init( tab: Tab, profile: Profile, dataSource: NTPDataSource, feedDataSource: FeedDataSource, - rewards: BraveRewards + rewards: BraveRewards, + privateBrowsingManager: PrivateBrowsingManager ) { self.tab = tab self.rewards = rewards self.feedDataSource = feedDataSource + self.privateBrowsingManager = privateBrowsingManager + self.backgroundButtonsView = NewTabPageBackgroundButtonsView(privateBrowsingManager: privateBrowsingManager) background = NewTabPageBackground(dataSource: dataSource) notifications = NewTabPageNotifications(rewards: rewards) collectionView = NewTabCollectionView(frame: .zero, collectionViewLayout: layout) @@ -149,12 +153,12 @@ class NewTabPageViewController: UIViewController { Preferences.NewTabPage.showNewTabFavourites.observe(from: self) sections = [ - StatsSectionProvider(openPrivacyHubPressed: { [weak self] in - if PrivateBrowsingManager.shared.isPrivateBrowsing { + StatsSectionProvider(isPrivateBrowsing: tab.isPrivate, openPrivacyHubPressed: { [weak self] in + if self?.privateBrowsingManager.isPrivateBrowsing == true { return } - let host = UIHostingController(rootView: PrivacyReportsManager.prepareView()) + let host = UIHostingController(rootView: PrivacyReportsManager.prepareView(isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing)) host.rootView.onDismiss = { [weak self] in DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { guard let self = self else { return } @@ -183,7 +187,7 @@ class NewTabPageViewController: UIViewController { self?.handleFavoriteAction(favorite: bookmark, action: action) }, legacyLongPressAction: { [weak self] alertController in self?.present(alertController, animated: true) - }), + }, isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing), FavoritesOverflowSectionProvider(action: { [weak self] in self?.delegate?.focusURLBar() }), @@ -194,7 +198,7 @@ class NewTabPageViewController: UIViewController { sections.insert(NTPDefaultBrowserCalloutProvider(), at: 0) } - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !privateBrowsingManager.isPrivateBrowsing { sections.append( BraveNewsSectionProvider( dataSource: feedDataSource, @@ -467,7 +471,7 @@ class NewTabPageViewController: UIViewController { } private func presentNotification() { - if PrivateBrowsingManager.shared.isPrivateBrowsing || notificationShowing { + if privateBrowsingManager.isPrivateBrowsing || notificationShowing { return } @@ -882,7 +886,7 @@ extension NewTabPageViewController: PreferencesObserver { // MARK: - UIScrollViewDelegate extension NewTabPageViewController { var isBraveNewsVisible: Bool { - return !PrivateBrowsingManager.shared.isPrivateBrowsing && (Preferences.BraveNews.isEnabled.value || Preferences.BraveNews.isShowingOptIn.value) + return !privateBrowsingManager.isPrivateBrowsing && (Preferences.BraveNews.isEnabled.value || Preferences.BraveNews.isShowingOptIn.value) } func scrollViewDidScroll(_ scrollView: UIScrollView) { diff --git a/Sources/Brave/Frontend/Browser/New Tab Page/Sections/BraveNewsSectionProvider.swift b/Sources/Brave/Frontend/Browser/New Tab Page/Sections/BraveNewsSectionProvider.swift index fa5263d7266..4af7eca05ae 100644 --- a/Sources/Brave/Frontend/Browser/New Tab Page/Sections/BraveNewsSectionProvider.swift +++ b/Sources/Brave/Frontend/Browser/New Tab Page/Sections/BraveNewsSectionProvider.swift @@ -433,7 +433,7 @@ class BraveNewsSectionProvider: NSObject, NTPObservableSectionProvider { openInNewTab, // Brave News is only available in normal tabs, so this isn't technically required // but good to be on the safe side - !PrivateBrowsingManager.shared.isPrivateBrowsing ? openInNewPrivateTab : nil, + openInNewPrivateTab, ].compactMap { $0 } let children: [UIMenu] = [ UIMenu(title: "", options: [.displayInline], children: openActions) @@ -490,7 +490,7 @@ class BraveNewsSectionProvider: NSObject, NTPObservableSectionProvider { openInNewTab, // Brave News is only available in normal tabs, so this isn't technically required // but good to be on the safe side - !PrivateBrowsingManager.shared.isPrivateBrowsing ? openInNewPrivateTab : nil, + openInNewPrivateTab, ].compactMap({ $0 }) let manageActions = [ diff --git a/Sources/Brave/Frontend/Browser/New Tab Page/Sections/FavoritesSectionProvider.swift b/Sources/Brave/Frontend/Browser/New Tab Page/Sections/FavoritesSectionProvider.swift index 5de238af39c..b61d180166f 100644 --- a/Sources/Brave/Frontend/Browser/New Tab Page/Sections/FavoritesSectionProvider.swift +++ b/Sources/Brave/Frontend/Browser/New Tab Page/Sections/FavoritesSectionProvider.swift @@ -21,6 +21,8 @@ class FavoritesSectionProvider: NSObject, NTPObservableSectionProvider { var sectionDidChange: (() -> Void)? var action: (Favorite, BookmarksAction) -> Void var legacyLongPressAction: (UIAlertController) -> Void + + private let isPrivateBrowsing: Bool var hasMoreThanOneFavouriteItems: Bool { frc.fetchedObjects?.count ?? 0 > 0 @@ -30,10 +32,12 @@ class FavoritesSectionProvider: NSObject, NTPObservableSectionProvider { init( action: @escaping (Favorite, BookmarksAction) -> Void, - legacyLongPressAction: @escaping (UIAlertController) -> Void + legacyLongPressAction: @escaping (UIAlertController) -> Void, + isPrivateBrowsing: Bool ) { self.action = action self.legacyLongPressAction = legacyLongPressAction + self.isPrivateBrowsing = isPrivateBrowsing frc = Favorite.frc() super.init() @@ -110,7 +114,7 @@ class FavoritesSectionProvider: NSObject, NTPObservableSectionProvider { cell.imageView.cancelLoading() if let url = fav.url?.asURL { - cell.imageView.loadFavicon(siteURL: url) + cell.imageView.loadFavicon(siteURL: url, isPrivateBrowsing: isPrivateBrowsing) } cell.accessibilityLabel = cell.textLabel.text } @@ -174,7 +178,7 @@ class FavoritesSectionProvider: NSObject, NTPObservableSectionProvider { }) var urlChildren: [UIAction] = [openInNewTab] - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !self.isPrivateBrowsing { let openInNewPrivateTab = UIAction( title: Strings.openNewPrivateTabButtonTitle, handler: UIAction.deferredActionHandler { _ in diff --git a/Sources/Brave/Frontend/Browser/New Tab Page/Sections/StatsSectionProvider.swift b/Sources/Brave/Frontend/Browser/New Tab Page/Sections/StatsSectionProvider.swift index e6539d70c6b..0e1443fcc9f 100644 --- a/Sources/Brave/Frontend/Browser/New Tab Page/Sections/StatsSectionProvider.swift +++ b/Sources/Brave/Frontend/Browser/New Tab Page/Sections/StatsSectionProvider.swift @@ -12,10 +12,12 @@ import BraveUI import UIKit class StatsSectionProvider: NSObject, NTPSectionProvider { + private let isPrivateBrowsing: Bool var openPrivacyHubPressed: () -> Void var hidePrivacyHubPressed: () -> Void - init(openPrivacyHubPressed: @escaping () -> Void, hidePrivacyHubPressed: @escaping () -> Void) { + init(isPrivateBrowsing: Bool, openPrivacyHubPressed: @escaping () -> Void, hidePrivacyHubPressed: @escaping () -> Void) { + self.isPrivateBrowsing = isPrivateBrowsing self.openPrivacyHubPressed = openPrivacyHubPressed self.hidePrivacyHubPressed = hidePrivacyHubPressed } @@ -48,6 +50,7 @@ class StatsSectionProvider: NSObject, NTPSectionProvider { let longPress = UILongPressGestureRecognizer(target: self, action: #selector(tappedButton(_:))) cell.view.do { + $0.isPrivateBrowsing = self.isPrivateBrowsing $0.addGestureRecognizer(tap) $0.addGestureRecognizer(longPress) @@ -147,45 +150,10 @@ class BraveShieldStatsView: SpringButton { }() } + private let background = UIView() + override init(frame: CGRect) { - super.init(frame: frame) - - if !PrivateBrowsingManager.shared.isPrivateBrowsing, Preferences.NewTabPage.showNewTabPrivacyHub.value { - let background = UIView() - background.backgroundColor = .init(white: 0, alpha: 0.25) - background.layer.cornerRadius = 12 - background.layer.cornerCurve = .continuous - background.isUserInteractionEnabled = false - insertSubview(background, at: 0) - background.snp.makeConstraints { - $0.edges.equalToSuperview() - } - - let settingsButton = BraveButton(type: .system).then { - $0.setImage(UIImage(named: "privacy_reports_3dots", in: .module, compatibleWith: nil)!.template, for: .normal) - $0.tintColor = .white - $0.hitTestSlop = UIEdgeInsets(equalInset: -25) - } - - let hidePrivacyHub = UIAction( - title: Strings.PrivacyHub.hidePrivacyHubWidgetActionTitle, - image: UIImage(braveSystemNamed: "leo.eye.off"), - handler: UIAction.deferredActionHandler { [unowned self] _ in - self.hidePrivacyHubPressed?() - }) - - let showPrivacyHub = UIAction( - title: Strings.PrivacyHub.openPrivacyHubWidgetActionTitle, - image: UIImage(named: "privacy_reports_shield", in: .module, compatibleWith: nil)?.template, - handler: UIAction.deferredActionHandler { [unowned self] _ in - self.openPrivacyHubPressed?() - }) - - settingsButton.menu = UIMenu(title: "", options: .displayInline, children: [hidePrivacyHub, showPrivacyHub]) - settingsButton.showsMenuAsPrimaryAction = true - - topStackView.addStackViewItems(.view(privacyReportLabel), .view(settingsButton)) - } + super.init(frame: .zero) statsStackView.addStackViewItems(.view(adsStatView), .view(dataSavedStatView), .view(timeStatView)) contentStackView.addStackViewItems(.view(topStackView), .view(statsStackView)) @@ -205,6 +173,59 @@ class BraveShieldStatsView: SpringButton { fatalError("init(coder:) has not been implemented") } + private var _isPrivateBrowsing: Bool = false + + var isPrivateBrowsing: Bool = false { + didSet { + if _isPrivateBrowsing == isPrivateBrowsing { + return + } + + _isPrivateBrowsing = isPrivateBrowsing + + if isPrivateBrowsing { + background.removeFromSuperview() + topStackView.arrangedSubviews.forEach { + $0.removeFromSuperview() + } + } else if Preferences.NewTabPage.showNewTabPrivacyHub.value { + background.backgroundColor = .init(white: 0, alpha: 0.25) + background.layer.cornerRadius = 12 + background.layer.cornerCurve = .continuous + background.isUserInteractionEnabled = false + insertSubview(background, at: 0) + background.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + let settingsButton = BraveButton(type: .system).then { + $0.setImage(UIImage(named: "privacy_reports_3dots", in: .module, compatibleWith: nil)!.template, for: .normal) + $0.tintColor = .white + $0.hitTestSlop = UIEdgeInsets(equalInset: -25) + } + + let hidePrivacyHub = UIAction( + title: Strings.PrivacyHub.hidePrivacyHubWidgetActionTitle, + image: UIImage(braveSystemNamed: "leo.eye.off"), + handler: UIAction.deferredActionHandler { [unowned self] _ in + self.hidePrivacyHubPressed?() + }) + + let showPrivacyHub = UIAction( + title: Strings.PrivacyHub.openPrivacyHubWidgetActionTitle, + image: UIImage(named: "privacy_reports_shield", in: .module, compatibleWith: nil)?.template, + handler: UIAction.deferredActionHandler { [unowned self] _ in + self.openPrivacyHubPressed?() + }) + + settingsButton.menu = UIMenu(title: "", options: .displayInline, children: [hidePrivacyHub, showPrivacyHub]) + settingsButton.showsMenuAsPrimaryAction = true + + topStackView.addStackViewItems(.view(privacyReportLabel), .view(settingsButton)) + } + } + } + deinit { NotificationCenter.default.removeObserver(self) } diff --git a/Sources/Brave/Frontend/Browser/PageZoom/PageZoomView.swift b/Sources/Brave/Frontend/Browser/PageZoom/PageZoomView.swift index 89e7138cbe8..4b02a9cea09 100644 --- a/Sources/Brave/Frontend/Browser/PageZoom/PageZoomView.swift +++ b/Sources/Brave/Frontend/Browser/PageZoom/PageZoomView.swift @@ -13,6 +13,7 @@ import Preferences private struct ZoomView: View { @ScaledMetric private var buttonWidth = 44.0 + var isPrivateBrowsing: Bool var minValue: Double var maxValue: Double @Binding var value: Double @@ -62,12 +63,12 @@ private struct ZoomView: View { Button(action: onReset) { Text(NSNumber(value: value), formatter: PageZoomView.percentFormatter) .font(.system(.footnote).weight(.medium)) - .foregroundColor((value == (PrivateBrowsingManager.shared.isPrivateBrowsing ? 1.0 : Preferences.General.defaultPageZoomLevel.value)) ? .accentColor : Color(UIColor.braveLabel)) + .foregroundColor((value == (isPrivateBrowsing ? 1.0 : Preferences.General.defaultPageZoomLevel.value)) ? .accentColor : Color(UIColor.braveLabel)) .padding() .contentShape(Rectangle()) } .fixedSize(horizontal: true, vertical: false) - .disabled(value == (PrivateBrowsingManager.shared.isPrivateBrowsing ? 1.0 : Preferences.General.defaultPageZoomLevel.value)) + .disabled(value == (isPrivateBrowsing ? 1.0 : Preferences.General.defaultPageZoomLevel.value)) } } @@ -75,6 +76,7 @@ struct PageZoomView: View { @Environment(\.managedObjectContext) private var context private var webView: WKWebView? + private let isPrivateBrowsing: Bool @State private var minValue = 0.5 @State private var maxValue = 3.0 @State private var currentValue: Double @@ -97,11 +99,12 @@ struct PageZoomView: View { var dismiss: (() -> Void)? - init(webView: WKWebView?) { + init(webView: WKWebView?, isPrivateBrowsing: Bool) { self.webView = webView + self.isPrivateBrowsing = isPrivateBrowsing // Private Browsing on Safari iOS always defaults to 100%, and isn't persistently saved. - if PrivateBrowsingManager.shared.isPrivateBrowsing { + if isPrivateBrowsing { _currentValue = State(initialValue: 1.0) return } @@ -126,6 +129,7 @@ struct PageZoomView: View { .frame(maxWidth: .infinity, alignment: .leading) ZoomView( + isPrivateBrowsing: isPrivateBrowsing, minValue: minValue, maxValue: maxValue, value: $currentValue, @@ -156,7 +160,7 @@ struct PageZoomView: View { webView.setValue(currentValue, forKey: PageZoomView.propertyName) // Do NOT store the changes in the Domain - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !isPrivateBrowsing { let domain = Domain.getPersistedDomain(for: url)?.then { $0.zoom_level = currentValue == $0.zoom_level?.doubleValue ? nil : NSNumber(value: currentValue) } @@ -188,7 +192,7 @@ struct PageZoomView: View { #if DEBUG struct PageZoomView_Previews: PreviewProvider { static var previews: some View { - PageZoomView(webView: nil) + PageZoomView(webView: nil, isPrivateBrowsing: false) .previewLayout(PreviewLayout.sizeThatFits) } } diff --git a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistDetailViewController.swift b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistDetailViewController.swift index 45c4c95d0d6..c40aa4ee348 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistDetailViewController.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistDetailViewController.swift @@ -12,8 +12,18 @@ import UIKit class PlaylistDetailViewController: UIViewController, UIGestureRecognizerDelegate { private var playerView: VideoView? + private let isPrivateBrowsing: Bool weak var delegate: PlaylistViewControllerDelegate? - + + init(isPrivateBrowsing: Bool) { + self.isPrivateBrowsing = isPrivateBrowsing + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() @@ -147,11 +157,10 @@ extension PlaylistDetailViewController { if let url = URL(string: item.pageSrc) { self.dismiss(animated: true, completion: nil) - - let isPrivateBrowsing = PrivateBrowsingManager.shared.isPrivateBrowsing + self.delegate?.openURLInNewTab( url, - isPrivate: isPrivateBrowsing, + isPrivate: self.isPrivateBrowsing, isPrivileged: false) } })) diff --git a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistListViewController+TableViewDelegate.swift b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistListViewController+TableViewDelegate.swift index 76dbdf91fa6..c0f6d3af14e 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 = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateBrowsing = self.privateBrowsingManager?.isPrivateBrowsing == true 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 = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateBrowsing = self.privateBrowsingManager?.isPrivateBrowsing == true 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 22f595c8fc6..ccf63578aa3 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistListViewController.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistListViewController.swift @@ -48,6 +48,8 @@ class PlaylistListViewController: UIViewController { weak var delegate: PlaylistViewControllerDelegate? private let playerView: VideoView + let privateBrowsingManager: PrivateBrowsingManager? + private var observers = Set() private var folderObserver: AnyCancellable? private var sharedFolderLoadingTask: Task? @@ -72,8 +74,9 @@ class PlaylistListViewController: UIViewController { $0.allowsSelectionDuringEditing = true } - init(playerView: VideoView) { + init(playerView: VideoView, privateBrowsingManager: PrivateBrowsingManager?) { self.playerView = playerView + self.privateBrowsingManager = privateBrowsingManager super.init(nibName: nil, bundle: nil) } @@ -638,7 +641,7 @@ extension PlaylistListViewController { activityIndicator.isHidden = false let selectedCell = tableView.cellForRow(at: indexPath) as? PlaylistCell - playerView.setVideoInfo(videoDomain: item.pageSrc, videoTitle: item.pageTitle) + playerView.setVideoInfo(videoDomain: item.pageSrc, videoTitle: item.pageTitle, isPrivateBrowsing: privateBrowsingManager?.isPrivateBrowsing == true) PlaylistMediaStreamer.setNowPlayingMediaArtwork(image: selectedCell?.iconView.image) completion?(item) } @@ -753,7 +756,7 @@ extension PlaylistListViewController { if let url = URL(string: item.pageSrc) { self.dismiss(animated: true, completion: nil) - let isPrivateBrowsing = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateBrowsing = self.privateBrowsingManager?.isPrivateBrowsing == true self.delegate?.openURLInNewTab( url, isPrivate: isPrivateBrowsing, @@ -826,7 +829,7 @@ extension PlaylistListViewController { let pageURL = URL(string: currentItem.pageSrc) { self.dismiss(animated: true) { - let isPrivateBrowsing = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateBrowsing = self.privateBrowsingManager?.isPrivateBrowsing == true browser.tabManager.addTabAndSelect( URLRequest(url: pageURL), isPrivate: isPrivateBrowsing) diff --git a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistNewFolderView.swift b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistNewFolderView.swift index ea8d363503d..c13fae364e8 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistNewFolderView.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistNewFolderView.swift @@ -41,7 +41,7 @@ class PlaylistFolderImageLoader: ObservableObject { func load(domainUrl: URL) { faviconTask?.cancel() faviconTask = Task { @MainActor in - let favicon = try await FaviconFetcher.loadIcon(url: domainUrl, persistent: !PrivateBrowsingManager.shared.isPrivateBrowsing) + let favicon = try await FaviconFetcher.loadIcon(url: domainUrl, persistent: true) self.image = favicon.image } } diff --git a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift index e87c085c5f6..199dbe8ac4b 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift @@ -49,11 +49,12 @@ class PlaylistViewController: UIViewController { private let player: MediaPlayer private let playerView = VideoView() private let mediaStreamer: PlaylistMediaStreamer + private let privateBrowsingManager: PrivateBrowsingManager? private let splitController = UISplitViewController() private let folderController = PlaylistFolderController() - private lazy var listController = PlaylistListViewController(playerView: playerView) - private let detailController = PlaylistDetailViewController() + private lazy var listController = PlaylistListViewController(playerView: playerView, privateBrowsingManager: privateBrowsingManager) + private lazy var detailController = PlaylistDetailViewController(isPrivateBrowsing: privateBrowsingManager?.isPrivateBrowsing == true) private var folderObserver: AnyCancellable? private var playerStateObservers = Set() @@ -68,14 +69,17 @@ class PlaylistViewController: UIViewController { profile: Profile?, mediaPlayer: MediaPlayer, initialItem: PlaylistInfo?, - initialItemPlaybackOffset: Double + initialItemPlaybackOffset: Double, + privateBrowsingManager: PrivateBrowsingManager? ) { self.openInNewTab = openInNewTab self.openPlaylistSettingsMenu = openPlaylistSettingsMenu self.player = mediaPlayer self.mediaStreamer = PlaylistMediaStreamer(playerView: playerView) + self.privateBrowsingManager = privateBrowsingManager self.folderSharingUrl = nil + super.init(nibName: nil, bundle: nil) listController.initialItem = initialItem @@ -309,7 +313,7 @@ class PlaylistViewController: UIViewController { } if let item = PlaylistCarplayManager.shared.currentPlaylistItem { - playerView.setVideoInfo(videoDomain: item.pageSrc, videoTitle: item.pageTitle) + playerView.setVideoInfo(videoDomain: item.pageSrc, videoTitle: item.pageTitle, isPrivateBrowsing: privateBrowsingManager?.isPrivateBrowsing == true) } else { playerView.resetVideoInfo() } @@ -326,7 +330,8 @@ class PlaylistViewController: UIViewController { } else if let item = PlaylistCarplayManager.shared.currentPlaylistItem { self.playerView.setVideoInfo( videoDomain: item.pageSrc, - videoTitle: item.pageTitle) + videoTitle: item.pageTitle, + isPrivateBrowsing: self.privateBrowsingManager?.isPrivateBrowsing == true) } self.listController.highlightActiveItem() @@ -908,7 +913,8 @@ extension PlaylistViewController: VideoViewDelegate { PlaylistMediaStreamer.clearNowPlayingInfo() self.playerView.setVideoInfo( videoDomain: item.pageSrc, - videoTitle: item.pageTitle) + videoTitle: item.pageTitle, + isPrivateBrowsing: self.privateBrowsingManager?.isPrivateBrowsing == true) PlaylistMediaStreamer.setNowPlayingInfo(item, withPlayer: self.player) } catch { PlaylistMediaStreamer.clearNowPlayingInfo() @@ -955,7 +961,8 @@ extension PlaylistViewController: VideoViewDelegate { playerView.setVideoInfo( videoDomain: item.pageSrc, - videoTitle: item.pageTitle) + videoTitle: item.pageTitle, + isPrivateBrowsing: privateBrowsingManager?.isPrivateBrowsing == true) PlaylistMediaStreamer.setNowPlayingInfo(item, withPlayer: self.player) return item diff --git a/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCacheLoader.swift b/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCacheLoader.swift index b5d27cc0038..093ba2698e5 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCacheLoader.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCacheLoader.swift @@ -554,8 +554,7 @@ extension PlaylistWebLoader: WKNavigationDelegate { tab.currentPageData = PageData(mainFrameURL: mainDocumentURL) } - let isPrivateBrowsing = PrivateBrowsingManager.shared.isPrivateBrowsing - let domainForMainFrame = Domain.getOrCreate(forUrl: mainDocumentURL, persistent: !isPrivateBrowsing) + let domainForMainFrame = Domain.getOrCreate(forUrl: mainDocumentURL, persistent: false) if let requestURL = navigationAction.request.url, let targetFrame = navigationAction.targetFrame { diff --git a/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift b/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift index 6fbed239f98..35583f27bc0 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift @@ -24,7 +24,7 @@ public class PlaylistCarplayManager: NSObject { private var carplaySessionConfiguration: CPSessionConfiguration? let onCarplayUIChangedToRoot = PassthroughSubject() - public var browserController: BrowserViewController? + public weak var browserController: BrowserViewController? var currentlyPlayingItemIndex = -1 var currentPlaylistItem: PlaylistInfo? @@ -72,23 +72,13 @@ public class PlaylistCarplayManager: NSObject { // Setup Playlist Download Resume Session PlaylistManager.shared.restoreSession() - // REFACTOR to support multiple windows. - // OR find a way to get WebKit to load `Youtube` and other sites WITHOUT having to be in the view hierarchy.. - var currentWindow = browserController?.view.window - - // BrowserController can actually be null when CarPlay is launched by the system - // The only such window that will be available is the actual CarPlay window - // So that's the window we need to use - // This does NOT break Multi-Window because we don't actually have multiple windows running - // when there is no browser controller. There is only a single CarPlay window. - if currentWindow == nil { - currentWindow = - UIApplication.shared.connectedScenes - .filter({ $0.activationState == .foregroundActive }) - .compactMap({ $0 as? UIWindowScene }) - .first?.windows - .filter({ $0.isKeyWindow }).first - } + // REFACTOR to find a way to get WebKit to load `Youtube` and other sites WITHOUT having to be in the view hierarchy.. + let currentWindow = + UIApplication.shared.connectedScenes + .filter({ $0.activationState == .foregroundActive }) + .compactMap({ $0 as? UIWindowScene }) + .first?.windows + .filter({ $0.isKeyWindow }).first // If there is no media player, create one, // pass it to the car-play controller @@ -125,7 +115,8 @@ public class PlaylistCarplayManager: NSObject { profile: browserController?.profile, mediaPlayer: mediaPlayer, initialItem: initialItem, - initialItemPlaybackOffset: initialItemPlaybackOffset) + initialItemPlaybackOffset: initialItemPlaybackOffset, + privateBrowsingManager: browserController?.privateBrowsingManager) self.mediaPlayer = mediaPlayer return playlistController } diff --git a/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistManager.swift b/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistManager.swift index b5327638417..77e58b0c43b 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistManager.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistManager.swift @@ -253,8 +253,6 @@ public class PlaylistManager: NSObject { public func restoreSession() { if !didRestoreSession { - didRestoreSession = true - downloadManager.restoreSession() { [weak self] in self?.reloadData() } diff --git a/Sources/Brave/Frontend/Browser/Playlist/Utilities/PlaylistThumbnailUtility.swift b/Sources/Brave/Frontend/Browser/Playlist/Utilities/PlaylistThumbnailUtility.swift index 45c8ac38574..1540cddb98b 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Utilities/PlaylistThumbnailUtility.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Utilities/PlaylistThumbnailUtility.swift @@ -150,7 +150,7 @@ public class PlaylistThumbnailRenderer { favIconGenerator = Task { @MainActor in do { - let favicon = try await FaviconFetcher.loadIcon(url: url, persistent: !PrivateBrowsingManager.shared.isPrivateBrowsing) + let favicon = try await FaviconFetcher.loadIcon(url: url, persistent: true) await SDImageCache.shared.store(favicon.image, forKey: url.absoluteString) completion(favicon.image) } catch { diff --git a/Sources/Brave/Frontend/Browser/Playlist/VideoPlayer/UI/VideoPlayer.swift b/Sources/Brave/Frontend/Browser/Playlist/VideoPlayer/UI/VideoPlayer.swift index 0bed804aedf..23139986c02 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/VideoPlayer/UI/VideoPlayer.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/VideoPlayer/UI/VideoPlayer.swift @@ -574,7 +574,7 @@ class VideoView: UIView, VideoTrackerBarDelegate { staticImageView.contentMode = .scaleAspectFit } - func setVideoInfo(videoDomain: String, videoTitle: String?) { + func setVideoInfo(videoDomain: String, videoTitle: String?, isPrivateBrowsing: Bool) { var displayTitle = videoTitle ?? "" if displayTitle.isEmpty { @@ -596,7 +596,7 @@ class VideoView: UIView, VideoTrackerBarDelegate { } infoView.titleLabel.text = displayTitle - infoView.updateFavIcon(domain: videoDomain) + infoView.updateFavIcon(domain: videoDomain, isPrivateBrowsing: isPrivateBrowsing) } func resetVideoInfo() { diff --git a/Sources/Brave/Frontend/Browser/Playlist/VideoPlayer/UI/VideoPlayerInfoBar.swift b/Sources/Brave/Frontend/Browser/Playlist/VideoPlayer/UI/VideoPlayerInfoBar.swift index 3b83a3ce0bf..f144ad03bc3 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/VideoPlayer/UI/VideoPlayerInfoBar.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/VideoPlayer/UI/VideoPlayerInfoBar.swift @@ -108,14 +108,14 @@ class VideoPlayerInfoBar: UIView { fatalError("init(coder:) has not been implemented") } - func updateFavIcon(domain: String) { + func updateFavIcon(domain: String, isPrivateBrowsing: Bool) { favIconImageView.cancelFaviconLoad() favIconImageView.clearMonogramFavicon() favIconImageView.contentMode = .scaleAspectFit favIconImageView.image = Favicon.defaultImage if let url = URL(string: domain) { - favIconImageView.loadFavicon(for: url) + favIconImageView.loadFavicon(for: url, isPrivateBrowsing: isPrivateBrowsing) } } diff --git a/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyHubAllTimeSection.swift b/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyHubAllTimeSection.swift index 2b99f6aa9fe..2b84039014c 100644 --- a/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyHubAllTimeSection.swift +++ b/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyHubAllTimeSection.swift @@ -21,6 +21,7 @@ extension PrivacyReportsView { @State private var mostFrequentTrackerLoading = true @State private var riskiestWebsiteLoading = true + private(set) var isPrivateBrowsing: Bool private(set) var onDismiss: () -> Void private func allTimeItemView(trackerOrWebsite: CountableEntity?, countableLabel: String, header: String) -> some View { @@ -87,7 +88,7 @@ extension PrivacyReportsView { } NavigationLink( - destination: PrivacyReportAllTimeListsView(onDismiss: onDismiss) + destination: PrivacyReportAllTimeListsView(isPrivateBrowsing: isPrivateBrowsing, onDismiss: onDismiss) ) { HStack { Text(Strings.PrivacyHub.allTimeListsButtonText) @@ -122,7 +123,7 @@ extension PrivacyReportsView { #if DEBUG struct PrivacyHubAllTimeSection_Previews: PreviewProvider { static var previews: some View { - PrivacyReportsView.PrivacyHubAllTimeSection(onDismiss: {}) + PrivacyReportsView.PrivacyHubAllTimeSection(isPrivateBrowsing: false, onDismiss: {}) } } #endif diff --git a/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportAllTimeListsView.swift b/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportAllTimeListsView.swift index d08a5b221ce..597815e7ea4 100644 --- a/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportAllTimeListsView.swift +++ b/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportAllTimeListsView.swift @@ -18,6 +18,7 @@ struct PrivacyReportAllTimeListsView: View { @State private var trackersLoading = true @State private var websitesLoading = true + private(set) var isPrivateBrowsing: Bool private(set) var onDismiss: () -> Void enum Page: String, CaseIterable, Identifiable { @@ -126,7 +127,7 @@ struct PrivacyReportAllTimeListsView: View { Section { ForEach(websites) { item in HStack { - FaviconImage(url: item.faviconUrl) + FaviconImage(url: item.faviconUrl, isPrivateBrowsing: isPrivateBrowsing) Text(item.domain) Spacer() Text("\(item.count)") @@ -199,8 +200,8 @@ struct PrivacyReportAllTimeListsView: View { struct PrivacyReportAllTimeListsView_Previews: PreviewProvider { static var previews: some View { Group { - PrivacyReportAllTimeListsView(onDismiss: {}) - PrivacyReportAllTimeListsView(onDismiss: {}) + PrivacyReportAllTimeListsView(isPrivateBrowsing: false, onDismiss: {}) + PrivacyReportAllTimeListsView(isPrivateBrowsing: false, onDismiss: {}) .preferredColorScheme(.dark) } } diff --git a/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportsManager.swift b/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportsManager.swift index 235222dd33c..5d583feb08d 100644 --- a/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportsManager.swift +++ b/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportsManager.swift @@ -19,8 +19,8 @@ public struct PrivacyReportsManager { /// Instead a periodic timer is run and all requests gathered during this timeframe are saved in one database transaction. public static var pendingBlockedRequests: [(host: String, domain: URL, date: Date)] = [] - public static func processBlockedRequests() { - if PrivateBrowsingManager.shared.isPrivateBrowsing { return } + public static func processBlockedRequests(isPrivateBrowsing: Bool) { + if isPrivateBrowsing { return } let itemsToSave = pendingBlockedRequests pendingBlockedRequests.removeAll() @@ -35,13 +35,13 @@ public struct PrivacyReportsManager { private static var saveBlockedResourcesTimer: Timer? private static var vpnAlertsTimer: Timer? - public static func scheduleProcessingBlockedRequests() { + public static func scheduleProcessingBlockedRequests(isPrivateBrowsing: Bool) { saveBlockedResourcesTimer?.invalidate() let timeInterval = AppConstants.buildChannel.isPublic ? 60.0 : 10.0 saveBlockedResourcesTimer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: true) { _ in - processBlockedRequests() + processBlockedRequests(isPrivateBrowsing: isPrivateBrowsing) } } @@ -80,9 +80,9 @@ public struct PrivacyReportsManager { // MARK: - View /// Fetches required data to present the privacy reports view and returns the view. - static func prepareView() -> PrivacyReportsView { + static func prepareView(isPrivateBrowsing: Bool) -> PrivacyReportsView { let last = BraveVPNAlert.last(3) - let view = PrivacyReportsView(lastVPNAlerts: last) + let view = PrivacyReportsView(lastVPNAlerts: last, isPrivateBrowsing: isPrivateBrowsing) Preferences.PrivacyReports.ntpOnboardingCompleted.value = true diff --git a/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportsView.swift b/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportsView.swift index a5aabf188ba..0dade80a4b8 100644 --- a/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportsView.swift +++ b/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportsView.swift @@ -13,6 +13,7 @@ struct PrivacyReportsView: View { let lastVPNAlerts: [BraveVPNAlert]? + private(set) var isPrivateBrowsing: Bool var onDismiss: (() -> Void)? var openPrivacyReportsUrl: (() -> Void)? @@ -97,7 +98,7 @@ struct PrivacyReportsView: View { Divider() } - PrivacyHubAllTimeSection(onDismiss: dismissView) + PrivacyHubAllTimeSection(isPrivateBrowsing: isPrivateBrowsing, onDismiss: dismissView) VStack { Text(Strings.PrivacyHub.privacyReportsDisclaimer) @@ -142,9 +143,9 @@ struct PrivacyReports_Previews: PreviewProvider { static var previews: some View { Group { - PrivacyReportsView(lastVPNAlerts: nil) + PrivacyReportsView(lastVPNAlerts: nil, isPrivateBrowsing: false) - PrivacyReportsView(lastVPNAlerts: nil) + PrivacyReportsView(lastVPNAlerts: nil, isPrivateBrowsing: false) .preferredColorScheme(.dark) } } diff --git a/Sources/Brave/Frontend/Browser/PrivacyProtection/PrivateBrowsingManager.swift b/Sources/Brave/Frontend/Browser/PrivacyProtection/PrivateBrowsingManager.swift index 1020d7f7cfa..d5bf540fb68 100644 --- a/Sources/Brave/Frontend/Browser/PrivacyProtection/PrivateBrowsingManager.swift +++ b/Sources/Brave/Frontend/Browser/PrivacyProtection/PrivateBrowsingManager.swift @@ -7,6 +7,8 @@ import Data import Combine public final class PrivateBrowsingManager: ObservableObject { + + public init() {} @Published public var isPrivateBrowsing = false { didSet { @@ -17,6 +19,4 @@ public final class PrivateBrowsingManager: ObservableObject { } } } - - public static let shared = PrivateBrowsingManager() } diff --git a/Sources/Brave/Frontend/Browser/Search/SearchEngines.swift b/Sources/Brave/Frontend/Browser/Search/SearchEngines.swift index 01877f39744..8d318719aed 100644 --- a/Sources/Brave/Frontend/Browser/Search/SearchEngines.swift +++ b/Sources/Brave/Frontend/Browser/Search/SearchEngines.swift @@ -77,9 +77,7 @@ public class SearchEngines { } /// If no engine type is specified this method returns search engine for regular browsing. - func defaultEngine(forType type: DefaultEngineType? = nil) -> OpenSearchEngine { - let engineType = type ?? (PrivateBrowsingManager.shared.isPrivateBrowsing ? .privateMode : .standard) - + func defaultEngine(forType engineType: DefaultEngineType) -> OpenSearchEngine { if let name = engineType.option.value, let defaultEngine = orderedEngines.first(where: { $0.engineID == name || $0.shortName == name }) { return defaultEngine @@ -141,7 +139,7 @@ public class SearchEngines { } } - func isEngineDefault(_ engine: OpenSearchEngine, type: DefaultEngineType? = nil) -> Bool { + func isEngineDefault(_ engine: OpenSearchEngine, type: DefaultEngineType) -> Bool { return defaultEngine(forType: type).shortName == engine.shortName } @@ -192,8 +190,8 @@ public class SearchEngines { disabledEngineNames.removeValue(forKey: engine.shortName) } - func disableEngine(_ engine: OpenSearchEngine) { - if isEngineDefault(engine) { + func disableEngine(_ engine: OpenSearchEngine, isPrivateBrowsing: Bool) { + if isEngineDefault(engine, type: isPrivateBrowsing ? .privateMode : .standard) { // Can't disable default engine. return } @@ -232,8 +230,8 @@ public class SearchEngines { } } - func queryForSearchURL(_ url: URL?) -> String? { - return defaultEngine().queryForSearchURL(url) + func queryForSearchURL(_ url: URL?, forType engineType: DefaultEngineType) -> String? { + return defaultEngine(forType: engineType).queryForSearchURL(url) } fileprivate func getDisabledEngineNames() -> [String: Bool] { diff --git a/Sources/Brave/Frontend/Browser/Search/SearchSuggestionDataSource.swift b/Sources/Brave/Frontend/Browser/Search/SearchSuggestionDataSource.swift index 43530a5702e..9b6df7efa32 100644 --- a/Sources/Brave/Frontend/Browser/Search/SearchSuggestionDataSource.swift +++ b/Sources/Brave/Frontend/Browser/Search/SearchSuggestionDataSource.swift @@ -53,7 +53,7 @@ class SearchSuggestionDataSource { // In that case, we count it as if there are no quick suggestions to show // Unless Default Search Engine is different than Quick Search Engine var hasQuickSearchEngines: Bool { - let isDefaultEngineQuickEngine = searchEngines?.defaultEngine().engineID == quickSearchEngines.first?.engineID + let isDefaultEngineQuickEngine = searchEngines?.defaultEngine(forType: tabType == .private ? .privateMode : .standard).engineID == quickSearchEngines.first?.engineID if quickSearchEngines.count == 1 { return !isDefaultEngineQuickEngine @@ -82,7 +82,7 @@ class SearchSuggestionDataSource { var braveSearchPromotionAvailable: Bool { guard Preferences.Review.launchCount.value > 1, - searchEngines?.defaultEngine().shortName != OpenSearchEngine.EngineNames.brave, + searchEngines?.defaultEngine(forType: tabType == .private ? .privateMode : .standard).shortName != OpenSearchEngine.EngineNames.brave, let braveSearchPromotionLaunchDate = Preferences.BraveSearch.braveSearchPromotionLaunchDate.value, Preferences.BraveSearch.braveSearchPromotionCompletionState.value != BraveSearchPromotionState.dismissed.rawValue, Preferences.BraveSearch.braveSearchPromotionCompletionState.value != BraveSearchPromotionState.maybeLaterSameSession.rawValue else { @@ -167,7 +167,7 @@ class SearchSuggestionDataSource { // Show the default search engine first. if !tabType.isPrivate, let userAgent = SearchViewController.userAgent, - let engines = searchEngines?.defaultEngine() { + let engines = searchEngines?.defaultEngine(forType: tabType == .private ? .privateMode : .standard) { suggestClient = SearchSuggestClient(searchEngine: engines, userAgent: userAgent) } } diff --git a/Sources/Brave/Frontend/Browser/Search/SearchViewController.swift b/Sources/Brave/Frontend/Browser/Search/SearchViewController.swift index 8f29ebaa876..1017ee05499 100644 --- a/Sources/Brave/Frontend/Browser/Search/SearchViewController.swift +++ b/Sources/Brave/Frontend/Browser/Search/SearchViewController.swift @@ -333,7 +333,7 @@ public class SearchViewController: SiteTableViewController, LoaderListener { } private func submitSeachTemplateQuery(isBraveSearchPromotion: Bool) { - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !dataSource.tabType.isPrivate { RecentSearch.addItem(type: .text, text: dataSource.searchQuery, websiteUrl: nil) } searchDelegate?.searchViewController( @@ -359,7 +359,7 @@ public class SearchViewController: SiteTableViewController, LoaderListener { return } - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !dataSource.tabType.isPrivate { RecentSearch.addItem(type: .website, text: localSearchQuery, websiteUrl: url.absoluteString) } searchDelegate?.searchViewController(self, didSelectURL: url) @@ -381,7 +381,7 @@ public class SearchViewController: SiteTableViewController, LoaderListener { case .searchSuggestions: if !isBraveSearchPrompt(for: indexPath) { // Assume that only the default search engine can provide search suggestions. - let engine = dataSource.searchEngines?.defaultEngine() + let engine = dataSource.searchEngines?.defaultEngine(forType: dataSource.tabType == .private ? .privateMode : .standard) let suggestion = dataSource.suggestions[indexPath.row] var url = URIFixup.getURL(suggestion) @@ -390,7 +390,7 @@ public class SearchViewController: SiteTableViewController, LoaderListener { } if let url = url { - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !dataSource.tabType.isPrivate { RecentSearch.addItem(type: .website, text: suggestion, websiteUrl: url.absoluteString) } searchDelegate?.searchViewController(self, didSelectURL: url) @@ -441,7 +441,7 @@ public class SearchViewController: SiteTableViewController, LoaderListener { case .quickBar: return nil case .searchSuggestionsOptIn: return nil case .searchSuggestions: - if let defaultSearchEngine = dataSource.searchEngines?.defaultEngine() { + if let defaultSearchEngine = dataSource.searchEngines?.defaultEngine(forType: dataSource.tabType == .private ? .privateMode : .standard) { if defaultSearchEngine.shortName.contains(Strings.searchSuggestionSectionTitleNoSearchFormat) || defaultSearchEngine.shortName.lowercased().contains("search") { return defaultSearchEngine.displayName } @@ -622,7 +622,7 @@ public class SearchViewController: SiteTableViewController, LoaderListener { cell.imageView?.contentMode = .scaleAspectFit cell.imageView?.layer.borderColor = SearchViewControllerUX.iconBorderColor.cgColor cell.imageView?.layer.borderWidth = SearchViewControllerUX.iconBorderWidth - cell.imageView?.loadFavicon(for: site.tileURL) + cell.imageView?.loadFavicon(for: site.tileURL, isPrivateBrowsing: dataSource.tabType.isPrivate) cell.backgroundColor = .secondaryBraveBackground } diff --git a/Sources/Brave/Frontend/Browser/Tab.swift b/Sources/Brave/Frontend/Browser/Tab.swift index 30dcb4f59f2..f4c5ffdf967 100644 --- a/Sources/Brave/Frontend/Browser/Tab.swift +++ b/Sources/Brave/Frontend/Browser/Tab.swift @@ -555,7 +555,7 @@ class Tab: NSObject { /// In private browsing the URL is in memory but this is not the case for normal mode /// For Normal Mode Tab information is fetched using Tab ID from var fetchedURL: URL? { - if PrivateBrowsingManager.shared.isPrivateBrowsing { + if isPrivate { if let url = url, url.isWebPage() { return url } diff --git a/Sources/Brave/Frontend/Browser/TabManager.swift b/Sources/Brave/Frontend/Browser/TabManager.swift index e2cb9ccf62f..35f262f3637 100644 --- a/Sources/Brave/Frontend/Browser/TabManager.swift +++ b/Sources/Brave/Frontend/Browser/TabManager.swift @@ -92,6 +92,8 @@ class TabManager: NSObject { private let syncedTabsQueue = DispatchQueue(label: "synced-tabs-queue") private var syncTabsTask: DispatchWorkItem? private var metricsHeartbeat: Timer? + private let windowId: UUID + public let privateBrowsingManager: PrivateBrowsingManager /// The property returning only existing tab is NTP for current mode var isBrowserEmptyForCurrentMode: Bool { @@ -106,13 +108,15 @@ class TabManager: NSObject { } } - init(prefs: Prefs, rewards: BraveRewards?, tabGeneratorAPI: BraveTabGeneratorAPI?) { + init(windowId: UUID, prefs: Prefs, rewards: BraveRewards?, tabGeneratorAPI: BraveTabGeneratorAPI?, privateBrowsingManager: PrivateBrowsingManager) { assert(Thread.isMainThread) + self.windowId = windowId self.prefs = prefs self.navDelegate = TabManagerNavDelegate() self.rewards = rewards self.tabGeneratorAPI = tabGeneratorAPI + self.privateBrowsingManager = privateBrowsingManager self.tabEventHandlers = TabEventHandlers.create(with: prefs) super.init() @@ -192,7 +196,7 @@ class TabManager: NSObject { // What the users sees displayed based on current private browsing mode var tabsForCurrentMode: [Tab] { - let tabType: TabType = PrivateBrowsingManager.shared.isPrivateBrowsing ? .private : .regular + let tabType: TabType = privateBrowsingManager.isPrivateBrowsing ? .private : .regular return tabs(withType: tabType) } @@ -207,7 +211,7 @@ class TabManager: NSObject { func tabsForCurrentMode(for query: String? = nil) -> [Tab] { if let query = query { - let tabType: TabType = PrivateBrowsingManager.shared.isPrivateBrowsing ? .private : .regular + let tabType: TabType = privateBrowsingManager.isPrivateBrowsing ? .private : .regular return tabs(withType: tabType, query: query) } else { return tabsForCurrentMode @@ -299,11 +303,11 @@ class TabManager: NSObject { return } // Convert the global mode to private if opening private tab from normal tab/ history/bookmark. - if selectedTab?.isPrivate == false && tab?.isPrivate == true { - PrivateBrowsingManager.shared.isPrivateBrowsing = true + if selectedTab?.isPrivate != true && tab?.isPrivate == true { + 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() } @@ -356,7 +360,7 @@ class TabManager: NSObject { guard let newSelectedTab = tab, let previousTab = previous, let newTabUrl = newSelectedTab.url, let previousTabUrl = previousTab.url else { return } - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !privateBrowsingManager.isPrivateBrowsing { if previousTab.displayFavicon == nil { adsRewardsLog.warning("No favicon found in \(previousTab) to report to rewards panel") } @@ -378,7 +382,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() + } } } @@ -495,7 +501,7 @@ class TabManager: NSObject { } private func saveTabOrder() { - if PrivateBrowsingManager.shared.isPrivateBrowsing { return } + if Preferences.Privacy.privateBrowsingOnly.value || (privateBrowsingManager.isPrivateBrowsing && !Preferences.Privacy.persistentPrivateBrowsing.value) { return } let allTabIds = allTabs.compactMap { $0.id } SessionTab.saveTabOrder(tabIds: allTabIds) } @@ -504,10 +510,14 @@ class TabManager: NSObject { assert(Thread.isMainThread) let isPrivate = tab.type == .private - if !isPrivate { - SessionTab.createIfNeeded(tabId: tab.id, + 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) } @@ -539,7 +549,7 @@ class TabManager: NSObject { } // Ignore on restore. - if flushToDisk && !zombie && !isPrivate { + if flushToDisk && !zombie && isPersistentTab { saveTab(tab, saveOrder: true) } @@ -554,11 +564,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) } } } @@ -579,8 +591,10 @@ class TabManager: NSObject { } func saveAllTabs() { - if PrivateBrowsingManager.shared.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) } @@ -589,7 +603,7 @@ class TabManager: NSObject { } func saveTab(_ tab: Tab, saveOrder: Bool = false) { - if PrivateBrowsingManager.shared.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() @@ -911,6 +925,7 @@ class TabManager: NSObject { savedTabs = SessionTab.all() } + savedTabs = savedTabs.filter({ $0.sessionWindow?.windowId == windowId }) if savedTabs.isEmpty { return nil } var tabToSelect: Tab? @@ -925,20 +940,19 @@ class TabManager: NSObject { flushToDisk: false, zombie: true, id: savedTab.tabId, - isPrivate: false) - - let isPrivateBrowsing = PrivateBrowsingManager.shared.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 } } @@ -1161,8 +1175,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) } diff --git a/Sources/Brave/Frontend/Browser/TabManagerNavDelegate.swift b/Sources/Brave/Frontend/Browser/TabManagerNavDelegate.swift index b7fa66e9258..357944324ae 100644 --- a/Sources/Brave/Frontend/Browser/TabManagerNavDelegate.swift +++ b/Sources/Brave/Frontend/Browser/TabManagerNavDelegate.swift @@ -68,7 +68,7 @@ class TabManagerNavDelegate: NSObject, WKNavigationDelegate { } private func defaultAllowPolicy() -> WKNavigationActionPolicy { - let isPrivateBrowsing = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateBrowsing = tabManager?.privateBrowsingManager.isPrivateBrowsing == true if isPrivateBrowsing || !Preferences.General.followUniversalLinks.value { // Stop Brave from opening universal links by using the private enum value // `_WKNavigationActionPolicyAllowWithoutTryingAppLink` which is defined here: diff --git a/Sources/Brave/Frontend/Browser/Tabs/RecentlyClosedTabs/RecentlyClosedTabsView.swift b/Sources/Brave/Frontend/Browser/Tabs/RecentlyClosedTabs/RecentlyClosedTabsView.swift index 5a08cb5d6e8..247608a6ba3 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/RecentlyClosedTabs/RecentlyClosedTabsView.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/RecentlyClosedTabs/RecentlyClosedTabsView.swift @@ -57,7 +57,7 @@ struct RecentlyClosedTabsView: View { onRecentlyClosedSelected?(recentlyClosed) }) { HStack { - FaviconImage(url: recentlyClosed.url) + FaviconImage(url: recentlyClosed.url, isPrivateBrowsing: tabManager?.privateBrowsingManager.isPrivateBrowsing == true) VStack(alignment: .leading) { Text(recentlyClosed.title ?? "") .font(.footnote.weight(.semibold)) diff --git a/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabBarCell.swift b/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabBarCell.swift index 6df2d9ffb35..0273eb7d4ee 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabBarCell.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabBarCell.swift @@ -39,7 +39,23 @@ class TabBarCell: UICollectionViewCell { } } weak var tab: Tab? - weak var tabManager: TabManager? + weak var tabManager: TabManager? { + didSet { + privateModeCancellable = tabManager?.privateBrowsingManager + .$isPrivateBrowsing + .removeDuplicates() + .sink(receiveValue: { [weak self] isPrivateBrowsing in + self?.updateColors(isPrivateBrowsing) + }) + + Preferences.General.nightModeEnabled.objectWillChange + .receive(on: RunLoop.main) + .sink { [weak self] _ in + self?.updateColors(self?.tabManager?.privateBrowsingManager.isPrivateBrowsing == true) + } + .store(in: &cancellables) + } + } var closeTabCallback: ((Tab) -> Void)? private var cancellables: Set = [] @@ -62,19 +78,6 @@ class TabBarCell: UICollectionViewCell { updateFont() isSelected = false - privateModeCancellable = PrivateBrowsingManager.shared - .$isPrivateBrowsing - .removeDuplicates() - .sink(receiveValue: { [weak self] isPrivateBrowsing in - self?.updateColors(isPrivateBrowsing) - }) - - Preferences.General.nightModeEnabled.objectWillChange - .receive(on: RunLoop.main) - .sink { [weak self] _ in - self?.updateColors(PrivateBrowsingManager.shared.isPrivateBrowsing) - } - .store(in: &cancellables) } private var privateModeCancellable: AnyCancellable? diff --git a/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift b/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift index 5f046eac2f4..14286481cfa 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift @@ -13,6 +13,7 @@ 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) } @@ -100,8 +101,9 @@ class TabsBarViewController: UIViewController { } var newTabMenu: [UIAction] = [] + let isPrivateBrowsing = tabManager?.privateBrowsingManager.isPrivateBrowsing == true - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !isPrivateBrowsing { let openNewPrivateTab = UIAction( title: Strings.Hotkey.newPrivateTabTitle, image: UIImage(systemName: "plus.square.fill.on.square.fill"), @@ -113,16 +115,20 @@ class TabsBarViewController: UIViewController { } let openNewTab = UIAction( - title: PrivateBrowsingManager.shared.isPrivateBrowsing ? Strings.Hotkey.newPrivateTabTitle : Strings.Hotkey.newTabTitle, - image: PrivateBrowsingManager.shared.isPrivateBrowsing ? UIImage(systemName: "plus.square.fill.on.square.fill") : UIImage(systemName: "plus.square.on.square"), + title: isPrivateBrowsing ? Strings.Hotkey.newPrivateTabTitle : Strings.Hotkey.newTabTitle, + image: isPrivateBrowsing ? UIImage(systemName: "plus.square.fill.on.square.fill") : UIImage(systemName: "plus.square.on.square"), handler: UIAction.deferredActionHandler { [unowned self] _ in - self.delegate?.tabsBarDidSelectAddNewTab(PrivateBrowsingManager.shared.isPrivateBrowsing) + self.delegate?.tabsBarDidSelectAddNewTab(isPrivateBrowsing) }) 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 = PrivateBrowsingManager.shared + privateModeCancellable = tabManager?.privateBrowsingManager .$isPrivateBrowsing .removeDuplicates() .sink(receiveValue: { [weak self] isPrivateBrowsing in @@ -132,7 +138,7 @@ class TabsBarViewController: UIViewController { Preferences.General.nightModeEnabled.objectWillChange .receive(on: RunLoop.main) .sink { [weak self] _ in - self?.updateColors(PrivateBrowsingManager.shared.isPrivateBrowsing) + self?.updateColors(self?.tabManager?.privateBrowsingManager.isPrivateBrowsing == true) } .store(in: &cancellables) } @@ -199,7 +205,7 @@ class TabsBarViewController: UIViewController { } @objc func addTabPressed() { - tabManager?.addTabAndSelect(isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing) + tabManager?.addTabAndSelect(isPrivate: tabManager?.privateBrowsingManager.isPrivateBrowsing == true) } @objc private func didLongPressAddTab(_ longPress: UILongPressGestureRecognizer) { diff --git a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController+KeyCommands.swift b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController+KeyCommands.swift index 53ef5ed254f..5c5d947e8ea 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController+KeyCommands.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController+KeyCommands.swift @@ -105,7 +105,7 @@ extension TabTrayController { UIKeyCommand(title: Strings.Hotkey.openNewTabFromTabTrayKeyCodeTitle, action: #selector(didOpenNewTabKeyCommand), input: "t", modifierFlags: .command) ] - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !tabManager.privateBrowsingManager.isPrivateBrowsing { navigationCommands += [ UIKeyCommand(title: Strings.Hotkey.recentlyClosedTabTitle, action: #selector(reopenRecentlyClosedTabCommand), input: "t", modifierFlags: [.command, .shift]) ] diff --git a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController+TableViewDelegate.swift b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController+TableViewDelegate.swift index cc4ecb978cc..19c053f2dac 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController+TableViewDelegate.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController+TableViewDelegate.swift @@ -50,7 +50,7 @@ extension TabTrayController: UITableViewDataSource, UITableViewDelegate { $0.layer.cornerRadius = 6 $0.layer.cornerCurve = .continuous $0.layer.masksToBounds = true - $0.loadFavicon(for: distantTab.url) + $0.loadFavicon(for: distantTab.url, isPrivateBrowsing: tabManager.privateBrowsingManager.isPrivateBrowsing) } } diff --git a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift index e5cd30b65d6..5c050670eb7 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift @@ -85,7 +85,7 @@ class TabTrayController: LoadingViewController { private(set) var privateMode: Bool = false { didSet { // Should be set immediately before other logic executes - PrivateBrowsingManager.shared.isPrivateBrowsing = privateMode + tabManager.privateBrowsingManager.isPrivateBrowsing = privateMode applySnapshot() privateModeButton.isSelected = privateMode @@ -176,7 +176,7 @@ class TabTrayController: LoadingViewController { overlayDetails: EmptyOverlayStateDetails(title: Strings.noSearchResultsfound)) override var preferredStatusBarStyle: UIStatusBarStyle { - if PrivateBrowsingManager.shared.isPrivateBrowsing { + if tabManager.privateBrowsingManager.isPrivateBrowsing { return .lightContent } @@ -298,7 +298,7 @@ class TabTrayController: LoadingViewController { UIBarButtonItem(customView: doneButton), ] - privateModeCancellable = PrivateBrowsingManager.shared + privateModeCancellable = tabManager.privateBrowsingManager .$isPrivateBrowsing .removeDuplicates() .sink(receiveValue: { [weak self] isPrivateBrowsing in @@ -418,7 +418,7 @@ class TabTrayController: LoadingViewController { } override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { - guard !PrivateBrowsingManager.shared.isPrivateBrowsing, let recentlyClosedTab = RecentlyClosed.all().first else { + guard !tabManager.privateBrowsingManager.isPrivateBrowsing, let recentlyClosedTab = RecentlyClosed.all().first else { return } @@ -568,7 +568,7 @@ class TabTrayController: LoadingViewController { } @objc private func tappedButton(_ gestureRecognizer: UIGestureRecognizer) { - if PrivateBrowsingManager.shared.isPrivateBrowsing { + if tabManager.privateBrowsingManager.isPrivateBrowsing { return } @@ -609,10 +609,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() diff --git a/Sources/Brave/Frontend/Browser/Tabs/TabTray/Views/TabTrayCell.swift b/Sources/Brave/Frontend/Browser/Tabs/TabTray/Views/TabTrayCell.swift index 0cb35657b94..a6ff3538714 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabTray/Views/TabTrayCell.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabTray/Views/TabTrayCell.swift @@ -77,7 +77,7 @@ class TabCell: UICollectionViewCell { if let displayFavicon = tab.displayFavicon { favicon.image = displayFavicon.image ?? Favicon.defaultImage } else if let url = tab.url, !url.isLocal, !InternalURL.isValid(url: url) { - favicon.loadFavicon(for: url) + favicon.loadFavicon(for: url, isPrivateBrowsing: tab.isPrivate) } else { favicon.image = Favicon.defaultImage } diff --git a/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/BottomToolbarView.swift b/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/BottomToolbarView.swift index dc6c6f33f11..6eccfcf63c7 100644 --- a/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/BottomToolbarView.swift +++ b/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/BottomToolbarView.swift @@ -26,10 +26,12 @@ class BottomToolbarView: UIView, ToolbarProtocol { private let contentView = UIStackView() private var cancellables: Set = [] let line = UIView.separatorLine + private let privateBrowsingManager: PrivateBrowsingManager - fileprivate override init(frame: CGRect) { + init(privateBrowsingManager: PrivateBrowsingManager) { + self.privateBrowsingManager = privateBrowsingManager actionButtons = [backButton, forwardButton, addTabButton, searchButton, tabsButton, menuButton] - super.init(frame: frame) + super.init(frame: .zero) setupAccessibility() backgroundColor = Preferences.General.nightModeEnabled.value ? .nightModeBackground : .urlBarBackground @@ -48,7 +50,7 @@ class BottomToolbarView: UIView, ToolbarProtocol { $0.leading.trailing.equalToSuperview() } - privateModeCancellable = PrivateBrowsingManager.shared + privateModeCancellable = privateBrowsingManager .$isPrivateBrowsing .removeDuplicates() .sink(receiveValue: { [weak self] isPrivateBrowsing in @@ -58,7 +60,7 @@ class BottomToolbarView: UIView, ToolbarProtocol { Preferences.General.nightModeEnabled.objectWillChange .receive(on: RunLoop.main) .sink { [weak self] _ in - self?.updateColors(PrivateBrowsingManager.shared.isPrivateBrowsing) + self?.updateColors(privateBrowsingManager.isPrivateBrowsing) } .store(in: &cancellables) diff --git a/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/Bookmarks/AddEditBookmarkTableViewController.swift b/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/Bookmarks/AddEditBookmarkTableViewController.swift index eeb428b73ee..d444799ce13 100644 --- a/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/Bookmarks/AddEditBookmarkTableViewController.swift +++ b/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/Bookmarks/AddEditBookmarkTableViewController.swift @@ -49,11 +49,11 @@ class AddEditBookmarkTableViewController: UITableViewController { private lazy var bookmarkDetailsView: BookmarkFormFieldsProtocol = { switch mode { case .addBookmark(let title, let url): - return BookmarkDetailsView(title: title, url: url) + return BookmarkDetailsView(title: title, url: url, isPrivateBrowsing: isPrivateBrowsing) case .addFolder(let title), .addFolderUsingTabs(title: let title, _): return FolderDetailsViewTableViewCell(title: title, viewHeight: UX.cellHeight) case .editBookmark(let bookmark), .editFavorite(let bookmark): - return BookmarkDetailsView(title: bookmark.title, url: bookmark.url) + return BookmarkDetailsView(title: bookmark.title, url: bookmark.url, isPrivateBrowsing: isPrivateBrowsing) case .editFolder(let folder): return FolderDetailsViewTableViewCell(title: folder.title, viewHeight: UX.cellHeight) } @@ -150,10 +150,12 @@ class AddEditBookmarkTableViewController: UITableViewController { private var saveLocation: BookmarkSaveLocation private var rootFolderName: String private var rootFolderId: Int = 0 // MobileBookmarks Folder Id + private var isPrivateBrowsing: Bool - init(bookmarkManager: BookmarkManager, mode: BookmarkEditMode) { + init(bookmarkManager: BookmarkManager, mode: BookmarkEditMode, isPrivateBrowsing: Bool) { self.bookmarkManager = bookmarkManager self.mode = mode + self.isPrivateBrowsing = isPrivateBrowsing saveLocation = mode.initialSaveLocation presentationMode = .currentSelection @@ -371,7 +373,7 @@ class AddEditBookmarkTableViewController: UITableViewController { isLoading = true for tab in tabs { - if PrivateBrowsingManager.shared.isPrivateBrowsing { + if tab.isPrivate { if let url = tab.url, url.isWebPage(), !(InternalURL(url)?.isAboutHomeURL ?? false) { bookmarkManager.add(url: url, title: tab.title, parentFolder: parentFolder) } @@ -437,7 +439,9 @@ class AddEditBookmarkTableViewController: UITableViewController { } private func showNewFolderVC() { - let vc = AddEditBookmarkTableViewController(bookmarkManager: bookmarkManager, mode: .addFolder(title: Strings.newFolderDefaultName)) + let vc = AddEditBookmarkTableViewController(bookmarkManager: bookmarkManager, + mode: .addFolder(title: Strings.newFolderDefaultName), + isPrivateBrowsing: isPrivateBrowsing) navigationController?.pushViewController(vc, animated: true) } diff --git a/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/Bookmarks/BookmarkDetailsView.swift b/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/Bookmarks/BookmarkDetailsView.swift index ec422c8b7ad..fa07d94e89c 100644 --- a/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/Bookmarks/BookmarkDetailsView.swift +++ b/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/Bookmarks/BookmarkDetailsView.swift @@ -52,7 +52,7 @@ class BookmarkDetailsView: AddEditHeaderView, BookmarkFormFieldsProtocol { // MARK: - Initialization - convenience init(title: String?, url: String?) { + convenience init(title: String?, url: String?, isPrivateBrowsing: Bool) { self.init(frame: .zero) backgroundColor = .secondaryBraveGroupedBackground @@ -76,7 +76,7 @@ class BookmarkDetailsView: AddEditHeaderView, BookmarkFormFieldsProtocol { if url?.isBookmarklet == true { url = url?.removingPercentEncoding } else if let url = url, let favUrl = URL(string: url) { - faviconImageView.loadFavicon(siteURL: favUrl) + faviconImageView.loadFavicon(siteURL: favUrl, isPrivateBrowsing: isPrivateBrowsing) } titleTextField.text = title ?? Strings.newBookmarkDefaultName diff --git a/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/Bookmarks/BookmarksViewController.swift b/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/Bookmarks/BookmarksViewController.swift index c89412ca5b6..8ae67253b2f 100644 --- a/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/Bookmarks/BookmarksViewController.swift +++ b/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/Bookmarks/BookmarksViewController.swift @@ -417,7 +417,7 @@ class BookmarksViewController: SiteTableViewController, ToolbarUrlActionsProtoco cell.imageView?.clearMonogramFavicon() if let urlString = item.url, let url = URL(string: urlString) { - cell.imageView?.loadFavicon(for: url) { [weak cell] favicon in + cell.imageView?.loadFavicon(for: url, isPrivateBrowsing: isPrivateBrowsing) { [weak cell] favicon in if favicon?.isMonogramImage == true, let icon = item.bookmarkNode.icon { cell?.imageView?.image = icon } @@ -614,7 +614,7 @@ class BookmarksViewController: SiteTableViewController, ToolbarUrlActionsProtoco var newTabActionMenu: [UIAction] = [openInNewTabAction] - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !isPrivateBrowsing { newTabActionMenu.append(newPrivateTabAction) } @@ -701,7 +701,7 @@ extension BookmarksViewController { } if let mode = mode { - let vc = AddEditBookmarkTableViewController(bookmarkManager: bookmarkManager, mode: mode) + let vc = AddEditBookmarkTableViewController(bookmarkManager: bookmarkManager, mode: mode, isPrivateBrowsing: isPrivateBrowsing) self.navigationController?.pushViewController(vc, animated: true) } } diff --git a/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/HistoryViewController.swift b/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/HistoryViewController.swift index 32d9990386f..4477c39f12d 100644 --- a/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/HistoryViewController.swift +++ b/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/HistoryViewController.swift @@ -240,10 +240,10 @@ class HistoryViewController: SiteTableViewController, ToolbarUrlActionsProtocol let domain = Domain.getOrCreate( forUrl: historyItem.url, - persistent: !PrivateBrowsingManager.shared.isPrivateBrowsing) + persistent: !isPrivateBrowsing) if domain.url?.asURL != nil { - cell.imageView?.loadFavicon(for: historyItem.url) + cell.imageView?.loadFavicon(for: historyItem.url, isPrivateBrowsing: isPrivateBrowsing) } else { cell.imageView?.clearMonogramFavicon() cell.imageView?.image = Favicon.defaultImage @@ -340,7 +340,7 @@ class HistoryViewController: SiteTableViewController, ToolbarUrlActionsProtocol var newTabActionMenu: [UIAction] = [openInNewTabAction] - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !isPrivateBrowsing { newTabActionMenu.append(newPrivateTabAction) } diff --git a/Sources/Brave/Frontend/Browser/Toolbars/HeaderContainerView.swift b/Sources/Brave/Frontend/Browser/Toolbars/HeaderContainerView.swift index e08c09a52a5..10f0d5929c7 100644 --- a/Sources/Brave/Frontend/Browser/Toolbars/HeaderContainerView.swift +++ b/Sources/Brave/Frontend/Browser/Toolbars/HeaderContainerView.swift @@ -32,8 +32,8 @@ class HeaderContainerView: UIView { private var cancellables: Set = [] - override init(frame: CGRect) { - super.init(frame: frame) + init(privateBrowsingManager: PrivateBrowsingManager) { + super.init(frame: .zero) addSubview(contentView) contentView.addSubview(expandedBarStackView) @@ -51,7 +51,7 @@ class HeaderContainerView: UIView { $0.edges.equalToSuperview() } - PrivateBrowsingManager.shared + privateBrowsingManager .$isPrivateBrowsing .removeDuplicates() .sink(receiveValue: { [weak self] isPrivateBrowsing in @@ -62,7 +62,7 @@ class HeaderContainerView: UIView { Preferences.General.nightModeEnabled.objectWillChange .receive(on: RunLoop.main) .sink { [weak self] _ in - self?.updateColors(PrivateBrowsingManager.shared.isPrivateBrowsing) + self?.updateColors(privateBrowsingManager.isPrivateBrowsing) } .store(in: &cancellables) } diff --git a/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TabLocationView.swift b/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TabLocationView.swift index 0ebfbbede90..89ef061fa58 100644 --- a/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TabLocationView.swift +++ b/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TabLocationView.swift @@ -251,7 +251,7 @@ class TabLocationView: UIView { private var isVoiceSearchAvailable: Bool - init(voiceSearchSupported: Bool) { + init(voiceSearchSupported: Bool, privateBrowsingManager: privateBrowsingManager) { isVoiceSearchAvailable = voiceSearchSupported super.init(frame: .zero) @@ -306,7 +306,7 @@ class TabLocationView: UIView { dragInteraction.allowsSimultaneousRecognitionDuringLift = true self.addInteraction(dragInteraction) - privateModeCancellable = PrivateBrowsingManager.shared.$isPrivateBrowsing + privateModeCancellable = privateBrowsingManager.$isPrivateBrowsing .removeDuplicates() .sink(receiveValue: { [weak self] isPrivateBrowsing in self?.updateColors(isPrivateBrowsing) diff --git a/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TopToolbarView.swift b/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TopToolbarView.swift index 2aab9368269..81b6f204912 100644 --- a/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TopToolbarView.swift +++ b/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TopToolbarView.swift @@ -293,7 +293,7 @@ class TopToolbarView: UIView, ToolbarProtocol { // Make sure we hide any views that shouldn't be showing in non-overlay mode. updateViewsForOverlayModeAndToolbarChanges() - privateModeCancellable = PrivateBrowsingManager.shared + privateModeCancellable = privateBrowsingManager .$isPrivateBrowsing .removeDuplicates() .sink(receiveValue: { [weak self] isPrivateBrowsing in @@ -303,7 +303,7 @@ class TopToolbarView: UIView, ToolbarProtocol { Preferences.General.nightModeEnabled.objectWillChange .receive(on: RunLoop.main) .sink { [weak self] _ in - self?.updateColors(PrivateBrowsingManager.shared.isPrivateBrowsing) + self?.updateColors(privateBrowsingManager.isPrivateBrowsing) } .store(in: &cancellables) diff --git a/Sources/Brave/Frontend/ClientPreferences.swift b/Sources/Brave/Frontend/ClientPreferences.swift index a32cbeeec4a..57432a61399 100644 --- a/Sources/Brave/Frontend/ClientPreferences.swift +++ b/Sources/Brave/Frontend/ClientPreferences.swift @@ -125,6 +125,8 @@ extension Preferences { static let lockWithPasscode = Option(key: "privacy.lock-with-passcode", default: false) /// 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) /// 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/Login/LoginListViewController.swift b/Sources/Brave/Frontend/Login/LoginListViewController.swift index 49ff9940926..6d762ec4f02 100644 --- a/Sources/Brave/Frontend/Login/LoginListViewController.swift +++ b/Sources/Brave/Frontend/Login/LoginListViewController.swift @@ -212,7 +212,7 @@ extension LoginListViewController { $0.layer.cornerCurve = .continuous $0.layer.masksToBounds = true if let signOnRealmURL = URL(string: loginInfo.signOnRealm) { - $0.loadFavicon(for: signOnRealmURL) + $0.loadFavicon(for: signOnRealmURL, isPrivateBrowsing: false) } } diff --git a/Sources/Brave/Frontend/Settings/SearchCustomEngineViewController.swift b/Sources/Brave/Frontend/Settings/SearchCustomEngineViewController.swift index 40d6aca27de..a05b903e573 100644 --- a/Sources/Brave/Frontend/Settings/SearchCustomEngineViewController.swift +++ b/Sources/Brave/Frontend/Settings/SearchCustomEngineViewController.swift @@ -41,6 +41,7 @@ class SearchCustomEngineViewController: UIViewController { // MARK: Properties private var profile: Profile + private var privateBrowsingManager: PrivateBrowsingManager private var urlText: String? @@ -84,8 +85,9 @@ class SearchCustomEngineViewController: UIViewController { // MARK: Lifecycle - init(profile: Profile) { + init(profile: Profile, privateBrowsingManager: PrivateBrowsingManager) { self.profile = profile + self.privateBrowsingManager = privateBrowsingManager super.init(nibName: nil, bundle: nil) } @@ -170,7 +172,7 @@ class SearchCustomEngineViewController: UIViewController { // MARK: Actions - @objc func checkAddEngineType(_ nav: UINavigationController?) { + @objc func checkAddEngineType() { if isAutoAddEnabled { addOpenSearchEngine() } else { @@ -392,7 +394,7 @@ extension SearchCustomEngineViewController { faviconTask?.cancel() faviconTask = Task { @MainActor in do { - let icon = try await FaviconFetcher.loadIcon(url: url, persistent: !PrivateBrowsingManager.shared.isPrivateBrowsing) + let icon = try await FaviconFetcher.loadIcon(url: url, persistent: !privateBrowsingManager.isPrivateBrowsing) self.faviconImage = icon.image ?? Favicon.defaultImage } catch { self.faviconImage = Favicon.defaultImage @@ -474,7 +476,7 @@ extension SearchCustomEngineViewController { faviconTask?.cancel() faviconTask = Task { @MainActor in - let favicon = try? await FaviconFetcher.loadIcon(url: hostUrl, persistent: !PrivateBrowsingManager.shared.isPrivateBrowsing) + let favicon = try? await FaviconFetcher.loadIcon(url: hostUrl, persistent: !privateBrowsingManager.isPrivateBrowsing) if let image = favicon?.image { engineImage = image } diff --git a/Sources/Brave/Frontend/Settings/SearchQuickEnginesViewController.swift b/Sources/Brave/Frontend/Settings/SearchQuickEnginesViewController.swift index 439584771fb..2576854a495 100644 --- a/Sources/Brave/Frontend/Settings/SearchQuickEnginesViewController.swift +++ b/Sources/Brave/Frontend/Settings/SearchQuickEnginesViewController.swift @@ -28,11 +28,13 @@ class SearchQuickEnginesViewController: UITableViewController { private var searchEngines: SearchEngines private let profile: Profile + private let isPrivateBrowsing: Bool // MARK: Lifecycle - init(profile: Profile) { + init(profile: Profile, isPrivateBrowsing: Bool) { self.profile = profile + self.isPrivateBrowsing = isPrivateBrowsing self.searchEngines = profile.searchEngines super.init(nibName: nil, bundle: nil) } @@ -142,7 +144,7 @@ extension SearchQuickEnginesViewController { if toggle.isOn { searchEngines.enableEngine(engine) } else { - searchEngines.disableEngine(engine) + searchEngines.disableEngine(engine, isPrivateBrowsing: isPrivateBrowsing) } } } diff --git a/Sources/Brave/Frontend/Settings/SearchSettingsTableViewController.swift b/Sources/Brave/Frontend/Settings/SearchSettingsTableViewController.swift index c33cf2fe9fb..f20bb01de8a 100644 --- a/Sources/Brave/Frontend/Settings/SearchSettingsTableViewController.swift +++ b/Sources/Brave/Frontend/Settings/SearchSettingsTableViewController.swift @@ -59,6 +59,7 @@ class SearchSettingsTableViewController: UITableViewController { private var searchEngines: SearchEngines private let profile: Profile private var showDeletion = false + private var privateBrowsingManager: PrivateBrowsingManager private func searchPickerEngines(type: DefaultEngineType) -> [OpenSearchEngine] { let isPrivate = type == .privateMode @@ -84,8 +85,9 @@ class SearchSettingsTableViewController: UITableViewController { // MARK: Lifecycle - init(profile: Profile) { + init(profile: Profile, privateBrowsingManager: PrivateBrowsingManager) { self.profile = profile + self.privateBrowsingManager = privateBrowsingManager self.searchEngines = profile.searchEngines super.init(nibName: nil, bundle: nil) } @@ -283,10 +285,10 @@ class SearchSettingsTableViewController: UITableViewController { } else if indexPath.section == Section.current.rawValue && indexPath.item == CurrentEngineType.private.rawValue { navigationController?.pushViewController(configureSearchEnginePicker(.privateMode), animated: true) } else if indexPath.section == Section.current.rawValue && indexPath.item == CurrentEngineType.quick.rawValue { - let quickSearchEnginesViewController = SearchQuickEnginesViewController(profile: profile) + let quickSearchEnginesViewController = SearchQuickEnginesViewController(profile: profile, isPrivateBrowsing: privateBrowsingManager.isPrivateBrowsing) navigationController?.pushViewController(quickSearchEnginesViewController, animated: true) } else if indexPath.section == Section.customSearch.rawValue && indexPath.item == customSearchEngines.count { - let customEngineViewController = SearchCustomEngineViewController(profile: profile) + let customEngineViewController = SearchCustomEngineViewController(profile: profile, privateBrowsingManager: privateBrowsingManager) navigationController?.pushViewController(customEngineViewController, animated: true) } diff --git a/Sources/Brave/Frontend/Settings/SettingsViewController.swift b/Sources/Brave/Frontend/Settings/SettingsViewController.swift index 80897fd7af7..e8b5852b8cb 100644 --- a/Sources/Brave/Frontend/Settings/SettingsViewController.swift +++ b/Sources/Brave/Frontend/Settings/SettingsViewController.swift @@ -315,7 +315,7 @@ class SettingsViewController: TableViewController { Row( text: Strings.searchEngines, selection: { [unowned self] in - let viewController = SearchSettingsTableViewController(profile: self.profile) + let viewController = SearchSettingsTableViewController(profile: self.profile, privateBrowsingManager: tabManager.privateBrowsingManager) self.navigationController?.pushViewController(viewController, animated: true) }, image: UIImage(braveSystemNamed: "leo.search"), accessory: .disclosureIndicator, cellClass: MultilineValue1Cell.self), Row( @@ -514,7 +514,7 @@ class SettingsViewController: TableViewController { )) // We do NOT persistently save page-zoom settings in Private Browsing - if !PrivateBrowsingManager.shared.isPrivateBrowsing { + if !tabManager.privateBrowsingManager.isPrivateBrowsing { display.rows.append( Row(text: Strings.PageZoom.settingsMenuTitle, selection: { [weak self] in diff --git a/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift b/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift index 3dfc390891d..721e8dd139c 100644 --- a/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift +++ b/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift @@ -7,6 +7,7 @@ import SwiftUI import Preferences import Strings import BraveShared +import Data struct OtherPrivacySettingsSectionView: View { @State private var showPrivateBrowsingConfirmation = false @@ -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. @@ -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, diff --git a/Sources/Brave/Frontend/Shields/ShieldsActivityItemSourceProvider.swift b/Sources/Brave/Frontend/Shields/ShieldsActivityItemSourceProvider.swift index 79b703a094b..b4d99a87464 100644 --- a/Sources/Brave/Frontend/Shields/ShieldsActivityItemSourceProvider.swift +++ b/Sources/Brave/Frontend/Shields/ShieldsActivityItemSourceProvider.swift @@ -23,12 +23,13 @@ final class ShieldsActivityItemSourceProvider { static let shared = ShieldsActivityItemSourceProvider() - func setupGlobalShieldsActivityController() -> UIActivityViewController { + func setupGlobalShieldsActivityController(isPrivateBrowsing: Bool) -> UIActivityViewController { let backgroundImage = UIImage(named: "share-activity-background", in: .module, compatibleWith: nil)! let statsView = UIView(frame: CGRect(size: backgroundImage.size)).then { let backgroundImageView = UIImageView(image: backgroundImage) let statsInfoView = BraveShieldStatsView() + statsInfoView.isPrivateBrowsing = isPrivateBrowsing $0.addSubview(backgroundImageView) $0.addSubview(statsInfoView) diff --git a/Sources/Brave/Frontend/Shields/ShieldsViewController.swift b/Sources/Brave/Frontend/Shields/ShieldsViewController.swift index f936d5f914f..843f0fe60d8 100644 --- a/Sources/Brave/Frontend/Shields/ShieldsViewController.swift +++ b/Sources/Brave/Frontend/Shields/ShieldsViewController.swift @@ -62,7 +62,7 @@ class ShieldsViewController: UIViewController, PopoverContentComponent { private func updateToggleStatus() { var domain: Domain? if let url = url { - let isPrivateBrowsing = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateBrowsing = tab.isPrivate domain = Domain.getOrCreate(forUrl: url, persistent: !isPrivateBrowsing) } @@ -104,7 +104,7 @@ class ShieldsViewController: UIViewController, PopoverContentComponent { let isOn = allOff ? !on : on Domain.setBraveShield( forUrl: url, shield: shield, isOn: isOn, - isPrivateBrowsing: PrivateBrowsingManager.shared.isPrivateBrowsing) + isPrivateBrowsing: tab.isPrivate) } private func updateGlobalShieldState(_ on: Bool, animated: Bool = false) { @@ -229,7 +229,7 @@ class ShieldsViewController: UIViewController, PopoverContentComponent { super.viewDidLoad() if let url = url { - shieldsView.simpleShieldView.faviconImageView.loadFavicon(for: url) + shieldsView.simpleShieldView.faviconImageView.loadFavicon(for: url, isPrivateBrowsing: tab.isPrivate) } else { shieldsView.simpleShieldView.faviconImageView.isHidden = true } @@ -309,7 +309,7 @@ class ShieldsViewController: UIViewController, PopoverContentComponent { @objc private func tappedShareShieldsButton() { let globalShieldsActivityController = - ShieldsActivityItemSourceProvider.shared.setupGlobalShieldsActivityController() + ShieldsActivityItemSourceProvider.shared.setupGlobalShieldsActivityController(isPrivateBrowsing: tab.isPrivate) globalShieldsActivityController.popoverPresentationController?.sourceView = view present(globalShieldsActivityController, animated: true, completion: nil) diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveSearchScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveSearchScriptHandler.swift index 6077fd6ccb5..7803e334741 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveSearchScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveSearchScriptHandler.swift @@ -98,7 +98,7 @@ class BraveSearchScriptHandler: TabContentScript { } private func handleCanSetBraveSearchAsDefault(replyHandler: (Any?, String?) -> Void) { - if PrivateBrowsingManager.shared.isPrivateBrowsing { + if tab?.isPrivate == true { Logger.module.debug("Private mode detected, skipping setting Brave Search as a default") replyHandler(false, nil) return diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveTalkScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveTalkScriptHandler.swift index 89f6f4c11dd..faa37a3a60e 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveTalkScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveTalkScriptHandler.swift @@ -123,7 +123,7 @@ class BraveTalkScriptHandler: TabContentScript { } private func handleBraveRequestAdsEnabled(_ replyHandler: @escaping (Any?, String?) -> Void) { - guard let rewards = rewards, !PrivateBrowsingManager.shared.isPrivateBrowsing else { + guard let rewards = rewards, tab?.isPrivate != true else { replyHandler(false, nil) return } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/ContentBlockerScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/ContentBlockerScriptHandler.swift index bb02aaadd34..84af8fe7769 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/ContentBlockerScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/ContentBlockerScriptHandler.swift @@ -65,7 +65,7 @@ extension ContentBlockerHelper: TabContentScript { let dto = try JSONDecoder().decode(ContentBlockerDTO.self, from: data) Task { @MainActor in - let isPrivateBrowsing = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateBrowsing = self.tab?.isPrivate == true let domain = Domain.getOrCreate(forUrl: currentTabURL, persistent: !isPrivateBrowsing) if domain.areAllShieldsOff { // if domain is "all_off", can just skip @@ -110,7 +110,7 @@ extension ContentBlockerHelper: TabContentScript { if blockedType == .ad, Preferences.PrivacyReports.captureShieldsData.value, let domainURL = URL(string: domainURLString), let blockedResourceHost = requestURL.baseDomain, - !PrivateBrowsingManager.shared.isPrivateBrowsing { + tab?.isPrivate != true { PrivacyReportsManager.pendingBlockedRequests.append((blockedResourceHost, domainURL, Date())) } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RequestBlockingContentScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RequestBlockingContentScriptHandler.swift index 72c2bbaa502..d5fac2ab186 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RequestBlockingContentScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RequestBlockingContentScriptHandler.swift @@ -66,7 +66,7 @@ class RequestBlockingContentScriptHandler: TabContentScript { // we use `NSURL(idnString: String)` to parse them guard let requestURL = NSURL(idnString: dto.data.resourceURL) as URL? else { return } guard let sourceURL = NSURL(idnString: dto.data.sourceURL) as URL? else { return } - let isPrivateBrowsing = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateBrowsing = tab.isPrivate Task { @MainActor in let domain = Domain.getOrCreate(forUrl: currentTabURL, persistent: !isPrivateBrowsing) @@ -90,7 +90,7 @@ class RequestBlockingContentScriptHandler: TabContentScript { if shouldBlock, Preferences.PrivacyReports.captureShieldsData.value, let domainURL = URL(string: domainURLString), let blockedResourceHost = requestURL.baseDomain, - !PrivateBrowsingManager.shared.isPrivateBrowsing { + !isPrivateBrowsing { PrivacyReportsManager.pendingBlockedRequests.append((blockedResourceHost, domainURL, Date())) } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RewardsReportingScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RewardsReportingScriptHandler.swift index 5b7a654637a..acdd7f3dba3 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RewardsReportingScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RewardsReportingScriptHandler.swift @@ -43,7 +43,7 @@ class RewardsReportingScriptHandler: TabContentScript { var referrerUrl: String? } - if PrivateBrowsingManager.shared.isPrivateBrowsing || !rewards.isEnabled { + if tab?.isPrivate == true || !rewards.isEnabled { return } diff --git a/Sources/Brave/Migration/Migration.swift b/Sources/Brave/Migration/Migration.swift index ff4f14d3f85..70a717ec5cd 100644 --- a/Sources/Brave/Migration/Migration.swift +++ b/Sources/Brave/Migration/Migration.swift @@ -74,7 +74,7 @@ public class Migration { // Migrate from TabMO to SessionTab and SessionWindow public static func migrateTabStateToWebkitState(diskImageStore: DiskImageStore?) { - let isPrivate = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivate = false // Private tabs at the time of writing this code was never persistent, so it wouldn't "restore". if Preferences.Migration.tabMigrationToInteractionStateCompleted.value { SessionWindow.createIfNeeded(index: 0, isPrivate: isPrivate, isSelected: true) diff --git a/Sources/Brave/Shortcuts/ActivityShortcutManager.swift b/Sources/Brave/Shortcuts/ActivityShortcutManager.swift index a8bcb2ed701..3649fc7e95b 100644 --- a/Sources/Brave/Shortcuts/ActivityShortcutManager.swift +++ b/Sources/Brave/Shortcuts/ActivityShortcutManager.swift @@ -129,7 +129,7 @@ public class ActivityShortcutManager: NSObject { private func handleActivityDetails(type: ActivityType, using bvc: BrowserViewController) { switch type { case .newTab: - bvc.openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing, isExternal: true) + bvc.openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: bvc.privateBrowsingManager.isPrivateBrowsing, isExternal: true) bvc.popToBVC() case .newPrivateTab: bvc.openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: true, isExternal: true) @@ -137,7 +137,7 @@ public class ActivityShortcutManager: NSObject { case .clearBrowsingHistory: bvc.clearHistoryAndOpenNewTab() case .enableBraveVPN: - bvc.openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: PrivateBrowsingManager.shared.isPrivateBrowsing, isExternal: true) + bvc.openBlankNewTab(attemptLocationFieldFocus: false, isPrivate: bvc.privateBrowsingManager.isPrivateBrowsing, isExternal: true) bvc.popToBVC() switch BraveVPN.vpnState { @@ -216,8 +216,7 @@ public class ActivityShortcutManager: NSObject { // MARK: Intent Donation Methods public func donateCustomIntent(for type: IntentType, with urlString: String) { - guard !PrivateBrowsingManager.shared.isPrivateBrowsing, - !urlString.isEmpty, + guard !urlString.isEmpty, URL(string: urlString) != nil else { return } diff --git a/Sources/Brave/States/AppState.swift b/Sources/Brave/States/AppState.swift new file mode 100644 index 00000000000..7aabf82fd3f --- /dev/null +++ b/Sources/Brave/States/AppState.swift @@ -0,0 +1,232 @@ +// +// AppState.swift +// +// +// Created by Brandon T on 2023-05-15. +// + +import Foundation +import UIKit +import BraveCore +import Data +import RuntimeWarnings +import Shared +import Growth +import Preferences +import Storage + +#if DEBUG +import os +#endif + +/// Class that does startup initialization +/// Everything in this class can only be execute ONCE +/// IE: BraveCore initialization, BuildChannel, Migrations, etc. +public class AppState { + 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 var state: State = .launching(options: [:], active: false) { + didSet { + switch state { + case .launching(_, let isActive): + if isActive { + // We have to wait until pre 1.12 migration is done until we proceed with database + // initialization. This is because Database container may change. See bugs #3416, #3377. + DataController.shared.initializeOnce() + Migration.postCoreDataInitMigrations() + Migration.migrateTabStateToWebkitState(diskImageStore: diskImageStore) + } + break + case .active: + break + case .backgrounded: + break + case .terminating: + break + } + } + } + + private(set) public lazy var diskImageStore = { () -> DiskImageStore? in + do { + return try DiskImageStore( + files: profile.files, + 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)") + } + return nil + }() + + private init() { + // Setup Constants + AppState.setupConstants() + + // Setup BraveCore + braveCore = AppState.setupBraveCore().then { + $0.scheduleLowPriorityStartupTasks() + } + + // Setup DAU + dau = DAU(braveCoreStats: braveCore.braveStats) + + // Setup Profile + profile = BrowserProfile(localName: "profile") + + // Setup Migrations + migration = Migration(braveCore: braveCore) + + // Perform Migrations + migration.launchMigrations(keyPrefix: profile.prefs.getBranchPrefix(), profile: profile) + + // Setup Rewards & Ads + let configuration = BraveRewards.Configuration.current() + Self.migrateAdsConfirmations(for: configuration) + rewards = BraveRewards(configuration: configuration) + + // Setup Custom URL scheme handlers + setupCustomSchemeHandlers(profile: profile) + } + + public enum State { + case launching(options: [UIApplication.LaunchOptionsKey: Any], active: Bool) + case active + case backgrounded + case terminating + } + + private static func setupConstants() { + // Application Constants must be initialized first +// #if MOZ_CHANNEL_RELEASE +// AppConstants.buildChannel = .release +// #elseif MOZ_CHANNEL_BETA +// AppConstants.buildChannel = .beta +// #elseif MOZ_CHANNEL_DEV +// AppConstants.buildChannel = .dev +// #elseif MOZ_CHANNEL_ENTERPRISE +// AppConstants.buildChannel = .enterprise +// #elseif MOZ_CHANNEL_DEBUG +// AppConstants.buildChannel = .debug +// #endif + } + + private static func setupBraveCore() -> BraveCoreMain { + // BraveCore Log Handler + BraveCoreMain.setLogHandler { severity, file, line, messageStartIndex, message in + let message = String(message.dropFirst(messageStartIndex).dropLast()) + .trimmingCharacters(in: .whitespacesAndNewlines) + if message.isEmpty { + // Nothing to print + return true + } + + if severity == .fatal { + let filename = URL(fileURLWithPath: file).lastPathComponent + #if DEBUG + // Prints a special runtime warning instead of crashing. + os_log( + .fault, + dso: os_rw.dso, + log: os_rw.log(category: "BraveCore"), + "[%@:%ld] > %@", filename, line, message + ) + return true + #else + fatalError("Fatal BraveCore Error at \(filename):\(line).\n\(message)") + #endif + } + + let level: OSLogType = { + switch severity { + case .fatal: return .fault + case .error: return .error + // No `.warning` level exists for OSLogType. os_Log.warning is an alias for `.error`. + case .warning: return .error + case .info: return .info + default: return .debug + } + }() + + let braveCoreLogger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "brave-core") + if AppConstants.buildChannel.isPublic { + braveCoreLogger.log(level: level, "\(message, privacy: .private)") + } else { + braveCoreLogger.log(level: level, "\(message, privacy: .public)") + } + + return true + } + + // Initialize BraveCore Switches + var switches: [BraveCoreSwitch] = [] + if !AppConstants.buildChannel.isPublic { + // Check prefs for additional switches + let activeSwitches = Set(Preferences.BraveCore.activeSwitches.value) + let switchValues = Preferences.BraveCore.switchValues.value + for activeSwitch in activeSwitches { + let key = BraveCoreSwitchKey(rawValue: activeSwitch) + if key.isValueless { + switches.append(.init(key: key)) + } else if let value = switchValues[activeSwitch], !value.isEmpty { + switches.append(.init(key: key, value: value)) + } + } + } + switches.append(.init(key: .rewardsFlags, value: BraveRewards.Configuration.current().flags)) + + // Initialize BraveCore + return BraveCoreMain(userAgent: UserAgent.mobile, additionalSwitches: switches) + } + + private func setupCustomSchemeHandlers(profile: Profile) { + let responders: [(String, InternalSchemeResponse)] = [ + (AboutHomeHandler.path, AboutHomeHandler()), + (AboutLicenseHandler.path, AboutLicenseHandler()), + (SessionRestoreHandler.path, SessionRestoreHandler()), + (ErrorPageHandler.path, ErrorPageHandler()), + (ReaderModeHandler.path, ReaderModeHandler(profile: profile)), + (IPFSSchemeHandler.path, IPFSSchemeHandler()), + (Web3DomainHandler.path, Web3DomainHandler()) + ] + + responders.forEach { (path, responder) in + InternalSchemeHandler.responders[path] = responder + } + } + + private static func migrateAdsConfirmations(for configruation: 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 ledgerStateContainer = base.appendingPathComponent("ledger/random_state.plist") + let adsConfirmations = base.appendingPathComponent("ads/confirmations.json") + let fm = FileManager.default + + if !fm.fileExists(atPath: ledgerStateContainer.path) || fm.fileExists(atPath: adsConfirmations.path) { + // Nothing to migrate or already migrated + return + } + + do { + let contents = NSDictionary(contentsOfFile: ledgerStateContainer.path) + guard let confirmations = contents?["confirmations.json"] as? String else { + adsRewardsLog.debug("No confirmations found to migrate in ledger's state container") + return + } + try confirmations.write(toFile: adsConfirmations.path, atomically: true, encoding: .utf8) + } catch { + adsRewardsLog.error("Failed to migrate confirmations.json to ads folder: \(error.localizedDescription)") + } + } +} diff --git a/Sources/Brave/States/BrowserState.swift b/Sources/Brave/States/BrowserState.swift new file mode 100644 index 00000000000..4c0dde43fda --- /dev/null +++ b/Sources/Brave/States/BrowserState.swift @@ -0,0 +1,32 @@ +// +// BrowserState.swift +// +// +// Created by Brandon T on 2023-05-15. +// + +import Foundation +import UIKit + +public class BrowserState { + public static let sceneId = "com.brave.ios.browser-scene" + + let window: UIWindow + let profile: Profile + //let browser: BrowserViewController + + init(window: UIWindow, profile: Profile) { + self.window = window + self.profile = profile + } + + public static func userActivity(for windowId: UUID, isPrivate: Bool) -> NSUserActivity { + return NSUserActivity(activityType: sceneId).then { + $0.targetContentIdentifier = windowId.uuidString + $0.addUserInfoEntries(from: [ + "WindowID": windowId.uuidString, + "isPrivate": isPrivate + ]) + } + } +} diff --git a/Sources/BraveStrings/BraveStrings.swift b/Sources/BraveStrings/BraveStrings.swift index 14f90fc5045..31dc607a88a 100644 --- a/Sources/BraveStrings/BraveStrings.swift +++ b/Sources/BraveStrings/BraveStrings.swift @@ -1104,6 +1104,7 @@ extension Strings { public static let youtubeMediaQualityOn = NSLocalizedString("YoutubeMediaQualityOn", tableName: "BraveShared", bundle: .module, value: "On", 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 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: "") diff --git a/Sources/BraveWallet/Crypto/Stores/KeyringStore.swift b/Sources/BraveWallet/Crypto/Stores/KeyringStore.swift index 3595b964f80..77b3b7a4082 100644 --- a/Sources/BraveWallet/Crypto/Stores/KeyringStore.swift +++ b/Sources/BraveWallet/Crypto/Stores/KeyringStore.swift @@ -116,6 +116,13 @@ public class KeyringStore: ObservableObject { /// The origin of the active tab (if applicable). Used for fetching/selecting network for the DApp origin. public var origin: URLOrigin? + /// If this KeyringStore instance is creating a wallet. + /// This flag is used to know when to dismiss onboarding when multiple windows are visible. + private var isCreatingWallet = false + /// If this KeyringStore instance is restoring a wallet. + /// This flag is used to know when to dismiss onboarding when multiple windows are visible. + private var isRestoringWallet = false + /// Internal flag kept for when `setSelectedAccount` is executing so we can wait for /// completion before reacting to observed changes. Ex. chain changed event fires after /// `setSelectedAccount` changes network, but before it can set the new account. @@ -229,6 +236,8 @@ public class KeyringStore: ObservableObject { } func markOnboardingCompleted() { + self.isCreatingWallet = false + self.isRestoringWallet = false self.isOnboardingVisible = false } @@ -277,6 +286,7 @@ public class KeyringStore: ObservableObject { } func createWallet(password: String, completion: ((String) -> Void)? = nil) { + isCreatingWallet = true keyringService.createWallet(password) { [weak self] mnemonic in self?.updateKeyringInfo() completion?(mnemonic) @@ -298,6 +308,7 @@ public class KeyringStore: ObservableObject { } func restoreWallet(phrase: String, password: String, isLegacyBraveWallet: Bool, completion: ((Bool) -> Void)? = nil) { + isRestoringWallet = true keyringService.restoreWallet( phrase, password: password, @@ -445,6 +456,12 @@ extension KeyringStore: BraveWalletKeyringServiceObserver { } public func keyringCreated(_ keyringId: BraveWallet.KeyringId) { + if isOnboardingVisible, !isCreatingWallet, keyringId == BraveWallet.DefaultKeyringId { + // Another window has created a wallet. We should dismiss onboarding on this + // window and allow the other window to continue with it's onboarding flow. + isOnboardingVisible = false + } + Task { @MainActor in let newKeyring = await keyringService.keyringInfo(keyringId) if let newKeyringCoin = newKeyring.coin { @@ -459,6 +476,12 @@ extension KeyringStore: BraveWalletKeyringServiceObserver { } public func keyringRestored(_ keyringId: BraveWallet.KeyringId) { + if isOnboardingVisible && !isRestoringWallet, keyringId == BraveWallet.DefaultKeyringId { + // Another window has restored a wallet. We should dismiss onboarding on this + // window and allow the other window to continue with it's onboarding flow. + isOnboardingVisible = false + } + updateKeyringInfo() } diff --git a/Sources/Data/models/SessionTab.swift b/Sources/Data/models/SessionTab.swift index 8866fb396a6..c087e433ba7 100644 --- a/Sources/Data/models/SessionTab.swift +++ b/Sources/Data/models/SessionTab.swift @@ -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 @@ -207,17 +212,17 @@ extension SessionTab { } } - public static func createIfNeeded(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.getActiveWindow(context: context) else { return } + let window = SessionWindow.from(windowId: windowId, in: context) else { return } _ = SessionTab(context: context, sessionWindow: window, sessionTabGroup: nil, index: Int32(window.sessionTabs?.count ?? 0), interactionState: Data(), - isPrivate: false, + isPrivate: isPrivate, isSelected: false, lastUpdated: .now, screenshotData: Data(), diff --git a/Sources/Data/models/SessionWindow.swift b/Sources/Data/models/SessionWindow.swift index be4eafb553e..defccb0475d 100644 --- a/Sources/Data/models/SessionWindow.swift +++ b/Sources/Data/models/SessionWindow.swift @@ -64,6 +64,11 @@ extension SessionWindow { return Self.from(windowId: windowId, in: DataController.viewContext) } + public static func from(windowId: UUID, in context: NSManagedObjectContext) -> SessionWindow? { + let predicate = NSPredicate(format: "\(#keyPath(SessionWindow.windowId)) == %@", windowId.uuidString) + return first(where: predicate, context: context) + } + public static func createIfNeeded(index: Int32, isPrivate: Bool, isSelected: Bool) { DataController.performOnMainContext { context in if SessionWindow.getActiveWindow(context: context) != nil { @@ -80,11 +85,34 @@ extension SessionWindow { } } + public static func createWindow(isPrivate: Bool, isSelected: Bool, uuid: UUID) { + DataController.performOnMainContext { context in + if let sessionWindow = SessionWindow.from(windowId: uuid, in: context) { + Self.all().forEach { + $0.isSelected = false + } + + sessionWindow.isSelected = isSelected + return + } + + let count = SessionWindow.count(context: context) ?? 0 + let window = SessionWindow(context: context, index: Int32(count), isPrivate: isPrivate, isSelected: isSelected) + window.windowId = uuid + + do { + try context.save() + } catch { + Logger.module.error("performTask save error: \(error.localizedDescription, privacy: .public)") + } + } + } + /// Marks the specified window as selected /// Since only one window can be active at a time, all other windows are marked as deselected public static func setSelected(windowId: UUID) { DataController.perform { context in - guard let window = Self.from(windowId: windowId) else { return } + guard let window = Self.from(windowId: windowId, in: context) else { return } let predicate = NSPredicate(format: "isSelected == true") all(where: predicate, context: context)?.forEach { @@ -94,16 +122,34 @@ extension SessionWindow { window.isSelected = true } } + + public static func all() -> [SessionWindow] { + let sortDescriptors = [NSSortDescriptor(key: #keyPath(SessionWindow.index), ascending: true)] + return all(sortDescriptors: sortDescriptors) ?? [] + } + + public static func delete(windowId: UUID) { + DataController.perform { context in + guard let sessionWindow = SessionWindow.from(windowId: windowId, in: context) else { + return + } + + sessionWindow.sessionTabs?.forEach { + $0.delete(context: .existing(context)) + } + + sessionWindow.sessionTabGroups?.forEach { + $0.delete(context: .existing(context)) + } + + sessionWindow.delete(context: .existing(context)) + } + } } // MARK: - Private extension SessionWindow { - private static func from(windowId: UUID, in context: NSManagedObjectContext) -> SessionWindow? { - let predicate = NSPredicate(format: "\(#keyPath(SessionWindow.windowId)) == %@", windowId.uuidString) - return first(where: predicate, context: context) - } - private static func entity(_ context: NSManagedObjectContext) -> NSEntityDescription? { return NSEntityDescription.entity(forEntityName: "SessionWindow", in: context) } From 34cefaca5f5bd475728e80ba5c5ec81eca1f376e Mon Sep 17 00:00:00 2001 From: Brandon T Date: Thu, 15 Jun 2023 15:15:53 -0400 Subject: [PATCH 02/13] Fix private-tab mode message not showing all the time. Fix private tab deleting when exiting persistence mode. --- App/iOS/Delegates/AppDelegate.swift | 3 -- App/iOS/Delegates/SceneDelegate.swift | 2 +- .../Brave/Frontend/Browser/TabManager.swift | 2 +- .../Tabs/TabTray/TabTrayController.swift | 2 +- .../OtherPrivacySettingsSectionView.swift | 8 ++--- Sources/Brave/States/AppState.swift | 35 +++++++------------ Sources/Brave/States/BrowserState.swift | 11 +++--- 7 files changed, 23 insertions(+), 40 deletions(-) diff --git a/App/iOS/Delegates/AppDelegate.swift b/App/iOS/Delegates/AppDelegate.swift index 32c3f2ad7fe..0e71961dcb6 100644 --- a/App/iOS/Delegates/AppDelegate.swift +++ b/App/iOS/Delegates/AppDelegate.swift @@ -253,9 +253,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { PlaylistManager.shared.restoreSession() } - UserDefaults.standard.set(true, forKey: "UIStateRestorationDebugLogging") - UserDefaults.standard.set(true, forKey: "UIStateRestorationDeveloperMode") - return shouldPerformAdditionalDelegateHandling } diff --git a/App/iOS/Delegates/SceneDelegate.swift b/App/iOS/Delegates/SceneDelegate.swift index e6e1e48dfee..54cb9683a78 100644 --- a/App/iOS/Delegates/SceneDelegate.swift +++ b/App/iOS/Delegates/SceneDelegate.swift @@ -155,7 +155,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func sceneDidDisconnect(_ scene: UIScene) { - print("SCENE DISCONNECTED") + log.debug("SCENE DISCONNECTED") } func sceneDidBecomeActive(_ scene: UIScene) { diff --git a/Sources/Brave/Frontend/Browser/TabManager.swift b/Sources/Brave/Frontend/Browser/TabManager.swift index 35f262f3637..bfc97b75caf 100644 --- a/Sources/Brave/Frontend/Browser/TabManager.swift +++ b/Sources/Brave/Frontend/Browser/TabManager.swift @@ -947,7 +947,7 @@ class TabManager: NSObject { tab.setScreenshot(savedTab.screenshot) Task { @MainActor in - tab.favicon = try await FaviconFetcher.loadIcon(url: tabURL, kind: .smallIcon, persistent: tab.isPrivate) + tab.favicon = try await FaviconFetcher.loadIcon(url: tabURL, kind: .smallIcon, persistent: !tab.isPrivate) tab.setScreenshot(savedTab.screenshot) } diff --git a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift index 5c050670eb7..5f3add9d2a7 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift @@ -609,7 +609,7 @@ class TabTrayController: LoadingViewController { tabTypeSelector.selectedSegmentIndex = 0 tabTypeSelector.sendActions(for: UIControl.Event.valueChanged) - if !Preferences.Privacy.persistentPrivateBrowsing.value { + if !Preferences.Privacy.persistentPrivateBrowsing.value || tabManager.tabsForCurrentMode.isEmpty { 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. diff --git a/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift b/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift index 721e8dd139c..f214e228fe4 100644 --- a/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift +++ b/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift @@ -63,10 +63,10 @@ struct OtherPrivacySettingsSectionView: View { 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) + SessionTab.deleteAll(tabIds: tabs.map({ $0.id })) + + if !settings.tabManager.privateBrowsingManager.isPrivateBrowsing { + settings.tabManager.willSwitchTabMode(leavingPBM: true) } } } diff --git a/Sources/Brave/States/AppState.swift b/Sources/Brave/States/AppState.swift index 7aabf82fd3f..e07a73ed108 100644 --- a/Sources/Brave/States/AppState.swift +++ b/Sources/Brave/States/AppState.swift @@ -1,9 +1,7 @@ -// -// AppState.swift -// -// -// Created by Brandon T on 2023-05-15. -// +// Copyright 2023 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/. import Foundation import UIKit @@ -14,10 +12,7 @@ import Shared import Growth import Preferences import Storage - -#if DEBUG -import os -#endif +import os.log /// Class that does startup initialization /// Everything in this class can only be execute ONCE @@ -30,14 +25,20 @@ public class AppState { public let migration: Migration public let profile: Profile public let rewards: BraveRewards + private var didBecomeActive = false public var state: State = .launching(options: [:], active: false) { didSet { switch state { case .launching(_, let isActive): - if isActive { + if didBecomeActive { + assertionFailure("Cannot set launching state twice!") + } + + if isActive && !didBecomeActive { // We have to wait until pre 1.12 migration is done until we proceed with database // initialization. This is because Database container may change. See bugs #3416, #3377. + didBecomeActive = true DataController.shared.initializeOnce() Migration.postCoreDataInitMigrations() Migration.migrateTabStateToWebkitState(diskImageStore: diskImageStore) @@ -103,18 +104,6 @@ public class AppState { } private static func setupConstants() { - // Application Constants must be initialized first -// #if MOZ_CHANNEL_RELEASE -// AppConstants.buildChannel = .release -// #elseif MOZ_CHANNEL_BETA -// AppConstants.buildChannel = .beta -// #elseif MOZ_CHANNEL_DEV -// AppConstants.buildChannel = .dev -// #elseif MOZ_CHANNEL_ENTERPRISE -// AppConstants.buildChannel = .enterprise -// #elseif MOZ_CHANNEL_DEBUG -// AppConstants.buildChannel = .debug -// #endif } private static func setupBraveCore() -> BraveCoreMain { diff --git a/Sources/Brave/States/BrowserState.swift b/Sources/Brave/States/BrowserState.swift index 4c0dde43fda..c2165f18a45 100644 --- a/Sources/Brave/States/BrowserState.swift +++ b/Sources/Brave/States/BrowserState.swift @@ -1,9 +1,7 @@ -// -// BrowserState.swift -// -// -// Created by Brandon T on 2023-05-15. -// +// Copyright 2023 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/. import Foundation import UIKit @@ -13,7 +11,6 @@ public class BrowserState { let window: UIWindow let profile: Profile - //let browser: BrowserViewController init(window: UIWindow, profile: Profile) { self.window = window From 61d21736602a2426398194e3770596ab42d17419 Mon Sep 17 00:00:00 2001 From: Brandon T Date: Mon, 19 Jun 2023 19:55:51 -0400 Subject: [PATCH 03/13] Fix Brave-News not syncing across multiple windows. --- App/iOS/Delegates/SceneDelegate.swift | 12 +++++++++++- .../Frontend/Browser/BrowserViewController.swift | 4 +++- Sources/Brave/States/AppState.swift | 3 +++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/App/iOS/Delegates/SceneDelegate.swift b/App/iOS/Delegates/SceneDelegate.swift index 54cb9683a78..61ef8023152 100644 --- a/App/iOS/Delegates/SceneDelegate.swift +++ b/App/iOS/Delegates/SceneDelegate.swift @@ -17,6 +17,7 @@ import BraveVPN import Growth import os.log import BraveCore +import BraveNews import Preferences class SceneDelegate: UIResponder, UIWindowSceneDelegate { @@ -40,6 +41,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { diskImageStore: AppState.shared.diskImageStore, migration: AppState.shared.migration, rewards: AppState.shared.rewards, + newsFeedDataSource: AppState.shared.newsFeedDataSource, userActivity: connectionOptions.userActivities.first ?? session.stateRestorationActivity ) @@ -382,7 +384,14 @@ extension SceneDelegate { } extension SceneDelegate { - private func createBrowserWindow(scene: UIWindowScene, braveCore: BraveCoreMain, profile: Profile, diskImageStore: DiskImageStore?, migration: Migration?, rewards: Brave.BraveRewards, userActivity: NSUserActivity?) -> BrowserViewController { + private func createBrowserWindow(scene: UIWindowScene, + braveCore: BraveCoreMain, + profile: Profile, + diskImageStore: DiskImageStore?, + migration: Migration?, + rewards: Brave.BraveRewards, + newsFeedDataSource: BraveNews.FeedDataSource, + userActivity: NSUserActivity?) -> BrowserViewController { let privateBrowsingManager = PrivateBrowsingManager() @@ -468,6 +477,7 @@ extension SceneDelegate { migration: migration, crashedLastSession: crashedLastSession, rewards: rewards, + newsFeedDataSource: newsFeedDataSource, privateBrowsingManager: privateBrowsingManager ) diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController.swift b/Sources/Brave/Frontend/Browser/BrowserViewController.swift index 9279f6eaecd..7e45a441eed 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController.swift @@ -282,6 +282,7 @@ public class BrowserViewController: UIViewController { migration: Migration?, crashedLastSession: Bool, rewards: BraveRewards, + newsFeedDataSource: FeedDataSource, privateBrowsingManager: PrivateBrowsingManager ) { self.windowId = windowId @@ -293,6 +294,7 @@ public class BrowserViewController: UIViewController { self.crashedLastSession = crashedLastSession self.rewards = rewards self.privateBrowsingManager = privateBrowsingManager + self.feedDataSource = newsFeedDataSource feedDataSource.historyAPI = braveCore.historyAPI backgroundDataSource = .init(service: braveCore.backgroundImagesService) @@ -350,7 +352,7 @@ public class BrowserViewController: UIViewController { } } - feedDataSource.ads = rewards.ads + self.feedDataSource.ads = rewards.ads // Observer watching tab information is sent by another device openTabsModelStateListener = braveCore.sendTabAPI.add( diff --git a/Sources/Brave/States/AppState.swift b/Sources/Brave/States/AppState.swift index e07a73ed108..53055fc9af6 100644 --- a/Sources/Brave/States/AppState.swift +++ b/Sources/Brave/States/AppState.swift @@ -12,6 +12,7 @@ import Shared import Growth import Preferences import Storage +import BraveNews import os.log /// Class that does startup initialization @@ -25,6 +26,7 @@ public class AppState { public let migration: Migration public let profile: Profile public let rewards: BraveRewards + public let newsFeedDataSource: FeedDataSource private var didBecomeActive = false public var state: State = .launching(options: [:], active: false) { @@ -91,6 +93,7 @@ public class AppState { let configuration = BraveRewards.Configuration.current() Self.migrateAdsConfirmations(for: configuration) rewards = BraveRewards(configuration: configuration) + newsFeedDataSource = FeedDataSource() // Setup Custom URL scheme handlers setupCustomSchemeHandlers(profile: profile) From 72e49796bc4866d00ff282baf7a60aaa789a3d9a Mon Sep 17 00:00:00 2001 From: Brandon T Date: Mon, 19 Jun 2023 20:06:07 -0400 Subject: [PATCH 04/13] Fix shields not reloading tabs on other windows --- ...rowserViewController+ToolbarDelegate.swift | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+ToolbarDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+ToolbarDelegate.swift index 2c4002ba24c..806c9c0ffbe 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+ToolbarDelegate.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+ToolbarDelegate.swift @@ -449,12 +449,21 @@ extension BrowserViewController: TopToolbarDelegate { let shields = ShieldsViewController(tab: selectedTab) shields.shieldsSettingsChanged = { [unowned self] _, shield in - // Update the shields status immediately - self.topToolbar.refreshShieldsStatus() - - // Reload this tab. This will also trigger an update of the brave icon in `TabLocationView` if - // the setting changed is the global `.AllOff` shield - self.tabManager.selectedTab?.reload() + let currentDomain = self.tabManager.selectedTab?.url?.baseDomain + let browsers = UIApplication.shared.connectedScenes.compactMap({ $0 as? UIWindowScene }).compactMap({ $0.browserViewController }) + + browsers.forEach { browser in + // Update the shields status immediately + browser.topToolbar.refreshShieldsStatus() + + // Reload the tabs. This will also trigger an update of the brave icon in `TabLocationView` if + // the setting changed is the global `.AllOff` shield + browser.tabManager.allTabs.forEach { + if $0.url?.baseDomain == currentDomain { + $0.reload() + } + } + } // Record P3A shield changes DispatchQueue.main.asyncAfter(deadline: .now() + 1) { From 2cf3575274daab3036fb5b4d139c51f7970b7045 Mon Sep 17 00:00:00 2001 From: Brandon T Date: Mon, 19 Jun 2023 20:14:44 -0400 Subject: [PATCH 05/13] Fix playlist URL bar not updating on another window when added on a different window. --- .../Browser/BrowserViewController.swift | 5 ++- .../Browser/HomePanel/NTPDataSource.swift | 5 ++- .../Sections/BraveNewsSectionProvider.swift | 18 ++------ .../Sections/StatsSectionProvider.swift | 24 +++++------ .../BrowserViewController+Playlist.swift | 43 ++++++++++--------- .../PrivacyHub/PrivacyReportsManager.swift | 8 ++-- .../Browser/Search/SearchEngines.swift | 4 +- .../Toolbars/UrlBar/TopToolbarView.swift | 8 ++-- .../SearchQuickEnginesViewController.swift | 2 +- Tests/ClientTests/SearchEnginesTests.swift | 22 +++++----- 10 files changed, 65 insertions(+), 74 deletions(-) diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController.swift b/Sources/Brave/Frontend/Browser/BrowserViewController.swift index 7e45a441eed..9ded57017ed 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController.swift @@ -133,7 +133,7 @@ public class BrowserViewController: UIViewController { // Single data source used for all favorites vcs public let backgroundDataSource: NTPDataSource - let feedDataSource = FeedDataSource() + let feedDataSource: FeedDataSource private var postSetupTasks: [() -> Void] = [] private var setupTasksCompleted: Bool = false @@ -296,7 +296,8 @@ public class BrowserViewController: UIViewController { self.privateBrowsingManager = privateBrowsingManager self.feedDataSource = newsFeedDataSource feedDataSource.historyAPI = braveCore.historyAPI - backgroundDataSource = .init(service: braveCore.backgroundImagesService) + backgroundDataSource = .init(service: braveCore.backgroundImagesService, + privateBrowsingManager: privateBrowsingManager) // Initialize TabManager self.tabManager = TabManager( diff --git a/Sources/Brave/Frontend/Browser/HomePanel/NTPDataSource.swift b/Sources/Brave/Frontend/Browser/HomePanel/NTPDataSource.swift index 657c408dd53..c8cc2b2bee9 100644 --- a/Sources/Brave/Frontend/Browser/HomePanel/NTPDataSource.swift +++ b/Sources/Brave/Frontend/Browser/HomePanel/NTPDataSource.swift @@ -53,7 +53,7 @@ enum NTPWallpaper { public class NTPDataSource { - var privateBrowsingManager: PrivateBrowsingManager + private(set) var privateBrowsingManager: PrivateBrowsingManager var initializeFavorites: ((_ sites: [NTPSponsoredImageTopSite]?) -> Void)? @@ -87,8 +87,9 @@ public class NTPDataSource { let service: NTPBackgroundImagesService - public init(service: NTPBackgroundImagesService) { + public init(service: NTPBackgroundImagesService, privateBrowsingManager: PrivateBrowsingManager) { self.service = service + self.privateBrowsingManager = privateBrowsingManager Preferences.NewTabPage.selectedCustomTheme.observe(from: self) diff --git a/Sources/Brave/Frontend/Browser/New Tab Page/Sections/BraveNewsSectionProvider.swift b/Sources/Brave/Frontend/Browser/New Tab Page/Sections/BraveNewsSectionProvider.swift index 4af7eca05ae..b0a09dc15ca 100644 --- a/Sources/Brave/Frontend/Browser/New Tab Page/Sections/BraveNewsSectionProvider.swift +++ b/Sources/Brave/Frontend/Browser/New Tab Page/Sections/BraveNewsSectionProvider.swift @@ -429,14 +429,9 @@ class BraveNewsSectionProvider: NSObject, NTPObservableSectionProvider { var openInNewPrivateTab: UIAction { .init(title: Strings.openNewPrivateTabButtonTitle, image: UIImage(braveSystemNamed: "leo.product.private-window"), handler: mapDeferredHandler(openInNewPrivateTabHandler)) } - let openActions: [UIAction] = [ - openInNewTab, - // Brave News is only available in normal tabs, so this isn't technically required - // but good to be on the safe side - openInNewPrivateTab, - ].compactMap { $0 } + let children: [UIMenu] = [ - UIMenu(title: "", options: [.displayInline], children: openActions) + UIMenu(title: "", options: [.displayInline], children: [openInNewTab, openInNewPrivateTab]) ] return UIMenu(title: ad.targetURL, children: children) } @@ -486,19 +481,12 @@ class BraveNewsSectionProvider: NSObject, NTPObservableSectionProvider { .init(title: String(format: Strings.BraveNews.enablePublisherContent, item.source.name), image: UIImage(braveSystemNamed: "leo.eye.on"), handler: mapDeferredHandler(toggleSourceHandler)) } - let openActions: [UIAction] = [ - openInNewTab, - // Brave News is only available in normal tabs, so this isn't technically required - // but good to be on the safe side - openInNewPrivateTab, - ].compactMap({ $0 }) - let manageActions = [ self.dataSource.isSourceHidden(item.source) ? enableSource : disableSource ] var children: [UIMenu] = [ - UIMenu(title: "", options: [.displayInline], children: openActions) + UIMenu(title: "", options: [.displayInline], children: [openInNewTab, openInNewPrivateTab]) ] if context.item.content.contentType != .sponsor { children.append(UIMenu(title: "", options: [.displayInline], children: manageActions)) diff --git a/Sources/Brave/Frontend/Browser/New Tab Page/Sections/StatsSectionProvider.swift b/Sources/Brave/Frontend/Browser/New Tab Page/Sections/StatsSectionProvider.swift index 0e1443fcc9f..fe41ddb8b59 100644 --- a/Sources/Brave/Frontend/Browser/New Tab Page/Sections/StatsSectionProvider.swift +++ b/Sources/Brave/Frontend/Browser/New Tab Page/Sections/StatsSectionProvider.swift @@ -150,7 +150,7 @@ class BraveShieldStatsView: SpringButton { }() } - private let background = UIView() + private let backgroundView = UIView() override init(frame: CGRect) { super.init(frame: .zero) @@ -173,28 +173,24 @@ class BraveShieldStatsView: SpringButton { fatalError("init(coder:) has not been implemented") } - private var _isPrivateBrowsing: Bool = false - var isPrivateBrowsing: Bool = false { didSet { - if _isPrivateBrowsing == isPrivateBrowsing { + if oldValue == isPrivateBrowsing { return } - - _isPrivateBrowsing = isPrivateBrowsing - + if isPrivateBrowsing { - background.removeFromSuperview() + backgroundView.removeFromSuperview() topStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } } else if Preferences.NewTabPage.showNewTabPrivacyHub.value { - background.backgroundColor = .init(white: 0, alpha: 0.25) - background.layer.cornerRadius = 12 - background.layer.cornerCurve = .continuous - background.isUserInteractionEnabled = false - insertSubview(background, at: 0) - background.snp.makeConstraints { + backgroundView.backgroundColor = .init(white: 0, alpha: 0.25) + backgroundView.layer.cornerRadius = 12 + backgroundView.layer.cornerCurve = .continuous + backgroundView.isUserInteractionEnabled = false + insertSubview(backgroundView, at: 0) + backgroundView.snp.makeConstraints { $0.edges.equalToSuperview() } diff --git a/Sources/Brave/Frontend/Browser/Playlist/Browser/BrowserViewController+Playlist.swift b/Sources/Brave/Frontend/Browser/Playlist/Browser/BrowserViewController+Playlist.swift index 4e76e80d4b2..0fcca931f4a 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Browser/BrowserViewController+Playlist.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Browser/BrowserViewController+Playlist.swift @@ -61,32 +61,35 @@ extension BrowserViewController: PlaylistScriptHandlerDelegate, PlaylistFolderSh guard let tab = tab else { return } if tab === tabManager.selectedTab { - openInPlayListActivity(info: state == .existingItem ? item : nil) - addToPlayListActivity(info: state == .newItem ? item : nil, itemDetected: state == .newItem) - tab.playlistItemState = state tab.playlistItem = item let shouldShowPlaylistURLBarButton = tab.url?.isPlaylistSupportedSiteURL == true && Preferences.Playlist.enablePlaylistURLBarButton.value - switch state { - case .none: - topToolbar.updatePlaylistButtonState(.none) - topToolbar.menuButton.removeBadge(.playlist, animated: true) - toolbar?.menuButton.removeBadge(.playlist, animated: true) - case .newItem: - topToolbar.updatePlaylistButtonState(shouldShowPlaylistURLBarButton ? .addToPlaylist : .none) - if Preferences.Playlist.enablePlaylistMenuBadge.value { - topToolbar.menuButton.addBadge(.playlist, animated: true) - toolbar?.menuButton.addBadge(.playlist, animated: true) - } else { - topToolbar.menuButton.removeBadge(.playlist, animated: true) - toolbar?.menuButton.removeBadge(.playlist, animated: true) + let browsers = UIApplication.shared.connectedScenes.compactMap({ $0 as? UIWindowScene }).compactMap({ $0.browserViewController }) + browsers.forEach { browser in + browser.openInPlayListActivity(info: state == .existingItem ? item : nil) + browser.addToPlayListActivity(info: state == .newItem ? item : nil, itemDetected: state == .newItem) + + switch state { + case .none: + browser.topToolbar.updatePlaylistButtonState(.none) + browser.topToolbar.menuButton.removeBadge(.playlist, animated: true) + browser.toolbar?.menuButton.removeBadge(.playlist, animated: true) + case .newItem: + browser.topToolbar.updatePlaylistButtonState(shouldShowPlaylistURLBarButton ? .addToPlaylist : .none) + if Preferences.Playlist.enablePlaylistMenuBadge.value { + browser.topToolbar.menuButton.addBadge(.playlist, animated: true) + browser.toolbar?.menuButton.addBadge(.playlist, animated: true) + } else { + browser.topToolbar.menuButton.removeBadge(.playlist, animated: true) + browser.toolbar?.menuButton.removeBadge(.playlist, animated: true) + } + case .existingItem: + browser.topToolbar.updatePlaylistButtonState(shouldShowPlaylistURLBarButton ? .addedToPlaylist(item) : .none) + browser.topToolbar.menuButton.removeBadge(.playlist, animated: true) + browser.toolbar?.menuButton.removeBadge(.playlist, animated: true) } - case .existingItem: - topToolbar.updatePlaylistButtonState(shouldShowPlaylistURLBarButton ? .addedToPlaylist(item) : .none) - topToolbar.menuButton.removeBadge(.playlist, animated: true) - toolbar?.menuButton.removeBadge(.playlist, animated: true) } } } diff --git a/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportsManager.swift b/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportsManager.swift index 5d583feb08d..41119e82940 100644 --- a/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportsManager.swift +++ b/Sources/Brave/Frontend/Browser/PrivacyHub/PrivacyReportsManager.swift @@ -19,9 +19,7 @@ public struct PrivacyReportsManager { /// Instead a periodic timer is run and all requests gathered during this timeframe are saved in one database transaction. public static var pendingBlockedRequests: [(host: String, domain: URL, date: Date)] = [] - public static func processBlockedRequests(isPrivateBrowsing: Bool) { - if isPrivateBrowsing { return } - + private static func processBlockedRequests() { let itemsToSave = pendingBlockedRequests pendingBlockedRequests.removeAll() @@ -41,7 +39,9 @@ public struct PrivacyReportsManager { let timeInterval = AppConstants.buildChannel.isPublic ? 60.0 : 10.0 saveBlockedResourcesTimer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: true) { _ in - processBlockedRequests(isPrivateBrowsing: isPrivateBrowsing) + if !isPrivateBrowsing { + processBlockedRequests() + } } } diff --git a/Sources/Brave/Frontend/Browser/Search/SearchEngines.swift b/Sources/Brave/Frontend/Browser/Search/SearchEngines.swift index 8d318719aed..aaad03c9209 100644 --- a/Sources/Brave/Frontend/Browser/Search/SearchEngines.swift +++ b/Sources/Brave/Frontend/Browser/Search/SearchEngines.swift @@ -190,8 +190,8 @@ public class SearchEngines { disabledEngineNames.removeValue(forKey: engine.shortName) } - func disableEngine(_ engine: OpenSearchEngine, isPrivateBrowsing: Bool) { - if isEngineDefault(engine, type: isPrivateBrowsing ? .privateMode : .standard) { + func disableEngine(_ engine: OpenSearchEngine, type: DefaultEngineType) { + if isEngineDefault(engine, type: type) { // Can't disable default engine. return } diff --git a/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TopToolbarView.swift b/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TopToolbarView.swift index 81b6f204912..1b3fc2fa259 100644 --- a/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TopToolbarView.swift +++ b/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TopToolbarView.swift @@ -70,6 +70,7 @@ class TopToolbarView: UIView, ToolbarProtocol { private var cancellables: Set = [] private var privateModeCancellable: AnyCancellable? + private let privateBrowsingManager: PrivateBrowsingManager private(set) var displayTabTraySwipeGestureRecognizer: UISwipeGestureRecognizer? @@ -119,7 +120,7 @@ class TopToolbarView: UIView, ToolbarProtocol { private var locationTextField: AutocompleteTextField? - lazy var locationView = TabLocationView(voiceSearchSupported: isVoiceSearchAvailable).then { + lazy var locationView = TabLocationView(voiceSearchSupported: isVoiceSearchAvailable, privateBrowsingManager: privateBrowsingManager).then { $0.translatesAutoresizingMaskIntoConstraints = false $0.readerModeState = ReaderModeState.unavailable $0.delegate = self @@ -245,8 +246,9 @@ class TopToolbarView: UIView, ToolbarProtocol { // MARK: Lifecycle - init(voiceSearchSupported: Bool) { + init(voiceSearchSupported: Bool, privateBrowsingManager: PrivateBrowsingManager) { isVoiceSearchAvailable = voiceSearchSupported + self.privateBrowsingManager = privateBrowsingManager super.init(frame: .zero) @@ -652,7 +654,7 @@ class TopToolbarView: UIView, ToolbarProtocol { var shieldIcon = "brave.logo" let shieldsOffIcon = "brave.logo.greyscale" if let currentURL = currentURL { - let isPrivateBrowsing = PrivateBrowsingManager.shared.isPrivateBrowsing + let isPrivateBrowsing = privateBrowsingManager.isPrivateBrowsing let domain = Domain.getOrCreate(forUrl: currentURL, persistent: !isPrivateBrowsing) if domain.areAllShieldsOff { shieldIcon = shieldsOffIcon diff --git a/Sources/Brave/Frontend/Settings/SearchQuickEnginesViewController.swift b/Sources/Brave/Frontend/Settings/SearchQuickEnginesViewController.swift index 2576854a495..85ac796cf25 100644 --- a/Sources/Brave/Frontend/Settings/SearchQuickEnginesViewController.swift +++ b/Sources/Brave/Frontend/Settings/SearchQuickEnginesViewController.swift @@ -144,7 +144,7 @@ extension SearchQuickEnginesViewController { if toggle.isOn { searchEngines.enableEngine(engine) } else { - searchEngines.disableEngine(engine, isPrivateBrowsing: isPrivateBrowsing) + searchEngines.disableEngine(engine, type: isPrivateBrowsing ? .privateMode : .standard) } } } diff --git a/Tests/ClientTests/SearchEnginesTests.swift b/Tests/ClientTests/SearchEnginesTests.swift index 35b4aa3937d..be470b2502c 100644 --- a/Tests/ClientTests/SearchEnginesTests.swift +++ b/Tests/ClientTests/SearchEnginesTests.swift @@ -45,7 +45,7 @@ class SearchEnginesTests: XCTestCase { // If this is our first run, Google should be first for the en locale. let profile = MockProfile() let engines = SearchEngines(files: profile.files, locale: Locale(identifier: "pl_PL")) - XCTAssertEqual(engines.defaultEngine().shortName, DefaultSearchEngineName) + XCTAssertEqual(engines.defaultEngine(forType: .standard).shortName, DefaultSearchEngineName) // The default is `DefaultSearchEngineName` for both regular and private browsing. // Different search engine options might apply to certain regions. // Default locale for running tests should be en_US. @@ -74,20 +74,20 @@ class SearchEnginesTests: XCTestCase { let engineSet = engines.orderedEngines engines.updateDefaultEngine((engineSet?[0])!.shortName, forType: .standard) - XCTAssertTrue(engines.isEngineDefault((engineSet?[0])!)) - XCTAssertFalse(engines.isEngineDefault((engineSet?[1])!)) + XCTAssertTrue(engines.isEngineDefault((engineSet?[0])!, type: .standard)) + XCTAssertFalse(engines.isEngineDefault((engineSet?[1])!, type: .standard)) // The first ordered engine is the default. XCTAssertEqual(engines.orderedEngines[0].shortName, engineSet?[0].shortName) engines.updateDefaultEngine((engineSet?[1])!.shortName, forType: .standard) - XCTAssertFalse(engines.isEngineDefault((engineSet?[0])!)) - XCTAssertTrue(engines.isEngineDefault((engineSet?[1])!)) + XCTAssertFalse(engines.isEngineDefault((engineSet?[0])!, type: .standard)) + XCTAssertTrue(engines.isEngineDefault((engineSet?[1])!, type: .standard)) // The first ordered engine is the default. XCTAssertEqual(engines.orderedEngines[0].shortName, engineSet?[1].shortName) let engines2 = SearchEngines(files: profile.files) // The default engine should have been persisted. - XCTAssertTrue(engines2.isEngineDefault((engineSet?[1])!)) + XCTAssertTrue(engines2.isEngineDefault((engineSet?[1])!, type: .standard)) // The first ordered engine is the default. XCTAssertEqual(engines.orderedEngines[0].shortName, engineSet?[1].shortName) } @@ -136,7 +136,7 @@ class SearchEnginesTests: XCTestCase { // You can't disable the default engine. engines.updateDefaultEngine((engineSet?[1])!.shortName, forType: .standard) - engines.disableEngine((engineSet?[1])!) + engines.disableEngine((engineSet?[1])!, type: .standard) XCTAssertTrue(engines.isEngineEnabled((engineSet?[1])!)) // The default engine is included in the quick search engines. @@ -147,7 +147,7 @@ class SearchEnginesTests: XCTestCase { XCTAssertTrue(engines.isEngineEnabled((engineSet?[0])!)) XCTAssertEqual(1, engines.quickSearchEngines.filter { engine in engine.shortName == engineSet?[0].shortName }.count) - engines.disableEngine((engineSet?[0])!) + engines.disableEngine((engineSet?[0])!, type: .standard) XCTAssertFalse(engines.isEngineEnabled((engineSet?[0])!)) XCTAssertEqual(0, engines.quickSearchEngines.filter { engine in engine.shortName == engineSet?[0].shortName }.count) @@ -161,7 +161,7 @@ class SearchEnginesTests: XCTestCase { // The enabling should be persisted. engines.enableEngine((engineSet?[2])!) - engines.disableEngine((engineSet?[1])!) + engines.disableEngine((engineSet?[1])!, type: .standard) engines.enableEngine((engineSet?[0])!) let engines2 = SearchEngines(files: profile.files) @@ -232,7 +232,7 @@ class SearchEnginesTests: XCTestCase { engines.migrateDefaultYahooSearchEngines() - XCTAssertTrue(engines.defaultEngine().shortName == "Yahoo") + XCTAssertTrue(engines.defaultEngine(forType: .standard).shortName == "Yahoo") XCTAssertTrue(engines.defaultEngine(forType: .privateMode).shortName == "Yahoo") // Testing Both standard and private mode Yahoo JAPAN SE @@ -242,7 +242,7 @@ class SearchEnginesTests: XCTestCase { engines.migrateDefaultYahooSearchEngines() - XCTAssertTrue(engines.defaultEngine().shortName == "Yahoo! JAPAN") + XCTAssertTrue(engines.defaultEngine(forType: .standard).shortName == "Yahoo! JAPAN") XCTAssertTrue(engines.defaultEngine(forType: .privateMode).shortName == "Yahoo! JAPAN") engines.updateDefaultEngine("Google", forType: .standard) From e2a56b128b5295d962673558ac3d8f9c69511a25 Mon Sep 17 00:00:00 2001 From: Brandon T Date: Fri, 23 Jun 2023 19:08:50 -0400 Subject: [PATCH 06/13] Adding drag-drop --- App/iOS/Delegates/SceneDelegate.swift | 18 +++++++ .../Browser/BrowserViewController.swift | 11 ++++ .../Brave/Frontend/Browser/TabManager.swift | 4 +- .../Tabs/TabBar/TabsBarViewController.swift | 50 +++++++++++++++++++ Sources/Data/models/SessionTab.swift | 17 +++++++ 5 files changed, 98 insertions(+), 2 deletions(-) diff --git a/App/iOS/Delegates/SceneDelegate.swift b/App/iOS/Delegates/SceneDelegate.swift index 61ef8023152..58cbe4baa59 100644 --- a/App/iOS/Delegates/SceneDelegate.swift +++ b/App/iOS/Delegates/SceneDelegate.swift @@ -491,6 +491,24 @@ extension SceneDelegate { // Remove Ad-Grant Reminders $0.removeScheduledAdGrantReminders() } + + if let tabIdString = userActivity?.userInfo?["TabID"] as? String, + let tabWindowId = userActivity?.userInfo?["TabWindowID"] as? String, + let tabId = UUID(uuidString: tabIdString) { + + let currentTabScene = UIApplication.shared.connectedScenes.compactMap({ $0 as? UIWindowScene }).filter({ + guard let sceneWindowId = $0.session.userInfo?["WindowID"] as? String else { + return false + } + + return sceneWindowId == tabWindowId + }).first + + if let currentTabScene = currentTabScene, let currentTabSceneBrowser = currentTabScene.browserViewController { + browserViewController.loadViewIfNeeded() + currentTabSceneBrowser.moveTab(tabId: tabId, to: browserViewController) + } + } return browserViewController } diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController.swift b/Sources/Brave/Frontend/Browser/BrowserViewController.swift index 9ded57017ed..2c38fc6bb42 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController.swift @@ -1869,6 +1869,17 @@ public class BrowserViewController: UIViewController { navigationToolbar.updatePageStatus(isPage) updateWebViewPageZoom(tab: tab) } + + public func moveTab(tabId: UUID, to browser: BrowserViewController) { + guard let tab = tabManager.allTabs.filter({ $0.id == tabId }).first, + let url = tab.url else { + return + } + + let isPrivate = tab.isPrivate + tabManager.removeTab(tab) + browser.tabManager.addTabsForURLs([url], zombie: false, isPrivate: isPrivate) + } public func switchToTabForURLOrOpen(_ url: URL, isPrivate: Bool = false, isPrivileged: Bool, isExternal: Bool = false) { if !isExternal { diff --git a/Sources/Brave/Frontend/Browser/TabManager.swift b/Sources/Brave/Frontend/Browser/TabManager.swift index bfc97b75caf..67ce8e422d4 100644 --- a/Sources/Brave/Frontend/Browser/TabManager.swift +++ b/Sources/Brave/Frontend/Browser/TabManager.swift @@ -92,9 +92,10 @@ class TabManager: NSObject { private let syncedTabsQueue = DispatchQueue(label: "synced-tabs-queue") private var syncTabsTask: DispatchWorkItem? private var metricsHeartbeat: Timer? - private let windowId: UUID public let privateBrowsingManager: PrivateBrowsingManager + let windowId: UUID + /// The property returning only existing tab is NTP for current mode var isBrowserEmptyForCurrentMode: Bool { get { @@ -629,7 +630,6 @@ class TabManager: NSObject { // reaches zero and destroys all its data. BraveWebView.removeNonPersistentStore() - configuration.websiteDataStore = WKWebsiteDataStore.nonPersistent() } } diff --git a/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift b/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift index 14286481cfa..05c3f0c7fd2 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift @@ -71,6 +71,8 @@ class TabsBarViewController: UIViewController { view.backgroundColor = Preferences.General.nightModeEnabled.value ? .nightModeBackground : .urlBarBackground collectionView.backgroundColor = view.backgroundColor + collectionView.dragDelegate = UIApplication.shared.supportsMultipleScenes ? self : nil + collectionView.dropDelegate = UIApplication.shared.supportsMultipleScenes ? self : nil tabManager?.addDelegate(self) @@ -429,6 +431,54 @@ extension TabsBarViewController: UICollectionViewDataSource { } } +// MARK: - UICollectionViewDragDelegate +extension TabsBarViewController: UICollectionViewDragDelegate, UICollectionViewDropDelegate { + func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] { + guard let tab = tabList[indexPath.row], + let windowId = tabManager?.windowId else { + return [] + } + + let userActivity = NSUserActivity(activityType: BrowserState.sceneId).then { + $0.addUserInfoEntries(from: [ + "TabID": tab.id.uuidString, + "TabWindowID": windowId.uuidString, + "isPrivate": tab.isPrivate + ]) + } + + let itemProvider = NSItemProvider(object: userActivity) + itemProvider.registerObject(userActivity, visibility: .all) + return [UIDragItem(itemProvider: itemProvider)] + } + + func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) { + guard coordinator.items.first?.sourceIndexPath == nil else { return } + let destinationIndexPath: IndexPath + + if let indexPath = coordinator.destinationIndexPath { + destinationIndexPath = indexPath + } else { + let section = max(collectionView.numberOfSections - 1, 0) + let row = collectionView.numberOfItems(inSection: section) + destinationIndexPath = IndexPath(row: max(row - 1, 0), section: section) + } + + if coordinator.proposal.operation == .move { + guard let item = coordinator.items.first else { return } + + // TODO: Figure out how to get the item here... + // LocalObject is nil because the tab is in another process? :S + + _ = coordinator.drop(item.dragItem, toItemAt: destinationIndexPath) + } + } + + func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal { + return .init(operation: .move, intent: .insertAtDestinationIndexPath) + } +} + // MARK: - TabManagerDelegate extension TabsBarViewController: TabManagerDelegate { func tabManager(_ tabManager: TabManager, didSelectedTabChange selected: Tab?, previous: Tab?) { diff --git a/Sources/Data/models/SessionTab.swift b/Sources/Data/models/SessionTab.swift index c087e433ba7..1ab593b212c 100644 --- a/Sources/Data/models/SessionTab.swift +++ b/Sources/Data/models/SessionTab.swift @@ -237,6 +237,23 @@ extension SessionTab { } } } + + public static func move(tab tabId: UUID, toWindow windowId: UUID) { + DataController.performOnMainContext { context in + guard let tab = SessionTab.from(tabId: tabId, in: context), + let window = SessionWindow.from(windowId: windowId, in: context) else { + return + } + + tab.sessionWindow = window + + do { + try context.save() + } catch { + Logger.module.error("performTask save error: \(error.localizedDescription, privacy: .public)") + } + } + } } // MARK: - Private From f6e57537bd0941d04131c7fe5d8aa90c0f7b534d Mon Sep 17 00:00:00 2001 From: Brandon T Date: Mon, 26 Jun 2023 15:42:21 -0400 Subject: [PATCH 07/13] Fix private mode search tabs bar disappearing --- .../Frontend/Browser/Tabs/TabTray/TabTrayController.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift index 5f3add9d2a7..cfd6282b0bb 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift @@ -614,6 +614,7 @@ class TabTrayController: LoadingViewController { // 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) + navigationController?.setNavigationBarHidden(true, animated: false) } else { if tabManager.tabsForCurrentMode.isEmpty { tabManager.addTabAndSelect(isPrivate: true) @@ -621,6 +622,7 @@ class TabTrayController: LoadingViewController { tabTrayView.hidePrivateModeInfo() tabTrayView.collectionView.reloadData() + navigationController?.setNavigationBarHidden(false, animated: false) } } else { tabTrayView.hidePrivateModeInfo() @@ -629,11 +631,10 @@ class TabTrayController: LoadingViewController { // Reloding the collection view in order to mark the selecte the tab tabManager.selectTab(tabManager.tabsForCurrentMode[safe: tabManager.normalTabSelectedIndex]) tabTrayView.collectionView.reloadData() + navigationController?.setNavigationBarHidden(false, animated: false) } - navigationController?.setNavigationBarHidden(privateMode, animated: false) tabTypeSelector.isHidden = privateMode - } func remove(tab: Tab) { From 798bf3176d685f8920e9d192ce8cdb6d539aa7cf Mon Sep 17 00:00:00 2001 From: Brandon T Date: Tue, 27 Jun 2023 14:07:26 -0400 Subject: [PATCH 08/13] Block playlist from showing twice. --- .../BrowserViewController+Menu.swift | 13 +++++++++++++ .../Controllers/PlaylistViewController.swift | 2 ++ .../Managers & Cache/PlaylistCarplayManager.swift | 1 + Sources/BraveStrings/BraveStrings.swift | 10 ++++++++++ 4 files changed, 26 insertions(+) diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Menu.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Menu.swift index c936c44dbd1..78bfd6c4deb 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Menu.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+Menu.swift @@ -201,11 +201,23 @@ extension BrowserViewController { } private func presentPlaylistController() { + if PlaylistCarplayManager.shared.isPlaylistControllerPresented { + let alert = UIAlertController(title: Strings.PlayList.playlistAlreadyShowingTitle, + message: Strings.PlayList.playlistAlreadyShowingBody, + preferredStyle: .alert) + alert.addAction(UIAlertAction(title: Strings.OKString, style: .default)) + dismiss(animated: true) { + self.present(alert, animated: true) + } + return + } + // Present existing playlist controller if let playlistController = PlaylistCarplayManager.shared.playlistController { PlaylistP3A.recordUsage() dismiss(animated: true) { + PlaylistCarplayManager.shared.isPlaylistControllerPresented = true self.present(playlistController, animated: true) } } else { @@ -218,6 +230,7 @@ extension BrowserViewController { PlaylistP3A.recordUsage() self.dismiss(animated: true) { + PlaylistCarplayManager.shared.isPlaylistControllerPresented = true self.present(playlistController, animated: true) } } diff --git a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift index 199dbe8ac4b..7649791dea1 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift @@ -93,6 +93,8 @@ class PlaylistViewController: UIViewController { } deinit { + PlaylistCarplayManager.shared.isPlaylistControllerPresented = false + // Store the last played item's time-offset if let item = PlaylistCarplayManager.shared.currentPlaylistItem { updateLastPlayedItem(item: item) diff --git a/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift b/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift index 35583f27bc0..07c8f1c27d4 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift @@ -28,6 +28,7 @@ public class PlaylistCarplayManager: NSObject { var currentlyPlayingItemIndex = -1 var currentPlaylistItem: PlaylistInfo? + var isPlaylistControllerPresented = false // When Picture-In-Picture is enabled, we need to store a reference to the controller to keep it alive, otherwise if it deallocates, the system automatically kills Picture-In-Picture. var playlistController: PlaylistViewController? { diff --git a/Sources/BraveStrings/BraveStrings.swift b/Sources/BraveStrings/BraveStrings.swift index 31dc607a88a..afe2df9e067 100644 --- a/Sources/BraveStrings/BraveStrings.swift +++ b/Sources/BraveStrings/BraveStrings.swift @@ -1902,6 +1902,16 @@ extension Strings { NSLocalizedString("playlist.deleteForOfflineButtonTitle", tableName: "BraveShared", bundle: .module, value: "Delete Offline Cache", comment: "The title of the button indicating that the user delete the offline data. (deletes the data that allows them to play offline)") + + public static let playlistAlreadyShowingTitle = + NSLocalizedString("playlist.playlistAlreadyShowingTitle", tableName: "BraveShared", bundle: .module, + value: "Sorry", + comment: "Playlist alert title when playlist is already showing on a different window") + + public static let playlistAlreadyShowingBody = + NSLocalizedString("playlist.playlistAlreadyShowingBody", tableName: "BraveShared", bundle: .module, + value: "Playlist is already active on another window", + comment: "Playlist alert message when playlist is already showing on a different window") } public struct PlaylistFolders { From 366802124b0e696b360a0b1250ba400c89756b84 Mon Sep 17 00:00:00 2001 From: Brandon T Date: Tue, 4 Jul 2023 11:06:51 -0400 Subject: [PATCH 09/13] Add persistent private browsing alert for existing users Move AppState to the same location as delegates Open context menus like Safari to allow opening URLs in new windows Moved alert for existing users and updated text. Updated icons with leo-sfsymbol --- App/Client.xcodeproj/project.pbxproj | 4 +++ App/iOS/Delegates/AppDelegate.swift | 3 ++ .../iOS/Delegates}/AppState.swift | 13 ++++--- App/iOS/Delegates/SceneDelegate.swift | 11 ++++-- .../Browser/BrowserViewController.swift | 36 ++++++++++--------- ...serViewController+TabManagerDelegate.swift | 4 --- ...rViewController+WKNavigationDelegate.swift | 26 ++++++++++++-- ...ListViewController+TableViewDelegate.swift | 4 +-- .../PlaylistListViewController.swift | 12 +++---- .../Controllers/PlaylistViewController.swift | 18 +++++----- .../PlaylistCarplayManager.swift | 2 +- Sources/Brave/Frontend/Browser/Tab.swift | 1 - .../Tabs/TabBar/TabsBarViewController.swift | 5 --- .../Tabs/TabTray/TabTrayController.swift | 16 +++++++++ .../Brave/Frontend/ClientPreferences.swift | 4 ++- .../Brave/Frontend/Rewards/BraveRewards.swift | 2 +- .../OtherPrivacySettingsSectionView.swift | 20 ++++++----- Sources/BraveStrings/BraveStrings.swift | 4 +++ .../leo.window.symbolset/Contents.json | 11 ++++++ .../Contents.json | 11 ++++++ ...ookieNotificationBlockingConsentView.swift | 2 -- Tests/ClientTests/NavigationRouterTests.swift | 26 +++++++------- Tests/ClientTests/SearchEnginesTests.swift | 2 -- Tests/ClientTests/TabManagerTests.swift | 7 ++-- Tests/ClientTests/TabSessionTests.swift | 3 +- Tests/ClientTests/TestFavicons.swift | 2 +- package-lock.json | 14 ++++---- package.json | 2 +- 28 files changed, 172 insertions(+), 93 deletions(-) rename {Sources/Brave/States => App/iOS/Delegates}/AppState.swift (93%) create mode 100644 Sources/DesignSystem/Icons/Symbols.xcassets/leo.window.symbolset/Contents.json create mode 100644 Sources/DesignSystem/Icons/Symbols.xcassets/leo.window.tab-private.symbolset/Contents.json 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 2c38fc6bb42..16728c244a4 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 @@ -1953,6 +1951,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 @@ -2497,21 +2514,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 f2b30d5d671..2e69b309a2c 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 416d2055b1d..870e18a5972 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift @@ -1002,7 +1002,6 @@ extension BrowserViewController: WKUIDelegate { } openNewTabAction.accessibilityLabel = "linkContextMenu.openInNewTab" - actions.append(openNewTabAction) } @@ -1013,8 +1012,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 05c3f0c7fd2..52721a9937c 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 cfd6282b0bb..56ae90544e4 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift @@ -624,6 +624,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 57432a61399..bf4b420cd72 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 afe2df9e067..5f0a12c0ad0 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 @@ -1104,6 +1106,8 @@ extension Strings { public static let youtubeMediaQualityOn = NSLocalizedString("YoutubeMediaQualityOn", tableName: "BraveShared", bundle: .module, value: "On", 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" }, From 31f1c5ffadf425f88fa556479cf3ca051cc49a91 Mon Sep 17 00:00:00 2001 From: Brandon T Date: Wed, 12 Jul 2023 12:05:39 -0400 Subject: [PATCH 10/13] Add context menu to be on par with Safari. --- .../Brave/Frontend/Browser/BrowserViewController.swift | 4 ++++ .../Browser/Tabs/TabBar/TabsBarViewController.swift | 10 +++++++++- Sources/BraveStrings/BraveStrings.swift | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController.swift b/Sources/Brave/Frontend/Browser/BrowserViewController.swift index 16728c244a4..6c2c84b9982 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController.swift @@ -2543,6 +2543,10 @@ extension BrowserViewController: TabsBarViewControllerDelegate { break } } + + func tabsBarDidSelectAddNewWindow(_ isPrivate: Bool) { + self.openInNewWindow(url: nil, isPrivate: isPrivate) + } } extension BrowserViewController: TabDelegate { diff --git a/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift b/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift index 52721a9937c..6264b8c2fc6 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabBar/TabsBarViewController.swift @@ -14,7 +14,7 @@ protocol TabsBarViewControllerDelegate: AnyObject { func tabsBarDidLongPressAddTab(_ tabsBarController: TabsBarViewController, button: UIButton) func tabsBarDidSelectAddNewTab(_ isPrivate: Bool) func tabsBarDidChangeReaderModeVisibility(_ isHidden: Bool) - + func tabsBarDidSelectAddNewWindow(_ isPrivate: Bool) } class TabsBarViewController: UIViewController { @@ -123,6 +123,14 @@ class TabsBarViewController: UIViewController { }) newTabMenu.append(openNewTab) + + newTabMenu.append(UIAction(title: Strings.newWindowTitle, image: UIImage(braveSystemNamed: "leo.window"), handler: UIAction.deferredActionHandler { [unowned self] _ in + self.delegate?.tabsBarDidSelectAddNewWindow(false) + })) + + newTabMenu.append(UIAction(title: Strings.newPrivateWindowTitle, image: UIImage(braveSystemNamed: "leo.window.tab-private"), handler: UIAction.deferredActionHandler { [unowned self] _ in + self.delegate?.tabsBarDidSelectAddNewWindow(true) + })) plusButton.menu = UIMenu(title: "", identifier: nil, children: newTabMenu) privateModeCancellable = tabManager?.privateBrowsingManager diff --git a/Sources/BraveStrings/BraveStrings.swift b/Sources/BraveStrings/BraveStrings.swift index 5f0a12c0ad0..e72e2a22ef3 100644 --- a/Sources/BraveStrings/BraveStrings.swift +++ b/Sources/BraveStrings/BraveStrings.swift @@ -143,6 +143,8 @@ extension Strings { 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") + public static let newWindowTitle = NSLocalizedString("NewWindowTitle", tableName: "BraveShared", bundle: .module, value: "New Window", comment: "Context menu item for opening a new window") + public static let newPrivateWindowTitle = NSLocalizedString("NewPrivateWindowTitle", tableName: "BraveShared", bundle: .module, value: "New Private Window", comment: "Context menu item for opening a new private browsing window") } // MARK:- DefaultBrowserIntroCalloutViewController.swift From 558d78e0a88f9daf52f7e0bf17fbab8f325a185b Mon Sep 17 00:00:00 2001 From: Brandon T Date: Thu, 13 Jul 2023 12:48:39 -0400 Subject: [PATCH 11/13] Fix unit tests Rebase & Fixed bug in alert --- App/iOS/Delegates/AppDelegate.swift | 2 +- .../Toolbars/UrlBar/TabLocationView.swift | 2 +- Tests/ClientTests/TabManagerTests.swift | 112 +++++++++++++++++- 3 files changed, 111 insertions(+), 5 deletions(-) diff --git a/App/iOS/Delegates/AppDelegate.swift b/App/iOS/Delegates/AppDelegate.swift index ca3126b527b..fcece9db00d 100644 --- a/App/iOS/Delegates/AppDelegate.swift +++ b/App/iOS/Delegates/AppDelegate.swift @@ -178,7 +178,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { Preferences.PrivacyReports.ntpOnboardingCompleted.value = false Preferences.Privacy.shouldShowPersistentPrivateBrowsingAlert.value = false } else { - Preferences.Privacy.shouldShowPersistentPrivateBrowsingAlert.value = false + Preferences.Privacy.shouldShowPersistentPrivateBrowsingAlert.value = true } Preferences.General.isFirstLaunch.value = false diff --git a/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TabLocationView.swift b/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TabLocationView.swift index 89ef061fa58..1fb90bcba3d 100644 --- a/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TabLocationView.swift +++ b/Sources/Brave/Frontend/Browser/Toolbars/UrlBar/TabLocationView.swift @@ -251,7 +251,7 @@ class TabLocationView: UIView { private var isVoiceSearchAvailable: Bool - init(voiceSearchSupported: Bool, privateBrowsingManager: privateBrowsingManager) { + init(voiceSearchSupported: Bool, privateBrowsingManager: PrivateBrowsingManager) { isVoiceSearchAvailable = voiceSearchSupported super.init(frame: .zero) diff --git a/Tests/ClientTests/TabManagerTests.swift b/Tests/ClientTests/TabManagerTests.swift index 7b2c34f4727..08b2df90587 100644 --- a/Tests/ClientTests/TabManagerTests.swift +++ b/Tests/ClientTests/TabManagerTests.swift @@ -5,6 +5,7 @@ @testable import Brave @testable import Data import Shared +import Preferences import Storage import UIKit import WebKit @@ -122,6 +123,14 @@ open class MockTabManagerDelegate: TabManagerDelegate { privateBrowsingManager.isPrivateBrowsing = false super.tearDown() } + + private func setPersistentPrivateMode(_ isPersistent: Bool) { + Preferences.Privacy.persistentPrivateBrowsing.value = isPersistent + + if isPersistent { + Preferences.Privacy.privateBrowsingOnly.value = false + } + } // BRAVE TODO: We no longer "store tabs", happens async from CoreData, so this test has to reflect CD instead /* @@ -211,7 +220,39 @@ open class MockTabManagerDelegate: TabManagerDelegate { delegate.verify("Not all delegate methods were called") } - func testDeletePrivateTabsOnExit() { + func testDeletePrivateTabsPersistenceOnExit() { + setPersistentPrivateMode(true) + + // create one private and one normal tab + let tab = manager.addTab(isPrivate: false) + manager.selectTab(tab) + manager.selectTab(manager.addTab(isPrivate: true)) + + XCTAssertEqual(TabType.of(manager.selectedTab).isPrivate, true, "The selected tab should be the private tab") + XCTAssertEqual(manager.tabs(withType: .private).count, 1, "There should only be one private tab") + + manager.selectTab(tab) + XCTAssertEqual(manager.tabs(withType: .private).count, 1, "If the normal tab is selected the private tab should NOT be deleted") + XCTAssertEqual(manager.tabs(withType: .regular).count, 1, "The regular tab should stil be around") + + manager.selectTab(manager.addTab(isPrivate: true)) + XCTAssertEqual(manager.tabs(withType: .private).count, 2, "There should be two private tabs") + manager.willSwitchTabMode(leavingPBM: true) + XCTAssertEqual(manager.tabs(withType: .private).count, 2, "After willSwitchTabMode there should be 2 private tabs") + + manager.selectTab(manager.addTab(isPrivate: true)) + manager.selectTab(manager.addTab(isPrivate: true)) + XCTAssertEqual(manager.tabs(withType: .private).count, 4, "Private tabs should not be deleted when another one is added") + manager.selectTab(manager.addTab(isPrivate: false)) + XCTAssertEqual(manager.tabs(withType: .private).count, 4, "But once we add a normal tab we've switched out of private mode. Private tabs should be be persistent") + XCTAssertEqual(manager.tabs(withType: .regular).count, 2, "The original normal tab and the new one should both still exist") + + setPersistentPrivateMode(false) + } + + func testDeletePrivateTabsOnNonPersistenceExit() { + setPersistentPrivateMode(false) + // create one private and one normal tab let tab = manager.addTab(isPrivate: false) manager.selectTab(tab) @@ -237,7 +278,27 @@ open class MockTabManagerDelegate: TabManagerDelegate { XCTAssertEqual(manager.tabs(withType: .regular).count, 2, "The original normal tab and the new one should both still exist") } - func testTogglePBMDelete() { + func testTogglePBMDeletePersistent() { + setPersistentPrivateMode(true) + + let tab = manager.addTab(isPrivate: false) + manager.selectTab(tab) + manager.selectTab(manager.addTab(isPrivate: false)) + manager.selectTab(manager.addTab(isPrivate: true)) + + manager.willSwitchTabMode(leavingPBM: false) + XCTAssertEqual(manager.tabs(withType: .private).count, 1, "There should be 1 private tab") + manager.willSwitchTabMode(leavingPBM: true) + XCTAssertEqual(manager.tabs(withType: .private).count, 1, "There should be 1 private tab") + manager.removeTab(tab) + XCTAssertEqual(manager.tabs(withType: .regular).count, 1, "There should be 1 normal tab") + + setPersistentPrivateMode(false) + } + + func testTogglePBMDeleteNonPersistent() { + setPersistentPrivateMode(false) + let tab = manager.addTab(isPrivate: false) manager.selectTab(tab) manager.selectTab(manager.addTab(isPrivate: false)) @@ -328,7 +389,52 @@ open class MockTabManagerDelegate: TabManagerDelegate { delegate.verify("Not all delegate methods were called") } - func testDelegatesCalledWhenRemovingPrivateTabs() { + func testDelegatesCalledWhenRemovingPrivateTabsPersistence() { + setPersistentPrivateMode(true) + + //setup + let delegate = MockTabManagerDelegate() + + // create one private and one normal tab + let tab = manager.addTab(isPrivate: false) + let newTab = manager.addTab(isPrivate: false) + manager.selectTab(tab) + manager.selectTab(manager.addTab(isPrivate: true)) + manager.addDelegate(delegate) + + // Double check a few things + XCTAssertEqual(TabType.of(manager.selectedTab).isPrivate, true, "The selected tab should be the private tab") + XCTAssertEqual(manager.tabs(withType: .private).count, 1, "There should only be one private tab") + + // switch to normal mode. Which should not delete the private tabs + manager.willSwitchTabMode(leavingPBM: true) + + //make sure tabs are NOT cleared properly and indexes are reset + XCTAssertEqual(manager.tabs(withType: .private).count, 1, "Private tab should not have been deleted") + XCTAssertNotEqual(manager.selectedIndex, -1, "The selected index should have been reset") + + // didSelect should still be called when switching between a tab + let didSelect = MethodSpy(functionName: "tabManager(_:didSelectedTabChange:previous:)") { tabs in + XCTAssertNotNil(tabs[1], "there should be a previous tab") + let next = tabs[0]! + XCTAssertFalse(next.isPrivate) + } + + // make sure delegate method is actually called + delegate.expect([didSelect]) + + // select the new tab to trigger the delegate methods + manager.selectTab(newTab) + + // check + delegate.verify("Not all delegate methods were called") + + setPersistentPrivateMode(false) + } + + func testDelegatesCalledWhenRemovingPrivateTabsNonPersistence() { + setPersistentPrivateMode(false) + //setup let delegate = MockTabManagerDelegate() From 1f32d85e8f7df335ce4a937f34b08c374b99f5aa Mon Sep 17 00:00:00 2001 From: Brandon T Date: Thu, 3 Aug 2023 13:22:29 -0400 Subject: [PATCH 12/13] Removed preference and popup onboarding the user Default persistent private browsing to off. --- .../Tabs/TabTray/TabTrayController.swift | 16 --------------- .../Brave/Frontend/ClientPreferences.swift | 4 +--- .../OtherPrivacySettingsSectionView.swift | 20 +++++++++---------- 3 files changed, 10 insertions(+), 30 deletions(-) diff --git a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift index 56ae90544e4..cfd6282b0bb 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift @@ -624,22 +624,6 @@ 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 bf4b420cd72..57432a61399 100644 --- a/Sources/Brave/Frontend/ClientPreferences.swift +++ b/Sources/Brave/Frontend/ClientPreferences.swift @@ -126,9 +126,7 @@ 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: 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) + public static let persistentPrivateBrowsing = Option(key: "privacy.private-browsing-persistence", 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/Settings/Shields/OtherPrivacySettingsSectionView.swift b/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift index 99f8c1017e1..f214e228fe4 100644 --- a/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift +++ b/Sources/Brave/Frontend/Settings/Shields/OtherPrivacySettingsSectionView.swift @@ -58,17 +58,15 @@ struct OtherPrivacySettingsSectionView: View { OptionToggleView(title: Strings.persistentPrivateBrowsing, subtitle: nil, option: Preferences.Privacy.persistentPrivateBrowsing) { newValue in - 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) - } + 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) } } } From c03bb7e70b8b3c12c61f6c8057c2384ad5a3b913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Buczek?= Date: Fri, 4 Aug 2023 19:09:58 +0200 Subject: [PATCH 13/13] Rebase fixes. --- App/iOS/Delegates/AppDelegate.swift | 3 --- .../BrowserViewController+TabManagerDelegate.swift | 2 +- Sources/BraveWallet/Crypto/Stores/KeyringStore.swift | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/App/iOS/Delegates/AppDelegate.swift b/App/iOS/Delegates/AppDelegate.swift index fcece9db00d..0e71961dcb6 100644 --- a/App/iOS/Delegates/AppDelegate.swift +++ b/App/iOS/Delegates/AppDelegate.swift @@ -176,9 +176,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { if isFirstLaunch { Preferences.PrivacyReports.ntpOnboardingCompleted.value = false - Preferences.Privacy.shouldShowPersistentPrivateBrowsingAlert.value = false - } else { - Preferences.Privacy.shouldShowPersistentPrivateBrowsingAlert.value = true } Preferences.General.isFirstLaunch.value = false diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift index 2e69b309a2c..94c84346d2a 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+TabManagerDelegate.swift @@ -326,7 +326,7 @@ extension BrowserViewController: TabManagerDelegate { handler: UIAction.deferredActionHandler { [weak self] _ in guard let self = self else { return } - if PrivateBrowsingManager.shared.isPrivateBrowsing { + if privateBrowsingManager.isPrivateBrowsing { return } diff --git a/Sources/BraveWallet/Crypto/Stores/KeyringStore.swift b/Sources/BraveWallet/Crypto/Stores/KeyringStore.swift index 77b3b7a4082..075851ff452 100644 --- a/Sources/BraveWallet/Crypto/Stores/KeyringStore.swift +++ b/Sources/BraveWallet/Crypto/Stores/KeyringStore.swift @@ -456,7 +456,7 @@ extension KeyringStore: BraveWalletKeyringServiceObserver { } public func keyringCreated(_ keyringId: BraveWallet.KeyringId) { - if isOnboardingVisible, !isCreatingWallet, keyringId == BraveWallet.DefaultKeyringId { + if isOnboardingVisible, !isCreatingWallet, keyringId == BraveWallet.KeyringId.default { // Another window has created a wallet. We should dismiss onboarding on this // window and allow the other window to continue with it's onboarding flow. isOnboardingVisible = false @@ -476,7 +476,7 @@ extension KeyringStore: BraveWalletKeyringServiceObserver { } public func keyringRestored(_ keyringId: BraveWallet.KeyringId) { - if isOnboardingVisible && !isRestoringWallet, keyringId == BraveWallet.DefaultKeyringId { + if isOnboardingVisible && !isRestoringWallet, keyringId == BraveWallet.KeyringId.default { // Another window has restored a wallet. We should dismiss onboarding on this // window and allow the other window to continue with it's onboarding flow. isOnboardingVisible = false