123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047 |
- // Copyright 2014 The Chromium Authors. All rights reserved.
- // 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/cwv_web_view_internal.h"
- #include <memory>
- #include <unordered_map>
- #include <utility>
- #import <WebKit/WebKit.h>
- #include "base/bind.h"
- #include "base/json/json_writer.h"
- #include "base/mac/foundation_util.h"
- #include "base/strings/sys_string_conversions.h"
- #import "components/autofill/ios/browser/autofill_agent.h"
- #include "components/autofill/ios/form_util/unique_id_data_tab_helper.h"
- #include "components/language/ios/browser/ios_language_detection_tab_helper.h"
- #include "components/password_manager/core/browser/password_manager.h"
- #import "components/password_manager/ios/ios_password_manager_driver.h"
- #import "components/password_manager/ios/shared_password_controller.h"
- #import "components/safe_browsing/ios/browser/safe_browsing_url_allow_list.h"
- #include "components/url_formatter/elide_url.h"
- #include "google_apis/google_api_keys.h"
- #import "ios/components/security_interstitials/lookalikes/lookalike_url_container.h"
- #import "ios/components/security_interstitials/lookalikes/lookalike_url_tab_allow_list.h"
- #import "ios/components/security_interstitials/lookalikes/lookalike_url_tab_helper.h"
- #import "ios/components/security_interstitials/safe_browsing/safe_browsing_client.h"
- #import "ios/components/security_interstitials/safe_browsing/safe_browsing_query_manager.h"
- #import "ios/components/security_interstitials/safe_browsing/safe_browsing_tab_helper.h"
- #import "ios/components/security_interstitials/safe_browsing/safe_browsing_unsafe_resource_container.h"
- #import "ios/web/public/deprecated/crw_js_injection_receiver.h"
- #include "ios/web/public/favicon/favicon_url.h"
- #include "ios/web/public/js_messaging/web_frame.h"
- #import "ios/web/public/js_messaging/web_frame_util.h"
- #import "ios/web/public/js_messaging/web_frames_manager.h"
- #import "ios/web/public/navigation/navigation_context.h"
- #import "ios/web/public/navigation/navigation_item.h"
- #import "ios/web/public/navigation/navigation_manager.h"
- #include "ios/web/public/navigation/referrer.h"
- #include "ios/web/public/navigation/reload_type.h"
- #import "ios/web/public/ui/context_menu_params.h"
- #import "ios/web/public/ui/crw_web_view_proxy.h"
- #import "ios/web/public/ui/crw_web_view_scroll_view_proxy.h"
- #import "ios/web/public/web_client.h"
- #import "ios/web/public/web_state.h"
- #import "ios/web/public/web_view_only/wk_web_view_configuration_util.h"
- #include "ios/web_view/internal/app/application_context.h"
- #import "ios/web_view/internal/autofill/cwv_autofill_controller_internal.h"
- #import "ios/web_view/internal/autofill/web_view_autofill_client_ios.h"
- #import "ios/web_view/internal/cwv_back_forward_list_internal.h"
- #import "ios/web_view/internal/cwv_favicon_internal.h"
- #import "ios/web_view/internal/cwv_html_element_internal.h"
- #import "ios/web_view/internal/cwv_navigation_action_internal.h"
- #import "ios/web_view/internal/cwv_script_command_internal.h"
- #import "ios/web_view/internal/cwv_ssl_status_internal.h"
- #import "ios/web_view/internal/cwv_web_view_configuration_internal.h"
- #import "ios/web_view/internal/language/web_view_url_language_histogram_factory.h"
- #import "ios/web_view/internal/passwords/web_view_password_manager_client.h"
- #import "ios/web_view/internal/safe_browsing/web_view_safe_browsing_client_factory.h"
- #import "ios/web_view/internal/translate/cwv_translation_controller_internal.h"
- #import "ios/web_view/internal/translate/web_view_translate_client.h"
- #include "ios/web_view/internal/web_view_browser_state.h"
- #include "ios/web_view/internal/web_view_global_state_util.h"
- #import "ios/web_view/internal/web_view_java_script_dialog_presenter.h"
- #import "ios/web_view/internal/web_view_message_handler_java_script_feature.h"
- #import "ios/web_view/internal/web_view_web_state_policy_decider.h"
- #import "ios/web_view/public/cwv_navigation_delegate.h"
- #import "ios/web_view/public/cwv_preview_element_info.h"
- #import "ios/web_view/public/cwv_ui_delegate.h"
- #import "ios/web_view/public/cwv_web_view_configuration.h"
- #import "net/base/mac/url_conversions.h"
- #include "ui/base/page_transition_types.h"
- #include "url/gurl.h"
- #if !defined(__has_feature) || !__has_feature(objc_arc)
- #error "This file requires ARC support."
- #endif
- namespace {
- // A key used in NSCoder to store the session storage object.
- NSString* const kSessionStorageKey = @"sessionStorage";
- // Converts base::Value expected to be a dictionary or list to NSDictionary or
- // NSArray, respectively.
- id NSObjectFromCollectionValue(const base::Value* value) {
- DCHECK(value->is_dict() || value->is_list())
- << "Incorrect value type: " << value->type();
- std::string json;
- const bool success = base::JSONWriter::Write(*value, &json);
- DCHECK(success) << "Failed to convert base::Value to JSON";
- NSData* json_data = [NSData dataWithBytes:json.c_str() length:json.length()];
- id ns_object = [NSJSONSerialization JSONObjectWithData:json_data
- options:kNilOptions
- error:nil];
- DCHECK(ns_object) << "Failed to convert JSON to Collection";
- return ns_object;
- }
- // Converts base::Value to an appropriate Obj-C object.
- // |value| must not be null.
- id NSObjectFromValue(const base::Value* value) {
- switch (value->type()) {
- case base::Value::Type::NONE:
- return nil;
- case base::Value::Type::BOOLEAN:
- return @(value->GetBool());
- case base::Value::Type::INTEGER:
- return @(value->GetInt());
- case base::Value::Type::DOUBLE:
- return @(value->GetDouble());
- case base::Value::Type::STRING:
- return base::SysUTF8ToNSString(value->GetString());
- case base::Value::Type::BINARY:
- // Unsupported.
- return nil;
- case base::Value::Type::DICT:
- case base::Value::Type::LIST:
- return NSObjectFromCollectionValue(value);
- }
- return nil;
- }
- // Converts base::Value expected to be a dictionary to NSDictionary.
- NSDictionary* NSDictionaryFromDictionaryValue(const base::Value& value) {
- DCHECK(value.is_dict()) << "Incorrect value type: " << value.type();
- NSDictionary* ns_dictionary = base::mac::ObjCCastStrict<NSDictionary>(
- NSObjectFromCollectionValue(&value));
- DCHECK(ns_dictionary) << "Failed to convert JSON to NSDictionary";
- return ns_dictionary;
- }
- // Converts base::Value::Dict to NSDictionary.
- NSDictionary* NSDictionaryFromDictValue(const base::Value::Dict& value) {
- std::string json;
- const bool success = base::JSONWriter::Write(value, &json);
- DCHECK(success) << "Failed to convert base::Value to JSON";
- NSData* json_data = [NSData dataWithBytes:json.c_str() length:json.length()];
- NSDictionary* ns_dictionary = base::mac::ObjCCastStrict<NSDictionary>(
- [NSJSONSerialization JSONObjectWithData:json_data
- options:kNilOptions
- error:nil]);
- DCHECK(ns_dictionary) << "Failed to convert JSON to NSDictionary";
- return ns_dictionary;
- }
- // A WebStateUserData to hold a reference to a corresponding CWVWebView.
- class WebViewHolder : public web::WebStateUserData<WebViewHolder> {
- public:
- explicit WebViewHolder(web::WebState* web_state) {}
- CWVWebView* web_view() const { return web_view_; }
- void set_web_view(CWVWebView* web_view) { web_view_ = web_view; }
- private:
- friend class web::WebStateUserData<WebViewHolder>;
- __weak CWVWebView* web_view_ = nil;
- WEB_STATE_USER_DATA_KEY_DECL();
- };
- WEB_STATE_USER_DATA_KEY_IMPL(WebViewHolder)
- } // namespace
- @interface CWVWebView () {
- CWVWebViewConfiguration* _configuration;
- std::unique_ptr<web::WebState> _webState;
- std::unique_ptr<web::WebStateDelegateBridge> _webStateDelegate;
- std::unique_ptr<web::WebStateObserverBridge> _webStateObserver;
- std::unique_ptr<ios_web_view::WebViewWebStatePolicyDecider>
- _webStatePolicyDecider;
- double _estimatedProgress;
- // Handles presentation of JavaScript dialogs.
- std::unique_ptr<ios_web_view::WebViewJavaScriptDialogPresenter>
- _javaScriptDialogPresenter;
- // Stores the script command callbacks with subscriptions.
- std::unordered_map<std::string,
- std::pair<web::WebState::ScriptCommandCallback,
- base::CallbackListSubscription>>
- _scriptCommandCallbacks;
- CRWSessionStorage* _cachedSessionStorage;
- // Stores the last NavigationContext::IsSameDocument() value from
- // |webState:didFinishNavigation:|.
- BOOL _lastDidFinishNavigationContextIsSameDocument;
- }
- // Redefine these properties as readwrite to define setters, which send KVO
- // notifications.
- @property(nonatomic, readwrite) double estimatedProgress;
- @property(nonatomic, readwrite) BOOL canGoBack;
- @property(nonatomic, readwrite) BOOL canGoForward;
- @property(nonatomic, readwrite) NSURL* lastCommittedURL;
- @property(nonatomic, readwrite) BOOL loading;
- @property(nonatomic, readwrite, copy) NSString* title;
- @property(nonatomic, readwrite) NSURL* visibleURL;
- @property(nonatomic, readwrite) NSString* visibleLocationString;
- @property(nonatomic, readwrite) CWVSSLStatus* visibleSSLStatus;
- // Updates the availability of the back/forward navigation properties exposed
- // through |canGoBack| and |canGoForward|, and also updates |backForwardList|.
- - (void)updateNavigationAvailability;
- // Updates the URLs exposed through |lastCommittedURL| and |visibleURL|.
- - (void)updateCurrentURLs;
- // Updates |title| property.
- - (void)updateTitle;
- // Returns a new CWVAutofillController created from |_webState|.
- - (CWVAutofillController*)newAutofillController;
- // Returns a new CWVTranslationController created from |_webState|.
- - (CWVTranslationController*)newTranslationController;
- // Updates |_webState| visiblity.
- - (void)updateWebStateVisibility;
- @end
- namespace {
- NSString* gCustomUserAgent = nil;
- NSString* gUserAgentProduct = nil;
- BOOL gChromeContextMenuEnabled = NO;
- } // namespace
- @implementation CWVWebView
- @synthesize autofillController = _autofillController;
- @synthesize backForwardList = _backForwardList;
- @synthesize canGoBack = _canGoBack;
- @synthesize canGoForward = _canGoForward;
- @synthesize configuration = _configuration;
- @synthesize estimatedProgress = _estimatedProgress;
- @synthesize lastCommittedURL = _lastCommittedURL;
- @synthesize loading = _loading;
- @synthesize navigationDelegate = _navigationDelegate;
- @synthesize title = _title;
- @synthesize translationController = _translationController;
- @synthesize UIDelegate = _UIDelegate;
- @synthesize visibleURL = _visibleURL;
- @synthesize visibleSSLStatus = _visibleSSLStatus;
- + (void)initialize {
- if (self != [CWVWebView class]) {
- return;
- }
- ios_web_view::InitializeGlobalState();
- }
- + (BOOL)chromeContextMenuEnabled {
- return gChromeContextMenuEnabled;
- }
- + (void)setChromeContextMenuEnabled:(BOOL)newValue {
- gChromeContextMenuEnabled = newValue;
- }
- + (NSString*)customUserAgent {
- return gCustomUserAgent;
- }
- + (void)setCustomUserAgent:(NSString*)customUserAgent {
- gCustomUserAgent = [customUserAgent copy];
- }
- + (NSString*)userAgentProduct {
- return gUserAgentProduct;
- }
- + (void)setUserAgentProduct:(NSString*)product {
- gUserAgentProduct = [product copy];
- }
- + (void)setGoogleAPIKey:(NSString*)googleAPIKey
- clientID:(NSString*)clientID
- clientSecret:(NSString*)clientSecret {
- google_apis::SetAPIKey(base::SysNSStringToUTF8(googleAPIKey));
- std::string clientIDString = base::SysNSStringToUTF8(clientID);
- std::string clientSecretString = base::SysNSStringToUTF8(clientSecret);
- for (size_t i = 0; i < google_apis::CLIENT_NUM_ITEMS; ++i) {
- google_apis::OAuth2Client client =
- static_cast<google_apis::OAuth2Client>(i);
- google_apis::SetOAuth2ClientID(client, clientIDString);
- google_apis::SetOAuth2ClientSecret(client, clientSecretString);
- }
- }
- + (CWVWebView*)webViewForWebState:(web::WebState*)webState {
- WebViewHolder* holder = WebViewHolder::FromWebState(webState);
- CWVWebView* webView = holder->web_view();
- DCHECK(webView);
- return webView;
- }
- - (instancetype)initWithFrame:(CGRect)frame
- configuration:(CWVWebViewConfiguration*)configuration {
- return [self initWithFrame:frame
- configuration:configuration
- WKConfiguration:nil
- createdWKWebView:nil];
- }
- - (instancetype)initWithFrame:(CGRect)frame
- configuration:(CWVWebViewConfiguration*)configuration
- WKConfiguration:(WKWebViewConfiguration*)wkConfiguration
- createdWKWebView:(WKWebView**)createdWebView {
- self = [super initWithFrame:frame];
- if (self) {
- _configuration = configuration;
- [_configuration registerWebView:self];
- [self resetWebStateWithSessionStorage:nil
- WKConfiguration:wkConfiguration
- createdWKWebView:createdWebView];
- }
- return self;
- }
- - (UIScrollView*)scrollView {
- return [_webState->GetWebViewProxy().scrollViewProxy asUIScrollView];
- }
- - (BOOL)allowsBackForwardNavigationGestures {
- return _webState->GetWebViewProxy().allowsBackForwardNavigationGestures;
- }
- - (void)setAllowsBackForwardNavigationGestures:
- (BOOL)allowsBackForwardNavigationGestures {
- _webState->GetWebViewProxy().allowsBackForwardNavigationGestures =
- allowsBackForwardNavigationGestures;
- }
- - (void)dealloc {
- if (_webState) {
- if (_webStateObserver) {
- _webState->RemoveObserver(_webStateObserver.get());
- _webStateObserver.reset();
- }
- WebViewHolder::RemoveFromWebState(_webState.get());
- }
- }
- - (void)goBack {
- if (_webState->GetNavigationManager())
- _webState->GetNavigationManager()->GoBack();
- }
- - (void)goForward {
- if (_webState->GetNavigationManager())
- _webState->GetNavigationManager()->GoForward();
- }
- - (BOOL)goToBackForwardListItem:(CWVBackForwardListItem*)item {
- if (!_backForwardList) {
- return NO; // Do nothing if |_backForwardList| is not generated yet.
- }
- if ([item isEqual:_backForwardList.currentItem]) {
- return NO;
- }
- int index = [_backForwardList internalIndexOfItem:item];
- if (index == -1) {
- return NO;
- }
- DCHECK(_webState);
- web::NavigationManager* navigationManager = _webState->GetNavigationManager();
- navigationManager->GoToIndex(index);
- return YES;
- }
- - (void)reload {
- // |check_for_repost| is false because CWVWebView does not support repost form
- // dialogs.
- _webState->GetNavigationManager()->Reload(web::ReloadType::NORMAL,
- false /* check_for_repost */);
- }
- - (void)stopLoading {
- _webState->Stop();
- }
- - (void)loadRequest:(NSURLRequest*)request {
- DCHECK_EQ(nil, request.HTTPBodyStream)
- << "request.HTTPBodyStream is not supported.";
- web::NavigationManager::WebLoadParams params(net::GURLWithNSURL(request.URL));
- params.transition_type = ui::PAGE_TRANSITION_TYPED;
- params.extra_headers = [request.allHTTPHeaderFields copy];
- params.post_data = [request.HTTPBody copy];
- _webState->GetNavigationManager()->LoadURLWithParams(params);
- [self updateCurrentURLs];
- }
- - (void)evaluateJavaScript:(NSString*)javaScriptString
- completionHandler:(void (^)(id, NSError*))completionHandler {
- [_webState->GetJSInjectionReceiver() executeJavaScript:javaScriptString
- completionHandler:completionHandler];
- }
- - (void)evaluateJavaScript:(NSString*)javaScriptString
- completion:(void (^)(id, BOOL))completion {
- web::WebFrame* mainFrame = web::GetMainFrame(_webState.get());
- if (!mainFrame) {
- if (completion) {
- completion(nil, NO);
- }
- return;
- }
- if (!completion) {
- mainFrame->ExecuteJavaScript(base::SysNSStringToUTF16(javaScriptString));
- return;
- }
- mainFrame->ExecuteJavaScript(
- base::SysNSStringToUTF16(javaScriptString),
- base::BindOnce(^(const base::Value* result, bool error) {
- id jsResult;
- if (!error && result) {
- jsResult = NSObjectFromValue(result);
- }
- completion(jsResult, !error);
- }));
- }
- - (void)setUIDelegate:(id<CWVUIDelegate>)UIDelegate {
- _UIDelegate = UIDelegate;
- _javaScriptDialogPresenter->SetUIDelegate(_UIDelegate);
- }
- - (void)setNavigationDelegate:(id<CWVNavigationDelegate>)navigationDelegate {
- _navigationDelegate = navigationDelegate;
- [self attachSecurityInterstitialHelpersToWebStateIfNecessary];
- }
- #pragma mark - UIResponder
- - (BOOL)becomeFirstResponder {
- if (_webState) {
- return [_webState->GetWebViewProxy() becomeFirstResponder];
- } else {
- return [super becomeFirstResponder];
- }
- }
- #pragma mark - UIView
- - (void)didMoveToSuperview {
- [super didMoveToSuperview];
- [self updateWebStateVisibility];
- }
- #pragma mark - CRWWebStateObserver
- - (void)webStateDestroyed:(web::WebState*)webState {
- webState->RemoveObserver(_webStateObserver.get());
- _webStateObserver.reset();
- }
- - (void)webState:(web::WebState*)webState
- didStartNavigation:(web::NavigationContext*)navigation {
- [self updateNavigationAvailability];
- if (!navigation->IsSameDocument()) {
- SEL oldSelector = @selector(webViewDidStartProvisionalNavigation:);
- if ([_navigationDelegate respondsToSelector:oldSelector]) {
- [_navigationDelegate webViewDidStartProvisionalNavigation:self];
- }
- SEL newSelector = @selector(webViewDidStartNavigation:);
- if ([_navigationDelegate respondsToSelector:newSelector]) {
- [_navigationDelegate webViewDidStartNavigation:self];
- }
- }
- }
- - (void)webState:(web::WebState*)webState
- didFinishNavigation:(web::NavigationContext*)navigation {
- [self updateNavigationAvailability];
- [self updateCurrentURLs];
- // TODO(crbug.com/898357): Remove this once crbug.com/898357 is fixed.
- [self updateVisibleSSLStatus];
- // Store NavigationContext::IsSameDocument() for later use in
- // |webState:didLoadPageWithSuccess:|.
- _lastDidFinishNavigationContextIsSameDocument = navigation->IsSameDocument();
- if (navigation->HasCommitted() && !navigation->IsSameDocument() &&
- [_navigationDelegate
- respondsToSelector:@selector(webViewDidCommitNavigation:)]) {
- [_navigationDelegate webViewDidCommitNavigation:self];
- }
- NSError* error = navigation->GetError();
- SEL selector = @selector(webView:didFailNavigationWithError:);
- if (error && [_navigationDelegate respondsToSelector:selector]) {
- [_navigationDelegate webView:self didFailNavigationWithError:error];
- }
- }
- - (void)webState:(web::WebState*)webState didLoadPageWithSuccess:(BOOL)success {
- DCHECK_EQ(_webState.get(), webState);
- if (!success) {
- // Failure callbacks will be handled inside |webState:didFinishNavigation:|.
- return;
- }
- // We do not want to call -[CWVNavigationDelegate webViewDidFinishNavigation:]
- // for same document navigations because we also exclude them for others like
- // -[CWVNavigationDelegate webViewDidStartNavigation:] and
- // -[CWVNavigationDelegate webViewDidCommitNavigation:].
- // It is guaranteed that |webState:didLoadPageWithSuccess:| is only called
- // after |webState:didFinishNavigation:|, so we will always have an up to date
- // value to use here.
- // TODO(crbug.com/1196799): Remove this work around once a NavigationContext
- // is passed in to this method.
- SEL selector = @selector(webViewDidFinishNavigation:);
- if (!_lastDidFinishNavigationContextIsSameDocument &&
- [_navigationDelegate respondsToSelector:selector]) {
- [_navigationDelegate webViewDidFinishNavigation:self];
- }
- }
- - (void)webState:(web::WebState*)webState
- didChangeLoadingProgress:(double)progress {
- self.estimatedProgress = progress;
- }
- - (void)webStateDidChangeBackForwardState:(web::WebState*)webState {
- [self updateNavigationAvailability];
- }
- - (void)webStateDidStopLoading:(web::WebState*)webState {
- self.loading = _webState->IsLoading();
- }
- - (void)webStateDidStartLoading:(web::WebState*)webState {
- self.loading = _webState->IsLoading();
- }
- - (void)webStateDidChangeTitle:(web::WebState*)webState {
- [self updateTitle];
- }
- - (void)webStateDidChangeVisibleSecurityState:(web::WebState*)webState {
- [self updateVisibleSSLStatus];
- }
- - (void)renderProcessGoneForWebState:(web::WebState*)webState {
- SEL selector = @selector(webViewWebContentProcessDidTerminate:);
- if ([_navigationDelegate respondsToSelector:selector]) {
- [_navigationDelegate webViewWebContentProcessDidTerminate:self];
- }
- }
- #pragma mark - CRWWebStateDelegate
- - (web::WebState*)webState:(web::WebState*)webState
- createNewWebStateForURL:(const GURL&)URL
- openerURL:(const GURL&)openerURL
- initiatedByUser:(BOOL)initiatedByUser {
- SEL selector =
- @selector(webView:createWebViewWithConfiguration:forNavigationAction:);
- if (![_UIDelegate respondsToSelector:selector]) {
- return nullptr;
- }
- NSURLRequest* request =
- [[NSURLRequest alloc] initWithURL:net::NSURLWithGURL(URL)];
- CWVNavigationAction* navigationAction =
- [[CWVNavigationAction alloc] initWithRequest:request
- userInitiated:initiatedByUser];
- CWVWebView* webView = [_UIDelegate webView:self
- createWebViewWithConfiguration:_configuration
- forNavigationAction:navigationAction];
- if (!webView) {
- return nullptr;
- }
- web::WebState* webViewWebState = webView->_webState.get();
- webViewWebState->SetHasOpener(true);
- return webViewWebState;
- }
- - (void)closeWebState:(web::WebState*)webState {
- SEL selector = @selector(webViewDidClose:);
- if ([_UIDelegate respondsToSelector:selector]) {
- [_UIDelegate webViewDidClose:self];
- }
- }
- - (web::WebState*)webState:(web::WebState*)webState
- openURLWithParams:(const web::WebState::OpenURLParams&)params {
- web::NavigationManager::WebLoadParams load_params(params.url);
- load_params.referrer = params.referrer;
- load_params.transition_type = params.transition;
- load_params.is_renderer_initiated = params.is_renderer_initiated;
- load_params.virtual_url = params.virtual_url;
- _webState->GetNavigationManager()->LoadURLWithParams(load_params);
- [self updateCurrentURLs];
- return _webState.get();
- }
- - (web::JavaScriptDialogPresenter*)javaScriptDialogPresenterForWebState:
- (web::WebState*)webState {
- return _javaScriptDialogPresenter.get();
- }
- - (void)webState:(web::WebState*)webState
- contextMenuConfigurationForParams:(const web::ContextMenuParams&)params
- completionHandler:(void (^)(UIContextMenuConfiguration*))
- completionHandler {
- SEL selector = @selector(webView:
- contextMenuConfigurationForElement:completionHandler:);
- if ([_UIDelegate respondsToSelector:selector]) {
- NSURL* hyperlink = net::NSURLWithGURL(params.link_url);
- NSURL* mediaSource = net::NSURLWithGURL(params.src_url);
- CWVHTMLElement* HTMLElement =
- [[CWVHTMLElement alloc] initWithHyperlink:hyperlink
- mediaSource:mediaSource
- text:params.text];
- [_UIDelegate webView:self
- contextMenuConfigurationForElement:HTMLElement
- completionHandler:completionHandler];
- } else {
- completionHandler(nil);
- }
- }
- - (void)webState:(web::WebState*)webState
- contextMenuWillCommitWithAnimator:
- (id<UIContextMenuInteractionCommitAnimating>)animator {
- SEL selector = @selector(webView:contextMenuWillCommitWithAnimator:);
- if ([_UIDelegate respondsToSelector:selector]) {
- [_UIDelegate webView:self contextMenuWillCommitWithAnimator:animator];
- }
- }
- - (void)webState:(web::WebState*)webState
- didUpdateFaviconURLCandidates:
- (const std::vector<web::FaviconURL>&)candidates {
- if ([_UIDelegate respondsToSelector:@selector(webView:didLoadFavicons:)]) {
- [_UIDelegate webView:self
- didLoadFavicons:[CWVFavicon faviconsFromFaviconURLs:candidates]];
- }
- }
- - (id<CRWResponderInputView>)webStateInputViewProvider:
- (web::WebState*)webState {
- if (self.inputAccessoryView != nil) {
- return self;
- } else {
- return nil;
- }
- }
- - (void)addScriptCommandHandler:(id<CWVScriptCommandHandler>)handler
- commandPrefix:(NSString*)commandPrefix {
- CWVWebView* __weak weakSelf = self;
- const web::WebState::ScriptCommandCallback callback = base::BindRepeating(
- ^(const base::Value& content, const GURL& mainDocumentURL,
- bool userInteracting, web::WebFrame* senderFrame) {
- NSDictionary* nsContent = NSDictionaryFromDictionaryValue(content);
- CWVScriptCommand* command = [[CWVScriptCommand alloc]
- initWithContent:nsContent
- mainDocumentURL:net::NSURLWithGURL(mainDocumentURL)
- userInteracting:userInteracting];
- [handler webView:weakSelf
- handleScriptCommand:command
- fromMainFrame:senderFrame->IsMainFrame()];
- });
- std::string stdCommandPrefix = base::SysNSStringToUTF8(commandPrefix);
- auto subscription =
- _webState->AddScriptCommandCallback(callback, stdCommandPrefix);
- _scriptCommandCallbacks[stdCommandPrefix] = {callback,
- std::move(subscription)};
- }
- - (void)removeScriptCommandHandlerForCommandPrefix:(NSString*)commandPrefix {
- std::string stdCommandPrefix = base::SysNSStringToUTF8(commandPrefix);
- _scriptCommandCallbacks.erase(stdCommandPrefix);
- }
- - (void)addMessageHandler:(void (^)(NSDictionary* payload))handler
- forCommand:(NSString*)nsCommand {
- DCHECK(handler);
- std::string command = base::SysNSStringToUTF8(nsCommand);
- WebViewMessageHandlerJavaScriptFeature::GetInstance()->RegisterHandler(
- command, base::BindRepeating(^(const base::Value::Dict& payload) {
- handler(NSDictionaryFromDictValue(payload));
- }));
- }
- - (void)removeMessageHandlerForCommand:(NSString*)nsCommand {
- std::string command = base::SysNSStringToUTF8(nsCommand);
- WebViewMessageHandlerJavaScriptFeature::GetInstance()->UnregisterHandler(
- command);
- }
- #pragma mark - Translation
- - (CWVTranslationController*)translationController {
- if (!_translationController) {
- _translationController = [self newTranslationController];
- }
- return _translationController;
- }
- - (CWVTranslationController*)newTranslationController {
- ios_web_view::WebViewBrowserState* browserState =
- ios_web_view::WebViewBrowserState::FromBrowserState(
- _webState->GetBrowserState());
- language::IOSLanguageDetectionTabHelper::CreateForWebState(
- _webState.get(),
- ios_web_view::WebViewUrlLanguageHistogramFactory::GetForBrowserState(
- browserState));
- auto translateClient = ios_web_view::WebViewTranslateClient::Create(
- browserState, _webState.get());
- return [[CWVTranslationController alloc]
- initWithWebState:_webState.get()
- translateClient:std::move(translateClient)];
- }
- #pragma mark - Autofill
- - (CWVAutofillController*)autofillController {
- if (!_autofillController) {
- _autofillController = [self newAutofillController];
- }
- return _autofillController;
- }
- - (CWVAutofillController*)newAutofillController {
- UniqueIDDataTabHelper::CreateForWebState(_webState.get());
- auto autofillClient = autofill::WebViewAutofillClientIOS::Create(
- _webState.get(), _configuration.browserState);
- AutofillAgent* autofillAgent = [[AutofillAgent alloc]
- initWithPrefService:_configuration.browserState->GetPrefs()
- webState:_webState.get()];
- auto passwordManagerClient =
- ios_web_view::WebViewPasswordManagerClient::Create(
- _webState.get(), _configuration.browserState);
- auto passwordManager = std::make_unique<password_manager::PasswordManager>(
- passwordManagerClient.get());
- PasswordFormHelper* formHelper =
- [[PasswordFormHelper alloc] initWithWebState:_webState.get()];
- PasswordSuggestionHelper* suggestionHelper =
- [[PasswordSuggestionHelper alloc] init];
- SharedPasswordController* passwordController =
- [[SharedPasswordController alloc] initWithWebState:_webState.get()
- manager:passwordManager.get()
- formHelper:formHelper
- suggestionHelper:suggestionHelper];
- auto passwordManagerDriver = std::make_unique<IOSPasswordManagerDriver>(
- passwordController, passwordManager.get());
- return [[CWVAutofillController alloc]
- initWithWebState:_webState.get()
- autofillClient:std::move(autofillClient)
- autofillAgent:autofillAgent
- passwordManager:std::move(passwordManager)
- passwordManagerClient:std::move(passwordManagerClient)
- passwordManagerDriver:std::move(passwordManagerDriver)
- passwordController:passwordController
- applicationLocale:ios_web_view::ApplicationContext::GetInstance()
- ->GetApplicationLocale()];
- }
- #pragma mark - Preserving and Restoring State
- - (void)encodeRestorableStateWithCoder:(NSCoder*)coder {
- [super encodeRestorableStateWithCoder:coder];
- // It is possible for this instance to be encoded when the |_webState| is a
- // nullptr, i.e. when this method is called after |shutDown| has occurred.
- CRWSessionStorage* sessionStorage;
- if (_webState) {
- sessionStorage = _webState->BuildSessionStorage();
- } else if (_cachedSessionStorage) {
- sessionStorage = _cachedSessionStorage;
- } else {
- return;
- }
- [coder encodeObject:sessionStorage forKey:kSessionStorageKey];
- }
- - (void)decodeRestorableStateWithCoder:(NSCoder*)coder {
- [super decodeRestorableStateWithCoder:coder];
- CRWSessionStorage* sessionStorage =
- [coder decodeObjectForKey:kSessionStorageKey];
- [self resetWebStateWithSessionStorage:sessionStorage
- WKConfiguration:nil
- createdWKWebView:nil];
- }
- #pragma mark - Private methods
- - (void)updateWebStateVisibility {
- if (_webState == nullptr) {
- return;
- }
- if (self.superview) {
- _webState->WasShown();
- } else {
- _webState->WasHidden();
- }
- }
- // Creates a WebState instance and assigns it to |_webState|.
- // It replaces the old |_webState| if any.
- // The WebState is restored from |sessionStorage| if provided.
- //
- // If |wkConfiguration| is provided, the underlying WKWebView is
- // initialized with |wkConfiguration|, and assigned to
- // |*createdWKWebView| if |createdWKWebView| is not nil.
- // |*createdWKWebView| will be provided only if |wkConfiguration| is provided,
- // otherwise it will always be reset to nil.
- - (void)resetWebStateWithSessionStorage:(CRWSessionStorage*)sessionStorage
- WKConfiguration:(WKWebViewConfiguration*)wkConfiguration
- createdWKWebView:(WKWebView**)createdWebView {
- if (_webState) {
- if (_webStateObserver) {
- _webState->RemoveObserver(_webStateObserver.get());
- }
- WebViewHolder::RemoveFromWebState(_webState.get());
- if (_webState->GetView().superview == self) {
- // The web view provided by the old |_webState| has been added as a
- // subview. It must be removed and replaced with a new |_webState|'s web
- // view, which is added later.
- [_webState->GetView() removeFromSuperview];
- }
- }
- BOOL allowsBackForwardNavigationGestures =
- _webState &&
- _webState->GetWebViewProxy().allowsBackForwardNavigationGestures;
- web::WebState::CreateParams webStateCreateParams(_configuration.browserState);
- if (sessionStorage) {
- _webState = web::WebState::CreateWithStorageSession(webStateCreateParams,
- sessionStorage);
- } else {
- _webState = web::WebState::Create(webStateCreateParams);
- }
- // WARNING: NOTHING should be here between |web::WebState::Create()| and
- // |web::EnsureWebViewCreatedWithConfiguration()|, as this is the requirement
- // of |web::EnsureWebViewCreatedWithConfiguration()|
- WKWebView* webView = nil;
- if (wkConfiguration) {
- // When |wkConfiguration| is nil, |self| could be a newly opened web view
- // e.g., triggered by JavaScript "window.open()" function. In that case, if
- // |self| is not created by the WKWebViewConfiguration provided by WebKit's
- // delegate method
- // (https://cs.chromium.org/chromium/src/ios/web/web_state/ui/crw_wk_ui_handler.mm?q=crw_wk_ui_handler&sq=package:chromium&dr=C&l=61)
- // then calling |web::EnsureWebViewCreatedWithConfiguration()| here would
- // result in a crash (https://crbug.com/1054276). Now, we lazily create the
- // WKWebView inside |_webState| when |wkConfiguration| is not nil, and the
- // correct WKWebViewConfiguration will be passed inside //ios/web.
- webView = web::EnsureWebViewCreatedWithConfiguration(_webState.get(),
- wkConfiguration);
- }
- if (createdWebView) {
- // If the created webView is needed, returns it by the out variable way.
- *createdWebView = webView;
- }
- [self attachSecurityInterstitialHelpersToWebStateIfNecessary];
- WebViewHolder::CreateForWebState(_webState.get());
- WebViewHolder::FromWebState(_webState.get())->set_web_view(self);
- if (!_webStateObserver) {
- _webStateObserver = std::make_unique<web::WebStateObserverBridge>(self);
- }
- _webState->AddObserver(_webStateObserver.get());
- if (_backForwardList) {
- _backForwardList.navigationManager = _webState->GetNavigationManager();
- }
- [self updateWebStateVisibility];
- _webStateDelegate = std::make_unique<web::WebStateDelegateBridge>(self);
- _webState->SetDelegate(_webStateDelegate.get());
- _webStatePolicyDecider =
- std::make_unique<ios_web_view::WebViewWebStatePolicyDecider>(
- _webState.get(), self);
- _javaScriptDialogPresenter =
- std::make_unique<ios_web_view::WebViewJavaScriptDialogPresenter>(self,
- nullptr);
- for (auto& pair : _scriptCommandCallbacks) {
- pair.second.second =
- _webState->AddScriptCommandCallback(pair.second.first, pair.first);
- }
- _webState->GetWebViewProxy().allowsBackForwardNavigationGestures =
- allowsBackForwardNavigationGestures;
- if (_translationController) {
- id<CWVTranslationControllerDelegate> delegate =
- _translationController.delegate;
- _translationController = [self newTranslationController];
- _translationController.delegate = delegate;
- }
- // Recreate and restore the delegate only if previously lazily loaded.
- if (_autofillController) {
- id<CWVAutofillControllerDelegate> delegate = _autofillController.delegate;
- _autofillController = [self newAutofillController];
- _autofillController.delegate = delegate;
- }
- [self addInternalWebViewAsSubview];
- [self updateNavigationAvailability];
- [self updateCurrentURLs];
- [self updateTitle];
- [self updateVisibleSSLStatus];
- self.loading = NO;
- self.estimatedProgress = 0.0;
- // TODO(crbug.com/873729): The session will not be restored until
- // LoadIfNecessary call. Fix the bug and remove extra call.
- if (sessionStorage) {
- _webState->GetNavigationManager()->LoadIfNecessary();
- }
- }
- // Adds the web view provided by |_webState| as a subview unless it has already.
- - (void)addInternalWebViewAsSubview {
- UIView* subview = _webState->GetView();
- if (subview.superview == self) {
- return;
- }
- subview.frame = self.bounds;
- subview.autoresizingMask =
- UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- [self addSubview:subview];
- }
- - (CWVBackForwardList*)backForwardList {
- if (!_backForwardList) {
- _backForwardList = [[CWVBackForwardList alloc]
- initWithNavigationManager:_webState->GetNavigationManager()];
- }
- return _backForwardList;
- }
- - (void)updateNavigationAvailability {
- self.canGoBack = _webState && _webState->GetNavigationManager()->CanGoBack();
- self.canGoForward =
- _webState && _webState->GetNavigationManager()->CanGoForward();
- self.backForwardList.navigationManager = _webState->GetNavigationManager();
- }
- - (void)updateCurrentURLs {
- self.lastCommittedURL = net::NSURLWithGURL(_webState->GetLastCommittedURL());
- self.visibleURL = net::NSURLWithGURL(_webState->GetVisibleURL());
- self.visibleLocationString = base::SysUTF16ToNSString(
- url_formatter::FormatUrlForSecurityDisplay(_webState->GetVisibleURL()));
- }
- - (void)updateTitle {
- self.title = base::SysUTF16ToNSString(_webState->GetTitle());
- }
- - (void)updateVisibleSSLStatus {
- web::NavigationItem* visibleItem =
- _webState->GetNavigationManager()->GetVisibleItem();
- if (visibleItem) {
- self.visibleSSLStatus =
- [[CWVSSLStatus alloc] initWithInternalStatus:visibleItem->GetSSL()];
- } else {
- self.visibleSSLStatus = nil;
- }
- }
- - (void)attachSecurityInterstitialHelpersToWebStateIfNecessary {
- if (!_webState) {
- return;
- }
- // Lookalike URLs should only be intercepted if handled by the delegate.
- if ([_navigationDelegate respondsToSelector:@selector
- (webView:handleLookalikeURLWithHandler:)]) {
- LookalikeUrlTabHelper::CreateForWebState(_webState.get());
- LookalikeUrlTabAllowList::CreateForWebState(_webState.get());
- LookalikeUrlContainer::CreateForWebState(_webState.get());
- } else {
- LookalikeUrlTabHelper::RemoveFromWebState(_webState.get());
- LookalikeUrlTabAllowList::RemoveFromWebState(_webState.get());
- LookalikeUrlContainer::RemoveFromWebState(_webState.get());
- }
- // Unsafe URLs should only be intercepted if handled by the delegate.
- if ([_navigationDelegate
- respondsToSelector:@selector(webView:handleUnsafeURLWithHandler:)]) {
- SafeBrowsingClient* client =
- ios_web_view::WebViewSafeBrowsingClientFactory::GetForBrowserState(
- _webState->GetBrowserState());
- SafeBrowsingQueryManager::CreateForWebState(_webState.get(), client);
- SafeBrowsingTabHelper::CreateForWebState(_webState.get(), client);
- SafeBrowsingUrlAllowList::CreateForWebState(_webState.get());
- SafeBrowsingUnsafeResourceContainer::CreateForWebState(_webState.get());
- } else {
- SafeBrowsingQueryManager::RemoveFromWebState(_webState.get());
- SafeBrowsingTabHelper::RemoveFromWebState(_webState.get());
- SafeBrowsingUrlAllowList::RemoveFromWebState(_webState.get());
- SafeBrowsingUnsafeResourceContainer::RemoveFromWebState(_webState.get());
- }
- }
- #pragma mark - Internal Methods
- - (void)shutDown {
- if (_webState) {
- // To handle the case where -[CWVWebView encodeRestorableStateWithCoder:] is
- // called after this method, precompute the session storage so it may be
- // used during encoding later.
- _cachedSessionStorage = _webState->BuildSessionStorage();
- if (_webStateObserver) {
- _webState->RemoveObserver(_webStateObserver.get());
- _webStateObserver.reset();
- }
- WebViewHolder::RemoveFromWebState(_webState.get());
- _webState.reset();
- }
- }
- @end
|