Fix breakages in https://codereview.chromium.org/1155713003/
[chromium-blink-merge.git] / ios / web / web_state / web_state_impl.mm
blob745080915e6ae2df356d0750464a2e8ae7859a99
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ios/web/web_state/web_state_impl.h"
7 #include "base/strings/sys_string_conversions.h"
8 #include "ios/web/interstitials/web_interstitial_impl.h"
9 #import "ios/web/navigation/crw_session_controller.h"
10 #import "ios/web/navigation/crw_session_entry.h"
11 #include "ios/web/navigation/navigation_item_impl.h"
12 #include "ios/web/net/request_group_util.h"
13 #include "ios/web/public/browser_state.h"
14 #include "ios/web/public/navigation_item.h"
15 #include "ios/web/public/url_util.h"
16 #include "ios/web/public/web_client.h"
17 #include "ios/web/public/web_state/credential.h"
18 #include "ios/web/public/web_state/web_state_observer.h"
19 #import "ios/web/web_state/ui/crw_web_controller.h"
20 #include "ios/web/web_state/web_state_facade_delegate.h"
21 #import "ios/web/webui/web_ui_ios_controller_factory_registry.h"
22 #import "ios/web/webui/web_ui_ios_impl.h"
23 #include "net/http/http_response_headers.h"
25 namespace web {
27 WebStateImpl::WebStateImpl(BrowserState* browser_state)
28     : is_loading_(false),
29       facade_delegate_(nullptr),
30       web_controller_(nil),
31       navigation_manager_(this, browser_state),
32       interstitial_(nullptr),
33       cache_mode_(net::RequestTracker::CACHE_NORMAL) {
36 WebStateImpl::~WebStateImpl() {
37   // WebUI depends on web state so it must be destroyed first in case any WebUI
38   // implementations depends on accessing web state during destruction.
39   ClearWebUI();
41   // The facade layer (owned by the delegate) should be destroyed before the web
42   // layer.
43   DCHECK(!facade_delegate_);
45   FOR_EACH_OBSERVER(WebStateObserver, observers_, WebStateDestroyed());
46   FOR_EACH_OBSERVER(WebStateObserver, observers_, ResetWebState());
47   DCHECK(script_command_callbacks_.empty());
48   if (request_tracker_.get())
49     CloseRequestTracker();
52 void WebStateImpl::AddObserver(WebStateObserver* observer) {
53   DCHECK(!observers_.HasObserver(observer));
54   observers_.AddObserver(observer);
57 void WebStateImpl::RemoveObserver(WebStateObserver* observer) {
58   DCHECK(observers_.HasObserver(observer));
59   observers_.RemoveObserver(observer);
62 bool WebStateImpl::Configured() const {
63   return web_controller_ != nil;
66 void WebStateImpl::SetWebController(CRWWebController* web_controller) {
67   DCHECK(!web_controller_);
68   web_controller_ = web_controller;
71 WebStateFacadeDelegate* WebStateImpl::GetFacadeDelegate() const {
72   return facade_delegate_;
75 void WebStateImpl::SetFacadeDelegate(WebStateFacadeDelegate* facade_delegate) {
76   facade_delegate_ = facade_delegate;
79 WebStateImpl* WebStateImpl::CopyForSessionWindow() {
80   WebStateImpl* copy = new WebStateImpl(GetBrowserState());
81   copy->GetNavigationManagerImpl().CopyState(&navigation_manager_);
82   return copy;
85 void WebStateImpl::OnUrlHashChanged() {
86   FOR_EACH_OBSERVER(WebStateObserver, observers_, UrlHashChanged());
89 void WebStateImpl::OnHistoryStateChanged() {
90   FOR_EACH_OBSERVER(WebStateObserver, observers_, HistoryStateChanged());
93 bool WebStateImpl::OnScriptCommandReceived(const std::string& command,
94                                            const base::DictionaryValue& value,
95                                            const GURL& url,
96                                            bool user_is_interacting) {
97   size_t dot_position = command.find_first_of('.');
98   if (dot_position == 0 || dot_position == std::string::npos)
99     return false;
101   std::string prefix = command.substr(0, dot_position);
102   auto it = script_command_callbacks_.find(prefix);
103   if (it == script_command_callbacks_.end())
104     return false;
106   return it->second.Run(value, url, user_is_interacting);
109 void WebStateImpl::SetIsLoading(bool is_loading) {
110   DCHECK(Configured());
111   if (is_loading == is_loading_)
112     return;
114   is_loading_ = is_loading;
115   if (facade_delegate_)
116     facade_delegate_->OnLoadingStateChanged();
118   if (is_loading) {
119     FOR_EACH_OBSERVER(WebStateObserver, observers_, DidStartLoading());
120   } else {
121     FOR_EACH_OBSERVER(WebStateObserver, observers_, DidStopLoading());
122   }
125 bool WebStateImpl::IsLoading() const {
126   return is_loading_;
129 void WebStateImpl::OnPageLoaded(const GURL& url, bool load_success) {
130   UpdateHttpResponseHeaders(url);
131   if (facade_delegate_)
132     facade_delegate_->OnPageLoaded();
134   PageLoadCompletionStatus load_completion_status =
135       load_success ? PageLoadCompletionStatus::SUCCESS
136                    : PageLoadCompletionStatus::FAILURE;
137   FOR_EACH_OBSERVER(WebStateObserver, observers_,
138                     PageLoaded(load_completion_status));
141 void WebStateImpl::OnFormActivityRegistered(const std::string& form_name,
142                                             const std::string& field_name,
143                                             const std::string& type,
144                                             const std::string& value,
145                                             int key_code,
146                                             bool input_missing) {
147   FOR_EACH_OBSERVER(WebStateObserver, observers_,
148                     FormActivityRegistered(form_name, field_name, type, value,
149                                            key_code, input_missing));
152 void WebStateImpl::OnAutocompleteRequested(const GURL& source_url,
153                                            const std::string& form_name,
154                                            bool user_initiated) {
155   FOR_EACH_OBSERVER(
156       WebStateObserver, observers_,
157       AutocompleteRequested(source_url, form_name, user_initiated));
160 void WebStateImpl::OnFaviconUrlUpdated(
161     const std::vector<FaviconURL>& candidates) {
162   FOR_EACH_OBSERVER(WebStateObserver, observers_,
163                     FaviconUrlUpdated(candidates));
166 void WebStateImpl::OnCredentialsRequested(
167     int request_id,
168     const GURL& source_url,
169     bool suppress_ui,
170     const std::vector<std::string>& federations,
171     bool user_interaction) {
172   FOR_EACH_OBSERVER(WebStateObserver, observers_,
173                     CredentialsRequested(request_id, source_url, suppress_ui,
174                                          federations, user_interaction));
177 void WebStateImpl::OnSignedIn(int request_id,
178                               const GURL& source_url,
179                               const web::Credential& credential) {
180   FOR_EACH_OBSERVER(WebStateObserver, observers_,
181                     SignedIn(request_id, source_url, credential));
184 void WebStateImpl::OnSignedIn(int request_id, const GURL& source_url) {
185   FOR_EACH_OBSERVER(WebStateObserver, observers_,
186                     SignedIn(request_id, source_url));
189 void WebStateImpl::OnSignedOut(int request_id, const GURL& source_url) {
190   FOR_EACH_OBSERVER(WebStateObserver, observers_,
191                     SignedOut(request_id, source_url));
194 void WebStateImpl::OnSignInFailed(int request_id,
195                                   const GURL& source_url,
196                                   const web::Credential& credential) {
197   FOR_EACH_OBSERVER(WebStateObserver, observers_,
198                     SignInFailed(request_id, source_url, credential));
201 void WebStateImpl::OnSignInFailed(int request_id, const GURL& source_url) {
202   FOR_EACH_OBSERVER(WebStateObserver, observers_,
203                     SignInFailed(request_id, source_url));
206 void WebStateImpl::OnDocumentSubmitted(const std::string& form_name,
207                                        bool user_initiated) {
208   FOR_EACH_OBSERVER(WebStateObserver, observers_,
209                     DocumentSubmitted(form_name, user_initiated));
212 NavigationManagerImpl& WebStateImpl::GetNavigationManagerImpl() {
213   return navigation_manager_;
216 const NavigationManagerImpl& WebStateImpl::GetNavigationManagerImpl() const {
217   return navigation_manager_;
220 // There are currently two kinds of WebUI: those that have been adapted to
221 // web::WebUIIOS, and those that are still using content::WebUI. Try to create
222 // it as the first, and then fall back to the latter if necessary.
223 void WebStateImpl::CreateWebUI(const GURL& url) {
224   web_ui_.reset(CreateWebUIIOS(url));
225   if (!web_ui_ && facade_delegate_)
226     facade_delegate_->CreateLegacyWebUI(url);
229 void WebStateImpl::ClearWebUI() {
230   if (facade_delegate_)
231     facade_delegate_->ClearLegacyWebUI();
232   web_ui_.reset();
235 bool WebStateImpl::HasWebUI() {
236   return web_ui_ || (facade_delegate_ && facade_delegate_->HasLegacyWebUI());
239 void WebStateImpl::ProcessWebUIMessage(const GURL& source_url,
240                                        const std::string& message,
241                                        const base::ListValue& args) {
242   if (web_ui_) {
243     DCHECK(!facade_delegate_ || !facade_delegate_->HasLegacyWebUI());
244     web_ui_->ProcessWebUIIOSMessage(source_url, message, args);
245   } else if (facade_delegate_) {
246     facade_delegate_->ProcessLegacyWebUIMessage(source_url, message, args);
247   }
250 void WebStateImpl::LoadWebUIHtml(const base::string16& html, const GURL& url) {
251   CHECK(web::GetWebClient()->IsAppSpecificURL(url));
252   [web_controller_ loadHTML:base::SysUTF16ToNSString(html)
253           forAppSpecificURL:url];
256 const base::string16& WebStateImpl::GetTitle() const {
257   // TODO(stuartmorgan): Implement the NavigationManager logic necessary to
258   // match the WebContents implementation of this method.
259   DCHECK(Configured());
260   web::NavigationItem* item = navigation_manager_.GetLastCommittedItem();
261   if (item) {
262     return item->GetTitleForDisplay(
263         web::GetWebClient()->GetAcceptLangs(GetBrowserState()));
264   }
265   return empty_string16_;
268 bool WebStateImpl::IsShowingWebInterstitial() const {
269   // Technically we could have |interstitial_| set but its view isn't
270   // being displayed, but there's no code path where that could occur.
271   return interstitial_ != nullptr;
274 WebInterstitial* WebStateImpl::GetWebInterstitial() const {
275   return interstitial_;
278 net::HttpResponseHeaders* WebStateImpl::GetHttpResponseHeaders() const {
279   return http_response_headers_.get();
282 void WebStateImpl::OnHttpResponseHeadersReceived(
283     net::HttpResponseHeaders* response_headers,
284     const GURL& resource_url) {
285   // Store the headers in a map until the page finishes loading, as we do not
286   // know which URL corresponds to the main page yet.
287   // Remove the hash (if any) as it is sometimes altered by in-page navigations.
288   const GURL& url = GURLByRemovingRefFromGURL(resource_url);
289   response_headers_map_[url] = response_headers;
292 void WebStateImpl::UpdateHttpResponseHeaders(const GURL& url) {
293   // Reset the state.
294   http_response_headers_ = NULL;
295   mime_type_.clear();
296   content_language_header_.clear();
298   // Discard all the response headers except the ones for |main_page_url|.
299   auto it = response_headers_map_.find(GURLByRemovingRefFromGURL(url));
300   if (it != response_headers_map_.end())
301     http_response_headers_ = it->second;
302   response_headers_map_.clear();
304   if (!http_response_headers_.get())
305     return;
307   // MIME type.
308   std::string mime_type;
309   http_response_headers_->GetMimeType(&mime_type);
310   mime_type_ = mime_type;
312   // Content-Language
313   std::string content_language;
314   http_response_headers_->GetNormalizedHeader("content-language",
315                                               &content_language);
316   // Remove everything after the comma ',' if any.
317   size_t comma_index = content_language.find_first_of(',');
318   if (comma_index != std::string::npos)
319     content_language.resize(comma_index);
320   content_language_header_ = content_language;
323 void WebStateImpl::ClearWebInterstitialForNavigation() {
324   if (interstitial_) {
325     // DontProceed() dismisses the interstitial page in the same way as if it
326     // was closed by user action. Clearing interstitial_ early makes
327     // IsShowingWebInterstitial() return false so the code that is triggered in
328     // DontProceed knows that the interstitial page is not visible anymore.
329     WebInterstitialImpl* interstitial = interstitial_;
330     DismissWebInterstitial();
331     // In this case, DontProceed() may not remove an unsafe page from the nav
332     // history.
333     CRWSessionController* sessionController =
334         navigation_manager_.GetSessionController();
335     web::NavigationItem* currentItem =
336         [sessionController.currentEntry navigationItem];
337     if (currentItem->IsUnsafe()) {
338       // The unsafe page should be removed from history, and, in fact,
339       // SafeBrowsingBlockingPage will do just that *provided* that it
340       // isn't the current page. So to make this happen, before removing the
341       // interstitial, have the session controller go back one page.
342       [sessionController goBack];
343     }
344     [sessionController discardNonCommittedEntries];
345     interstitial->DontProceed();
346   }
349 void WebStateImpl::DisplayWebInterstitial(WebInterstitialImpl* interstitial) {
350   DCHECK(Configured());
351   DCHECK(interstitial);
352   interstitial_ = interstitial;
353   CGSize content_view_size = [web_controller_ view].bounds.size;
354   interstitial_->SetSize(
355       gfx::Size(content_view_size.width, content_view_size.height));
356   [web_controller_ displayInterstitialView:interstitial_->GetView()
357                             withScrollView:interstitial_->GetScrollView()];
360 void WebStateImpl::DismissWebInterstitial() {
361   if (interstitial_) {
362     FOR_EACH_OBSERVER(WebStateObserver, observers_, InsterstitialDismissed());
363     [interstitial_->GetView() removeFromSuperview];
364     interstitial_ = nullptr;
365   }
368 WebUIIOS* WebStateImpl::CreateWebUIIOS(const GURL& url) {
369   WebUIIOSControllerFactory* factory =
370       WebUIIOSControllerFactoryRegistry::GetInstance();
371   if (!factory)
372     return NULL;
373   WebUIIOSImpl* web_ui = new WebUIIOSImpl(this);
374   WebUIIOSController* controller =
375       factory->CreateWebUIIOSControllerForURL(web_ui, url);
376   if (controller) {
377     web_ui->SetController(controller);
378     return web_ui;
379   }
381   delete web_ui;
382   return NULL;
385 void WebStateImpl::ExecuteJavaScriptAsync(const base::string16& javascript) {
386   DCHECK(Configured());
387   [web_controller_ evaluateJavaScript:base::SysUTF16ToNSString(javascript)
388                   stringResultHandler:nil];
391 #pragma mark - RequestTracker management
393 void WebStateImpl::InitializeRequestTracker(
394     id<CRWRequestTrackerDelegate> delegate) {
395   BrowserState* browser_state = navigation_manager_.GetBrowserState();
396   request_tracker_ = RequestTrackerImpl::CreateTrackerForRequestGroupID(
397       GetRequestGroupID(), browser_state, browser_state->GetRequestContext(),
398       delegate);
401 void WebStateImpl::CloseRequestTracker() {
402   request_tracker_->Close();
403   request_tracker_ = NULL;
406 RequestTrackerImpl* WebStateImpl::GetRequestTracker() {
407   DCHECK(request_tracker_.get());
408   return request_tracker_.get();
411 net::RequestTracker::CacheMode WebStateImpl::GetCacheMode() {
412   return cache_mode_;
415 void WebStateImpl::SetCacheMode(net::RequestTracker::CacheMode mode) {
416   cache_mode_ = mode;
419 NSString* WebStateImpl::GetRequestGroupID() {
420   if (request_group_id_.get() == nil)
421     request_group_id_.reset([GenerateNewRequestGroupID() copy]);
423   return request_group_id_;
426 int WebStateImpl::DownloadImage(
427     const GURL& url,
428     bool is_favicon,
429     uint32_t max_bitmap_size,
430     bool bypass_cache,
431     const ImageDownloadCallback& callback) {
432   // |is_favicon| specifies whether the download of the image occurs with
433   // cookies or not. Currently, only downloads without cookies are supported.
434   // |bypass_cache| is ignored since the downloads never go through a cache.
435   DCHECK(is_favicon);
436   return [[web_controller_ delegate] downloadImageAtUrl:url
437                                           maxBitmapSize:max_bitmap_size
438                                               callback:callback];
441 #pragma mark - WebState implementation
443 UIView* WebStateImpl::GetView() {
444   return [web_controller_ view];
447 WebViewType WebStateImpl::GetWebViewType() const {
448   return [web_controller_ webViewType];
451 BrowserState* WebStateImpl::GetBrowserState() const {
452   return navigation_manager_.GetBrowserState();
455 void WebStateImpl::OpenURL(const WebState::OpenURLParams& params) {
456   DCHECK(Configured());
457   ClearWebInterstitialForNavigation();
458   [[web_controller_ delegate] openURLWithParams:params];
461 NavigationManager* WebStateImpl::GetNavigationManager() {
462   return &GetNavigationManagerImpl();
465 CRWJSInjectionReceiver* WebStateImpl::GetJSInjectionReceiver() const {
466   return [web_controller_ jsInjectionReceiver];
469 const std::string& WebStateImpl::GetContentLanguageHeader() const {
470   return content_language_header_;
473 const std::string& WebStateImpl::GetContentsMimeType() const {
474   return mime_type_;
477 bool WebStateImpl::ContentIsHTML() const {
478   return [web_controller_ contentIsHTML];
481 const GURL& WebStateImpl::GetVisibleURL() const {
482   web::NavigationItem* item = navigation_manager_.GetVisibleItem();
483   return item ? item->GetVirtualURL() : GURL::EmptyGURL();
486 const GURL& WebStateImpl::GetLastCommittedURL() const {
487   web::NavigationItem* item = navigation_manager_.GetLastCommittedItem();
488   return item ? item->GetVirtualURL() : GURL::EmptyGURL();
491 GURL WebStateImpl::GetCurrentURL(URLVerificationTrustLevel* trust_level) const {
492   return [web_controller_ currentURLWithTrustLevel:trust_level];
495 void WebStateImpl::AddScriptCommandCallback(
496     const ScriptCommandCallback& callback,
497     const std::string& command_prefix) {
498   DCHECK(!command_prefix.empty());
499   DCHECK(command_prefix.find_first_of('.') == std::string::npos);
500   DCHECK(script_command_callbacks_.find(command_prefix) ==
501          script_command_callbacks_.end());
502   script_command_callbacks_[command_prefix] = callback;
505 void WebStateImpl::RemoveScriptCommandCallback(
506     const std::string& command_prefix) {
507   DCHECK(script_command_callbacks_.find(command_prefix) !=
508          script_command_callbacks_.end());
509   script_command_callbacks_.erase(command_prefix);
512 id<CRWWebViewProxy> WebStateImpl::GetWebViewProxy() const {
513   return [web_controller_ webViewProxy];
516 void WebStateImpl::OnProvisionalNavigationStarted(const GURL& url) {
517   FOR_EACH_OBSERVER(WebStateObserver, observers_,
518                     ProvisionalNavigationStarted(url));
521 #pragma mark - NavigationManagerDelegate implementation
523 // Mirror WebContentsImpl::NavigateToPendingEntry() so that
524 // NavigationControllerIO::GoBack() actually goes back.
525 void WebStateImpl::NavigateToPendingEntry() {
526   [web_controller_ loadCurrentURL];
529 void WebStateImpl::OnNavigationItemCommitted(
530     const LoadCommittedDetails& load_details) {
531   FOR_EACH_OBSERVER(WebStateObserver, observers_,
532                     NavigationItemCommitted(load_details));
535 WebState* WebStateImpl::GetWebState() {
536   return this;
539 }  // namespace web