diff --git a/BraveShareTo/ShareToBraveViewController.swift b/BraveShareTo/ShareToBraveViewController.swift index 536b2ffa0d3..11d5fdba73f 100644 --- a/BraveShareTo/ShareToBraveViewController.swift +++ b/BraveShareTo/ShareToBraveViewController.swift @@ -8,11 +8,47 @@ import MobileCoreServices import BraveShared class ShareToBraveViewController: SLComposeServiceViewController { + private struct Scheme { + private enum SchemeType { + case url, query + } + + private let type: SchemeType + private let urlOrQuery: String + + init?(item: NSSecureCoding) { + if let text = item as? String { + urlOrQuery = text + type = .query + } else if let url = (item as? URL)?.absoluteString.firstURL?.absoluteString { + urlOrQuery = url + type = .url + } else { + return nil + } + } + + var schemeUrl: URL? { + var components = URLComponents() + let queryItem: URLQueryItem + + components.scheme = "brave" + + switch type { + case .url: + components.host = "open-url" + queryItem = URLQueryItem(name: "url", value: urlOrQuery) + case .query: + components.host = "search" + queryItem = URLQueryItem(name: "q", value: urlOrQuery) + } + + components.queryItems = [queryItem] + return components.url + } + } // TODO: Separate scheme for debug builds, so it can be tested without need to uninstall production app. - private func urlScheme(for url: String) -> URL? { - return URL(string: "brave://open-url?url=\(url)") - } override func configurationItems() -> [Any]! { guard let inputItems = extensionContext?.inputItems as? [NSExtensionItem] else { @@ -34,24 +70,12 @@ class ShareToBraveViewController: SLComposeServiceViewController { } provider.loadItem(of: provider.isUrl ? kUTTypeURL : kUTTypeText) { item, error in - var urlItem: URL? - - // We can get urls from other apps as a kUTTypeText type, for example from Apple's mail.app. - if let text = item as? String { - urlItem = text.firstURL - } else if let url = item as? URL { - urlItem = url.absoluteString.firstURL - } else { + guard let item = item, let schemeUrl = Scheme(item: item)?.schemeUrl else { self.cancel() return - } - - // Just open the app if we don't find a url. In the future we could - // use this entry point to search instead of open a given URL - let urlString = urlItem?.absoluteString ?? "" - if let braveUrl = urlString.addingPercentEncoding(withAllowedCharacters: .alphanumerics).flatMap(self.urlScheme) { - self.handleUrl(braveUrl) - } + } + + self.handleUrl(schemeUrl) } return [] diff --git a/Client/Application/NavigationRouter.swift b/Client/Application/NavigationRouter.swift index 51c67587866..ff7a81fa488 100644 --- a/Client/Application/NavigationRouter.swift +++ b/Client/Application/NavigationRouter.swift @@ -60,6 +60,9 @@ enum NavigationPath: Equatable { } else if urlString.starts(with: "\(scheme)://open-text") { let text = components.valueForQuery("text") self = .text(text ?? "") + } else if urlString.starts(with: "\(scheme)://search") { + let text = components.valueForQuery("q") + self = .text(text ?? "") } else { return nil } diff --git a/Client/Frontend/Browser/BrowserViewController.swift b/Client/Frontend/Browser/BrowserViewController.swift index 73a081b4eaf..c9c0f778dc9 100644 --- a/Client/Frontend/Browser/BrowserViewController.swift +++ b/Client/Frontend/Browser/BrowserViewController.swift @@ -1725,7 +1725,7 @@ class BrowserViewController: UIViewController { // This let's the user spam the Cmd+T button without lots of responder changes. guard freshTab == self.tabManager.selectedTab else { return } if let text = searchText { - self.topToolbar.setLocation(text, search: true) + self.topToolbar.submitLocation(text) } } } diff --git a/Client/Frontend/Browser/Toolbars/UrlBar/TopToolbarView.swift b/Client/Frontend/Browser/Toolbars/UrlBar/TopToolbarView.swift index 0ab51870aad..93ba033126b 100644 --- a/Client/Frontend/Browser/Toolbars/UrlBar/TopToolbarView.swift +++ b/Client/Frontend/Browser/Toolbars/UrlBar/TopToolbarView.swift @@ -392,6 +392,15 @@ class TopToolbarView: UIView, ToolbarProtocol { } } + func submitLocation(_ location: String?) { + locationTextField?.text = location + guard let text = location, !text.isEmpty else { + return + } + // Not notifying when empty agrees with AutocompleteTextField.textDidChange. + delegate?.topToolbar(self, didSubmitText: text) + } + func enterOverlayMode(_ locationText: String?, pasted: Bool, search: Bool) { createLocationTextField() diff --git a/ClientTests/NavigationRouterTests.swift b/ClientTests/NavigationRouterTests.swift index d9caf851c32..3bbb4d0f062 100644 --- a/ClientTests/NavigationRouterTests.swift +++ b/ClientTests/NavigationRouterTests.swift @@ -42,6 +42,14 @@ class NavigationRouterTests: XCTestCase { XCTAssertEqual(badNav, NavigationPath.url(webURL: URL(string: "blah"), isPrivate: false)) } + func testSearchScheme() { + let query = "Foo Bar".addingPercentEncoding(withAllowedCharacters: .alphanumerics)! + let appURL = "\(appScheme)://search?q="+query + let navItem = NavigationPath(url: URL(string: appURL)!)! + + XCTAssertEqual(navItem, NavigationPath.text("Foo Bar")) + } + func testDefaultNavigationPath() { let url = URL(string: "https://duckduckgo.com")! let appURL = URL(string: "\(self.appScheme)://open-url?url=\(url.absoluteString.escape()!)")!