Files
Attractor/SBrowser/Browser View/BrowserViewController.swift

182 lines
6.5 KiB
Swift
Raw Normal View History

//
// BrowserViewController.swift
// SBrowser
//
// Created by James Magahern on 7/21/20.
//
import UIKit
2020-07-24 19:26:35 -07:00
class BrowserViewController: UIViewController,
SBRProcessBundleBridgeDelegate, WKNavigationDelegate,
UITextFieldDelegate, ScriptPolicyViewControllerDelegate
{
let bridge = SBRProcessBundleBridge()
let browserView = BrowserView()
var javaScriptEnabledForTab: Bool = false
private let policyManager = ResourcePolicyManager()
2020-07-24 19:26:35 -07:00
private let toolbarController = ToolbarViewController()
private var allowedScriptOrigins = Set<String>()
private var blockedScriptOrigins = Set<String>()
2020-07-24 19:26:35 -07:00
override var canBecomeFirstResponder: Bool { true }
2020-07-29 14:16:25 -07:00
private var titleObservation: NSKeyValueObservation?
2020-07-28 11:31:30 -07:00
private var loadingObservation: NSKeyValueObservation?
2020-07-29 14:16:25 -07:00
override var preferredStatusBarStyle: UIStatusBarStyle { .lightContent }
2020-07-24 19:26:35 -07:00
init() {
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
2020-07-24 19:26:35 -07:00
override func loadView() {
bridge.delegate = self
bridge.policyDataSource = policyManager
2020-07-22 19:42:38 -07:00
let webView = bridge.webView
webView.allowsBackForwardNavigationGestures = true
2020-07-24 19:26:35 -07:00
webView.navigationDelegate = self
2020-07-22 19:42:38 -07:00
browserView.webView = webView
2020-07-24 19:26:35 -07:00
browserView.toolbarView = toolbarController.toolbarView
// Refresh button
toolbarController.urlBar.refreshButton.addAction(UIAction(handler: { action in
2020-07-28 11:37:10 -07:00
if webView.isLoading {
webView.stopLoading()
} else {
webView.reload()
}
2020-07-24 19:26:35 -07:00
}), for: .touchUpInside)
// Script button
toolbarController.scriptControllerIconView.addAction(UIAction(handler: { action in
let hostOrigin = webView.url?.host ?? ""
let loadedScripts = self.allowedScriptOrigins.union(self.blockedScriptOrigins)
let scriptViewController = ScriptPolicyViewController(policyManager: self.policyManager, hostOrigin: hostOrigin, loadedScripts: loadedScripts)
2020-07-24 19:26:35 -07:00
scriptViewController.delegate = self
scriptViewController.allowScriptsForTab = self.javaScriptEnabledForTab
2020-07-24 19:26:35 -07:00
let navController = UINavigationController(rootViewController: scriptViewController)
self.present(navController, animated: true, completion: nil)
}), for: .touchUpInside)
2020-07-29 18:17:22 -07:00
// Dark mode button
toolbarController.darkModeButton.addAction(UIAction(handler: { _ in
self.bridge.darkModeEnabled = !self.bridge.darkModeEnabled
self.toolbarController.darkModeEnabled = self.bridge.darkModeEnabled
}), for: .touchUpInside)
2020-07-24 19:26:35 -07:00
// TextField delegate
toolbarController.urlBar.textField.delegate = self
2020-07-28 11:31:30 -07:00
// Load progress
loadingObservation = webView.observe(\.estimatedProgress) { (webView, observedChange) in
if webView.estimatedProgress == 1.0 {
self.toolbarController.urlBar.loadProgress = .complete
} else {
self.toolbarController.urlBar.loadProgress = .loading(progress: webView.estimatedProgress)
}
2020-07-28 11:31:30 -07:00
}
2020-07-29 14:16:25 -07:00
// Title observer
titleObservation = webView.observe(\.title, changeHandler: { (webView, observedChange) in
self.browserView.titlebarView.titleLabelView.text = webView.title
})
self.view = browserView
}
2020-07-24 19:26:35 -07:00
override func viewDidLoad() {
beginLoadingURL(URL(string: "https://google.com")!)
}
2020-07-24 19:26:35 -07:00
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
becomeFirstResponder()
}
private func updateScriptBlockerButton() {
toolbarController.scriptControllerIconView.setBlockedScriptsNumber(blockedScriptOrigins.count)
}
func beginLoadingURL(_ url: URL) {
let request = URLRequest(url: url)
bridge.webView.load(request)
}
// MARK: SBRProcessBundleBridgeDelegate
func webProcess(_ bridge: SBRProcessBundleBridge, didAllowScriptResourceFromOrigin origin: String) {
print("Allowed script resource from origin: \(origin)")
allowedScriptOrigins.formUnion([ origin ])
updateScriptBlockerButton()
}
2020-07-24 19:26:35 -07:00
func webProcess(_ bridge: SBRProcessBundleBridge, didBlockScriptResourceFromOrigin origin: String) {
print("Blocked script resource from origin: \(origin)")
blockedScriptOrigins.formUnion([ origin ])
updateScriptBlockerButton()
}
2020-07-24 19:26:35 -07:00
// MARK: Navigation Delegate
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
// Reset tracking this
blockedScriptOrigins.removeAll()
if let urlString = webView.url?.absoluteString {
toolbarController.urlBar.textField.text = urlString
}
}
2020-07-28 11:31:30 -07:00
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
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)
}
2020-07-24 19:26:35 -07:00
// MARK: UITextField Delegate
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let text = textField.text, let url = URL(string: text) {
if url.scheme == nil {
let urlString = "https://\(text)"
if let url = URL(string: urlString) {
beginLoadingURL(url)
}
}
}
textField.resignFirstResponder()
return false
}
// MARK: Script Policy View Controller Delegate
func didChangeScriptPolicy() {
bridge.policyDataSourceDidChange()
bridge.webView.reload()
}
func setScriptsEnabledForTab(_ enabled: Bool) {
javaScriptEnabledForTab = enabled
bridge.allowAllScripts = enabled
}
}