Bug 165627 - itunes app links fail to navigate window.top when URL inside timeout
Summary: itunes app links fail to navigate window.top when URL inside timeout
Status: NEW
Alias: None
Product: WebKit
Classification: Unclassified
Component: Frames (show other bugs)
Version: Safari 10
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2016-12-08 14:37 PST by Mohammed Khatib
Modified: 2018-05-10 01:07 PDT (History)
12 users (show)

See Also:


Attachments
testcase (706 bytes, text/html)
2018-04-12 00:21 PDT, Frédéric Wang (:fredw)
no flags Details
video of failure (simulator) (1.94 MB, video/quicktime)
2018-04-12 00:25 PDT, Frédéric Wang (:fredw)
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Mohammed Khatib 2016-12-08 14:37:22 PST
These tests were done in this environment: 

top: https://hello.com
  iframe: https://whatsapp.com
     script loaded from: https://whatsapp.com
        executes in timeout -> window.top.location.replace('https://anylink.com') // <- Navigation works as expected
        executes in timeout -> window.top.location.replace('https://itunes.apple.com/us/app/id828256236') // <- Nothing happens.
        executes in timeout -> window.top.location.href = 'https://anylink.com' // <- Navigation works as expected.
        executes in timeout -> window.top.location.href = 'https://itunes.apple.com/us/app/id828256236' // <- Nothing happens.



Here's the real examples:
Using href = url inside timeout in cross origin iframes: http://top.webjs.run/top-location-redirect/index-12.html
Using top.location.replace(url) inside timeout in cross origin iframes: http://top.webjs.run/top-location-redirect/index-13.html


Notice in both examples, on Safari Mac OS X both of them redirects the user after 5seconds to itunes store (as expected).
On iOS Safari however, the first timeout (5 seconds) fails to navigate the user to itunes store (app or web page) and instead after 7 seconds the second timeout takes the user to google.com normally.
Comment 1 Radar WebKit Bug Importer 2017-02-26 17:07:49 PST
<rdar://problem/30725488>
Comment 2 Frédéric Wang (:fredw) 2018-02-12 06:38:15 PST
(In reply to Mohammed Khatib from comment #0)
> Here's the real examples:
> Using href = url inside timeout in cross origin iframes:
> http://top.webjs.run/top-location-redirect/index-12.html
> Using top.location.replace(url) inside timeout in cross origin iframes:
> http://top.webjs.run/top-location-redirect/index-13.html

@Mohammed: Can you please provide new test cases to reproduce the issue? These two URLs do not seem to work any more :-(
Comment 3 Frédéric Wang (:fredw) 2018-04-12 00:21:50 PDT
Created attachment 337779 [details]
testcase

This is a testcase provided by AMP developers. It works well with a device but is a bit harder to reproduce on the simulator (and I've not identified a reliable way to reproduce it).
Comment 4 Frédéric Wang (:fredw) 2018-04-12 00:25:01 PDT
Created attachment 337780 [details]
video of failure (simulator)

This video shows when the testcase fails with the simulator. A popup message opens with "Safari cannot open the page because the address is invalid" and blocks the redirection to itunes and then the redirection to google happens. Looking for this message on the Web, I see similar issues e.g. https://stackoverflow.com/questions/27049228/safari-cannot-open-the-page-because-the-address-is-invalid-for-google-maps-lin
Comment 5 Frédéric Wang (:fredw) 2018-04-27 10:48:57 PDT
So trying to debug on the simulator, "window.top.location.href = 'https://itunes.apple.com/us/app/id828256236'" triggers the following backtrace on the Web process:

#0 WebCore::NavigationScheduler::startTimer()
#1 WebCore::NavigationScheduler::schedule
#2 WebCore::NavigationScheduler::scheduleLocationChange
#3 WebCore::DOMWindow::setLocation
#4 WebCore::Location::setLocation
#5 WebCore::Location::setHref

Which in turn schedules a load to 'https://itunes.apple.com/us/app/id828256236':

#0 WebKit::WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction
#1 WebCore::PolicyChecker::checkNavigationPolicy
#2 WebCore::FrameLoader::loadWithDocumentLoader
#3 WebCore::FrameLoader::loadWithNavigationAction
#4 WebCore::FrameLoader::loadURL
#5 WebCore::FrameLoader::loadFrameRequest
#6 WebCore::FrameLoader::urlSelected
#7 WebCore::FrameLoader::changeLocation
#8 WebCore::ScheduledLocationChange::fire
#9 WebCore::NavigationScheduler::timerFired()

As said in comment 3, the issue is a bit difficult to reproduce on the simulator, but when the message "Safari cannot open the page because the address is invalid" does show up, I actually later see a request to 'itmss://itunes.apple.com/us/app/id828256236':

#0 WebKit::WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction
#1 WebCore::PolicyChecker::checkNavigationPolicy
#2 WebCore::PolicyChecker::checkNavigationPolicy
#3 WebCore::DocumentLoader::willSendRequest
#4 WebCore::DocumentLoader::redirectReceived
#5 WebCore::iterateClients
#6 WebCore::CachedRawResource::redirectReceived
#7 WebCore::SubresourceLoader::willSendRequestInternal
#8 WebCore::ResourceLoader::willSendRequest
#9 WebKit::WebResourceLoader::willSendRequest

Debugging the network process, the redirection from 'https://' to 'itmss://' protocol actually passes here: 

#0 WebKit::NetworkResourceLoader::continueWillSendRedirectedRequest
#1 WebKit::NetworkResourceLoader::willSendRedirectedRequest
#2 WebKit::NetworkLoad::willPerformHTTPRedirection
#3 WebKit::NetworkDataTaskCocoa::willPerformHTTPRedirection
#4 ::-[WKNetworkSessionDelegate URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:](NSURLSession *, NSURLSessionTask *, NSHTTPURLResponse *, NSURLRequest *, void (^)(NSURLRequest *))

I could not find out from where willPerformHTTPRedirection is called and hence where the protocol changes actually happens though.

I noticed that opening directly the 'itmss://' URL on the simulator does result in the "Safari cannot open the page because the address is invalid" alert showing up, so that would explain what I see on the simulator. Not sure if that's the same issue on a device.
Comment 6 Frédéric Wang (:fredw) 2018-04-30 06:08:41 PDT
So now debugging the UIProcess after the DecidePolicyForNavigationAction is dispatched, I get the following trace:

# decisionHandlerWithPolicies(WKNavigationActionPolicy, _WKWebsitePolicies*)
# ...
# (Proprietary code in MobileSafari)
# ...
# WebKit::NavigationState::NavigationClient::decidePolicyForNavigationAction
# WebKit::WebPageProxy::decidePolicyForNavigationAction

For the 'https://' URL, the action policy passed to decisionHandlerWithPolicies is "_WKNavigationActionPolicyAllowWithoutTryingAppLink" and for the 'itmss://' URL it is "WKNavigationActionPolicyCancel" so the app link won't be open in any case.

Even if the action policy was "WKNavigationActionPolicyAllow", tryAppLink() would still exit early because navigationAction->shouldOpenAppLinks() is false. More precisely, WebPageProxy::decidePolicyForNavigationAction sets navigationAction.m_shouldOpenAppLink true but has navigationActionData.shouldOpenExternalURLsPolicy == ShouldNotAllow.

I'm not even sure what an "App Link" is but attachment 337779 [details] also fails if we directly use itmss://itunes.apple.com/us/app/id828256236 or itms-apps://itunes.apple.com/us/app/id828256236 to avoid the redirect. On the simulator, I also get the "Safari cannot open the page because the address is invalid" message if I directly open these two links.
Comment 7 Tim Horton 2018-04-30 09:13:25 PDT
Those are URLs with custom schemes, not app links (universal links: https://developer.apple.com/library/content/documentation/General/Conceptual/AppSearch/UniversalLinks.html).