2020-07-22 19:29:38 -07:00
|
|
|
//
|
|
|
|
|
// BrowserViewController.swift
|
|
|
|
|
// SBrowser
|
|
|
|
|
//
|
|
|
|
|
// Created by James Magahern on 7/21/20.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
import UIKit
|
2020-07-31 14:08:10 -07:00
|
|
|
import UniformTypeIdentifiers
|
2020-07-22 19:29:38 -07:00
|
|
|
|
2020-09-21 15:39:41 -07:00
|
|
|
class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegate,
|
2020-07-29 19:24:05 -07:00
|
|
|
UITextFieldDelegate, ScriptPolicyViewControllerDelegate,
|
2020-09-21 17:56:22 -07:00
|
|
|
UIPopoverPresentationControllerDelegate, TabDelegate, TabPickerViewControllerDelegate,
|
2020-09-21 18:35:39 -07:00
|
|
|
AutocompleteViewControllerDelegate, ShortcutResponder
|
2020-07-22 19:29:38 -07:00
|
|
|
{
|
|
|
|
|
let browserView = BrowserView()
|
2020-07-30 23:54:20 -07:00
|
|
|
var tab: Tab { didSet { didChangeTab(tab) } }
|
|
|
|
|
var webView: WKWebView { tab.webView }
|
2020-07-22 19:29:38 -07:00
|
|
|
|
2020-07-30 23:54:20 -07:00
|
|
|
private let tabController = TabController()
|
2020-07-24 19:26:35 -07:00
|
|
|
private let toolbarController = ToolbarViewController()
|
2020-07-30 23:54:20 -07:00
|
|
|
|
2020-09-21 17:56:22 -07:00
|
|
|
private let autocompleteViewController = AutocompleteViewController()
|
|
|
|
|
|
2020-07-30 23:54:20 -07:00
|
|
|
private var policyManager: ResourcePolicyManager { tabController.policyManager }
|
|
|
|
|
|
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-31 14:08:10 -07:00
|
|
|
private var backButtonObservation: NSKeyValueObservation?
|
|
|
|
|
private var forwardButtonObservation: NSKeyValueObservation?
|
2020-07-28 11:31:30 -07:00
|
|
|
|
2020-07-31 18:29:44 -07:00
|
|
|
private var loadError: Error?
|
|
|
|
|
|
2020-07-29 14:16:25 -07:00
|
|
|
override var preferredStatusBarStyle: UIStatusBarStyle { .lightContent }
|
|
|
|
|
|
2020-07-24 19:26:35 -07:00
|
|
|
init() {
|
2020-07-30 23:54:20 -07:00
|
|
|
self.tab = tabController.tabs.first!
|
2020-07-22 19:29:38 -07:00
|
|
|
super.init(nibName: nil, bundle: nil)
|
2020-07-30 23:54:20 -07:00
|
|
|
|
|
|
|
|
addChild(toolbarController)
|
|
|
|
|
didChangeTab(tab)
|
2020-07-22 19:29:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
|
|
|
|
|
2020-07-24 19:26:35 -07:00
|
|
|
override func loadView() {
|
|
|
|
|
browserView.toolbarView = toolbarController.toolbarView
|
|
|
|
|
|
|
|
|
|
// Refresh button
|
2020-07-31 14:39:18 -07:00
|
|
|
toolbarController.urlBar.refreshButton.addAction(UIAction(handler: { [unowned self] action in
|
|
|
|
|
if self.webView.isLoading {
|
|
|
|
|
self.webView.stopLoading()
|
2020-07-28 11:37:10 -07:00
|
|
|
} else {
|
2020-07-31 14:39:18 -07:00
|
|
|
self.webView.reload()
|
2020-07-31 14:08:10 -07:00
|
|
|
}
|
|
|
|
|
}), for: .touchUpInside)
|
|
|
|
|
|
|
|
|
|
// Back button
|
2020-07-31 14:39:18 -07:00
|
|
|
toolbarController.backButton.addAction(UIAction(handler: { [unowned self] _ in
|
|
|
|
|
self.webView.goBack()
|
2020-07-31 14:08:10 -07:00
|
|
|
}), for: .touchUpInside)
|
|
|
|
|
|
|
|
|
|
// Forward button
|
2020-07-31 14:39:18 -07:00
|
|
|
toolbarController.forwardButton.addAction(UIAction(handler: { [unowned self] _ in
|
|
|
|
|
self.webView.goForward()
|
2020-07-31 14:08:10 -07:00
|
|
|
}), for: .touchUpInside)
|
|
|
|
|
|
|
|
|
|
// Share button
|
2020-07-31 14:39:18 -07:00
|
|
|
toolbarController.shareButton.addAction(UIAction(handler: { [unowned self, toolbarController] _ in
|
|
|
|
|
if let url = self.webView.url {
|
2020-07-31 14:08:10 -07:00
|
|
|
let itemProvider = NSItemProvider(item: url as NSURL, typeIdentifier: UTType.url.identifier)
|
|
|
|
|
let config = UIActivityItemsConfiguration(itemProviders: [ itemProvider ])
|
|
|
|
|
config.metadataProvider = { metadataKey in
|
|
|
|
|
switch metadataKey {
|
2020-07-31 14:39:18 -07:00
|
|
|
case .title: return self.webView.title
|
|
|
|
|
case .messageBody: return self.webView.title
|
2020-07-31 14:08:10 -07:00
|
|
|
default: return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
config.previewProvider = { index, intent, suggestedSize in
|
|
|
|
|
NSItemProvider(item: self.tab.favicon, typeIdentifier: UTType.image.identifier)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let activityController = UIActivityViewController(activityItemsConfiguration: config)
|
|
|
|
|
activityController.popoverPresentationController?.sourceView = toolbarController.shareButton
|
|
|
|
|
self.present(activityController, animated: true, completion: nil)
|
2020-07-28 11:37:10 -07:00
|
|
|
}
|
2020-07-24 19:26:35 -07:00
|
|
|
}), for: .touchUpInside)
|
|
|
|
|
|
|
|
|
|
// Script button
|
2020-07-31 14:08:10 -07:00
|
|
|
toolbarController.scriptControllerIconView.addAction(UIAction(handler: { [unowned self] action in
|
2020-07-30 23:54:20 -07:00
|
|
|
let hostOrigin = self.webView.url?.host ?? ""
|
|
|
|
|
let loadedScripts = self.tab.allowedScriptOrigins.union(self.tab.blockedScriptOrigins)
|
2020-07-29 18:58:31 -07:00
|
|
|
let scriptViewController = ScriptPolicyViewController(policyManager: self.policyManager,
|
|
|
|
|
hostOrigin: hostOrigin,
|
|
|
|
|
loadedScripts: loadedScripts,
|
2020-07-30 23:54:20 -07:00
|
|
|
scriptsAllowedForTab: self.tab.javaScriptEnabled)
|
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
|
2020-07-31 14:47:18 -07:00
|
|
|
toolbarController.darkModeButton.addAction(UIAction(handler: { [unowned self] _ in
|
|
|
|
|
self.tab.bridge.darkModeEnabled = !self.tab.bridge.darkModeEnabled
|
|
|
|
|
self.toolbarController.darkModeEnabled = self.tab.bridge.darkModeEnabled
|
2020-07-30 23:54:20 -07:00
|
|
|
}), for: .touchUpInside)
|
|
|
|
|
|
|
|
|
|
// Tabs button
|
2020-07-31 14:08:10 -07:00
|
|
|
toolbarController.windowButton.addAction(UIAction(handler: { [unowned self] _ in
|
2020-07-30 23:54:20 -07:00
|
|
|
let tabPickerController = TabPickerViewController(tabController: self.tabController)
|
|
|
|
|
tabPickerController.delegate = self
|
|
|
|
|
tabPickerController.selectedTab = self.tab
|
|
|
|
|
|
|
|
|
|
let navController = UINavigationController(rootViewController: tabPickerController)
|
|
|
|
|
navController.modalPresentationStyle = .popover
|
|
|
|
|
navController.popoverPresentationController?.sourceView = self.toolbarController.windowButton
|
|
|
|
|
navController.popoverPresentationController?.delegate = self
|
|
|
|
|
|
|
|
|
|
self.present(navController, animated: true, completion: nil)
|
2020-07-29 18:17:22 -07:00
|
|
|
}), for: .touchUpInside)
|
|
|
|
|
|
2020-07-31 16:36:10 -07:00
|
|
|
let newTabAction = UIAction { [unowned self] action in
|
|
|
|
|
if let gestureRecognizer = action.sender as? UILongPressGestureRecognizer {
|
|
|
|
|
if gestureRecognizer.state != .began { return }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create new tab
|
|
|
|
|
let newTab = tabController.createNewTab(url: nil)
|
|
|
|
|
self.tab = newTab
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let gestureRecognizer = UILongPressGestureRecognizer(action: newTabAction)
|
|
|
|
|
toolbarController.windowButton.addGestureRecognizer(gestureRecognizer)
|
|
|
|
|
|
|
|
|
|
// New tab button
|
|
|
|
|
toolbarController.newTabButton.addAction(newTabAction, for: .touchUpInside)
|
|
|
|
|
|
2020-07-31 18:29:44 -07:00
|
|
|
// Error button
|
|
|
|
|
toolbarController.urlBar.errorButton.addAction(UIAction(handler: { [unowned self] _ in
|
|
|
|
|
let alert = UIAlertController(title: "Error", message: self.loadError?.localizedDescription, preferredStyle: .actionSheet)
|
2020-07-31 18:33:04 -07:00
|
|
|
alert.popoverPresentationController?.sourceView = self.toolbarController.urlBar.errorButton
|
2020-07-31 18:29:44 -07:00
|
|
|
|
|
|
|
|
alert.addAction(UIAlertAction(title: "Reload", style: .destructive, handler: { _ in
|
|
|
|
|
self.webView.reload()
|
|
|
|
|
alert.dismiss(animated: true, completion: nil)
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
|
|
|
|
|
alert.dismiss(animated: true, completion: nil)
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
self.present(alert, animated: true, completion: nil)
|
|
|
|
|
}), for: .touchUpInside)
|
|
|
|
|
|
2020-09-21 17:56:22 -07:00
|
|
|
// Cancel button: hide autocomplete if applicable
|
|
|
|
|
toolbarController.toolbarView.cancelButton.addAction(UIAction(handler: { [unowned self] _ in
|
|
|
|
|
self.autocompleteViewController.view.isHidden = true
|
|
|
|
|
}), 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-09-21 17:56:22 -07:00
|
|
|
// Autocomplete controller
|
|
|
|
|
autocompleteViewController.delegate = self
|
|
|
|
|
autocompleteViewController.view.isHidden = true
|
|
|
|
|
|
2020-07-30 23:54:20 -07:00
|
|
|
self.view = browserView
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func updateLoadProgress(forWebView webView: WKWebView) {
|
2020-07-31 18:29:44 -07:00
|
|
|
if let loadError = loadError {
|
|
|
|
|
toolbarController.urlBar.loadProgress = .error(error: loadError)
|
|
|
|
|
} else if webView.estimatedProgress == 1.0 {
|
2020-07-30 23:54:20 -07:00
|
|
|
toolbarController.urlBar.loadProgress = .complete
|
|
|
|
|
} else {
|
|
|
|
|
toolbarController.urlBar.loadProgress = .loading(progress: webView.estimatedProgress)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func updateTitleAndURL(forWebView webView: WKWebView) {
|
2020-07-31 17:44:38 -07:00
|
|
|
browserView.titlebarView.setTitle(webView.title ?? "")
|
2020-07-30 23:54:20 -07:00
|
|
|
|
|
|
|
|
if let urlString = webView.url?.absoluteString {
|
|
|
|
|
toolbarController.urlBar.textField.text = urlString
|
2020-07-31 14:39:18 -07:00
|
|
|
} else {
|
|
|
|
|
toolbarController.urlBar.textField.text = ""
|
2020-07-30 23:54:20 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func didChangeTab(_ tab: Tab) {
|
|
|
|
|
tab.delegate = self
|
|
|
|
|
|
|
|
|
|
let webView = tab.webView
|
|
|
|
|
webView.allowsBackForwardNavigationGestures = true
|
|
|
|
|
webView.navigationDelegate = self
|
2020-09-21 15:39:41 -07:00
|
|
|
webView.uiDelegate = self
|
2020-07-30 23:54:20 -07:00
|
|
|
|
|
|
|
|
// Change webView
|
|
|
|
|
browserView.webView = webView
|
|
|
|
|
|
2020-09-21 17:56:22 -07:00
|
|
|
// Autocomplete view
|
2020-09-21 18:09:00 -07:00
|
|
|
browserView.autocompleteView = autocompleteViewController.view
|
2020-09-21 17:56:22 -07:00
|
|
|
|
2020-07-28 11:31:30 -07:00
|
|
|
// Load progress
|
2020-07-30 23:54:20 -07:00
|
|
|
updateLoadProgress(forWebView: webView)
|
2020-07-31 14:08:10 -07:00
|
|
|
loadingObservation = webView.observe(\.estimatedProgress) { [unowned self] (webView, observedChange) in
|
|
|
|
|
self.updateLoadProgress(forWebView: webView)
|
2020-07-28 11:31:30 -07:00
|
|
|
}
|
|
|
|
|
|
2020-07-29 14:16:25 -07:00
|
|
|
// Title observer
|
2020-07-30 23:54:20 -07:00
|
|
|
updateTitleAndURL(forWebView: webView)
|
2020-07-31 14:08:10 -07:00
|
|
|
titleObservation = webView.observe(\.title, changeHandler: { [unowned self] (webView, observedChange) in
|
|
|
|
|
self.updateTitleAndURL(forWebView: webView)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Back/forward observer
|
|
|
|
|
toolbarController.backButton.isEnabled = webView.canGoBack
|
|
|
|
|
backButtonObservation = webView.observe(\.canGoBack, changeHandler: { [toolbarController] (webView, observedChange) in
|
|
|
|
|
toolbarController.backButton.isEnabled = webView.canGoBack
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
toolbarController.forwardButton.isEnabled = webView.canGoForward
|
|
|
|
|
forwardButtonObservation = webView.observe(\.canGoForward, changeHandler: { [toolbarController] (webView, observedChange) in
|
|
|
|
|
toolbarController.forwardButton.isEnabled = webView.canGoForward
|
2020-07-29 14:16:25 -07:00
|
|
|
})
|
|
|
|
|
|
2020-07-30 23:54:20 -07:00
|
|
|
// Script blocker button
|
|
|
|
|
updateScriptBlockerButton()
|
|
|
|
|
|
2020-07-31 14:26:10 -07:00
|
|
|
// Enforce dark mode setting
|
|
|
|
|
tab.bridge.darkModeEnabled = toolbarController.darkModeEnabled
|
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()
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-31 15:25:22 -07:00
|
|
|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
|
|
|
|
super.traitCollectionDidChange(previousTraitCollection)
|
|
|
|
|
|
|
|
|
|
// Not sure why this doesn't happen automatically...
|
|
|
|
|
toolbarController.traitCollectionDidChange(previousTraitCollection)
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-24 19:26:35 -07:00
|
|
|
private func updateScriptBlockerButton() {
|
2020-07-31 14:39:18 -07:00
|
|
|
var numBlockedScripts: Int = tab.blockedScriptOrigins.count
|
2020-07-31 15:03:00 -07:00
|
|
|
if tab.url != nil, tab.javaScriptEnabled == false {
|
2020-07-31 14:39:18 -07:00
|
|
|
// Because the page is blocked too, notify.
|
|
|
|
|
numBlockedScripts += 1
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-31 15:03:00 -07:00
|
|
|
var scriptsAllowedForHost = false
|
|
|
|
|
if let url = webView.url, let host = url.host, policyManager.allowedOriginsForScriptResources().contains(host) {
|
|
|
|
|
scriptsAllowedForHost = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let iconView = toolbarController.scriptControllerIconView
|
|
|
|
|
iconView.shieldsDown = tab.javaScriptEnabled
|
|
|
|
|
iconView.someScriptsAllowed = scriptsAllowedForHost
|
|
|
|
|
iconView.setBlockedScriptsNumber(numBlockedScripts)
|
2020-07-22 19:29:38 -07:00
|
|
|
}
|
2020-07-30 23:54:20 -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-24 19:26:35 -07:00
|
|
|
// MARK: Navigation Delegate
|
|
|
|
|
|
|
|
|
|
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
2020-07-31 18:29:44 -07:00
|
|
|
loadError = nil
|
|
|
|
|
|
2020-09-22 12:30:09 -07:00
|
|
|
// Check to make sure we have connected to the web content process
|
|
|
|
|
if !tab.bridge.webContentProcessConnected {
|
|
|
|
|
// This means we started loading a page but the web content process hasn't loaded, which means
|
|
|
|
|
// scripts are not getting blocked.
|
|
|
|
|
|
|
|
|
|
// If you're ad-hoc signing this, you'll need to disable library validation:
|
|
|
|
|
// sudo defaults write /Library/Preferences/com.apple.security.libraryvalidation DisableLibraryValidation -bool YES
|
|
|
|
|
|
|
|
|
|
DispatchQueue.main.async { [unowned self] in
|
|
|
|
|
// Stop loading now
|
|
|
|
|
webView.stopLoading()
|
|
|
|
|
|
|
|
|
|
// Show an alert
|
|
|
|
|
let alert = UIAlertController(title: "Web Process Not Loaded",
|
|
|
|
|
message: "The web content process never contacted the host application",
|
|
|
|
|
preferredStyle: .alert)
|
|
|
|
|
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
|
|
|
|
|
alert.dismiss(animated: true, completion: nil)
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
present(alert, animated: true, completion: nil)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-24 19:26:35 -07:00
|
|
|
// Reset tracking this
|
2020-07-31 14:39:18 -07:00
|
|
|
tab.allowedScriptOrigins.removeAll()
|
2020-07-30 23:54:20 -07:00
|
|
|
tab.blockedScriptOrigins.removeAll()
|
2020-07-31 14:39:18 -07:00
|
|
|
updateScriptBlockerButton()
|
2020-07-24 19:26:35 -07:00
|
|
|
|
2020-09-21 17:56:22 -07:00
|
|
|
// Blur url bar if applicable
|
|
|
|
|
toolbarController.urlBar.textField.resignFirstResponder()
|
|
|
|
|
|
2020-07-30 23:54:20 -07:00
|
|
|
updateTitleAndURL(forWebView: webView)
|
|
|
|
|
|
|
|
|
|
if let url = webView.url {
|
2020-08-14 20:05:36 -07:00
|
|
|
// Start requesting favicon
|
2020-07-30 23:54:20 -07:00
|
|
|
tab.updateFaviconForURL(url)
|
2020-07-24 19:26:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-28 11:31:30 -07:00
|
|
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
|
|
|
toolbarController.urlBar.loadProgress = .complete
|
2020-08-14 20:05:36 -07:00
|
|
|
|
|
|
|
|
// Update history
|
|
|
|
|
if let url = webView.url {
|
|
|
|
|
let title = webView.title ?? ""
|
|
|
|
|
BrowserHistory.shared.didNavigate(toURL: url, title: title)
|
|
|
|
|
}
|
2020-07-28 11:31:30 -07:00
|
|
|
}
|
|
|
|
|
|
2020-07-29 17:46:53 -07:00
|
|
|
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void)
|
|
|
|
|
{
|
2020-07-30 23:54:20 -07:00
|
|
|
var allowJavaScript = tab.javaScriptEnabled
|
2020-07-29 17:46:53 -07:00
|
|
|
if !allowJavaScript, let host = navigationAction.request.url?.host {
|
|
|
|
|
// Check origin policy
|
|
|
|
|
allowJavaScript = policyManager.allowedOriginsForScriptResources().contains(host)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
preferences.allowsContentJavaScript = allowJavaScript
|
|
|
|
|
decisionHandler(.allow, preferences)
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-31 18:29:44 -07:00
|
|
|
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
|
|
|
|
|
self.loadError = error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
|
|
|
|
self.loadError = error
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-21 15:39:41 -07:00
|
|
|
// MARK: WKUIDelegate
|
|
|
|
|
|
|
|
|
|
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView?
|
|
|
|
|
{
|
|
|
|
|
let newTab = tabController.createNewTab(url: nil)
|
|
|
|
|
newTab.webView.load(navigationAction.request)
|
|
|
|
|
|
|
|
|
|
self.tab = newTab
|
|
|
|
|
|
|
|
|
|
return newTab.webView
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-24 19:26:35 -07:00
|
|
|
// MARK: UITextField Delegate
|
|
|
|
|
|
2020-08-14 20:05:36 -07:00
|
|
|
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
|
|
|
|
if let text = textField.text {
|
|
|
|
|
let matches = BrowserHistory.shared.visitedToplevelHistoryItems(matching: text)
|
2020-09-21 17:56:22 -07:00
|
|
|
autocompleteViewController.historyItems = matches
|
|
|
|
|
|
|
|
|
|
autocompleteViewController.view.isHidden = matches.count == 0
|
2020-08-14 20:05:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-24 19:26:35 -07:00
|
|
|
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
2020-07-31 15:19:39 -07:00
|
|
|
if let text = textField.text?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) {
|
|
|
|
|
|
|
|
|
|
// Dumb rules for stuff that "looks like" a URL
|
|
|
|
|
if !text.contains(" "),
|
|
|
|
|
text.components(separatedBy: ".").count > 1,
|
|
|
|
|
var url = URL(string: text)
|
|
|
|
|
{
|
|
|
|
|
if url.scheme == nil {
|
2020-08-02 12:08:49 -07:00
|
|
|
let urlString = "http://\(text)"
|
2020-07-31 15:19:39 -07:00
|
|
|
if let fixedURL = URL(string: urlString) {
|
|
|
|
|
url = fixedURL
|
|
|
|
|
}
|
2020-07-24 19:26:35 -07:00
|
|
|
}
|
2020-07-31 15:19:39 -07:00
|
|
|
|
|
|
|
|
tab.beginLoadingURL(url)
|
|
|
|
|
} else {
|
|
|
|
|
// Assume google search
|
|
|
|
|
let queryString = text.replacingOccurrences(of: " ", with: "+")
|
|
|
|
|
let searchURL = URL(string: "https://google.com/search?q=\(queryString)&gbv=1")! // gbv=1: no JS
|
|
|
|
|
tab.beginLoadingURL(searchURL)
|
2020-07-24 19:26:35 -07:00
|
|
|
}
|
2020-07-31 14:43:02 -07:00
|
|
|
|
2020-07-31 15:19:39 -07:00
|
|
|
textField.resignFirstResponder()
|
2020-07-24 19:26:35 -07:00
|
|
|
}
|
2020-09-21 17:56:22 -07:00
|
|
|
|
2020-07-24 19:26:35 -07:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-21 17:56:22 -07:00
|
|
|
func textFieldDidEndEditing(_ textField: UITextField) {
|
|
|
|
|
autocompleteViewController.view.isHidden = true
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-30 23:54:20 -07:00
|
|
|
// MARK: Tab Delegate
|
|
|
|
|
|
|
|
|
|
func didBlockScriptOrigin(_ origin: String, forTab: Tab) {
|
|
|
|
|
updateScriptBlockerButton()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: Tab Picker Delegate
|
|
|
|
|
|
|
|
|
|
func tabPicker(_ picker: TabPickerViewController, didSelectTab tab: Tab) {
|
|
|
|
|
self.tab = tab
|
|
|
|
|
picker.dismiss(animated: true, completion: nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func tabPicker(_ picker: TabPickerViewController, willCloseTab tab: Tab) {
|
|
|
|
|
// If closed tab is current tab, pick another one.
|
|
|
|
|
if tab == self.tab {
|
|
|
|
|
if let nextTab = tabController.tabs.last(where: { $0 != tab }) {
|
|
|
|
|
self.tab = nextTab
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
picker.dismiss(animated: true, completion: nil)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-24 19:26:35 -07:00
|
|
|
// MARK: Script Policy View Controller Delegate
|
|
|
|
|
|
|
|
|
|
func didChangeScriptPolicy() {
|
2020-07-30 23:54:20 -07:00
|
|
|
tab.bridge.policyDataSourceDidChange()
|
|
|
|
|
webView.reload()
|
2020-07-24 19:26:35 -07:00
|
|
|
}
|
|
|
|
|
|
2020-07-29 17:46:53 -07:00
|
|
|
func setScriptsEnabledForTab(_ enabled: Bool) {
|
2020-07-30 23:54:20 -07:00
|
|
|
tab.javaScriptEnabled = enabled
|
2020-07-31 15:03:00 -07:00
|
|
|
toolbarController.scriptControllerIconView.shieldsDown = enabled
|
2020-07-29 17:46:53 -07:00
|
|
|
}
|
2020-09-21 17:56:22 -07:00
|
|
|
|
|
|
|
|
// MARK: Autocomplete Controller Delegate
|
|
|
|
|
|
|
|
|
|
func autocompleteController(_: AutocompleteViewController, didSelectHistoryItem item: HistoryItem) {
|
|
|
|
|
tab.beginLoadingURL(item.url)
|
|
|
|
|
autocompleteViewController.view.isHidden = true
|
|
|
|
|
}
|
2020-09-21 18:35:39 -07:00
|
|
|
|
|
|
|
|
// MARK: Keyboard shortcuts
|
|
|
|
|
|
|
|
|
|
func focusURLBar(_ sender: Any?) {
|
|
|
|
|
toolbarController.urlBar.textField.becomeFirstResponder()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func goBack(_ sender: Any?) {
|
|
|
|
|
tab.webView.goBack()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func goForward(_ sender: Any?) {
|
|
|
|
|
tab.webView.goForward()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func createTab(_ sender: Any?) {
|
|
|
|
|
let newTab = tabController.createNewTab(url: nil)
|
|
|
|
|
self.tab = newTab
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func previousTab(_ sender: Any?) {
|
|
|
|
|
if let tabIndex = tabController.tabs.firstIndex(of: self.tab) {
|
|
|
|
|
if tabIndex - 1 >= 0 {
|
|
|
|
|
self.tab = tabController.tabs[tabIndex - 1]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func nextTab(_ sender: Any?) {
|
|
|
|
|
if let tabIndex = tabController.tabs.firstIndex(of: self.tab) {
|
|
|
|
|
if tabIndex + 1 < tabController.tabs.count {
|
|
|
|
|
self.tab = tabController.tabs[tabIndex + 1]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-22 19:29:38 -07:00
|
|
|
}
|