Files
Attractor/App/Browser View/BrowserViewController.swift

680 lines
27 KiB
Swift
Raw Normal View History

//
// BrowserViewController.swift
// SBrowser
//
// Created by James Magahern on 7/21/20.
//
import Combine
import UIKit
import UniformTypeIdentifiers
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
{
let browserView = BrowserView()
var tab: Tab { didSet { didChangeTab(tab) } }
var webView: WKWebView { tab.webView }
private let tabController = TabController()
private let tabBarViewController: TabBarViewController
2020-07-24 19:26:35 -07:00
private let toolbarController = ToolbarViewController()
2020-09-30 18:06:47 -07:00
private let findOnPageController = FindOnPageViewController()
2020-09-21 17:56:22 -07:00
private let autocompleteViewController = AutocompleteViewController()
private let redirectRules = PersonalRedirectRules()
2020-09-21 17:56:22 -07:00
private var policyManager: ResourcePolicyManager { tabController.policyManager }
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?
private var backButtonObservation: NSKeyValueObservation?
private var forwardButtonObservation: NSKeyValueObservation?
private var activeTabObservation: AnyCancellable?
2020-07-28 11:31:30 -07:00
2020-07-31 18:29:44 -07:00
private var loadError: Error?
private var commandKeyHeld: Bool = false
2020-07-29 14:16:25 -07:00
override var preferredStatusBarStyle: UIStatusBarStyle { .lightContent }
2020-07-24 19:26:35 -07:00
init() {
self.tab = tabController.tabs.first!
self.tabBarViewController = TabBarViewController(tabController: tabController)
super.init(nibName: nil, bundle: nil)
addChild(toolbarController)
2020-09-30 18:06:47 -07:00
addChild(findOnPageController)
addChild(tabBarViewController)
2020-09-30 18:06:47 -07:00
didChangeTab(tab)
}
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
2020-09-30 18:06:47 -07:00
browserView.findOnPageView = findOnPageController.findOnPageView
browserView.tabBarView = tabBarViewController.tabBarView
2020-07-24 19:26:35 -07:00
// 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()
}
}), for: .touchUpInside)
// Back button
2020-07-31 14:39:18 -07:00
toolbarController.backButton.addAction(UIAction(handler: { [unowned self] _ in
self.webView.goBack()
}), for: .touchUpInside)
// Forward button
2020-07-31 14:39:18 -07:00
toolbarController.forwardButton.addAction(UIAction(handler: { [unowned self] _ in
self.webView.goForward()
}), 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 {
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
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
toolbarController.scriptControllerIconView.addAction(UIAction(handler: { [unowned self] action in
let hostOrigin = self.webView.url?.host ?? ""
let loadedScripts = self.tab.allowedScriptOrigins.union(self.tab.blockedScriptOrigins)
let scriptViewController = ScriptPolicyViewController(policyManager: self.policyManager,
hostOrigin: hostOrigin,
loadedScripts: loadedScripts,
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
toolbarController.darkModeButton.addAction(UIAction(handler: { [unowned self] _ in
self.tab.bridge.darkModeEnabled = !self.tab.bridge.darkModeEnabled
self.toolbarController.darkModeEnabled = self.tab.bridge.darkModeEnabled
}), for: .touchUpInside)
// Tabs button
toolbarController.windowButton.addAction(UIAction(handler: { [unowned self] _ in
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)
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)
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)
// Clears out the error state
toolbarController.urlBar.loadProgress = .complete
2020-07-31 18:29:44 -07:00
}))
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-09-21 17:56:22 -07:00
// Autocomplete controller
autocompleteViewController.delegate = self
autocompleteViewController.view.isHidden = true
2020-09-22 15:37:13 -07:00
// Font size adjust
toolbarController.urlBar.documentButton.addAction(UIAction(handler: { [unowned self] _ in
let documentControls = DocumentControlViewController()
documentControls.modalPresentationStyle = .popover
documentControls.popoverPresentationController?.permittedArrowDirections = [ .down, .up ]
documentControls.popoverPresentationController?.sourceView = toolbarController.urlBar.documentButton
documentControls.popoverPresentationController?.delegate = self
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .percent
let label = documentControls.fontSizeAdjustView.labelView
label.text = numberFormatter.string(for: tab.webView._viewScale)
documentControls.fontSizeAdjustView.decreaseSizeButton.addAction(UIAction(handler: { [unowned self] sender in
self.decreaseSize(sender)
2020-09-22 15:37:13 -07:00
label.text = numberFormatter.string(for: tab.webView._viewScale)
}), for: .touchUpInside)
documentControls.fontSizeAdjustView.increaseSizeButton.addAction(UIAction(handler: { [unowned self] sender in
self.increaseSize(sender)
2020-09-22 15:37:13 -07:00
label.text = numberFormatter.string(for: tab.webView._viewScale)
}), for: .touchUpInside)
2020-09-30 18:06:47 -07:00
documentControls.findOnPageControlView.addAction(UIAction(handler: { [unowned self] _ in
documentControls.dismiss(animated: true, completion: nil)
browserView.setFindOnPageVisible(true, animated: true)
}), for: .touchUpInside)
2020-09-22 15:37:13 -07:00
present(documentControls, animated: true, completion: nil)
}), for: .touchUpInside)
2020-09-30 18:06:47 -07:00
// Find on page dismiss
findOnPageController.findOnPageView.doneButton.addAction(UIAction(handler: { [unowned self] _ in
browserView.setFindOnPageVisible(false, animated: true)
}), for: .touchUpInside)
// Tab controller
activeTabObservation = tabController.$activeTabIndex
.receive(on: RunLoop.main)
.sink(receiveValue: { [unowned self] (activeTab: Int) in
let tab = tabController.tabs[activeTab]
if self.tab != tab {
self.tab = tab
}
})
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 {
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 ?? "")
if let urlString = webView.url?.absoluteString {
toolbarController.urlBar.textField.text = urlString
2020-07-31 14:39:18 -07:00
} else {
toolbarController.urlBar.textField.text = ""
}
// Figure out which tab this corresponds to
let tab = tabController.tabs.first { $0.webView == webView }
if let tab = tab, let tabIndex = tabController.tabs.firstIndex(of: tab) {
tabBarViewController.tabBarView.reloadTab(atIndex: tabIndex)
}
}
private func didChangeTab(_ tab: Tab) {
if let activeIndex = tabController.tabs.firstIndex(of: tab) {
tabController.activeTabIndex = activeIndex
}
tab.delegate = self
let webView = tab.webView
webView.allowsBackForwardNavigationGestures = true
webView.navigationDelegate = self
webView.uiDelegate = self
// Change webView
browserView.webView = webView
2020-09-30 18:06:47 -07:00
findOnPageController.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
// Show tab bar view?
browserView.tabBarViewVisible = tabController.tabs.count > 1
2020-07-28 11:31:30 -07:00
// Load progress
updateLoadProgress(forWebView: webView)
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
updateTitleAndURL(forWebView: webView)
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
})
// Script blocker button
updateScriptBlockerButton()
2020-07-31 14:26:10 -07:00
// Enforce dark mode setting
tab.bridge.darkModeEnabled = toolbarController.darkModeEnabled
}
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)
}
private 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)
}
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
if tab.url != nil, tab.javaScriptEnabled == false {
2020-07-31 14:39:18 -07:00
// Because the page is blocked too, notify.
numBlockedScripts += 1
}
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)
}
public func createNewTab(withURL url: URL?) {
let newTab = tabController.createNewTab(url: url)
self.tab = newTab
}
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
// 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()
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()
updateTitleAndURL(forWebView: webView)
if let url = webView.url {
2020-08-14 20:05:36 -07:00
// Start requesting favicon
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
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void)
{
// Handle command+click
if commandKeyHeld && 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
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)
}
}
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
}
// MARK: WKUIDelegate
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView?
{
2020-09-24 16:36:31 -07:00
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",
2020-09-22 14:33:00 -07:00
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)
}
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 {
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
2020-11-10 16:57:56 -06:00
let queryString = text
.replacingOccurrences(of: " ", with: "+")
.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
2020-07-31 15:19:39 -07:00
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 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
}
// 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() {
tab.bridge.policyDataSourceDidChange()
webView.reload()
2020-07-24 19:26:35 -07:00
}
func setScriptsEnabledForTab(_ enabled: Bool) {
tab.javaScriptEnabled = enabled
toolbarController.scriptControllerIconView.shieldsDown = enabled
}
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?) {
createNewTab(withURL: nil)
2020-09-21 18:35:39 -07:00
}
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-09-30 18:06:47 -07:00
2020-11-10 11:57:40 -06:00
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
}
}
2020-09-30 18:06:47 -07:00
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
}
}