diff --git a/ios/BUILD.gn b/ios/BUILD.gn index 16a730a76f04..85a8badc311d 100644 --- a/ios/BUILD.gn +++ b/ios/BUILD.gn @@ -21,6 +21,7 @@ import("//brave/ios/browser/api/debounce/headers.gni") import("//brave/ios/browser/api/developer_options_code/headers.gni") import("//brave/ios/browser/api/favicon/headers.gni") import("//brave/ios/browser/api/features/headers.gni") +import("//brave/ios/browser/api/https_upgrade_exceptions/headers.gni") import("//brave/ios/browser/api/ipfs/headers.gni") import("//brave/ios/browser/api/ntp_background_images/headers.gni") import("//brave/ios/browser/api/opentabs/headers.gni") @@ -92,6 +93,8 @@ brave_core_public_headers += browser_api_url_sanitizer_public_headers brave_core_public_headers += browser_url_sanitizer_public_headers brave_core_public_headers += browser_api_de_amp_public_headers brave_core_public_headers += browser_api_debounce_public_headers +brave_core_public_headers += + browser_api_https_upgrade_exceptions_service_public_headers brave_core_public_headers += browser_debounce_public_headers brave_core_public_headers += brave_stats_public_headers brave_core_public_headers += brave_wallet_public_headers diff --git a/ios/app/BUILD.gn b/ios/app/BUILD.gn index d4e9aef05fb8..9bff336d3663 100644 --- a/ios/app/BUILD.gn +++ b/ios/app/BUILD.gn @@ -49,6 +49,7 @@ source_set("app") { "//brave/ios/browser/api/brave_wallet:wallet_mojom_wrappers", "//brave/ios/browser/api/de_amp", "//brave/ios/browser/api/history", + "//brave/ios/browser/api/https_upgrade_exceptions", "//brave/ios/browser/api/ipfs", "//brave/ios/browser/api/ntp_background_images", "//brave/ios/browser/api/opentabs", diff --git a/ios/app/brave_core_main.h b/ios/app/brave_core_main.h index cfe47fc840c6..13b11e3e81a6 100644 --- a/ios/app/brave_core_main.h +++ b/ios/app/brave_core_main.h @@ -26,6 +26,7 @@ @class NTPBackgroundImagesService; @class DeAmpPrefs; @class AIChat; +@class HTTPSUpgradeExceptionsService; @protocol AIChatDelegate; @protocol IpfsAPI; @@ -65,6 +66,9 @@ OBJC_EXPORT @property(nonatomic, readonly) WebImageDownloader* webImageDownloader; +@property(nonatomic, readonly) + HTTPSUpgradeExceptionsService* httpsUpgradeExceptionsService; + /// Sets the global log handler for Chromium & BraveCore logs. /// /// When a custom log handler is set, it is the responsibility of the client diff --git a/ios/app/brave_core_main.mm b/ios/app/brave_core_main.mm index ab1c60fc4ed4..92444d0c96c5 100644 --- a/ios/app/brave_core_main.mm +++ b/ios/app/brave_core_main.mm @@ -34,6 +34,7 @@ #include "brave/ios/browser/api/brave_wallet/brave_wallet_api+private.h" #include "brave/ios/browser/api/de_amp/de_amp_prefs+private.h" #include "brave/ios/browser/api/history/brave_history_api+private.h" +#include "brave/ios/browser/api/https_upgrade_exceptions/https_upgrade_exceptions_service+private.h" #include "brave/ios/browser/api/ipfs/ipfs_api+private.h" #include "brave/ios/browser/api/ntp_background_images/ntp_background_images_service_ios+private.h" #include "brave/ios/browser/api/opentabs/brave_opentabs_api+private.h" @@ -126,6 +127,8 @@ @interface BraveCoreMain () { @property(nonatomic) BraveP3AUtils* p3aUtils; @property(nonatomic) DeAmpPrefs* deAmpPrefs; @property(nonatomic) NTPBackgroundImagesService* backgroundImagesService; +@property(nonatomic) + HTTPSUpgradeExceptionsService* httpsUpgradeExceptionsService; @end @implementation BraveCoreMain @@ -471,6 +474,14 @@ - (BraveWalletAPI*)braveWalletAPI { return _braveWalletAPI; } +- (HTTPSUpgradeExceptionsService*)httpsUpgradeExceptionsService { + if (!_httpsUpgradeExceptionsService) { + _httpsUpgradeExceptionsService = + [[HTTPSUpgradeExceptionsService alloc] init]; + } + return _httpsUpgradeExceptionsService; +} + - (BraveStats*)braveStats { return [[BraveStats alloc] initWithBrowserState:_mainBrowserState]; } diff --git a/ios/brave-ios/App/iOS/Delegates/AppState.swift b/ios/brave-ios/App/iOS/Delegates/AppState.swift index 230666d0a426..745fdb9012b1 100644 --- a/ios/brave-ios/App/iOS/Delegates/AppState.swift +++ b/ios/brave-ios/App/iOS/Delegates/AppState.swift @@ -206,6 +206,7 @@ public class AppState { (IPFSSchemeHandler.path, IPFSSchemeHandler()), (Web3DomainHandler.path, Web3DomainHandler()), (BlockedDomainHandler.path, BlockedDomainHandler()), + (HTTPBlockedHandler.path, HTTPBlockedHandler()), ] responders.forEach { (path, responder) in diff --git a/ios/brave-ios/Package.swift b/ios/brave-ios/Package.swift index cb69faedc7f5..560d84a5db80 100644 --- a/ios/brave-ios/Package.swift +++ b/ios/brave-ios/Package.swift @@ -469,6 +469,7 @@ var braveTarget: PackageDescription.Target = .target( .copy("Assets/Fonts/NewYorkMedium-Regular.otf"), .copy("Assets/Fonts/NewYorkMedium-RegularItalic.otf"), .copy("Assets/Interstitial Pages/Pages/BlockedDomain.html"), + .copy("Assets/Interstitial Pages/Pages/HTTPBlocked.html"), .copy("Assets/Interstitial Pages/Pages/CertificateError.html"), .copy("Assets/Interstitial Pages/Pages/GenericError.html"), .copy("Assets/Interstitial Pages/Pages/NetworkError.html"), diff --git a/ios/brave-ios/Sources/Brave/Assets/Interstitial Pages/Pages/HTTPBlocked.html b/ios/brave-ios/Sources/Brave/Assets/Interstitial Pages/Pages/HTTPBlocked.html new file mode 100644 index 000000000000..c007dacedaf8 --- /dev/null +++ b/ios/brave-ios/Sources/Brave/Assets/Interstitial Pages/Pages/HTTPBlocked.html @@ -0,0 +1,52 @@ + + + + + + + + %page_title% + + + + +
+ Icon + +

%blocked_title%

+

+ %blocked_description% + + %learn_more% + +

+
+ + +
+
+ + + + diff --git a/ios/brave-ios/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+WKNavigationDelegate.swift b/ios/brave-ios/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+WKNavigationDelegate.swift index 616c17312133..add931f469e5 100644 --- a/ios/brave-ios/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+WKNavigationDelegate.swift +++ b/ios/brave-ios/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+WKNavigationDelegate.swift @@ -59,6 +59,8 @@ extension UTType { // MARK: WKNavigationDelegate extension BrowserViewController: WKNavigationDelegate { + private static let log = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "navigation") + public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { if tabManager.selectedTab?.webView !== webView { @@ -314,8 +316,8 @@ extension BrowserViewController: WKNavigationDelegate { tab.loadRequest(modifiedRequest) if let url = modifiedRequest.url { - ContentBlockerManager.log.debug( - "Redirected user to `\(url.absoluteString, privacy: .private)`" + Self.log.debug( + "Redirected to `\(url.absoluteString, privacy: .private)`" ) } @@ -606,6 +608,26 @@ extension BrowserViewController: WKNavigationDelegate { let response = navigationResponse.response let responseURL = response.url let tab = tab(for: webView) + let isInvalid: Bool + if let httpResponse = response as? HTTPURLResponse { + isInvalid = httpResponse.statusCode >= 400 + } else { + isInvalid = true + } + + // Handle invalid upgrade to https + if isInvalid, + navigationResponse.isForMainFrame, + let responseURL = responseURL, + let tab = tab, + let originalResponse = handleInvalidHTTPSUpgrade( + tab: tab, + responseURL: responseURL + ) + { + tab.loadRequest(originalResponse) + return .cancel + } // Store the response in the tab if let responseURL = responseURL { @@ -1001,6 +1023,17 @@ extension BrowserViewController: WKNavigationDelegate { ) { guard let tab = tab(for: webView) else { return } + // Handle invalid upgrade to https + if let responseURL = webView.url, + let response = handleInvalidHTTPSUpgrade( + tab: tab, + responseURL: responseURL + ) + { + tab.loadRequest(response) + return + } + // Ignore the "Frame load interrupted" error that is triggered when we cancel a request // to open an external application and hand it over to UIApplication.openURL(). The result // will be that we switch to the external app, for example the app store, while keeping the @@ -1708,16 +1741,74 @@ extension BrowserViewController: WKUIDelegate { modifiedRequest.setValue(headerValue, forHTTPHeaderField: headerKey) } + Self.log.debug( + "Debouncing `\(requestURL.absoluteString)`" + ) + return modifiedRequest } } // Handle query param stripping - return navigationAction.request.stripQueryParams( + if let request = navigationAction.request.stripQueryParams( initiatorURL: tab.committedURL, redirectSourceURL: tab.redirectSourceURL, isInternalRedirect: tab.isInternalRedirect - ) + ) { + Self.log.debug( + "Stripping query params for `\(requestURL.absoluteString)`" + ) + return request + } + + // Attempt to upgrade to HTTPS + if ShieldPreferences.httpsUpgradeLevel.isEnabled, + let upgradedURL = braveCore.httpsUpgradeExceptionsService.upgradeToHTTPS(for: requestURL) + { + Self.log.debug( + "Upgrading `\(requestURL.absoluteString)` to HTTPS" + ) + + tab.upgradedHTTPSRequest = navigationAction.request + var request = navigationAction.request + request.url = upgradedURL + return request + } + + return nil + } + + /// Upon an invalid response, check that we need to roll back any HTTPS upgrade + /// or show the interstitial page + private func handleInvalidHTTPSUpgrade(tab: Tab, responseURL: URL) -> URLRequest? { + // Handle invalid upgrade to https + guard responseURL.scheme == "https", + let originalRequest = tab.upgradedHTTPSRequest, + let originalURL = originalRequest.url, + responseURL.baseDomain == originalURL.baseDomain + else { + braveCore.httpsUpgradeExceptionsService.addException(for: responseURL) + return nil + } + + if ShieldPreferences.httpsUpgradeLevel.isStrict, + let url = originalURL.encodeEmbeddedInternalURL(for: .httpBlocked) + { + Self.log.debug( + "Show http blocked interstitial for `\(originalURL.absoluteString)`" + ) + + let request = PrivilegedRequest(url: url) as URLRequest + return request + } else { + Self.log.debug( + "Revert HTTPS upgrade for `\(originalURL.absoluteString)`" + ) + + tab.upgradedHTTPSRequest = nil + braveCore.httpsUpgradeExceptionsService.addException(for: originalURL) + return originalRequest + } } } diff --git a/ios/brave-ios/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController.swift b/ios/brave-ios/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController.swift index eeca31ae7de7..86ffa7bd17f5 100644 --- a/ios/brave-ios/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController.swift +++ b/ios/brave-ios/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController.swift @@ -502,6 +502,7 @@ public class BrowserViewController: UIViewController { Preferences.Playlist.syncSharedFoldersAutomatically.observe(from: self) Preferences.NewTabPage.backgroundSponsoredImages.observe(from: self) ShieldPreferences.blockAdsAndTrackingLevelRaw.observe(from: self) + ShieldPreferences.httpsUpgradeLevelRaw.observe(from: self) Preferences.Privacy.screenTimeEnabled.observe(from: self) pageZoomListener = NotificationCenter.default.addObserver( @@ -2658,6 +2659,7 @@ extension BrowserViewController: TabDelegate { ErrorPageHelper(certStore: profile.certStore), SessionRestoreScriptHandler(tab: tab), BlockedDomainScriptHandler(tab: tab), + HTTPBlockedScriptHandler(tab: tab, exceptionService: braveCore.httpsUpgradeExceptionsService), PrintScriptHandler(browserController: self, tab: tab), CustomSearchScriptHandler(tab: tab), NightModeScriptHandler(tab: tab), @@ -3313,7 +3315,7 @@ extension BrowserViewController: PreferencesObserver { ?? Preferences.General.defaultPageZoomLevel.value $0.webView?.setValue(zoomLevel, forKey: PageZoomHandler.propertyName) }) - case Preferences.Shields.httpsEverywhere.key: + case ShieldPreferences.httpsUpgradeLevelRaw.key: tabManager.reset() tabManager.reloadSelectedTab() case Preferences.Privacy.blockAllCookies.key, diff --git a/ios/brave-ios/Sources/Brave/Frontend/Browser/Handlers/HTTPBlockedHandler.swift b/ios/brave-ios/Sources/Brave/Frontend/Browser/Handlers/HTTPBlockedHandler.swift new file mode 100644 index 000000000000..f0cb1820a74e --- /dev/null +++ b/ios/brave-ios/Sources/Brave/Frontend/Browser/Handlers/HTTPBlockedHandler.swift @@ -0,0 +1,72 @@ +// Copyright 2024 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 https://mozilla.org/MPL/2.0/. + +import BraveShared +import BraveShields +import Foundation +import Shared +import WebKit + +public class HTTPBlockedHandler: InternalSchemeResponse { + public static let path = InternalURL.Path.httpBlocked.rawValue + + public init() {} + + public func response(forRequest request: URLRequest) -> (URLResponse, Data)? { + guard let url = request.url, let internalURL = InternalURL(url), + let originalURL = internalURL.extractedUrlParam + else { return nil } + let response = InternalSchemeHandler.response(forUrl: internalURL.url) + + guard let asset = Bundle.module.path(forResource: "HTTPBlocked", ofType: "html") else { + assert(false) + return nil + } + + var html = try? String(contentsOfFile: asset) + .replacingOccurrences( + of: "%page_title%", + with: Strings.Shields.siteIsNotSecure + ) + .replacingOccurrences( + of: "%blocked_title%", + with: String.localizedStringWithFormat( + Strings.Shields.theConnectionIsNotSecure, + "\(originalURL.domainURL.absoluteDisplayString)" + ) + ) + .replacingOccurrences( + of: "%blocked_description%", + with: Strings.Shields.httpBlockedDescription + ) + .replacingOccurrences( + of: "%learn_more%", + with: Strings.learnMore + ) + .replacingOccurrences( + of: "%proceed_action%", + with: Strings.Shields.domainBlockedProceedAction + ) + .replacingOccurrences(of: "%go_back_action%", with: Strings.Shields.domainBlockedGoBackAction) + .replacingOccurrences( + of: "%message_handler%", + with: HTTPBlockedScriptHandler.messageHandlerName + ) + .replacingOccurrences(of: "%security_token%", with: UserScriptManager.securityToken) + + if #available(iOS 16.0, *) { + html = html?.replacingOccurrences( + of: "", + with: "" + ) + } + + guard let data = html?.data(using: .utf8) else { + return nil + } + + return (response, data) + } +} diff --git a/ios/brave-ios/Sources/Brave/Frontend/Browser/Tab.swift b/ios/brave-ios/Sources/Brave/Frontend/Browser/Tab.swift index d537beccdea3..55a44f19a51a 100644 --- a/ios/brave-ios/Sources/Brave/Frontend/Browser/Tab.swift +++ b/ios/brave-ios/Sources/Brave/Frontend/Browser/Tab.swift @@ -3,6 +3,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. import BraveCore +import BraveShields import BraveWallet import CertificateUtilities import Data @@ -302,6 +303,10 @@ class Tab: NSObject { var playlistItem: PlaylistInfo? var playlistItemState: PlaylistItemAddedState = .none + /// This is the request that was upgraded to HTTPS + /// This allows us to rollback the upgrade when we encounter a 4xx+ + var upgradedHTTPSRequest: URLRequest? + /// The tabs new tab page controller. /// /// Should be setup in BVC then assigned here for future use. @@ -459,7 +464,7 @@ class Tab: NSObject { configuration!.allowsInlineMediaPlayback = true // Enables Zoom in website by ignoring their javascript based viewport Scale limits. configuration!.ignoresViewportScaleLimits = true - configuration!.upgradeKnownHostsToHTTPS = Preferences.Shields.httpsEverywhere.value + configuration!.upgradeKnownHostsToHTTPS = ShieldPreferences.httpsUpgradeLevel.isEnabled configuration!.enablePageTopColorSampling() if configuration!.urlSchemeHandler(forURLScheme: InternalURL.scheme) == nil { diff --git a/ios/brave-ios/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/AdvancedShieldSettings.swift b/ios/brave-ios/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/AdvancedShieldSettings.swift index 210a30bd0f0f..563be5edf3b7 100644 --- a/ios/brave-ios/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/AdvancedShieldSettings.swift +++ b/ios/brave-ios/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/AdvancedShieldSettings.swift @@ -77,6 +77,11 @@ import os } } } + @Published var httpsUpgradeLevel: HTTPSUpgradeLevel { + didSet { + ShieldPreferences.httpsUpgradeLevel = httpsUpgradeLevel + } + } typealias ClearDataCallback = @MainActor (Bool, Bool) -> Void @Published var clearableSettings: [ClearableSetting] @@ -105,6 +110,7 @@ import os self.isP3AEnabled = p3aUtilities.isP3AEnabled self.clearDataCallback = clearDataCallback self.adBlockAndTrackingPreventionLevel = ShieldPreferences.blockAdsAndTrackingLevel + self.httpsUpgradeLevel = ShieldPreferences.httpsUpgradeLevel self.isDeAmpEnabled = deAmpPrefs.isDeAmpEnabled self.isDebounceEnabled = debounceService?.isEnabled ?? false diff --git a/ios/brave-ios/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/DefaultShieldsSectionView.swift b/ios/brave-ios/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/DefaultShieldsSectionView.swift index cda63f9f983e..8604f0e5460a 100644 --- a/ios/brave-ios/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/DefaultShieldsSectionView.swift +++ b/ios/brave-ios/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/DefaultShieldsSectionView.swift @@ -38,11 +38,20 @@ struct DefaultShieldsViewView: View { } .listRowBackground(Color(.secondaryBraveGroupedBackground)) - OptionToggleView( - title: Strings.HTTPSEverywhere, - subtitle: Strings.HTTPSEverywhereDescription, - option: Preferences.Shields.httpsEverywhere - ) + Picker(selection: $settings.httpsUpgradeLevel) { + ForEach(HTTPSUpgradeLevel.allCases) { level in + Text(level.localizedTitle) + .foregroundColor(Color(.secondaryBraveLabel)) + .tag(level) + } + } label: { + LabelView( + title: Strings.Shields.upgradeConnectionsToHTTPS, + subtitle: nil + ) + } + .listRowBackground(Color(.secondaryBraveGroupedBackground)) + ToggleView( title: Strings.autoRedirectAMPPages, subtitle: Strings.autoRedirectAMPPagesDescription, @@ -169,3 +178,17 @@ extension ShieldLevel: Identifiable { } } } + +extension HTTPSUpgradeLevel: Identifiable { + public var id: String { + return rawValue + } + + public var localizedTitle: String { + switch self { + case .strict: return Strings.Shields.httpsUpgradeLevelStrict + case .disabled: return Strings.Shields.trackersAndAdsBlockingDisabled + case .standard: return Strings.Shields.trackersAndAdsBlockingStandard + } + } +} diff --git a/ios/brave-ios/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/BlockedDomainScriptHandler.swift b/ios/brave-ios/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/BlockedDomainScriptHandler.swift index d8f7d3d5b82c..bfdac0f56e41 100644 --- a/ios/brave-ios/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/BlockedDomainScriptHandler.swift +++ b/ios/brave-ios/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/BlockedDomainScriptHandler.swift @@ -75,6 +75,7 @@ class BlockedDomainScriptHandler: TabContentScript { // All testing indicates no, so we will not handle. // If we find it is, then we need to disable or hide the "Go Back" button in these cases. // But this would require heavy changes or ugly mechanisms to InternalSchemeHandler. + tab?.goBack() return } diff --git a/ios/brave-ios/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/HTTPBlockedScriptHandler.swift b/ios/brave-ios/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/HTTPBlockedScriptHandler.swift new file mode 100644 index 000000000000..694460466c79 --- /dev/null +++ b/ios/brave-ios/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/HTTPBlockedScriptHandler.swift @@ -0,0 +1,88 @@ +// Copyright 2023 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 https://mozilla.org/MPL/2.0/. + +import BraveCore +import Foundation +import Shared +import WebKit + +class HTTPBlockedScriptHandler: TabContentScript { + private weak var tab: Tab? + private weak var exceptionService: HTTPSUpgradeExceptionsService? + + required init(tab: Tab, exceptionService: HTTPSUpgradeExceptionsService) { + self.tab = tab + self.exceptionService = exceptionService + } + + static let scriptName = "HTTPBlockedScript" + static let scriptId = UUID().uuidString + static let messageHandlerName = "\(scriptName)_\(messageUUID)" + static let scriptSandbox: WKContentWorld = .page + static let userScript: WKUserScript? = nil + + func userContentController( + _ userContentController: WKUserContentController, + didReceiveScriptMessage message: WKScriptMessage, + replyHandler: (Any?, String?) -> Void + ) { + defer { replyHandler(nil, nil) } + + if !verifyMessage(message: message, securityToken: UserScriptManager.securityToken) { + assertionFailure("Missing required security token.") + return + } + + guard let params = message.body as? [String: AnyObject], + let action = params["action"] as? String + else { + assertionFailure("Missing required params.") + return + } + + switch action { + case "didProceed": + didProceed() + case "didGoBack": + didGoBack() + default: + assertionFailure("Unhandled action `\(action)`") + } + } + + private func didProceed() { + guard let url = tab?.upgradedHTTPSRequest?.url ?? tab?.url?.strippedInternalURL else { + // assertionFailure( + // "There should be no way this method can be triggered if the tab is not on an internal url" + // ) + return + } + + // When restoring the page, `upgradedHTTPSRequest` will be nil + // So we default to the embedded internal page URL + let request = tab?.upgradedHTTPSRequest ?? URLRequest(url: url) + exceptionService?.addException(for: url) + tab?.loadRequest(request) + } + + private func didGoBack() { + let etldP1 = + tab?.upgradedHTTPSRequest?.url?.baseDomain + ?? tab?.url?.strippedInternalURL?.baseDomain + + guard + let listItem = tab?.backList?.reversed().first(where: { + // It is not the blocked page or the internal page + $0.url.baseDomain != etldP1 && $0.url != tab?.webView?.url + }) + else { + tab?.goBack() + return + } + + tab?.upgradedHTTPSRequest = nil + tab?.goToBackForwardListItem(listItem) + } +} diff --git a/ios/brave-ios/Sources/Brave/Migration/Migration.swift b/ios/brave-ios/Sources/Brave/Migration/Migration.swift index 895b3b40f9c4..084fa85ab4fb 100644 --- a/ios/brave-ios/Sources/Brave/Migration/Migration.swift +++ b/ios/brave-ios/Sources/Brave/Migration/Migration.swift @@ -24,6 +24,7 @@ public class Migration { Preferences.migratePreferences(keyPrefix: keyPrefix) Preferences.migrateWalletPreferences() Preferences.migrateAdAndTrackingProtection() + Preferences.migrateHTTPSUpgradeLevel() if Preferences.General.isFirstLaunch.value { if UIDevice.current.userInterfaceIdiom == .phone { @@ -169,6 +170,12 @@ extension Preferences { key: "shields.block-ads-and-tracking", default: true ) + + /// Websites will be upgraded to HTTPS if a loaded page attempts to use HTTP + public static let httpsEverywhere = Option( + key: "shields.https-everywhere", + default: true + ) } /// Migration preferences @@ -198,6 +205,13 @@ extension Preferences { default: false ) + /// A more complicated https upgrades preference + /// allows a user to select between `standard`, `strict` and `disabled` instead of a simple on/off `Bool` + static let httpsUpgradesLivelCompleted = Option( + key: "migration.https-upgrades-level-completed", + default: false + ) + static let lostTabsWindowIDMigration = Option( key: "migration.lost-tabs-window-id-two", default: !UIApplication.shared.supportsMultipleScenes @@ -271,7 +285,7 @@ extension Preferences { // Shields migrate(key: "braveBlockAdsAndTracking", to: DeprecatedPreferences.blockAdsAndTracking) - migrate(key: "braveHttpsEverywhere", to: Preferences.Shields.httpsEverywhere) + migrate(key: "braveHttpsEverywhere", to: DeprecatedPreferences.httpsEverywhere) migrate(key: "noscript_on", to: Preferences.Shields.blockScripts) migrate(key: "fingerprintprotection_on", to: Preferences.Shields.fingerprintingProtection) migrate(key: "braveAdblockUseRegional", to: Preferences.Shields.useRegionAdBlock) @@ -313,6 +327,17 @@ extension Preferences { Migration.adBlockAndTrackingProtectionShieldLevelCompleted.value = true } + fileprivate class func migrateHTTPSUpgradeLevel() { + guard !Migration.httpsUpgradesLivelCompleted.value else { return } + + // Migrate old tracking protection setting to new BraveShields setting + DeprecatedPreferences.httpsEverywhere.migrate { isEnabled in + ShieldPreferences.httpsUpgradeLevel = isEnabled ? .standard : .disabled + } + + Migration.adBlockAndTrackingProtectionShieldLevelCompleted.value = true + } + /// Migrate Wallet Preferences from version <1.43 fileprivate class func migrateWalletPreferences() { guard Preferences.Migration.walletProviderAccountRequestCompleted.value != true else { return } diff --git a/ios/brave-ios/Sources/BraveShared/Extensions/URLExtensions.swift b/ios/brave-ios/Sources/BraveShared/Extensions/URLExtensions.swift index 9836e3d35ee0..4809de2b7dc8 100644 --- a/ios/brave-ios/Sources/BraveShared/Extensions/URLExtensions.swift +++ b/ios/brave-ios/Sources/BraveShared/Extensions/URLExtensions.swift @@ -28,10 +28,7 @@ extension URL { /// /// Returns the original url without internal parameters public var strippedInternalURL: URL? { - if InternalURL.isValid(url: self), - let internalURL = InternalURL(self) - { - + if let internalURL = InternalURL(self) { switch internalURL.urlType { case .errorPage: return internalURL.originalURLFromErrorPage @@ -39,6 +36,8 @@ extension URL { return internalURL.extractedUrlParam case .blockedPage: return decodeEmbeddedInternalURL(for: .blocked) + case .httpBlockedPage: + return decodeEmbeddedInternalURL(for: .httpBlocked) case .readerModePage: return decodeEmbeddedInternalURL(for: .readermode) default: @@ -69,7 +68,8 @@ extension URL { } if let internalUrl = InternalURL(self), - internalUrl.isSessionRestore || internalUrl.isWeb3URL || internalUrl.isBlockedPage + internalUrl.isSessionRestore || internalUrl.isWeb3URL || internalUrl.isHTTPBlockedPage + || internalUrl.isBlockedPage { return internalUrl.extractedUrlParam?.displayURL } @@ -138,6 +138,7 @@ extension InternalURL { enum URLType { case blockedPage + case httpBlockedPage case sessionRestorePage case errorPage case readerModePage @@ -147,6 +148,13 @@ extension InternalURL { } var urlType: URLType { + // This needs to be before `isBlockedPage` + // because http-blocked has the word "blocked" in it + // We should refactor this code because its really iffy. + if isHTTPBlockedPage { + return .httpBlockedPage + } + if isBlockedPage { return .blockedPage } diff --git a/ios/brave-ios/Sources/BraveShields/HTTPSUpgradeLevel.swift b/ios/brave-ios/Sources/BraveShields/HTTPSUpgradeLevel.swift new file mode 100644 index 000000000000..c26470e2d1f8 --- /dev/null +++ b/ios/brave-ios/Sources/BraveShields/HTTPSUpgradeLevel.swift @@ -0,0 +1,32 @@ +// Copyright 2024 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 https://mozilla.org/MPL/2.0/. + +import Foundation + +/// A 3 part option for shield levels varying in strength of blocking content +public enum HTTPSUpgradeLevel: String, CaseIterable, Hashable { + /// Always upgrade websites and block websites that cannot be upgraded + case strict + /// Always upgrade websites but allow http when it cannot be upgraded + case standard + /// Mode indicating this setting is disabled + case disabled + + /// Wether this setting indicates that the shields are enabled or not + public var isEnabled: Bool { + switch self { + case .strict, .standard: return true + case .disabled: return false + } + } + + /// Wether this setting indicates that the shields are aggressive or not + public var isStrict: Bool { + switch self { + case .strict: return true + case .disabled, .standard: return false + } + } +} diff --git a/ios/brave-ios/Sources/BraveShields/ShieldPreferences.swift b/ios/brave-ios/Sources/BraveShields/ShieldPreferences.swift index 92b52d37b965..ba7c86576047 100644 --- a/ios/brave-ios/Sources/BraveShields/ShieldPreferences.swift +++ b/ios/brave-ios/Sources/BraveShields/ShieldPreferences.swift @@ -6,8 +6,10 @@ import Foundation import Preferences +/// All the settings pertaining to shields and privacy public class ShieldPreferences { private static let defaultBlockAdsAndTrackingLevel: ShieldLevel = .standard + private static let defaultHTTPsUpgradeLevel: HTTPSUpgradeLevel = .standard /// Get the level of the adblock and tracking protection as a stored preference /// - Warning: You should not access this directly but through ``blockAdsAndTrackingLevel`` @@ -16,6 +18,13 @@ public class ShieldPreferences { default: defaultBlockAdsAndTrackingLevel.rawValue ) + /// Get the level of the https upgrade setting as a stored preference + /// - Warning: You should not access this directly but through ``httpsUpgradeLevel`` + public static var httpsUpgradeLevelRaw = Preferences.Option( + key: "shields.https-upgrade-level", + default: defaultHTTPsUpgradeLevel.rawValue + ) + /// Get the level of the adblock and tracking protection public static var blockAdsAndTrackingLevel: ShieldLevel { get { @@ -24,6 +33,14 @@ public class ShieldPreferences { set { blockAdsAndTrackingLevelRaw.value = newValue.rawValue } } + /// Get the level of HTTPS upgrades + public static var httpsUpgradeLevel: HTTPSUpgradeLevel { + get { + HTTPSUpgradeLevel(rawValue: httpsUpgradeLevelRaw.value) ?? defaultHTTPsUpgradeLevel + } + set { httpsUpgradeLevelRaw.value = newValue.rawValue } + } + /// A boolean value inidicating if GPC is enabled public static var enableGPC = Preferences.Option( key: "shields.enable-gpc", diff --git a/ios/brave-ios/Sources/BraveShields/ShieldStrings.swift b/ios/brave-ios/Sources/BraveShields/ShieldStrings.swift index 87190c56a8f8..59a3f38f0104 100644 --- a/ios/brave-ios/Sources/BraveShields/ShieldStrings.swift +++ b/ios/brave-ios/Sources/BraveShields/ShieldStrings.swift @@ -317,3 +317,51 @@ extension Strings.Shields { "Text for a button in a blocked page info screen that takes you back where you came from" ) } + +// MARK: - HTTPS Upgrades + +extension Strings.Shields { + /// The option the user can select to do aggressive ad and tracker blocking + public static let httpsUpgradeLevelStrict = NSLocalizedString( + "HttpsUpgradeLevelStrict", + tableName: "BraveShared", + bundle: .module, + value: "Strict", + comment: "The option the user can select to do strict https upgrading" + ) + /// The option the user can select for the type of https upgrading + public static let upgradeConnectionsToHTTPS = NSLocalizedString( + "UpgradeConnectionsToHTTPS", + tableName: "BraveShared", + bundle: .module, + value: "Upgrade Connections to HTTPS", + comment: "The option the user can select for the type of https upgrading" + ) + + /// A page title for the warning page that appears when http was blocked + public static let siteIsNotSecure = NSLocalizedString( + "SiteIsNotSecure", + tableName: "BraveShared", + bundle: .module, + value: "Site is not secure", + comment: "A page title for the warning page that appears when http was blocked" + ) + + /// A page title for the warning page that appears when http was blocked + public static let theConnectionIsNotSecure = NSLocalizedString( + "TheConnectionIsNotSecure", + tableName: "BraveShared", + bundle: .module, + value: "The connection to %@ is not secure", + comment: "A page title for the warning page that appears when http was blocked" + ) + + /// A tab title that appears when a page was blocked + public static let httpBlockedDescription = NSLocalizedString( + "YourConnectionIsNotPrivate", + tableName: "BraveShared", + bundle: .module, + value: "You are seeing this warning because this site does not support HTTPS.", + comment: "A description shown an a page where the http page was blocked" + ) +} diff --git a/ios/brave-ios/Sources/Preferences/GlobalPreferences.swift b/ios/brave-ios/Sources/Preferences/GlobalPreferences.swift index 59df5f968ef2..a4dbe461279d 100644 --- a/ios/brave-ios/Sources/Preferences/GlobalPreferences.swift +++ b/ios/brave-ios/Sources/Preferences/GlobalPreferences.swift @@ -40,10 +40,8 @@ extension Preferences { public final class Shields { public static let allShields = [ - httpsEverywhere, googleSafeBrowsing, blockScripts, fingerprintingProtection, blockImages, + googleSafeBrowsing, blockScripts, fingerprintingProtection, blockImages, ] - /// Websites will be upgraded to HTTPS if a loaded page attempts to use HTTP - public static let httpsEverywhere = Option(key: "shields.https-everywhere", default: true) /// Enable Google Safe Browsing public static let googleSafeBrowsing = Option( key: "shields.google-safe-browsing", diff --git a/ios/brave-ios/Sources/Shared/Extensions/URLExtensions.swift b/ios/brave-ios/Sources/Shared/Extensions/URLExtensions.swift index f7ff8f1dd23f..b87f9e06d585 100644 --- a/ios/brave-ios/Sources/Shared/Extensions/URLExtensions.swift +++ b/ios/brave-ios/Sources/Shared/Extensions/URLExtensions.swift @@ -467,6 +467,7 @@ public struct InternalURL { case sessionrestore case readermode = "reader-mode" case blocked + case httpBlocked = "http-blocked" func matches(_ string: String) -> Bool { return string.range( @@ -547,6 +548,10 @@ public struct InternalURL { return InternalURL.Path.blocked.matches(url.path) } + public var isHTTPBlockedPage: Bool { + return InternalURL.Path.httpBlocked.matches(url.path) + } + public var isReaderModePage: Bool { return InternalURL.Path.readermode.matches(url.path) } diff --git a/ios/brave-ios/Tests/BraveSharedTests/URLExtensionTests.swift b/ios/brave-ios/Tests/BraveSharedTests/URLExtensionTests.swift index 34f337f03d18..7ed457576c2f 100644 --- a/ios/brave-ios/Tests/BraveSharedTests/URLExtensionTests.swift +++ b/ios/brave-ios/Tests/BraveSharedTests/URLExtensionTests.swift @@ -49,11 +49,15 @@ class URLExtensionTests: XCTestCase { XCTAssertEqual( embeddedURL.encodeEmbeddedInternalURL(for: .readermode)?.strippedInternalURL, - URL(string: "https://en.m.wikipedia.org/wiki/Main_Page?somequery=abc-123") + embeddedURL ) XCTAssertEqual( embeddedURL.encodeEmbeddedInternalURL(for: .blocked)?.strippedInternalURL, - URL(string: "https://en.m.wikipedia.org/wiki/Main_Page?somequery=abc-123") + embeddedURL + ) + XCTAssertEqual( + embeddedURL.encodeEmbeddedInternalURL(for: .httpBlocked)?.strippedInternalURL, + embeddedURL ) XCTAssertNil(embeddedURL.strippedInternalURL) } @@ -116,15 +120,19 @@ class URLExtensionTests: XCTestCase { XCTAssertEqual( embeddedURL.encodeEmbeddedInternalURL(for: .readermode)?.displayURL, - URL(string: "https://en.m.wikipedia.org/wiki/Main_Page?somequery=abc-123") + embeddedURL ) XCTAssertEqual( embeddedURL.encodeEmbeddedInternalURL(for: .blocked)?.displayURL, - URL(string: "https://en.m.wikipedia.org/wiki/Main_Page?somequery=abc-123") + embeddedURL + ) + XCTAssertEqual( + embeddedURL.encodeEmbeddedInternalURL(for: .httpBlocked)?.displayURL, + embeddedURL ) XCTAssertEqual( embeddedURL.displayURL, - URL(string: "https://en.m.wikipedia.org/wiki/Main_Page?somequery=abc-123") + embeddedURL ) } } diff --git a/ios/browser/api/https_upgrade_exceptions/BUILD.gn b/ios/browser/api/https_upgrade_exceptions/BUILD.gn new file mode 100644 index 000000000000..230408dc90a9 --- /dev/null +++ b/ios/browser/api/https_upgrade_exceptions/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright (c) 2024 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 https://mozilla.org/MPL/2.0/. + +source_set("https_upgrade_exceptions") { + sources = [ + "https_upgrade_exceptions_service+private.h", + "https_upgrade_exceptions_service.h", + "https_upgrade_exceptions_service.mm", + ] + deps = [ + "//base", + "//brave/components/https_upgrade_exceptions/browser", + "//brave/ios/browser/application_context", + "//ios/chrome/browser/shared/model/application_context", + "//net", + "//url", + ] + frameworks = [ "Foundation.framework" ] +} diff --git a/ios/browser/api/https_upgrade_exceptions/headers.gni b/ios/browser/api/https_upgrade_exceptions/headers.gni new file mode 100644 index 000000000000..b075c1ae253a --- /dev/null +++ b/ios/browser/api/https_upgrade_exceptions/headers.gni @@ -0,0 +1,6 @@ +# Copyright (c) 2024 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 https://mozilla.org/MPL/2.0/. + +browser_api_https_upgrade_exceptions_service_public_headers = [ "//brave/ios/browser/api/https_upgrade_exceptions/https_upgrade_exceptions_service.h" ] diff --git a/ios/browser/api/https_upgrade_exceptions/https_upgrade_exceptions_service+private.h b/ios/browser/api/https_upgrade_exceptions/https_upgrade_exceptions_service+private.h new file mode 100644 index 000000000000..e2b33eb35574 --- /dev/null +++ b/ios/browser/api/https_upgrade_exceptions/https_upgrade_exceptions_service+private.h @@ -0,0 +1,14 @@ +// Copyright (c) 2024 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 https://mozilla.org/MPL/2.0/. + +#ifndef BRAVE_IOS_BROWSER_API_HTTPS_UPGRADE_EXCEPTIONS_HTTPS_UPGRADE_EXCEPTIONS_SERVICE_PRIVATE_H_ +#define BRAVE_IOS_BROWSER_API_HTTPS_UPGRADE_EXCEPTIONS_HTTPS_UPGRADE_EXCEPTIONS_SERVICE_PRIVATE_H_ + +#include "brave/ios/browser/api/https_upgrade_exceptions/https_upgrade_exceptions_service.h" + +@interface HTTPSUpgradeExceptionsService (Private) +@end + +#endif // BRAVE_IOS_BROWSER_API_HTTPS_UPGRADE_EXCEPTIONS_HTTPS_UPGRADE_EXCEPTIONS_SERVICE_PRIVATE_H_ diff --git a/ios/browser/api/https_upgrade_exceptions/https_upgrade_exceptions_service.h b/ios/browser/api/https_upgrade_exceptions/https_upgrade_exceptions_service.h new file mode 100644 index 000000000000..57b12a6f4a58 --- /dev/null +++ b/ios/browser/api/https_upgrade_exceptions/https_upgrade_exceptions_service.h @@ -0,0 +1,28 @@ +// Copyright (c) 2024 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 https://mozilla.org/MPL/2.0/. + +#ifndef BRAVE_IOS_BROWSER_API_HTTPS_UPGRADE_EXCEPTIONS_HTTPS_UPGRADE_EXCEPTIONS_SERVICE_H_ +#define BRAVE_IOS_BROWSER_API_HTTPS_UPGRADE_EXCEPTIONS_HTTPS_UPGRADE_EXCEPTIONS_SERVICE_H_ + +#import + +NS_ASSUME_NONNULL_BEGIN + +OBJC_EXPORT +@interface HTTPSUpgradeExceptionsService : NSObject +/// Tells us if the "HTTPS by default" feature is enabled +@property(readonly) bool isHttpsByDefaultFeatureEnabled; + +- (instancetype)init; +/// This returns a new URL with HTTPS if the url can be upgraded to HTTPS +- (nullable NSURL*)upgradeToHTTPSForURL:(NSURL*)url; +/// Add an exception for the URL so it will no longer upgrade it to HTTPS +/// It will use the host of the URL to add the exception +- (void)addExceptionForURL:(NSURL*)url; +@end + +NS_ASSUME_NONNULL_END + +#endif // BRAVE_IOS_BROWSER_API_HTTPS_UPGRADE_EXCEPTIONS_HTTPS_UPGRADE_EXCEPTIONS_SERVICE_H_ diff --git a/ios/browser/api/https_upgrade_exceptions/https_upgrade_exceptions_service.mm b/ios/browser/api/https_upgrade_exceptions/https_upgrade_exceptions_service.mm new file mode 100644 index 000000000000..eedac8fcb539 --- /dev/null +++ b/ios/browser/api/https_upgrade_exceptions/https_upgrade_exceptions_service.mm @@ -0,0 +1,82 @@ +// Copyright (c) 2024 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 https://mozilla.org/MPL/2.0/. + +#include "brave/ios/browser/api/https_upgrade_exceptions/https_upgrade_exceptions_service+private.h" + +#include "base/containers/contains.h" +#include "base/feature_list.h" +#include "base/memory/raw_ptr.h" +#include "brave/components/https_upgrade_exceptions/browser/https_upgrade_exceptions_service.h" +#include "brave/ios/browser/application_context/brave_application_context_impl.h" +#include "ios/chrome/browser/shared/model/application_context/application_context.h" +#include "net/base/apple/url_conversions.h" +#include "net/base/features.h" +#include "url/gurl.h" +#include "url/origin.h" + +@interface HTTPSUpgradeExceptionsService () { + /// set of domain exceptions + std::set exceptional_domains_; +} + +@property(nonatomic) bool isHttpsByDefaultFeatureEnabled; +@end + +@implementation HTTPSUpgradeExceptionsService +- (instancetype)init { + if ((self = [super init])) { + } + return self; +} + +- (bool)isHttpsByDefaultFeatureEnabled { + return base::FeatureList::IsEnabled(net::features::kBraveHttpsByDefault); +} + +- (NSURL*)upgradeToHTTPSForURL:(NSURL*)url { + GURL gurl = net::GURLWithNSURL(url); + if (![self isHttpsByDefaultFeatureEnabled]) { + return nil; + } + + // Check url validity + if (!gurl.SchemeIs("http") || !gurl.is_valid()) { + return nil; + } + + // Ensure we didn't add an exception for this domain + if (base::Contains(exceptional_domains_, gurl.host())) { + return nil; + } + + // Finally ask the service if we should upgrade + BraveApplicationContextImpl* braveContext = + static_cast(GetApplicationContext()); + if (!braveContext->https_upgrade_exceptions_service()->CanUpgradeToHTTPS( + gurl)) { + return nil; + } + + GURL::Replacements replacements; + replacements.SetSchemeStr("https"); + GURL final_url = gurl.ReplaceComponents(replacements); + + if (final_url.is_valid()) { + return net::NSURLWithGURL(final_url); + } else { + return nil; + } +} + +- (void)addExceptionForURL:(NSURL*)url { + GURL gurl = net::GURLWithNSURL(url); + + if (gurl.is_empty()) { + return; + } + + exceptional_domains_.insert(gurl.host()); +} +@end diff --git a/ios/browser/application_context/BUILD.gn b/ios/browser/application_context/BUILD.gn index c62ff763033d..79d4859cf074 100644 --- a/ios/browser/application_context/BUILD.gn +++ b/ios/browser/application_context/BUILD.gn @@ -16,9 +16,11 @@ source_set("application_context") { "//brave/components/brave_component_updater/browser", "//brave/components/brave_wallet/browser", "//brave/components/debounce/core/browser", + "//brave/components/https_upgrade_exceptions/browser", "//brave/components/url_sanitizer/browser", "//brave/ios/browser/brave_wallet", "//ios/chrome/browser/application_context/model", "//ios/chrome/browser/shared/model/application_context", + "//net", ] } diff --git a/ios/browser/application_context/brave_application_context_impl.h b/ios/browser/application_context/brave_application_context_impl.h index 254527661f99..de137628a564 100644 --- a/ios/browser/application_context/brave_application_context_impl.h +++ b/ios/browser/application_context/brave_application_context_impl.h @@ -11,6 +11,7 @@ #include "brave/components/brave_component_updater/browser/brave_component.h" #include "brave/components/debounce/core/browser/debounce_component_installer.h" +#include "brave/components/https_upgrade_exceptions/browser/https_upgrade_exceptions_service.h" #include "brave/components/url_sanitizer/browser/url_sanitizer_component_installer.h" #include "ios/chrome/browser/application_context/model/application_context_impl.h" @@ -42,6 +43,8 @@ class BraveApplicationContextImpl : public ApplicationContextImpl { // BraveApplicationContextImpl brave::URLSanitizerComponentInstaller* url_sanitizer_component_installer(); debounce::DebounceComponentInstaller* debounce_component_installer(); + https_upgrade_exceptions::HttpsUpgradeExceptionsService* + https_upgrade_exceptions_service(); // Start any services that we may need later void StartBraveServices(); @@ -59,6 +62,8 @@ class BraveApplicationContextImpl : public ApplicationContextImpl { url_sanitizer_component_installer_; std::unique_ptr debounce_component_installer_; + std::unique_ptr + https_upgrade_exceptions_service_; }; #endif // BRAVE_IOS_BROWSER_APPLICATION_CONTEXT_BRAVE_APPLICATION_CONTEXT_IMPL_H_ diff --git a/ios/browser/application_context/brave_application_context_impl.mm b/ios/browser/application_context/brave_application_context_impl.mm index 54762ce98bbc..be632c6a56a4 100644 --- a/ios/browser/application_context/brave_application_context_impl.mm +++ b/ios/browser/application_context/brave_application_context_impl.mm @@ -14,9 +14,11 @@ #include "brave/components/brave_component_updater/browser/local_data_files_service.h" #include "brave/components/brave_wallet/browser/wallet_data_files_installer.h" #include "brave/components/debounce/core/browser/debounce_component_installer.h" +#include "brave/components/https_upgrade_exceptions/browser/https_upgrade_exceptions_service.h" #include "brave/components/url_sanitizer/browser/url_sanitizer_component_installer.h" #include "brave/ios/browser/brave_wallet/wallet_data_files_installer_delegate_impl.h" #include "ios/chrome/browser/shared/model/application_context/application_context.h" +#include "net/base/features.h" BraveApplicationContextImpl::BraveApplicationContextImpl( base::SequencedTaskRunner* local_state_task_runner, @@ -84,12 +86,26 @@ return debounce_component_installer_.get(); } +https_upgrade_exceptions::HttpsUpgradeExceptionsService* +BraveApplicationContextImpl::https_upgrade_exceptions_service() { + if (!https_upgrade_exceptions_service_) { + https_upgrade_exceptions_service_ = + https_upgrade_exceptions::HttpsUpgradeExceptionsServiceFactory( + local_data_files_service()); + } + return https_upgrade_exceptions_service_.get(); +} + void BraveApplicationContextImpl::StartBraveServices() { // We need to Initialize the component installers // before calling Start on the local_data_files_service url_sanitizer_component_installer(); debounce_component_installer(); + if (base::FeatureList::IsEnabled(net::features::kBraveHttpsByDefault)) { + https_upgrade_exceptions_service(); + } + // Start the local data file service local_data_files_service()->Start();