| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "ios/web_view/internal/web_view_web_client.h" |
| |
| #import <dispatch/dispatch.h> |
| |
| #import <string_view> |
| |
| #import "base/apple/bundle_locations.h" |
| #import "base/check.h" |
| #import "base/functional/bind.h" |
| #import "base/functional/callback_helpers.h" |
| #import "base/ios/ns_error_util.h" |
| #import "base/strings/sys_string_conversions.h" |
| #import "components/autofill/ios/browser/autofill_java_script_feature.h" |
| #import "components/autofill/ios/browser/suggestion_controller_java_script_feature.h" |
| #import "components/autofill/ios/form_util/form_handlers_java_script_feature.h" |
| #import "components/autofill/ios/form_util/programmatic_form_submission_handler_java_script_feature.h" |
| #import "components/language/ios/browser/language_detection_java_script_feature.h" |
| #import "components/password_manager/ios/password_manager_java_script_feature.h" |
| #import "components/security_interstitials/core/unsafe_resource.h" |
| #import "components/ssl_errors/error_info.h" |
| #import "components/strings/grit/components_strings.h" |
| #import "components/translate/ios/browser/translate_java_script_feature.h" |
| #import "ios/components/security_interstitials/https_only_mode/feature.h" |
| #import "ios/components/security_interstitials/ios_security_interstitial_java_script_feature.h" |
| #import "ios/components/security_interstitials/lookalikes/lookalike_url_container.h" |
| #import "ios/components/security_interstitials/lookalikes/lookalike_url_error.h" |
| #import "ios/components/security_interstitials/safe_browsing/safe_browsing_error.h" |
| #import "ios/components/security_interstitials/safe_browsing/safe_browsing_unsafe_resource_container.h" |
| #import "ios/components/webui/web_ui_url_constants.h" |
| #import "ios/web/common/user_agent.h" |
| #import "ios/web/public/navigation/navigation_manager.h" |
| #import "ios/web/public/security/ssl_status.h" |
| #import "ios/web/public/thread/web_task_traits.h" |
| #import "ios/web/public/thread/web_thread.h" |
| #import "ios/web_view/internal/cwv_global_state_internal.h" |
| #import "ios/web_view/internal/cwv_lookalike_url_handler_internal.h" |
| #import "ios/web_view/internal/cwv_ssl_error_handler_internal.h" |
| #import "ios/web_view/internal/cwv_ssl_status_internal.h" |
| #import "ios/web_view/internal/cwv_ssl_util.h" |
| #import "ios/web_view/internal/cwv_web_view_internal.h" |
| #import "ios/web_view/internal/js_messaging/web_view_scripts_java_script_feature.h" |
| #import "ios/web_view/internal/safe_browsing/cwv_unsafe_url_handler_internal.h" |
| #import "ios/web_view/internal/web_view_browser_state.h" |
| #import "ios/web_view/internal/web_view_message_handler_java_script_feature.h" |
| #import "ios/web_view/internal/web_view_web_main_parts.h" |
| #import "ios/web_view/public/cwv_navigation_delegate.h" |
| #import "ios/web_view/public/cwv_web_view.h" |
| #import "net/base/apple/url_conversions.h" |
| #import "net/cert/cert_status_flags.h" |
| #import "ui/base/l10n/l10n_util.h" |
| #import "ui/base/resource/resource_bundle.h" |
| |
| namespace ios_web_view { |
| |
| WebViewWebClient::WebViewWebClient() = default; |
| |
| WebViewWebClient::~WebViewWebClient() = default; |
| |
| std::unique_ptr<web::WebMainParts> WebViewWebClient::CreateWebMainParts() { |
| return std::make_unique<WebViewWebMainParts>(); |
| } |
| |
| void WebViewWebClient::AddAdditionalSchemes(Schemes* schemes) const { |
| schemes->standard_schemes.push_back(kChromeUIScheme); |
| schemes->secure_schemes.push_back(kChromeUIScheme); |
| } |
| |
| bool WebViewWebClient::IsAppSpecificURL(const GURL& url) const { |
| return url.SchemeIs(kChromeUIScheme); |
| } |
| |
| std::string WebViewWebClient::GetUserAgent(web::UserAgentType type) const { |
| if (CWVGlobalState.sharedInstance.customUserAgent) { |
| return base::SysNSStringToUTF8( |
| CWVGlobalState.sharedInstance.customUserAgent); |
| } else { |
| return web::BuildMobileUserAgent(base::SysNSStringToUTF8( |
| CWVGlobalState.sharedInstance.userAgentProduct)); |
| } |
| } |
| |
| std::string_view WebViewWebClient::GetDataResource( |
| int resource_id, |
| ui::ResourceScaleFactor scale_factor) const { |
| return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale( |
| resource_id, scale_factor); |
| } |
| |
| base::RefCountedMemory* WebViewWebClient::GetDataResourceBytes( |
| int resource_id) const { |
| return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes( |
| resource_id); |
| } |
| |
| std::vector<web::JavaScriptFeature*> WebViewWebClient::GetJavaScriptFeatures( |
| web::BrowserState* browser_state) const { |
| std::vector<web::JavaScriptFeature*> features = { |
| autofill::AutofillJavaScriptFeature::GetInstance(), |
| autofill::FormHandlersJavaScriptFeature::GetInstance(), |
| autofill::SuggestionControllerJavaScriptFeature::GetInstance(), |
| language::LanguageDetectionJavaScriptFeature::GetInstance(), |
| password_manager::PasswordManagerJavaScriptFeature::GetInstance(), |
| security_interstitials::IOSSecurityInterstitialJavaScriptFeature:: |
| GetInstance(), |
| translate::TranslateJavaScriptFeature::GetInstance(), |
| WebViewMessageHandlerJavaScriptFeature::FromBrowserState(browser_state), |
| WebViewScriptsJavaScriptFeature::FromBrowserState(browser_state), |
| autofill::ProgrammaticFormSubmissionHandlerJavaScriptFeature:: |
| GetInstance()}; |
| |
| return features; |
| } |
| |
| void WebViewWebClient::PrepareErrorPage( |
| web::WebState* web_state, |
| const GURL& url, |
| NSError* error, |
| bool is_post, |
| bool is_off_the_record, |
| const std::optional<net::SSLInfo>& info, |
| int64_t navigation_id, |
| base::OnceCallback<void(NSString*)> callback) { |
| DCHECK(error); |
| |
| CWVWebView* web_view = [CWVWebView webViewForWebState:web_state]; |
| id<CWVNavigationDelegate> navigation_delegate = web_view.navigationDelegate; |
| |
| // |final_underlying_error| should be checked first for any specific error |
| // cases such as lookalikes and safebrowsing errors. |info| is only non-empty |
| // if this is a SSL related error. |
| NSError* final_underlying_error = |
| base::ios::GetFinalUnderlyingErrorFromError(error); |
| if ([final_underlying_error.domain isEqual:kSafeBrowsingErrorDomain] && |
| [navigation_delegate |
| respondsToSelector:@selector(webView:handleUnsafeURLWithHandler:)]) { |
| DCHECK_EQ(SafeBrowsingErrorCode::kUnsafeResource, |
| static_cast<SafeBrowsingErrorCode>(final_underlying_error.code)); |
| SafeBrowsingUnsafeResourceContainer* container = |
| SafeBrowsingUnsafeResourceContainer::FromWebState(web_state); |
| const security_interstitials::UnsafeResource* resource = |
| container->GetMainFrameUnsafeResource(); |
| CWVUnsafeURLHandler* handler = |
| [[CWVUnsafeURLHandler alloc] initWithWebState:web_state |
| unsafeResource:*resource |
| htmlCallback:std::move(callback)]; |
| [navigation_delegate webView:web_view handleUnsafeURLWithHandler:handler]; |
| } else if ([final_underlying_error.domain isEqual:kLookalikeUrlErrorDomain] && |
| [navigation_delegate respondsToSelector:@selector |
| (webView:handleLookalikeURLWithHandler:)]) { |
| DCHECK_EQ(kLookalikeUrlErrorCode, final_underlying_error.code); |
| LookalikeUrlContainer* container = |
| LookalikeUrlContainer::FromWebState(web_state); |
| std::unique_ptr<LookalikeUrlContainer::LookalikeUrlInfo> lookalike_info = |
| container->ReleaseLookalikeUrlInfo(); |
| CWVLookalikeURLHandler* handler = [[CWVLookalikeURLHandler alloc] |
| initWithWebState:web_state |
| lookalikeURLInfo:std::move(lookalike_info) |
| htmlCallback:std::move(callback)]; |
| [navigation_delegate webView:web_view |
| handleLookalikeURLWithHandler:handler]; |
| } else if (info.has_value() && |
| [navigation_delegate respondsToSelector:@selector |
| (webView:handleSSLErrorWithHandler:)]) { |
| CWVSSLErrorHandler* handler = [[CWVSSLErrorHandler alloc] |
| initWithWebState:web_state |
| URL:net::NSURLWithGURL(url) |
| error:error |
| SSLInfo:info.value() |
| errorPageHTMLCallback:base::CallbackToBlock(std::move(callback))]; |
| [navigation_delegate webView:web_view handleSSLErrorWithHandler:handler]; |
| } else { |
| std::move(callback).Run(error.localizedDescription); |
| } |
| } |
| |
| bool WebViewWebClient::EnableLongPressUIContextMenu() const { |
| return CWVWebView.chromeContextMenuEnabled; |
| } |
| |
| bool WebViewWebClient::EnableWebInspector( |
| web::BrowserState* browser_state) const { |
| return CWVWebView.webInspectorEnabled; |
| } |
| |
| bool WebViewWebClient::IsInsecureFormWarningEnabled( |
| web::BrowserState* browser_state) const { |
| // ios/web_view doesn't receive variations seeds at runtime, so this will |
| // only ever use the default value of the feature. |
| return base::FeatureList::IsEnabled( |
| security_interstitials::features::kInsecureFormSubmissionInterstitial); |
| } |
| |
| void WebViewWebClient::BuildEditMenu(web::WebState* web_state, |
| id<UIMenuBuilder>) const {} |
| |
| } // namespace ios_web_view |