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

Fixes #3678 - RequestHandler class refactoring #3680

Closed
Closed
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
119 changes: 71 additions & 48 deletions Blockzilla/Lib/RequestHandler/RequestHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,79 +8,102 @@ import Telemetry
private let internalSchemes: Set<String> = ["http", "https", "ftp", "file", "about", "javascript", "data"]

class RequestHandler {
func handle(request: URLRequest, alertCallback: (UIAlertController) -> Void) -> Bool {
guard let url = request.url,
let scheme = request.url?.scheme?.lowercased() else {
return false
}
private var alertCallback: (UIAlertController) -> Void = { _ in }
private var title: String = ""
private var url: URL!

// If the URL isn't a scheme the browser can open, let the system handle it if
// it's a scheme we want to support.
guard internalSchemes.contains(scheme) else {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
return false
}
func handle(request: URLRequest, alertCallback: @escaping (UIAlertController) -> Void) -> Bool {
self.alertCallback = alertCallback

let title = components.path
if !isValidURLAndScheme(url: request.url, scheme: request.url?.scheme) { return false }

switch scheme {
case "tel":
// Don't present our dialog as the system presents its own
UIApplication.shared.open(url, options: [:])
case "facetime", "facetime-audio":
let alert = RequestHandler.makeAlert(title: title, action: "FaceTime", forURL: url)
alertCallback(alert)
case "mailto":
let alert = RequestHandler.makeAlert(title: title, action: UIConstants.strings.externalLinkEmail, forURL: url)
alertCallback(alert)
default:
let openAction = UIAlertAction(title: UIConstants.strings.open, style: .default) { _ in
Telemetry.default.recordEvent(category: TelemetryEventCategory.action, method: TelemetryEventMethod.open, object: TelemetryEventObject.requestHandler, value: "external link")
UIApplication.shared.open(url, options: [:])
}
url = request.url!
let scheme = request.url!.scheme!

let cancelAction = UIAlertAction(title: UIConstants.strings.externalLinkCancel, style: .cancel) { _ in
Telemetry.default.recordEvent(category: TelemetryEventCategory.action, method: TelemetryEventMethod.cancel, object: TelemetryEventObject.requestHandler, value: "external link")
}

let alert = UIAlertController(title: String(format: UIConstants.strings.externalAppLink, AppInfo.productName),
message: nil,
preferredStyle: .alert)

alert.addAction(cancelAction)
alert.addAction(openAction)
alert.preferredAction = openAction
alertCallback(alert)
}

return false
guard internalSchemes.contains(scheme) else {
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
if !isValidURLComponents(components) { return false }
title = components!.path
return handleURLScheme(scheme)
}

guard scheme == "http" || scheme == "https",
let host = url.host?.lowercased() else {
return true
}
return handleURLHost(host)
}

private func isValidURLAndScheme(url: URL?, scheme: String?) -> Bool {
guard url != nil, scheme != nil else { return false }
return true
}

private func isValidURLComponents(_ components: URLComponents?) -> Bool {
guard components != nil else { return false }
return true
}

private func handleURLScheme(_ scheme: String) -> Bool {
switch scheme {
case "tel":
// Don't present our dialog as the system presents its own
UIApplication.shared.open(url, options: [:])
case "facetime", "facetime-audio":
presentAlert(title: title, action: "FaceTime", for: url)
case "mailto":
presentAlert(title: title, action: UIConstants.strings.externalLinkEmail, for: url)
default:
presentAlert(title: String(format: UIConstants.strings.externalAppLink, AppInfo.productName),
action: UIConstants.strings.open,
for: url,
telemetryEvent: true)
}
return false
}

private func handleURLHost(_ host: String) -> Bool {
switch host {
case "maps.apple.com":
let alert = RequestHandler.makeAlert(title: String(format: UIConstants.strings.externalAppLinkWithAppName, AppInfo.productName, "Maps"), action: UIConstants.strings.open, forURL: url)
alertCallback(alert)
presentAlert(title: String(format: UIConstants.strings.externalAppLinkWithAppName, AppInfo.productName, "Maps"),
action: UIConstants.strings.open,
for: url)
return false
case "itunes.apple.com":
let alert = RequestHandler.makeAlert(title: String(format: UIConstants.strings.externalAppLinkWithAppName, AppInfo.productName, "App Store"), action: UIConstants.strings.open, forURL: url)
alertCallback(alert)
presentAlert(title: String(format: UIConstants.strings.externalAppLinkWithAppName, AppInfo.productName, "App Store"),
action: UIConstants.strings.open,
for: url)
return false
default:
return true
}
}

static private func makeAlert(title: String, action: String, forURL url: URL) -> UIAlertController {
private func presentAlert(title: String, action: String, for url: URL, telemetryEvent: Bool = false) {
let alert = makeAlert(title: title, action: action, forURL: url, telemetryEvent: telemetryEvent)
alertCallback(alert)
}

private func makeAlert(title: String, action: String, forURL url: URL, telemetryEvent: Bool = false) -> UIAlertController {
let openAction = UIAlertAction(title: action, style: .default) { _ in
if telemetryEvent {
Telemetry.default.recordEvent(category: TelemetryEventCategory.action,
method: TelemetryEventMethod.open,
object: TelemetryEventObject.requestHandler,
value: "external link")
}
UIApplication.shared.open(url, options: [:])
}
let cancelAction = UIAlertAction(title: UIConstants.strings.externalLinkCancel, style: .cancel) { _ in
if telemetryEvent {
Telemetry.default.recordEvent(category: TelemetryEventCategory.action,
method: TelemetryEventMethod.cancel,
object: TelemetryEventObject.requestHandler,
value: "external link")
}
}
let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: UIConstants.strings.externalLinkCancel, style: .cancel, handler: nil))
alert.addAction(cancelAction)
alert.addAction(openAction)
alert.preferredAction = openAction
return alert
Expand Down