diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController.swift b/Sources/Brave/Frontend/Browser/BrowserViewController.swift index 7a65c1c8daf..c85d17f1368 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController.swift @@ -192,6 +192,8 @@ public class BrowserViewController: UIViewController { var downloadToast: DownloadToast? // A toast that is showing the combined download progress var addToPlayListActivityItem: (enabled: Bool, item: PlaylistInfo?)? // A boolean to determine If AddToListActivity should be added var openInPlaylistActivityItem: (enabled: Bool, item: PlaylistInfo?)? // A boolean to determine if OpenInPlaylistActivity should be shown + var shouldDownloadNavigationResponse: Bool = false + var pendingDownloads = [WKDownload: PendingDownload]() var navigationToolbar: ToolbarProtocol { return toolbar ?? topToolbar diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKDownloadDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKDownloadDelegate.swift new file mode 100644 index 00000000000..4f70128fb8d --- /dev/null +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKDownloadDelegate.swift @@ -0,0 +1,71 @@ +// Copyright 2021 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import Foundation +import WebKit +import PassKit +import Shared + +extension BrowserViewController: WKDownloadDelegate { + + struct PendingDownload: Hashable, Equatable { + let fileURL: URL + let response: URLResponse + let suggestedFileName: String + } + + public func download(_ download: WKDownload, decideDestinationUsing response: URLResponse, suggestedFilename: String) async -> URL? { + let temporaryDir = NSTemporaryDirectory() + let fileName = temporaryDir + "/" + suggestedFilename + let url = URL(fileURLWithPath: fileName) + pendingDownloads[download] = PendingDownload(fileURL: url, response: response, suggestedFileName: suggestedFilename) + return url + } + + public func download(_ download: WKDownload, decidedPolicyForHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest) async -> WKDownload.RedirectPolicy { + return .allow + } + + public func download(_ download: WKDownload, respondTo challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) { + return (.performDefaultHandling, nil) + } + + @MainActor + public func downloadDidFinish(_ download: WKDownload) { + guard let downloadInfo = pendingDownloads[download] else { + return + } + + pendingDownloads.removeValue(forKey: download) + + let response = URLResponse(url: downloadInfo.fileURL, + mimeType: downloadInfo.response.mimeType, + expectedContentLength: Int(downloadInfo.response.expectedContentLength), + textEncodingName: downloadInfo.response.textEncodingName) + + if let passbookHelper = OpenPassBookHelper(request: nil, response: response, canShowInWebView: false, forceDownload: false, browserViewController: self) { + passbookHelper.open() + } + + Task { + try FileManager.default.removeItem(at: downloadInfo.fileURL) + } + } + + @MainActor + public func download(_ download: WKDownload, didFailWithError error: Error, resumeData: Data?) { + // display an error + let alertController = UIAlertController( + title: Strings.unableToAddPassErrorTitle, + message: Strings.unableToAddPassErrorMessage, + preferredStyle: .alert) + alertController.addAction( + UIAlertAction(title: Strings.unableToAddPassErrorDismiss, style: .cancel) { (action) in + // Do nothing. + } + ) + present(alertController, animated: true, completion: nil) + } +} diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift index 11968302b83..db9684cf54b 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift @@ -394,6 +394,10 @@ extension BrowserViewController: WKNavigationDelegate { self.tabManager.selectedTab?.blockAllAlerts = false } + if navigationAction.shouldPerformDownload { + self.shouldDownloadNavigationResponse = true + } + return (.allow, preferences) } @@ -493,6 +497,14 @@ extension BrowserViewController: WKNavigationDelegate { } // Check if this response should be handed off to Passbook. + if shouldDownloadNavigationResponse { + shouldDownloadNavigationResponse = false + + if response.mimeType == MIMEType.passbook { + return .download + } + } + if let passbookHelper = OpenPassBookHelper(request: request, response: response, canShowInWebView: canShowInWebView, forceDownload: forceDownload, browserViewController: self) { // Open our helper and cancel this response from the webview. passbookHelper.open() @@ -529,11 +541,23 @@ extension BrowserViewController: WKNavigationDelegate { tab.mimeType = response.mimeType } + + if canShowInWebView { + return .allow + } // If none of our helpers are responsible for handling this response, // just let the webview handle it as normal. return .allow } + + public func webView(_ webView: WKWebView, navigationAction: WKNavigationAction, didBecome download: WKDownload) { + Logger.module.error("ERROR: Should Never download NavigationAction since we never return .download from decidePolicyForAction.") + } + + public func webView(_ webView: WKWebView, navigationResponse: WKNavigationResponse, didBecome download: WKDownload) { + download.delegate = self + } nonisolated public func webView(_ webView: WKWebView, respondTo challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) { diff --git a/Sources/Brave/Frontend/Browser/TabManagerNavDelegate.swift b/Sources/Brave/Frontend/Browser/TabManagerNavDelegate.swift index 4f86026253b..9abb964c380 100644 --- a/Sources/Brave/Frontend/Browser/TabManagerNavDelegate.swift +++ b/Sources/Brave/Frontend/Browser/TabManagerNavDelegate.swift @@ -105,6 +105,10 @@ class TabManagerNavDelegate: NSObject, WKNavigationDelegate { res = policy } + if policy == .download { + res = policy + } + pref = preferences } } @@ -127,6 +131,10 @@ class TabManagerNavDelegate: NSObject, WKNavigationDelegate { if policy == .cancel { res = policy } + + if policy == .download { + res = policy + } } } @@ -137,4 +145,24 @@ class TabManagerNavDelegate: NSObject, WKNavigationDelegate { return res } + + @MainActor + public func webView(_ webView: WKWebView, navigationAction: WKNavigationAction, didBecome download: WKDownload) { + for delegate in delegates { + delegate.webView?(webView, navigationAction: navigationAction, didBecome: download) + if download.delegate != nil { + return + } + } + } + + @MainActor + public func webView(_ webView: WKWebView, navigationResponse: WKNavigationResponse, didBecome download: WKDownload) { + for delegate in delegates { + delegate.webView?(webView, navigationResponse: navigationResponse, didBecome: download) + if download.delegate != nil { + return + } + } + } }