Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ios / web / web_state / web_state_impl.mm
blobfd6aa9a89c5a8ca1147d3b865fea8c06348eda9a
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/ui/crw_content_view.h"
19 #include "ios/web/public/web_state/web_state_observer.h"
20 #import "ios/web/web_state/ui/crw_web_controller.h"
21 #import "ios/web/web_state/ui/crw_web_controller_container_view.h"
22 #include "ios/web/web_state/web_state_facade_delegate.h"
23 #import "ios/web/webui/web_ui_ios_controller_factory_registry.h"
24 #import "ios/web/webui/web_ui_ios_impl.h"
25 #include "net/http/http_response_headers.h"
27 namespace web {
29 WebStateImpl::WebStateImpl(BrowserState* browser_state)
30     : is_loading_(false),
31       facade_delegate_(nullptr),
32       web_controller_(nil),
33       navigation_manager_(this, browser_state),
34       interstitial_(nullptr),
35       cache_mode_(net::RequestTracker::CACHE_NORMAL) {
38 WebStateImpl::~WebStateImpl() {
39   // WebUI depends on web state so it must be destroyed first in case any WebUI
40   // implementations depends on accessing web state during destruction.
41   ClearWebUI();
43   // The facade layer (owned by the delegate) should be destroyed before the web
44   // layer.
45   DCHECK(!facade_delegate_);
47   FOR_EACH_OBSERVER(WebStateObserver, observers_, WebStateDestroyed());
48   FOR_EACH_OBSERVER(WebStateObserver, observers_, ResetWebState());
49   DCHECK(script_command_callbacks_.empty());
50   if (request_tracker_.get())
51     CloseRequestTracker();
54 void WebStateImpl::AddObserver(WebStateObserver* observer) {
55   DCHECK(!observers_.HasObserver(observer));
56   observers_.AddObserver(observer);
59 void WebStateImpl::RemoveObserver(WebStateObserver* observer) {
60   DCHECK(observers_.HasObserver(observer));
61   observers_.RemoveObserver(observer);
64 bool WebStateImpl::Configured() const {
65   return web_controller_ != nil;
68 void WebStateImpl::SetWebController(CRWWebController* web_controller) {
69   DCHECK(!web_controller_);
70   web_controller_ = web_controller;
73 WebStateFacadeDelegate* WebStateImpl::GetFacadeDelegate() const {
74   return facade_delegate_;
77 void WebStateImpl::SetFacadeDelegate(WebStateFacadeDelegate* facade_delegate) {
78   facade_delegate_ = facade_delegate;
81 WebStateImpl* WebStateImpl::CopyForSessionWindow() {
82   WebStateImpl* copy = new WebStateImpl(GetBrowserState());
83   copy->GetNavigationManagerImpl().CopyState(&navigation_manager_);
84   return copy;
87 void WebStateImpl::OnUrlHashChanged() {
88   FOR_EACH_OBSERVER(WebStateObserver, observers_, UrlHashChanged());
91 void WebStateImpl::OnHistoryStateChanged() {
92   FOR_EACH_OBSERVER(WebStateObserver, observers_, HistoryStateChanged());
95 bool WebStateImpl::OnScriptCommandReceived(const std::string& command,
96                                            const base::DictionaryValue& value,
97                                            const GURL& url,
98                                            bool user_is_interacting) {
99   size_t dot_position = command.find_first_of('.');
100   if (dot_position == 0 || dot_position == std::string::npos)
101     return false;
103   std::string prefix = command.substr(0, dot_position);
104   auto it = script_command_callbacks_.find(prefix);
105   if (it == script_command_callbacks_.end())
106     return false;
108   return it->second.Run(value, url, user_is_interacting);
111 void WebStateImpl::SetIsLoading(bool is_loading) {
112   DCHECK(Configured());
113   if (is_loading == is_loading_)
114     return;
116   is_loading_ = is_loading;
117   if (facade_delegate_)
118     facade_delegate_->OnLoadingStateChanged();
120   if (is_loading) {
121     FOR_EACH_OBSERVER(WebStateObserver, observers_, DidStartLoading());
122   } else {
123     FOR_EACH_OBSERVER(WebStateObserver, observers_, DidStopLoading());
124   }
127 bool WebStateImpl::IsLoading() const {
128   return is_loading_;
131 void WebStateImpl::OnPageLoaded(const GURL& url, bool load_success) {
132   UpdateHttpResponseHeaders(url);
133   if (facade_delegate_)
134     facade_delegate_->OnPageLoaded();
136   PageLoadCompletionStatus load_completion_status =
137       load_success ? PageLoadCompletionStatus::SUCCESS
138                    : PageLoadCompletionStatus::FAILURE;
139   FOR_EACH_OBSERVER(WebStateObserver, observers_,
140                     PageLoaded(load_completion_status));
143 void WebStateImpl::OnFormActivityRegistered(const std::string& form_name,
144                                             const std::string& field_name,
145                                             const std::string& type,
146                                             const std::string& value,
147                                             int key_code,
148                                             bool input_missing) {
149   FOR_EACH_OBSERVER(WebStateObserver, observers_,
150                     FormActivityRegistered(form_name, field_name, type, value,
151                                            key_code, input_missing));
154 void WebStateImpl::OnAutocompleteRequested(const GURL& source_url,
155                                            const std::string& form_name,
156                                            bool user_initiated) {
157   FOR_EACH_OBSERVER(
158       WebStateObserver, observers_,
159       AutocompleteRequested(source_url, form_name, user_initiated));
162 void WebStateImpl::OnFaviconUrlUpdated(
163     const std::vector<FaviconURL>& candidates) {
164   FOR_EACH_OBSERVER(WebStateObserver, observers_,
165                     FaviconUrlUpdated(candidates));
168 void WebStateImpl::OnCredentialsRequested(
169     int request_id,
170     const GURL& source_url,
171     bool suppress_ui,
172     const std::vector<std::string>& federations,
173     bool user_interaction) {
174   FOR_EACH_OBSERVER(WebStateObserver, observers_,
175                     CredentialsRequested(request_id, source_url, suppress_ui,
176                                          federations, user_interaction));
179 void WebStateImpl::OnSignedIn(int request_id,
180                               const GURL& source_url,
181                               const web::Credential& credential) {
182   FOR_EACH_OBSERVER(WebStateObserver, observers_,
183                     SignedIn(request_id, source_url, credential));
186 void WebStateImpl::OnSignedIn(int request_id, const GURL& source_url) {
187   FOR_EACH_OBSERVER(WebStateObserver, observers_,
188                     SignedIn(request_id, source_url));
191 void WebStateImpl::OnSignedOut(int request_id, const GURL& source_url) {
192   FOR_EACH_OBSERVER(WebStateObserver, observers_,
193                     SignedOut(request_id, source_url));
196 void WebStateImpl::OnSignInFailed(int request_id,
197                                   const GURL& source_url,
198                                   const web::Credential& credential) {
199   FOR_EACH_OBSERVER(WebStateObserver, observers_,
200                     SignInFailed(request_id, source_url, credential));
203 void WebStateImpl::OnSignInFailed(int request_id, const GURL& source_url) {
204   FOR_EACH_OBSERVER(WebStateObserver, observers_,
205                     SignInFailed(request_id, source_url));
208 void WebStateImpl::OnDocumentSubmitted(const std::string& form_name,
209                                        bool user_initiated) {
210   FOR_EACH_OBSERVER(WebStateObserver, observers_,
211                     DocumentSubmitted(form_name, user_initiated));
214 NavigationManagerImpl& WebStateImpl::GetNavigationManagerImpl() {
215   return navigation_manager_;
218 const NavigationManagerImpl& WebStateImpl::GetNavigationManagerImpl() const {
219   return navigation_manager_;
222 // There are currently two kinds of WebUI: those that have been adapted to
223 // web::WebUIIOS, and those that are still using content::WebUI. Try to create
224 // it as the first, and then fall back to the latter if necessary.
225 void WebStateImpl::CreateWebUI(const GURL& url) {
226   web_ui_.reset(CreateWebUIIOS(url));
227   if (!web_ui_ && facade_delegate_)
228     facade_delegate_->CreateLegacyWebUI(url);
231 void WebStateImpl::ClearWebUI() {
232   if (facade_delegate_)
233     facade_delegate_->ClearLegacyWebUI();
234   web_ui_.reset();
237 bool WebStateImpl::HasWebUI() {
238   return web_ui_ || (facade_delegate_ && facade_delegate_->HasLegacyWebUI());
241 void WebStateImpl::ProcessWebUIMessage(const GURL& source_url,
242                                        const std::string& message,
243                                        const base::ListValue& args) {
244   if (web_ui_) {
245     DCHECK(!facade_delegate_ || !facade_delegate_->HasLegacyWebUI());
246     web_ui_->ProcessWebUIIOSMessage(source_url, message, args);
247   } else if (facade_delegate_) {
248     facade_delegate_->ProcessLegacyWebUIMessage(source_url, message, args);
249   }
252 void WebStateImpl::LoadWebUIHtml(const base::string16& html, const GURL& url) {
253   CHECK(web::GetWebClient()->IsAppSpecificURL(url));
254   [web_controller_ loadHTML:base::SysUTF16ToNSString(html)
255           forAppSpecificURL:url];
258 const base::string16& WebStateImpl::GetTitle() const {
259   // TODO(stuartmorgan): Implement the NavigationManager logic necessary to
260   // match the WebContents implementation of this method.
261   DCHECK(Configured());
262   web::NavigationItem* item = navigation_manager_.GetLastCommittedItem();
263   if (item) {
264     return item->GetTitleForDisplay(
265         web::GetWebClient()->GetAcceptLangs(GetBrowserState()));
266   }
267   return empty_string16_;
270 void WebStateImpl::ShowTransientContentView(CRWContentView* content_view) {
271   DCHECK(Configured());
272   DCHECK(content_view);
273   DCHECK(content_view.scrollView);
274   DCHECK([content_view.scrollView isDescendantOfView:content_view]);
275   [web_controller_ showTransientContentView:content_view];
278 bool WebStateImpl::IsShowingWebInterstitial() const {
279   // Technically we could have |interstitial_| set but its view isn't
280   // being displayed, but there's no code path where that could occur.
281   return interstitial_ != nullptr;
284 WebInterstitial* WebStateImpl::GetWebInterstitial() const {
285   return interstitial_;
288 net::HttpResponseHeaders* WebStateImpl::GetHttpResponseHeaders() const {
289   return http_response_headers_.get();
292 void WebStateImpl::OnHttpResponseHeadersReceived(
293     net::HttpResponseHeaders* response_headers,
294     const GURL& resource_url) {
295   // Store the headers in a map until the page finishes loading, as we do not
296   // know which URL corresponds to the main page yet.
297   // Remove the hash (if any) as it is sometimes altered by in-page navigations.
298   const GURL& url = GURLByRemovingRefFromGURL(resource_url);
299   response_headers_map_[url] = response_headers;
302 void WebStateImpl::UpdateHttpResponseHeaders(const GURL& url) {
303   // Reset the state.
304   http_response_headers_ = NULL;
305   mime_type_.clear();
306   content_language_header_.clear();
308   // Discard all the response headers except the ones for |main_page_url|.
309   auto it = response_headers_map_.find(GURLByRemovingRefFromGURL(url));
310   if (it != response_headers_map_.end())
311     http_response_headers_ = it->second;
312   response_headers_map_.clear();
314   if (!http_response_headers_.get())
315     return;
317   // MIME type.
318   std::string mime_type;
319   http_response_headers_->GetMimeType(&mime_type);
320   mime_type_ = mime_type;
322   // Content-Language
323   std::string content_language;
324   http_response_headers_->GetNormalizedHeader("content-language",
325                                               &content_language);
326   // Remove everything after the comma ',' if any.
327   size_t comma_index = content_language.find_first_of(',');
328   if (comma_index != std::string::npos)
329     content_language.resize(comma_index);
330   content_language_header_ = content_language;
333 void WebStateImpl::ShowWebInterstitial(WebInterstitialImpl* interstitial) {
334   DCHECK(Configured());
335   DCHECK(interstitial);
336   interstitial_ = interstitial;
337   ShowTransientContentView(interstitial_->GetContentView());
340 void WebStateImpl::ClearTransientContentView() {
341   if (interstitial_) {
342     CRWSessionController* sessionController =
343         navigation_manager_.GetSessionController();
344     web::NavigationItem* currentItem =
345         [sessionController.currentEntry navigationItem];
346     if (currentItem->IsUnsafe()) {
347       // The unsafe page should be removed from history, and, in fact,
348       // SafeBrowsingBlockingPage will do just that *provided* that it
349       // isn't the current page. So to make this happen, before removing the
350       // interstitial, have the session controller go back one page.
351       [sessionController goBack];
352     }
353     [sessionController discardNonCommittedEntries];
354     // Store the currently displayed interstitial in a local variable and reset
355     // |interstitial_| early.  This is to prevent an infinite loop, as
356     // |DontProceed()| internally calls |ClearTransientContentView()|.
357     web::WebInterstitial* interstitial = interstitial_;
358     interstitial_ = nullptr;
359     interstitial->DontProceed();
360     // Don't access |interstitial| after calling |DontProceed()|, as it triggers
361     // deletion.
362     FOR_EACH_OBSERVER(WebStateObserver, observers_, InsterstitialDismissed());
363   }
364   [web_controller_ clearTransientContentView];
367 WebUIIOS* WebStateImpl::CreateWebUIIOS(const GURL& url) {
368   WebUIIOSControllerFactory* factory =
369       WebUIIOSControllerFactoryRegistry::GetInstance();
370   if (!factory)
371     return NULL;
372   WebUIIOSImpl* web_ui = new WebUIIOSImpl(this);
373   WebUIIOSController* controller =
374       factory->CreateWebUIIOSControllerForURL(web_ui, url);
375   if (controller) {
376     web_ui->SetController(controller);
377     return web_ui;
378   }
380   delete web_ui;
381   return NULL;
384 void WebStateImpl::ExecuteJavaScriptAsync(const base::string16& javascript) {
385   DCHECK(Configured());
386   [web_controller_ evaluateJavaScript:base::SysUTF16ToNSString(javascript)
387                   stringResultHandler:nil];
390 #pragma mark - RequestTracker management
392 void WebStateImpl::InitializeRequestTracker(
393     id<CRWRequestTrackerDelegate> delegate) {
394   BrowserState* browser_state = navigation_manager_.GetBrowserState();
395   request_tracker_ = RequestTrackerImpl::CreateTrackerForRequestGroupID(
396       GetRequestGroupID(), browser_state, browser_state->GetRequestContext(),
397       delegate);
400 void WebStateImpl::CloseRequestTracker() {
401   request_tracker_->Close();
402   request_tracker_ = NULL;
405 RequestTrackerImpl* WebStateImpl::GetRequestTracker() {
406   DCHECK(request_tracker_.get());
407   return request_tracker_.get();
410 net::RequestTracker::CacheMode WebStateImpl::GetCacheMode() {
411   return cache_mode_;
414 void WebStateImpl::SetCacheMode(net::RequestTracker::CacheMode mode) {
415   cache_mode_ = mode;
418 NSString* WebStateImpl::GetRequestGroupID() {
419   if (request_group_id_.get() == nil)
420     request_group_id_.reset([GenerateNewRequestGroupID() copy]);
422   return request_group_id_;
425 int WebStateImpl::DownloadImage(
426     const GURL& url,
427     bool is_favicon,
428     uint32_t max_bitmap_size,
429     bool bypass_cache,
430     const ImageDownloadCallback& callback) {
431   // |is_favicon| specifies whether the download of the image occurs with
432   // cookies or not. Currently, only downloads without cookies are supported.
433   // |bypass_cache| is ignored since the downloads never go through a cache.
434   DCHECK(is_favicon);
435   return [[web_controller_ delegate] downloadImageAtUrl:url
436                                           maxBitmapSize:max_bitmap_size
437                                               callback:callback];
440 #pragma mark - WebState implementation
442 UIView* WebStateImpl::GetView() {
443   return [web_controller_ view];
446 WebViewType WebStateImpl::GetWebViewType() const {
447   return [web_controller_ webViewType];
450 BrowserState* WebStateImpl::GetBrowserState() const {
451   return navigation_manager_.GetBrowserState();
454 void WebStateImpl::OpenURL(const WebState::OpenURLParams& params) {
455   DCHECK(Configured());
456   ClearTransientContentView();
457   [[web_controller_ delegate] openURLWithParams:params];
460 NavigationManager* WebStateImpl::GetNavigationManager() {
461   return &GetNavigationManagerImpl();
464 CRWJSInjectionReceiver* WebStateImpl::GetJSInjectionReceiver() const {
465   return [web_controller_ jsInjectionReceiver];
468 const std::string& WebStateImpl::GetContentLanguageHeader() const {
469   return content_language_header_;
472 const std::string& WebStateImpl::GetContentsMimeType() const {
473   return mime_type_;
476 bool WebStateImpl::ContentIsHTML() const {
477   return [web_controller_ contentIsHTML];
480 const GURL& WebStateImpl::GetVisibleURL() const {
481   web::NavigationItem* item = navigation_manager_.GetVisibleItem();
482   return item ? item->GetVirtualURL() : GURL::EmptyGURL();
485 const GURL& WebStateImpl::GetLastCommittedURL() const {
486   web::NavigationItem* item = navigation_manager_.GetLastCommittedItem();
487   return item ? item->GetVirtualURL() : GURL::EmptyGURL();
490 GURL WebStateImpl::GetCurrentURL(URLVerificationTrustLevel* trust_level) const {
491   return [web_controller_ currentURLWithTrustLevel:trust_level];
494 void WebStateImpl::AddScriptCommandCallback(
495     const ScriptCommandCallback& callback,
496     const std::string& command_prefix) {
497   DCHECK(!command_prefix.empty());
498   DCHECK(command_prefix.find_first_of('.') == std::string::npos);
499   DCHECK(script_command_callbacks_.find(command_prefix) ==
500          script_command_callbacks_.end());
501   script_command_callbacks_[command_prefix] = callback;
504 void WebStateImpl::RemoveScriptCommandCallback(
505     const std::string& command_prefix) {
506   DCHECK(script_command_callbacks_.find(command_prefix) !=
507          script_command_callbacks_.end());
508   script_command_callbacks_.erase(command_prefix);
511 id<CRWWebViewProxy> WebStateImpl::GetWebViewProxy() const {
512   return [web_controller_ webViewProxy];
515 void WebStateImpl::OnProvisionalNavigationStarted(const GURL& url) {
516   FOR_EACH_OBSERVER(WebStateObserver, observers_,
517                     ProvisionalNavigationStarted(url));
520 #pragma mark - NavigationManagerDelegate implementation
522 // Mirror WebContentsImpl::NavigateToPendingEntry() so that
523 // NavigationControllerIO::GoBack() actually goes back.
524 void WebStateImpl::NavigateToPendingEntry() {
525   [web_controller_ loadCurrentURL];
528 void WebStateImpl::OnNavigationItemCommitted(
529     const LoadCommittedDetails& load_details) {
530   FOR_EACH_OBSERVER(WebStateObserver, observers_,
531                     NavigationItemCommitted(load_details));
534 WebState* WebStateImpl::GetWebState() {
535   return this;
538 }  // namespace web