mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 15:58:39 +09:00
Dactyloidae iOS initial commit
This commit is contained in:
parent
daa6179d22
commit
7154a0497e
2123 changed files with 197052 additions and 0 deletions
|
|
@ -0,0 +1,100 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import Shared
|
||||
|
||||
// Naming functions: use the suffix 'KeyCommand' for an additional level of namespacing (bug 1415830)
|
||||
|
||||
extension BrowserViewController {
|
||||
|
||||
@objc private func reloadTabKeyCommand() {
|
||||
if homePanelController == nil {
|
||||
tabManager.selectedTab?.reload()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func goBackKeyCommand() {
|
||||
if tabManager.selectedTab?.canGoBack == true && homePanelController == nil {
|
||||
tabManager.selectedTab?.goBack()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func goForwardKeyCommand() {
|
||||
if tabManager.selectedTab?.canGoForward == true && homePanelController == nil {
|
||||
tabManager.selectedTab?.goForward()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func findOnPageKeyCommand() {
|
||||
if homePanelController == nil {
|
||||
tab( (tabManager.selectedTab)!, didSelectFindInPageForSelection: "")
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func selectLocationBarKeyCommand() {
|
||||
scrollController.showToolbars(animated: true)
|
||||
urlBar.tabLocationViewDidTapLocation(urlBar.locationView)
|
||||
}
|
||||
|
||||
@objc private func newTabKeyCommand() {
|
||||
openBlankNewTab(focusLocationField: false, isPrivate: false)
|
||||
}
|
||||
|
||||
@objc private func newPrivateTabKeyCommand() {
|
||||
openBlankNewTab(focusLocationField: false, isPrivate: true)
|
||||
}
|
||||
|
||||
@objc private func closeTabKeyCommand() {
|
||||
guard let currentTab = tabManager.selectedTab else {
|
||||
return
|
||||
}
|
||||
tabManager.removeTab(currentTab)
|
||||
}
|
||||
|
||||
@objc private func nextTabKeyCommand() {
|
||||
guard let currentTab = tabManager.selectedTab else {
|
||||
return
|
||||
}
|
||||
|
||||
let tabs = currentTab.isPrivate ? tabManager.privateTabs : tabManager.normalTabs
|
||||
if let index = tabs.index(of: currentTab), index + 1 < tabs.count {
|
||||
tabManager.selectTab(tabs[index + 1])
|
||||
} else if let firstTab = tabs.first {
|
||||
tabManager.selectTab(firstTab)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func previousTabKeyCommand() {
|
||||
guard let currentTab = tabManager.selectedTab else {
|
||||
return
|
||||
}
|
||||
|
||||
let tabs = currentTab.isPrivate ? tabManager.privateTabs : tabManager.normalTabs
|
||||
if let index = tabs.index(of: currentTab), index - 1 < tabs.count && index != 0 {
|
||||
tabManager.selectTab(tabs[index - 1])
|
||||
} else if let lastTab = tabs.last {
|
||||
tabManager.selectTab(lastTab)
|
||||
}
|
||||
}
|
||||
|
||||
override var keyCommands: [UIKeyCommand]? {
|
||||
return [
|
||||
UIKeyCommand(input: "r", modifierFlags: .command, action: #selector(BrowserViewController.reloadTabKeyCommand), discoverabilityTitle: Strings.ReloadPageTitle),
|
||||
UIKeyCommand(input: "[", modifierFlags: .command, action: #selector(BrowserViewController.goBackKeyCommand), discoverabilityTitle: Strings.BackTitle),
|
||||
UIKeyCommand(input: UIKeyInputLeftArrow, modifierFlags: .command, action: #selector(BrowserViewController.goBackKeyCommand), discoverabilityTitle: Strings.BackTitle),
|
||||
UIKeyCommand(input: "]", modifierFlags: .command, action: #selector(BrowserViewController.goForwardKeyCommand), discoverabilityTitle: Strings.ForwardTitle),
|
||||
UIKeyCommand(input: UIKeyInputRightArrow, modifierFlags: .command, action: #selector(BrowserViewController.goForwardKeyCommand), discoverabilityTitle: Strings.ForwardTitle),
|
||||
|
||||
UIKeyCommand(input: "f", modifierFlags: .command, action: #selector(BrowserViewController.findOnPageKeyCommand), discoverabilityTitle: Strings.FindTitle),
|
||||
UIKeyCommand(input: "l", modifierFlags: .command, action: #selector(BrowserViewController.selectLocationBarKeyCommand), discoverabilityTitle: Strings.SelectLocationBarTitle),
|
||||
UIKeyCommand(input: "t", modifierFlags: .command, action: #selector(BrowserViewController.newTabKeyCommand), discoverabilityTitle: Strings.NewTabTitle),
|
||||
UIKeyCommand(input: "p", modifierFlags: [.command, .shift], action: #selector(BrowserViewController.newPrivateTabKeyCommand), discoverabilityTitle: Strings.NewPrivateTabTitle),
|
||||
UIKeyCommand(input: "w", modifierFlags: .command, action: #selector(BrowserViewController.closeTabKeyCommand), discoverabilityTitle: Strings.CloseTabTitle),
|
||||
UIKeyCommand(input: "\t", modifierFlags: .control, action: #selector(BrowserViewController.nextTabKeyCommand), discoverabilityTitle: Strings.ShowNextTabTitle),
|
||||
UIKeyCommand(input: UIKeyInputRightArrow, modifierFlags: [.command, .shift], action: #selector(BrowserViewController.nextTabKeyCommand), discoverabilityTitle: Strings.ShowNextTabTitle),
|
||||
UIKeyCommand(input: "\t", modifierFlags: [.control, .shift], action: #selector(BrowserViewController.previousTabKeyCommand), discoverabilityTitle: Strings.ShowPreviousTabTitle),
|
||||
UIKeyCommand(input: UIKeyInputLeftArrow, modifierFlags: [.command, .shift], action: #selector(BrowserViewController.previousTabKeyCommand), discoverabilityTitle: Strings.ShowPreviousTabTitle),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import Foundation
|
||||
import WebKit
|
||||
import Shared
|
||||
|
||||
private let log = Logger.browserLogger
|
||||
|
||||
extension WKNavigationAction {
|
||||
/// Allow local requests only if the request is privileged.
|
||||
var isAllowed: Bool {
|
||||
guard let url = request.url else {
|
||||
return true
|
||||
}
|
||||
|
||||
return !url.isWebPage(includeDataURIs: false) || !url.isLocal || request.isPrivileged
|
||||
}
|
||||
}
|
||||
|
||||
extension BrowserViewController: WKNavigationDelegate {
|
||||
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
||||
if tabManager.selectedTab?.webView !== webView {
|
||||
return
|
||||
}
|
||||
|
||||
updateFindInPageVisibility(visible: false)
|
||||
|
||||
// If we are going to navigate to a new page, hide the reader mode button. Unless we
|
||||
// are going to a about:reader page. Then we keep it on screen: it will change status
|
||||
// (orange color) as soon as the page has loaded.
|
||||
if let url = webView.url {
|
||||
if !url.isReaderModeURL {
|
||||
urlBar.updateReaderModeState(ReaderModeState.unavailable)
|
||||
hideReaderModeBar(animated: false)
|
||||
}
|
||||
|
||||
// remove the open in overlay view if it is present
|
||||
removeOpenInView()
|
||||
}
|
||||
}
|
||||
|
||||
// Recognize an Apple Maps URL. This will trigger the native app. But only if a search query is present. Otherwise
|
||||
// it could just be a visit to a regular page on maps.apple.com.
|
||||
fileprivate func isAppleMapsURL(_ url: URL) -> Bool {
|
||||
if url.scheme == "http" || url.scheme == "https" {
|
||||
if url.host == "maps.apple.com" && url.query != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Recognize a iTunes Store URL. These all trigger the native apps. Note that appstore.com and phobos.apple.com
|
||||
// used to be in this list. I have removed them because they now redirect to itunes.apple.com. If we special case
|
||||
// them then iOS will actually first open Safari, which then redirects to the app store. This works but it will
|
||||
// leave a 'Back to Safari' button in the status bar, which we do not want.
|
||||
fileprivate func isStoreURL(_ url: URL) -> Bool {
|
||||
if url.scheme == "http" || url.scheme == "https" {
|
||||
if url.host == "itunes.apple.com" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// This is the place where we decide what to do with a new navigation action. There are a number of special schemes
|
||||
// and http(s) urls that need to be handled in a different way. All the logic for that is inside this delegate
|
||||
// method.
|
||||
|
||||
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
||||
guard let url = navigationAction.request.url else {
|
||||
decisionHandler(WKNavigationActionPolicy.cancel)
|
||||
return
|
||||
}
|
||||
|
||||
if url.scheme == "about" {
|
||||
decisionHandler(WKNavigationActionPolicy.allow)
|
||||
return
|
||||
}
|
||||
|
||||
if !navigationAction.isAllowed && navigationAction.navigationType != .backForward {
|
||||
log.warning("Denying unprivileged request: \(navigationAction.request)")
|
||||
decisionHandler(WKNavigationActionPolicy.cancel)
|
||||
return
|
||||
}
|
||||
|
||||
// First special case are some schemes that are about Calling. We prompt the user to confirm this action. This
|
||||
// gives us the exact same behaviour as Safari.
|
||||
if url.scheme == "tel" || url.scheme == "facetime" || url.scheme == "facetime-audio" {
|
||||
UIApplication.shared.openURL(url)
|
||||
decisionHandler(WKNavigationActionPolicy.cancel)
|
||||
return
|
||||
}
|
||||
|
||||
// Second special case are a set of URLs that look like regular http links, but should be handed over to iOS
|
||||
// instead of being loaded in the webview. Note that there is no point in calling canOpenURL() here, because
|
||||
// iOS will always say yes. TODO Is this the same as isWhitelisted?
|
||||
|
||||
if isAppleMapsURL(url) {
|
||||
UIApplication.shared.openURL(url)
|
||||
decisionHandler(WKNavigationActionPolicy.cancel)
|
||||
return
|
||||
}
|
||||
|
||||
if let tab = tabManager.selectedTab, isStoreURL(url) {
|
||||
decisionHandler(WKNavigationActionPolicy.cancel)
|
||||
|
||||
let alreadyShowingSnackbarOnThisTab = tab.bars.count > 0
|
||||
if !alreadyShowingSnackbarOnThisTab {
|
||||
TimerSnackBar.showAppStoreConfirmationBar(forTab: tab, appStoreURL: url)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Handles custom mailto URL schemes.
|
||||
if url.scheme == "mailto" {
|
||||
if let mailToMetadata = url.mailToMetadata(), let mailScheme = self.profile.prefs.stringForKey(PrefsKeys.KeyMailToOption), mailScheme != "mailto" {
|
||||
self.mailtoLinkHandler.launchMailClientForScheme(mailScheme, metadata: mailToMetadata, defaultMailtoURL: url)
|
||||
} else {
|
||||
UIApplication.shared.openURL(url)
|
||||
}
|
||||
|
||||
LeanPlumClient.shared.track(event: .openedMailtoLink)
|
||||
decisionHandler(WKNavigationActionPolicy.cancel)
|
||||
return
|
||||
}
|
||||
|
||||
// This is the normal case, opening a http or https url, which we handle by loading them in this WKWebView. We
|
||||
// always allow this. Additionally, data URIs are also handled just like normal web pages.
|
||||
|
||||
if url.scheme == "http" || url.scheme == "https" || url.scheme == "data" || url.scheme == "blob" {
|
||||
if navigationAction.navigationType == .linkActivated {
|
||||
resetSpoofedUserAgentIfRequired(webView, newURL: url)
|
||||
} else if navigationAction.navigationType == .backForward {
|
||||
restoreSpoofedUserAgentIfRequired(webView, newRequest: navigationAction.request)
|
||||
}
|
||||
decisionHandler(WKNavigationActionPolicy.allow)
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore JS navigated links, the intention is to match Safari and native WKWebView behaviour.
|
||||
if navigationAction.navigationType == .linkActivated {
|
||||
UIApplication.shared.open(url, options: [:]) { openedURL in
|
||||
if !openedURL {
|
||||
let alert = UIAlertController(title: Strings.UnableToOpenURLErrorTitle, message: Strings.UnableToOpenURLError, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: UIConstants.OKString, style: UIAlertActionStyle.default, handler: nil))
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
decisionHandler(WKNavigationActionPolicy.cancel)
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
||||
|
||||
// If this is a certificate challenge, see if the certificate has previously been
|
||||
// accepted by the user.
|
||||
let origin = "\(challenge.protectionSpace.host):\(challenge.protectionSpace.port)"
|
||||
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
|
||||
let trust = challenge.protectionSpace.serverTrust,
|
||||
let cert = SecTrustGetCertificateAtIndex(trust, 0), profile.certStore.containsCertificate(cert, forOrigin: origin) {
|
||||
completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: trust))
|
||||
return
|
||||
}
|
||||
|
||||
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic ||
|
||||
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPDigest ||
|
||||
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodNTLM,
|
||||
let tab = tabManager[webView] else {
|
||||
completionHandler(URLSession.AuthChallengeDisposition.performDefaultHandling, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// If this is a request to our local web server, use our private credentials.
|
||||
if challenge.protectionSpace.host == "localhost" && challenge.protectionSpace.port == Int(WebServer.sharedInstance.server.port) {
|
||||
completionHandler(.useCredential, WebServer.sharedInstance.credentials)
|
||||
return
|
||||
}
|
||||
|
||||
// The challenge may come from a background tab, so ensure it's the one visible.
|
||||
tabManager.selectTab(tab)
|
||||
|
||||
let loginsHelper = tab.getContentScript(name: LoginsHelper.name()) as? LoginsHelper
|
||||
Authenticator.handleAuthRequest(self, challenge: challenge, loginsHelper: loginsHelper).uponQueue(DispatchQueue.main) { res in
|
||||
if let credentials = res.successValue {
|
||||
completionHandler(.useCredential, credentials.credentials)
|
||||
} else {
|
||||
completionHandler(URLSession.AuthChallengeDisposition.rejectProtectionSpace, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
||||
guard let tab = tabManager[webView] else { return }
|
||||
|
||||
tab.url = webView.url
|
||||
self.scrollController.resetZoomState()
|
||||
|
||||
if tabManager.selectedTab === tab {
|
||||
updateUIForReaderHomeStateForTab(tab)
|
||||
}
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||
if let tab = tabManager[webView] {
|
||||
navigateInTab(tab: tab, to: navigation)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue