diff --git a/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift b/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift index 13afd90eb0..80b783a455 100644 --- a/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift +++ b/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift @@ -596,6 +596,13 @@ final class DuckPlayerNavigationHandler: NSObject { duckPlayerModeCancellable = nil } + /// Checks if a URL contains a hash + /// + /// - Parameter url: The `URL` used to determine the tab type. + private func urlContainsHash(_ url: URL) -> Bool { + return url.fragment != nil && !url.fragment!.isEmpty + } + } extension DuckPlayerNavigationHandler: DuckPlayerNavigationHandling { @@ -937,13 +944,10 @@ extension DuckPlayerNavigationHandler: DuckPlayerNavigationHandling { } // Allow Youtube's internal navigation when DuckPlayer is enabled and user is watching on Youtube - // This is to prevent DuckPlayer from interfering with Youtube's internal navigation for search and settings - // https://app.asana.com/0/1204099484721401/1208930843675395/f - if let (destinationVideoID, _) = url.youtubeVideoParams, - let (originVideoID, _) = webView.url?.youtubeVideoParams, - destinationVideoID == originVideoID, - duckPlayerMode == .enabled { - return false + // Youtube uses hashes to navigate within some settings + // This allows any navigation that includes a hash # (#searching, #bottom-sheet, etc) + if urlContainsHash(url), url.isYoutubeWatch { + return false } // Redirect to Duck Player if enabled diff --git a/DuckDuckGoTests/YoutublePlayerNavigationHandlerTests.swift b/DuckDuckGoTests/YoutublePlayerNavigationHandlerTests.swift index 3ae7bc73ea..c804b5047f 100644 --- a/DuckDuckGoTests/YoutublePlayerNavigationHandlerTests.swift +++ b/DuckDuckGoTests/YoutublePlayerNavigationHandlerTests.swift @@ -583,20 +583,37 @@ class DuckPlayerNavigationHandlerTests: XCTestCase { } @MainActor - func testHandleDelegateNavigation_YoutubeWatchURLWithDuckPlayerEnabledAndSameVideoNavigation_ReturnsFalse() async { + func testHandleDelegateNavigation_YoutubeInternalNavigation_ReturnsFalse() async { // Arrange - let youtubeURL = URL(string: "https://www.youtube.com/watch?v=abc123")! - let youtubeInternalURL = URL(string: "https://www.youtube.com/watch?v=abc123&settings")! + let youtubeURL = URL(string: "https://www.youtube.com/watch?v=abc123#searching")! let request = URLRequest(url: youtubeURL) let mockFrameInfo = MockFrameInfo(isMainFrame: true) let navigationAction = MockNavigationAction(request: request, targetFrame: mockFrameInfo) playerSettings.mode = .enabled featureFlagger.enabledFeatures = [.duckPlayer, .duckPlayerOpenInNewTab] - mockWebView.setCurrentURL(youtubeInternalURL) + mockWebView.setCurrentURL(youtubeURL) // Act - let shouldCancel = handler.handleDelegateNavigation(navigationAction: navigationAction, webView: mockWebView) + var shouldCancel = handler.handleDelegateNavigation(navigationAction: navigationAction, webView: mockWebView) + + // Assert + XCTAssertFalse(shouldCancel, "Expected navigation NOT to be cancelled as it's Youtube Internal navigation") + + // Arrange + playerSettings.mode = .disabled + + // Act + shouldCancel = handler.handleDelegateNavigation(navigationAction: navigationAction, webView: mockWebView) + + // Assert + XCTAssertFalse(shouldCancel, "Expected navigation NOT to be cancelled as it's Youtube Internal navigation") + + // Arrange + featureFlagger.enabledFeatures = [.duckPlayer] + + // Act + shouldCancel = handler.handleDelegateNavigation(navigationAction: navigationAction, webView: mockWebView) // Assert XCTAssertFalse(shouldCancel, "Expected navigation NOT to be cancelled as it's Youtube Internal navigation")