2020-07-22 19:29:38 -07:00
|
|
|
//
|
|
|
|
|
// 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,
|
2020-07-29 19:24:05 -07:00
|
|
|
UITextFieldDelegate, ScriptPolicyViewControllerDelegate,
|
|
|
|
|
UIPopoverPresentationControllerDelegate
|
2020-07-22 19:29:38 -07:00
|
|
|
{
|
|
|
|
|
let bridge = SBRProcessBundleBridge()
|
|
|
|
|
let browserView = BrowserView()
|
|
|
|
|
|
2020-07-29 17:46:53 -07:00
|
|
|
var javaScriptEnabledForTab: Bool = false
|
|
|
|
|
|
2020-07-22 19:29:38 -07:00
|
|
|
private let policyManager = ResourcePolicyManager()
|
2020-07-24 19:26:35 -07:00
|
|
|
private let toolbarController = ToolbarViewController()
|
2020-07-29 17:46:53 -07:00
|
|
|
private var allowedScriptOrigins = Set<String>()
|
2020-07-22 19:29:38 -07:00
|
|
|
private var blockedScriptOrigins = Set<String>()
|
2020-07-24 19:26:35 -07:00
|
|
|
override var canBecomeFirstResponder: Bool { true }
|
2020-07-22 19:29:38 -07:00
|
|
|
|
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() {
|
2020-07-22 19:29:38 -07:00
|
|
|
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() {
|
2020-07-22 19:29:38 -07:00
|
|
|
bridge.delegate = self
|
|
|
|
|
bridge.policyDataSource = policyManager
|
|
|
|
|
|
2020-07-29 19:24:05 -07:00
|
|
|
addChild(toolbarController)
|
|
|
|
|
|
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
|
2020-07-29 17:46:53 -07:00
|
|
|
let hostOrigin = webView.url?.host ?? ""
|
|
|
|
|
let loadedScripts = self.allowedScriptOrigins.union(self.blockedScriptOrigins)
|
2020-07-29 18:58:31 -07:00
|
|
|
let scriptViewController = ScriptPolicyViewController(policyManager: self.policyManager,
|
|
|
|
|
hostOrigin: hostOrigin,
|
|
|
|
|
loadedScripts: loadedScripts,
|
|
|
|
|
scriptsAllowedForTab: self.javaScriptEnabledForTab)
|
2020-07-24 19:26:35 -07:00
|
|
|
scriptViewController.delegate = self
|
|
|
|
|
|
|
|
|
|
let navController = UINavigationController(rootViewController: scriptViewController)
|
2020-07-29 18:34:46 -07:00
|
|
|
navController.modalPresentationStyle = .popover
|
|
|
|
|
navController.popoverPresentationController?.sourceView = self.toolbarController.scriptControllerIconView
|
2020-07-29 19:24:05 -07:00
|
|
|
navController.popoverPresentationController?.delegate = self
|
2020-07-29 18:34:46 -07:00
|
|
|
|
2020-07-24 19:26:35 -07:00
|
|
|
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-22 19:29:38 -07:00
|
|
|
|
2020-07-28 11:31:30 -07:00
|
|
|
// Load progress
|
|
|
|
|
loadingObservation = webView.observe(\.estimatedProgress) { (webView, observedChange) in
|
2020-07-29 17:46:53 -07:00
|
|
|
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
|
|
|
|
|
})
|
|
|
|
|
|
2020-07-22 19:29:38 -07:00
|
|
|
self.view = browserView
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-24 19:26:35 -07:00
|
|
|
override func viewDidLoad() {
|
2020-07-29 18:58:31 -07:00
|
|
|
beginLoadingURL(URL(string: "https://news.ycombinator.com")!)
|
2020-07-22 19:29:38 -07:00
|
|
|
}
|
|
|
|
|
|
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)
|
2020-07-22 19:29:38 -07:00
|
|
|
}
|
|
|
|
|
|
2020-07-29 19:24:05 -07:00
|
|
|
// MARK: UIPopoverPresentationControllerDelegate
|
|
|
|
|
|
|
|
|
|
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
|
|
|
|
|
// Forces popovers to present on iPhone
|
|
|
|
|
return .none
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 19:29:38 -07:00
|
|
|
// MARK: SBRProcessBundleBridgeDelegate
|
|
|
|
|
|
2020-07-29 17:46:53 -07:00
|
|
|
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) {
|
2020-07-22 19:29:38 -07:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-29 17:46:53 -07:00
|
|
|
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()
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-29 17:46:53 -07:00
|
|
|
func setScriptsEnabledForTab(_ enabled: Bool) {
|
|
|
|
|
javaScriptEnabledForTab = enabled
|
|
|
|
|
bridge.allowAllScripts = enabled
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 19:29:38 -07:00
|
|
|
}
|