Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Fix #4358: Add support for downloading mobileconfig #8245

Merged
merged 1 commit into from
Oct 18, 2023
Merged
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Growth
import SafariServices
import LocalAuthentication
import BraveShared
import UniformTypeIdentifiers

extension WKNavigationAction {
/// Allow local requests only if the request is privileged.
Expand Down Expand Up @@ -49,6 +50,11 @@ extension WKNavigationType: CustomDebugStringConvertible {
}
}

extension UTType {
static let textCalendar = UTType(mimeType: "text/calendar")! // Not the same as `calendarEvent`
static let mobileConfiguration = UTType(mimeType: "application/x-apple-aspen-config")!
}

// MARK: WKNavigationDelegate
extension BrowserViewController: WKNavigationDelegate {
public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
Expand Down Expand Up @@ -195,18 +201,6 @@ extension BrowserViewController: WKNavigationDelegate {
return (.cancel, preferences)
}

// Handling calendar .ics files
if navigationAction.targetFrame?.isMainFrame == true, requestURL.pathExtension.lowercased() == "ics" {
// This is not ideal. It pushes a new view controller on top of the BVC
// and you have to dismiss it manually after you managed the calendar event.
// I do not see a workaround for it, Chrome iOS does the same thing.
let vc = SFSafariViewController(url: requestURL, configuration: .init())
vc.modalPresentationStyle = .formSheet
self.present(vc, animated: true)

return (.cancel, preferences)
}

// handles IPFS URL schemes
if requestURL.isIPFSScheme {
if navigationAction.targetFrame?.isMainFrame == true {
Expand Down Expand Up @@ -419,6 +413,24 @@ extension BrowserViewController: WKNavigationDelegate {

return (.cancel, preferences)
}

/// Handles a link by opening it in an SFSafariViewController and presenting it on the BVC.
///
/// This is unfortunately neccessary to handle certain downloads natively such as ics/calendar invites and
/// mobileconfiguration files.
///
/// The user unfortunately has to dismiss it manually after they have handled the file.
/// Chrome iOS does the same
private func handleLinkWithSafariViewController(_ url: URL, tab: Tab?) {
let vc = SFSafariViewController(url: url, configuration: .init())
vc.modalPresentationStyle = .formSheet
self.present(vc, animated: true)

// If the website opened this URL in a separate tab, remove the empty tab
if let tab = tab, tab.url == nil || tab.url?.absoluteString == "about:blank" {
tabManager.removeTab(tab)
}
}

@MainActor
public func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse) async -> WKNavigationResponsePolicy {
Expand Down Expand Up @@ -450,6 +462,19 @@ extension BrowserViewController: WKNavigationDelegate {
// download via the context menu.
let canShowInWebView = navigationResponse.canShowMIMEType && (webView != pendingDownloadWebView)
let forceDownload = webView == pendingDownloadWebView

let mimeTypesThatRequireSFSafariViewControllerHandling: [UTType] = [
.textCalendar,
.mobileConfiguration
]

// SFSafariViewController only supports http/https links
if navigationResponse.isForMainFrame, let url = responseURL, url.isWebPage(includeDataURIs: false),
let mimeType = response.mimeType.flatMap({ UTType(mimeType: $0) }),
mimeTypesThatRequireSFSafariViewControllerHandling.contains(mimeType) {
handleLinkWithSafariViewController(url, tab: tab)
return .cancel
}

// Check if this response should be handed off to Passbook.
if let passbookHelper = OpenPassBookHelper(request: request, response: response, canShowInWebView: canShowInWebView, forceDownload: forceDownload, browserViewController: self) {
Expand Down