Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DuckPlayer updates and rewrites #3455

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6fc8a2e
Open DuckPlayer Videos in New Tab #1
afterxleep Oct 8, 2024
5c75a0c
Handle JS Based navigation
afterxleep Oct 11, 2024
425ec8f
Add Setting
afterxleep Oct 11, 2024
0fef7ec
Added Unit tests
afterxleep Oct 11, 2024
e3510f0
Added Feature Flag
afterxleep Oct 11, 2024
fe9c60c
Merge branch 'main' into daniel/dp.new.tab.base
afterxleep Oct 11, 2024
20ea632
Correct Feature Flag
afterxleep Oct 11, 2024
5212141
Use Local navigationType instead
afterxleep Oct 11, 2024
c63c339
Fix unit tests
afterxleep Oct 11, 2024
5dd2f22
Adding Pixels
afterxleep Oct 14, 2024
44bd50e
Only trigger JS Navigation if not WatchInYoutube
afterxleep Oct 14, 2024
d59d700
Default to true
afterxleep Oct 14, 2024
b42f186
Updated DuckPlayer logic to base on URL changes
afterxleep Oct 17, 2024
c3d184f
Updated Navigation
afterxleep Oct 17, 2024
c0098ae
Fixes back navigation issue
afterxleep Oct 18, 2024
5ee35b5
Improved Duck Navigation Handling
afterxleep Oct 18, 2024
da66c1f
Add Documentation
afterxleep Oct 18, 2024
54b9d6f
Added tests for URL navigation
afterxleep Oct 18, 2024
f12cdb1
Improve back Navigation
afterxleep Oct 18, 2024
81ccbd4
Added backForward navigation
afterxleep Oct 18, 2024
1e61ea7
Handler optimizations
afterxleep Oct 18, 2024
1c39641
Fixed OpenInYoutube navigation and added test
afterxleep Oct 18, 2024
e0db486
Fixed age restricted video
afterxleep Oct 18, 2024
8186c52
Make back-forward navigation safe
afterxleep Oct 18, 2024
670f24d
Remove DuckPlayer Experiment logic
afterxleep Oct 18, 2024
dd37f16
Remove Experiment from tests
afterxleep Oct 18, 2024
55d1a9d
Update DuckPlayer load timer
afterxleep Oct 18, 2024
52ca138
Moves the stopLoading to happen after the delay
afterxleep Oct 18, 2024
ccda462
Reduce Request time
afterxleep Oct 18, 2024
db8970c
Properly handle Youtube Player internal links
afterxleep Oct 21, 2024
7449f76
Update navigation cancel handler
afterxleep Oct 22, 2024
c390464
Point to BSK temp branch
afterxleep Oct 22, 2024
67a118a
Merge branch 'main' into daniel/dp.new.tab.base
afterxleep Oct 22, 2024
419a253
Update back navigation
afterxleep Oct 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 48 additions & 4 deletions DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ final class DuckPlayerNavigationHandler {
static let urlInternalReferrer = "embeds_referring_euri"
static let youtubeScheme = "youtube://"
static let duckPlayerScheme = URL.NavigationalScheme.duck.rawValue
static let duckPlayerHeaderKey = "X-Navigation-Source"
static let duckPlayerHeaderValue = "DuckPlayer"
static let duckPlayerReferrerHeaderKey = "X-Navigation-DuckPlayerReferrer"
}

init(duckPlayer: DuckPlayerProtocol = DuckPlayer(),
Expand Down Expand Up @@ -171,7 +174,7 @@ final class DuckPlayerNavigationHandler {
renderedVideoID = videoID
let duckPlayerURL = URL.duckPlayer(videoID)
Logger.duckPlayer.debug("DP: Redirecting to DuckPlayer Video: \(duckPlayerURL.absoluteString)")
webView.load(URLRequest(url: duckPlayerURL))
loadWithDuckPlayerHeaders(URLRequest(url: duckPlayerURL), referrer: referrer, webView: webView)

}

Expand All @@ -189,7 +192,7 @@ final class DuckPlayerNavigationHandler {
duckPlayer.settings.allowFirstVideo = true
renderedVideoID = videoID
if let finalURL = redirectURL.addingWatchInYoutubeQueryParameter() {
webView.load(URLRequest(url: redirectURL))
loadWithDuckPlayerHeaders(URLRequest(url: redirectURL), referrer: referrer, webView: webView)
}
}

Expand Down Expand Up @@ -226,6 +229,17 @@ final class DuckPlayerNavigationHandler {
}
}

// Replaces webView.load to add DuckPlayer headers, used for navigation
func loadWithDuckPlayerHeaders(_ request: URLRequest, referrer: DuckPlayerReferrer, webView: WKWebView) {

var newRequest = request

newRequest.addValue("DuckPlayer", forHTTPHeaderField: DuckPlayerNavigationHandler.Constants.duckPlayerHeaderKey)
newRequest.addValue(referrer.stringValue, forHTTPHeaderField: DuckPlayerNavigationHandler.Constants.duckPlayerReferrerHeaderKey)

webView.load(newRequest)
}

}

extension DuckPlayerNavigationHandler: DuckPlayerNavigationHandling {
Expand All @@ -235,15 +249,15 @@ extension DuckPlayerNavigationHandler: DuckPlayerNavigationHandling {
func handleNavigation(_ navigationAction: WKNavigationAction, webView: WKWebView) {

Logger.duckPlayer.debug("Handling Navigation for \(navigationAction.request.url?.absoluteString ?? "")")

duckPlayer.settings.allowFirstVideo = false // Disable overlay for first video

guard let url = navigationAction.request.url else { return }

// Redirect to YouTube if DuckPlayer is disabled
guard featureFlagger.isFeatureOn(.duckPlayer) && duckPlayer.settings.mode != .disabled else {
if let (videoID, _) = url.youtubeVideoParams {
webView.load(URLRequest(url: URL.youtube(videoID)))
loadWithDuckPlayerHeaders(URLRequest(url: URL.youtube(videoID)), referrer: referrer, webView: webView)
}
return
}
Expand Down Expand Up @@ -514,10 +528,40 @@ extension DuckPlayerNavigationHandler: DuckPlayerNavigationHandling {
return false
}

// Determine if navigation should be cancelled
// This is to be used in DecidePolicy For to prevent the webView
// from opening the Youtube app on user-triggered links
@MainActor
func shouldCancelNavigation(navigationAction: WKNavigationAction, webView: WKWebView) -> Bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a test case for this?


// Check if the custom "X-Navigation-Source" header is present
if let headers = navigationAction.request.allHTTPHeaderFields,
let navigationSource = headers[Constants.duckPlayerHeaderKey],
navigationSource == Constants.duckPlayerHeaderValue {
return false
}

// Otherwise, validate if the page is a Youtube page, and DuckPlayer is Enabled
if featureFlagger.isFeatureOn(.duckPlayer),
duckPlayer.settings.mode != .disabled,
let url = navigationAction.request.url,
url.isYoutube || url.isYoutubeWatch {

// Cancel the current loading and load with DuckPlayer headers
webView.stopLoading()
loadWithDuckPlayerHeaders(navigationAction.request, referrer: referrer, webView: webView)
return true
}

// Allow all other navigations
return false
}

}

extension WKWebView {
var isEmptyTab: Bool {
return self.url == nil || self.url?.absoluteString == "about:blank"
}

}
1 change: 1 addition & 0 deletions DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandling.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ protocol DuckPlayerNavigationHandling: AnyObject {
func handleEvent(event: DuckPlayerNavigationEvent,
url: URL?,
navigationAction: WKNavigationAction?)
func shouldCancelNavigation(navigationAction: WKNavigationAction, webView: WKWebView) -> Bool
func shouldOpenInNewTab(_ navigationAction: WKNavigationAction, webView: WKWebView) -> Bool


Expand Down
8 changes: 8 additions & 0 deletions DuckDuckGo/TabViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1704,6 +1704,14 @@ extension TabViewController: WKNavigationDelegate {
}
}

// Prevent the YouTube app from intercepting
// links based on DuckPlayer settings
if let handler = duckPlayerNavigationHandler,
handler.shouldCancelNavigation(navigationAction: navigationAction, webView: webView) {
decisionHandler(.cancel)
return
}

if let url = navigationAction.request.url,
!url.isDuckDuckGoSearch,
true == shouldWaitUntilContentBlockingIsLoaded({ [weak self, webView /* decision handler must be called */] in
Expand Down