diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+Wallet.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+Wallet.swift index ce8704c7337..a4bbe4e0749 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+Wallet.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+Wallet.swift @@ -180,7 +180,7 @@ extension Tab: BraveWalletProviderDelegate { // assigned for a specific origin, the provider(s) will check origin // of all open Tab's to see if that provider needs(s) updated too. // We can get the url from the SessionTab, and return it's origin. - if let sessionTabOrigin = SessionTab.from(tabId: id)?.url.origin { + if let sessionTabOrigin = SessionTab.from(tabId: id)?.url?.origin { return sessionTabOrigin } assert(false, "We should have a valid origin to get to this point") diff --git a/Sources/Brave/Frontend/Browser/Tab.swift b/Sources/Brave/Frontend/Browser/Tab.swift index f669fb93fdf..261b154ed7e 100644 --- a/Sources/Brave/Frontend/Browser/Tab.swift +++ b/Sources/Brave/Frontend/Browser/Tab.swift @@ -613,7 +613,7 @@ class Tab: NSObject { } else { if let tabUrl = url, tabUrl.isWebPage() { return tabUrl - } else if let fetchedTab = SessionTab.from(tabId: id), fetchedTab.url.isWebPage() { + } else if let fetchedTab = SessionTab.from(tabId: id), fetchedTab.url?.isWebPage() == true { return url } } diff --git a/Sources/Brave/Frontend/Browser/TabManager.swift b/Sources/Brave/Frontend/Browser/TabManager.swift index d5542ccb330..3cc2706f62b 100644 --- a/Sources/Brave/Frontend/Browser/TabManager.swift +++ b/Sources/Brave/Frontend/Browser/TabManager.swift @@ -52,6 +52,9 @@ class TabManager: NSObject { /// Internal url to access the new tab page. static let ntpInteralURL = URL(string: "\(InternalURL.baseUrl)/\(AboutHomeHandler.path)#panel=0")! + + /// When a URL is invalid and can't be restored or loaded, we display about:blank#blocked (same as on Desktop) + static let aboutBlankBlockedURL = URL(string: "about:blank")! func addDelegate(_ delegate: TabManagerDelegate) { assert(Thread.isMainThread) @@ -513,9 +516,15 @@ class TabManager: NSObject { @MainActor func configureTab(_ tab: Tab, request: URLRequest?, afterTab parent: Tab? = nil, flushToDisk: Bool, zombie: Bool, isPopup: Bool = false) { assert(Thread.isMainThread) + var request = request let isPrivate = tab.type == .private let isPersistentTab = !isPrivate || (isPrivate && !Preferences.Privacy.privateBrowsingOnly.value && Preferences.Privacy.persistentPrivateBrowsing.value) + // WebKit can sometimes return a URL that isn't valid at all! + if let requestURL = request?.url, NSURL(idnString: requestURL.absoluteString) == nil { + request?.url = TabManager.aboutBlankBlockedURL + } + if isPersistentTab { SessionTab.createIfNeeded(windowId: windowId, tabId: tab.id, @@ -901,30 +910,46 @@ class TabManager: NSObject { var tabToSelect: Tab? for savedTab in savedTabs { - let tabURL = savedTab.url - // Provide an empty request to prevent a new tab from loading the home screen - let request = InternalURL.isValid(url: tabURL) ? - PrivilegedRequest(url: tabURL) as URLRequest : - URLRequest(url: tabURL) + if let tabURL = savedTab.url { + // Provide an empty request to prevent a new tab from loading the home screen + let request = InternalURL.isValid(url: tabURL) ? + PrivilegedRequest(url: tabURL) as URLRequest : + URLRequest(url: tabURL) - let tab = addTab(request, - flushToDisk: false, - zombie: true, - id: savedTab.tabId, - 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: !tab.isPrivate) + let tab = addTab(request, + flushToDisk: false, + zombie: true, + id: savedTab.tabId, + isPrivate: savedTab.isPrivate) + + tab.lastTitle = savedTab.title + tab.favicon = FaviconFetcher.getIconFromCache(for: tabURL) ?? Favicon.default tab.setScreenshot(savedTab.screenshot) - } - - // Do not select the private tab since we always restore to regular mode! - if savedTab.isSelected && !savedTab.isPrivate { - tabToSelect = tab + + Task { @MainActor in + tab.favicon = try await FaviconFetcher.loadIcon(url: tabURL, kind: .smallIcon, persistent: !tab.isPrivate) + tab.setScreenshot(savedTab.screenshot) + } + + // Do not select the private tab since we always restore to regular mode! + if savedTab.isSelected && !savedTab.isPrivate { + tabToSelect = tab + } + } else { + let tab = addTab(nil, + flushToDisk: false, + zombie: true, + id: savedTab.tabId, + isPrivate: savedTab.isPrivate) + + tab.lastTitle = savedTab.title + tab.favicon = Favicon.default + tab.setScreenshot(savedTab.screenshot) + + // Do not select the private tab since we always restore to regular mode! + if savedTab.isSelected && !savedTab.isPrivate { + tabToSelect = tab + } } } @@ -969,12 +994,17 @@ class TabManager: NSObject { if sessionTab.interactionState.isEmpty { tab.navigationDelegate = navDelegate - let request = InternalURL.isValid(url: sessionTab.url) ? - PrivilegedRequest(url: sessionTab.url) as URLRequest : - URLRequest(url: sessionTab.url) - - tab.restore(webView, - requestRestorationData: (sessionTab.url.absoluteDisplayString, request)) + if let tabURL = sessionTab.url { + let request = InternalURL.isValid(url: tabURL) ? + PrivilegedRequest(url: tabURL) as URLRequest : + URLRequest(url: tabURL) + + tab.restore(webView, + requestRestorationData: (tabURL.absoluteDisplayString, request)) + } else { + tab.restore(webView, + requestRestorationData: (TabManager.aboutBlankBlockedURL.absoluteDisplayString, URLRequest(url: TabManager.aboutBlankBlockedURL))) + } return } 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 036b91882a1..bd8d4ad3a26 100644 --- a/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/Bookmarks/AddEditBookmarkTableViewController.swift +++ b/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/Bookmarks/AddEditBookmarkTableViewController.swift @@ -377,10 +377,10 @@ class AddEditBookmarkTableViewController: UITableViewController { if let url = tab.url, url.isWebPage(), !(InternalURL(url)?.isAboutHomeURL ?? false) { bookmarkManager.add(url: url, title: tab.title, parentFolder: parentFolder) } - } else if let fetchedTab = SessionTab.from(tabId: tab.id) { - if fetchedTab.url.isWebPage(), !(InternalURL(fetchedTab.url)?.isAboutHomeURL ?? false) { + } else if let fetchedTab = SessionTab.from(tabId: tab.id), let tabURL = fetchedTab.url { + if tabURL.isWebPage(), !(InternalURL(tabURL)?.isAboutHomeURL ?? false) { bookmarkManager.add( - url: fetchedTab.url, + url: tabURL, title: fetchedTab.title, parentFolder: parentFolder) } diff --git a/Sources/Brave/Migration/Migration.swift b/Sources/Brave/Migration/Migration.swift index 6cb3d0f24eb..f999ba4d9bc 100644 --- a/Sources/Brave/Migration/Migration.swift +++ b/Sources/Brave/Migration/Migration.swift @@ -85,7 +85,7 @@ public class Migration { // Restore private tabs if persistency is enabled if Preferences.Privacy.persistentPrivateBrowsing.value { zombieTabs.filter({ $0.isPrivate }).forEach { - if !activeURLs.contains($0.url) { + if let url = $0.url, !activeURLs.contains(url) { SessionTab.move(tab: $0.tabId, toWindow: activeWindow.windowId) } } @@ -93,7 +93,7 @@ public class Migration { // Restore regular tabs zombieTabs.filter({ !$0.isPrivate }).forEach { - if !activeURLs.contains($0.url) { + if let url = $0.url, !activeURLs.contains(url) { SessionTab.move(tab: $0.tabId, toWindow: activeWindow.windowId) } } diff --git a/Sources/Data/models/Model.xcdatamodeld/Model27.xcdatamodel/contents b/Sources/Data/models/Model.xcdatamodeld/Model27.xcdatamodel/contents new file mode 100644 index 00000000000..485d2b7d359 --- /dev/null +++ b/Sources/Data/models/Model.xcdatamodeld/Model27.xcdatamodel/contents @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Sources/Data/models/SessionTab.swift b/Sources/Data/models/SessionTab.swift index 1ab593b212c..b1c4c082ed7 100644 --- a/Sources/Data/models/SessionTab.swift +++ b/Sources/Data/models/SessionTab.swift @@ -16,7 +16,7 @@ public final class SessionTab: NSManagedObject, CRUD { @NSManaged public var lastUpdated: Date @NSManaged public var screenshotData: Data @NSManaged public var title: String - @NSManaged public var url: URL + @NSManaged public var url: URL? @NSManaged private(set) public var tabId: UUID @NSManaged private(set) public var sessionTabGroup: SessionTabGroup? @@ -47,7 +47,7 @@ public final class SessionTab: NSManagedObject, CRUD { lastUpdated: Date, screenshotData: Data, title: String, - url: URL, + url: URL?, tabId: UUID = UUID()) { guard let entity = Self.entity(context) else { fatalError("No such Entity: SessionTab")