Script origins control inline policy, all scripts allowed for tab

This commit is contained in:
James Magahern
2020-07-29 17:46:53 -07:00
parent f330293606
commit 32cdcf71f7
14 changed files with 301 additions and 57 deletions

View File

@@ -37,31 +37,48 @@ class BrowserView: UIView
addSubview(titlebarView)
keyboardWillShowObserver = NotificationCenter.default.publisher(for: UIWindow.keyboardWillShowNotification).sink { notification in
if let keyboardFrame = notification.userInfo?[UIWindow.keyboardFrameEndUserInfoKey] as? CGRect {
self.keyboardLayoutOffset = self.bounds.height - keyboardFrame.minY
}
self.adjustOffsetForKeyboardNotification(userInfo: notification.userInfo!)
}
keyboardWillHideObserver = NotificationCenter.default.publisher(for: UIWindow.keyboardWillHideNotification).sink { notification in
if let keyboardFrame = notification.userInfo?[UIWindow.keyboardFrameEndUserInfoKey] as? CGRect {
self.keyboardLayoutOffset = self.bounds.height - keyboardFrame.minY
}
self.adjustOffsetForKeyboardNotification(userInfo: notification.userInfo!)
}
}
override func layoutSubviews()
{
private func adjustOffsetForKeyboardNotification(userInfo: [AnyHashable : Any]) {
guard let keyboardEndFrame = userInfo[UIWindow.keyboardFrameEndUserInfoKey] as? CGRect else { return }
guard let animationDuration = userInfo[UIWindow.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return }
guard let animationCurve = userInfo[UIWindow.keyboardAnimationCurveUserInfoKey] as? Int else { return }
let animationOptions: UIView.AnimationOptions = { curve -> UIView.AnimationOptions in
switch UIView.AnimationCurve(rawValue: curve) {
case .easeIn: return .curveEaseIn
case .easeOut: return .curveEaseOut
case .easeInOut: return .curveEaseInOut
default: return .init()
}
}(animationCurve)
self.keyboardLayoutOffset = bounds.height - keyboardEndFrame.minY
UIView.animate(withDuration: animationDuration, delay: 0.0, options: animationOptions, animations: { self.layoutIfNeeded() }, completion: nil)
}
override func layoutSubviews() {
super.layoutSubviews()
webView?.frame = bounds
if let toolbarView = toolbarView {
var toolbarSize = toolbarView.sizeThatFits(bounds.size)
var bottomOffset: CGFloat = 0.0
if keyboardLayoutOffset == 0 {
toolbarSize.height += safeAreaInsets.bottom
} else if toolbarView.urlBar?.textField.isFirstResponder ?? false {
bottomOffset = keyboardLayoutOffset
}
toolbarView.bounds = CGRect(origin: .zero, size: toolbarSize)
toolbarView.center = CGPoint(x: bounds.center.x, y: bounds.maxY - (toolbarView.bounds.height / 2) - keyboardLayoutOffset)
toolbarView.center = CGPoint(x: bounds.center.x, y: bounds.maxY - (toolbarView.bounds.height / 2) - bottomOffset)
}
bringSubviewToFront(titlebarView)
@@ -77,7 +94,7 @@ class BrowserView: UIView
var webViewContentInset = UIEdgeInsets()
webViewContentInset.top = titlebarView.frame.height
webViewContentInset.bottom = toolbarView?.frame.height ?? 0
webView?.scrollView.contentInset = webViewContentInset
webView?.scrollView.contentInset = webViewContentInset.subtracting(safeAreaInsets)
webView?.scrollView.scrollIndicatorInsets = webViewContentInset
}
}

View File

@@ -14,8 +14,11 @@ class BrowserViewController: UIViewController,
let bridge = SBRProcessBundleBridge()
let browserView = BrowserView()
var javaScriptEnabledForTab: Bool = false
private let policyManager = ResourcePolicyManager()
private let toolbarController = ToolbarViewController()
private var allowedScriptOrigins = Set<String>()
private var blockedScriptOrigins = Set<String>()
override var canBecomeFirstResponder: Bool { true }
@@ -52,8 +55,11 @@ class BrowserViewController: UIViewController,
// Script button
toolbarController.scriptControllerIconView.addAction(UIAction(handler: { action in
let scriptViewController = ScriptPolicyViewController(policyManager: self.policyManager, blockedScripts: self.blockedScriptOrigins)
let hostOrigin = webView.url?.host ?? ""
let loadedScripts = self.allowedScriptOrigins.union(self.blockedScriptOrigins)
let scriptViewController = ScriptPolicyViewController(policyManager: self.policyManager, hostOrigin: hostOrigin, loadedScripts: loadedScripts)
scriptViewController.delegate = self
scriptViewController.allowScriptsForTab = self.javaScriptEnabledForTab
let navController = UINavigationController(rootViewController: scriptViewController)
self.present(navController, animated: true, completion: nil)
@@ -64,7 +70,11 @@ class BrowserViewController: UIViewController,
// Load progress
loadingObservation = webView.observe(\.estimatedProgress) { (webView, observedChange) in
self.toolbarController.urlBar.loadProgress = .loading(progress: webView.estimatedProgress)
if webView.estimatedProgress == 1.0 {
self.toolbarController.urlBar.loadProgress = .complete
} else {
self.toolbarController.urlBar.loadProgress = .loading(progress: webView.estimatedProgress)
}
}
// Title observer
@@ -76,7 +86,7 @@ class BrowserViewController: UIViewController,
}
override func viewDidLoad() {
beginLoadingURL(URL(string: "https://reddit.com")!)
beginLoadingURL(URL(string: "https://google.com")!)
}
override func viewWillAppear(_ animated: Bool) {
@@ -95,9 +105,14 @@ class BrowserViewController: UIViewController,
// MARK: SBRProcessBundleBridgeDelegate
func webProcess(_ bridge: SBRProcessBundleBridge, didAllowScriptResourceFromOrigin origin: String) {
print("Allowed script resource from origin: \(origin)")
allowedScriptOrigins.formUnion([ origin ])
updateScriptBlockerButton()
}
func webProcess(_ bridge: SBRProcessBundleBridge, didBlockScriptResourceFromOrigin origin: String) {
print("Blocked script resource from origin: \(origin)")
blockedScriptOrigins.formUnion([ origin ])
updateScriptBlockerButton()
}
@@ -117,6 +132,18 @@ class BrowserViewController: UIViewController,
toolbarController.urlBar.loadProgress = .complete
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void)
{
var allowJavaScript = javaScriptEnabledForTab
if !allowJavaScript, let host = navigationAction.request.url?.host {
// Check origin policy
allowJavaScript = policyManager.allowedOriginsForScriptResources().contains(host)
}
preferences.allowsContentJavaScript = allowJavaScript
decisionHandler(.allow, preferences)
}
// MARK: UITextField Delegate
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
@@ -140,4 +167,9 @@ class BrowserViewController: UIViewController,
bridge.webView.reload()
}
func setScriptsEnabledForTab(_ enabled: Bool) {
javaScriptEnabledForTab = enabled
bridge.allowAllScripts = enabled
}
}

View File

@@ -9,7 +9,7 @@ import UIKit
class ToolbarButtonView: UIView
{
private var buttonPadding = CGFloat(8.0)
private var buttonPadding = CGFloat(24.0)
private var buttonViews: [UIView] = []
func addButtonView(_ button: UIView) {
@@ -28,7 +28,7 @@ class ToolbarButtonView: UIView
override func layoutSubviews() {
var buttonRect = CGRect(origin: .zero, size: CGSize(width: 0, height: bounds.height))
buttonRect.origin.x = buttonPadding
buttonRect.origin.x = layoutMargins.left
for button in buttonViews {
let buttonSize = button.sizeThatFits(bounds.size)
@@ -44,9 +44,12 @@ class ToolbarView: UIView
{
var urlBar: URLBar? { didSet { containerView.addSubview(urlBar!) } }
var cancelButtonVisible: Bool = false { didSet { layoutSubviews() } }
let containerView = UIView(frame: .zero)
let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemThickMaterial))
let buttonsView = ToolbarButtonView(frame: .zero)
let cancelButton = UIButton(type: .system)
convenience init()
{
@@ -55,6 +58,9 @@ class ToolbarView: UIView
addSubview(containerView)
containerView.addSubview(buttonsView)
cancelButton.setTitle("Cancel", for: .normal)
containerView.addSubview(cancelButton)
}
override func sizeThatFits(_ size: CGSize) -> CGSize
@@ -73,12 +79,32 @@ class ToolbarView: UIView
containerView.frame = containerBounds
containerView.frame = containerView.frame.insetBy(dx: 8.0, dy: 4.0)
// Cancel button
let urlBarPadding: CGFloat = 8.0
let cancelButtonSize = cancelButton.sizeThatFits(containerView.bounds.size)
cancelButton.frame = CGRect(origin: CGPoint(x: (urlBar?.frame.maxX ?? 0) + urlBarPadding, y: 0),
size: CGSize(width: cancelButtonSize.width, height: containerView.bounds.height))
// Toolbar buttons
let toolbarSize = buttonsView.sizeThatFits(containerView.bounds.size)
if let urlBar = urlBar {
urlBar.frame = CGRect(origin: .zero, size: CGSize(width: containerView.bounds.width - toolbarSize.width, height: toolbarSize.height))
buttonsView.frame = CGRect(origin: CGPoint(x: (urlBar?.frame.maxX ?? 0) + urlBarPadding, y: 0), size: toolbarSize)
var avoidingSize: CGSize = .zero
if cancelButtonVisible {
cancelButton.alpha = 1.0
buttonsView.alpha = 0.0
avoidingSize = cancelButtonSize
} else {
cancelButton.alpha = 0.0
buttonsView.alpha = 1.0
avoidingSize = toolbarSize
}
buttonsView.frame = CGRect(origin: CGPoint(x: urlBar?.frame.maxX ?? 0 + 8.0, y: 0), size: toolbarSize)
if let urlBar = urlBar {
urlBar.frame = CGRect(origin: .zero, size: CGSize(width: containerView.bounds.width - avoidingSize.width - urlBarPadding, height: containerView.bounds.height))
}
}
}
@@ -88,15 +114,30 @@ class ToolbarViewController: UIViewController
let toolbarView = ToolbarView()
let scriptControllerIconView = ScriptControllerIconView()
let shareButton = UIButton(frame: .zero)
let darkModeButton = UIButton(frame: .zero)
init() {
super.init(nibName: nil, bundle: nil)
toolbarView.urlBar = urlBar
darkModeButton.setImage(UIImage(systemName: "moon.circle"), for: .normal)
toolbarView.buttonsView.addButtonView(darkModeButton)
shareButton.setImage(UIImage(systemName: "square.and.arrow.up"), for: .normal)
toolbarView.buttonsView.addButtonView(shareButton)
toolbarView.buttonsView.addButtonView(scriptControllerIconView)
let toolbarAnimationDuration: TimeInterval = 0.3
urlBar.textField.addAction(.init(handler: { _ in
UIView.animate(withDuration: toolbarAnimationDuration) {
self.toolbarView.cancelButtonVisible = self.urlBar.textField.isFirstResponder
}
}), for: [ .editingDidBegin, .editingDidEnd ])
toolbarView.cancelButton.addAction(.init(handler: { action in
self.urlBar.textField.resignFirstResponder()
}), for: .touchUpInside)
}
override func loadView() {

View File

@@ -48,8 +48,13 @@ class URLBar: UIView
textField.autocapitalizationType = .none
textField.font = .systemFont(ofSize: 14.0)
textField.clearingBehavior = .clearOnInsertionAndShowSelectionTint
textField.clearButtonMode = .whileEditing
addSubview(textField)
textField.addAction(.init(handler: { _ in
self.refreshButton.isHidden = self.textField.isFirstResponder
}), for: [ .editingDidBegin, .editingDidEnd ])
refreshButton.tintColor = .secondaryLabel
refreshButton.setImage(refreshImage, for: .normal)
addSubview(refreshButton)