Bug 158801 - WKWebView should expose API for download of a file.
Summary: WKWebView should expose API for download of a file.
Status: NEW
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebKit2 (show other bugs)
Version: Safari 9
Hardware: iPhone / iPad All
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2016-06-15 11:38 PDT by michaeldo
Modified: 2019-11-27 07:48 PST (History)
8 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description michaeldo 2016-06-15 11:38:45 PDT
Summary:
Files can't be downloaded from WKWebView if the file is behind a login wall, as the cookies in WKWebView are unavailable.

Steps to Reproduce:
1. Create a WKWebView application.
2. Attempt to download a file which is protected by authentication into the application Documents directory.

Expected Results:
File can be downloaded.

NOTE: _WKNavigationActionPolicyDownload is defined privately in WebKit2, but it is not exposed in the public WKNavigationDelegate.

Actual Results:
Authentication to file fails.

Version:
iOS 9.3.2 (13F69)

radar://26818508
Comment 1 Brady Eidson 2016-06-15 17:45:04 PDT
"Attempt to download a file which is protected by authentication into the application Documents directory."

Could you give us more details on this?

How are you attempting to load the file?
How is it protected by auth?
Comment 2 michaeldo 2016-06-17 10:31:18 PDT
I will create a sample project with this scenario.
Comment 3 michaeldo 2016-06-17 14:59:34 PDT
You can reproduce this by logging into a cloud service (ex: GoogleDrive) in a WKWebView with the websiteDataStore configuration set to +nonPersistentDataStore.

Or attempt to download a file which is directly linked to from a webpage instead of displaying it inside of the WKWebView. This type of webpage may be protected by HTTP Basic Authentication for example. (I can provide such a sample site if desired.)

In order to download the file into the application, intercept the request in webView:decidePolicyForNavigationAction:decisionHandler:. I am attempting the file download using an NSURLSessionDownloadTask. Attempting to use this request to download the file outside of WKWebView fails.
Comment 4 Brady Eidson 2016-06-17 15:22:21 PDT
(In reply to comment #3)
> You can reproduce this by logging into a cloud service (ex: GoogleDrive) in
> a WKWebView with the websiteDataStore configuration set to
> +nonPersistentDataStore.
> 
> Or attempt to download a file which is directly linked to from a webpage
> instead of displaying it inside of the WKWebView. This type of webpage may
> be protected by HTTP Basic Authentication for example. (I can provide such a
> sample site if desired.)
> 
> In order to download the file into the application, intercept the request in
> webView:decidePolicyForNavigationAction:decisionHandler:. I am attempting
> the file download using an NSURLSessionDownloadTask. Attempting to use this
> request to download the file outside of WKWebView fails.

So, to clarify:

- The user is browsing around in a WKWebView app.
- The user clicks on a link that the app would like to download instead of display in the WKWebView.
- The app denies the load in the policy handler, and then attempts to load the URL using an NSURLSessionDownloadTask inside the app process.
- That fails when the URL is protected by some sort of auth (cookie, http auth, ntlm, etc)

If my recounting of the above scenario is correct, then the reason it fails is because the browsing session (including cookies, credentials, etc) is held by the networking process, but since the app is trying the load inside the app process, the download doesn't go through.

Is my understanding correct?
Comment 5 michaeldo 2016-06-17 15:46:21 PDT
Yes, your understanding of the issue is correct. Thanks for verifying!
Comment 6 Eugene But 2017-08-04 11:00:11 PDT
iOS 11 now provides API for retrieving cookies. However it is still useful to have public API for WKWebView downloads, because some URLs (like bank statements) can be visited only one, so the only way to download those URLs would be using _WKNavigationActionPolicyDownload in decidePolicyForNavigationResponse.
Comment 7 Geoffrey Garen 2017-08-25 13:43:47 PDT
> some URLs (like bank statements) can be visited only one, so the only way to download
> those URLs would be using _WKNavigationActionPolicyDownload

Brady's suggestion was to use WKNavigationActionPolicyCancel and then perform the download in the client app.

If the WebKit navigation is canceled, there's only one visit to the target URL -- the visit in the client app.

Can you clarify why a load-once download URL would fail?
Comment 8 Eugene But 2017-08-25 13:58:35 PDT
Sorry for the confusion. I was thinking about _WKNavigationResponsePolicyBecomeDownload not about _WKNavigationActionPolicyDownload, my bad.


Here is the scenario where host app will have to visit the URL again:

1.) webView:decidePolicyForNavigationResponse:decisionHandler: gets called
2.) navigationResponse.canShowMIMEType returns NO
3.) navigationResponse.response.MIMEType is application/vnd.apple.pkpass

In this case a browser wants to start downloading PassKit file so it can be added into Wallet app. And the only way to do that is to fetch the URL again.
Comment 9 Eugene But 2018-09-27 15:43:30 PDT
Another useful usecase for Download API are blob:// URLs. Consider the following JavaScript:

var b = URL.createObjectURL(new File(["content"],
    "filename.txt", {
        type: "application/octet-stream",
  	lastModified: new Date()
     }));
document.getElementById("link").href = b;
document.getElementById("link").innerHTML = b;


This script will create the link, and tapping on the link will call decidePolicyForNavigationResponse:.
The URL for passed response will be a blob URL (something like blob:https://<hostname>/<id-of-blob-url>).
And there is no way for the browser to download that URL, because only WKWebView knows which data backs this URL up.
Comment 10 Brady Eidson 2019-11-06 15:50:43 PST
(In reply to michaeldo from comment #0) 
> radar://26818508

Better link: <rdar://problem/26818508>