From 5df4eb4eeeb7b87eb847d8ef504d0d614777f924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Buczek?= Date: Tue, 15 Jun 2021 11:50:37 +0200 Subject: [PATCH] Fix #3641, fix #3745 add Brave Search engine, add BraveSearch default SE callbacks. (#3535) --- Client.xcodeproj/project.pbxproj | 8 ++ Client/Application/ClientPreferences.swift | 3 + Client/Assets/SearchPlugins/bravesearch.xml | 19 +++ .../Frontend/Browser/BraveSearchHelper.swift | 122 ++++++++++++++++++ .../Browser/BrowserViewController.swift | 2 + .../BrowserViewController+OpenSearch.swift | 2 +- .../Frontend/Browser/DomainUserScript.swift | 24 +++- .../Browser/Search/InitialSearchEngines.swift | 13 +- .../Frontend/Browser/Search/OpenSearch.swift | 1 + .../Browser/Search/SearchEngines.swift | 2 +- .../Frontend/Browser/UserScriptManager.swift | 2 +- .../UserScripts/BraveSearchHelper.js | 50 +++++++ ClientTests/InitialSearchEnginesTests.swift | 9 ++ 13 files changed, 246 insertions(+), 11 deletions(-) create mode 100644 Client/Assets/SearchPlugins/bravesearch.xml create mode 100644 Client/Frontend/Browser/BraveSearchHelper.swift create mode 100644 Client/Frontend/UserContent/UserScripts/BraveSearchHelper.js diff --git a/Client.xcodeproj/project.pbxproj b/Client.xcodeproj/project.pbxproj index f3eda8a63fb..7b7446c5eef 100644 --- a/Client.xcodeproj/project.pbxproj +++ b/Client.xcodeproj/project.pbxproj @@ -162,6 +162,8 @@ 0ADCD45B231973650078CC67 /* UserAgentBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ADCD45A231973650078CC67 /* UserAgentBuilderTests.swift */; }; 0ADCD45F2319799F0078CC67 /* RollingFileLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E61453BD1B750A1700C3F9D7 /* RollingFileLoggerTests.swift */; }; 0AE16F8D251BEF8C00A688ED /* InitialSearchEnginesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE16F8C251BEF8C00A688ED /* InitialSearchEnginesTests.swift */; }; + 0AE50854261C63D70099C6A3 /* BraveSearchHelper.js in Resources */ = {isa = PBXBuildFile; fileRef = 0AE50853261C63D70099C6A3 /* BraveSearchHelper.js */; }; + 0AE5086A261C6F2E0099C6A3 /* BraveSearchHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE50869261C6F2E0099C6A3 /* BraveSearchHelper.swift */; }; 0AE5C09922CAA01E00DFF3EE /* RewardsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE5C09822CAA01E00DFF3EE /* RewardsButton.swift */; }; 0AE5C69124F0059D004CBC9B /* OnboardingPrivacyConsentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE5C69024F0059D004CBC9B /* OnboardingPrivacyConsentViewController.swift */; }; 0AE5C69424F005F9004CBC9B /* OnboardingPrivacyConsentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE5C69324F005F9004CBC9B /* OnboardingPrivacyConsentView.swift */; }; @@ -1532,6 +1534,8 @@ 0ADCD4512319640F0078CC67 /* UserAgentBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentBuilder.swift; sourceTree = ""; }; 0ADCD45A231973650078CC67 /* UserAgentBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentBuilderTests.swift; sourceTree = ""; }; 0AE16F8C251BEF8C00A688ED /* InitialSearchEnginesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialSearchEnginesTests.swift; sourceTree = ""; }; + 0AE50853261C63D70099C6A3 /* BraveSearchHelper.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = BraveSearchHelper.js; sourceTree = ""; }; + 0AE50869261C6F2E0099C6A3 /* BraveSearchHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BraveSearchHelper.swift; sourceTree = ""; }; 0AE5C09822CAA01E00DFF3EE /* RewardsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RewardsButton.swift; sourceTree = ""; }; 0AE5C69024F0059D004CBC9B /* OnboardingPrivacyConsentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPrivacyConsentViewController.swift; sourceTree = ""; }; 0AE5C69324F005F9004CBC9B /* OnboardingPrivacyConsentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPrivacyConsentView.swift; sourceTree = ""; }; @@ -4769,6 +4773,7 @@ D0FCF7E81FE44D8F004A7995 /* AllFrames */, D0FCF7E91FE44DA2004A7995 /* MainFrame */, 595E0EDA21CAD97C00813D49 /* CookieControl.js */, + 0AE50853261C63D70099C6A3 /* BraveSearchHelper.js */, F930CDAB227000F200A23FE1 /* U2F.js */, F99505FE22937E3900CC6543 /* U2F-low-level.js */, 5E3477E822D7771700B0D5F8 /* ResourceDownloader.js */, @@ -4962,6 +4967,7 @@ 0A8A6224257905E300B035F4 /* UniversalLinkManager.swift */, 4452CAEF255412800053EFE6 /* DefaultBrowserIntroCalloutViewController.swift */, 0A0A5ED025B1F080007B3E74 /* DefaultBrowserIntroManager.swift */, + 0AE50869261C6F2E0099C6A3 /* BraveSearchHelper.swift */, ); indentWidth = 4; path = Browser; @@ -6306,6 +6312,7 @@ 0A5E04F923FEADA800E5A3E9 /* LaunchScreen.storyboard in Resources */, 0AB22A8A257EADA900126ADC /* corwin-prescott_olympic.jpg in Resources */, 0AB22A8F257EADA900126ADC /* su-san-lee.jpg in Resources */, + 0AE50854261C63D70099C6A3 /* BraveSearchHelper.js in Resources */, 5DB474F1237F4CC9007B7652 /* ntp-data.json in Resources */, 595E0EDB21CAD97C00813D49 /* CookieControl.js in Resources */, 0A0D3D3C21A4BE6400BEE65B /* adblock-list.txt in Resources */, @@ -7197,6 +7204,7 @@ 0A4BEF5D221AF1910005551A /* AdblockResourceDownloader.swift in Sources */, 0BA1E02E1B046F1E007675AF /* ErrorPageHelper.swift in Sources */, 5EC2C07325BF988E005EA984 /* PlaylistCell.swift in Sources */, + 0AE5086A261C6F2E0099C6A3 /* BraveSearchHelper.swift in Sources */, D3A9949C1A3686BD008AD1AC /* BrowserViewController.swift in Sources */, 59A681BDFC95A19F05E07223 /* SearchViewController.swift in Sources */, 0A8C6993225BC7B100988715 /* ToolbarUrlActionsProtocol.swift in Sources */, diff --git a/Client/Application/ClientPreferences.swift b/Client/Application/ClientPreferences.swift index a53bd179f03..4e20bddc720 100644 --- a/Client/Application/ClientPreferences.swift +++ b/Client/Application/ClientPreferences.swift @@ -147,6 +147,9 @@ extension Preferences { static let shouldShowRecentSearches = Option(key: "search.should-show-recent-searches", default: false) /// Whether or not to show recent searches opt-in static let shouldShowRecentSearchesOptIn = Option(key: "search.should-show-recent-searches.opt-in", default: true) + /// How many times Brave Search websites has asked the user to check whether Brave Search can be set as a default + static let braveSearchDefaultBrowserPromptCount = + Option(key: "search.brave-search-default-website-prompt", default: 0) } final class Privacy { /// Forces all private tabs diff --git a/Client/Assets/SearchPlugins/bravesearch.xml b/Client/Assets/SearchPlugins/bravesearch.xml new file mode 100644 index 00000000000..41115fc448a --- /dev/null +++ b/Client/Assets/SearchPlugins/bravesearch.xml @@ -0,0 +1,19 @@ + + + +Brave Search beta +UTF-8 +data:image/png;base64,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 + + + + + + + + +https://search.brave.com + diff --git a/Client/Frontend/Browser/BraveSearchHelper.swift b/Client/Frontend/Browser/BraveSearchHelper.swift new file mode 100644 index 00000000000..e2fe092cdd3 --- /dev/null +++ b/Client/Frontend/Browser/BraveSearchHelper.swift @@ -0,0 +1,122 @@ +// Copyright 2021 The Brave Authors. All rights reserved. +// 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 +import BraveShared + +private let log = Logger.browserLogger + +class BraveSearchHelper: TabContentScript { + private weak var tab: Tab? + private let profile: Profile + + /// Tracks how many in current browsing session the user has been prompted to set Brave Search as a default + /// while on one of Brave Search websites. + private static var canSetAsDefaultCounter = 0 + /// How many times user should be shown the default browser prompt on Brave Search websites. + private let maxCountOfDefaultBrowserPromptsPerSession = 3 + /// How many times user is shown the default browser prompt in total, this does not reset between app launches. + private let maxCountOfDefaultBrowserPromptsTotal = 10 + + required init(tab: Tab, profile: Profile) { + self.tab = tab + self.profile = profile + } + + static func name() -> String { "BraveSearchHelper" } + + func scriptMessageHandlerName() -> String? { BraveSearchHelper.name() } + + private enum Method: Int { + case canSetBraveSearchAsDefault = 1 + case setBraveSearchDefault = 2 + } + + private struct MethodModel: Codable { + enum CodingKeys: String, CodingKey { + case methodId = "method_id" + } + + let methodId: Int + } + + func userContentController(_ userContentController: WKUserContentController, + didReceiveScriptMessage message: WKScriptMessage) { + let allowedHosts = ["search.brave.com", + "search-dev.brave.com", + "search-dev-local.brave.com", + "search.brave.software", + "search.bravesoftware.com"] + + guard let requestHost = message.frameInfo.request.url?.host, + allowedHosts.contains(requestHost), + message.frameInfo.isMainFrame else { + log.error("Backup search request called from disallowed host") + return + } + + guard let data = try? JSONSerialization.data(withJSONObject: message.body, options: []), + let method = try? JSONDecoder().decode(MethodModel.self, from: data).methodId else { + log.error("Failed to retrieve method id") + return + } + + switch method { + case Method.canSetBraveSearchAsDefault.rawValue: + handleCanSetBraveSearchAsDefault(methodId: method) + case Method.setBraveSearchDefault.rawValue: + handleSetBraveSearchDefault(methodId: method) + default: + break + } + } + + private func handleCanSetBraveSearchAsDefault(methodId: Int) { + + if PrivateBrowsingManager.shared.isPrivateBrowsing { + log.debug("Private mode detected, skipping setting Brave Search as a default") + callback(methodId: methodId, result: false) + return + } + + let maximumPromptCount = Preferences.Search.braveSearchDefaultBrowserPromptCount + if Self.canSetAsDefaultCounter >= maxCountOfDefaultBrowserPromptsPerSession || + maximumPromptCount.value >= maxCountOfDefaultBrowserPromptsTotal { + log.debug("Maximum number of tries of Brave Search website prompts reached") + callback(methodId: methodId, result: false) + return + } + + Self.canSetAsDefaultCounter += 1 + maximumPromptCount.value += 1 + + let defaultEngine = profile.searchEngines.defaultEngine(forType: .standard).shortName + let canSetAsDefault = defaultEngine != OpenSearchEngine.EngineNames.brave + + callback(methodId: methodId, result: canSetAsDefault) + } + + private func handleSetBraveSearchDefault(methodId: Int) { + profile.searchEngines.updateDefaultEngine(OpenSearchEngine.EngineNames.brave, forType: .standard) + callback(methodId: methodId, result: nil) + } + + private func callback(methodId: Int, result: Bool?) { + let functionName = + "window.__firefox__.BSH\(UserScriptManager.messageHandlerTokenString).resolve" + + var args: [Any] = [methodId] + if let result = result { + args.append(result) + } + + self.tab?.webView?.evaluateSafeJavaScript( + functionName: functionName, + args: args, + sandboxed: false) + } +} diff --git a/Client/Frontend/Browser/BrowserViewController.swift b/Client/Frontend/Browser/BrowserViewController.swift index e089d04f9ba..24932d77fb9 100644 --- a/Client/Frontend/Browser/BrowserViewController.swift +++ b/Client/Frontend/Browser/BrowserViewController.swift @@ -2045,6 +2045,8 @@ extension BrowserViewController: TabDelegate { tab.addContentScript(FingerprintingProtection(tab: tab), name: FingerprintingProtection.name(), sandboxed: false) tab.addContentScript(BraveGetUA(tab: tab), name: BraveGetUA.name(), sandboxed: false) + tab.addContentScript(BraveSearchHelper(tab: tab, profile: profile), + name: BraveSearchHelper.name(), sandboxed: false) if YubiKitDeviceCapabilities.supportsMFIAccessoryKey { tab.addContentScript(U2FExtensions(tab: tab), name: U2FExtensions.name(), sandboxed: false) diff --git a/Client/Frontend/Browser/BrowserViewController/OpenSearch/BrowserViewController+OpenSearch.swift b/Client/Frontend/Browser/BrowserViewController/OpenSearch/BrowserViewController+OpenSearch.swift index 5c991535195..c4dda158dfd 100644 --- a/Client/Frontend/Browser/BrowserViewController/OpenSearch/BrowserViewController+OpenSearch.swift +++ b/Client/Frontend/Browser/BrowserViewController/OpenSearch/BrowserViewController+OpenSearch.swift @@ -49,7 +49,7 @@ extension BrowserViewController { // Add Reference Object as Open Search Engine openSearchEngine = referenceObject - // Open Search guidlines requires Title to be same as Short Name but it is not enforced, + // Open Search guidelines requires Title to be same as Short Name but it is not enforced, // thus in case of yahoo.com the title is 'Yahoo Search' and Shortname is 'Yahoo' // We are checking referenceURL match to determine searchEngine is added or not // In addition we are also checking if there is another engine with same name diff --git a/Client/Frontend/Browser/DomainUserScript.swift b/Client/Frontend/Browser/DomainUserScript.swift index 16530ba1674..273827c45ed 100644 --- a/Client/Frontend/Browser/DomainUserScript.swift +++ b/Client/Frontend/Browser/DomainUserScript.swift @@ -13,6 +13,7 @@ private let log = Logger.browserLogger enum DomainUserScript: CaseIterable { case youtube case archive + case braveSearch static func get(for domain: String) -> Self? { var found: DomainUserScript? @@ -33,7 +34,7 @@ enum DomainUserScript: CaseIterable { switch self { case .youtube: return .AdblockAndTp - case .archive: + case .archive, .braveSearch: return nil } } @@ -44,6 +45,8 @@ enum DomainUserScript: CaseIterable { return .init(arrayLiteral: "youtube.com") case .archive: return .init(arrayLiteral: "archive.is", "archive.today", "archive.vn", "archive.fo") + case .braveSearch: + return .init(arrayLiteral: "brave.com") } } @@ -53,14 +56,16 @@ enum DomainUserScript: CaseIterable { return "YoutubeAdblock" case .archive: return "ArchiveIsCompat" + case .braveSearch: + return "BraveSearchHelper" } } var script: WKUserScript? { + guard let source = sourceFile else { return nil } + switch self { case .youtube: - guard let source = sourceFile else { return nil } - // Verify that the application itself is making a call to the JS script instead of other scripts on the page. // This variable will be unique amongst scripts loaded in the page. // When the script is called, the token is provided in order to access the script variable. @@ -76,8 +81,19 @@ enum DomainUserScript: CaseIterable { return WKUserScript(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false) case .archive: - guard let source = sourceFile else { return nil } return WKUserScript(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: false) + case .braveSearch: + var alteredSource = source + + let securityToken = UserScriptManager.securityToken.uuidString + .replacingOccurrences(of: "-", with: "", options: .literal) + alteredSource = alteredSource + .replacingOccurrences(of: "$", + with: "BSH\(UserScriptManager.messageHandlerTokenString)", + options: .literal) + .replacingOccurrences(of: "$", with: securityToken) + + return WKUserScript(source: alteredSource, injectionTime: .atDocumentStart, forMainFrameOnly: false) } } diff --git a/Client/Frontend/Browser/Search/InitialSearchEngines.swift b/Client/Frontend/Browser/Search/InitialSearchEngines.swift index e54a8c994d1..dc212143b6f 100644 --- a/Client/Frontend/Browser/Search/InitialSearchEngines.swift +++ b/Client/Frontend/Browser/Search/InitialSearchEngines.swift @@ -10,14 +10,17 @@ import Foundation class InitialSearchEngines { /// Type of search engine available to the user. enum SearchEngineID: String { - case google, bing, duckduckgo, yandex, qwant, startpage, yahoo, ecosia + case google, braveSearch, bing, duckduckgo, yandex, qwant, startpage, yahoo, ecosia - var excludedFromOnboarding: Bool { + func excludedFromOnboarding(for locale: Locale) -> Bool { switch self { case .google, .bing, .duckduckgo, .yandex, .qwant, .startpage, .ecosia: return false case .yahoo: return true + case .braveSearch: + guard let region = locale.regionCode else { return true } + return !InitialSearchEngines.braveSearchOnboardingRegions.contains(region) } } } @@ -50,9 +53,10 @@ class InitialSearchEngines { /// Lists of engines available during onboarding. var onboardingEngines: [SearchEngine] { - engines.filter { !$0.id.excludedFromOnboarding } + engines.filter { !$0.id.excludedFromOnboarding(for: locale) } } + static let braveSearchOnboardingRegions = ["US", "CA"] static let ddgDefaultRegions = ["DE", "AU", "NZ", "IE"] static let qwantDefaultRegions = ["FR"] static let yandexDefaultRegions = ["AM", "AZ", "BY", "KG", "KZ", "MD", "RU", "TJ", "TM", "TZ"] @@ -90,7 +94,8 @@ class InitialSearchEngines { self.locale = locale // Default order and available search engines, applies to all locales - engines = [.init(id: .google), + engines = [.init(id: .braveSearch), + .init(id: .google), .init(id: .bing), .init(id: .duckduckgo), .init(id: .qwant), diff --git a/Client/Frontend/Browser/Search/OpenSearch.swift b/Client/Frontend/Browser/Search/OpenSearch.swift index a8bd0829b90..3459b8e25e2 100644 --- a/Client/Frontend/Browser/Search/OpenSearch.swift +++ b/Client/Frontend/Browser/Search/OpenSearch.swift @@ -16,6 +16,7 @@ class OpenSearchEngine: NSObject, NSSecureCoding { struct EngineNames { static let duckDuckGo = "DuckDuckGo" static let qwant = "Qwant" + static let brave = "Brave Search beta" } static let defaultSearchClientName = "brave" diff --git a/Client/Frontend/Browser/Search/SearchEngines.swift b/Client/Frontend/Browser/Search/SearchEngines.swift index 03ae56b59b4..b6e7185d2f2 100644 --- a/Client/Frontend/Browser/Search/SearchEngines.swift +++ b/Client/Frontend/Browser/Search/SearchEngines.swift @@ -295,7 +295,7 @@ class SearchEngines { let se = InitialSearchEngines() let engines = isOnboarding ? se.onboardingEngines : se.engines - let engineNames = engines.map { $0.customId ?? $0.id.rawValue } + let engineNames = engines.map { ($0.customId ?? $0.id.rawValue).lowercased() } assert(!engineNames.isEmpty, "No search engines") return engineNames.map({ (name: $0, path: pluginDirectory.appendingPathComponent("\($0).xml").path) }) diff --git a/Client/Frontend/Browser/UserScriptManager.swift b/Client/Frontend/Browser/UserScriptManager.swift index 473c3cddb19..f7b1830d9a1 100644 --- a/Client/Frontend/Browser/UserScriptManager.swift +++ b/Client/Frontend/Browser/UserScriptManager.swift @@ -233,7 +233,7 @@ class UserScriptManager { alteredSource = alteredSource.replacingOccurrences(of: "$", with: "ResourceDownloadManager\(messageHandlerTokenString)", options: .literal) return WKUserScript(source: alteredSource, injectionTime: .atDocumentEnd, forMainFrameOnly: false) - }() + }() private let WindowRenderHelperScript: WKUserScript? = { guard let path = Bundle.main.path(forResource: "WindowRenderHelper", ofType: "js"), let source = try? String(contentsOfFile: path) else { diff --git a/Client/Frontend/UserContent/UserScripts/BraveSearchHelper.js b/Client/Frontend/UserContent/UserScripts/BraveSearchHelper.js new file mode 100644 index 00000000000..aa81d71e1c5 --- /dev/null +++ b/Client/Frontend/UserContent/UserScripts/BraveSearchHelper.js @@ -0,0 +1,50 @@ +// Copyright 2021 The Brave Authors. All rights reserved. +// 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/. + +'use strict'; + +Object.defineProperty(window.__firefox__, '$', { + enumerable: false, + configurable: true, + writable: false, + value: { + id: 1, + resolution_handlers: {}, + resolve(id, data, error) { + if (error && window.__firefox__.$.resolution_handlers[id].reject) { + window.__firefox__.$.resolution_handlers[id].reject(error); + } else if (window.__firefox__.$.resolution_handlers[id].resolve) { + window.__firefox__.$.resolution_handlers[id].resolve(data); + } else if (window.__firefox__.$.resolution_handlers[id].reject) { + window.__firefox__.$.resolution_handlers[id].reject(new Error("Invalid Data!")); + } else { + console.log("Invalid Promise ID: ", id); + } + + delete window.__firefox__.$.resolution_handlers[id]; + }, + sendMessage(method_id) { + return new Promise((resolve, reject) => { + window.__firefox__.$.resolution_handlers[method_id] = { resolve, reject }; + webkit.messageHandlers.BraveSearchHelper.postMessage({ 'securitytoken': '$' ,'method_id': method_id}); + }); + } + } +}); + +Object.defineProperty(window, 'brave', { +enumerable: false, +configurable: true, +writable: false, + value: { + getCanSetDefaultSearchProvider() { + return window.__firefox__.$.sendMessage(1); + }, + + setIsDefaultSearchProvider() { + return window.__firefox__.$.sendMessage(2); + } +} +}); diff --git a/ClientTests/InitialSearchEnginesTests.swift b/ClientTests/InitialSearchEnginesTests.swift index 640f0fa74a2..45e70523b0f 100644 --- a/ClientTests/InitialSearchEnginesTests.swift +++ b/ClientTests/InitialSearchEnginesTests.swift @@ -15,6 +15,7 @@ class InitialSearchEnginesTests: XCTestCase { let engines = unknownLocaleSE.engines.map { $0.id } XCTAssertEqual(engines, [.google, + .braveSearch, .bing, .duckduckgo, .qwant, @@ -66,6 +67,7 @@ class InitialSearchEnginesTests: XCTestCase { let availableEngines = localeSE.engines.map { $0.id } XCTAssertEqual(availableEngines, [.google, + .braveSearch, .bing, .duckduckgo, .qwant, @@ -75,6 +77,7 @@ class InitialSearchEnginesTests: XCTestCase { let onboardingEngines = localeSE.onboardingEngines.map { $0.id } XCTAssertEqual(onboardingEngines, [.google, + .braveSearch, .bing, .duckduckgo, .qwant, @@ -92,6 +95,7 @@ class InitialSearchEnginesTests: XCTestCase { let availableEngines = localeSE.engines.map { $0.id } XCTAssertEqual(availableEngines, [.google, + .braveSearch, .bing, .duckduckgo, .qwant, @@ -114,6 +118,7 @@ class InitialSearchEnginesTests: XCTestCase { let localeSE = SE(locale: Locale(identifier: "en_GB")) let availableEngines = localeSE.engines.map { $0.id } XCTAssertEqual(availableEngines, [.google, + .braveSearch, .bing, .duckduckgo, .qwant, @@ -138,6 +143,7 @@ class InitialSearchEnginesTests: XCTestCase { let availableEngines = localeSE.engines.map { $0.id } XCTAssertEqual(availableEngines, [.duckduckgo, + .braveSearch, .google, .bing, .qwant, @@ -162,6 +168,7 @@ class InitialSearchEnginesTests: XCTestCase { let availableEngines = localeSE.engines.map { $0.id } XCTAssertEqual(availableEngines, [.qwant, + .braveSearch, .google, .bing, .duckduckgo, @@ -186,6 +193,7 @@ class InitialSearchEnginesTests: XCTestCase { let availableEngines = unknownLocaleSE.engines.map { $0.id } XCTAssertEqual(availableEngines, [.google, + .braveSearch, .bing, .duckduckgo, .qwant, @@ -207,6 +215,7 @@ class InitialSearchEnginesTests: XCTestCase { let availableEngines = russianLocale.engines.map { $0.id } XCTAssertEqual(availableEngines, [.yandex, + .braveSearch, .google, .bing, .duckduckgo,