diff --git a/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift b/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift index 122ef9f834..29f0ebab75 100644 --- a/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift +++ b/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift @@ -22,12 +22,14 @@ import ContentScopeScripts import WebKit import Core import Common +import BrowserServicesKit final class DuckPlayerNavigationHandler { var duckPlayer: DuckPlayerProtocol var referrer: DuckPlayerReferrer = .other var lastHandledVideoID: String? + var featureFlagger: FeatureFlagger private struct Constants { static let SERPURL = "https://duckduckgo.com/" @@ -45,8 +47,10 @@ final class DuckPlayerNavigationHandler { static let urlInternalReferrer = "embeds_referring_euri" } - init(duckPlayer: DuckPlayerProtocol = DuckPlayer()) { + init(duckPlayer: DuckPlayerProtocol = DuckPlayer(), + featureFlagger: FeatureFlagger = AppDependencyProvider.shared.featureFlagger) { self.duckPlayer = duckPlayer + self.featureFlagger = featureFlagger } static var htmlTemplatePath: String { @@ -99,6 +103,10 @@ final class DuckPlayerNavigationHandler { guard let url else { return } + guard featureFlagger.isFeatureOn(.duckPlayer) else { + return + } + if let (videoID, _) = url.youtubeVideoParams, videoID == lastHandledVideoID { os_log("DP: URL (%s) already handled, skipping", log: .duckPlayerLog, type: .debug, url.absoluteString) @@ -136,6 +144,10 @@ extension DuckPlayerNavigationHandler: DuckNavigationHandling { guard let url = navigationAction.request.url else { return } + guard featureFlagger.isFeatureOn(.duckPlayer) else { + return + } + // Handle Youtube internal links like "Age restricted" and "Copyright restricted" videos // These should not be handled by DuckPlayer if url.isYoutubeVideo, @@ -202,6 +214,11 @@ extension DuckPlayerNavigationHandler: DuckNavigationHandling { return } + guard featureFlagger.isFeatureOn(.duckPlayer) else { + completion(.allow) + return + } + if let (videoID, _) = url.youtubeVideoParams, videoID == lastHandledVideoID, !url.hasWatchInYoutubeQueryParameter { @@ -240,6 +257,11 @@ extension DuckPlayerNavigationHandler: DuckNavigationHandling { @MainActor func handleJSNavigation(url: URL?, webView: WKWebView) { + + guard featureFlagger.isFeatureOn(.duckPlayer) else { + return + } + handleURLChange(url: url, webView: webView) } @@ -248,6 +270,11 @@ extension DuckPlayerNavigationHandler: DuckNavigationHandling { os_log("DP: Handling Back Navigation", log: .duckPlayerLog, type: .debug) + guard featureFlagger.isFeatureOn(.duckPlayer) else { + webView.goBack() + return + } + lastHandledVideoID = nil webView.stopLoading() @@ -280,6 +307,11 @@ extension DuckPlayerNavigationHandler: DuckNavigationHandling { @MainActor func handleReload(webView: WKWebView) { + guard featureFlagger.isFeatureOn(.duckPlayer) else { + webView.reload() + return + } + lastHandledVideoID = nil webView.stopLoading() if let url = webView.url, url.isDuckPlayer, @@ -296,6 +328,10 @@ extension DuckPlayerNavigationHandler: DuckNavigationHandling { @MainActor func handleAttach(webView: WKWebView) { + guard featureFlagger.isFeatureOn(.duckPlayer) else { + return + } + if let url = webView.url, url.isDuckPlayer, !url.isDuckURLScheme, duckPlayer.settings.mode == .enabled || duckPlayer.settings.mode == .alwaysAsk { diff --git a/DuckDuckGoTests/DuckPlayerMocks.swift b/DuckDuckGoTests/DuckPlayerMocks.swift index 09d95354a8..0294c12287 100644 --- a/DuckDuckGoTests/DuckPlayerMocks.swift +++ b/DuckDuckGoTests/DuckPlayerMocks.swift @@ -152,3 +152,10 @@ final class MockDuckPlayer: DuckPlayerProtocol { nil } } + +final class MockDuckPlayerFeatureFlagger: FeatureFlagger { + func isFeatureOn(forProvider: F) -> Bool where F: BrowserServicesKit.FeatureFlagSourceProviding { + return true + } + +} diff --git a/DuckDuckGoTests/YoutublePlayerNavigationHandlerTests.swift b/DuckDuckGoTests/YoutublePlayerNavigationHandlerTests.swift index 8fbdc48685..2b19d9b41d 100644 --- a/DuckDuckGoTests/YoutublePlayerNavigationHandlerTests.swift +++ b/DuckDuckGoTests/YoutublePlayerNavigationHandlerTests.swift @@ -34,6 +34,7 @@ class DuckPlayerNavigationHandlerTests: XCTestCase { var mockPrivacyConfig: PrivacyConfigurationManagerMock! var playerSettings: MockDuckPlayerSettings! var player: MockDuckPlayer! + var featureFlagger: FeatureFlagger! override func setUp() { super.setUp() @@ -42,7 +43,9 @@ class DuckPlayerNavigationHandlerTests: XCTestCase { mockNavigationDelegate = MockWKNavigationDelegate() mockAppSettings = AppSettingsMock() mockPrivacyConfig = PrivacyConfigurationManagerMock() + featureFlagger = MockDuckPlayerFeatureFlagger() webView.navigationDelegate = mockNavigationDelegate + } override func tearDown() { @@ -129,7 +132,7 @@ class DuckPlayerNavigationHandlerTests: XCTestCase { let expectation = self.expectation(description: "Completion handler called") let playerSettings = MockDuckPlayerSettings(appSettings: mockAppSettings, privacyConfigManager: mockPrivacyConfig) let player = MockDuckPlayer(settings: playerSettings) - let handler = DuckPlayerNavigationHandler(duckPlayer: player) + let handler = DuckPlayerNavigationHandler(duckPlayer: player, featureFlagger: featureFlagger) var navigationPolicy: WKNavigationActionPolicy? handler.handleDecidePolicyFor(navigationAction, completion: { policy in @@ -152,7 +155,7 @@ class DuckPlayerNavigationHandlerTests: XCTestCase { let playerSettings = MockDuckPlayerSettings(appSettings: mockAppSettings, privacyConfigManager: mockPrivacyConfig) playerSettings.mode = .enabled let player = MockDuckPlayer(settings: playerSettings) - let handler = DuckPlayerNavigationHandler(duckPlayer: player) + let handler = DuckPlayerNavigationHandler(duckPlayer: player, featureFlagger: featureFlagger) var navigationPolicy: WKNavigationActionPolicy? handler.handleDecidePolicyFor(navigationAction, completion: { policy in @@ -175,7 +178,7 @@ class DuckPlayerNavigationHandlerTests: XCTestCase { let playerSettings = MockDuckPlayerSettings(appSettings: mockAppSettings, privacyConfigManager: mockPrivacyConfig) playerSettings.mode = .enabled let player = MockDuckPlayer(settings: playerSettings) - let handler = DuckPlayerNavigationHandler(duckPlayer: player) + let handler = DuckPlayerNavigationHandler(duckPlayer: player, featureFlagger: featureFlagger) var navigationPolicy: WKNavigationActionPolicy? handler.handleDecidePolicyFor(navigationAction, completion: { policy in @@ -200,7 +203,7 @@ class DuckPlayerNavigationHandlerTests: XCTestCase { let playerSettings = MockDuckPlayerSettings(appSettings: mockAppSettings, privacyConfigManager: mockPrivacyConfig) let player = MockDuckPlayer(settings: playerSettings) - let handler = DuckPlayerNavigationHandler(duckPlayer: player) + let handler = DuckPlayerNavigationHandler(duckPlayer: player, featureFlagger: featureFlagger) handler.lastHandledVideoID = "abc123" handler.handleJSNavigation(url: youtubeURL, webView: webView) @@ -217,7 +220,7 @@ class DuckPlayerNavigationHandlerTests: XCTestCase { let playerSettings = MockDuckPlayerSettings(appSettings: mockAppSettings, privacyConfigManager: mockPrivacyConfig) let player = MockDuckPlayer(settings: playerSettings) - let handler = DuckPlayerNavigationHandler(duckPlayer: player) + let handler = DuckPlayerNavigationHandler(duckPlayer: player, featureFlagger: featureFlagger) handler.handleJSNavigation(url: youtubeURL, webView: webView) @@ -233,7 +236,7 @@ class DuckPlayerNavigationHandlerTests: XCTestCase { let playerSettings = MockDuckPlayerSettings(appSettings: mockAppSettings, privacyConfigManager: mockPrivacyConfig) playerSettings.mode = .enabled let player = MockDuckPlayer(settings: playerSettings) - let handler = DuckPlayerNavigationHandler(duckPlayer: player) + let handler = DuckPlayerNavigationHandler(duckPlayer: player, featureFlagger: featureFlagger) handler.handleJSNavigation(url: youtubeURL, webView: webView) @@ -250,7 +253,7 @@ class DuckPlayerNavigationHandlerTests: XCTestCase { let playerSettings = MockDuckPlayerSettings(appSettings: mockAppSettings, privacyConfigManager: mockPrivacyConfig) playerSettings.mode = .enabled let player = MockDuckPlayer(settings: playerSettings) - let handler = DuckPlayerNavigationHandler(duckPlayer: player) + let handler = DuckPlayerNavigationHandler(duckPlayer: player, featureFlagger: featureFlagger) handler.handleNavigation(navigationAction, webView: webView) XCTAssertEqual(webView.url, nil) @@ -265,7 +268,7 @@ class DuckPlayerNavigationHandlerTests: XCTestCase { let playerSettings = MockDuckPlayerSettings(appSettings: mockAppSettings, privacyConfigManager: mockPrivacyConfig) playerSettings.mode = .enabled let player = MockDuckPlayer(settings: playerSettings) - let handler = DuckPlayerNavigationHandler(duckPlayer: player) + let handler = DuckPlayerNavigationHandler(duckPlayer: player, featureFlagger: featureFlagger) handler.handleNavigation(navigationAction, webView: webView) @@ -287,7 +290,7 @@ class DuckPlayerNavigationHandlerTests: XCTestCase { let playerSettings = MockDuckPlayerSettings(appSettings: mockAppSettings, privacyConfigManager: mockPrivacyConfig) playerSettings.mode = .enabled let player = MockDuckPlayer(settings: playerSettings) - let handler = DuckPlayerNavigationHandler(duckPlayer: player) + let handler = DuckPlayerNavigationHandler(duckPlayer: player, featureFlagger: featureFlagger) handler.handleReload(webView: mockWebView) if let loadedRequest = mockWebView.lastLoadedRequest { @@ -305,7 +308,7 @@ class DuckPlayerNavigationHandlerTests: XCTestCase { let playerSettings = MockDuckPlayerSettings(appSettings: mockAppSettings, privacyConfigManager: mockPrivacyConfig) playerSettings.mode = .enabled let player = MockDuckPlayer(settings: playerSettings) - let handler = DuckPlayerNavigationHandler(duckPlayer: player) + let handler = DuckPlayerNavigationHandler(duckPlayer: player, featureFlagger: featureFlagger) handler.handleAttach(webView: mockWebView) if let loadedRequest = mockWebView.lastLoadedRequest {