Skip to content

[Bug] <handleAppLink ignores shouldHaltWebNavigation for same-domain app links after first trigger> #7097

@chrisegener22

Description

@chrisegener22

Describe the bug

In DuckDuckGoAppLinksHandler.handleAppLink, when previousUrl is non-null and isSameOrSubdomain(previousUrl, urlString) is true, the method can launch the app link but always returns false unless the domain is in alwaysTriggerList.

This means:

For the first app link navigation on a domain (when previousUrl is null), handleAppLink returns shouldHaltWebNavigation, so the caller can stop web navigation if requested.

For subsequent navigations on the same (sub)domain, the function can call launchAppLink() but always returns false (unless alwaysTriggerList matches), ignoring the shouldHaltWebNavigation flag completely.

This leads to inconsistent behavior: the first navigation correctly halts the web view according to shouldHaltWebNavigation, but subsequent navigations on the same domain do not, even though the app link is launched again.

Relevant code:

override fun handleAppLink(
isForMainFrame: Boolean,
urlString: String,
appLinksEnabled: Boolean,
shouldHaltWebNavigation: Boolean,
launchAppLink: () -> Unit,
): Boolean {
if (!appLinksEnabled || !isForMainFrame) {
return false
}

previousUrl?.let {
    if (isSameOrSubdomain(it, urlString)) {
        val shouldTrigger = alwaysTriggerList.contains(urlString.extractDomain())
        if (isAUserQuery || !hasTriggeredForDomain || shouldTrigger) {
            previousUrl = urlString
            launchAppLink()
            hasTriggeredForDomain = true
            if (shouldTrigger) return true
        }
        return false        // <- shouldHaltWebNavigation is ignored here
    }
}

previousUrl = urlString
launchAppLink()
hasTriggeredForDomain = true
return shouldHaltWebNavigation

}

How to Reproduce

High-level reproduction using the handler logic:

Ensure:

appLinksEnabled = true

isForMainFrame = true

isAUserQuery = true (or hasTriggeredForDomain = false)

urlString is an app-link-eligible URL on domain example.com, not in alwaysTriggerList.

shouldHaltWebNavigation = true.

First navigation:

previousUrl is null.

Call handleAppLink(..., urlString = "https://example.com/path1", shouldHaltWebNavigation = true, launchAppLink = { /* record call */ }).

handleAppLink:

Skips previousUrl?.let block.

Sets previousUrl = "https://example.com/path1".

Calls launchAppLink().

Returns true (because shouldHaltWebNavigation = true).

Second navigation on same domain:

Now previousUrl = "https://example.com/path1", hasTriggeredForDomain = true, but isAUserQuery = true (or explicitly set hasTriggeredForDomain = false to force another trigger).

Call handleAppLink(..., urlString = "https://example.com/path2", shouldHaltWebNavigation = true, launchAppLink = { /* record call again */ }).

isSameOrSubdomain(previousUrl, urlString) is true.

Observe:

Within the previousUrl?.let block, the conditions are met so:

launchAppLink() is called.

The function then returns false because shouldTrigger is false (domain not in alwaysTriggerList).

Expected behavior

For same-domain app links where launchAppLink() is actually invoked, handleAppLink should respect shouldHaltWebNavigation consistently, just like it does for the first navigation.

That is, when:

appLinksEnabled = true

isForMainFrame = true

The handler decides to launch the app link

shouldHaltWebNavigation = true

Then handleAppLink should return true so the caller can halt web navigation, regardless of whether this is the first app link on the domain or a subsequent same-domain navigation.

Environment

- DDG App Version:
- Device:
- OS:

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions