-
Notifications
You must be signed in to change notification settings - Fork 3k
/
Copy pathBrowserPrompts.swift
135 lines (116 loc) · 5.21 KB
/
BrowserPrompts.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/* 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
@objc protocol JSPromptAlertControllerDelegate: class {
func promptAlertControllerDidDismiss(_ alertController: JSPromptAlertController)
}
/// A simple version of UIAlertController that attaches a delegate to the viewDidDisappear method
/// to allow forwarding the event. The reason this is needed for prompts from Javascript is we
/// need to invoke the completionHandler passed to us from the WKWebView delegate or else
/// a runtime exception is thrown.
class JSPromptAlertController: UIAlertController {
var alertInfo: JSAlertInfo?
weak var delegate: JSPromptAlertControllerDelegate?
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
delegate?.promptAlertControllerDidDismiss(self)
}
}
/**
* An JSAlertInfo is used to store information about an alert we want to show either immediately or later.
* Since alerts are generated by web pages and have no upper limit it would be unwise to allocate a
* UIAlertController instance for each generated prompt which could potentially be queued in the background.
* Instead, the JSAlertInfo structure retains the relevant data needed for the prompt along with a copy
* of the provided completionHandler to let us generate the UIAlertController when needed.
*/
protocol JSAlertInfo {
func alertController() -> JSPromptAlertController
func cancel()
}
struct MessageAlert: JSAlertInfo {
let message: String
let frame: WKFrameInfo
let completionHandler: () -> Void
func alertController() -> JSPromptAlertController {
let alertController = JSPromptAlertController(title: titleForJavaScriptPanelInitiatedByFrame(frame),
message: message,
preferredStyle: UIAlertControllerStyle.alert)
alertController.addAction(UIAlertAction(title: UIConstants.OKString, style: UIAlertActionStyle.default) { _ in
self.completionHandler()
})
alertController.alertInfo = self
return alertController
}
func cancel() {
completionHandler()
}
}
struct ConfirmPanelAlert: JSAlertInfo {
let message: String
let frame: WKFrameInfo
let completionHandler: (Bool) -> Void
init(message: String, frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
self.message = message
self.frame = frame
self.completionHandler = completionHandler
}
func alertController() -> JSPromptAlertController {
// Show JavaScript confirm dialogs.
let alertController = JSPromptAlertController(title: titleForJavaScriptPanelInitiatedByFrame(frame), message: message, preferredStyle: UIAlertControllerStyle.alert)
alertController.addAction(UIAlertAction(title: UIConstants.OKString, style: UIAlertActionStyle.default) { _ in
self.completionHandler(true)
})
alertController.addAction(UIAlertAction(title: UIConstants.CancelString, style: UIAlertActionStyle.cancel) { _ in
self.cancel()
})
alertController.alertInfo = self
return alertController
}
func cancel() {
completionHandler(false)
}
}
struct TextInputAlert: JSAlertInfo {
let message: String
let frame: WKFrameInfo
let completionHandler: (String?) -> Void
let defaultText: String?
var input: UITextField!
init(message: String, frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void, defaultText: String?) {
self.message = message
self.frame = frame
self.completionHandler = completionHandler
self.defaultText = defaultText
}
func alertController() -> JSPromptAlertController {
let alertController = JSPromptAlertController(title: titleForJavaScriptPanelInitiatedByFrame(frame), message: message, preferredStyle: UIAlertControllerStyle.alert)
var input: UITextField!
alertController.addTextField(configurationHandler: { (textField: UITextField) in
input = textField
input.text = self.defaultText
})
alertController.addAction(UIAlertAction(title: UIConstants.OKString, style: UIAlertActionStyle.default) { _ in
self.completionHandler(input.text)
})
alertController.addAction(UIAlertAction(title: UIConstants.CancelString, style: UIAlertActionStyle.cancel) { _ in
self.cancel()
})
alertController.alertInfo = self
return alertController
}
func cancel() {
completionHandler(nil)
}
}
/// Show a title for a JavaScript Panel (alert) based on the WKFrameInfo. On iOS9 we will use the new securityOrigin
/// and on iOS 8 we will fall back to the request URL. If the request URL is nil, which happens for JavaScript pages,
/// we fall back to "JavaScript" as a title.
private func titleForJavaScriptPanelInitiatedByFrame(_ frame: WKFrameInfo) -> String {
var title = "\(frame.securityOrigin.`protocol`)://\(frame.securityOrigin.host)"
if frame.securityOrigin.port != 0 {
title += ":\(frame.securityOrigin.port)"
}
return title
}