diff --git a/chromium_src/ios/CPPLINT.cfg b/chromium_src/ios/CPPLINT.cfg new file mode 100644 index 00000000000..5c959f3835e --- /dev/null +++ b/chromium_src/ios/CPPLINT.cfg @@ -0,0 +1,2 @@ +# Common linting false posititives from Obj-C syntax in headers +filter=-readability/casting,-whitespace/parens,-whitespace/operators diff --git a/chromium_src/ios/chrome/browser/DEPS b/chromium_src/ios/chrome/browser/DEPS index b6dc5a082ac..2f286419316 100644 --- a/chromium_src/ios/chrome/browser/DEPS +++ b/chromium_src/ios/chrome/browser/DEPS @@ -3,6 +3,8 @@ include_rules = [ "+brave/common", "+brave/components/ai_chat", "+brave/components/constants", + "+brave/components/https_upgrade_exceptions/browser", + "+brave/ios/browser/application_context", "+components/component_updater", "+components/gcm_driver/gcm_driver.h", "+components/history/core/browser", diff --git a/chromium_src/ios/chrome/browser/profile/model/keyed_service_factories.mm b/chromium_src/ios/chrome/browser/profile/model/keyed_service_factories.mm index aeb867f7222..08b9861f328 100644 --- a/chromium_src/ios/chrome/browser/profile/model/keyed_service_factories.mm +++ b/chromium_src/ios/chrome/browser/profile/model/keyed_service_factories.mm @@ -6,13 +6,16 @@ #include "ios/chrome/browser/profile/model/keyed_service_factories.h" #include "brave/ios/browser/profile/model/brave_keyed_service_factories.h" +#include "ios/chrome/browser/affiliations/model/ios_chrome_affiliation_service_factory.h" #include "ios/chrome/browser/autocomplete/model/autocomplete_classifier_factory.h" #include "ios/chrome/browser/autocomplete/model/zero_suggest_cache_service_factory.h" +#include "ios/chrome/browser/autofill/model/autofill_log_router_factory.h" #include "ios/chrome/browser/autofill/model/personal_data_manager_factory.h" #include "ios/chrome/browser/bookmarks/model/account_bookmark_sync_service_factory.h" #include "ios/chrome/browser/bookmarks/model/bookmark_model_factory.h" #include "ios/chrome/browser/bookmarks/model/bookmark_undo_service_factory.h" #include "ios/chrome/browser/bookmarks/model/local_or_syncable_bookmark_sync_service_factory.h" +#include "ios/chrome/browser/browsing_data/model/browsing_data_remover_factory.h" #include "ios/chrome/browser/consent_auditor/model/consent_auditor_factory.h" #include "ios/chrome/browser/content_settings/model/host_content_settings_map_factory.h" #include "ios/chrome/browser/credential_provider/model/credential_provider_buildflags.h" @@ -22,25 +25,33 @@ #include "ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.h" #include "ios/chrome/browser/favicon/model/ios_chrome_large_icon_cache_factory.h" #include "ios/chrome/browser/favicon/model/ios_chrome_large_icon_service_factory.h" +#include "ios/chrome/browser/gcm/model/ios_chrome_gcm_profile_service_factory.h" #include "ios/chrome/browser/history/model/history_service_factory.h" #include "ios/chrome/browser/history/model/top_sites_factory.h" #include "ios/chrome/browser/history/model/web_history_service_factory.h" #include "ios/chrome/browser/https_upgrades/model/https_upgrade_service_factory.h" #include "ios/chrome/browser/invalidation/model/ios_chrome_profile_invalidation_provider_factory.h" +#include "ios/chrome/browser/language/model/accept_languages_service_factory.h" +#include "ios/chrome/browser/language/model/language_model_manager_factory.h" +#include "ios/chrome/browser/language/model/url_language_histogram_factory.h" #include "ios/chrome/browser/metrics/model/google_groups_manager_factory.h" #include "ios/chrome/browser/optimization_guide/model/optimization_guide_service_factory.h" #include "ios/chrome/browser/page_info/about_this_site_service_factory.h" #include "ios/chrome/browser/passwords/model/ios_chrome_account_password_store_factory.h" +#include "ios/chrome/browser/passwords/model/ios_chrome_bulk_leak_check_service_factory.h" #include "ios/chrome/browser/passwords/model/ios_chrome_password_receiver_service_factory.h" #include "ios/chrome/browser/passwords/model/ios_chrome_password_reuse_manager_factory.h" #include "ios/chrome/browser/passwords/model/ios_chrome_password_sender_service_factory.h" #include "ios/chrome/browser/passwords/model/ios_chrome_profile_password_store_factory.h" #include "ios/chrome/browser/passwords/model/ios_password_manager_settings_service_factory.h" +#include "ios/chrome/browser/passwords/model/ios_password_requirements_service_factory.h" +#include "ios/chrome/browser/passwords/model/password_manager_log_router_factory.h" #include "ios/chrome/browser/plus_addresses/model/plus_address_service_factory.h" #include "ios/chrome/browser/plus_addresses/model/plus_address_setting_service_factory.h" #include "ios/chrome/browser/power_bookmarks/model/power_bookmark_service_factory.h" #include "ios/chrome/browser/push_notification/model/push_notification_profile_service_factory.h" #include "ios/chrome/browser/reading_list/model/reading_list_model_factory.h" +#include "ios/chrome/browser/safe_browsing/model/safe_browsing_client_factory.h" #include "ios/chrome/browser/safe_browsing/model/safe_browsing_metrics_collector_factory.h" #include "ios/chrome/browser/saved_tab_groups/model/tab_group_sync_service_factory.h" #include "ios/chrome/browser/search_engines/model/template_url_service_factory.h" @@ -50,6 +61,7 @@ #include "ios/chrome/browser/signin/model/account_consistency_service_factory.h" #include "ios/chrome/browser/signin/model/account_reconcilor_factory.h" #include "ios/chrome/browser/signin/model/identity_manager_factory.h" +#include "ios/chrome/browser/signin/model/signin_client_factory.h" #include "ios/chrome/browser/supervised_user/model/child_account_service_factory.h" #include "ios/chrome/browser/supervised_user/model/list_family_members_service_factory.h" #include "ios/chrome/browser/supervised_user/model/supervised_user_service_factory.h" @@ -70,20 +82,22 @@ #endif void EnsureProfileKeyedServiceFactoriesBuilt() { + autofill::AutofillLogRouterFactory::GetInstance(); autofill::PersonalDataManagerFactory::GetInstance(); data_sharing::DataSharingServiceFactory::GetInstance(); ios::AccountBookmarkSyncServiceFactory::GetInstance(); ios::AccountConsistencyServiceFactory::GetInstance(); ios::AccountReconcilorFactory::GetInstance(); + ios::AutocompleteClassifierFactory::GetInstance(); ios::BookmarkModelFactory::GetInstance(); ios::BookmarkUndoServiceFactory::GetInstance(); ios::FaviconServiceFactory::GetInstance(); ios::HistoryServiceFactory::GetInstance(); - ios::LocalOrSyncableBookmarkSyncServiceFactory::GetInstance(); - ios::TopSitesFactory::GetInstance(); - ios::AutocompleteClassifierFactory::GetInstance(); ios::HostContentSettingsMapFactory::GetInstance(); + ios::LocalOrSyncableBookmarkSyncServiceFactory::GetInstance(); + ios::PasswordManagerLogRouterFactory::GetInstance(); ios::TemplateURLServiceFactory::GetInstance(); + ios::TopSitesFactory::GetInstance(); ios::WebDataServiceFactory::GetInstance(); ios::WebHistoryServiceFactory::GetInstance(); ios::ZeroSuggestCacheServiceFactory::GetInstance(); @@ -91,26 +105,33 @@ void EnsureProfileKeyedServiceFactoriesBuilt() { tab_groups::TabGroupSyncServiceFactory::GetInstance(); translate::TranslateRankerFactory::GetInstance(); AboutThisSiteServiceFactory::GetInstance(); + AcceptLanguagesServiceFactory::GetInstance(); BackgroundDownloadServiceFactory::GetInstance(); BrowserListFactory::GetInstance(); + BrowsingDataRemoverFactory::GetInstance(); ChildAccountServiceFactory::GetInstance(); ConsentAuditorFactory::GetInstance(); DeviceInfoSyncServiceFactory::GetInstance(); GoogleGroupsManagerFactory::GetInstance(); + HttpsUpgradeServiceFactory::GetInstance(); IdentityManagerFactory::GetInstance(); IOSChromeAccountPasswordStoreFactory::GetInstance(); + IOSChromeAffiliationServiceFactory::GetInstance(); + IOSChromeBulkLeakCheckServiceFactory::GetInstance(); IOSChromeFaviconLoaderFactory::GetInstance(); + IOSChromeGCMProfileServiceFactory::GetInstance(); IOSChromeLargeIconCacheFactory::GetInstance(); IOSChromeLargeIconServiceFactory::GetInstance(); IOSChromePasswordReceiverServiceFactory::GetInstance(); IOSChromePasswordReuseManagerFactory::GetInstance(); IOSChromePasswordSenderServiceFactory::GetInstance(); - IOSChromeProfilePasswordStoreFactory::GetInstance(); IOSChromeProfileInvalidationProviderFactory::GetInstance(); + IOSChromeProfilePasswordStoreFactory::GetInstance(); IOSPasskeyModelFactory::GetInstance(); IOSPasswordManagerSettingsServiceFactory::GetInstance(); + IOSPasswordRequirementsServiceFactory::GetInstance(); IOSUserEventServiceFactory::GetInstance(); - HttpsUpgradeServiceFactory::GetInstance(); + LanguageModelManagerFactory::GetInstance(); ListFamilyMembersServiceFactory::GetInstance(); OptimizationGuideServiceFactory::GetInstance(); DataTypeStoreServiceFactory::GetInstance(); @@ -122,11 +143,17 @@ void EnsureProfileKeyedServiceFactoriesBuilt() { SyncServiceFactory::GetInstance(); UnifiedConsentServiceFactory::GetInstance(); ReadingListModelFactory::GetInstance(); + SafeBrowsingClientFactory::GetInstance(); SafeBrowsingMetricsCollectorFactory::GetInstance(); SendTabToSelfSyncServiceFactory::GetInstance(); SessionRestorationServiceFactory::GetInstance(); SessionSyncServiceFactory::GetInstance(); + SigninClientFactory::GetInstance(); + SupervisedUserServiceFactory::GetInstance(); SupervisedUserSettingsServiceFactory::GetInstance(); + SyncServiceFactory::GetInstance(); + UnifiedConsentServiceFactory::GetInstance(); + UrlLanguageHistogramFactory::GetInstance(); #if BUILDFLAG(IOS_CREDENTIAL_PROVIDER_ENABLED) CredentialProviderServiceFactory::GetInstance(); diff --git a/chromium_src/ios/chrome/browser/shared/model/prefs/browser_prefs.mm b/chromium_src/ios/chrome/browser/shared/model/prefs/browser_prefs.mm index 21292c869bc..735adc52536 100644 --- a/chromium_src/ios/chrome/browser/shared/model/prefs/browser_prefs.mm +++ b/chromium_src/ios/chrome/browser/shared/model/prefs/browser_prefs.mm @@ -26,6 +26,7 @@ #include "brave/components/skus/browser/skus_utils.h" #include "brave/ios/browser/brave_stats/brave_stats_prefs.h" #include "components/pref_registry/pref_registry_syncable.h" +#include "ios/chrome/browser/shared/model/prefs/pref_names.h" void BraveRegisterBrowserStatePrefs( user_prefs::PrefRegistrySyncable* registry) { diff --git a/chromium_src/ios/chrome/browser/tabs/model/tab_helper_util.mm b/chromium_src/ios/chrome/browser/tabs/model/tab_helper_util.mm index b7c5680b541..d079c826c3d 100644 --- a/chromium_src/ios/chrome/browser/tabs/model/tab_helper_util.mm +++ b/chromium_src/ios/chrome/browser/tabs/model/tab_helper_util.mm @@ -5,14 +5,25 @@ #import "ios/chrome/browser/tabs/model/tab_helper_util.h" +#import "components/omnibox/common/omnibox_features.h" #include "ios/chrome/browser/complex_tasks/model/ios_task_tab_helper.h" +#import "ios/chrome/browser/https_upgrades/model/https_only_mode_upgrade_tab_helper.h" +#import "ios/chrome/browser/https_upgrades/model/https_upgrade_service_factory.h" +#import "ios/chrome/browser/https_upgrades/model/typed_navigation_upgrade_tab_helper.h" +#import "ios/chrome/browser/prerender/model/prerender_service_factory.h" #include "ios/chrome/browser/sessions/model/ios_chrome_session_tab_helper.h" -#import "ios/chrome/browser/sessions/model/web_session_state_tab_helper.h" +#include "ios/chrome/browser/sessions/model/web_session_state_tab_helper.h" +#import "ios/chrome/browser/shared/model/profile/profile_ios.h" #include "ios/chrome/browser/tabs/model/ios_chrome_synced_tab_delegate.h" +#import "ios/components/security_interstitials/https_only_mode/https_only_mode_container.h" +#import "ios/components/security_interstitials/ios_blocking_page_tab_helper.h" void AttachTabHelpers(web::WebState* web_state, TabHelperFilter filter_flags) { IOSChromeSessionTabHelper::CreateForWebState(web_state); IOSChromeSyncedTabDelegate::CreateForWebState(web_state); WebSessionStateTabHelper::CreateForWebState(web_state); IOSTaskTabHelper::CreateForWebState(web_state); + + security_interstitials::IOSBlockingPageTabHelper::CreateForWebState( + web_state); } diff --git a/chromium_src/ios/web/navigation/crw_wk_navigation_handler.h b/chromium_src/ios/web/navigation/crw_wk_navigation_handler.h new file mode 100644 index 00000000000..6c13bd7d054 --- /dev/null +++ b/chromium_src/ios/web/navigation/crw_wk_navigation_handler.h @@ -0,0 +1,14 @@ +// Copyright (c) 2025 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_CHROMIUM_SRC_IOS_WEB_NAVIGATION_CRW_WK_NAVIGATION_HANDLER_H_ +#define BRAVE_CHROMIUM_SRC_IOS_WEB_NAVIGATION_CRW_WK_NAVIGATION_HANDLER_H_ + +#include "src/ios/web/navigation/crw_wk_navigation_handler.h" // IWYU pragma: export + +@interface BraveCRWWKNavigationHandler : CRWWKNavigationHandler +@end + +#endif // BRAVE_CHROMIUM_SRC_IOS_WEB_NAVIGATION_CRW_WK_NAVIGATION_HANDLER_H_ diff --git a/chromium_src/ios/web/navigation/crw_wk_navigation_handler.mm b/chromium_src/ios/web/navigation/crw_wk_navigation_handler.mm new file mode 100644 index 00000000000..179941280c9 --- /dev/null +++ b/chromium_src/ios/web/navigation/crw_wk_navigation_handler.mm @@ -0,0 +1,61 @@ +// 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 "ios/web/navigation/crw_wk_navigation_handler.h" + +#import +#import + +namespace web { +class WebState; +} + +namespace brave { +bool ShouldBlockUniversalLinks(web::WebState* webState, NSURLRequest* request); +bool ShouldBlockJavaScript(web::WebState* webState, NSURLRequest* request); +} // namespace brave + +#include "src/ios/web/navigation/crw_wk_navigation_handler.mm" + +// Setup a CRWWKNavigationHandler subclass so that we may integrate Brave +// features that Chrome does not support or expose access to (such as the +// requiring the use of WKWebpagePreferences) +@implementation BraveCRWWKNavigationHandler + +- (void)webView:(WKWebView*)webView + decidePolicyForNavigationAction:(WKNavigationAction*)action + preferences:(WKWebpagePreferences*)preferences + decisionHandler:(void (^)(WKNavigationActionPolicy, + WKWebpagePreferences*))handler { + auto decisionHandler = + ^(WKNavigationActionPolicy policy, WKWebpagePreferences*) { + // Check if we want to block JavaScript execution on the page + // Typically this would go through `ContentSettingsType::JAVASCRIPT`, + // but Chrome iOS does not support this yet. + bool shouldBlockJavaScript = brave::ShouldBlockJavaScript( + static_cast(self.webStateImpl), action.request); + if (shouldBlockJavaScript && preferences) { + // Only ever update it to false + preferences.allowsContentJavaScript = false; + } + if (policy == WKNavigationActionPolicyAllow) { + // Check if we want to explicitly block universal links + bool forceBlockUniversalLinks = brave::ShouldBlockUniversalLinks( + static_cast(self.webStateImpl), action.request); + if (forceBlockUniversalLinks) { + handler(web::kNavigationActionPolicyAllowAndBlockUniversalLinks, + preferences); + return; + } + } + handler(policy, preferences); + }; + [super webView:webView + decidePolicyForNavigationAction:action + preferences:preferences + decisionHandler:decisionHandler]; +} + +@end diff --git a/chromium_src/ios/web/public/browser_state.h b/chromium_src/ios/web/public/browser_state.h new file mode 100644 index 00000000000..cfb91ba640b --- /dev/null +++ b/chromium_src/ios/web/public/browser_state.h @@ -0,0 +1,34 @@ +// 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_CHROMIUM_SRC_IOS_WEB_PUBLIC_BROWSER_STATE_H_ +#define BRAVE_CHROMIUM_SRC_IOS_WEB_PUBLIC_BROWSER_STATE_H_ + +#include "base/notreached.h" + +class PrefService; + +// This override is tied to the replaced implementation of `//ios/web_view`'s +// CWVWebViewConfiguration (see cwv_web_view_configuration_internal.h override) +// +// Since this replaced implementation replaces WebViewBrowserState with +// web::BrowserState, there are a few places that use the GetPrefs method +// exposed on WebViewBrowserState. +// +// This adds a `GetPrefs` method to the public web::BrowserState definition +// to avoid build failures, even though the code that is calling it will never +// be used by us. + +#define GetURLLoaderFactory() \ + GetURLLoaderFactory(); \ + PrefService* GetPrefs() { \ + NOTREACHED(); \ + return nullptr; \ + } \ + void Unused() +#include "src/ios/web/public/browser_state.h" // IWYU pragma: export +#undef GetURLLoaderFactory + +#endif // BRAVE_CHROMIUM_SRC_IOS_WEB_PUBLIC_BROWSER_STATE_H_ diff --git a/chromium_src/ios/web/web_state/ui/crw_web_controller.h b/chromium_src/ios/web/web_state/ui/crw_web_controller.h index 2fcf14e37de..082f70958c6 100644 --- a/chromium_src/ios/web/web_state/ui/crw_web_controller.h +++ b/chromium_src/ios/web/web_state/ui/crw_web_controller.h @@ -6,10 +6,12 @@ #ifndef BRAVE_CHROMIUM_SRC_IOS_WEB_WEB_STATE_UI_CRW_WEB_CONTROLLER_H_ #define BRAVE_CHROMIUM_SRC_IOS_WEB_WEB_STATE_UI_CRW_WEB_CONTROLLER_H_ +// This override exposes a few private properties for us to use #define webViewNavigationProxy \ webViewNavigationProxy_ChromiumImpl; \ @property(weak, nonatomic, readonly) id \ - webViewNavigationProxy + webViewNavigationProxy; \ + @property(weak, nonatomic, readonly) WKWebView* webView #include "src/ios/web/web_state/ui/crw_web_controller.h" // IWYU pragma: export diff --git a/chromium_src/ios/web/web_state/ui/crw_web_controller.mm b/chromium_src/ios/web/web_state/ui/crw_web_controller.mm index 425c8053196..57eb248284b 100644 --- a/chromium_src/ios/web/web_state/ui/crw_web_controller.mm +++ b/chromium_src/ios/web/web_state/ui/crw_web_controller.mm @@ -9,9 +9,17 @@ #include +// Replace the underlying CRWWKNavigationHandler with our subclass +#define _navigationHandler \ + _navigationHandler = \ + [[BraveCRWWKNavigationHandler alloc] initWithDelegate:self]; \ + if (false) [[maybe_unused]] \ + auto* _ +// Support for tab sync #define webViewNavigationProxy webViewNavigationProxy_ChromiumImpl #include "src/ios/web/web_state/ui/crw_web_controller.mm" #undef webViewNavigationProxy +#undef _navigationHandler #pragma mark - BackForwardList diff --git a/chromium_src/ios/web/web_state/ui/wk_web_view_configuration_provider.h b/chromium_src/ios/web/web_state/ui/wk_web_view_configuration_provider.h new file mode 100644 index 00000000000..07cdae8249f --- /dev/null +++ b/chromium_src/ios/web/web_state/ui/wk_web_view_configuration_provider.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2025 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_CHROMIUM_SRC_IOS_WEB_WEB_STATE_UI_WK_WEB_VIEW_CONFIGURATION_PROVIDER_H_ +#define BRAVE_CHROMIUM_SRC_IOS_WEB_WEB_STATE_UI_WK_WEB_VIEW_CONFIGURATION_PROVIDER_H_ + +// Support for subclassing WKWebViewConfigurationProvider: +// - Add a friend class as the subclass needs access to private fields +// (`configuration_` in this case) +// - Make `ResetWithWebViewConfiguration` virtual so that we can override it +#define browser_state_ \ + browser_state_; \ + friend class BraveWKWebViewConfigurationProvider +#define ResetWithWebViewConfiguration \ + Unused() {} \ + virtual void ResetWithWebViewConfiguration +#include "src/ios/web/web_state/ui/wk_web_view_configuration_provider.h" // IWYU pragma: export +#undef ResetWithWebViewConfiguration +#undef browser_state_ + +namespace web { +class BraveWKWebViewConfigurationProvider + : public WKWebViewConfigurationProvider { + using WKWebViewConfigurationProvider::WKWebViewConfigurationProvider; + + void ResetWithWebViewConfiguration( + WKWebViewConfiguration* configuration) override; +}; +} // namespace web + +#endif // BRAVE_CHROMIUM_SRC_IOS_WEB_WEB_STATE_UI_WK_WEB_VIEW_CONFIGURATION_PROVIDER_H_ diff --git a/chromium_src/ios/web/web_state/ui/wk_web_view_configuration_provider.mm b/chromium_src/ios/web/web_state/ui/wk_web_view_configuration_provider.mm new file mode 100644 index 00000000000..d20880f66d7 --- /dev/null +++ b/chromium_src/ios/web/web_state/ui/wk_web_view_configuration_provider.mm @@ -0,0 +1,39 @@ +/* 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 "base/supports_user_data.h" + +// Replace the `WKWebViewConfigurationProvider` constructor call with the Brave +// subclass inside of `WKWebViewConfigurationProvider::FromBrowserState` +#define SetUserData(key, ...) \ + SetUserData(key, base::WrapUnique(new BraveWKWebViewConfigurationProvider( \ + browser_state))); +#include "src/ios/web/web_state/ui/wk_web_view_configuration_provider.mm" +#undef SetUserData + +namespace web { + +void BraveWKWebViewConfigurationProvider::ResetWithWebViewConfiguration( + WKWebViewConfiguration* configuration) { + WKWebViewConfigurationProvider::ResetWithWebViewConfiguration(configuration); + + // Adjusts the underlying WKWebViewConfiguration for settings we don't want + // to inherit from Chromium + + // Restore WKWebView long press + @try { + [configuration_ setValue:@YES forKey:@"longPressActionsEnabled"]; + } @catch (NSException* exception) { + NOTREACHED() << "Error setting value for longPressActionsEnabled"; + } + + // Restore Apple's safe browsing implementation + [[configuration_ preferences] setFraudulentWebsiteWarningEnabled:YES]; + + // Reset fullscreen to default as it wasn't set in Brave + [[configuration_ preferences] setElementFullscreenEnabled:NO]; +} + +} // namespace web diff --git a/chromium_src/ios/web_view/internal/autofill/web_view_autofill_client_ios.h b/chromium_src/ios/web_view/internal/autofill/web_view_autofill_client_ios.h new file mode 100644 index 00000000000..d5ffd51f7e1 --- /dev/null +++ b/chromium_src/ios/web_view/internal/autofill/web_view_autofill_client_ios.h @@ -0,0 +1,37 @@ +// 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_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_AUTOFILL_WEB_VIEW_AUTOFILL_CLIENT_IOS_H_ +#define BRAVE_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_AUTOFILL_WEB_VIEW_AUTOFILL_CLIENT_IOS_H_ + +#include + +#include "components/autofill/core/browser/data_manager/personal_data_manager.h" +#include "components/autofill/ios/browser/autofill_client_ios.h" +#include "components/autofill/ios/browser/autofill_driver_ios_bridge.h" +#include "ios/web/public/browser_state.h" +#include "ios/web/public/web_state.h" +#import "ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h" + +namespace autofill { + +// We do not use any WebView* specific clients & services since we would instead +// use the Chrome iOS ones. +// +// Replaces the implementation of the WebViewAutofillClientIOS with one that +// does nothing +class WebViewAutofillClientIOS : public AutofillClientIOS { + public: + static std::unique_ptr Create( + FromWebStateImpl from_web_state_impl, + web::WebState* web_state, + id bridge); + + void set_bridge(id bridge) {} +}; + +} // namespace autofill + +#endif // BRAVE_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_AUTOFILL_WEB_VIEW_AUTOFILL_CLIENT_IOS_H_ diff --git a/chromium_src/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm b/chromium_src/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm new file mode 100644 index 00000000000..0a2888a268d --- /dev/null +++ b/chromium_src/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm @@ -0,0 +1,18 @@ +// 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 "ios/web_view/internal/autofill/web_view_autofill_client_ios.h" + +namespace autofill { + +// static +std::unique_ptr WebViewAutofillClientIOS::Create( + FromWebStateImpl from_web_state_impl, + web::WebState* web_state, + id bridge) { + return nullptr; +} + +} // namespace autofill diff --git a/chromium_src/ios/web_view/internal/browser_state_keyed_service_factories.mm b/chromium_src/ios/web_view/internal/browser_state_keyed_service_factories.mm new file mode 100644 index 00000000000..85ef910f3b9 --- /dev/null +++ b/chromium_src/ios/web_view/internal/browser_state_keyed_service_factories.mm @@ -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/. + +namespace ios_web_view { + +void EnsureBrowserStateKeyedServiceFactoriesBuilt() { + // Intentionally left empty, please add any factories that need to be built + // to + // chromium_src/ios/chrome/browser/browser_state/model/browser_state_keyed_service_factories.mm +} + +} // namespace ios_web_view diff --git a/chromium_src/ios/web_view/internal/cwv_ssl_status.mm b/chromium_src/ios/web_view/internal/cwv_ssl_status.mm new file mode 100644 index 00000000000..58049a9ed14 --- /dev/null +++ b/chromium_src/ios/web_view/internal/cwv_ssl_status.mm @@ -0,0 +1,12 @@ +// 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 "src/ios/web_view/internal/cwv_ssl_status.mm" + +@implementation CWVSSLStatus (Internal) +- (web::SSLStatus)internalStatus { + return _internalStatus; +} +@end diff --git a/chromium_src/ios/web_view/internal/cwv_ssl_status_internal.h b/chromium_src/ios/web_view/internal/cwv_ssl_status_internal.h new file mode 100644 index 00000000000..a2f28322327 --- /dev/null +++ b/chromium_src/ios/web_view/internal/cwv_ssl_status_internal.h @@ -0,0 +1,18 @@ +// 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_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_CWV_SSL_STATUS_INTERNAL_H_ +#define BRAVE_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_CWV_SSL_STATUS_INTERNAL_H_ + +#include "src/ios/web_view/internal/cwv_ssl_status_internal.h" // IWYU pragma: export + +// Expose the underlying web::SSLStatus that is not public so that +// cwv_ssl_status_extras.mm can access the private property and expose +// additional functionality +@interface CWVSSLStatus (Internal) +@property(readonly) web::SSLStatus internalStatus; +@end + +#endif // BRAVE_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_CWV_SSL_STATUS_INTERNAL_H_ diff --git a/chromium_src/ios/web_view/internal/cwv_web_view.mm b/chromium_src/ios/web_view/internal/cwv_web_view.mm new file mode 100644 index 00000000000..58031e98ce2 --- /dev/null +++ b/chromium_src/ios/web_view/internal/cwv_web_view.mm @@ -0,0 +1,35 @@ +/* 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 "src/ios/web_view/internal/cwv_web_view.mm" + +@implementation CWVWebView (Internal) + +- (web::WebState*)webState { + return _webState.get(); +} + ++ (BOOL)_isRestoreDataValid:(NSData*)data { + NSError* error = nil; + NSKeyedUnarchiver* coder = + [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:&error]; + if (error) { + return NO; + } + coder.requiresSecureCoding = NO; + CWVWebViewProtobufStorage* cachedProtobufStorage = + base::apple::ObjCCastStrict( + [coder decodeObjectForKey:kProtobufStorageKey]); + CRWSessionStorage* cachedSessionStorage = + base::apple::ObjCCastStrict( + [coder decodeObjectForKey:kSessionStorageKey]); + return (cachedProtobufStorage != nil || cachedSessionStorage != nil); +} + ++ (id)objectFromValue:(const base::Value*)value { + return NSObjectFromValue(value); +} + +@end diff --git a/chromium_src/ios/web_view/internal/cwv_web_view_configuration.mm b/chromium_src/ios/web_view/internal/cwv_web_view_configuration.mm new file mode 100644 index 00000000000..d79a08b34a7 --- /dev/null +++ b/chromium_src/ios/web_view/internal/cwv_web_view_configuration.mm @@ -0,0 +1,122 @@ +// 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/. + +#import + +#import "base/memory/raw_ptr.h" +#import "base/notreached.h" +#import "base/threading/thread_restrictions.h" +#import "components/keyed_service/core/service_access_type.h" +#import "components/keyed_service/ios/browser_state_dependency_manager.h" +#import "components/password_manager/core/browser/password_store/password_store_interface.h" +#import "ios/web/public/browser_state.h" +#import "ios/web_view/internal/app/application_context.h" +#import "ios/web_view/internal/autofill/cwv_autofill_data_manager_internal.h" +#import "ios/web_view/internal/autofill/web_view_personal_data_manager_factory.h" +#import "ios/web_view/internal/browser_state_keyed_service_factories.h" +#import "ios/web_view/internal/cwv_preferences_internal.h" +#import "ios/web_view/internal/cwv_user_content_controller_internal.h" +#import "ios/web_view/internal/cwv_web_view_configuration_internal.h" +#import "ios/web_view/internal/cwv_web_view_internal.h" +#import "ios/web_view/internal/passwords/web_view_account_password_store_factory.h" +#import "ios/web_view/internal/web_view_global_state_util.h" + +// We completely replace the CWVWebViewConfiguration implementation so that +// we can nop certain services such as access to CWVWebView specific wrappers +// around autofill, sync, etc (since we would use Chrome's) and replace +// the exposed browser state to be a plain `web::BrowserState` +@interface CWVWebViewConfiguration () { + // The BrowserState for this configuration. + raw_ptr _browserState; + + // Holds all CWVWebViews created with this class. Weak references. + NSHashTable* _webViews; +} +@end + +@implementation CWVWebViewConfiguration + ++ (void)shutDown { +} + ++ (instancetype)defaultConfiguration { + NOTREACHED(); + return nil; +} + ++ (instancetype)incognitoConfiguration { + NOTREACHED(); + return nil; +} + ++ (CWVWebViewConfiguration*)nonPersistentConfiguration { + NOTREACHED(); + return nil; +} + +- (instancetype)initWithBrowserState:(web::BrowserState*)browserState { + self = [super init]; + if (self) { + _browserState = browserState; + _webViews = [NSHashTable weakObjectsHashTable]; + } + return self; +} + +#pragma mark - Unused Services + +- (CWVPreferences*)preferences { + // This property is not nullable, so will crash anyways on the Swift side if + // accessed. + NOTREACHED(); + return nil; +} + +- (CWVUserContentController*)userContentController { + // This property is not nullable, so will crash anyways on the Swift side if + // accessed. + NOTREACHED(); + return nil; +} + +- (CWVAutofillDataManager*)autofillDataManager { + return nil; +} + +- (CWVSyncController*)syncController { + return nil; +} + +- (CWVLeakCheckService*)leakCheckService { + return nil; +} + +- (CWVReuseCheckService*)reuseCheckService { + return nil; +} + +#pragma mark - Public Methods + +- (BOOL)isPersistent { + return !_browserState->IsOffTheRecord(); +} + +#pragma mark - Private Methods + +- (web::BrowserState*)browserState { + return _browserState; +} + +- (void)registerWebView:(CWVWebView*)webView { + [_webViews addObject:webView]; +} + +- (void)shutDown { + for (CWVWebView* webView in _webViews) { + [webView shutDown]; + } +} + +@end diff --git a/chromium_src/ios/web_view/internal/cwv_web_view_configuration_internal.h b/chromium_src/ios/web_view/internal/cwv_web_view_configuration_internal.h new file mode 100644 index 00000000000..de4638bafe9 --- /dev/null +++ b/chromium_src/ios/web_view/internal/cwv_web_view_configuration_internal.h @@ -0,0 +1,49 @@ +// 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_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_CWV_WEB_VIEW_CONFIGURATION_INTERNAL_H_ +#define BRAVE_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_CWV_WEB_VIEW_CONFIGURATION_INTERNAL_H_ + +#import "ios/web_view/public/cwv_web_view_configuration.h" + +NS_ASSUME_NONNULL_BEGIN + +namespace web { +class BrowserState; +} // namespace web + +@class CWVWebView; + +// We completely replace the CWVWebViewConfiguration implementation so that +// we can nop certain services such as access to CWVWebView specific wrappers +// around autofill, sync, etc (since we would use Chrome's) and replace +// the exposed browser state to be a plain `web::BrowserState` +@interface CWVWebViewConfiguration () + +// The browser state associated with this configuration. +@property(nonatomic, readonly) web::BrowserState* browserState; + +// Calls |shutDown| on the singletons returned by |defaultConfiguration| and +// |incognitoConfiguration|. ++ (void)shutDown; + +// Initializes with |browserState| that this instance is to be associated with. +- (instancetype)initWithBrowserState:(web::BrowserState*)browserState + NS_DESIGNATED_INITIALIZER; + +// Registers a |webView| so that this class can call |shutDown| on it later on. +// Only weak references are held, so no need for de-register method. +- (void)registerWebView:(CWVWebView*)webView; + +// If this instance is going to outlive the globals created in +// ios_web_view::InitializeGlobalState, this method must be called to ensure the +// correct tear down order. +- (void)shutDown; + +@end + +NS_ASSUME_NONNULL_END + +#endif // BRAVE_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_CWV_WEB_VIEW_CONFIGURATION_INTERNAL_H_ diff --git a/chromium_src/ios/web_view/internal/cwv_web_view_internal.h b/chromium_src/ios/web_view/internal/cwv_web_view_internal.h new file mode 100644 index 00000000000..be2193d436c --- /dev/null +++ b/chromium_src/ios/web_view/internal/cwv_web_view_internal.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_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_CWV_WEB_VIEW_INTERNAL_H_ +#define BRAVE_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_CWV_WEB_VIEW_INTERNAL_H_ + +#include "src/ios/web_view/internal/cwv_web_view_internal.h" // IWYU pragma: export + +NS_ASSUME_NONNULL_BEGIN + +namespace base { +class Value; +} + +// Expose the underlying web::WebState and a few other properties that are not +// public so that cwv_web_view_extras.mm can access the private property and +// implement additional functionality +@interface CWVWebView (Internal) +@property(readonly) web::WebState* webState; ++ (BOOL)_isRestoreDataValid:(NSData*)data; ++ (id)objectFromValue:(const base::Value*)value; +@end + +NS_ASSUME_NONNULL_END + +#endif // BRAVE_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_CWV_WEB_VIEW_INTERNAL_H_ diff --git a/chromium_src/ios/web_view/internal/cwv_x509_certificate.mm b/chromium_src/ios/web_view/internal/cwv_x509_certificate.mm new file mode 100644 index 00000000000..d9024eab4df --- /dev/null +++ b/chromium_src/ios/web_view/internal/cwv_x509_certificate.mm @@ -0,0 +1,12 @@ +// 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 "src/ios/web_view/internal/cwv_x509_certificate.mm" + +@implementation CWVX509Certificate (Internal) +- (scoped_refptr)internalCertificate { + return _internalCertificate; +} +@end diff --git a/chromium_src/ios/web_view/internal/cwv_x509_certificate_internal.h b/chromium_src/ios/web_view/internal/cwv_x509_certificate_internal.h new file mode 100644 index 00000000000..dc3d15f9544 --- /dev/null +++ b/chromium_src/ios/web_view/internal/cwv_x509_certificate_internal.h @@ -0,0 +1,18 @@ +// 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_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_CWV_X509_CERTIFICATE_INTERNAL_H_ +#define BRAVE_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_CWV_X509_CERTIFICATE_INTERNAL_H_ + +#include "src/ios/web_view/internal/cwv_x509_certificate_internal.h" // IWYU pragma: export + +// Expose the underlying net::X509Certificate that is not public so that +// cwv_x509_certificate_extras.mm can access the private property and expose +// additional functionality +@interface CWVX509Certificate (Internal) +@property(readonly) scoped_refptr internalCertificate; +@end + +#endif // BRAVE_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_CWV_X509_CERTIFICATE_INTERNAL_H_ diff --git a/chromium_src/ios/web_view/internal/passwords/web_view_password_manager_client.h b/chromium_src/ios/web_view/internal/passwords/web_view_password_manager_client.h new file mode 100644 index 00000000000..75f388b3af7 --- /dev/null +++ b/chromium_src/ios/web_view/internal/passwords/web_view_password_manager_client.h @@ -0,0 +1,35 @@ +// 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_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_PASSWORDS_WEB_VIEW_PASSWORD_MANAGER_CLIENT_H_ +#define BRAVE_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_PASSWORDS_WEB_VIEW_PASSWORD_MANAGER_CLIENT_H_ + +#include + +#include "components/password_manager/core/browser/password_manager_client.h" +#include "components/password_manager/ios/password_manager_client_bridge.h" +#include "ios/web/public/browser_state.h" +#include "ios/web/public/web_state.h" + +namespace ios_web_view { + +// We do not use any WebView* specific clients & services since we would instead +// use the Chrome iOS ones. +// +// Replaces the implementation of the WebViewPasswordManagerClient with one that +// does nothing +class WebViewPasswordManagerClient + : public password_manager::PasswordManagerClient { + public: + static std::unique_ptr Create( + web::WebState* web_state, + web::BrowserState* browser_state); + + void set_bridge(id bridge) {} +}; + +} // namespace ios_web_view + +#endif // BRAVE_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_PASSWORDS_WEB_VIEW_PASSWORD_MANAGER_CLIENT_H_ diff --git a/chromium_src/ios/web_view/internal/passwords/web_view_password_manager_client.mm b/chromium_src/ios/web_view/internal/passwords/web_view_password_manager_client.mm new file mode 100644 index 00000000000..8bb3c59c4bc --- /dev/null +++ b/chromium_src/ios/web_view/internal/passwords/web_view_password_manager_client.mm @@ -0,0 +1,17 @@ +// 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 "ios/web_view/internal/passwords/web_view_password_manager_client.h" + +namespace ios_web_view { + +// static +std::unique_ptr +WebViewPasswordManagerClient::Create(web::WebState* web_state, + web::BrowserState* browser_state) { + return nullptr; +} + +} // namespace ios_web_view diff --git a/chromium_src/ios/web_view/internal/translate/web_view_translate_client.h b/chromium_src/ios/web_view/internal/translate/web_view_translate_client.h new file mode 100644 index 00000000000..6f4ef69c0ec --- /dev/null +++ b/chromium_src/ios/web_view/internal/translate/web_view_translate_client.h @@ -0,0 +1,39 @@ +// 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_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_TRANSLATE_WEB_VIEW_TRANSLATE_CLIENT_H_ +#define BRAVE_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_TRANSLATE_WEB_VIEW_TRANSLATE_CLIENT_H_ + +#include + +#include "components/translate/core/browser/translate_client.h" +#include "ios/web/public/browser_state.h" +#include "ios/web/public/web_state.h" +#import "ios/web_view/internal/translate/cwv_translation_controller_internal.h" + +namespace ios_web_view { + +// We do not use any WebView* specific clients & services since we would instead +// use the Chrome iOS ones. +// +// Replaces the implementation of the WebViewTranslateClient with one that +// does nothing +class WebViewTranslateClient : public translate::TranslateClient { + public: + static std::unique_ptr Create( + web::BrowserState* browser_state, + web::WebState* web_state); + + void set_translation_controller(CWVTranslationController* controller) {} + void TranslatePage(const std::string& source_lang, + const std::string& target_lang, + bool triggered_from_menu) {} + void RevertTranslation() {} + bool RequestTranslationOffer() { return false; } +}; + +} // namespace ios_web_view + +#endif // BRAVE_CHROMIUM_SRC_IOS_WEB_VIEW_INTERNAL_TRANSLATE_WEB_VIEW_TRANSLATE_CLIENT_H_ diff --git a/chromium_src/ios/web_view/internal/translate/web_view_translate_client.mm b/chromium_src/ios/web_view/internal/translate/web_view_translate_client.mm new file mode 100644 index 00000000000..7f30bceb6ed --- /dev/null +++ b/chromium_src/ios/web_view/internal/translate/web_view_translate_client.mm @@ -0,0 +1,17 @@ +// 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 "ios/web_view/internal/translate/web_view_translate_client.h" + +namespace ios_web_view { + +// static +std::unique_ptr WebViewTranslateClient::Create( + web::BrowserState* browser_state, + web::WebState* web_state) { + return nullptr; +} + +} // namespace ios_web_view diff --git a/ios/BUILD.gn b/ios/BUILD.gn index af0cccb9ec3..6976fb83e1e 100644 --- a/ios/BUILD.gn +++ b/ios/BUILD.gn @@ -16,6 +16,7 @@ import("//brave/ios/browser/api/brave_shields/headers.gni") import("//brave/ios/browser/api/brave_stats/headers.gni") import("//brave/ios/browser/api/brave_wallet/headers.gni") import("//brave/ios/browser/api/certificate/headers.gni") +import("//brave/ios/browser/api/content_settings/headers.gni") import("//brave/ios/browser/api/credential_provider/headers.gni") import("//brave/ios/browser/api/de_amp/headers.gni") import("//brave/ios/browser/api/debounce/headers.gni") @@ -40,9 +41,11 @@ import("//brave/ios/browser/api/url/headers.gni") import("//brave/ios/browser/api/url_sanitizer/headers.gni") import("//brave/ios/browser/api/web/ui/headers.gni") import("//brave/ios/browser/api/web/web_state/headers.gni") +import("//brave/ios/browser/api/web_view/headers.gni") import("//brave/ios/browser/api/webcompat_reporter/headers.gni") import("//brave/ios/browser/debounce/headers.gni") import("//brave/ios/browser/url_sanitizer/headers.gni") +import("//brave/ios/web_view/headers.gni") import("//build/apple/tweak_info_plist.gni") import("//build/config/compiler/compiler.gni") import("//build/config/ios/rules.gni") @@ -128,9 +131,11 @@ brave_core_public_headers += browser_api_features_public_headers brave_core_public_headers += credential_provider_public_headers brave_core_public_headers += developer_options_code_public_headers brave_core_public_headers += browser_api_storekit_receipt_public_headers +brave_core_public_headers += content_settings_public_headers brave_core_public_headers += webcompat_reporter_public_headers brave_core_public_headers += browser_api_translate_public_headers brave_core_public_headers += browser_api_unicode_public_headers +brave_core_public_headers += brave_web_view_public_headers action("brave_core_umbrella_header") { script = "//build/config/ios/generate_umbrella_header.py" @@ -144,6 +149,7 @@ action("brave_core_umbrella_header") { ] args += rebase_path(brave_core_public_headers, root_build_dir) + args += rebase_path(ios_web_view_public_headers, root_build_dir) } tweak_info_plist("info_plist") { @@ -174,6 +180,7 @@ ios_framework_bundle("brave_core_ios_framework") { ":brave_core_plist", ":brave_core_umbrella_header", "//brave/ios/app", + "//brave/ios/web_view", ] deps += ads_public_deps @@ -185,9 +192,11 @@ ios_framework_bundle("brave_core_ios_framework") { deps += webcompat_reporter_public_deps sources = brave_core_public_headers + sources += ios_web_view_public_headers public_headers = get_target_outputs(":brave_core_umbrella_header") public_headers += brave_core_public_headers + public_headers += ios_web_view_public_headers } ios_create_xcframework("brave_core_xcframework") { diff --git a/ios/app/BUILD.gn b/ios/app/BUILD.gn index 314888967d5..2c23cfe7096 100644 --- a/ios/app/BUILD.gn +++ b/ios/app/BUILD.gn @@ -48,6 +48,7 @@ source_set("app") { "//brave/ios/browser/api/brave_stats", "//brave/ios/browser/api/brave_wallet", "//brave/ios/browser/api/brave_wallet:wallet_mojom_wrappers", + "//brave/ios/browser/api/content_settings", "//brave/ios/browser/api/de_amp", "//brave/ios/browser/api/history", "//brave/ios/browser/api/https_upgrade_exceptions", @@ -65,9 +66,11 @@ source_set("app") { "//brave/ios/browser/api/webcompat_reporter:webcompat_reporter_mojom_wrappers", "//brave/ios/browser/skus", "//brave/ios/browser/ui/webui", + "//brave/ios/browser/web", "//build:blink_buildflags", "//components/browser_sync", "//components/component_updater", + "//components/content_settings/core/browser", "//components/history/core/browser", "//components/keyed_service/core", "//components/password_manager/core/browser", @@ -81,6 +84,8 @@ source_set("app") { "//ios/chrome/app/startup:startup", "//ios/chrome/app/startup:startup_basic", "//ios/chrome/browser/bookmarks/model", + "//ios/chrome/browser/browsing_data/model", + "//ios/chrome/browser/content_settings/model", "//ios/chrome/browser/credential_provider/model:buildflags", "//ios/chrome/browser/flags:system_flags", "//ios/chrome/browser/history/model", @@ -101,6 +106,7 @@ source_set("app") { "//ios/public/provider/chrome/browser/ui_utils:ui_utils_api", "//ios/web/public/init", "//ios/web/public/webui", + "//ios/web_view:web_view_sources", # Use the provider API controlled by args.gn. ios_provider_target, diff --git a/ios/app/DEPS b/ios/app/DEPS index 1d72509bef8..07f14415ed5 100644 --- a/ios/app/DEPS +++ b/ios/app/DEPS @@ -4,6 +4,7 @@ inline_file('//ios/chrome/app/DEPS', globals(), locals()) include_rules += [ "+ios/chrome/app", + "+ios/web_view", "+brave/ios/app", "+brave/ios/browser", diff --git a/ios/app/brave_core_main.h b/ios/app/brave_core_main.h index c5c35a7ce71..c3589cdbe9f 100644 --- a/ios/app/brave_core_main.h +++ b/ios/app/brave_core_main.h @@ -27,6 +27,8 @@ @class DeAmpPrefs; @class AIChat; @class HTTPSUpgradeExceptionsService; +@class DefaultHostContentSettings; +@class CWVWebViewConfiguration; @protocol AIChatDelegate; @protocol IpfsAPI; @@ -78,9 +80,9 @@ OBJC_EXPORT - (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithUserAgent:(NSString*)userAgent; +- (instancetype)initWithUserAgent:(nullable NSString*)userAgent; -- (instancetype)initWithUserAgent:(NSString*)userAgent +- (instancetype)initWithUserAgent:(nullable NSString*)userAgent additionalSwitches: (NSArray*)additionalSwitches; @@ -103,6 +105,9 @@ OBJC_EXPORT @property(readonly) NTPBackgroundImagesService* backgroundImagesService; +/// The default content settings for regular browsing windows +@property(readonly) DefaultHostContentSettings* defaultHostContentSettings; + - (AIChat*)aiChatAPIWithDelegate:(id)delegate; /// Sets up bundle path overrides and initializes ICU from the BraveCore bundle @@ -113,6 +118,10 @@ OBJC_EXPORT + (void)initializeResourceBundleForTesting; +@property(readonly) CWVWebViewConfiguration* defaultWebViewConfiguration; +@property(readonly) CWVWebViewConfiguration* nonPersistentWebViewConfiguration; +- (void)notifyLastPrivateTabClosed; + @end NS_ASSUME_NONNULL_END diff --git a/ios/app/brave_core_main.mm b/ios/app/brave_core_main.mm index 61394b7398b..2330331df22 100644 --- a/ios/app/brave_core_main.mm +++ b/ios/app/brave_core_main.mm @@ -35,6 +35,8 @@ #include "brave/ios/browser/api/brave_shields/adblock_service+private.h" #include "brave/ios/browser/api/brave_stats/brave_stats+private.h" #include "brave/ios/browser/api/brave_wallet/brave_wallet_api+private.h" +#include "brave/ios/browser/api/content_settings/default_host_content_settings.h" +#include "brave/ios/browser/api/content_settings/default_host_content_settings_internal.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" @@ -48,10 +50,11 @@ #include "brave/ios/browser/api/sync/brave_sync_api+private.h" #include "brave/ios/browser/api/sync/driver/brave_sync_profile_service+private.h" #include "brave/ios/browser/api/web_image/web_image+private.h" -#include "brave/ios/browser/brave_web_client.h" #include "brave/ios/browser/ui/webui/brave_web_ui_controller_factory.h" +#include "brave/ios/browser/web/brave_web_client.h" #import "build/blink_buildflags.h" #include "components/component_updater/component_updater_paths.h" +#include "components/content_settings/core/browser/content_settings_utils.h" #include "components/history/core/browser/history_service.h" #include "components/keyed_service/core/service_access_type.h" #include "components/password_manager/core/browser/password_store/password_store.h" @@ -62,6 +65,9 @@ #include "ios/chrome/app/startup/provider_registration.h" #include "ios/chrome/browser/bookmarks/model/bookmark_model_factory.h" #include "ios/chrome/browser/bookmarks/model/bookmark_undo_service_factory.h" +#include "ios/chrome/browser/browsing_data/model/browsing_data_remover.h" +#include "ios/chrome/browser/browsing_data/model/browsing_data_remover_factory.h" +#include "ios/chrome/browser/content_settings/model/host_content_settings_map_factory.h" #include "ios/chrome/browser/credential_provider/model/credential_provider_buildflags.h" #include "ios/chrome/browser/history/model/history_service_factory.h" #include "ios/chrome/browser/history/model/web_history_service_factory.h" @@ -83,6 +89,11 @@ #include "ios/public/provider/chrome/browser/overrides/overrides_api.h" #include "ios/public/provider/chrome/browser/ui_utils/ui_utils_api.h" #include "ios/web/public/init/web_main.h" +#include "ios/web/public/thread/web_task_traits.h" +#include "ios/web/public/thread/web_thread.h" +#include "ios/web_view/internal/cwv_web_view_configuration_internal.h" +#include "ios/web_view/internal/web_view_browser_state.h" +#include "ios/web_view/internal/web_view_download_manager.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_paths.h" @@ -130,6 +141,8 @@ const BraveCoreLogSeverity BraveCoreLogSeverityVerbose = std::unique_ptr _webMain; std::unique_ptr _browser; std::unique_ptr _otr_browser; + std::unique_ptr _downloadManager; + std::unique_ptr _otrDownloadManager; raw_ptr _browserList; raw_ptr _otr_browserList; raw_ptr _main_profile; @@ -152,6 +165,9 @@ const BraveCoreLogSeverity BraveCoreLogSeverityVerbose = @property(nonatomic) NTPBackgroundImagesService* backgroundImagesService; @property(nonatomic) HTTPSUpgradeExceptionsService* httpsUpgradeExceptionsService; +@property(nonatomic) DefaultHostContentSettings* defaultHostContentSettings; +@property(nonatomic) CWVWebViewConfiguration* defaultWebViewConfiguration; +@property(nonatomic) CWVWebViewConfiguration* nonPersistentWebViewConfiguration; @end @implementation BraveCoreMain @@ -203,7 +219,9 @@ const BraveCoreLogSeverity BraveCoreLogSeverityVerbose = // Setup WebClient ([ClientRegistration registerClients]) _webClient.reset(new BraveWebClient()); - _webClient->SetUserAgent(base::SysNSStringToUTF8(userAgent)); + if (userAgent != nil) { + _webClient->SetLegacyUserAgent(base::SysNSStringToUTF8(userAgent)); + } web::SetWebClient(_webClient.get()); _delegate.reset(new BraveMainDelegate()); @@ -279,6 +297,15 @@ const BraveCoreLogSeverity BraveCoreLogSeverityVerbose = _tabGeneratorAPI = nil; _webImageDownloader = nil; + [_nonPersistentWebViewConfiguration shutDown]; + [_defaultWebViewConfiguration shutDown]; + + _nonPersistentWebViewConfiguration = nil; + _defaultWebViewConfiguration = nil; + + _downloadManager.reset(); + _otrDownloadManager.reset(); + _otr_browserList = BrowserListFactory::GetForProfile(_otr_browser->GetProfile()); [_otr_browser->GetCommandDispatcher() prepareForShutdown]; @@ -320,6 +347,12 @@ const BraveCoreLogSeverity BraveCoreLogSeverityVerbose = _otr_browser = Browser::Create(otr_last_used_profile, {}); _otr_browserList->AddBrowser(_otr_browser.get()); + // Setup download managers for CWVWebView + _downloadManager = + std::make_unique(profile); + _otrDownloadManager = std::make_unique( + otr_last_used_profile); + #if BUILDFLAG(IOS_CREDENTIAL_PROVIDER_ENABLED) CredentialProviderServiceFactory::GetForProfile(profile); #endif @@ -588,4 +621,112 @@ static bool CustomLogHandler(int severity, } #endif +- (DefaultHostContentSettings*)defaultHostContentSettings { + if (!_defaultHostContentSettings) { + HostContentSettingsMap* map = + ios::HostContentSettingsMapFactory::GetForProfile(_main_profile); + _defaultHostContentSettings = + [[DefaultHostContentSettings alloc] initWithSettingsMap:map]; + } + return _defaultHostContentSettings; +} + +- (CWVWebViewConfiguration*)defaultWebViewConfiguration { + if (!_defaultWebViewConfiguration) { + _defaultWebViewConfiguration = [[CWVWebViewConfiguration alloc] + initWithBrowserState:ios_web_view::WebViewBrowserState:: + FromBrowserState(_main_profile)]; + } + return _defaultWebViewConfiguration; +} + +- (CWVWebViewConfiguration*)nonPersistentWebViewConfiguration { + if (!_nonPersistentWebViewConfiguration) { + _nonPersistentWebViewConfiguration = [[CWVWebViewConfiguration alloc] + initWithBrowserState:ios_web_view::WebViewBrowserState:: + FromBrowserState( + _main_profile->GetOffTheRecordProfile())]; + } + return _nonPersistentWebViewConfiguration; +} + +#pragma mark - Handling of destroying the incognito BrowserState + +// The incognito BrowserState should be closed when the last incognito tab is +// closed (i.e. if there are other incognito tabs open in another Scene, the +// BrowserState must not be destroyed). +- (BOOL)shouldDestroyAndRebuildIncognitoProfile { + return _main_profile->HasOffTheRecordProfile(); +} + +// Matches lastIncognitoTabClosed from Chrome's SceneController +- (void)notifyLastPrivateTabClosed { + // If no other window has incognito tab, then destroy and rebuild the + // BrowserState. Otherwise, just do the state transition animation. + if ([self shouldDestroyAndRebuildIncognitoProfile]) { + // Incognito browser state cannot be deleted before all the requests are + // deleted. Queue empty task on IO thread and destroy the BrowserState + // when the task has executed, again verifying that no incognito tabs are + // present. When an incognito tab is moved between browsers, there is + // a point where the tab isn't attached to any web state list. However, when + // this queued cleanup step executes, the moved tab will be attached, so + // the cleanup shouldn't proceed. + + auto cleanup = ^{ + if ([self shouldDestroyAndRebuildIncognitoProfile]) { + [self destroyAndRebuildIncognitoProfile]; + } + }; + + web::GetIOThreadTaskRunner({})->PostTaskAndReply( + FROM_HERE, base::DoNothing(), base::BindRepeating(cleanup)); + } +} + +// Matches cleanupBrowser from Chrome's BrowserViewWrangler +- (void)cleanupBrowser:(Browser*)browser { + DCHECK(browser); + + // Remove the Browser from the browser list. The browser itself is still + // alive during this call, so any observer can act on it. + ProfileIOS* profile = browser->GetProfile(); + BrowserList* browserList = BrowserListFactory::GetForProfile(profile); + browserList->RemoveBrowser(browser); + + WebStateList* webStateList = browser->GetWebStateList(); + // Close all webstates in `webStateList`. Do this in an @autoreleasepool as + // WebStateList observers will be notified (they are unregistered later). As + // some of them may be implemented in Objective-C and unregister themselves + // in their -dealloc method, ensure the -autorelease introduced by ARC are + // processed before the WebStateList destructor is called. + @autoreleasepool { + CloseAllWebStates(*webStateList, WebStateList::CLOSE_NO_FLAGS); + } +} + +- (void)destroyAndRebuildIncognitoProfile { + DCHECK(_main_profile->HasOffTheRecordProfile()); + _nonPersistentWebViewConfiguration = nil; + + ProfileIOS* otrProfile = _main_profile->GetOffTheRecordProfile(); + + BrowsingDataRemover* browsingDataRemover = + BrowsingDataRemoverFactory::GetForProfile(otrProfile); + browsingDataRemover->Remove(browsing_data::TimePeriod::ALL_TIME, + BrowsingDataRemoveMask::REMOVE_ALL, + base::DoNothing()); + + [self cleanupBrowser:_otr_browser.get()]; + _otr_browser.reset(); + + // Destroy and recreate the off-the-record BrowserState. + _main_profile->DestroyOffTheRecordProfile(); + + otrProfile = _main_profile->GetOffTheRecordProfile(); + _otr_browser = Browser::Create(otrProfile, {}); + + BrowserList* browserList = BrowserListFactory::GetForProfile(otrProfile); + browserList->AddBrowser(_otr_browser.get()); +} + @end diff --git a/ios/brave-ios/App/Client.xcodeproj/project.pbxproj b/ios/brave-ios/App/Client.xcodeproj/project.pbxproj index 89ef080b47c..68b7a3f6fbb 100644 --- a/ios/brave-ios/App/Client.xcodeproj/project.pbxproj +++ b/ios/brave-ios/App/Client.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -23,6 +23,7 @@ 272FC5AF2979EA700027C53D /* TopNewsWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272FC5AE2979E9D60027C53D /* TopNewsWidget.swift */; }; 27462BEA29DF39EE00715268 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = 27462BE929DF39EE00715268 /* OrderedCollections */; }; 27466BB1288EDB9C00584C90 /* RuntimeWarnings in Frameworks */ = {isa = PBXBuildFile; productRef = 27466BB0288EDB9C00584C90 /* RuntimeWarnings */; }; + 2749F5CA2D088E6500A5DAAB /* BraveCore in Frameworks */ = {isa = PBXBuildFile; productRef = 2749F5C92D088E6500A5DAAB /* BraveCore */; }; 2759468D29CCB62F0094BDDE /* GRDWireGuardKit in Frameworks */ = {isa = PBXBuildFile; productRef = 2759468C29CCB62F0094BDDE /* GRDWireGuardKit */; }; 2759468F29CCB6350094BDDE /* GRDWireGuardKit in Frameworks */ = {isa = PBXBuildFile; productRef = 2759468E29CCB6350094BDDE /* GRDWireGuardKit */; }; 2787B06A2B7ED260004796F8 /* BraveCore in Frameworks */ = {isa = PBXBuildFile; productRef = 2787B0692B7ED260004796F8 /* BraveCore */; }; @@ -200,6 +201,7 @@ 273DFB18284FDB1D007781AF /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = Shortcuts/ja.lproj/BrowserIntents.strings; sourceTree = ""; }; 27444B6A29831BBE002E1EBE /* topics_news.en_US.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = topics_news.en_US.json; sourceTree = ""; }; 27465DC3284FDB6F0056FDB2 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = Shortcuts/sv.lproj/BrowserIntents.strings; sourceTree = ""; }; + 2749F5B72D088E0100A5DAAB /* WebShell.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WebShell.app; sourceTree = BUILT_PRODUCTS_DIR; }; 275A4C39284FDB290016F5A8 /* ko-KR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ko-KR"; path = "Shortcuts/ko-KR.lproj/BrowserIntents.strings"; sourceTree = ""; }; 276FEA8B284FDB7A001A0718 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = Shortcuts/tr.lproj/BrowserIntents.strings; sourceTree = ""; }; 277CA004284FDB34002E8470 /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = Shortcuts/ms.lproj/BrowserIntents.strings; sourceTree = ""; }; @@ -482,6 +484,20 @@ F926647A234D4EF400359492 /* CoreNFC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreNFC.framework; path = System/Library/Frameworks/CoreNFC.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 2749F5CC2D08904200A5DAAB /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = 2749F5B62D088E0100A5DAAB /* WebShell */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 2749F5B82D088E0100A5DAAB /* WebShell */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2749F5CC2D08904200A5DAAB /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = WebShell; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 2714EE662AF2D4CD002090A9 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -493,6 +509,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2749F5B42D088E0100A5DAAB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2749F5CA2D088E6500A5DAAB /* BraveCore in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 27F443922135E11200296C58 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -805,6 +829,7 @@ CA0391B7271E1023000EB13C /* BraveWidgets */, 2FD860C329C3D686005AADD1 /* BraveWireGuard */, 2714EE6C2AF2D4CD002090A9 /* AutoFillProvider */, + 2749F5B82D088E0100A5DAAB /* WebShell */, 7B604FC11C496005006EEEC3 /* Frameworks */, F84B21BF1A090F8100AAB793 /* Products */, ); @@ -823,6 +848,7 @@ 2FD860C129C3D677005AADD1 /* BraveWireGuard.appex */, 2FE7D51B2B509DA40039FBA4 /* ActionExtension.appex */, 2714EE692AF2D4CD002090A9 /* AutoFillProvider.appex */, + 2749F5B72D088E0100A5DAAB /* WebShell.app */, ); name = Products; sourceTree = ""; @@ -861,6 +887,29 @@ productReference = 2714EE692AF2D4CD002090A9 /* AutoFillProvider.appex */; productType = "com.apple.product-type.app-extension"; }; + 2749F5B62D088E0100A5DAAB /* WebShell */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2749F5C82D088E0200A5DAAB /* Build configuration list for PBXNativeTarget "WebShell" */; + buildPhases = ( + 2749F5B32D088E0100A5DAAB /* Sources */, + 2749F5B42D088E0100A5DAAB /* Frameworks */, + 2749F5B52D088E0100A5DAAB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 2749F5B82D088E0100A5DAAB /* WebShell */, + ); + name = WebShell; + packageProductDependencies = ( + 2749F5C92D088E6500A5DAAB /* BraveCore */, + ); + productName = WebShell; + productReference = 2749F5B72D088E0100A5DAAB /* WebShell.app */; + productType = "com.apple.product-type.application"; + }; 27F443942135E11200296C58 /* ShareExtension */ = { isa = PBXNativeTarget; buildConfigurationList = 27F443AB2135E11200296C58 /* Build configuration list for PBXNativeTarget "ShareExtension" */; @@ -1020,13 +1069,16 @@ attributes = { DefaultBuildSystemTypeForWorkspace = Latest; LastSwiftMigration = 0700; - LastSwiftUpdateCheck = 1530; + LastSwiftUpdateCheck = 1620; LastUpgradeCheck = 1250; ORGANIZATIONNAME = Mozilla; TargetAttributes = { 2714EE682AF2D4CD002090A9 = { CreatedOnToolsVersion = 15.0.1; }; + 2749F5B62D088E0100A5DAAB = { + CreatedOnToolsVersion = 16.2; + }; 27F443942135E11200296C58 = { CreatedOnToolsVersion = 10.0; LastSwiftMigration = 1010; @@ -1129,6 +1181,7 @@ CA0391B1271E1023000EB13C /* BraveWidgetsExtension */, 2FD860C029C3D676005AADD1 /* BraveWireGuard */, 2714EE682AF2D4CD002090A9 /* AutoFillProvider */, + 2749F5B62D088E0100A5DAAB /* WebShell */, ); }; /* End PBXProject section */ @@ -1143,6 +1196,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2749F5B52D088E0100A5DAAB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 27F443932135E11200296C58 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1230,6 +1290,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2749F5B32D088E0100A5DAAB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 27F443912135E11200296C58 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1807,6 +1874,266 @@ }; name = Beta; }; + 2749F5C22D088E0200A5DAAB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = WebShell/WebShell.entitlements; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1.0; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = KL8N8XSYF4; + ENABLE_PREVIEWS = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = WebShell/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.brave.ios.WebShell; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 2749F5C32D088E0200A5DAAB /* Debug (AppStore) */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = WebShell/WebShell.entitlements; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1.0; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = KL8N8XSYF4; + ENABLE_PREVIEWS = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = WebShell/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.brave.ios.WebShell; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Debug (AppStore)"; + }; + 2749F5C42D088E0200A5DAAB /* Nightly */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = WebShell/WebShell.entitlements; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1.0; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = KL8N8XSYF4; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = WebShell/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.brave.ios.WebShell; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Nightly; + }; + 2749F5C52D088E0200A5DAAB /* Release (AppStore) */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = WebShell/WebShell.entitlements; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1.0; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = KL8N8XSYF4; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = WebShell/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.brave.ios.WebShell; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = "Release (AppStore)"; + }; + 2749F5C62D088E0200A5DAAB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = WebShell/WebShell.entitlements; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1.0; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = KL8N8XSYF4; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = WebShell/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.brave.ios.WebShell; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 2749F5C72D088E0200A5DAAB /* Beta */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = WebShell/WebShell.entitlements; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1.0; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = KL8N8XSYF4; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = WebShell/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.brave.ios.WebShell; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Beta; + }; 2774D3712B682277008A5F42 /* Nightly */ = { isa = XCBuildConfiguration; buildSettings = { @@ -3502,6 +3829,19 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Debug; }; + 2749F5C82D088E0200A5DAAB /* Build configuration list for PBXNativeTarget "WebShell" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2749F5C22D088E0200A5DAAB /* Debug */, + 2749F5C32D088E0200A5DAAB /* Debug (AppStore) */, + 2749F5C42D088E0200A5DAAB /* Nightly */, + 2749F5C52D088E0200A5DAAB /* Release (AppStore) */, + 2749F5C62D088E0200A5DAAB /* Release */, + 2749F5C72D088E0200A5DAAB /* Beta */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; 27F443AB2135E11200296C58 /* Build configuration list for PBXNativeTarget "ShareExtension" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -3644,6 +3984,10 @@ isa = XCSwiftPackageProductDependency; productName = RuntimeWarnings; }; + 2749F5C92D088E6500A5DAAB /* BraveCore */ = { + isa = XCSwiftPackageProductDependency; + productName = BraveCore; + }; 2759468C29CCB62F0094BDDE /* GRDWireGuardKit */ = { isa = XCSwiftPackageProductDependency; productName = GRDWireGuardKit; diff --git a/ios/brave-ios/App/Client.xcodeproj/xcshareddata/xcschemes/WebShell.xcscheme b/ios/brave-ios/App/Client.xcodeproj/xcshareddata/xcschemes/WebShell.xcscheme new file mode 100644 index 00000000000..bec05c43fe5 --- /dev/null +++ b/ios/brave-ios/App/Client.xcodeproj/xcshareddata/xcschemes/WebShell.xcscheme @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/brave-ios/App/WebShell/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/brave-ios/App/WebShell/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000000..eb878970081 --- /dev/null +++ b/ios/brave-ios/App/WebShell/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/brave-ios/App/WebShell/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/brave-ios/App/WebShell/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000000..2305880107d --- /dev/null +++ b/ios/brave-ios/App/WebShell/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/brave-ios/App/WebShell/Assets.xcassets/Contents.json b/ios/brave-ios/App/WebShell/Assets.xcassets/Contents.json new file mode 100644 index 00000000000..73c00596a7f --- /dev/null +++ b/ios/brave-ios/App/WebShell/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/brave-ios/App/WebShell/ContentView.swift b/ios/brave-ios/App/WebShell/ContentView.swift new file mode 100644 index 00000000000..632860d5572 --- /dev/null +++ b/ios/brave-ios/App/WebShell/ContentView.swift @@ -0,0 +1,193 @@ +// 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 http://mozilla.org/MPL/2.0/. + +import BraveCore +import SwiftUI + +private let braveCoreMain: BraveCoreMain = { + let main = BraveCoreMain(userAgent: nil) + main.scheduleLowPriorityStartupTasks() + return main +}() + +// Basic nav delegate to test some Brave specific features in the web shell app +private class NavDelegate: NSObject, BraveWebViewNavigationDelegate { + func webView(_ webView: CWVWebView, shouldBlockJavaScriptFor request: URLRequest) -> Bool { + return true + } + func webView(_ webView: CWVWebView, shouldBlockUniversalLinksFor request: URLRequest) -> Bool { + return true + } +} + +// A basic observable model to bind observable CWVWebView changes to the SwiftUI view +@dynamicMemberLookup +class WebViewModel: ObservableObject { + private let delegate = NavDelegate() + + @Published var webView: BraveWebView { + didSet { + setUpObservers() + self.webView.navigationDelegate = delegate + } + } + + init(webView: BraveWebView) { + self.webView = webView + setUpObservers() + self.webView.navigationDelegate = delegate + } + + private func setUpObservers() { + func subscriber(for keyPath: KeyPath) -> NSKeyValueObservation { + return webView.observe(keyPath, options: [.prior]) { _, change in + if change.isPrior { + self.objectWillChange.send() + } + } + } + observers = [ + subscriber(for: \.title), + subscriber(for: \.visibleURL), + subscriber(for: \.visibleLocationString), + subscriber(for: \.lastCommittedURL), + subscriber(for: \.isLoading), + subscriber(for: \.estimatedProgress), + subscriber(for: \.visibleSSLStatus), + subscriber(for: \.canGoBack), + subscriber(for: \.canGoForward), + ] + } + + private var observers: [NSKeyValueObservation] = [] + + public subscript(dynamicMember keyPath: KeyPath) -> T { + webView[keyPath: keyPath] + } +} + +struct WebView: UIViewRepresentable { + var webView: BraveWebView + + func makeUIView(context: Context) -> BraveWebView { + webView + } + func updateUIView(_ uiView: BraveWebView, context: Context) { + } +} + +struct ContentView: View { + @StateObject private var model: WebViewModel = .init( + webView: .init(frame: .zero, configuration: braveCoreMain.defaultWebViewConfiguration) + ) + @State private var isPresentingAlert: Bool = false + @State private var urlSubmitText: String = "" + + var body: some View { + NavigationStack { + VStack(spacing: 0) { + WebView(webView: model.webView) + Divider() + VStack { + Button { + isPresentingAlert = true + } label: { + HStack { + Group { + if let status = model.visibleSSLStatus { + switch status.securityStyle { + case .unknown: + Image(systemName: "exclamationmark.circle.fill") + case .authenticationBroken: + Image(systemName: "exclamationmark.octagon.fill") + case .unauthenticated: + Image(systemName: "exclamationmark.triangle.fill") + case .authenticated: + if !status.hasOnlySecureContent { + Image(systemName: "exclamationmark.triangle") + } else { + EmptyView() + } + @unknown default: + EmptyView() + } + } + } + .font(.caption) + VStack { + Text(model.visibleLocationString) + .font(.callout) + if let visibleURL = model.visibleURL, + visibleURL.absoluteString != model.visibleLocationString + { + Text(visibleURL.absoluteString) + .font(.caption) + .lineLimit(1) + } + } + } + .foregroundStyle(.primary) + } + HStack(spacing: 16) { + Button { + model.webView.goBack() + } label: { + Image(systemName: "chevron.left") + } + .disabled(!model.canGoBack) + Button { + model.webView.goForward() + } label: { + Image(systemName: "chevron.right") + } + .disabled(!model.canGoForward) + Spacer() + Button { + model.webView.reload() + } label: { + Image(systemName: "arrow.clockwise") + } + } + } + .padding(.horizontal, 16) + .padding(.vertical, 8) + .imageScale(.large) + .background(Material.bar) + } + } + .alert( + "URL", + isPresented: $isPresentingAlert, + actions: { + TextField("URL or search term", text: $urlSubmitText) + .autocorrectionDisabled() + .textInputAutocapitalization(.never) + .keyboardType(.URL) + Button("Cancel", role: .cancel) { + urlSubmitText = "" + } + Button("Submit") { + let classification = AutocompleteClassifier.classify(urlSubmitText) + if let url = classification?.destinationURL { + model.webView.load(URLRequest(url: url)) + } + urlSubmitText = "" + } + }, + message: { + + } + ) + .onAppear { + DispatchQueue.main.async { + model.webView.load(URLRequest(url: URL(string: "https://search.brave.com")!)) + } + } + } +} + +#Preview { + ContentView() +} diff --git a/ios/brave-ios/App/WebShell/Info.plist b/ios/brave-ios/App/WebShell/Info.plist new file mode 100644 index 00000000000..8923780b062 --- /dev/null +++ b/ios/brave-ios/App/WebShell/Info.plist @@ -0,0 +1,13 @@ + + + + + KSApplicationGroup + group.brave.ios.webshell + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + + diff --git a/ios/brave-ios/App/WebShell/WebShell.entitlements b/ios/brave-ios/App/WebShell/WebShell.entitlements new file mode 100644 index 00000000000..ff9f66523a4 --- /dev/null +++ b/ios/brave-ios/App/WebShell/WebShell.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.brave.ios.webshell + + + diff --git a/ios/brave-ios/App/WebShell/WebShellApp.swift b/ios/brave-ios/App/WebShell/WebShellApp.swift new file mode 100644 index 00000000000..c1045b55772 --- /dev/null +++ b/ios/brave-ios/App/WebShell/WebShellApp.swift @@ -0,0 +1,15 @@ +// 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 http://mozilla.org/MPL/2.0/. + +import SwiftUI + +@main +struct WebShellApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/ios/browser/BUILD.gn b/ios/browser/BUILD.gn index 8b80cdac708..99dc6a6216f 100644 --- a/ios/browser/BUILD.gn +++ b/ios/browser/BUILD.gn @@ -6,14 +6,7 @@ import("//build/config/ios/rules.gni") import("//ios/build/config.gni") -source_set("browser") { - sources = [ - "brave_web_client.h", - "brave_web_client.mm", - "brave_web_main_parts.h", - "brave_web_main_parts.mm", - ] - +group("browser") { deps = [ "api/ai_chat", "api/bookmarks", @@ -24,6 +17,7 @@ source_set("browser") { "api/brave_shields", "api/brave_wallet:wallet_mojom_wrappers", "api/certificate", + "api/content_settings", "api/credential_provider", "api/developer_options_code", "api/favicon", @@ -46,40 +40,15 @@ source_set("browser") { "api/version_info", "api/web", "api/web_image", + "api/web_view", "brave_wallet", - "//base", "//brave/browser/sync", - "//brave/components/ai_chat/core/browser", - "//brave/components/brave_component_updater/browser", - "//brave/components/brave_wallet/browser", "//brave/components/constants", "//brave/ios/browser/application_context", "//brave/ios/browser/brave_wallet", "//brave/ios/browser/profile/model", "//brave/ios/browser/ui/webui", - "//components/component_updater/installer_policies", - "//components/flags_ui", - "//components/metrics", - "//components/metrics_services_manager", - "//components/variations", - "//components/variations/service", - "//ios/chrome/browser/application_context/model", - "//ios/chrome/browser/flags", - "//ios/chrome/browser/flags:client_side", - "//ios/chrome/browser/profile/model:keyed_service_factories", - "//ios/chrome/browser/providers/raccoon:chromium_raccoon", - "//ios/chrome/browser/shared/model/paths", + "//brave/ios/browser/web", "//ios/chrome/browser/shared/model/prefs", - "//ios/chrome/browser/shared/model/profile", - "//ios/chrome/browser/shared/model/url:constants", - "//ios/chrome/browser/web/model:web_internal", - "//ios/components/webui:url_constants", - "//ios/public/provider/chrome/browser/url_rewriters:url_rewriters_api", - "//ios/web/public", - "//ios/web/public/init", - "//ios/web/public/navigation", - "//ios/web/public/thread", - "//ui/base", - "//url", ] } diff --git a/ios/browser/DEPS b/ios/browser/DEPS index ec6678a299d..90e525a04f0 100644 --- a/ios/browser/DEPS +++ b/ios/browser/DEPS @@ -10,4 +10,5 @@ include_rules += [ "+brave/components/p3a", "+ios/chrome/browser", "+ios/web/web_state", + "+ios/web_view", ] diff --git a/ios/browser/api/content_settings/BUILD.gn b/ios/browser/api/content_settings/BUILD.gn new file mode 100644 index 00000000000..4f2686a4e13 --- /dev/null +++ b/ios/browser/api/content_settings/BUILD.gn @@ -0,0 +1,16 @@ +# 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("content_settings") { + sources = [ + "default_host_content_settings.h", + "default_host_content_settings.mm", + "default_host_content_settings_internal.h", + ] + deps = [ + "//base", + "//components/content_settings/core/browser", + ] +} diff --git a/ios/browser/api/content_settings/default_host_content_settings.h b/ios/browser/api/content_settings/default_host_content_settings.h new file mode 100644 index 00000000000..4fb47c3f03a --- /dev/null +++ b/ios/browser/api/content_settings/default_host_content_settings.h @@ -0,0 +1,30 @@ +// 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_CONTENT_SETTINGS_DEFAULT_HOST_CONTENT_SETTINGS_H_ +#define BRAVE_IOS_BROWSER_API_CONTENT_SETTINGS_DEFAULT_HOST_CONTENT_SETTINGS_H_ + +#import + +NS_ASSUME_NONNULL_BEGIN + +// The mode in which pages should be loaded. +typedef NS_ENUM(NSUInteger, DefaultPageMode) { + DefaultPageModeMobile, + DefaultPageModeDesktop, +}; + +/// A list of default website preferences. +OBJC_EXPORT +@interface DefaultHostContentSettings : NSObject + +/// The default page mode in which pages should be loaded. +@property(nonatomic) DefaultPageMode defaultPageMode; + +@end + +NS_ASSUME_NONNULL_END + +#endif // BRAVE_IOS_BROWSER_API_CONTENT_SETTINGS_DEFAULT_HOST_CONTENT_SETTINGS_H_ diff --git a/ios/browser/api/content_settings/default_host_content_settings.mm b/ios/browser/api/content_settings/default_host_content_settings.mm new file mode 100644 index 00000000000..3e9f775b625 --- /dev/null +++ b/ios/browser/api/content_settings/default_host_content_settings.mm @@ -0,0 +1,36 @@ +// 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/content_settings/default_host_content_settings.h" + +#include "base/memory/scoped_refptr.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" + +@implementation DefaultHostContentSettings { + scoped_refptr _settingsMap; +} + +- (instancetype)initWithSettingsMap:(HostContentSettingsMap*)settingsMap { + if ((self = [super init])) { + _settingsMap = settingsMap; + } + return self; +} + +- (DefaultPageMode)defaultPageMode { + auto setting = _settingsMap->GetDefaultContentSetting( + ContentSettingsType::REQUEST_DESKTOP_SITE, nullptr); + return setting == CONTENT_SETTING_ALLOW ? DefaultPageModeDesktop + : DefaultPageModeMobile; +} + +- (void)setDefaultPageMode:(DefaultPageMode)defaultPageMode { + _settingsMap->SetDefaultContentSetting( + ContentSettingsType::REQUEST_DESKTOP_SITE, + defaultPageMode == DefaultPageModeDesktop ? CONTENT_SETTING_ALLOW + : CONTENT_SETTING_BLOCK); +} + +@end diff --git a/ios/browser/api/content_settings/default_host_content_settings_internal.h b/ios/browser/api/content_settings/default_host_content_settings_internal.h new file mode 100644 index 00000000000..e239d615a2d --- /dev/null +++ b/ios/browser/api/content_settings/default_host_content_settings_internal.h @@ -0,0 +1,18 @@ +// 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_CONTENT_SETTINGS_DEFAULT_HOST_CONTENT_SETTINGS_INTERNAL_H_ +#define BRAVE_IOS_BROWSER_API_CONTENT_SETTINGS_DEFAULT_HOST_CONTENT_SETTINGS_INTERNAL_H_ + +#include "brave/ios/browser/api/content_settings/default_host_content_settings.h" + +class HostContentSettingsMap; + +@interface DefaultHostContentSettings () +- (instancetype)initWithSettingsMap:(HostContentSettingsMap*)settingsMap + NS_DESIGNATED_INITIALIZER; +@end + +#endif // BRAVE_IOS_BROWSER_API_CONTENT_SETTINGS_DEFAULT_HOST_CONTENT_SETTINGS_INTERNAL_H_ diff --git a/ios/browser/api/content_settings/headers.gni b/ios/browser/api/content_settings/headers.gni new file mode 100644 index 00000000000..b2eaa46d497 --- /dev/null +++ b/ios/browser/api/content_settings/headers.gni @@ -0,0 +1,8 @@ +# 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/. + +content_settings_public_headers = [ + "//brave/ios/browser/api/content_settings/default_host_content_settings.h", +] diff --git a/ios/browser/api/url/url_formatter.mm b/ios/browser/api/url/url_formatter.mm index 5bc4097ee90..04314c350ef 100644 --- a/ios/browser/api/url/url_formatter.mm +++ b/ios/browser/api/url/url_formatter.mm @@ -31,6 +31,26 @@ BraveURLSchemeDisplay const BraveURLSchemeDisplayOmitCryptographic = // MARK: - Implementation +namespace { +constexpr char16_t kChromeSchema16[] = u"chrome://"; +constexpr char16_t kBraveSchema16[] = u"brave://"; +} // namespace + +namespace brave_utils { + +bool ReplaceChromeToBraveScheme(std::u16string& url_string) { + if (base::StartsWith(url_string, kChromeSchema16, + base::CompareCase::INSENSITIVE_ASCII)) { + base::ReplaceFirstSubstringAfterOffset(&url_string, 0, kChromeSchema16, + kBraveSchema16); + return true; + } + + return false; +} + +} // namespace brave_utils + @implementation BraveURLFormatter + (NSString*)formatURLOriginForSecurityDisplay:(NSString*)origin schemeDisplay: @@ -38,6 +58,7 @@ BraveURLSchemeDisplay const BraveURLSchemeDisplayOmitCryptographic = std::u16string result = url_formatter::FormatUrlForSecurityDisplay( GURL(base::SysNSStringToUTF8(origin)), static_cast(schemeDisplay)); + brave_utils::ReplaceChromeToBraveScheme(result); return base::SysUTF16ToNSString(result) ?: @""; } @@ -46,6 +67,7 @@ BraveURLSchemeDisplay const BraveURLSchemeDisplayOmitCryptographic = std::u16string result = url_formatter::FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains( GURL(base::SysNSStringToUTF8(origin))); + brave_utils::ReplaceChromeToBraveScheme(result); return base::SysUTF16ToNSString(result) ?: @""; } @@ -57,6 +79,7 @@ BraveURLSchemeDisplay const BraveURLSchemeDisplayOmitCryptographic = static_cast(formatTypes), static_cast(unescapeOptions), nullptr, nullptr, nullptr); + brave_utils::ReplaceChromeToBraveScheme(result); return base::SysUTF16ToNSString(result) ?: @""; } @end diff --git a/ios/browser/api/web_view/BUILD.gn b/ios/browser/api/web_view/BUILD.gn new file mode 100644 index 00000000000..ac6aacc173e --- /dev/null +++ b/ios/browser/api/web_view/BUILD.gn @@ -0,0 +1,24 @@ +# Copyright (c) 2025 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("web_view") { + sources = [ + "brave_web_view.h", + "brave_web_view.mm", + "crw_wk_navigation_handler_impl.mm", + ] + deps = [ + "//base", + "//brave/ios/web_view", + "//ios/chrome/browser/tabs/model", + "//ios/web/common", + "//ios/web/web_state", + "//ios/web_view:web_view_sources", + ] + frameworks = [ + "Foundation.framework", + "WebKit.framework", + ] +} diff --git a/ios/browser/api/web_view/brave_web_view.h b/ios/browser/api/web_view/brave_web_view.h new file mode 100644 index 00000000000..7c4b9f9ece5 --- /dev/null +++ b/ios/browser/api/web_view/brave_web_view.h @@ -0,0 +1,52 @@ +// Copyright (c) 2025 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_WEB_VIEW_BRAVE_WEB_VIEW_H_ +#define BRAVE_IOS_BROWSER_API_WEB_VIEW_BRAVE_WEB_VIEW_H_ + +#import +#import + +#import "cwv_export.h" // NOLINT +#import "cwv_navigation_delegate.h" // NOLINT +#import "cwv_web_view.h" // NOLINT + +NS_ASSUME_NONNULL_BEGIN + +/// Additional navigation delegate methods that extend functionality of +/// CWVWebView +CWV_EXPORT +@protocol BraveWebViewNavigationDelegate +@optional +/// Decides whether or not universal links should be blocked for a given request +- (BOOL)webView:(CWVWebView*)webView + shouldBlockUniversalLinksForRequest:(NSURLRequest*)request; +/// Decides whether or not JavaScript should be blocked on the resulting page +- (BOOL)webView:(CWVWebView*)webView + shouldBlockJavaScriptForRequest:(NSURLRequest*)request; +/// Notifies the delegate that basic authentication is required to access the +/// requested resource +- (void)webView:(CWVWebView*)webView + didRequestHTTPAuthForProtectionSpace:(NSURLProtectionSpace*)protectionSpace + proposedCredential:(NSURLCredential*)proposedCredential + completionHandler: + (void (^)(NSString* _Nullable username, + NSString* _Nullable password))handler; +@end + +/// A CWVWebView with Chrome tab helpers attached and the ability to handle +/// some Brave specific features +CWV_EXPORT +@interface BraveWebView : CWVWebView + +// This web view's navigation delegate. +@property(nonatomic, weak, nullable) id + navigationDelegate; + +@end + +NS_ASSUME_NONNULL_END + +#endif // BRAVE_IOS_BROWSER_API_WEB_VIEW_BRAVE_WEB_VIEW_H_ diff --git a/ios/browser/api/web_view/brave_web_view.mm b/ios/browser/api/web_view/brave_web_view.mm new file mode 100644 index 00000000000..78a5d27a458 --- /dev/null +++ b/ios/browser/api/web_view/brave_web_view.mm @@ -0,0 +1,57 @@ +// Copyright (c) 2025 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/web_view/brave_web_view.h" + +#include "base/notreached.h" +#include "ios/chrome/browser/tabs/model/tab_helper_util.h" +#include "ios/web/public/web_state.h" +#include "ios/web_view/internal/cwv_web_view_internal.h" + +@interface CWVWebView () +- (void)attachSecurityInterstitialHelpersToWebStateIfNecessary; +@end + +@implementation BraveWebView + +// These are shadowed CWVWebView properties +@dynamic navigationDelegate; + +- (void)attachSecurityInterstitialHelpersToWebStateIfNecessary { + [super attachSecurityInterstitialHelpersToWebStateIfNecessary]; + AttachTabHelpers(self.webState); +} + +- (CWVAutofillController*)autofillController { + NOTREACHED(); + return nil; +} + +- (CWVTranslationController*)translationController { + NOTREACHED(); + return nil; +} + +#pragma mark - CRWWebStateDelegate + +- (void)webState:(web::WebState*)webState + didRequestHTTPAuthForProtectionSpace:(NSURLProtectionSpace*)protectionSpace + proposedCredential:(NSURLCredential*)proposedCredential + completionHandler:(void (^)(NSString* username, + NSString* password))handler { + SEL selector = @selector(webView: + didRequestHTTPAuthForProtectionSpace:proposedCredential:completionHandler + :); + if ([self.navigationDelegate respondsToSelector:selector]) { + [self.navigationDelegate webView:self + didRequestHTTPAuthForProtectionSpace:protectionSpace + proposedCredential:proposedCredential + completionHandler:handler]; + } else { + handler(nil, nil); + } +} + +@end diff --git a/ios/browser/api/web_view/crw_wk_navigation_handler_impl.mm b/ios/browser/api/web_view/crw_wk_navigation_handler_impl.mm new file mode 100644 index 00000000000..d3f7983aa48 --- /dev/null +++ b/ios/browser/api/web_view/crw_wk_navigation_handler_impl.mm @@ -0,0 +1,52 @@ +// 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/. + +#import + +#import "brave/ios/browser/api/web_view/brave_web_view.h" +#include "ios/web/common/url_scheme_util.h" +#include "ios/web/public/web_state.h" +#import "ios/web_view/internal/cwv_web_view_internal.h" + +namespace brave { + +bool ShouldBlockJavaScript(web::WebState* webState, NSURLRequest* request) { + if (!web::UrlHasWebScheme(request.URL)) { + return false; + } + BraveWebView* webView = + static_cast([BraveWebView webViewForWebState:webState]); + if (!webView) { + return false; + } + id navigationDelegate = + webView.navigationDelegate; + + if ([navigationDelegate respondsToSelector:@selector + (webView:shouldBlockJavaScriptForRequest:)]) { + return [navigationDelegate webView:webView + shouldBlockJavaScriptForRequest:request]; + } + return false; +} + +bool ShouldBlockUniversalLinks(web::WebState* webState, NSURLRequest* request) { + BraveWebView* webView = + static_cast([BraveWebView webViewForWebState:webState]); + if (!webView) { + return false; + } + id navigationDelegate = + webView.navigationDelegate; + + if ([navigationDelegate respondsToSelector:@selector + (webView:shouldBlockUniversalLinksForRequest:)]) { + return [navigationDelegate webView:webView + shouldBlockUniversalLinksForRequest:request]; + } + return false; +} + +} // namespace brave diff --git a/ios/browser/api/web_view/headers.gni b/ios/browser/api/web_view/headers.gni new file mode 100644 index 00000000000..8b1dc02d934 --- /dev/null +++ b/ios/browser/api/web_view/headers.gni @@ -0,0 +1,7 @@ +# Copyright (c) 2025 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/. + +brave_web_view_public_headers = + [ "//brave/ios/browser/api/web_view/brave_web_view.h" ] diff --git a/ios/browser/brave_web_client.mm b/ios/browser/brave_web_client.mm deleted file mode 100644 index fa0e605e9ca..00000000000 --- a/ios/browser/brave_web_client.mm +++ /dev/null @@ -1,73 +0,0 @@ -/* Copyright (c) 2020 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 "brave/ios/browser/brave_web_client.h" - -#include "base/functional/bind.h" -#include "brave/components/constants/url_constants.h" -#include "brave/ios/browser/brave_web_main_parts.h" -#include "ios/chrome/browser/shared/model/url/chrome_url_constants.h" -#include "ios/components/webui/web_ui_url_constants.h" -#import "ios/public/provider/chrome/browser/url_rewriters/url_rewriters_api.h" -#import "ios/web/public/navigation/browser_url_rewriter.h" -#include "url/gurl.h" - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -BraveWebClient::BraveWebClient() {} - -BraveWebClient::~BraveWebClient() { -} - -std::unique_ptr BraveWebClient::CreateWebMainParts() { - return std::make_unique( - *base::CommandLine::ForCurrentProcess()); -} - -void BraveWebClient::SetUserAgent(const std::string& user_agent) { - user_agent_ = user_agent; -} - -std::string BraveWebClient::GetUserAgent(web::UserAgentType type) const { - if (user_agent_.empty()) { - return ChromeWebClient::GetUserAgent(type); - } - return user_agent_; -} - -void BraveWebClient::AddAdditionalSchemes(Schemes* schemes) const { - ChromeWebClient::AddAdditionalSchemes(schemes); - - schemes->standard_schemes.push_back(kBraveUIScheme); - schemes->secure_schemes.push_back(kBraveUIScheme); -} - -bool BraveWebClient::IsAppSpecificURL(const GURL& url) const { - return ChromeWebClient::IsAppSpecificURL(url) || url.SchemeIs(kBraveUIScheme); -} - -bool WillHandleBraveURLRedirect(GURL* url, web::BrowserState* browser_state) { - if (url->SchemeIs(kBraveUIScheme)) { - GURL::Replacements replacements; - replacements.SetSchemeStr(kChromeUIScheme); - *url = url->ReplaceComponents(replacements); - } - return false; -} - -std::vector BraveWebClient::GetJavaScriptFeatures( - web::BrowserState* browser_state) const { - // We don't use Chromium web views for anything but WebUI at the moment, so - // we don't need any JS features added. - return {}; -} - -void BraveWebClient::PostBrowserURLRewriterCreation( - web::BrowserURLRewriter* rewriter) { - rewriter->AddURLRewriter(&WillHandleBraveURLRedirect); - ChromeWebClient::PostBrowserURLRewriterCreation(rewriter); -} diff --git a/ios/browser/web/BUILD.gn b/ios/browser/web/BUILD.gn new file mode 100644 index 00000000000..5ea60b3ef78 --- /dev/null +++ b/ios/browser/web/BUILD.gn @@ -0,0 +1,37 @@ +# 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("web") { + sources = [ + "brave_web_client.h", + "brave_web_client.mm", + "brave_web_main_parts.h", + "brave_web_main_parts.mm", + ] + deps = [ + "//base", + "//brave/components/ai_chat/core/browser", + "//brave/components/brave_component_updater/browser", + "//brave/components/brave_wallet/browser", + "//brave/components/constants", + "//brave/ios/browser/application_context", + "//components/component_updater/installer_policies", + "//components/translate/ios/browser", + "//ios/chrome/browser/application_context/model", + "//ios/chrome/browser/providers/raccoon:chromium_raccoon", + "//ios/chrome/browser/shared/model/application_context", + "//ios/chrome/browser/shared/model/paths", + "//ios/chrome/browser/shared/model/url", + "//ios/chrome/browser/shared/model/url:constants", + "//ios/chrome/browser/web/model:web_internal", + "//ios/components/security_interstitials", + "//ios/components/security_interstitials/lookalikes", + "//ios/components/security_interstitials/safe_browsing", + "//ios/components/webui:url_constants", + "//ios/public/provider/chrome/browser/url_rewriters:url_rewriters_api", + "//ios/web_view:web_view_sources", + ] + frameworks = [ "Foundation.framework" ] +} diff --git a/ios/browser/brave_web_client.h b/ios/browser/web/brave_web_client.h similarity index 58% rename from ios/browser/brave_web_client.h rename to ios/browser/web/brave_web_client.h index f71eb62626a..5f9b262e52b 100644 --- a/ios/browser/brave_web_client.h +++ b/ios/browser/web/brave_web_client.h @@ -1,10 +1,10 @@ -/* Copyright (c) 2020 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/. */ +// Copyright (c) 2020 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_BRAVE_WEB_CLIENT_H_ -#define BRAVE_IOS_BROWSER_BRAVE_WEB_CLIENT_H_ +#ifndef BRAVE_IOS_BROWSER_WEB_BRAVE_WEB_CLIENT_H_ +#define BRAVE_IOS_BROWSER_WEB_BRAVE_WEB_CLIENT_H_ #include #include @@ -19,24 +19,26 @@ class BraveWebClient : public ChromeWebClient { BraveWebClient& operator=(const BraveWebClient&) = delete; ~BraveWebClient() override; - void SetUserAgent(const std::string& user_agent); + void SetLegacyUserAgent(const std::string& user_agent); // WebClient implementation. std::unique_ptr CreateWebMainParts() override; std::string GetUserAgent(web::UserAgentType type) const override; void AddAdditionalSchemes(Schemes* schemes) const override; - bool IsAppSpecificURL(const GURL& url) const override; std::vector GetJavaScriptFeatures( web::BrowserState* browser_state) const override; + bool EnableLongPressUIContextMenu() const override; + bool EnableWebInspector(web::BrowserState* browser_state) const override; + void PostBrowserURLRewriterCreation( web::BrowserURLRewriter* rewriter) override; private: - std::string user_agent_; + std::string legacy_user_agent_; }; -#endif // BRAVE_IOS_BROWSER_BRAVE_WEB_CLIENT_H_ +#endif // BRAVE_IOS_BROWSER_WEB_BRAVE_WEB_CLIENT_H_ diff --git a/ios/browser/web/brave_web_client.mm b/ios/browser/web/brave_web_client.mm new file mode 100644 index 00000000000..226e9fb9c38 --- /dev/null +++ b/ios/browser/web/brave_web_client.mm @@ -0,0 +1,95 @@ +// Copyright (c) 2020 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 "brave/ios/browser/web/brave_web_client.h" + +#include "base/check.h" +#include "base/functional/bind.h" +#include "base/ios/ns_error_util.h" +#include "base/strings/sys_string_conversions.h" +#include "brave/components/constants/url_constants.h" +#include "brave/ios/browser/web/brave_web_main_parts.h" +#import "components/translate/ios/browser/translate_java_script_feature.h" +#include "ios/chrome/browser/shared/model/url/chrome_url_constants.h" +#include "ios/chrome/browser/web/model/chrome_web_client.h" +#import "ios/components/security_interstitials/ios_security_interstitial_java_script_feature.h" +#import "ios/components/security_interstitials/lookalikes/lookalike_url_error.h" +#import "ios/components/security_interstitials/safe_browsing/safe_browsing_error.h" +#include "ios/components/webui/web_ui_url_constants.h" +#import "ios/public/provider/chrome/browser/url_rewriters/url_rewriters_api.h" +#import "ios/web/public/navigation/browser_url_rewriter.h" +#import "ios/web_view/internal/cwv_ssl_error_handler_internal.h" +#import "ios/web_view/internal/cwv_web_view_internal.h" +#import "ios/web_view/public/cwv_navigation_delegate.h" +#import "net/base/apple/url_conversions.h" +#include "url/gurl.h" + +BraveWebClient::BraveWebClient() {} + +BraveWebClient::~BraveWebClient() {} + +std::unique_ptr BraveWebClient::CreateWebMainParts() { + return std::make_unique( + *base::CommandLine::ForCurrentProcess()); +} + +std::string BraveWebClient::GetUserAgent(web::UserAgentType type) const { + if (!legacy_user_agent_.empty()) { + return legacy_user_agent_; + } + return ChromeWebClient::GetUserAgent(type); +} + +void BraveWebClient::AddAdditionalSchemes(Schemes* schemes) const { + ChromeWebClient::AddAdditionalSchemes(schemes); + + schemes->standard_schemes.push_back(kBraveUIScheme); + schemes->secure_schemes.push_back(kBraveUIScheme); +} + +bool BraveWebClient::IsAppSpecificURL(const GURL& url) const { + // temporarily add `internal://` scheme handling until those pages can be + // ported to WebUI + return ChromeWebClient::IsAppSpecificURL(url) || + url.SchemeIs(kBraveUIScheme) || url.SchemeIs("internal"); +} + +bool WillHandleBraveURLRedirect(GURL* url, web::BrowserState* browser_state) { + if (url->SchemeIs(kBraveUIScheme)) { + GURL::Replacements replacements; + replacements.SetSchemeStr(kChromeUIScheme); + *url = url->ReplaceComponents(replacements); + } + return false; +} + +std::vector BraveWebClient::GetJavaScriptFeatures( + web::BrowserState* browser_state) const { + // Add any JavaScriptFeature's from Chromium or Brave as needed + std::vector features; + features.push_back( + security_interstitials::IOSSecurityInterstitialJavaScriptFeature:: + GetInstance()); + return features; +} + +void BraveWebClient::PostBrowserURLRewriterCreation( + web::BrowserURLRewriter* rewriter) { + rewriter->AddURLRewriter(&WillHandleBraveURLRedirect); + ChromeWebClient::PostBrowserURLRewriterCreation(rewriter); +} + +bool BraveWebClient::EnableLongPressUIContextMenu() const { + return CWVWebView.chromeContextMenuEnabled; +} + +bool BraveWebClient::EnableWebInspector( + web::BrowserState* browser_state) const { + return CWVWebView.webInspectorEnabled; +} + +void BraveWebClient::SetLegacyUserAgent(const std::string& user_agent) { + legacy_user_agent_ = user_agent; +} diff --git a/ios/browser/brave_web_main_parts.h b/ios/browser/web/brave_web_main_parts.h similarity index 55% rename from ios/browser/brave_web_main_parts.h rename to ios/browser/web/brave_web_main_parts.h index 75f134f7dcf..df127881190 100644 --- a/ios/browser/brave_web_main_parts.h +++ b/ios/browser/web/brave_web_main_parts.h @@ -1,10 +1,10 @@ -/* Copyright (c) 2019 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/. */ +// Copyright (c) 2019 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_BRAVE_WEB_MAIN_PARTS_H_ -#define BRAVE_IOS_BROWSER_BRAVE_WEB_MAIN_PARTS_H_ +#ifndef BRAVE_IOS_BROWSER_WEB_BRAVE_WEB_MAIN_PARTS_H_ +#define BRAVE_IOS_BROWSER_WEB_BRAVE_WEB_MAIN_PARTS_H_ #include "ios/chrome/browser/web/model/chrome_main_parts.h" @@ -21,4 +21,4 @@ class BraveWebMainParts : public IOSChromeMainParts { void PreMainMessageLoopRun() override; }; -#endif // BRAVE_IOS_BROWSER_BRAVE_WEB_MAIN_PARTS_H_ +#endif // BRAVE_IOS_BROWSER_WEB_BRAVE_WEB_MAIN_PARTS_H_ diff --git a/ios/browser/brave_web_main_parts.mm b/ios/browser/web/brave_web_main_parts.mm similarity index 61% rename from ios/browser/brave_web_main_parts.mm rename to ios/browser/web/brave_web_main_parts.mm index 25d36396b64..6c11c0037a3 100644 --- a/ios/browser/brave_web_main_parts.mm +++ b/ios/browser/web/brave_web_main_parts.mm @@ -1,11 +1,11 @@ -/* Copyright (c) 2020 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/. */ +// Copyright (c) 2019 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/brave_web_main_parts.h" +#include "brave/ios/browser/web/brave_web_main_parts.h" -#include "base/metrics/user_metrics.h" +#include "base/command_line.h" #include "base/path_service.h" #include "base/strings/sys_string_conversions.h" #include "base/task/sequenced_task_runner.h" @@ -14,30 +14,12 @@ #include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h" #include "brave/components/brave_wallet/browser/wallet_data_files_installer.h" #include "brave/ios/browser/application_context/brave_application_context_impl.h" -#include "brave/ios/browser/profile/model/brave_keyed_service_factories.h" #include "components/component_updater/installer_policies/safety_tips_component_installer.h" -#include "components/flags_ui/pref_service_flags_storage.h" -#include "components/metrics/metrics_service.h" -#include "components/metrics_services_manager/metrics_services_manager.h" -#include "components/variations/service/variations_service.h" -#include "components/variations/synthetic_trial_registry.h" -#include "components/variations/synthetic_trials_active_group_id_provider.h" -#include "components/variations/variations_ids_provider.h" -#include "components/variations/variations_switches.h" #include "ios/chrome/browser/application_context/model/application_context_impl.h" -#include "ios/chrome/browser/flags/about_flags.h" -#include "ios/chrome/browser/profile/model/keyed_service_factories.h" #include "ios/chrome/browser/shared/model/paths/paths.h" -#include "ios/chrome/browser/shared/model/profile/profile_manager_ios.h" -#include "ios/web/public/thread/web_task_traits.h" -#include "ios/web/public/thread/web_thread.h" #include "ui/base/l10n/l10n_util_mac.h" #include "ui/base/resource/resource_bundle.h" -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - namespace { void RegisterComponentsForUpdate( component_updater::ComponentUpdateService* cus) { @@ -59,8 +41,7 @@ void BraveWebMainParts::PreCreateMainMessageLoop() { IOSChromeMainParts::PreCreateMainMessageLoop(); // Add Brave Resource Pack - base::FilePath brave_pack_path; - base::PathService::Get(base::DIR_ASSETS, &brave_pack_path); + auto brave_pack_path = base::PathService::CheckedGet(base::DIR_ASSETS); brave_pack_path = brave_pack_path.AppendASCII("brave_resources.pak"); ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath( brave_pack_path, ui::kScaleFactorNone); diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn new file mode 100644 index 00000000000..06cf2e5d692 --- /dev/null +++ b/ios/web_view/BUILD.gn @@ -0,0 +1,50 @@ +# 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/. + +import("//brave/ios/web_view/headers.gni") + +config("config") { + # TODO(crbug.com/40120082): This will only guarantee ios/web_view source files + # are extension safe. We also need to pass this flag to all all dependencies. + common_flags = [ "-fapplication-extension" ] + cflags_objc = common_flags + cflags_objcc = common_flags + defines = [ "CWV_IMPLEMENTATION" ] + frameworks = [ + "CoreGraphics.framework", + "Foundation.framework", + "MobileCoreServices.framework", + "UIKit.framework", + ] + + # For our own public headers to be able to import the official public headers + include_dirs = [ "//ios/web_view/public" ] +} + +source_set("web_view") { + sources = [ + "internal/cwv_ssl_status_extras.mm", + "internal/cwv_web_view_extras.mm", + "internal/cwv_x509_certificate_extras.mm", + "internal/web_view_global_state_util.mm", + "public/cwv_ssl_status_extras.h", + "public/cwv_web_view_extras.h", + "public/cwv_x509_certificate_extras.h", + ] + deps = [ + "//base", + "//components/ssl_errors", + "//ios/web/js_messaging", + "//ios/web/js_messaging:java_script_feature_manager_header", + "//ios/web/js_messaging:web_view_js_utils", + "//ios/web/web_state", + "//ios/web/web_state:web_state_impl_header", + "//ios/web/web_state/ui", + "//ios/web_view:web_view_sources", + "//net", + "//url", + ] + public_configs = [ ":config" ] +} diff --git a/ios/web_view/DEPS b/ios/web_view/DEPS new file mode 100644 index 00000000000..74d72de28cd --- /dev/null +++ b/ios/web_view/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+components/ssl_errors", + "+ios/web", + "+ios/web_view", +] diff --git a/ios/web_view/headers.gni b/ios/web_view/headers.gni new file mode 100644 index 00000000000..ead2f6a834d --- /dev/null +++ b/ios/web_view/headers.gni @@ -0,0 +1,69 @@ +# 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/. + +# This file exposes the //ios/web_view embedder as a regular source_set as well +# as any additions found in //brave/ios/web_view/public + +ios_web_view_public_headers = [ + "//brave/ios/web_view/public/cwv_ssl_status_extras.h", + "//brave/ios/web_view/public/cwv_x509_certificate_extras.h", + "//brave/ios/web_view/public/cwv_web_view_extras.h", + + "//ios/web_view/public/cwv_autofill_controller.h", + "//ios/web_view/public/cwv_autofill_controller_delegate.h", + "//ios/web_view/public/cwv_autofill_data_manager.h", + "//ios/web_view/public/cwv_autofill_data_manager_observer.h", + "//ios/web_view/public/cwv_autofill_form.h", + "//ios/web_view/public/cwv_autofill_profile.h", + "//ios/web_view/public/cwv_autofill_suggestion.h", + "//ios/web_view/public/cwv_back_forward_list.h", + "//ios/web_view/public/cwv_back_forward_list_item.h", + "//ios/web_view/public/cwv_cert_status.h", + "//ios/web_view/public/cwv_credential_provider_extension_utils.h", + "//ios/web_view/public/cwv_credit_card.h", + "//ios/web_view/public/cwv_credit_card_saver.h", + "//ios/web_view/public/cwv_credit_card_verifier.h", + "//ios/web_view/public/cwv_defines.h", + "//ios/web_view/public/cwv_download_task.h", + "//ios/web_view/public/cwv_export.h", + "//ios/web_view/public/cwv_favicon.h", + "//ios/web_view/public/cwv_find_in_page_controller.h", + "//ios/web_view/public/cwv_html_element.h", + "//ios/web_view/public/cwv_identity.h", + "//ios/web_view/public/cwv_leak_check_credential.h", + "//ios/web_view/public/cwv_leak_check_service.h", + "//ios/web_view/public/cwv_leak_check_service_observer.h", + "//ios/web_view/public/cwv_lookalike_url_handler.h", + "//ios/web_view/public/cwv_navigation_action.h", + "//ios/web_view/public/cwv_navigation_delegate.h", + "//ios/web_view/public/cwv_navigation_response.h", + "//ios/web_view/public/cwv_navigation_type.h", + "//ios/web_view/public/cwv_password.h", + "//ios/web_view/public/cwv_preferences.h", + "//ios/web_view/public/cwv_preview_element_info.h", + "//ios/web_view/public/cwv_reuse_check_service.h", + "//ios/web_view/public/cwv_ssl_error_handler.h", + "//ios/web_view/public/cwv_ssl_status.h", + "//ios/web_view/public/cwv_suggestion_type.h", + "//ios/web_view/public/cwv_sync_controller.h", + "//ios/web_view/public/cwv_sync_controller_data_source.h", + "//ios/web_view/public/cwv_sync_controller_delegate.h", + "//ios/web_view/public/cwv_sync_errors.h", + "//ios/web_view/public/cwv_translation_controller.h", + "//ios/web_view/public/cwv_translation_controller_delegate.h", + "//ios/web_view/public/cwv_translation_language.h", + "//ios/web_view/public/cwv_translation_policy.h", + "//ios/web_view/public/cwv_trusted_vault_observer.h", + "//ios/web_view/public/cwv_trusted_vault_provider.h", + "//ios/web_view/public/cwv_trusted_vault_utils.h", + "//ios/web_view/public/cwv_ui_delegate.h", + "//ios/web_view/public/cwv_unsafe_url_handler.h", + "//ios/web_view/public/cwv_user_content_controller.h", + "//ios/web_view/public/cwv_user_script.h", + "//ios/web_view/public/cwv_weak_check_utils.h", + "//ios/web_view/public/cwv_web_view.h", + "//ios/web_view/public/cwv_web_view_configuration.h", + "//ios/web_view/public/cwv_x509_certificate.h", +] diff --git a/ios/web_view/internal/cwv_ssl_status_extras.mm b/ios/web_view/internal/cwv_ssl_status_extras.mm new file mode 100644 index 00000000000..17bc2ba3d90 --- /dev/null +++ b/ios/web_view/internal/cwv_ssl_status_extras.mm @@ -0,0 +1,55 @@ +// 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/web_view/public/cwv_ssl_status_extras.h" + +#include + +#include "base/strings/sys_string_conversions.h" +#include "components/ssl_errors/error_info.h" +#include "ios/web_view/internal/cwv_ssl_status_internal.h" +#include "net/base/apple/url_conversions.h" +#include "net/cert/cert_status_flags.h" +#include "url/gurl.h" + +@interface CWVSSLErrorInformation () +@property(nonatomic, copy) NSString* details; +@property(nonatomic, copy) NSString* shortDescription; +@end + +@implementation CWVSSLErrorInformation + +- (instancetype)initWithErrorInfo:(ssl_errors::ErrorInfo)errorInfo { + if ((self = [super init])) { + self.details = base::SysUTF16ToNSString(errorInfo.details()); + self.shortDescription = + base::SysUTF16ToNSString(errorInfo.short_description()); + } + return self; +} + +@end + +@implementation CWVSSLStatus (Extras) + +- (BOOL)isCertStatusError { + return net::IsCertStatusError(self.internalStatus.cert_status); +} + +- (NSArray*)certStatusErrorsForURL:(NSURL*)url { + std::vector errors; + ssl_errors::ErrorInfo::GetErrorsForCertStatus( + self.internalStatus.certificate, self.internalStatus.cert_status, + net::GURLWithNSURL(url), &errors); + NSMutableArray* bridgedErrors = + [[NSMutableArray alloc] init]; + for (size_t i = 0; i < errors.size(); ++i) { + [bridgedErrors + addObject:[[CWVSSLErrorInformation alloc] initWithErrorInfo:errors[i]]]; + } + return [bridgedErrors copy]; +} + +@end diff --git a/ios/web_view/internal/cwv_web_view_extras.mm b/ios/web_view/internal/cwv_web_view_extras.mm new file mode 100644 index 00000000000..d1db5e0d988 --- /dev/null +++ b/ios/web_view/internal/cwv_web_view_extras.mm @@ -0,0 +1,131 @@ +/* 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/web_view/public/cwv_web_view_extras.h" + +#include "base/apple/foundation_util.h" +#include "base/json/json_writer.h" +#include "base/strings/sys_string_conversions.h" +#include "ios/web/common/user_agent.h" +#include "ios/web/js_messaging/java_script_feature_manager.h" +#include "ios/web/js_messaging/web_frame_internal.h" +#include "ios/web/js_messaging/web_view_js_utils.h" +#include "ios/web/public/js_messaging/web_frames_manager.h" +#include "ios/web/web_state/ui/crw_web_controller.h" +#include "ios/web/web_state/ui/wk_web_view_configuration_provider.h" +#include "ios/web/web_state/web_state_impl.h" +#include "ios/web_view/internal/cwv_web_view_configuration_internal.h" +#include "ios/web_view/internal/cwv_web_view_internal.h" +#include "ios/web_view/internal/web_view_browser_state.h" +#include "ios/web_view/public/cwv_navigation_delegate.h" + +const CWVUserAgentType CWVUserAgentTypeNone = + static_cast(web::UserAgentType::NONE); +const CWVUserAgentType CWVUserAgentTypeAutomatic = + static_cast(web::UserAgentType::AUTOMATIC); +const CWVUserAgentType CWVUserAgentTypeMobile = + static_cast(web::UserAgentType::MOBILE); +const CWVUserAgentType CWVUserAgentTypeDesktop = + static_cast(web::UserAgentType::DESKTOP); + +@implementation CWVWebView (Extras) + ++ (BOOL)isRestoreDataValid:(NSData*)data { + return [self _isRestoreDataValid:data]; +} + +- (void)updateScripts { + // This runs `UpdateScripts` on the configuration provider which we will need + // to call in-place of `-[WKUserContentController removeAllUserScripts]` until + // all Brave JavaScript features are ported over to actual Chromium + // JavascriptFeature types and added to BraveWebClient + web::WKWebViewConfigurationProvider& config_provider = + web::WKWebViewConfigurationProvider::FromBrowserState( + self.webState->GetBrowserState()); + config_provider.UpdateScripts(); +} + +- (void)createFullPagePDF:(void (^)(NSData* _Nullable))completionHandler { + self.webState->CreateFullPagePdf(base::BindOnce(completionHandler)); +} + +- (BOOL)canTakeSnapshot { + return self.webState->CanTakeSnapshot(); +} + +- (void)takeSnapshotWithRect:(CGRect)rect + completionHandler:(void (^)(UIImage* _Nullable))completionHandler { + self.webState->TakeSnapshot(rect, base::BindRepeating(completionHandler)); +} + +- (CWVUserAgentType)currentItemUserAgentType { + return static_cast(self.webState->GetNavigationManager() + ->GetVisibleItem() + ->GetUserAgentType()); +} + +- (void)reloadWithUserAgentType:(CWVUserAgentType)userAgentType { + self.webState->GetNavigationManager()->ReloadWithUserAgentType( + static_cast(userAgentType)); +} + +- (void)evaluateJavaScript:(NSString*)javaScriptString + contentWorld:(WKContentWorld*)contentWorld + completionHandler:(void (^)(id result, NSError* error))completion { + web::WebFrame* mainFrame = + self.webState->GetPageWorldWebFramesManager()->GetMainWebFrame(); + if (!mainFrame) { + if (completion) { + completion(nil, [NSError errorWithDomain:@"org.chromium.chromewebview" + code:0 + userInfo:nil]); + } + return; + } + web::WebFrameInternal* webFrame = mainFrame->GetWebFrameInternal(); + auto worlds = web::JavaScriptFeatureManager::FromBrowserState( + self.configuration.browserState) + ->GetAllContentWorlds(); + + auto jsContentWorld = + std::find_if(worlds.begin(), worlds.end(), [&contentWorld](auto world) { + return world->GetWKContentWorld() == contentWorld; + }); + if (jsContentWorld == worlds.end()) { + if (completion) { + completion(nil, [NSError errorWithDomain:@"org.chromium.chromewebview" + code:0 + userInfo:nil]); + } + return; + } + + webFrame->ExecuteJavaScriptInContentWorld( + base::SysNSStringToUTF16(javaScriptString), *jsContentWorld, + base::BindOnce(^(const base::Value* result, NSError* error) { + if (completion) { + id jsResult = nil; + if (!error && result) { + jsResult = [CWVWebView objectFromValue:result]; + } + completion(jsResult, error); + } + })); +} + +- (WKWebView*)internalWebView { + CRWWebController* web_controller = + web::WebStateImpl::FromWebState(self.webState)->GetWebController(); + return web_controller.webView; +} + +- (WKWebViewConfiguration*)WKConfiguration { + web::WKWebViewConfigurationProvider& config_provider = + web::WKWebViewConfigurationProvider::FromBrowserState( + self.webState->GetBrowserState()); + return config_provider.GetWebViewConfiguration(); +} + +@end diff --git a/ios/web_view/internal/cwv_x509_certificate_extras.mm b/ios/web_view/internal/cwv_x509_certificate_extras.mm new file mode 100644 index 00000000000..3b7d3985da3 --- /dev/null +++ b/ios/web_view/internal/cwv_x509_certificate_extras.mm @@ -0,0 +1,17 @@ +// 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 "ios/web_view/internal/cwv_x509_certificate_internal.h" +#include "net/cert/x509_util_apple.h" + +@implementation CWVX509Certificate (Extras) + +- (SecCertificateRef)certificateRef { + return net::x509_util::CreateSecCertificateFromX509Certificate( + self.internalCertificate.get()) + .release(); // Swift bridging should handle the lifetime +} + +@end diff --git a/ios/web_view/internal/web_view_global_state_util.mm b/ios/web_view/internal/web_view_global_state_util.mm new file mode 100644 index 00000000000..cc7a3db45e8 --- /dev/null +++ b/ios/web_view/internal/web_view_global_state_util.mm @@ -0,0 +1,26 @@ +// 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 "ios/web_view/internal/web_view_global_state_util.h" + +namespace ios_web_view { + +void InitializeGlobalState() { + // We already provide global initialization with -[BraveCoreMain init] so this + // is a stub of a required function. + // + // This function when implemented by the main `//ios/web_view` target would + // typically setup the required Chromium web infrastructure required to + // support CWVWebView: `WebMain`, `WebMainDelegate` and `WebClient` + // + // We already set this up in a similar fashion to Chrome iOS however so there + // is no need to do it here. + // + // Note: This method is removed in M135 and replaced with a new type that + // handles things similarily: `CWVGlobalState`. This new type will need a + // chromium_src override to remove said initialization +} + +} // namespace ios_web_view diff --git a/ios/web_view/public/cwv_ssl_status_extras.h b/ios/web_view/public/cwv_ssl_status_extras.h new file mode 100644 index 00000000000..3bb956f4d57 --- /dev/null +++ b/ios/web_view/public/cwv_ssl_status_extras.h @@ -0,0 +1,48 @@ +// 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_WEB_VIEW_PUBLIC_CWV_SSL_STATUS_EXTRAS_H_ +#define BRAVE_IOS_WEB_VIEW_PUBLIC_CWV_SSL_STATUS_EXTRAS_H_ + +#import + +#import "cwv_ssl_status.h" + +NS_ASSUME_NONNULL_BEGIN + +/// Describes an error that happened while showing a page over SSL. +OBJC_EXPORT +@interface CWVSSLErrorInformation : NSObject + +/// A description of the error. +@property(readonly) NSString* details; + +/// A short message describing the error (1 line). +@property(readonly) NSString* shortDescription; + +@end + +/// Adds additional functionality to CWVSSLStatus that is not be supported out +/// of the box but can be implemented using the underlying web::SSLStatus +OBJC_EXPORT +@interface CWVSSLStatus (Extras) + +/// Whether or not `certStatus` is an error +/// +/// It is possible to have `securityStyle` be `authenticationBroken` and +/// non-error `certStatus` for WKWebView because `securityStyle` and +/// `certStatus` are calculated using different API, which may lead to different +/// cert verification results. Check this before using error information from +/// `certStatusErrorsForURL` +@property(readonly) BOOL isCertStatusError; + +/// A list of error details for a given URL using this SSL certificate +- (NSArray*)certStatusErrorsForURL:(NSURL*)url; + +@end + +NS_ASSUME_NONNULL_END + +#endif // BRAVE_IOS_WEB_VIEW_PUBLIC_CWV_SSL_STATUS_EXTRAS_H_ diff --git a/ios/web_view/public/cwv_web_view_extras.h b/ios/web_view/public/cwv_web_view_extras.h new file mode 100644 index 00000000000..63b0c4d67fe --- /dev/null +++ b/ios/web_view/public/cwv_web_view_extras.h @@ -0,0 +1,88 @@ +/* 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_WEB_VIEW_PUBLIC_CWV_WEB_VIEW_EXTRAS_H_ +#define BRAVE_IOS_WEB_VIEW_PUBLIC_CWV_WEB_VIEW_EXTRAS_H_ + +#import +#import +#import + +#import "cwv_export.h" +#import "cwv_web_view.h" + +typedef NSInteger CWVUserAgentType NS_TYPED_ENUM; +OBJC_EXPORT const CWVUserAgentType CWVUserAgentTypeNone; +OBJC_EXPORT const CWVUserAgentType CWVUserAgentTypeAutomatic; +OBJC_EXPORT const CWVUserAgentType CWVUserAgentTypeMobile; +OBJC_EXPORT const CWVUserAgentType CWVUserAgentTypeDesktop; + +NS_ASSUME_NONNULL_BEGIN + +/// Adds additional functionality to CWVWebView that is not be supported out +/// of the box but can be implemented using the underlying WebState +CWV_EXPORT +@interface CWVWebView (Extras) + +/// The user agent type currently used on the page (e.g. mobile/desktop) +- (CWVUserAgentType)currentItemUserAgentType; + +/// Reloads the page with a specific user agent type +- (void)reloadWithUserAgentType:(CWVUserAgentType)userAgentType; + +#pragma mark - + +/// Creates a PDF of the current page +/// +/// Equivalent of -[WKWebView createPDFWithConfiguration:completionHandler:] +- (void)createFullPagePDF:(void (^)(NSData* _Nullable))completionHandler; + +/// Whether or not you can create a snapshot using `takeSnapshotWithRect` +- (BOOL)canTakeSnapshot; + +/// Creates an image from the current rendered page for a given CGRect +/// +/// Equivalent of -[WKWebView takeSnapshotWithConfiguration:completionHandler:] +- (void)takeSnapshotWithRect:(CGRect)rect + completionHandler:(void (^)(UIImage* _Nullable))completionHandler; + +/// Evaluates JavaScript on the page on a given WebKit content world +/// +/// Equivalent of -[WKWebView +/// evaluateJavaScript:contentWorld:completionHandler:] +- (void)evaluateJavaScript:(NSString*)javaScriptString + contentWorld:(WKContentWorld*)contentWorld + completionHandler: + (nullable void (^)(id _Nullable result, + NSError* _Nullable error))completion; + +#pragma mark - + +/// Determines if the data used to restore a CWVWebView is a WebState cache and +/// is valid. +/// +/// Used for migration purposes, can be removed in the future ++ (BOOL)isRestoreDataValid:(NSData*)data; + +/// Resets & reinjects all JavaScript features +- (void)updateScripts; + +/// The underlying WKWebView if one has been created already. +/// +/// This is only available for `use_blink=false` builds and be used for WebKit +/// specific paths. +@property(readonly, nullable) WKWebView* internalWebView; + +/// The underlying WKWebViewConfiguration for this CWVWebView +/// +/// This is only available for `use_blink=false` builds and be used for WebKit +/// specific paths. +@property(readonly) WKWebViewConfiguration* WKConfiguration; + +@end + +NS_ASSUME_NONNULL_END + +#endif // BRAVE_IOS_WEB_VIEW_PUBLIC_CWV_WEB_VIEW_EXTRAS_H_ diff --git a/ios/web_view/public/cwv_x509_certificate_extras.h b/ios/web_view/public/cwv_x509_certificate_extras.h new file mode 100644 index 00000000000..5e66653d728 --- /dev/null +++ b/ios/web_view/public/cwv_x509_certificate_extras.h @@ -0,0 +1,19 @@ +// 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_WEB_VIEW_PUBLIC_CWV_X509_CERTIFICATE_EXTRAS_H_ +#define BRAVE_IOS_WEB_VIEW_PUBLIC_CWV_X509_CERTIFICATE_EXTRAS_H_ + +#include "cwv_x509_certificate.h" // NOLINT + +/// Adds additional functionality to CWVX509Certificate that is not be supported +/// out of the box but can be implemented using the underlying +/// net::X509Certificate +@interface CWVX509Certificate (Extras) +/// The underlying security reference +@property(readonly, nullable) SecCertificateRef certificateRef; +@end + +#endif // BRAVE_IOS_WEB_VIEW_PUBLIC_CWV_X509_CERTIFICATE_EXTRAS_H_ diff --git a/ios/web_view/sources.gni b/ios/web_view/sources.gni new file mode 100644 index 00000000000..9931b2ac915 --- /dev/null +++ b/ios/web_view/sources.gni @@ -0,0 +1,22 @@ +# 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 exclusions for //ios/web_view +# - Excludes custom WebUI implementations as we use //ios/chrome's +# - Excludes the custom WebClient & WebMainParts as we don't use the +# //ios/web_view global state setup +brave_ios_web_view_excluded_sources = [ + "internal/web_view_web_client.h", + "internal/web_view_web_client.mm", + "internal/web_view_web_main_parts.h", + "internal/web_view_web_main_parts.mm", + "internal/webui/web_view_web_ui_ios_controller_factory.h", + "internal/webui/web_view_web_ui_ios_controller_factory.mm", + "internal/webui/web_view_web_ui_provider.mm", +] + +# Dep exclusions for //ios/web_view +# - Excludes the bundled resources since these are already bundled +brave_ios_web_view_excluded_public_deps = [ ":web_view_resources" ] diff --git a/patches/ios-web_view-BUILD.gn.patch b/patches/ios-web_view-BUILD.gn.patch new file mode 100644 index 00000000000..b38d7cdf3f9 --- /dev/null +++ b/patches/ios-web_view-BUILD.gn.patch @@ -0,0 +1,12 @@ +diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn +index 87d7a08bd5ef7c081169d2f56b0dc0b8416580b7..38f2fd8b0a65a927c97eddd4a4a1ce142f3ee2cd 100644 +--- a/ios/web_view/BUILD.gn ++++ b/ios/web_view/BUILD.gn +@@ -438,6 +438,7 @@ source_set("web_view_sources") { + "//components/version_info:channel", + ] + configs += [ ":config" ] ++ import("//brave/ios/web_view/sources.gni") sources -= brave_ios_web_view_excluded_sources public_deps -= brave_ios_web_view_excluded_public_deps + } + + action("web_view_umbrella_header") { diff --git a/patches/ios-web_view-public-cwv_web_view.h.patch b/patches/ios-web_view-public-cwv_web_view.h.patch new file mode 100644 index 00000000000..2ea1abc9eab --- /dev/null +++ b/patches/ios-web_view-public-cwv_web_view.h.patch @@ -0,0 +1,13 @@ +diff --git a/ios/web_view/public/cwv_web_view.h b/ios/web_view/public/cwv_web_view.h +index c17c6eb810f3122705cc0124e68e1b4a32cd10b8..c76bc2193fb3230585e719e35db730d24eee60e1 100644 +--- a/ios/web_view/public/cwv_web_view.h ++++ b/ios/web_view/public/cwv_web_view.h +@@ -71,7 +71,7 @@ CWV_EXPORT + // (not implemented for CWVWebView) + // |visibleURL| is the bad cert page URL. |lastCommittedURL| is the previous + // page URL. +-@property(nonatomic, readonly) NSURL* visibleURL; ++@property(nonatomic, readonly, nullable) NSURL* visibleURL; + + // A human-friendly string which represents the location of the document + // currently being loaded. KVO compliant.