BrowserViewController: break up into extensions
This commit is contained in:
103
App/Browser View/BrowserViewController+Keyboard.swift
Normal file
103
App/Browser View/BrowserViewController+Keyboard.swift
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
//
|
||||||
|
// BrowserViewController+Keyboard.swift
|
||||||
|
// App
|
||||||
|
//
|
||||||
|
// Created by James Magahern on 2/17/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension BrowserViewController: ShortcutResponder
|
||||||
|
{
|
||||||
|
internal func updateCommandKeyState(forPresses presses: Set<UIPress>) {
|
||||||
|
guard let press = presses.first else { return }
|
||||||
|
|
||||||
|
if let key = press.key {
|
||||||
|
if key.modifierFlags == [.command] {
|
||||||
|
let isDown = press.phase == .began || press.phase == .changed || press.phase == .stationary
|
||||||
|
self.commandKeyHeld = isDown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
|
||||||
|
super.pressesBegan(presses, with: event)
|
||||||
|
updateCommandKeyState(forPresses: presses)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func pressesCancelled(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
|
||||||
|
super.pressesCancelled(presses, with: event)
|
||||||
|
updateCommandKeyState(forPresses: presses)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
|
||||||
|
super.pressesEnded(presses, with: event)
|
||||||
|
updateCommandKeyState(forPresses: presses)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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?) {
|
||||||
|
createNewTab(withURL: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeTab(_ sender: Any?) {
|
||||||
|
if tabController.tabs.count > 1 {
|
||||||
|
tabController.closeTab(self.tab)
|
||||||
|
} else {
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
if let originWindowScene = self.view.window?.windowScene {
|
||||||
|
UIApplication.shared.requestSceneSessionDestruction(originWindowScene.session, options: nil) { error in
|
||||||
|
print("Error when requesting scene destruction: " + error.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findOnPage(_ sender: Any?) {
|
||||||
|
browserView.setFindOnPageVisible(true, animated: true)
|
||||||
|
findOnPageController.findOnPageView.textField.becomeFirstResponder()
|
||||||
|
}
|
||||||
|
|
||||||
|
func refresh(_ sender: Any?) {
|
||||||
|
webView.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func increaseSize(_ sender: Any?) {
|
||||||
|
tab.webView._viewScale += 0.10
|
||||||
|
}
|
||||||
|
|
||||||
|
override func decreaseSize(_ sender: Any?) {
|
||||||
|
tab.webView._viewScale -= 0.10
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
146
App/Browser View/BrowserViewController+WebKitDelegate.swift
Normal file
146
App/Browser View/BrowserViewController+WebKitDelegate.swift
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
//
|
||||||
|
// BrowserViewController+WebKitDelegate.swift
|
||||||
|
// App
|
||||||
|
//
|
||||||
|
// Created by James Magahern on 2/17/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import WebKit
|
||||||
|
|
||||||
|
extension BrowserViewController: WKNavigationDelegate, WKUIDelegate
|
||||||
|
{
|
||||||
|
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
||||||
|
loadError = nil
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset tracking this
|
||||||
|
tab.allowedScriptOrigins.removeAll()
|
||||||
|
tab.blockedScriptOrigins.removeAll()
|
||||||
|
updateScriptBlockerButton()
|
||||||
|
|
||||||
|
// Blur url bar if applicable
|
||||||
|
toolbarController.urlBar.textField.resignFirstResponder()
|
||||||
|
|
||||||
|
updateTitleAndURL(forWebView: webView)
|
||||||
|
|
||||||
|
if let url = webView.url {
|
||||||
|
// Start requesting favicon
|
||||||
|
tab.updateFaviconForURL(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||||
|
toolbarController.urlBar.loadProgress = .complete
|
||||||
|
|
||||||
|
// Update history
|
||||||
|
if let url = webView.url {
|
||||||
|
let title = webView.title ?? ""
|
||||||
|
BrowserHistory.shared.didNavigate(toURL: url, title: title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void)
|
||||||
|
{
|
||||||
|
// Handle command+click
|
||||||
|
if (commandKeyHeld || windowButtonHeld) && navigationAction.navigationType == .linkActivated {
|
||||||
|
// Cancel navigation in this tab
|
||||||
|
decisionHandler(.cancel, preferences)
|
||||||
|
|
||||||
|
// Start navigation in a new tab
|
||||||
|
let tab = tabController.createNewTab(url: navigationAction.request.url)
|
||||||
|
self.tab = tab
|
||||||
|
|
||||||
|
// Reset this flag.
|
||||||
|
commandKeyHeld = false
|
||||||
|
windowButtonHeld = false
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var allowJavaScript = tab.javaScriptEnabled
|
||||||
|
if !allowJavaScript, let host = navigationAction.request.url?.host {
|
||||||
|
// Check origin policy
|
||||||
|
allowJavaScript = policyManager.allowedOriginsForScriptResources().contains(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
preferences.allowsContentJavaScript = allowJavaScript
|
||||||
|
|
||||||
|
if let url = navigationAction.request.url,
|
||||||
|
let redirectedURL = redirectRules.redirectedURL(for: url)
|
||||||
|
{
|
||||||
|
tab.beginLoadingURL(redirectedURL)
|
||||||
|
decisionHandler(.cancel, preferences)
|
||||||
|
} else {
|
||||||
|
decisionHandler(.allow, preferences)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: WKUIDelegate
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView?
|
||||||
|
{
|
||||||
|
let newTab = tabController.createNewTab(url: nil, webViewConfiguration: configuration)
|
||||||
|
newTab.webView.load(navigationAction.request)
|
||||||
|
|
||||||
|
self.tab = newTab
|
||||||
|
|
||||||
|
return newTab.webView
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, contextMenuConfigurationForElement elementInfo: WKContextMenuElementInfo, completionHandler: @escaping (UIContextMenuConfiguration?) -> Void) {
|
||||||
|
let menuConfig = UIContextMenuConfiguration(identifier: nil,
|
||||||
|
previewProvider: nil) { (menuElements: [UIMenuElement]) -> UIMenu? in
|
||||||
|
|
||||||
|
let openInNewTab = UIAction(title: "Open in New Tab",
|
||||||
|
image: UIImage(systemName: "plus.app"),
|
||||||
|
identifier: nil,
|
||||||
|
discoverabilityTitle: nil,
|
||||||
|
attributes: [],
|
||||||
|
state: .off) { [unowned self] _ in
|
||||||
|
let newTab = tabController.createNewTab(url: elementInfo.linkURL)
|
||||||
|
self.tab = newTab
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return UIMenu(title: elementInfo.linkURL?.absoluteString ?? "Link",
|
||||||
|
image: nil,
|
||||||
|
identifier: nil,
|
||||||
|
options: .displayInline,
|
||||||
|
children: [ openInNewTab ] + menuElements)
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(menuConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,23 +9,21 @@ import Combine
|
|||||||
import UIKit
|
import UIKit
|
||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegate, ScriptPolicyViewControllerDelegate,
|
class BrowserViewController: UIViewController
|
||||||
UIPopoverPresentationControllerDelegate, TabDelegate, TabPickerViewControllerDelegate,
|
|
||||||
AutocompleteViewControllerDelegate, ShortcutResponder
|
|
||||||
{
|
{
|
||||||
let browserView = BrowserView()
|
let browserView = BrowserView()
|
||||||
var tab: Tab { didSet { didChangeTab(tab) } }
|
var tab: Tab { didSet { didChangeTab(tab) } }
|
||||||
var webView: WKWebView { tab.webView }
|
var webView: WKWebView { tab.webView }
|
||||||
|
|
||||||
private let tabController = TabController()
|
internal let tabController = TabController()
|
||||||
private let tabBarViewController: TabBarViewController
|
internal let tabBarViewController: TabBarViewController
|
||||||
private let toolbarController = ToolbarViewController()
|
internal let toolbarController = ToolbarViewController()
|
||||||
private let findOnPageController = FindOnPageViewController()
|
internal let findOnPageController = FindOnPageViewController()
|
||||||
|
|
||||||
private let autocompleteViewController = AutocompleteViewController()
|
internal let autocompleteViewController = AutocompleteViewController()
|
||||||
private let redirectRules = PersonalRedirectRules()
|
internal let redirectRules = PersonalRedirectRules()
|
||||||
|
|
||||||
private var policyManager: ResourcePolicyManager { tabController.policyManager }
|
internal var policyManager: ResourcePolicyManager { tabController.policyManager }
|
||||||
|
|
||||||
override var canBecomeFirstResponder: Bool { true }
|
override var canBecomeFirstResponder: Bool { true }
|
||||||
|
|
||||||
@@ -36,10 +34,10 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
private var activeTabObservation: AnyCancellable?
|
private var activeTabObservation: AnyCancellable?
|
||||||
private var faviconObservation: AnyCancellable?
|
private var faviconObservation: AnyCancellable?
|
||||||
|
|
||||||
private var loadError: Error?
|
internal var loadError: Error?
|
||||||
|
|
||||||
private var commandKeyHeld: Bool = false
|
internal var commandKeyHeld: Bool = false
|
||||||
private var windowButtonHeld: Bool {
|
internal var windowButtonHeld: Bool {
|
||||||
get { toolbarController.newTabButton.isTracking }
|
get { toolbarController.newTabButton.isTracking }
|
||||||
set { toolbarController.newTabButton.cancelTracking(with: nil) }
|
set { toolbarController.newTabButton.cancelTracking(with: nil) }
|
||||||
}
|
}
|
||||||
@@ -305,7 +303,7 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
self.view = browserView
|
self.view = browserView
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateLoadProgress(forWebView webView: WKWebView) {
|
internal func updateLoadProgress(forWebView webView: WKWebView) {
|
||||||
if let loadError = loadError {
|
if let loadError = loadError {
|
||||||
toolbarController.urlBar.loadProgress = .error(error: loadError)
|
toolbarController.urlBar.loadProgress = .error(error: loadError)
|
||||||
} else if webView.estimatedProgress == 1.0 {
|
} else if webView.estimatedProgress == 1.0 {
|
||||||
@@ -315,7 +313,7 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateTitleAndURL(forWebView webView: WKWebView) {
|
internal func updateTitleAndURL(forWebView webView: WKWebView) {
|
||||||
if webView == browserView.webView {
|
if webView == browserView.webView {
|
||||||
browserView.titlebarView.setTitle(webView.title ?? "")
|
browserView.titlebarView.setTitle(webView.title ?? "")
|
||||||
|
|
||||||
@@ -403,33 +401,7 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
toolbarController.traitCollectionDidChange(previousTraitCollection)
|
toolbarController.traitCollectionDidChange(previousTraitCollection)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateCommandKeyState(forPresses presses: Set<UIPress>) {
|
internal func updateScriptBlockerButton() {
|
||||||
guard let press = presses.first else { return }
|
|
||||||
|
|
||||||
if let key = press.key {
|
|
||||||
if key.modifierFlags == [.command] {
|
|
||||||
let isDown = press.phase == .began || press.phase == .changed || press.phase == .stationary
|
|
||||||
self.commandKeyHeld = isDown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
|
|
||||||
super.pressesBegan(presses, with: event)
|
|
||||||
updateCommandKeyState(forPresses: presses)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func pressesCancelled(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
|
|
||||||
super.pressesCancelled(presses, with: event)
|
|
||||||
updateCommandKeyState(forPresses: presses)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
|
|
||||||
super.pressesEnded(presses, with: event)
|
|
||||||
updateCommandKeyState(forPresses: presses)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updateScriptBlockerButton() {
|
|
||||||
var numBlockedScripts: Int = tab.blockedScriptOrigins.count
|
var numBlockedScripts: Int = tab.blockedScriptOrigins.count
|
||||||
if tab.url != nil, tab.javaScriptEnabled == false {
|
if tab.url != nil, tab.javaScriptEnabled == false {
|
||||||
// Because the page is blocked too, notify.
|
// Because the page is blocked too, notify.
|
||||||
@@ -451,158 +423,46 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
let newTab = tabController.createNewTab(url: url)
|
let newTab = tabController.createNewTab(url: url)
|
||||||
self.tab = newTab
|
self.tab = newTab
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// MARK: UIPopoverPresentationControllerDelegate
|
|
||||||
|
extension BrowserViewController: UIPopoverPresentationControllerDelegate
|
||||||
|
{
|
||||||
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
|
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
|
||||||
// Forces popovers to present on iPhone
|
// Forces popovers to present on iPhone
|
||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// MARK: Navigation Delegate
|
|
||||||
|
extension BrowserViewController: ScriptPolicyViewControllerDelegate
|
||||||
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
{
|
||||||
loadError = nil
|
func didChangeScriptPolicy() {
|
||||||
|
tab.bridge.policyDataSourceDidChange()
|
||||||
// Check to make sure we have connected to the web content process
|
webView.reload()
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset tracking this
|
|
||||||
tab.allowedScriptOrigins.removeAll()
|
|
||||||
tab.blockedScriptOrigins.removeAll()
|
|
||||||
updateScriptBlockerButton()
|
|
||||||
|
|
||||||
// Blur url bar if applicable
|
|
||||||
toolbarController.urlBar.textField.resignFirstResponder()
|
|
||||||
|
|
||||||
updateTitleAndURL(forWebView: webView)
|
|
||||||
|
|
||||||
if let url = webView.url {
|
|
||||||
// Start requesting favicon
|
|
||||||
tab.updateFaviconForURL(url)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
func setScriptsEnabledForTab(_ enabled: Bool) {
|
||||||
toolbarController.urlBar.loadProgress = .complete
|
tab.javaScriptEnabled = enabled
|
||||||
|
toolbarController.scriptControllerIconView.shieldsDown = enabled
|
||||||
// Update history
|
|
||||||
if let url = webView.url {
|
|
||||||
let title = webView.title ?? ""
|
|
||||||
BrowserHistory.shared.didNavigate(toURL: url, title: title)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void)
|
|
||||||
{
|
extension BrowserViewController: AutocompleteViewControllerDelegate
|
||||||
// Handle command+click
|
{
|
||||||
if (commandKeyHeld || windowButtonHeld) && navigationAction.navigationType == .linkActivated {
|
func autocompleteController(_: AutocompleteViewController, didSelectHistoryItem item: HistoryItem) {
|
||||||
// Cancel navigation in this tab
|
tab.beginLoadingURL(item.url)
|
||||||
decisionHandler(.cancel, preferences)
|
autocompleteViewController.view.isHidden = true
|
||||||
|
|
||||||
// Start navigation in a new tab
|
|
||||||
let tab = tabController.createNewTab(url: navigationAction.request.url)
|
|
||||||
self.tab = tab
|
|
||||||
|
|
||||||
// Reset this flag.
|
|
||||||
commandKeyHeld = false
|
|
||||||
windowButtonHeld = false
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var allowJavaScript = tab.javaScriptEnabled
|
|
||||||
if !allowJavaScript, let host = navigationAction.request.url?.host {
|
|
||||||
// Check origin policy
|
|
||||||
allowJavaScript = policyManager.allowedOriginsForScriptResources().contains(host)
|
|
||||||
}
|
|
||||||
|
|
||||||
preferences.allowsContentJavaScript = allowJavaScript
|
|
||||||
|
|
||||||
if let url = navigationAction.request.url,
|
|
||||||
let redirectedURL = redirectRules.redirectedURL(for: url)
|
|
||||||
{
|
|
||||||
tab.beginLoadingURL(redirectedURL)
|
|
||||||
decisionHandler(.cancel, preferences)
|
|
||||||
} else {
|
|
||||||
decisionHandler(.allow, preferences)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
|
|
||||||
self.loadError = error
|
extension BrowserViewController: TabDelegate
|
||||||
}
|
{
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
|
||||||
self.loadError = error
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: WKUIDelegate
|
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView?
|
|
||||||
{
|
|
||||||
let newTab = tabController.createNewTab(url: nil, webViewConfiguration: configuration)
|
|
||||||
newTab.webView.load(navigationAction.request)
|
|
||||||
|
|
||||||
self.tab = newTab
|
|
||||||
|
|
||||||
return newTab.webView
|
|
||||||
}
|
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, contextMenuConfigurationForElement elementInfo: WKContextMenuElementInfo, completionHandler: @escaping (UIContextMenuConfiguration?) -> Void) {
|
|
||||||
let menuConfig = UIContextMenuConfiguration(identifier: nil,
|
|
||||||
previewProvider: nil) { (menuElements: [UIMenuElement]) -> UIMenu? in
|
|
||||||
|
|
||||||
let openInNewTab = UIAction(title: "Open in New Tab",
|
|
||||||
image: UIImage(systemName: "plus.app"),
|
|
||||||
identifier: nil,
|
|
||||||
discoverabilityTitle: nil,
|
|
||||||
attributes: [],
|
|
||||||
state: .off) { [unowned self] _ in
|
|
||||||
let newTab = tabController.createNewTab(url: elementInfo.linkURL)
|
|
||||||
self.tab = newTab
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return UIMenu(title: elementInfo.linkURL?.absoluteString ?? "Link",
|
|
||||||
image: nil,
|
|
||||||
identifier: nil,
|
|
||||||
options: .displayInline,
|
|
||||||
children: [ openInNewTab ] + menuElements)
|
|
||||||
}
|
|
||||||
|
|
||||||
completionHandler(menuConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Tab Delegate
|
|
||||||
|
|
||||||
func didBlockScriptOrigin(_ origin: String, forTab: Tab) {
|
func didBlockScriptOrigin(_ origin: String, forTab: Tab) {
|
||||||
updateScriptBlockerButton()
|
updateScriptBlockerButton()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// MARK: Tab Picker Delegate
|
|
||||||
|
extension BrowserViewController: TabPickerViewControllerDelegate
|
||||||
|
{
|
||||||
func tabPicker(_ picker: TabPickerViewController, didSelectTab tab: Tab) {
|
func tabPicker(_ picker: TabPickerViewController, didSelectTab tab: Tab) {
|
||||||
self.tab = tab
|
self.tab = tab
|
||||||
picker.dismiss(animated: true, completion: nil)
|
picker.dismiss(animated: true, completion: nil)
|
||||||
@@ -614,90 +474,6 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
picker.dismiss(animated: true, completion: nil)
|
picker.dismiss(animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Script Policy View Controller Delegate
|
|
||||||
|
|
||||||
func didChangeScriptPolicy() {
|
|
||||||
tab.bridge.policyDataSourceDidChange()
|
|
||||||
webView.reload()
|
|
||||||
}
|
|
||||||
|
|
||||||
func setScriptsEnabledForTab(_ enabled: Bool) {
|
|
||||||
tab.javaScriptEnabled = enabled
|
|
||||||
toolbarController.scriptControllerIconView.shieldsDown = enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Autocomplete Controller Delegate
|
|
||||||
|
|
||||||
func autocompleteController(_: AutocompleteViewController, didSelectHistoryItem item: HistoryItem) {
|
|
||||||
tab.beginLoadingURL(item.url)
|
|
||||||
autocompleteViewController.view.isHidden = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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?) {
|
|
||||||
createNewTab(withURL: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
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]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func closeTab(_ sender: Any?) {
|
|
||||||
if tabController.tabs.count > 1 {
|
|
||||||
tabController.closeTab(self.tab)
|
|
||||||
} else {
|
|
||||||
#if targetEnvironment(macCatalyst)
|
|
||||||
if let originWindowScene = self.view.window?.windowScene {
|
|
||||||
UIApplication.shared.requestSceneSessionDestruction(originWindowScene.session, options: nil) { error in
|
|
||||||
print("Error when requesting scene destruction: " + error.localizedDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func findOnPage(_ sender: Any?) {
|
|
||||||
browserView.setFindOnPageVisible(true, animated: true)
|
|
||||||
findOnPageController.findOnPageView.textField.becomeFirstResponder()
|
|
||||||
}
|
|
||||||
|
|
||||||
func refresh(_ sender: Any?) {
|
|
||||||
webView.reload()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func increaseSize(_ sender: Any?) {
|
|
||||||
tab.webView._viewScale += 0.10
|
|
||||||
}
|
|
||||||
|
|
||||||
override func decreaseSize(_ sender: Any?) {
|
|
||||||
tab.webView._viewScale -= 0.10
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension BrowserViewController: UITextFieldDelegate
|
extension BrowserViewController: UITextFieldDelegate
|
||||||
|
|||||||
@@ -42,6 +42,8 @@
|
|||||||
1ADFF4D024CBBCD1006DC7AE /* ScriptPolicyControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4CF24CBBCD1006DC7AE /* ScriptPolicyControl.swift */; };
|
1ADFF4D024CBBCD1006DC7AE /* ScriptPolicyControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4CF24CBBCD1006DC7AE /* ScriptPolicyControl.swift */; };
|
||||||
CD01D5A5254A10BB00189CDC /* TabBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD01D5A4254A10BB00189CDC /* TabBarView.swift */; };
|
CD01D5A5254A10BB00189CDC /* TabBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD01D5A4254A10BB00189CDC /* TabBarView.swift */; };
|
||||||
CD01D5AB254A206D00189CDC /* TabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD01D5AA254A206D00189CDC /* TabBarViewController.swift */; };
|
CD01D5AB254A206D00189CDC /* TabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD01D5AA254A206D00189CDC /* TabBarViewController.swift */; };
|
||||||
|
CD470C4225DE056600AFBE0E /* BrowserViewController+WebKitDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD470C4125DE056600AFBE0E /* BrowserViewController+WebKitDelegate.swift */; };
|
||||||
|
CD470C4425DE070400AFBE0E /* BrowserViewController+Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD470C4325DE070400AFBE0E /* BrowserViewController+Keyboard.swift */; };
|
||||||
CD7A8915251975B70075991E /* AutocompleteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7A8914251975B70075991E /* AutocompleteViewController.swift */; };
|
CD7A8915251975B70075991E /* AutocompleteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7A8914251975B70075991E /* AutocompleteViewController.swift */; };
|
||||||
CD7A89172519872D0075991E /* KeyboardShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7A89162519872D0075991E /* KeyboardShortcuts.swift */; };
|
CD7A89172519872D0075991E /* KeyboardShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7A89162519872D0075991E /* KeyboardShortcuts.swift */; };
|
||||||
CD7A8919251989C90075991E /* UIKeyCommand+ConvInit.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7A8918251989C90075991E /* UIKeyCommand+ConvInit.swift */; };
|
CD7A8919251989C90075991E /* UIKeyCommand+ConvInit.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7A8918251989C90075991E /* UIKeyCommand+ConvInit.swift */; };
|
||||||
@@ -127,6 +129,8 @@
|
|||||||
1ADFF4CF24CBBCD1006DC7AE /* ScriptPolicyControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptPolicyControl.swift; sourceTree = "<group>"; };
|
1ADFF4CF24CBBCD1006DC7AE /* ScriptPolicyControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptPolicyControl.swift; sourceTree = "<group>"; };
|
||||||
CD01D5A4254A10BB00189CDC /* TabBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarView.swift; sourceTree = "<group>"; };
|
CD01D5A4254A10BB00189CDC /* TabBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarView.swift; sourceTree = "<group>"; };
|
||||||
CD01D5AA254A206D00189CDC /* TabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarViewController.swift; sourceTree = "<group>"; };
|
CD01D5AA254A206D00189CDC /* TabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarViewController.swift; sourceTree = "<group>"; };
|
||||||
|
CD470C4125DE056600AFBE0E /* BrowserViewController+WebKitDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BrowserViewController+WebKitDelegate.swift"; sourceTree = "<group>"; };
|
||||||
|
CD470C4325DE070400AFBE0E /* BrowserViewController+Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BrowserViewController+Keyboard.swift"; sourceTree = "<group>"; };
|
||||||
CD7A8914251975B70075991E /* AutocompleteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutocompleteViewController.swift; sourceTree = "<group>"; };
|
CD7A8914251975B70075991E /* AutocompleteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutocompleteViewController.swift; sourceTree = "<group>"; };
|
||||||
CD7A89162519872D0075991E /* KeyboardShortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardShortcuts.swift; sourceTree = "<group>"; };
|
CD7A89162519872D0075991E /* KeyboardShortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardShortcuts.swift; sourceTree = "<group>"; };
|
||||||
CD7A8918251989C90075991E /* UIKeyCommand+ConvInit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIKeyCommand+ConvInit.swift"; sourceTree = "<group>"; };
|
CD7A8918251989C90075991E /* UIKeyCommand+ConvInit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIKeyCommand+ConvInit.swift"; sourceTree = "<group>"; };
|
||||||
@@ -274,8 +278,10 @@
|
|||||||
1ADFF47724C7DFE8006DC7AE /* Browser View */ = {
|
1ADFF47724C7DFE8006DC7AE /* Browser View */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
1ADFF47324C7DE9C006DC7AE /* BrowserViewController.swift */,
|
|
||||||
1ADFF47824C7DFF8006DC7AE /* BrowserView.swift */,
|
1ADFF47824C7DFF8006DC7AE /* BrowserView.swift */,
|
||||||
|
1ADFF47324C7DE9C006DC7AE /* BrowserViewController.swift */,
|
||||||
|
CD470C4325DE070400AFBE0E /* BrowserViewController+Keyboard.swift */,
|
||||||
|
CD470C4125DE056600AFBE0E /* BrowserViewController+WebKitDelegate.swift */,
|
||||||
);
|
);
|
||||||
path = "Browser View";
|
path = "Browser View";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -490,6 +496,7 @@
|
|||||||
CDCE2666251AA840007FE92A /* StackView.swift in Sources */,
|
CDCE2666251AA840007FE92A /* StackView.swift in Sources */,
|
||||||
CD853BD124E778B800D2BDCC /* History.xcdatamodeld in Sources */,
|
CD853BD124E778B800D2BDCC /* History.xcdatamodeld in Sources */,
|
||||||
1AD31040252545BF00A4A952 /* FindOnPageView.swift in Sources */,
|
1AD31040252545BF00A4A952 /* FindOnPageView.swift in Sources */,
|
||||||
|
CD470C4225DE056600AFBE0E /* BrowserViewController+WebKitDelegate.swift in Sources */,
|
||||||
CDEDD8AA25D62ADB00862605 /* UITraitCollection+MacLike.swift in Sources */,
|
CDEDD8AA25D62ADB00862605 /* UITraitCollection+MacLike.swift in Sources */,
|
||||||
CD7A8919251989C90075991E /* UIKeyCommand+ConvInit.swift in Sources */,
|
CD7A8919251989C90075991E /* UIKeyCommand+ConvInit.swift in Sources */,
|
||||||
1ADFF4C724CA6DEB006DC7AE /* UIEdgeInsets+Layout.swift in Sources */,
|
1ADFF4C724CA6DEB006DC7AE /* UIEdgeInsets+Layout.swift in Sources */,
|
||||||
@@ -499,6 +506,7 @@
|
|||||||
CD01D5A5254A10BB00189CDC /* TabBarView.swift in Sources */,
|
CD01D5A5254A10BB00189CDC /* TabBarView.swift in Sources */,
|
||||||
1ADFF4D024CBBCD1006DC7AE /* ScriptPolicyControl.swift in Sources */,
|
1ADFF4D024CBBCD1006DC7AE /* ScriptPolicyControl.swift in Sources */,
|
||||||
1A03810D24E71CA700826501 /* ToolbarView.swift in Sources */,
|
1A03810D24E71CA700826501 /* ToolbarView.swift in Sources */,
|
||||||
|
CD470C4425DE070400AFBE0E /* BrowserViewController+Keyboard.swift in Sources */,
|
||||||
CD853BD424E77BF900D2BDCC /* HistoryItem.swift in Sources */,
|
CD853BD424E77BF900D2BDCC /* HistoryItem.swift in Sources */,
|
||||||
1ADFF48D24C8C176006DC7AE /* SBRProcessBundleBridge.m in Sources */,
|
1ADFF48D24C8C176006DC7AE /* SBRProcessBundleBridge.m in Sources */,
|
||||||
1AD3103D252541E600A4A952 /* PersonalRedirectRules.swift in Sources */,
|
1AD3103D252541E600A4A952 /* PersonalRedirectRules.swift in Sources */,
|
||||||
|
|||||||
Reference in New Issue
Block a user