diff --git a/DuckDuckGo/Autoconsent/AutoconsentUserScript.swift b/DuckDuckGo/Autoconsent/AutoconsentUserScript.swift index c43f7dc27c..2000073692 100644 --- a/DuckDuckGo/Autoconsent/AutoconsentUserScript.swift +++ b/DuckDuckGo/Autoconsent/AutoconsentUserScript.swift @@ -202,7 +202,7 @@ extension AutoconsentUserScript { return } - guard [.http, .https].contains(url.navigationalScheme) else { + guard url.navigationalScheme?.isHypertextScheme == true else { // ignore special schemes os_log("Ignoring special URL scheme: %s", log: .autoconsent, type: .debug, messageData.url) replyHandler([ "type": "ok" ], nil) // this is just to prevent a Promise rejection diff --git a/DuckDuckGo/Common/Extensions/URLExtension.swift b/DuckDuckGo/Common/Extensions/URLExtension.swift index 62b6c8fc2b..abd290dd0f 100644 --- a/DuckDuckGo/Common/Extensions/URLExtension.swift +++ b/DuckDuckGo/Common/Extensions/URLExtension.swift @@ -30,6 +30,11 @@ extension URL.NavigationalScheme { return [.http, .https, .file] } + /// HTTP or HTTPS + var isHypertextScheme: Bool { + Self.hypertextSchemes.contains(self) + } + } extension URL { diff --git a/DuckDuckGo/Feedback/View/FeedbackViewController.swift b/DuckDuckGo/Feedback/View/FeedbackViewController.swift index ff45c1a7e9..6bec572e75 100644 --- a/DuckDuckGo/Feedback/View/FeedbackViewController.swift +++ b/DuckDuckGo/Feedback/View/FeedbackViewController.swift @@ -75,7 +75,7 @@ final class FeedbackViewController: NSViewController { var currentTab: Tab? var currentTabUrl: URL? { - guard let url = currentTab?.content.url else { + guard let url = currentTab?.content.urlForWebView else { return nil } diff --git a/DuckDuckGo/Fireproofing/Extensions/FireproofingURLExtensions.swift b/DuckDuckGo/Fireproofing/Extensions/FireproofingURLExtensions.swift index d75068166c..da4b9df331 100644 --- a/DuckDuckGo/Fireproofing/Extensions/FireproofingURLExtensions.swift +++ b/DuckDuckGo/Fireproofing/Extensions/FireproofingURLExtensions.swift @@ -52,7 +52,7 @@ extension URL { ] var canFireproof: Bool { - guard let host = self.host else { return false } + guard let host = self.host, self.navigationalScheme?.isHypertextScheme == true else { return false } return (host != Self.cookieDomain) } diff --git a/DuckDuckGo/NavigationBar/View/AddressBarButtonsViewController.swift b/DuckDuckGo/NavigationBar/View/AddressBarButtonsViewController.swift index 566a7c413f..878114b49b 100644 --- a/DuckDuckGo/NavigationBar/View/AddressBarButtonsViewController.swift +++ b/DuckDuckGo/NavigationBar/View/AddressBarButtonsViewController.swift @@ -279,7 +279,7 @@ final class AddressBarButtonsViewController: NSViewController { guard let tabViewModel, tabViewModel.canBeBookmarked else { return false } var isUrlBookmarked = false - if let url = tabViewModel.tab.content.url, + if let url = tabViewModel.tab.content.userEditableUrl, bookmarkManager.isUrlBookmarked(url: url) { isUrlBookmarked = true } @@ -413,7 +413,7 @@ final class AddressBarButtonsViewController: NSViewController { permissions.microphone = tabViewModel.usedPermissions.microphone } - let url = tabViewModel.tab.content.url ?? .empty + let url = tabViewModel.tab.content.urlForWebView ?? .empty let domain = url.isFileURL ? .localhost : (url.host ?? "") PermissionContextMenu(permissions: permissions.map { ($0, $1) }, domain: domain, delegate: self) @@ -432,7 +432,7 @@ final class AddressBarButtonsViewController: NSViewController { return } - let url = tabViewModel.tab.content.url ?? .empty + let url = tabViewModel.tab.content.urlForWebView ?? .empty let domain = url.isFileURL ? .localhost : (url.host ?? "") PermissionContextMenu(permissions: [(.microphone, state)], domain: domain, delegate: self) @@ -451,7 +451,7 @@ final class AddressBarButtonsViewController: NSViewController { return } - let url = tabViewModel.tab.content.url ?? .empty + let url = tabViewModel.tab.content.urlForWebView ?? .empty let domain = url.isFileURL ? .localhost : (url.host ?? "") PermissionContextMenu(permissions: [(.geolocation, state)], domain: domain, delegate: self) @@ -475,7 +475,7 @@ final class AddressBarButtonsViewController: NSViewController { $0.append( (.popups, .requested($1)) ) } } else { - let url = tabViewModel.tab.content.url ?? .empty + let url = tabViewModel.tab.content.urlForWebView ?? .empty domain = url.isFileURL ? .localhost : (url.host ?? "") permissions = [(.popups, state)] } @@ -499,7 +499,7 @@ final class AddressBarButtonsViewController: NSViewController { } permissions = [(permissionType, state)] - let url = tabViewModel.tab.content.url ?? .empty + let url = tabViewModel.tab.content.urlForWebView ?? .empty let domain = url.isFileURL ? .localhost : (url.host ?? "") PermissionContextMenu(permissions: permissions, domain: domain, delegate: self) @@ -733,7 +733,7 @@ final class AddressBarButtonsViewController: NSViewController { } private func updateBookmarkButtonImage(isUrlBookmarked: Bool = false) { - if let url = tabViewModel?.tab.content.url, + if let url = tabViewModel?.tab.content.userEditableUrl, isUrlBookmarked || bookmarkManager.isUrlBookmarked(url: url) { bookmarkButton.image = .bookmarkFilled @@ -770,11 +770,11 @@ final class AddressBarButtonsViewController: NSViewController { private func updatePrivacyEntryPointButton() { guard let tabViewModel else { return } - let urlScheme = tabViewModel.tab.content.url?.scheme - let isHypertextUrl = urlScheme == "http" || urlScheme == "https" + let url = tabViewModel.tab.content.userEditableUrl + let isHypertextUrl = url?.navigationalScheme?.isHypertextScheme == true && url?.isDuckPlayer == false let isEditingMode = controllerMode?.isEditing ?? false let isTextFieldValueText = textFieldValue?.isText ?? false - let isLocalUrl = tabViewModel.tab.content.url?.isLocalURL ?? false + let isLocalUrl = url?.isLocalURL ?? false // Privacy entry point button privacyEntryPointButton.isHidden = isEditingMode @@ -922,7 +922,7 @@ final class AddressBarButtonsViewController: NSViewController { private func bookmarkForCurrentUrl(setFavorite: Bool, accessPoint: GeneralPixel.AccessPoint) -> (bookmark: Bookmark?, isNew: Bool) { guard let tabViewModel, - let url = tabViewModel.tab.content.url else { + let url = tabViewModel.tab.content.userEditableUrl else { assertionFailure("No URL for bookmarking") return (nil, false) } diff --git a/DuckDuckGo/NavigationBar/View/AddressBarTextField.swift b/DuckDuckGo/NavigationBar/View/AddressBarTextField.swift index 21bb3b0bfa..c9692f1546 100644 --- a/DuckDuckGo/NavigationBar/View/AddressBarTextField.swift +++ b/DuckDuckGo/NavigationBar/View/AddressBarTextField.swift @@ -275,7 +275,7 @@ final class AddressBarTextField: NSTextField { guard let selectedTabViewModel = selectedTabViewModel ?? tabCollectionViewModel.selectedTabViewModel else { return } let addressBarString = addressBarString ?? selectedTabViewModel.addressBarString - let isSearch = selectedTabViewModel.tab.content.url?.isDuckDuckGoSearch ?? false + let isSearch = selectedTabViewModel.tab.content.userEditableUrl?.isDuckDuckGoSearch ?? false self.value = Value(stringValue: addressBarString, userTyped: false, isSearch: isSearch) clearUndoManager() } diff --git a/DuckDuckGo/NavigationBar/View/AddressBarViewController.swift b/DuckDuckGo/NavigationBar/View/AddressBarViewController.swift index 43ff508e66..cac827debd 100644 --- a/DuckDuckGo/NavigationBar/View/AddressBarViewController.swift +++ b/DuckDuckGo/NavigationBar/View/AddressBarViewController.swift @@ -239,9 +239,9 @@ final class AddressBarViewController: NSViewController { func shouldShowLoadingIndicator(for tabViewModel: TabViewModel, isLoading: Bool, error: Error?) -> Bool { if isLoading, - let url = tabViewModel.tab.content.url, - [.http, .https].contains(url.navigationalScheme), - url.isDuckDuckGoSearch == false, + let url = tabViewModel.tab.content.urlForWebView, + url.navigationalScheme?.isHypertextScheme == true, + !url.isDuckDuckGoSearch, !url.isDuckPlayer, error == nil { return true } else { diff --git a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift index 2638bc96bb..49cb4e559a 100644 --- a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift +++ b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift @@ -402,10 +402,11 @@ final class MoreOptionsMenu: NSMenu { } private func addPageItems() { - guard let url = tabCollectionViewModel.selectedTabViewModel?.tab.content.url else { return } + guard let tabViewModel = tabCollectionViewModel.selectedTabViewModel, + let url = tabViewModel.tab.content.userEditableUrl else { return } + let oldItemsCount = items.count if url.canFireproof, let host = url.host { - let isFireproof = FireproofDomains.shared.isFireproof(fireproofDomain: host) let title = isFireproof ? UserText.removeFireproofing : UserText.fireproofSite let image: NSImage = isFireproof ? .burn : .fireproof @@ -413,25 +414,29 @@ final class MoreOptionsMenu: NSMenu { addItem(withTitle: title, action: #selector(toggleFireproofing(_:)), keyEquivalent: "") .targetting(self) .withImage(image) - } - addItem(withTitle: UserText.findInPageMenuItem, action: #selector(findInPage(_:)), keyEquivalent: "f") - .targetting(self) - .withImage(.findSearch) - .withAccessibilityIdentifier("MoreOptionsMenu.findInPage") - - addItem(withTitle: UserText.shareMenuItem, action: nil, keyEquivalent: "") - .targetting(self) - .withImage(.share) - .withSubmenu(sharingMenu) + if tabViewModel.canReload { + addItem(withTitle: UserText.findInPageMenuItem, action: #selector(findInPage(_:)), keyEquivalent: "f") + .targetting(self) + .withImage(.findSearch) + .withAccessibilityIdentifier("MoreOptionsMenu.findInPage") - addItem(withTitle: UserText.printMenuItem, action: #selector(doPrint(_:)), keyEquivalent: "") - .targetting(self) - .withImage(.print) + addItem(withTitle: UserText.shareMenuItem, action: nil, keyEquivalent: "") + .targetting(self) + .withImage(.share) + .withSubmenu(sharingMenu) + } - addItem(NSMenuItem.separator()) + if tabViewModel.canPrint { + addItem(withTitle: UserText.printMenuItem, action: #selector(doPrint(_:)), keyEquivalent: "") + .targetting(self) + .withImage(.print) + } + if items.count > oldItemsCount { + addItem(NSMenuItem.separator()) + } } private func makeNetworkProtectionItem() -> NSMenuItem { diff --git a/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift b/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift index d27b62fd5d..09d2f480c6 100644 --- a/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift +++ b/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift @@ -693,7 +693,7 @@ final class NavigationBarViewController: NSViewController { passwordManagementButton.menu = menu passwordManagementButton.toolTip = UserText.autofillShortcutTooltip - let url = tabCollectionViewModel.selectedTabViewModel?.tab.content.url + let url = tabCollectionViewModel.selectedTabViewModel?.tab.content.userEditableUrl passwordManagementButton.image = .passwordManagement diff --git a/DuckDuckGo/PinnedTabs/View/PinnedTabView.swift b/DuckDuckGo/PinnedTabs/View/PinnedTabView.swift index 3e067f1187..c9a0c87f8b 100644 --- a/DuckDuckGo/PinnedTabs/View/PinnedTabView.swift +++ b/DuckDuckGo/PinnedTabs/View/PinnedTabView.swift @@ -224,7 +224,9 @@ struct PinnedTabInnerView: View { .resizable() mutedTabIndicator } - } else if let domain = model.content.url?.host, let eTLDplus1 = ContentBlocking.shared.tld.eTLDplus1(domain), let firstLetter = eTLDplus1.capitalized.first.flatMap(String.init) { + } else if let domain = model.content.userEditableUrl?.host, + let eTLDplus1 = ContentBlocking.shared.tld.eTLDplus1(domain), + let firstLetter = eTLDplus1.capitalized.first.flatMap(String.init) { ZStack { Rectangle() .foregroundColor(.forString(eTLDplus1)) diff --git a/DuckDuckGo/PrivacyDashboard/PrivacyDashboardPermissionHandler.swift b/DuckDuckGo/PrivacyDashboard/PrivacyDashboardPermissionHandler.swift index f1734c4a6f..fe991eade4 100644 --- a/DuckDuckGo/PrivacyDashboard/PrivacyDashboardPermissionHandler.swift +++ b/DuckDuckGo/PrivacyDashboard/PrivacyDashboardPermissionHandler.swift @@ -60,7 +60,7 @@ final class PrivacyDashboardPermissionHandler { assertionFailure("PrivacyDashboardViewController: tabViewModel not set") return } - guard let domain = tabViewModel?.tab.content.url?.host else { + guard let domain = tabViewModel?.tab.content.urlForWebView?.host else { onPermissionChange?([]) return } diff --git a/DuckDuckGo/PrivacyDashboard/View/PrivacyDashboardViewController.swift b/DuckDuckGo/PrivacyDashboard/View/PrivacyDashboardViewController.swift index 41e1300195..befe61b87a 100644 --- a/DuckDuckGo/PrivacyDashboard/View/PrivacyDashboardViewController.swift +++ b/DuckDuckGo/PrivacyDashboard/View/PrivacyDashboardViewController.swift @@ -325,7 +325,7 @@ extension PrivacyDashboardViewController { // ⚠️ To limit privacy risk, site URL is trimmed to not include query and fragment guard let currentTab = tabViewModel?.tab, - let currentURL = currentTab.content.url?.trimmingQueryItemsAndFragment() else { + let currentURL = currentTab.content.urlForWebView?.trimmingQueryItemsAndFragment() else { throw BrokenSiteReportError.failedToFetchTheCurrentURL } let blockedTrackerDomains = currentTab.privacyInfo?.trackerInfo.trackersBlocked.compactMap { $0.domain } ?? [] @@ -335,7 +335,7 @@ extension PrivacyDashboardViewController { // current domain's protection status let configuration = ContentBlocking.shared.privacyConfigurationManager.privacyConfig - let protectionsState = configuration.isFeature(.contentBlocking, enabledForDomain: currentTab.content.url?.host) + let protectionsState = configuration.isFeature(.contentBlocking, enabledForDomain: currentTab.content.urlForWebView?.host) let webVitals = await calculateWebVitals(performanceMetrics: currentTab.brokenSiteInfo?.performanceMetrics, privacyConfig: configuration) diff --git a/DuckDuckGo/RecentlyClosed/Model/RecentlyClosedCacheItem.swift b/DuckDuckGo/RecentlyClosed/Model/RecentlyClosedCacheItem.swift index 3314b3da2a..2d313b152c 100644 --- a/DuckDuckGo/RecentlyClosed/Model/RecentlyClosedCacheItem.swift +++ b/DuckDuckGo/RecentlyClosed/Model/RecentlyClosedCacheItem.swift @@ -35,7 +35,7 @@ extension RecentlyClosedTab: RecentlyClosedCacheItemBurning { } private func contentContainsDomains(_ baseDomains: Set, tld: TLD) -> Bool { - if let host = tabContent.url?.host, let baseDomain = tld.eTLDplus1(host), baseDomains.contains(baseDomain) { + if let host = tabContent.urlForWebView?.host, let baseDomain = tld.eTLDplus1(host), baseDomains.contains(baseDomain) { return true } else { return false diff --git a/DuckDuckGo/RecentlyClosed/View/RecentlyClosedMenu.swift b/DuckDuckGo/RecentlyClosed/View/RecentlyClosedMenu.swift index cdbeec8739..3c24609a30 100644 --- a/DuckDuckGo/RecentlyClosed/View/RecentlyClosedMenu.swift +++ b/DuckDuckGo/RecentlyClosed/View/RecentlyClosedMenu.swift @@ -79,7 +79,7 @@ private extension NSMenuItem { case .url, .subscription, .identityTheftRestoration: image = recentlyClosedTab.favicon image?.size = NSSize.faviconSize - title = recentlyClosedTab.title ?? recentlyClosedTab.tabContent.url?.absoluteString ?? "" + title = recentlyClosedTab.title ?? recentlyClosedTab.tabContent.userEditableUrl?.absoluteString ?? "" if title.count > MainMenu.Constants.maxTitleLength { title = String(title.truncated(length: MainMenu.Constants.maxTitleLength)) diff --git a/DuckDuckGo/Sharing/SharingMenu.swift b/DuckDuckGo/Sharing/SharingMenu.swift index 992ddb6103..bdb91d56bf 100644 --- a/DuckDuckGo/Sharing/SharingMenu.swift +++ b/DuckDuckGo/Sharing/SharingMenu.swift @@ -49,7 +49,7 @@ final class SharingMenu: NSMenu { guard let tabViewModel = WindowControllersManager.shared.lastKeyMainWindowController?.mainViewController.tabCollectionViewModel.selectedTabViewModel, tabViewModel.canReload, !tabViewModel.isShowingErrorPage, - let url = tabViewModel.tab.content.url else { return nil } + let url = tabViewModel.tab.content.userEditableUrl else { return nil } let sharingData = DuckPlayer.shared.sharingData(for: tabViewModel.title, url: url) ?? (tabViewModel.title, url) diff --git a/DuckDuckGo/Tab/Model/Tab.swift b/DuckDuckGo/Tab/Model/Tab.swift index a799ebbb04..99f9fcbf2c 100644 --- a/DuckDuckGo/Tab/Model/Tab.swift +++ b/DuckDuckGo/Tab/Model/Tab.swift @@ -461,7 +461,7 @@ protocol NewWindowPolicyDecisionMaker { if let url = webView.url { let content = TabContent.contentFromURL(url, source: .webViewUpdated) - if self.content.isUrl, self.content.url == url { + if self.content.isUrl, self.content.urlForWebView == url { // ignore content updates when tab.content has userEntered or credential set but equal url as it comes from the WebView url updated event } else if content != self.content { self.content = content @@ -579,7 +579,7 @@ protocol NewWindowPolicyDecisionMaker { @MainActor var currentHistoryItem: BackForwardListItem? { webView.backForwardList.currentItem.map(BackForwardListItem.init) - ?? (content.url ?? navigationDelegate.currentNavigation?.url).map { url in + ?? (content.urlForWebView ?? navigationDelegate.currentNavigation?.url).map { url in BackForwardListItem(kind: .url(url), title: webView.title ?? title, identity: nil) } } @@ -608,7 +608,11 @@ protocol NewWindowPolicyDecisionMaker { let canGoBack = webView.canGoBack let canGoForward = webView.canGoForward - let canReload = self.content.userEditableUrl != nil + let canReload = if case .url(let url, credential: _, source: _) = content, !(url.isDuckPlayer || url.isDuckURLScheme) { + true + } else { + false + } if canGoBack != self.canGoBack { self.canGoBack = canGoBack @@ -721,7 +725,7 @@ protocol NewWindowPolicyDecisionMaker { if startupPreferences.launchToCustomHomePage, let customURL = URL(string: startupPreferences.formattedCustomHomePageURL) { - setContent(.url(customURL, credential: nil, source: .ui)) + setContent(.contentFromURL(customURL, source: .ui)) } else { setContent(.newtab) } @@ -895,9 +899,10 @@ protocol NewWindowPolicyDecisionMaker { } func requestFireproofToggle() { - guard let url = content.userEditableUrl, - let host = url.host - else { return } + guard case .url(let url, _, _) = content, + url.navigationalScheme?.isHypertextScheme == true, + !url.isDuckPlayer, + let host = url.host else { return } _ = FireproofDomains.shared.toggle(domain: host) } @@ -992,7 +997,7 @@ protocol NewWindowPolicyDecisionMaker { if cachedFavicon != favicon { favicon = cachedFavicon } - } else if oldValue?.url?.host != url.host { + } else if oldValue?.urlForWebView?.host != url.host { // If the domain matches the previous value, just keep the same favicon favicon = nil } @@ -1031,7 +1036,7 @@ extension Tab: FaviconUserScriptDelegate { for documentUrl: URL) { guard documentUrl != .error else { return } faviconManagement.handleFaviconLinks(faviconLinks, documentUrl: documentUrl) { favicon in - guard documentUrl == self.content.url, let favicon = favicon else { + guard documentUrl == self.content.urlForWebView, let favicon = favicon else { return } self.favicon = favicon.image @@ -1098,7 +1103,7 @@ extension Tab/*: NavigationResponder*/ { // to be moved to Tab+Navigation.swift // credential is removed from the URL and set to TabContent to be used on next Challenge self.content = .url(navigationAction.url.removingBasicAuthCredential(), credential: credential, source: .webViewUpdated) // reload URL without credentialss - request.url = self.content.url! + request.url = self.content.urlForWebView! navigator.load(request) } } diff --git a/DuckDuckGo/Tab/Model/TabContent.swift b/DuckDuckGo/Tab/Model/TabContent.swift index d052a791b9..369a036ba2 100644 --- a/DuckDuckGo/Tab/Model/TabContent.swift +++ b/DuckDuckGo/Tab/Model/TabContent.swift @@ -129,6 +129,8 @@ extension TabContent { if let settingsPane = url.flatMap(PreferencePaneIdentifier.init(url:)) { return .settings(pane: settingsPane) + } else if url?.isDuckPlayer == true, let (videoId, timestamp) = url?.youtubeVideoParams { + return .url(.duckPlayer(videoId, timestamp: timestamp), credential: nil, source: source) } else if let url, let credential = url.basicAuthCredential { // when navigating to a URL with basic auth username/password, cache it and redirect to a trimmed URL return .url(url.removingBasicAuthCredential(), credential: credential, source: source) @@ -190,19 +192,20 @@ extension TabContent { } } - var url: URL? { - userEditableUrl - } + // !!! don‘t add `url` property to avoid ambiguity with the `.url` enum case + // use `userEditableUrl` or `urlForWebView` instead. + /// user-editable URL displayed in the address bar var userEditableUrl: URL? { - switch self { - case .url(let url, credential: _, source: _) where !(url.isDuckPlayer || url.isDuckURLScheme): - return url - default: - return nil + let url = urlForWebView + if let url, url.isDuckPlayer, + let (videoID, timestamp) = url.youtubeVideoParams { + return .duckPlayer(videoID, timestamp: timestamp) } + return url } + /// `real` URL loaded in the web view var urlForWebView: URL? { switch self { case .url(let url, credential: _, source: _): @@ -290,10 +293,10 @@ extension TabContent { var canBeBookmarked: Bool { switch self { - case .subscription, .identityTheftRestoration, .dataBrokerProtection: + case .newtab, .onboarding, .none: return false - default: - return isUrl + case .url, .settings, .bookmarks, .subscription, .identityTheftRestoration, .dataBrokerProtection: + return true } } diff --git a/DuckDuckGo/Tab/TabExtensions/TabExtensions.swift b/DuckDuckGo/Tab/TabExtensions/TabExtensions.swift index 7624a298b5..139c9c2b97 100644 --- a/DuckDuckGo/Tab/TabExtensions/TabExtensions.swift +++ b/DuckDuckGo/Tab/TabExtensions/TabExtensions.swift @@ -179,7 +179,7 @@ extension TabExtensionsBuilder { HistoryTabExtension(isBurner: args.isTabBurner, historyCoordinating: dependencies.historyCoordinating, trackersPublisher: contentBlocking.trackersPublisher, - urlPublisher: args.contentPublisher.map { content in content.isUrl ? content.url : nil }, + urlPublisher: args.contentPublisher.map { content in content.isUrl ? content.urlForWebView : nil }, titlePublisher: args.titlePublisher) } add { diff --git a/DuckDuckGo/Tab/TabExtensions/TabSnapshotExtension.swift b/DuckDuckGo/Tab/TabExtensions/TabSnapshotExtension.swift index 66f84f5ebd..4aaee77a92 100644 --- a/DuckDuckGo/Tab/TabExtensions/TabSnapshotExtension.swift +++ b/DuckDuckGo/Tab/TabExtensions/TabSnapshotExtension.swift @@ -151,7 +151,8 @@ final class TabSnapshotExtension { @MainActor func renderWebViewSnapshot() async { - guard let webView, let tabContent, let url = tabContent.url else { + guard let webView, let tabContent, + let url = tabContent.userEditableUrl, !url.isDuckURLScheme else { // Previews of native views are rendered in renderNativePreview() return } diff --git a/DuckDuckGo/Tab/TabLazyLoader/LazyLoadable.swift b/DuckDuckGo/Tab/TabLazyLoader/LazyLoadable.swift index 9d2d84628d..7fb5446cb5 100644 --- a/DuckDuckGo/Tab/TabLazyLoader/LazyLoadable.swift +++ b/DuckDuckGo/Tab/TabLazyLoader/LazyLoadable.swift @@ -37,7 +37,7 @@ protocol LazyLoadable: AnyObject, Identifiable { extension Tab: LazyLoadable { var isUrl: Bool { content.isUrl } - var url: URL? { content.url } + var url: URL? { content.urlForWebView } var loadingFinishedPublisher: AnyPublisher { navigationStatePublisher.compactMap { $0 } diff --git a/DuckDuckGo/Tab/View/BrowserTabViewController.swift b/DuckDuckGo/Tab/View/BrowserTabViewController.swift index 327dc8975f..9c2f65619f 100644 --- a/DuckDuckGo/Tab/View/BrowserTabViewController.swift +++ b/DuckDuckGo/Tab/View/BrowserTabViewController.swift @@ -776,7 +776,7 @@ extension BrowserTabViewController: NSDraggingDestination { return true } - selectedTab.setContent(.url(url, source: .appOpenUrl)) + selectedTab.setContent(.contentFromURL(url, source: .appOpenUrl)) return true } diff --git a/DuckDuckGo/Tab/ViewModel/TabViewModel.swift b/DuckDuckGo/Tab/ViewModel/TabViewModel.swift index 42ac6fcd45..ef1365ec9c 100644 --- a/DuckDuckGo/Tab/ViewModel/TabViewModel.swift +++ b/DuckDuckGo/Tab/ViewModel/TabViewModel.swift @@ -233,11 +233,11 @@ final class TabViewModel { } private func updateCanBeBookmarked() { - canBeBookmarked = !isShowingErrorPage && (tab.content.url ?? .blankPage) != .blankPage + canBeBookmarked = !isShowingErrorPage && tab.content.canBeBookmarked } private var tabURL: URL? { - return tab.content.url + return tab.content.userEditableUrl } private var tabHostURL: URL? { diff --git a/DuckDuckGo/TabBar/View/TabBarViewController.swift b/DuckDuckGo/TabBar/View/TabBarViewController.swift index 8f0253224b..85e0d4811e 100644 --- a/DuckDuckGo/TabBar/View/TabBarViewController.swift +++ b/DuckDuckGo/TabBar/View/TabBarViewController.swift @@ -1041,7 +1041,7 @@ extension TabBarViewController: TabBarViewItemDelegate { func tabBarViewItemBookmarkThisPageAction(_ tabBarViewItem: TabBarViewItem) { guard let indexPath = collectionView.indexPath(for: tabBarViewItem), let tabViewModel = tabCollectionViewModel.tabViewModel(at: indexPath.item), - let url = tabViewModel.tab.content.url else { + let url = tabViewModel.tab.content.userEditableUrl else { os_log("TabBarViewController: Failed to get index path of tab bar view item", type: .error) return } diff --git a/DuckDuckGo/TabBar/View/TabBarViewItem.swift b/DuckDuckGo/TabBar/View/TabBarViewItem.swift index a92752aa56..5a75290fc9 100644 --- a/DuckDuckGo/TabBar/View/TabBarViewItem.swift +++ b/DuckDuckGo/TabBar/View/TabBarViewItem.swift @@ -253,7 +253,7 @@ final class TabBarViewItem: NSCollectionViewItem { }.store(in: &cancellables) tabViewModel.tab.$content.sink { [weak self] content in - self?.currentURL = content.url + self?.currentURL = content.userEditableUrl }.store(in: &cancellables) tabViewModel.$usedPermissions.assign(to: \.usedPermissions, onWeaklyHeld: self).store(in: &cancellables) diff --git a/DuckDuckGo/TabPreview/TabPreviewViewController.swift b/DuckDuckGo/TabPreview/TabPreviewViewController.swift index 9e888b4146..cdf6d3e721 100644 --- a/DuckDuckGo/TabPreview/TabPreviewViewController.swift +++ b/DuckDuckGo/TabPreview/TabPreviewViewController.swift @@ -185,7 +185,7 @@ extension TabPreviewViewController { let title: String var tabContent: Tab.TabContent let shouldShowPreview: Bool - var addressBarString: String { tabContent.url?.absoluteString ?? "Default" } + var addressBarString: String { tabContent.userEditableUrl?.absoluteString ?? "Default" } var snapshot: NSImage? { let image = NSImage(size: size) diff --git a/DuckDuckGo/Windows/View/WindowControllersManager.swift b/DuckDuckGo/Windows/View/WindowControllersManager.swift index 10758ce56d..1bea6538a8 100644 --- a/DuckDuckGo/Windows/View/WindowControllersManager.swift +++ b/DuckDuckGo/Windows/View/WindowControllersManager.swift @@ -174,12 +174,12 @@ extension WindowControllersManager { let firstTab = tabCollection.tabs.first, case .newtab = firstTab.content, !newTab { - firstTab.setContent(url.map { .url($0, source: source) } ?? .newtab) + firstTab.setContent(url.map { .contentFromURL($0, source: source) } ?? .newtab) } else if let tab = tabCollectionViewModel.selectedTabViewModel?.tab, !newTab { - tab.setContent(url.map { .url($0, source: source) } ?? .newtab) + tab.setContent(url.map { .contentFromURL($0, source: source) } ?? .newtab) } else { let newTab = Tab(content: url.map { .url($0, source: source) } ?? .newtab, shouldLoadInBackground: true, burnerMode: tabCollectionViewModel.burnerMode) - newTab.setContent(url.map { .url($0, source: source) } ?? .newtab) + newTab.setContent(url.map { .contentFromURL($0, source: source) } ?? .newtab) tabCollectionViewModel.append(tab: newTab) } } diff --git a/IntegrationTests/NavigationProtection/NavigationProtectionIntegrationTests.swift b/IntegrationTests/NavigationProtection/NavigationProtectionIntegrationTests.swift index f06aa2ade3..9cbc7831bf 100644 --- a/IntegrationTests/NavigationProtection/NavigationProtectionIntegrationTests.swift +++ b/IntegrationTests/NavigationProtection/NavigationProtectionIntegrationTests.swift @@ -77,7 +77,7 @@ class NavigationProtectionIntegrationTests: XCTestCase { for i in 0..