diff --git a/Client.xcodeproj/project.pbxproj b/Client.xcodeproj/project.pbxproj index 299447681c51..a78f19751598 100644 --- a/Client.xcodeproj/project.pbxproj +++ b/Client.xcodeproj/project.pbxproj @@ -63,7 +63,6 @@ 1D9E1FE524FEF56C006E561D /* TopSites in Resources */ = {isa = PBXBuildFile; fileRef = 3BC659481E5BA4AE006D560F /* TopSites */; }; 1DA3CE5D24EEE73100422BB2 /* OpenTabsWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DA3CE5C24EEE73100422BB2 /* OpenTabsWidget.swift */; }; 1DA3CE5F24EEE7C600422BB2 /* LegacyTabDataRetriever.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DA3CE5E24EEE7C600422BB2 /* LegacyTabDataRetriever.swift */; }; - 1DA3CE6324EEE83200422BB2 /* LegacySavedTab+ConfigureExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DA3CE6224EEE83200422BB2 /* LegacySavedTab+ConfigureExtension.swift */; }; 1DA3CE6724EEE86C00422BB2 /* AppInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65075641E37F7AB006961AC /* AppInfo.swift */; }; 1DA710072AE7106B00677F6B /* AppDataUsageReportSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DA710062AE7106B00677F6B /* AppDataUsageReportSetting.swift */; }; 1DDAD13E24F0651C007623C8 /* TopSitesWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DDAD13C24F064F7007623C8 /* TopSitesWidget.swift */; }; @@ -496,7 +495,6 @@ 60CE80C12667780D004026C7 /* CredentialListPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60CE80C02667780C004026C7 /* CredentialListPresenter.swift */; }; 60D71AEC26AAF45E00355588 /* UIColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60D71AEB26AAF45E00355588 /* UIColorExtension.swift */; }; 63306D3921103EAE00F25400 /* LegacySavedTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63306D3821103EAE00F25400 /* LegacySavedTab.swift */; }; - 63306D432110B3CD00F25400 /* LegacyTabManagerStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63306D422110B3CD00F25400 /* LegacyTabManagerStore.swift */; }; 6669B5E2211418A200CA117B /* WebsiteDataSearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6669B5E1211418A200CA117B /* WebsiteDataSearchResultsViewController.swift */; }; 66CE54A820FCF6CF00CC310B /* WebsiteDataManagementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66CE54A720FCF6CF00CC310B /* WebsiteDataManagementViewController.swift */; }; 6A3E5D8A283831D1001E706E /* DownloadQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A3E5D89283831D0001E706E /* DownloadQueueTests.swift */; }; @@ -2131,7 +2129,6 @@ 1DA24C60879E7D4B2073FD63 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/Search.strings; sourceTree = ""; }; 1DA3CE5C24EEE73100422BB2 /* OpenTabsWidget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenTabsWidget.swift; sourceTree = ""; }; 1DA3CE5E24EEE7C600422BB2 /* LegacyTabDataRetriever.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyTabDataRetriever.swift; sourceTree = ""; }; - 1DA3CE6224EEE83200422BB2 /* LegacySavedTab+ConfigureExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LegacySavedTab+ConfigureExtension.swift"; sourceTree = ""; }; 1DA64656B1F6A74800D22055 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/LoginManager.strings; sourceTree = ""; }; 1DA710062AE7106B00677F6B /* AppDataUsageReportSetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDataUsageReportSetting.swift; sourceTree = ""; }; 1DDAD13C24F064F7007623C8 /* TopSitesWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopSitesWidget.swift; sourceTree = ""; }; @@ -4726,7 +4723,6 @@ 62DF49B9976D5863CADF6EAA /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Menu.strings; sourceTree = ""; }; 63094229AA6EC744599B77A4 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/ClearPrivateData.strings; sourceTree = ""; }; 63306D3821103EAE00F25400 /* LegacySavedTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacySavedTab.swift; sourceTree = ""; }; - 63306D422110B3CD00F25400 /* LegacyTabManagerStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyTabManagerStore.swift; sourceTree = ""; }; 634148899F41CAA3BCF71E8B /* or */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = or; path = or.lproj/Localizable.strings; sourceTree = ""; }; 63B04BE882C41584580E0E59 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = "fa.lproj/Default Browser.strings"; sourceTree = ""; }; 63FE433D87018CDDFCF592E3 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/ErrorPages.strings; sourceTree = ""; }; @@ -8309,12 +8305,10 @@ isa = PBXGroup; children = ( 63306D3821103EAE00F25400 /* LegacySavedTab.swift */, - 1DA3CE6224EEE83200422BB2 /* LegacySavedTab+ConfigureExtension.swift */, 74C027441B2A348C001B1E88 /* LegacySessionData.swift */, 1DA3CE5E24EEE7C600422BB2 /* LegacyTabDataRetriever.swift */, 8A03309428C2653600286539 /* LegacyTabFileManager.swift */, 215B457E27D7FD4B00E5E800 /* LegacyTabGroupData.swift */, - 63306D422110B3CD00F25400 /* LegacyTabManagerStore.swift */, 215B458127DA420400E5E800 /* LegacyTabMetadataManager.swift */, 5A64225029CB506500EEC3E5 /* LegacyTabManager.swift */, ); @@ -12893,6 +12887,7 @@ C87DF9DB267247190097E707 /* UIConstants+BottomInset.swift in Sources */, 8AD08D1527E9198E00B8E907 /* TabsQuantityTelemetry.swift in Sources */, 1DA3CE6324EEE83200422BB2 /* LegacySavedTab+ConfigureExtension.swift in Sources */, + 81A8CA752AF97B2E0015BF04 /* AppStateAction.swift in Sources */, 74B420C92A1D0D7A00370E53 /* OnboardingInstructionsPopupInfoModel.swift in Sources */, E13E9AB32AAB0FB5001A0E9D /* FakespotViewController.swift in Sources */, E15DE7C4293A7B0F00B32667 /* PhotonActionSheetTitleHeaderView.swift in Sources */, @@ -13087,7 +13082,6 @@ BD1C89CA2A1E3CE7000A4201 /* PocketFooterView.swift in Sources */, E12BD0AE28AC38480029AAF0 /* UIImage+Extension.swift in Sources */, F886218C270CD3B8007F4562 /* DevicePasscodeRequiredViewController.swift in Sources */, - 63306D432110B3CD00F25400 /* LegacyTabManagerStore.swift in Sources */, D83822001FC7961D00303C12 /* DispatchQueueHelper.swift in Sources */, EBC486992195F46B00CDA48D /* InternalSchemeHandler.swift in Sources */, E18BAAFD28E4A44500098AE2 /* TopTabFader.swift in Sources */, diff --git a/Client/Application/UITestAppDelegate.swift b/Client/Application/UITestAppDelegate.swift index 8664777d2fd4..bbc36cabe6e1 100644 --- a/Client/Application/UITestAppDelegate.swift +++ b/Client/Application/UITestAppDelegate.swift @@ -73,7 +73,7 @@ class UITestAppDelegate: AppDelegate, FeatureFlaggable { if arg.starts(with: LaunchArguments.LoadTabsStateArchive) { let tabDirectory = "\(self.appRootDir())/profile.profile" if launchArguments.contains(LaunchArguments.ClearProfile) { - fatalError("Clearing profile and loading a \(LegacyTabManagerStoreImplementation.storePath) is not a supported combination.") + fatalError("Clearing profile and loading tabs, not a supported combination.") } // Grab the name of file in the bundle's test-fixtures dir, and copy it to the runtime app dir. diff --git a/Client/Frontend/Browser/BrowserViewController/BrowserViewController+TabToolbarDelegate.swift b/Client/Frontend/Browser/BrowserViewController/BrowserViewController+TabToolbarDelegate.swift index 736943d93ca3..b8e6dd386613 100644 --- a/Client/Frontend/Browser/BrowserViewController/BrowserViewController+TabToolbarDelegate.swift +++ b/Client/Frontend/Browser/BrowserViewController/BrowserViewController+TabToolbarDelegate.swift @@ -75,6 +75,8 @@ extension BrowserViewController: TabToolbarDelegate, PhotonActionSheetProtocol { let isPrivate = tabManager.selectedTab?.isPrivate ?? false tabManager.selectTab(tabManager.addTab(nil, isPrivate: isPrivate)) focusLocationTextField(forTab: tabManager.selectedTab) + overlayManager.openNewTab(url: nil, + newTabSettings: NewTabAccessors.getNewTabPage(profile.prefs)) } func tabToolbarDidPressMenu(_ tabToolbar: TabToolbarProtocol, button: UIButton) { diff --git a/Client/Frontend/Browser/Tabs/Legacy/LegacyGridTabViewController.swift b/Client/Frontend/Browser/Tabs/Legacy/LegacyGridTabViewController.swift index 85c6bf85fcc9..6065f613e334 100644 --- a/Client/Frontend/Browser/Tabs/Legacy/LegacyGridTabViewController.swift +++ b/Client/Frontend/Browser/Tabs/Legacy/LegacyGridTabViewController.swift @@ -375,8 +375,10 @@ class LegacyGridTabViewController: UIViewController, } func dismissTabTray() { - self.navigationController?.dismiss(animated: true, completion: nil) - TelemetryWrapper.recordEvent(category: .action, method: .close, object: .tabTray) + DispatchQueue.main.async { + self.navigationController?.dismiss(animated: true, completion: nil) + TelemetryWrapper.recordEvent(category: .action, method: .close, object: .tabTray) + } } /// Handles close tab by clicking on close button or swipe gesture diff --git a/Client/Frontend/Browser/Tabs/Legacy/LegacyTabTrayViewController.swift b/Client/Frontend/Browser/Tabs/Legacy/LegacyTabTrayViewController.swift index 5e13cf0fda8b..11432bd836a9 100644 --- a/Client/Frontend/Browser/Tabs/Legacy/LegacyTabTrayViewController.swift +++ b/Client/Frontend/Browser/Tabs/Legacy/LegacyTabTrayViewController.swift @@ -556,7 +556,9 @@ extension LegacyTabTrayViewController: UIAdaptivePresentationControllerDelegate, extension LegacyTabTrayViewController { @objc func didTapAddTab(_ sender: UIBarButtonItem) { + notificationCenter.post(name: .TabsTrayDidClose) viewModel.didTapAddTab(sender) + self.dismiss(animated: true, completion: nil) } @objc diff --git a/Client/TabManagement/Legacy/LegacySavedTab+ConfigureExtension.swift b/Client/TabManagement/Legacy/LegacySavedTab+ConfigureExtension.swift deleted file mode 100644 index 38507d37b58c..000000000000 --- a/Client/TabManagement/Legacy/LegacySavedTab+ConfigureExtension.swift +++ /dev/null @@ -1,69 +0,0 @@ -// 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 WebKit -import Storage -import Shared - -// This cannot be easily imported into extension targets, so we break it out here. -extension LegacySavedTab { - convenience init?(tab: Tab, isSelected: Bool) { - var sessionData = tab.sessionData - - ensureMainThread { - if sessionData == nil { - let currentItem: WKBackForwardListItem! = tab.webView?.backForwardList.currentItem - - // Freshly created web views won't have any history entries at all. - // If we have no history, abort. - if currentItem != nil { - // The back & forward list keep track of the users history within the session - let backList = tab.webView?.backForwardList.backList ?? [] - let forwardList = tab.webView?.backForwardList.forwardList ?? [] - let urls = (backList + [currentItem] + forwardList).map { $0.url } - let currentPage = -forwardList.count - sessionData = LegacySessionData(currentPage: currentPage, urls: urls, lastUsedTime: tab.lastExecutedTime ?? Date.now()) - } - } - } - - self.init(screenshotUUID: tab.screenshotUUID, - isSelected: isSelected, - title: tab.title ?? tab.lastTitle, - isPrivate: tab.isPrivate, - faviconURL: tab.faviconURL, - url: tab.url, - sessionData: sessionData, - uuid: tab.tabUUID, - tabGroupData: tab.metadataManager?.tabGroupData, - createdAt: tab.firstCreatedTime, - hasHomeScreenshot: tab.hasHomeScreenshot) - } - - func configureSavedTabUsing(_ tab: Tab, imageStore: DiskImageStore? = nil) -> Tab { - tab.url = url - - if let screenshotUUID = screenshotUUID, let imageStore = imageStore { - tab.screenshotUUID = screenshotUUID - if let uuidString = tab.screenshotUUID?.uuidString { - Task { - let screenshot = try? await imageStore.getImageForKey(uuidString) - if tab.screenshotUUID == screenshotUUID { - tab.setScreenshot(screenshot) - } - } - } - } - - tab.sessionData = sessionData - tab.lastTitle = title - tab.tabUUID = UUID ?? "" - tab.metadataManager?.tabGroupData = tabGroupData ?? LegacyTabGroupData() - tab.screenshotUUID = screenshotUUID - tab.firstCreatedTime = createdAt ?? sessionData?.lastUsedTime ?? Date.now() - tab.hasHomeScreenshot = hasHomeScreenshot - return tab - } -} diff --git a/Client/TabManagement/Legacy/LegacyTabManager.swift b/Client/TabManagement/Legacy/LegacyTabManager.swift index ebed0d99b8cf..afffd129322b 100644 --- a/Client/TabManagement/Legacy/LegacyTabManager.swift +++ b/Client/TabManagement/Legacy/LegacyTabManager.swift @@ -59,11 +59,11 @@ struct BackupCloseTab { class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler { // MARK: - Variables private let tabEventHandlers: [TabEventHandler] - let store: LegacyTabManagerStore let profile: Profile var isRestoringTabs = false + var tabRestoreHasFinished = false var tabs = [Tab]() - private var _selectedIndex = -1 + var _selectedIndex = -1 var selectedIndex: Int { return _selectedIndex } let logger: Logger var backupCloseTab: BackupCloseTab? @@ -71,9 +71,6 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler var tabDisplayType: TabDisplayType = .TabGrid let delaySelectingNewPopupTab: TimeInterval = 0.1 - // Enables undo of recently closed tabs - var recentlyClosedForUndo = [LegacySavedTab]() - var normalTabs: [Tab] { return tabs.filter { !$0.isPrivate } } @@ -158,7 +155,6 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler self.tabEventHandlers = TabEventHandlers.create(with: profile) self.logger = logger - self.store = LegacyTabManagerStoreImplementation(prefs: profile.prefs) super.init() register(self, forTabEvents: .didSetScreenshot) @@ -202,7 +198,7 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler }() // A WKWebViewConfiguration used for private mode tabs - private lazy var privateConfiguration: WKWebViewConfiguration = { + lazy var privateConfiguration: WKWebViewConfiguration = { return LegacyTabManager.makeWebViewConfig(isPrivate: true, prefs: profile.prefs) }() @@ -254,58 +250,13 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler return filterdTabs.first } - // MARK: - Select tab - - func selectTab(_ tab: Tab?, previous: Tab? = nil) { - selectTab(tab, previous: previous, sessionData: nil) - } - - // This function updates the _selectedIndex. - // Note: it is safe to call this with `tab` and `previous` as the same tab, for use in the case where the index of the tab has changed (such as after deletion). - func selectTab(_ tab: Tab?, previous: Tab? = nil, sessionData: Data?) { - let previous = previous ?? selectedTab - - previous?.metadataManager?.updateTimerAndObserving(state: .tabSwitched, isPrivate: previous?.isPrivate ?? false) - tab?.metadataManager?.updateTimerAndObserving(state: .tabSelected, isPrivate: tab?.isPrivate ?? false) - - // Make sure to wipe the private tabs if the user has the pref turned on - if shouldClearPrivateTabs(), !(tab?.isPrivate ?? false) { - removeAllPrivateTabs() - } - - if let tab = tab { - _selectedIndex = tabs.firstIndex(of: tab) ?? -1 - } else { - _selectedIndex = -1 - } - - preserveTabs() - - assert(tab === selectedTab, "Expected tab is selected") - if tab !== selectedTab { - logger.log("The expected tab wasn't selected!", - level: .warning, - category: .tabs) - } - - selectedTab?.createWebview(with: sessionData) - selectedTab?.lastExecutedTime = Date.now() + // TODO: FXIOS-7596 Remove when moving the TabManager protocol to TabManagerImplementation + func restoreTabs(_ forced: Bool = false) { fatalError("should never be called") } - delegates.forEach { $0.get()?.tabManager(self, didSelectedTabChange: tab, previous: previous, isRestoring: store.isRestoringTabs) } - if let tab = previous { - TabEvent.post(.didLoseFocus, for: tab) - } - if let tab = selectedTab { - TabEvent.post(.didGainFocus, for: tab) - } - TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .tab) + // MARK: - Select tab - // Note: we setup last session private case as the session is tied to user's selected - // tab but there are times when tab manager isn't available and we need to know - // users's last state (Private vs Regular) - UserDefaults.standard.set(selectedTab?.isPrivate ?? false, - forKey: PrefsKeys.LastSessionWasPrivate) - } + // TODO: FXIOS-7596 Remove when moving the TabManager protocol to TabManagerImplementation + func selectTab(_ tab: Tab?, previous: Tab? = nil) { fatalError("should never be called") } func getMostRecentHomepageTab() -> Tab? { let tabsToFilter = selectedTab?.isPrivate ?? false ? privateTabs : normalTabs @@ -357,37 +308,6 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler // TODO: FXIOS-7596 Remove when moving the TabManager protocol to TabManagerImplementation func storeChanges() { fatalError("should never be called") } - func restoreTabs(_ forced: Bool = false) { - defer { checkForSingleTab() } - guard forced || tabs.isEmpty, - !AppConstants.isRunningUITests, - !DebugSettingsBundleOptions.skipSessionRestore - else { return } - - isRestoringTabs = true - - var tabToSelect = store.restoreStartupTabs(clearPrivateTabs: shouldClearPrivateTabs(), - addTabClosure: addTabForRestoration(isPrivate:)) - - // If tabToSelect is nil after restoration, force selection of first tab normal tab - if tabToSelect == nil { - tabToSelect = tabs.first(where: { !$0.isPrivate }) - - // If tabToSelect is still nil, create a new tab - if tabToSelect == nil { - tabToSelect = addTab() - } - } - - selectTab(tabToSelect) - - for delegate in self.delegates { - delegate.get()?.tabManagerDidRestoreTabs(self) - } - - isRestoringTabs = false - } - private func addTabForRestoration(isPrivate: Bool) -> Tab { return addTab(flushToDisk: false, zombie: true, isPrivate: isPrivate) } @@ -531,7 +451,7 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler $0.get()?.tabManager(self, didAddTab: tab, placeNextToParentTab: placeNextToParentTab, - isRestoring: store.isRestoringTabs) + isRestoring: !tabRestoreHasFinished) } if !zombie { @@ -616,8 +536,6 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler // This is called by TabTrayVC when the private mode button is pressed and BEFORE we've switched to the new mode // we only want to remove all private tabs when leaving PBM and not when entering. func willSwitchTabMode(leavingPBM: Bool) { - recentlyClosedForUndo.removeAll() - // Clear every time entering/exiting this mode. Tab.ChangeUserAgent.privateModeHostList = Set() } @@ -672,7 +590,7 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler // Notify of tab removal ensureMainThread { [unowned self] in - delegates.forEach { $0.get()?.tabManager(self, didRemoveTab: tab, isRestoring: store.isRestoringTabs) } + delegates.forEach { $0.get()?.tabManager(self, didRemoveTab: tab, isRestoring: !tabRestoreHasFinished) } TabEvent.post(.didClose, for: tab) } @@ -891,22 +809,11 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler return false } - private func removeAllPrivateTabs() { - // reset the selectedTabIndex if we are on a private tab because we will be removing it. - if selectedTab?.isPrivate ?? false { - _selectedIndex = -1 - } - privateTabs.forEach { $0.close() } - tabs = normalTabs - - privateConfiguration = LegacyTabManager.makeWebViewConfig(isPrivate: true, prefs: profile.prefs) - } - // MARK: - Start at Home /// Public interface for checking whether the StartAtHome Feature should run. func startAtHomeCheck() -> Bool { - let startAtHomeManager = StartAtHomeHelper(prefs: profile.prefs, isRestoringTabs: isRestoringTabs) + let startAtHomeManager = StartAtHomeHelper(prefs: profile.prefs, isRestoringTabs: !tabRestoreHasFinished) guard !startAtHomeManager.shouldSkipStartHome else { return false } diff --git a/Client/TabManagement/Legacy/LegacyTabManagerStore.swift b/Client/TabManagement/Legacy/LegacyTabManagerStore.swift deleted file mode 100644 index 5831ffa41c4b..000000000000 --- a/Client/TabManagement/Legacy/LegacyTabManagerStore.swift +++ /dev/null @@ -1,157 +0,0 @@ -// 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 Common -import Foundation -import Storage -import Shared - -protocol LegacyTabManagerStore { - var isRestoringTabs: Bool { get } - var tabs: [LegacySavedTab] { get } - - func restoreStartupTabs(clearPrivateTabs: Bool, - addTabClosure: @escaping (Bool) -> Tab) -> Tab? -} - -class LegacyTabManagerStoreImplementation: LegacyTabManagerStore, FeatureFlaggable { - // MARK: - Variables - private let logger: Logger - private let tabsKey = "tabs" - private let prefs: Prefs - private var fileManager: LegacyTabFileManager - private var writeOperation = DispatchWorkItem {} - private let serialQueue: DispatchQueueInterface - private var lockedForReading = false - private var tabDataRetriever: LegacyTabDataRetriever - static let storePath = "codableTabsState.archive" - - var isRestoringTabs: Bool { - return lockedForReading - } - - // MARK: - Initializer - - init(prefs: Prefs, - fileManager: LegacyTabFileManager = FileManager.default, - serialQueue: DispatchQueueInterface = DispatchQueue(label: "tab-manager-write-queue"), - logger: Logger = DefaultLogger.shared) { - self.prefs = prefs - self.fileManager = fileManager - self.serialQueue = serialQueue - self.logger = logger - self.tabDataRetriever = LegacyTabDataRetrieverImplementation(fileManager: fileManager) - tabDataRetriever.tabsStateArchivePath = tabsStateArchivePath() - } - - // MARK: - Restoration - - func restoreStartupTabs(clearPrivateTabs: Bool, - addTabClosure: (Bool) -> Tab) -> Tab? { - return restoreTabs(savedTabs: tabs, - clearPrivateTabs: clearPrivateTabs, - addTabClosure: addTabClosure) - } - - func restoreTabs(savedTabs: [LegacySavedTab], - clearPrivateTabs: Bool, - addTabClosure: (Bool) -> Tab) -> Tab? { - // We are told "Restoration is a main-only operation" - guard !lockedForReading, - Thread.current.isMainThread, - !savedTabs.isEmpty - else { return nil } - - lockedForReading = true - defer { lockedForReading = false } - - var savedTabs = savedTabs - // Make sure to wipe the private tabs if the user has the pref turned on - if clearPrivateTabs { - savedTabs = savedTabs.filter { !$0.isPrivate } - } - - var tabToSelect: Tab? - for savedTab in savedTabs { - // Provide an empty request to prevent a new tab from loading the home screen - var tab = addTabClosure(savedTab.isPrivate) - tab = savedTab.configureSavedTabUsing(tab) - if savedTab.isSelected { - tabToSelect = tab - } - } - - return tabToSelect - } - - // MARK: - Private - - var tabs: [LegacySavedTab] { - guard let tabData = tabDataRetriever.getTabData() else { - return [] - } - - do { - let unarchiver = try NSKeyedUnarchiver(forReadingFrom: tabData) - guard let tabs = unarchiver.decodeDecodable([LegacySavedTab].self, forKey: tabsKey) else { - let message = "\(unarchiver.error?.localizedDescription ?? "Couldn't decode from tabsKey")" - return savedTabError(description: message) - } - return tabs - } catch let error { - return savedTabError(description: error.localizedDescription) - } - } - - private func savedTabError(description: String) -> [LegacySavedTab] { - logger.log("Failed to restore legacy tabs", - level: .warning, - category: .tabs, - description: description) - return [LegacySavedTab]() - } - - private func tabsStateArchivePath() -> URL? { - let profilePath: String? - if AppConstants.isRunningUITests || AppConstants.isRunningPerfTests { - profilePath = (UIApplication.shared.delegate as? UITestAppDelegate)?.dirForTestProfile - } else { - profilePath = fileManager.tabPath - } - guard let path = profilePath else { return nil } - return URL(fileURLWithPath: path).appendingPathComponent(LegacyTabManagerStoreImplementation.storePath) - } - - private func prepareSavedTabs(fromTabs tabs: [Tab], selectedTab: Tab?) -> [LegacySavedTab]? { - var savedTabs = [LegacySavedTab]() - var savedUUIDs = Set() - for tab in tabs { - tab.tabUUID = tab.tabUUID.isEmpty ? UUID().uuidString : tab.tabUUID - tab.screenshotUUID = tab.screenshotUUID ?? UUID() - tab.firstCreatedTime = tab.firstCreatedTime ?? tab.sessionData?.lastUsedTime ?? Date.now() - if let savedTab = LegacySavedTab(tab: tab, isSelected: tab == selectedTab) { - savedTabs.append(savedTab) - if let uuidString = tab.screenshotUUID?.uuidString { - savedUUIDs.insert(uuidString) - } - } - } - - return savedTabs.isEmpty ? nil : savedTabs - } -} - -// MARK: Tests -extension LegacyTabManagerStoreImplementation { - func testTabOnDisk() -> [LegacySavedTab] { - guard AppConstants.isRunningTest else { - logger.log("This method is being called in NON-TESTING code. Do NOT do this!", - level: .fatal, - category: .tabs) - fatalError() - } - - return tabs - } -} diff --git a/Client/TabManagement/TabManagerImplementation.swift b/Client/TabManagement/TabManagerImplementation.swift index 53b6d6c51a96..7895b4bbec85 100644 --- a/Client/TabManagement/TabManagerImplementation.swift +++ b/Client/TabManagement/TabManagerImplementation.swift @@ -15,7 +15,6 @@ class TabManagerImplementation: LegacyTabManager, Notifiable { private let tabSessionStore: TabSessionStore private let imageStore: DiskImageStore? private let tabMigration: TabMigrationUtility - var tabRestoreHasFinished = false var notificationCenter: NotificationProtocol init(profile: Profile, @@ -57,7 +56,7 @@ class TabManagerImplementation: LegacyTabManager, Notifiable { else { if tabs.isEmpty { let newTab = addTab() - super.selectTab(newTab) + selectTab(newTab) } return } @@ -300,46 +299,46 @@ class TabManagerImplementation: LegacyTabManager, Notifiable { // MARK: - Select Tab + /// This function updates the _selectedIndex. + /// Note: it is safe to call this with `tab` and `previous` as the same tab, for use in the case where the index of the tab has changed (such as after deletion). override func selectTab(_ tab: Tab?, previous: Tab? = nil) { let url = tab?.url - guard shouldUseNewTabStore(), - let tab = tab, + guard let tab = tab, let tabUUID = UUID(uuidString: tab.tabUUID) else { - willSelectTab(url) - super.selectTab(tab, previous: previous) - didSelectTab(url) + logger.log("Selected tab doesn't exist", + level: .debug, + category: .tabs) return } // Before moving to a new tab save the current tab session data in order to preseve things like scroll position saveCurrentTabSessionData() - guard !AppConstants.isRunningUITests, - !DebugSettingsBundleOptions.skipSessionRestore - else { - willSelectTab(url) - super.selectTab(tab, previous: previous) - didSelectTab(url) - return - } - willSelectTab(url) Task(priority: .high) { - if tab.isFxHomeTab { - await selectTabWithSession(tab: tab, - previous: previous, - sessionData: nil) - } else { - let sessionData = await tabSessionStore.fetchTabSession(tabID: tabUUID) - await selectTabWithSession(tab: tab, - previous: previous, - sessionData: sessionData) + var sessionData: Data? + if !tab.isFxHomeTab { + sessionData = await tabSessionStore.fetchTabSession(tabID: tabUUID) } + await selectTabWithSession(tab: tab, + previous: previous, + sessionData: sessionData) didSelectTab(url) } } + private func removeAllPrivateTabs() { + // reset the selectedTabIndex if we are on a private tab because we will be removing it. + if selectedTab?.isPrivate ?? false { + _selectedIndex = -1 + } + privateTabs.forEach { $0.close() } + tabs = normalTabs + + privateConfiguration = LegacyTabManager.makeWebViewConfig(isPrivate: true, prefs: profile.prefs) + } + private func willSelectTab(_ url: URL?) { guard let url else { return } AppEventQueue.started(.selectTab(url)) @@ -352,7 +351,40 @@ class TabManagerImplementation: LegacyTabManager, Notifiable { @MainActor private func selectTabWithSession(tab: Tab, previous: Tab?, sessionData: Data?) { - super.selectTab(tab, previous: previous, sessionData: sessionData) + let previous = previous ?? selectedTab + + previous?.metadataManager?.updateTimerAndObserving(state: .tabSwitched, isPrivate: previous?.isPrivate ?? false) + tab.metadataManager?.updateTimerAndObserving(state: .tabSelected, isPrivate: tab.isPrivate) + + // Make sure to wipe the private tabs if the user has the pref turned on + if shouldClearPrivateTabs(), !tab.isPrivate { + removeAllPrivateTabs() + } + + _selectedIndex = tabs.firstIndex(of: tab) ?? -1 + + preserveTabs() + + selectedTab?.createWebview(with: sessionData) + selectedTab?.lastExecutedTime = Date.now() + + delegates.forEach { + $0.get()?.tabManager(self, didSelectedTabChange: tab, previous: previous, isRestoring: !tabRestoreHasFinished) + } + + if let tab = previous { + TabEvent.post(.didLoseFocus, for: tab) + } + if let tab = selectedTab { + TabEvent.post(.didGainFocus, for: tab) + } + TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .tab) + + // Note: we setup last session private case as the session is tied to user's selected + // tab but there are times when tab manager isn't available and we need to know + // users's last state (Private vs Regular) + UserDefaults.standard.set(selectedTab?.isPrivate ?? false, + forKey: PrefsKeys.LastSessionWasPrivate) } private func shouldUseNewTabStore() -> Bool {