1 // Copyright (c) 2012 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. 4 5 #include "content/browser/web_contents/web_contents_impl.h" 6 7 #include <utility> 8 9 #include "base/command_line.h" 10 #include "base/debug/trace_event.h" 11 #include "base/lazy_instance.h" 12 #include "base/logging.h" 13 #include "base/metrics/histogram.h" 14 #include "base/metrics/stats_counters.h" 15 #include "base/process/process.h" 16 #include "base/strings/string16.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/string_util.h" 19 #include "base/strings/utf_string_conversions.h" 20 #include "base/time/time.h" 21 #include "content/browser/browser_plugin/browser_plugin_embedder.h" 22 #include "content/browser/browser_plugin/browser_plugin_guest.h" 23 #include "content/browser/child_process_security_policy_impl.h" 24 #include "content/browser/devtools/render_view_devtools_agent_host.h" 25 #include "content/browser/dom_storage/dom_storage_context_wrapper.h" 26 #include "content/browser/dom_storage/session_storage_namespace_impl.h" 27 #include "content/browser/download/download_stats.h" 28 #include "content/browser/download/mhtml_generation_manager.h" 29 #include "content/browser/download/save_package.h" 30 #include "content/browser/frame_host/cross_process_frame_connector.h" 31 #include "content/browser/frame_host/interstitial_page_impl.h" 32 #include "content/browser/frame_host/navigation_entry_impl.h" 33 #include "content/browser/frame_host/navigator_impl.h" 34 #include "content/browser/frame_host/render_frame_host_impl.h" 35 #include "content/browser/frame_host/render_widget_host_view_child_frame.h" 36 #include "content/browser/geolocation/geolocation_dispatcher_host.h" 37 #include "content/browser/host_zoom_map_impl.h" 38 #include "content/browser/loader/resource_dispatcher_host_impl.h" 39 #include "content/browser/media/midi_dispatcher_host.h" 40 #include "content/browser/message_port_message_filter.h" 41 #include "content/browser/message_port_service.h" 42 #include "content/browser/power_save_blocker_impl.h" 43 #include "content/browser/renderer_host/render_process_host_impl.h" 44 #include "content/browser/renderer_host/render_view_host_delegate_view.h" 45 #include "content/browser/renderer_host/render_view_host_impl.h" 46 #include "content/browser/renderer_host/render_widget_host_impl.h" 47 #include "content/browser/renderer_host/render_widget_host_view_base.h" 48 #include "content/browser/screen_orientation/screen_orientation_dispatcher_host.h" 49 #include "content/browser/site_instance_impl.h" 50 #include "content/browser/web_contents/web_contents_view_guest.h" 51 #include "content/browser/webui/generic_handler.h" 52 #include "content/browser/webui/web_ui_controller_factory_registry.h" 53 #include "content/browser/webui/web_ui_impl.h" 54 #include "content/common/browser_plugin/browser_plugin_constants.h" 55 #include "content/common/browser_plugin/browser_plugin_messages.h" 56 #include "content/common/frame_messages.h" 57 #include "content/common/image_messages.h" 58 #include "content/common/input_messages.h" 59 #include "content/common/ssl_status_serialization.h" 60 #include "content/common/view_messages.h" 61 #include "content/public/browser/ax_event_notification_details.h" 62 #include "content/public/browser/browser_context.h" 63 #include "content/public/browser/browser_plugin_guest_manager.h" 64 #include "content/public/browser/content_browser_client.h" 65 #include "content/public/browser/devtools_agent_host.h" 66 #include "content/public/browser/download_manager.h" 67 #include "content/public/browser/download_url_parameters.h" 68 #include "content/public/browser/invalidate_type.h" 69 #include "content/public/browser/javascript_dialog_manager.h" 70 #include "content/public/browser/load_from_memory_cache_details.h" 71 #include "content/public/browser/load_notification_details.h" 72 #include "content/public/browser/navigation_details.h" 73 #include "content/public/browser/notification_details.h" 74 #include "content/public/browser/notification_service.h" 75 #include "content/public/browser/resource_request_details.h" 76 #include "content/public/browser/storage_partition.h" 77 #include "content/public/browser/user_metrics.h" 78 #include "content/public/browser/web_contents_delegate.h" 79 #include "content/public/browser/web_contents_observer.h" 80 #include "content/public/common/bindings_policy.h" 81 #include "content/public/common/content_constants.h" 82 #include "content/public/common/content_switches.h" 83 #include "content/public/common/page_zoom.h" 84 #include "content/public/common/result_codes.h" 85 #include "content/public/common/url_constants.h" 86 #include "content/public/common/url_utils.h" 87 #include "net/base/mime_util.h" 88 #include "net/base/net_util.h" 89 #include "net/http/http_cache.h" 90 #include "net/http/http_transaction_factory.h" 91 #include "net/url_request/url_request_context.h" 92 #include "net/url_request/url_request_context_getter.h" 93 #include "ui/base/layout.h" 94 #include "ui/gfx/display.h" 95 #include "ui/gfx/screen.h" 96 #include "ui/gl/gl_switches.h" 97 #include "webkit/common/webpreferences.h" 98 99 #if defined(OS_ANDROID) 100 #include "content/browser/android/date_time_chooser_android.h" 101 #include "content/browser/media/android/browser_media_player_manager.h" 102 #include "content/browser/web_contents/web_contents_android.h" 103 #include "content/public/browser/android/content_view_core.h" 104 #endif 105 106 #if defined(OS_MACOSX) 107 #include "base/mac/foundation_util.h" 108 #endif 109 110 // Cross-Site Navigations 111 // 112 // If a WebContentsImpl is told to navigate to a different web site (as 113 // determined by SiteInstance), it will replace its current RenderViewHost with 114 // a new RenderViewHost dedicated to the new SiteInstance. This works as 115 // follows: 116 // 117 // - RVHM::Navigate determines whether the destination is cross-site, and if so, 118 // it creates a pending_render_view_host_. 119 // - The pending RVH is "suspended," so that no navigation messages are sent to 120 // its renderer until the beforeunload JavaScript handler has a chance to 121 // run in the current RVH. 122 // - The pending RVH tells CrossSiteRequestManager (a thread-safe singleton) 123 // that it has a pending cross-site request. We will check this on the IO 124 // thread when deciding how to handle the response. 125 // - The current RVH runs its beforeunload handler. If it returns false, we 126 // cancel all the pending logic. Otherwise we allow the pending RVH to send 127 // the navigation request to its renderer. 128 // - ResourceDispatcherHost receives a ResourceRequest on the IO thread for the 129 // main resource load on the pending RVH. It creates a 130 // CrossSiteResourceHandler to check whether a process swap is needed when 131 // the request is ready to commit. 132 // - When RDH receives a response, the BufferedResourceHandler determines 133 // whether it is a download. If so, it sends a message to the new renderer 134 // causing it to cancel the request, and the download proceeds. For now, the 135 // pending RVH remains until the next DidNavigate event for this 136 // WebContentsImpl. This isn't ideal, but it doesn't affect any functionality. 137 // - After RDH receives a response and determines that it is safe and not a 138 // download, the CrossSiteResourceHandler checks whether a process swap is 139 // needed (either because CrossSiteRequestManager has state for it or because 140 // a transfer was needed for a redirect). 141 // - If so, CrossSiteResourceHandler pauses the response to first run the old 142 // page's unload handler. It does this by asynchronously calling the 143 // OnCrossSiteResponse method of RenderFrameHostManager on the UI thread, 144 // which sends a SwapOut message to the current RVH. 145 // - Once the unload handler is finished, RVHM::SwappedOut checks if a transfer 146 // to a new process is needed, based on the stored pending_nav_params_. (This 147 // is independent of whether we started out with a cross-process navigation.) 148 // - If not, it just tells the ResourceDispatcherHost to resume the response 149 // to its current RenderViewHost. 150 // - If so, it cancels the current pending RenderViewHost and sets up a new 151 // navigation using RequestTransferURL. When the transferred request 152 // arrives in the ResourceDispatcherHost, we transfer the response and 153 // resume it. 154 // - The pending renderer sends a FrameNavigate message that invokes the 155 // DidNavigate method. This replaces the current RVH with the 156 // pending RVH. 157 // - The previous renderer is kept swapped out in RenderFrameHostManager in case 158 // the user goes back. The process only stays live if another tab is using 159 // it, but if so, the existing frame relationships will be maintained. 160 161 namespace content { 162 namespace { 163 164 const int kMinimumDelayBetweenLoadingUpdatesMS = 100; 165 166 // This matches what Blink's ProgressTracker has traditionally used for a 167 // minimum progress value. 168 const double kMinimumLoadingProgress = 0.1; 169 170 const char kDotGoogleDotCom[] = ".google.com"; 171 172 #if defined(OS_ANDROID) 173 const char kWebContentsAndroidKey[] = "web_contents_android"; 174 #endif // OS_ANDROID 175 176 base::LazyInstance<std::vector<WebContentsImpl::CreatedCallback> > 177 g_created_callbacks = LAZY_INSTANCE_INITIALIZER; 178 179 static int StartDownload(content::RenderFrameHost* rfh, 180 const GURL& url, 181 bool is_favicon, 182 uint32_t max_bitmap_size) { 183 static int g_next_image_download_id = 0; 184 rfh->Send(new ImageMsg_DownloadImage(rfh->GetRoutingID(), 185 ++g_next_image_download_id, 186 url, 187 is_favicon, 188 max_bitmap_size)); 189 return g_next_image_download_id; 190 } 191 192 void NotifyCacheOnIO( 193 scoped_refptr<net::URLRequestContextGetter> request_context, 194 const GURL& url, 195 const std::string& http_method) { 196 request_context->GetURLRequestContext()->http_transaction_factory()-> 197 GetCache()->OnExternalCacheHit(url, http_method); 198 } 199 200 // Helper function for retrieving all the sites in a frame tree. 201 bool CollectSites(BrowserContext* context, 202 std::set<GURL>* sites, 203 FrameTreeNode* node) { 204 sites->insert(SiteInstance::GetSiteForURL(context, node->current_url())); 205 return true; 206 } 207 208 bool ForEachFrameInternal( 209 const base::Callback<void(RenderFrameHost*)>& on_frame, 210 FrameTreeNode* node) { 211 on_frame.Run(node->current_frame_host()); 212 return true; 213 } 214 215 void SendToAllFramesInternal(IPC::Message* message, RenderFrameHost* rfh) { 216 IPC::Message* message_copy = new IPC::Message(*message); 217 message_copy->set_routing_id(rfh->GetRoutingID()); 218 rfh->Send(message_copy); 219 } 220 221 void AddRenderWidgetHostViewToSet(std::set<RenderWidgetHostView*>* set, 222 RenderFrameHost* rfh) { 223 RenderWidgetHostView* rwhv = static_cast<RenderFrameHostImpl*>(rfh) 224 ->frame_tree_node() 225 ->render_manager() 226 ->GetRenderWidgetHostView(); 227 set->insert(rwhv); 228 } 229 230 } // namespace 231 232 WebContents* WebContents::Create(const WebContents::CreateParams& params) { 233 return WebContentsImpl::CreateWithOpener( 234 params, static_cast<WebContentsImpl*>(params.opener)); 235 } 236 237 WebContents* WebContents::CreateWithSessionStorage( 238 const WebContents::CreateParams& params, 239 const SessionStorageNamespaceMap& session_storage_namespace_map) { 240 WebContentsImpl* new_contents = new WebContentsImpl( 241 params.browser_context, NULL); 242 243 for (SessionStorageNamespaceMap::const_iterator it = 244 session_storage_namespace_map.begin(); 245 it != session_storage_namespace_map.end(); 246 ++it) { 247 new_contents->GetController() 248 .SetSessionStorageNamespace(it->first, it->second.get()); 249 } 250 251 new_contents->Init(params); 252 return new_contents; 253 } 254 255 void WebContentsImpl::AddCreatedCallback(const CreatedCallback& callback) { 256 g_created_callbacks.Get().push_back(callback); 257 } 258 259 void WebContentsImpl::RemoveCreatedCallback(const CreatedCallback& callback) { 260 for (size_t i = 0; i < g_created_callbacks.Get().size(); ++i) { 261 if (g_created_callbacks.Get().at(i).Equals(callback)) { 262 g_created_callbacks.Get().erase(g_created_callbacks.Get().begin() + i); 263 return; 264 } 265 } 266 } 267 268 WebContents* WebContents::FromRenderViewHost(const RenderViewHost* rvh) { 269 return rvh->GetDelegate()->GetAsWebContents(); 270 } 271 272 WebContents* WebContents::FromRenderFrameHost(RenderFrameHost* rfh) { 273 RenderFrameHostImpl* rfh_impl = static_cast<RenderFrameHostImpl*>(rfh); 274 if (!rfh_impl) 275 return NULL; 276 return rfh_impl->delegate()->GetAsWebContents(); 277 } 278 279 // WebContentsImpl::DestructionObserver ---------------------------------------- 280 281 class WebContentsImpl::DestructionObserver : public WebContentsObserver { 282 public: 283 DestructionObserver(WebContentsImpl* owner, WebContents* watched_contents) 284 : WebContentsObserver(watched_contents), 285 owner_(owner) { 286 } 287 288 // WebContentsObserver: 289 virtual void WebContentsDestroyed() OVERRIDE { 290 owner_->OnWebContentsDestroyed( 291 static_cast<WebContentsImpl*>(web_contents())); 292 } 293 294 private: 295 WebContentsImpl* owner_; 296 297 DISALLOW_COPY_AND_ASSIGN(DestructionObserver); 298 }; 299 300 WebContentsImpl::ColorChooserInfo::ColorChooserInfo(int render_process_id, 301 int render_frame_id, 302 ColorChooser* chooser, 303 int identifier) 304 : render_process_id(render_process_id), 305 render_frame_id(render_frame_id), 306 chooser(chooser), 307 identifier(identifier) { 308 } 309 310 WebContentsImpl::ColorChooserInfo::~ColorChooserInfo() { 311 } 312 313 // WebContentsImpl ------------------------------------------------------------- 314 315 WebContentsImpl::WebContentsImpl( 316 BrowserContext* browser_context, 317 WebContentsImpl* opener) 318 : delegate_(NULL), 319 controller_(this, browser_context), 320 render_view_host_delegate_view_(NULL), 321 opener_(opener), 322 created_with_opener_(!!opener), 323 #if defined(OS_WIN) 324 accessible_parent_(NULL), 325 #endif 326 frame_tree_(new NavigatorImpl(&controller_, this), 327 this, this, this, this), 328 is_loading_(false), 329 is_load_to_different_document_(false), 330 crashed_status_(base::TERMINATION_STATUS_STILL_RUNNING), 331 crashed_error_code_(0), 332 waiting_for_response_(false), 333 load_state_(net::LOAD_STATE_IDLE, base::string16()), 334 loading_total_progress_(0.0), 335 loading_weak_factory_(this), 336 loading_frames_in_progress_(0), 337 upload_size_(0), 338 upload_position_(0), 339 displayed_insecure_content_(false), 340 has_accessed_initial_document_(false), 341 capturer_count_(0), 342 should_normally_be_visible_(true), 343 is_being_destroyed_(false), 344 notify_disconnection_(false), 345 dialog_manager_(NULL), 346 is_showing_before_unload_dialog_(false), 347 last_active_time_(base::TimeTicks::Now()), 348 closed_by_user_gesture_(false), 349 minimum_zoom_percent_(static_cast<int>(kMinimumZoomFactor * 100)), 350 maximum_zoom_percent_(static_cast<int>(kMaximumZoomFactor * 100)), 351 totalPinchGestureAmount_(0), 352 currentPinchZoomStepDelta_(0), 353 render_view_message_source_(NULL), 354 fullscreen_widget_routing_id_(MSG_ROUTING_NONE), 355 is_subframe_(false), 356 touch_emulation_enabled_(false), 357 last_dialog_suppressed_(false) { 358 for (size_t i = 0; i < g_created_callbacks.Get().size(); i++) 359 g_created_callbacks.Get().at(i).Run(this); 360 frame_tree_.SetFrameRemoveListener( 361 base::Bind(&WebContentsImpl::OnFrameRemoved, 362 base::Unretained(this))); 363 } 364 365 WebContentsImpl::~WebContentsImpl() { 366 is_being_destroyed_ = true; 367 368 // Delete all RFH pending shutdown, which will lead the corresponding RVH to 369 // shutdown and be deleted as well. 370 frame_tree_.ForEach( 371 base::Bind(&RenderFrameHostManager::ClearRFHsPendingShutdown)); 372 373 ClearAllPowerSaveBlockers(); 374 375 for (std::set<RenderWidgetHostImpl*>::iterator iter = 376 created_widgets_.begin(); iter != created_widgets_.end(); ++iter) { 377 (*iter)->DetachDelegate(); 378 } 379 created_widgets_.clear(); 380 381 // Clear out any JavaScript state. 382 if (dialog_manager_) 383 dialog_manager_->WebContentsDestroyed(this); 384 385 if (color_chooser_info_.get()) 386 color_chooser_info_->chooser->End(); 387 388 NotifyDisconnected(); 389 390 // Notify any observer that have a reference on this WebContents. 391 NotificationService::current()->Notify( 392 NOTIFICATION_WEB_CONTENTS_DESTROYED, 393 Source<WebContents>(this), 394 NotificationService::NoDetails()); 395 396 // Destroy all frame tree nodes except for the root; this notifies observers. 397 frame_tree_.ResetForMainFrameSwap(); 398 GetRenderManager()->ResetProxyHosts(); 399 400 // Manually call the observer methods for the root frame tree node. 401 RenderFrameHostManager* root = GetRenderManager(); 402 if (root->pending_frame_host()) { 403 FOR_EACH_OBSERVER(WebContentsObserver, 404 observers_, 405 RenderFrameDeleted(root->pending_frame_host())); 406 } 407 FOR_EACH_OBSERVER(WebContentsObserver, 408 observers_, 409 RenderFrameDeleted(root->current_frame_host())); 410 411 if (root->pending_render_view_host()) { 412 FOR_EACH_OBSERVER(WebContentsObserver, 413 observers_, 414 RenderViewDeleted(root->pending_render_view_host())); 415 } 416 417 FOR_EACH_OBSERVER(WebContentsObserver, 418 observers_, 419 RenderViewDeleted(root->current_host())); 420 421 FOR_EACH_OBSERVER(WebContentsObserver, 422 observers_, 423 WebContentsDestroyed()); 424 425 FOR_EACH_OBSERVER(WebContentsObserver, 426 observers_, 427 ResetWebContents()); 428 429 SetDelegate(NULL); 430 431 STLDeleteContainerPairSecondPointers(destruction_observers_.begin(), 432 destruction_observers_.end()); 433 } 434 435 WebContentsImpl* WebContentsImpl::CreateWithOpener( 436 const WebContents::CreateParams& params, 437 WebContentsImpl* opener) { 438 TRACE_EVENT0("browser", "WebContentsImpl::CreateWithOpener"); 439 WebContentsImpl* new_contents = new WebContentsImpl( 440 params.browser_context, params.opener_suppressed ? NULL : opener); 441 442 if (params.guest_instance_id) { 443 scoped_ptr<base::DictionaryValue> extra_params; 444 if (params.guest_extra_params) 445 extra_params.reset(params.guest_extra_params->DeepCopy()); 446 // This makes |new_contents| act as a guest. 447 // For more info, see comment above class BrowserPluginGuest. 448 BrowserPluginGuest::Create(params.guest_instance_id, 449 params.site_instance, 450 new_contents, 451 extra_params.Pass(), 452 opener ? opener->GetBrowserPluginGuest() : NULL); 453 // We are instantiating a WebContents for browser plugin. Set its subframe 454 // bit to true. 455 new_contents->is_subframe_ = true; 456 } 457 new_contents->Init(params); 458 return new_contents; 459 } 460 461 RenderFrameHostManager* WebContentsImpl::GetRenderManagerForTesting() { 462 return GetRenderManager(); 463 } 464 465 bool WebContentsImpl::OnMessageReceived(RenderViewHost* render_view_host, 466 const IPC::Message& message) { 467 return OnMessageReceived(render_view_host, NULL, message); 468 } 469 470 bool WebContentsImpl::OnMessageReceived(RenderViewHost* render_view_host, 471 RenderFrameHost* render_frame_host, 472 const IPC::Message& message) { 473 DCHECK(render_view_host || render_frame_host); 474 if (GetWebUI() && 475 static_cast<WebUIImpl*>(GetWebUI())->OnMessageReceived(message)) { 476 return true; 477 } 478 479 ObserverListBase<WebContentsObserver>::Iterator it(observers_); 480 WebContentsObserver* observer; 481 if (render_frame_host) { 482 while ((observer = it.GetNext()) != NULL) 483 if (observer->OnMessageReceived(message, render_frame_host)) 484 return true; 485 } else { 486 while ((observer = it.GetNext()) != NULL) 487 if (observer->OnMessageReceived(message)) 488 return true; 489 } 490 491 // Message handlers should be aware of which 492 // RenderViewHost/RenderFrameHost sent the message, which is temporarily 493 // stored in render_(view|frame)_message_source_. 494 if (render_frame_host) { 495 if (RenderViewDevToolsAgentHost::DispatchIPCMessage( 496 render_frame_host->GetRenderViewHost(), message)) 497 return true; 498 render_frame_message_source_ = render_frame_host; 499 } else { 500 if (RenderViewDevToolsAgentHost::DispatchIPCMessage( 501 render_view_host, message)) 502 return true; 503 render_view_message_source_ = render_view_host; 504 } 505 506 bool handled = true; 507 IPC_BEGIN_MESSAGE_MAP(WebContentsImpl, message) 508 IPC_MESSAGE_HANDLER(FrameHostMsg_PepperPluginHung, OnPepperPluginHung) 509 IPC_MESSAGE_HANDLER(FrameHostMsg_PluginCrashed, OnPluginCrashed) 510 IPC_MESSAGE_HANDLER(FrameHostMsg_DomOperationResponse, 511 OnDomOperationResponse) 512 IPC_MESSAGE_HANDLER(FrameHostMsg_DidChangeThemeColor, 513 OnThemeColorChanged) 514 IPC_MESSAGE_HANDLER(FrameHostMsg_DidFinishDocumentLoad, 515 OnDocumentLoadedInFrame) 516 IPC_MESSAGE_HANDLER(FrameHostMsg_DidFinishLoad, OnDidFinishLoad) 517 IPC_MESSAGE_HANDLER(FrameHostMsg_DidStartLoading, OnDidStartLoading) 518 IPC_MESSAGE_HANDLER(FrameHostMsg_DidStopLoading, OnDidStopLoading) 519 IPC_MESSAGE_HANDLER(FrameHostMsg_DidChangeLoadProgress, 520 OnDidChangeLoadProgress) 521 IPC_MESSAGE_HANDLER(FrameHostMsg_OpenColorChooser, OnOpenColorChooser) 522 IPC_MESSAGE_HANDLER(FrameHostMsg_EndColorChooser, OnEndColorChooser) 523 IPC_MESSAGE_HANDLER(FrameHostMsg_SetSelectedColorInColorChooser, 524 OnSetSelectedColorInColorChooser) 525 IPC_MESSAGE_HANDLER(FrameHostMsg_MediaPlayingNotification, 526 OnMediaPlayingNotification) 527 IPC_MESSAGE_HANDLER(FrameHostMsg_MediaPausedNotification, 528 OnMediaPausedNotification) 529 IPC_MESSAGE_HANDLER(ViewHostMsg_DidLoadResourceFromMemoryCache, 530 OnDidLoadResourceFromMemoryCache) 531 IPC_MESSAGE_HANDLER(ViewHostMsg_DidDisplayInsecureContent, 532 OnDidDisplayInsecureContent) 533 IPC_MESSAGE_HANDLER(ViewHostMsg_DidRunInsecureContent, 534 OnDidRunInsecureContent) 535 IPC_MESSAGE_HANDLER(ViewHostMsg_GoToEntryAtOffset, OnGoToEntryAtOffset) 536 IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateZoomLimits, OnUpdateZoomLimits) 537 IPC_MESSAGE_HANDLER(ViewHostMsg_EnumerateDirectory, OnEnumerateDirectory) 538 IPC_MESSAGE_HANDLER(ViewHostMsg_RegisterProtocolHandler, 539 OnRegisterProtocolHandler) 540 IPC_MESSAGE_HANDLER(ViewHostMsg_Find_Reply, OnFindReply) 541 IPC_MESSAGE_HANDLER(ViewHostMsg_AppCacheAccessed, OnAppCacheAccessed) 542 IPC_MESSAGE_HANDLER(ViewHostMsg_WebUISend, OnWebUISend) 543 IPC_MESSAGE_HANDLER(ViewHostMsg_RequestPpapiBrokerPermission, 544 OnRequestPpapiBrokerPermission) 545 IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginHostMsg_Attach, 546 OnBrowserPluginMessage(message)) 547 IPC_MESSAGE_HANDLER(ImageHostMsg_DidDownloadImage, OnDidDownloadImage) 548 IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateFaviconURL, OnUpdateFaviconURL) 549 IPC_MESSAGE_HANDLER(ViewHostMsg_DidFirstVisuallyNonEmptyPaint, 550 OnFirstVisuallyNonEmptyPaint) 551 IPC_MESSAGE_HANDLER(ViewHostMsg_ShowValidationMessage, 552 OnShowValidationMessage) 553 IPC_MESSAGE_HANDLER(ViewHostMsg_HideValidationMessage, 554 OnHideValidationMessage) 555 IPC_MESSAGE_HANDLER(ViewHostMsg_MoveValidationMessage, 556 OnMoveValidationMessage) 557 #if defined(OS_ANDROID) 558 IPC_MESSAGE_HANDLER(ViewHostMsg_FindMatchRects_Reply, 559 OnFindMatchRectsReply) 560 IPC_MESSAGE_HANDLER(ViewHostMsg_OpenDateTimeDialog, 561 OnOpenDateTimeDialog) 562 #endif 563 IPC_MESSAGE_UNHANDLED(handled = false) 564 IPC_END_MESSAGE_MAP() 565 render_view_message_source_ = NULL; 566 render_frame_message_source_ = NULL; 567 568 return handled; 569 } 570 571 void WebContentsImpl::RunFileChooser( 572 RenderViewHost* render_view_host, 573 const FileChooserParams& params) { 574 if (delegate_) 575 delegate_->RunFileChooser(this, params); 576 } 577 578 NavigationControllerImpl& WebContentsImpl::GetController() { 579 return controller_; 580 } 581 582 const NavigationControllerImpl& WebContentsImpl::GetController() const { 583 return controller_; 584 } 585 586 BrowserContext* WebContentsImpl::GetBrowserContext() const { 587 return controller_.GetBrowserContext(); 588 } 589 590 const GURL& WebContentsImpl::GetURL() const { 591 // We may not have a navigation entry yet. 592 NavigationEntry* entry = controller_.GetVisibleEntry(); 593 return entry ? entry->GetVirtualURL() : GURL::EmptyGURL(); 594 } 595 596 const GURL& WebContentsImpl::GetVisibleURL() const { 597 // We may not have a navigation entry yet. 598 NavigationEntry* entry = controller_.GetVisibleEntry(); 599 return entry ? entry->GetVirtualURL() : GURL::EmptyGURL(); 600 } 601 602 const GURL& WebContentsImpl::GetLastCommittedURL() const { 603 // We may not have a navigation entry yet. 604 NavigationEntry* entry = controller_.GetLastCommittedEntry(); 605 return entry ? entry->GetVirtualURL() : GURL::EmptyGURL(); 606 } 607 608 WebContentsDelegate* WebContentsImpl::GetDelegate() { 609 return delegate_; 610 } 611 612 void WebContentsImpl::SetDelegate(WebContentsDelegate* delegate) { 613 // TODO(cbentzel): remove this debugging code? 614 if (delegate == delegate_) 615 return; 616 if (delegate_) 617 delegate_->Detach(this); 618 delegate_ = delegate; 619 if (delegate_) { 620 delegate_->Attach(this); 621 // Ensure the visible RVH reflects the new delegate's preferences. 622 if (view_) 623 view_->SetOverscrollControllerEnabled(CanOverscrollContent()); 624 } 625 } 626 627 RenderProcessHost* WebContentsImpl::GetRenderProcessHost() const { 628 RenderViewHostImpl* host = GetRenderManager()->current_host(); 629 return host ? host->GetProcess() : NULL; 630 } 631 632 RenderFrameHost* WebContentsImpl::GetMainFrame() { 633 return frame_tree_.root()->current_frame_host(); 634 } 635 636 RenderFrameHost* WebContentsImpl::GetFocusedFrame() { 637 if (!frame_tree_.GetFocusedFrame()) 638 return NULL; 639 return frame_tree_.GetFocusedFrame()->current_frame_host(); 640 } 641 642 void WebContentsImpl::ForEachFrame( 643 const base::Callback<void(RenderFrameHost*)>& on_frame) { 644 frame_tree_.ForEach(base::Bind(&ForEachFrameInternal, on_frame)); 645 } 646 647 void WebContentsImpl::SendToAllFrames(IPC::Message* message) { 648 ForEachFrame(base::Bind(&SendToAllFramesInternal, message)); 649 delete message; 650 } 651 652 RenderViewHost* WebContentsImpl::GetRenderViewHost() const { 653 return GetRenderManager()->current_host(); 654 } 655 656 int WebContentsImpl::GetRoutingID() const { 657 if (!GetRenderViewHost()) 658 return MSG_ROUTING_NONE; 659 660 return GetRenderViewHost()->GetRoutingID(); 661 } 662 663 int WebContentsImpl::GetFullscreenWidgetRoutingID() const { 664 return fullscreen_widget_routing_id_; 665 } 666 667 RenderWidgetHostView* WebContentsImpl::GetRenderWidgetHostView() const { 668 return GetRenderManager()->GetRenderWidgetHostView(); 669 } 670 671 RenderWidgetHostView* WebContentsImpl::GetFullscreenRenderWidgetHostView() 672 const { 673 RenderWidgetHost* const widget_host = 674 RenderWidgetHostImpl::FromID(GetRenderProcessHost()->GetID(), 675 GetFullscreenWidgetRoutingID()); 676 return widget_host ? widget_host->GetView() : NULL; 677 } 678 679 WebContentsView* WebContentsImpl::GetView() const { 680 return view_.get(); 681 } 682 683 WebUI* WebContentsImpl::CreateWebUI(const GURL& url) { 684 WebUIImpl* web_ui = new WebUIImpl(this); 685 WebUIController* controller = WebUIControllerFactoryRegistry::GetInstance()-> 686 CreateWebUIControllerForURL(web_ui, url); 687 if (controller) { 688 web_ui->AddMessageHandler(new GenericHandler()); 689 web_ui->SetController(controller); 690 return web_ui; 691 } 692 693 delete web_ui; 694 return NULL; 695 } 696 697 WebUI* WebContentsImpl::GetWebUI() const { 698 return GetRenderManager()->web_ui() ? GetRenderManager()->web_ui() 699 : GetRenderManager()->pending_web_ui(); 700 } 701 702 WebUI* WebContentsImpl::GetCommittedWebUI() const { 703 return GetRenderManager()->web_ui(); 704 } 705 706 void WebContentsImpl::SetUserAgentOverride(const std::string& override) { 707 if (GetUserAgentOverride() == override) 708 return; 709 710 renderer_preferences_.user_agent_override = override; 711 712 // Send the new override string to the renderer. 713 RenderViewHost* host = GetRenderViewHost(); 714 if (host) 715 host->SyncRendererPrefs(); 716 717 // Reload the page if a load is currently in progress to avoid having 718 // different parts of the page loaded using different user agents. 719 NavigationEntry* entry = controller_.GetVisibleEntry(); 720 if (is_loading_ && entry != NULL && entry->GetIsOverridingUserAgent()) 721 controller_.ReloadIgnoringCache(true); 722 723 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 724 UserAgentOverrideSet(override)); 725 } 726 727 const std::string& WebContentsImpl::GetUserAgentOverride() const { 728 return renderer_preferences_.user_agent_override; 729 } 730 731 #if defined(OS_WIN) 732 void WebContentsImpl::SetParentNativeViewAccessible( 733 gfx::NativeViewAccessible accessible_parent) { 734 accessible_parent_ = accessible_parent; 735 if (GetRenderViewHost()) 736 GetRenderViewHostImpl()->SetParentNativeViewAccessible(accessible_parent); 737 } 738 #endif 739 740 const base::string16& WebContentsImpl::GetTitle() const { 741 // Transient entries take precedence. They are used for interstitial pages 742 // that are shown on top of existing pages. 743 NavigationEntry* entry = controller_.GetTransientEntry(); 744 std::string accept_languages = 745 GetContentClient()->browser()->GetAcceptLangs( 746 GetBrowserContext()); 747 if (entry) { 748 return entry->GetTitleForDisplay(accept_languages); 749 } 750 WebUI* our_web_ui = GetRenderManager()->pending_web_ui() ? 751 GetRenderManager()->pending_web_ui() : GetRenderManager()->web_ui(); 752 if (our_web_ui) { 753 // Don't override the title in view source mode. 754 entry = controller_.GetVisibleEntry(); 755 if (!(entry && entry->IsViewSourceMode())) { 756 // Give the Web UI the chance to override our title. 757 const base::string16& title = our_web_ui->GetOverriddenTitle(); 758 if (!title.empty()) 759 return title; 760 } 761 } 762 763 // We use the title for the last committed entry rather than a pending 764 // navigation entry. For example, when the user types in a URL, we want to 765 // keep the old page's title until the new load has committed and we get a new 766 // title. 767 entry = controller_.GetLastCommittedEntry(); 768 769 // We make an exception for initial navigations. 770 if (controller_.IsInitialNavigation()) { 771 // We only want to use the title from the visible entry in one of two cases: 772 // 1. There's already a committed entry for an initial navigation, in which 773 // case we are doing a history navigation in a new tab (e.g., Ctrl+Back). 774 // 2. The pending entry has been explicitly assigned a title to display. 775 // 776 // If there's no last committed entry and no assigned title, we should fall 777 // back to |page_title_when_no_navigation_entry_| rather than showing the 778 // URL. 779 if (entry || 780 (controller_.GetVisibleEntry() && 781 !controller_.GetVisibleEntry()->GetTitle().empty())) { 782 entry = controller_.GetVisibleEntry(); 783 } 784 } 785 786 if (entry) { 787 return entry->GetTitleForDisplay(accept_languages); 788 } 789 790 // |page_title_when_no_navigation_entry_| is finally used 791 // if no title cannot be retrieved. 792 return page_title_when_no_navigation_entry_; 793 } 794 795 int32 WebContentsImpl::GetMaxPageID() { 796 return GetMaxPageIDForSiteInstance(GetSiteInstance()); 797 } 798 799 int32 WebContentsImpl::GetMaxPageIDForSiteInstance( 800 SiteInstance* site_instance) { 801 if (max_page_ids_.find(site_instance->GetId()) == max_page_ids_.end()) 802 max_page_ids_[site_instance->GetId()] = -1; 803 804 return max_page_ids_[site_instance->GetId()]; 805 } 806 807 void WebContentsImpl::UpdateMaxPageID(int32 page_id) { 808 UpdateMaxPageIDForSiteInstance(GetSiteInstance(), page_id); 809 } 810 811 void WebContentsImpl::UpdateMaxPageIDForSiteInstance( 812 SiteInstance* site_instance, int32 page_id) { 813 if (GetMaxPageIDForSiteInstance(site_instance) < page_id) 814 max_page_ids_[site_instance->GetId()] = page_id; 815 } 816 817 void WebContentsImpl::CopyMaxPageIDsFrom(WebContents* web_contents) { 818 WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents); 819 max_page_ids_ = contents->max_page_ids_; 820 } 821 822 SiteInstance* WebContentsImpl::GetSiteInstance() const { 823 return GetRenderManager()->current_host()->GetSiteInstance(); 824 } 825 826 SiteInstance* WebContentsImpl::GetPendingSiteInstance() const { 827 RenderViewHost* dest_rvh = GetRenderManager()->pending_render_view_host() ? 828 GetRenderManager()->pending_render_view_host() : 829 GetRenderManager()->current_host(); 830 return dest_rvh->GetSiteInstance(); 831 } 832 833 bool WebContentsImpl::IsLoading() const { 834 return is_loading_; 835 } 836 837 bool WebContentsImpl::IsLoadingToDifferentDocument() const { 838 return is_loading_ && is_load_to_different_document_; 839 } 840 841 bool WebContentsImpl::IsWaitingForResponse() const { 842 return waiting_for_response_ && is_load_to_different_document_; 843 } 844 845 const net::LoadStateWithParam& WebContentsImpl::GetLoadState() const { 846 return load_state_; 847 } 848 849 const base::string16& WebContentsImpl::GetLoadStateHost() const { 850 return load_state_host_; 851 } 852 853 uint64 WebContentsImpl::GetUploadSize() const { 854 return upload_size_; 855 } 856 857 uint64 WebContentsImpl::GetUploadPosition() const { 858 return upload_position_; 859 } 860 861 std::set<GURL> WebContentsImpl::GetSitesInTab() const { 862 std::set<GURL> sites; 863 frame_tree_.ForEach(base::Bind(&CollectSites, 864 base::Unretained(GetBrowserContext()), 865 base::Unretained(&sites))); 866 return sites; 867 } 868 869 const std::string& WebContentsImpl::GetEncoding() const { 870 return canonical_encoding_; 871 } 872 873 bool WebContentsImpl::DisplayedInsecureContent() const { 874 return displayed_insecure_content_; 875 } 876 877 void WebContentsImpl::IncrementCapturerCount(const gfx::Size& capture_size) { 878 DCHECK(!is_being_destroyed_); 879 ++capturer_count_; 880 DVLOG(1) << "There are now " << capturer_count_ 881 << " capturing(s) of WebContentsImpl@" << this; 882 883 // Note: This provides a hint to upstream code to size the views optimally 884 // for quality (e.g., to avoid scaling). 885 if (!capture_size.IsEmpty() && preferred_size_for_capture_.IsEmpty()) { 886 preferred_size_for_capture_ = capture_size; 887 OnPreferredSizeChanged(preferred_size_); 888 } 889 } 890 891 void WebContentsImpl::DecrementCapturerCount() { 892 --capturer_count_; 893 DVLOG(1) << "There are now " << capturer_count_ 894 << " capturing(s) of WebContentsImpl@" << this; 895 DCHECK_LE(0, capturer_count_); 896 897 if (is_being_destroyed_) 898 return; 899 900 if (capturer_count_ == 0) { 901 const gfx::Size old_size = preferred_size_for_capture_; 902 preferred_size_for_capture_ = gfx::Size(); 903 OnPreferredSizeChanged(old_size); 904 } 905 906 if (IsHidden()) { 907 DVLOG(1) << "Executing delayed WasHidden()."; 908 WasHidden(); 909 } 910 } 911 912 int WebContentsImpl::GetCapturerCount() const { 913 return capturer_count_; 914 } 915 916 bool WebContentsImpl::IsCrashed() const { 917 return (crashed_status_ == base::TERMINATION_STATUS_PROCESS_CRASHED || 918 crashed_status_ == base::TERMINATION_STATUS_ABNORMAL_TERMINATION || 919 crashed_status_ == base::TERMINATION_STATUS_PROCESS_WAS_KILLED); 920 } 921 922 void WebContentsImpl::SetIsCrashed(base::TerminationStatus status, 923 int error_code) { 924 if (status == crashed_status_) 925 return; 926 927 crashed_status_ = status; 928 crashed_error_code_ = error_code; 929 NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB); 930 } 931 932 base::TerminationStatus WebContentsImpl::GetCrashedStatus() const { 933 return crashed_status_; 934 } 935 936 bool WebContentsImpl::IsBeingDestroyed() const { 937 return is_being_destroyed_; 938 } 939 940 void WebContentsImpl::NotifyNavigationStateChanged(unsigned changed_flags) { 941 if (delegate_) 942 delegate_->NavigationStateChanged(this, changed_flags); 943 } 944 945 base::TimeTicks WebContentsImpl::GetLastActiveTime() const { 946 return last_active_time_; 947 } 948 949 void WebContentsImpl::WasShown() { 950 controller_.SetActive(true); 951 952 std::set<RenderWidgetHostView*> widgets = GetRenderWidgetHostViewsInTree(); 953 for (std::set<RenderWidgetHostView*>::iterator iter = widgets.begin(); 954 iter != widgets.end(); 955 iter++) { 956 if (*iter) { 957 (*iter)->Show(); 958 #if defined(OS_MACOSX) 959 (*iter)->SetActive(true); 960 #endif 961 } 962 } 963 964 last_active_time_ = base::TimeTicks::Now(); 965 966 // The resize rect might have changed while this was inactive -- send the new 967 // one to make sure it's up to date. 968 RenderViewHostImpl* rvh = 969 static_cast<RenderViewHostImpl*>(GetRenderViewHost()); 970 if (rvh) { 971 rvh->ResizeRectChanged(GetRootWindowResizerRect()); 972 } 973 974 FOR_EACH_OBSERVER(WebContentsObserver, observers_, WasShown()); 975 976 should_normally_be_visible_ = true; 977 } 978 979 void WebContentsImpl::WasHidden() { 980 // If there are entities capturing screenshots or video (e.g., mirroring), 981 // don't activate the "disable rendering" optimization. 982 if (capturer_count_ == 0) { 983 // |GetRenderViewHost()| can be NULL if the user middle clicks a link to 984 // open a tab in the background, then closes the tab before selecting it. 985 // This is because closing the tab calls WebContentsImpl::Destroy(), which 986 // removes the |GetRenderViewHost()|; then when we actually destroy the 987 // window, OnWindowPosChanged() notices and calls WasHidden() (which 988 // calls us). 989 std::set<RenderWidgetHostView*> widgets = GetRenderWidgetHostViewsInTree(); 990 for (std::set<RenderWidgetHostView*>::iterator iter = widgets.begin(); 991 iter != widgets.end(); 992 iter++) { 993 if (*iter) 994 (*iter)->Hide(); 995 } 996 } 997 998 FOR_EACH_OBSERVER(WebContentsObserver, observers_, WasHidden()); 999 1000 should_normally_be_visible_ = false; 1001 } 1002 1003 bool WebContentsImpl::NeedToFireBeforeUnload() { 1004 // TODO(creis): Should we fire even for interstitial pages? 1005 return WillNotifyDisconnection() && 1006 !ShowingInterstitialPage() && 1007 !static_cast<RenderViewHostImpl*>( 1008 GetRenderViewHost())->SuddenTerminationAllowed(); 1009 } 1010 1011 void WebContentsImpl::DispatchBeforeUnload(bool for_cross_site_transition) { 1012 static_cast<RenderFrameHostImpl*>(GetMainFrame())->DispatchBeforeUnload( 1013 for_cross_site_transition); 1014 } 1015 1016 void WebContentsImpl::Stop() { 1017 GetRenderManager()->Stop(); 1018 FOR_EACH_OBSERVER(WebContentsObserver, observers_, NavigationStopped()); 1019 } 1020 1021 WebContents* WebContentsImpl::Clone() { 1022 // We use our current SiteInstance since the cloned entry will use it anyway. 1023 // We pass our own opener so that the cloned page can access it if it was 1024 // before. 1025 CreateParams create_params(GetBrowserContext(), GetSiteInstance()); 1026 create_params.initial_size = GetContainerBounds().size(); 1027 WebContentsImpl* tc = CreateWithOpener(create_params, opener_); 1028 tc->GetController().CopyStateFrom(controller_); 1029 FOR_EACH_OBSERVER(WebContentsObserver, 1030 observers_, 1031 DidCloneToNewWebContents(this, tc)); 1032 return tc; 1033 } 1034 1035 void WebContentsImpl::Observe(int type, 1036 const NotificationSource& source, 1037 const NotificationDetails& details) { 1038 switch (type) { 1039 case NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED: { 1040 RenderWidgetHost* host = Source<RenderWidgetHost>(source).ptr(); 1041 for (PendingWidgetViews::iterator i = pending_widget_views_.begin(); 1042 i != pending_widget_views_.end(); ++i) { 1043 if (host->GetView() == i->second) { 1044 pending_widget_views_.erase(i); 1045 break; 1046 } 1047 } 1048 break; 1049 } 1050 default: 1051 NOTREACHED(); 1052 } 1053 } 1054 1055 WebContents* WebContentsImpl::GetWebContents() { 1056 return this; 1057 } 1058 1059 void WebContentsImpl::Init(const WebContents::CreateParams& params) { 1060 // This is set before initializing the render manager since 1061 // RenderFrameHostManager::Init calls back into us via its delegate to ask if 1062 // it should be hidden. 1063 should_normally_be_visible_ = !params.initially_hidden; 1064 1065 GetRenderManager()->Init( 1066 params.browser_context, params.site_instance, params.routing_id, 1067 params.main_frame_routing_id); 1068 1069 WebContentsViewDelegate* delegate = 1070 GetContentClient()->browser()->GetWebContentsViewDelegate(this); 1071 1072 if (browser_plugin_guest_) { 1073 scoped_ptr<WebContentsView> platform_view(CreateWebContentsView( 1074 this, delegate, &render_view_host_delegate_view_)); 1075 1076 WebContentsViewGuest* rv = new WebContentsViewGuest( 1077 this, browser_plugin_guest_.get(), platform_view.Pass(), 1078 render_view_host_delegate_view_); 1079 render_view_host_delegate_view_ = rv; 1080 view_.reset(rv); 1081 } else { 1082 // Regular WebContentsView. 1083 view_.reset(CreateWebContentsView( 1084 this, delegate, &render_view_host_delegate_view_)); 1085 } 1086 CHECK(render_view_host_delegate_view_); 1087 CHECK(view_.get()); 1088 1089 gfx::Size initial_size = params.initial_size; 1090 view_->CreateView(initial_size, params.context); 1091 1092 // Listen for whether our opener gets destroyed. 1093 if (opener_) 1094 AddDestructionObserver(opener_); 1095 1096 registrar_.Add(this, 1097 NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED, 1098 NotificationService::AllBrowserContextsAndSources()); 1099 1100 geolocation_dispatcher_host_.reset(new GeolocationDispatcherHost(this)); 1101 midi_dispatcher_host_.reset(new MidiDispatcherHost(this)); 1102 1103 screen_orientation_dispatcher_host_.reset( 1104 new ScreenOrientationDispatcherHost(this)); 1105 1106 #if defined(OS_ANDROID) 1107 date_time_chooser_.reset(new DateTimeChooserAndroid()); 1108 #endif 1109 } 1110 1111 void WebContentsImpl::OnWebContentsDestroyed(WebContentsImpl* web_contents) { 1112 RemoveDestructionObserver(web_contents); 1113 1114 // Clear the opener if it has been closed. 1115 if (web_contents == opener_) { 1116 opener_ = NULL; 1117 return; 1118 } 1119 // Clear a pending contents that has been closed before being shown. 1120 for (PendingContents::iterator iter = pending_contents_.begin(); 1121 iter != pending_contents_.end(); 1122 ++iter) { 1123 if (iter->second != web_contents) 1124 continue; 1125 pending_contents_.erase(iter); 1126 return; 1127 } 1128 NOTREACHED(); 1129 } 1130 1131 void WebContentsImpl::AddDestructionObserver(WebContentsImpl* web_contents) { 1132 if (!ContainsKey(destruction_observers_, web_contents)) { 1133 destruction_observers_[web_contents] = 1134 new DestructionObserver(this, web_contents); 1135 } 1136 } 1137 1138 void WebContentsImpl::RemoveDestructionObserver(WebContentsImpl* web_contents) { 1139 DestructionObservers::iterator iter = 1140 destruction_observers_.find(web_contents); 1141 if (iter != destruction_observers_.end()) { 1142 delete destruction_observers_[web_contents]; 1143 destruction_observers_.erase(iter); 1144 } 1145 } 1146 1147 void WebContentsImpl::AddObserver(WebContentsObserver* observer) { 1148 observers_.AddObserver(observer); 1149 } 1150 1151 void WebContentsImpl::RemoveObserver(WebContentsObserver* observer) { 1152 observers_.RemoveObserver(observer); 1153 } 1154 1155 std::set<RenderWidgetHostView*> 1156 WebContentsImpl::GetRenderWidgetHostViewsInTree() { 1157 std::set<RenderWidgetHostView*> set; 1158 if (ShowingInterstitialPage()) { 1159 set.insert(GetRenderWidgetHostView()); 1160 } else { 1161 ForEachFrame( 1162 base::Bind(&AddRenderWidgetHostViewToSet, base::Unretained(&set))); 1163 } 1164 return set; 1165 } 1166 1167 void WebContentsImpl::Activate() { 1168 if (delegate_) 1169 delegate_->ActivateContents(this); 1170 } 1171 1172 void WebContentsImpl::Deactivate() { 1173 if (delegate_) 1174 delegate_->DeactivateContents(this); 1175 } 1176 1177 void WebContentsImpl::LostCapture() { 1178 if (delegate_) 1179 delegate_->LostCapture(); 1180 } 1181 1182 void WebContentsImpl::RenderWidgetDeleted( 1183 RenderWidgetHostImpl* render_widget_host) { 1184 if (is_being_destroyed_) { 1185 // |created_widgets_| might have been destroyed. 1186 return; 1187 } 1188 1189 std::set<RenderWidgetHostImpl*>::iterator iter = 1190 created_widgets_.find(render_widget_host); 1191 if (iter != created_widgets_.end()) 1192 created_widgets_.erase(iter); 1193 1194 if (render_widget_host && 1195 render_widget_host->GetRoutingID() == fullscreen_widget_routing_id_) { 1196 if (delegate_ && delegate_->EmbedsFullscreenWidget()) 1197 delegate_->ToggleFullscreenModeForTab(this, false); 1198 FOR_EACH_OBSERVER(WebContentsObserver, 1199 observers_, 1200 DidDestroyFullscreenWidget( 1201 fullscreen_widget_routing_id_)); 1202 fullscreen_widget_routing_id_ = MSG_ROUTING_NONE; 1203 } 1204 } 1205 1206 bool WebContentsImpl::PreHandleKeyboardEvent( 1207 const NativeWebKeyboardEvent& event, 1208 bool* is_keyboard_shortcut) { 1209 return delegate_ && 1210 delegate_->PreHandleKeyboardEvent(this, event, is_keyboard_shortcut); 1211 } 1212 1213 void WebContentsImpl::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) { 1214 if (delegate_) 1215 delegate_->HandleKeyboardEvent(this, event); 1216 } 1217 1218 bool WebContentsImpl::HandleWheelEvent( 1219 const blink::WebMouseWheelEvent& event) { 1220 #if !defined(OS_MACOSX) 1221 // On platforms other than Mac, control+mousewheel changes zoom. On Mac, this 1222 // isn't done for two reasons: 1223 // -the OS already has a gesture to do this through pinch-zoom 1224 // -if a user starts an inertial scroll, let's go, and presses control 1225 // (i.e. control+tab) then the OS's buffered scroll events will come in 1226 // with control key set which isn't what the user wants 1227 if (delegate_ && 1228 event.wheelTicksY && 1229 (event.modifiers & blink::WebInputEvent::ControlKey) && 1230 // Avoid adjusting the zoom in response to two-finger-scrolling touchpad 1231 // gestures, which are regrettably easy to trigger accidentally. 1232 !event.hasPreciseScrollingDeltas) { 1233 delegate_->ContentsZoomChange(event.wheelTicksY > 0); 1234 return true; 1235 } 1236 #endif 1237 return false; 1238 } 1239 1240 bool WebContentsImpl::PreHandleGestureEvent( 1241 const blink::WebGestureEvent& event) { 1242 return delegate_ && delegate_->PreHandleGestureEvent(this, event); 1243 } 1244 1245 bool WebContentsImpl::HandleGestureEvent( 1246 const blink::WebGestureEvent& event) { 1247 // Some platforms (eg. Mac) send GesturePinch events for trackpad pinch-zoom. 1248 // Use them to implement browser zoom, as for HandleWheelEvent above. 1249 if (event.type == blink::WebInputEvent::GesturePinchUpdate && 1250 event.sourceDevice == blink::WebGestureDeviceTouchpad) { 1251 // The scale difference necessary to trigger a zoom action. Derived from 1252 // experimentation to find a value that feels reasonable. 1253 const float kZoomStepValue = 0.6f; 1254 1255 // Find the (absolute) thresholds on either side of the current zoom factor, 1256 // then convert those to actual numbers to trigger a zoom in or out. 1257 // This logic deliberately makes the range around the starting zoom value 1258 // for the gesture twice as large as the other ranges (i.e., the notches are 1259 // at ..., -3*step, -2*step, -step, step, 2*step, 3*step, ... but not at 0) 1260 // so that it's easier to get back to your starting point than it is to 1261 // overshoot. 1262 float nextStep = (abs(currentPinchZoomStepDelta_) + 1) * kZoomStepValue; 1263 float backStep = abs(currentPinchZoomStepDelta_) * kZoomStepValue; 1264 float zoomInThreshold = (currentPinchZoomStepDelta_ >= 0) ? nextStep 1265 : -backStep; 1266 float zoomOutThreshold = (currentPinchZoomStepDelta_ <= 0) ? -nextStep 1267 : backStep; 1268 1269 totalPinchGestureAmount_ += (event.data.pinchUpdate.scale - 1.0); 1270 if (totalPinchGestureAmount_ > zoomInThreshold) { 1271 currentPinchZoomStepDelta_++; 1272 if (delegate_) 1273 delegate_->ContentsZoomChange(true); 1274 } else if (totalPinchGestureAmount_ < zoomOutThreshold) { 1275 currentPinchZoomStepDelta_--; 1276 if (delegate_) 1277 delegate_->ContentsZoomChange(false); 1278 } 1279 return true; 1280 } 1281 1282 return false; 1283 } 1284 1285 #if defined(OS_WIN) 1286 gfx::NativeViewAccessible WebContentsImpl::GetParentNativeViewAccessible() { 1287 return accessible_parent_; 1288 } 1289 #endif 1290 1291 void WebContentsImpl::HandleMouseDown() { 1292 if (delegate_) 1293 delegate_->HandleMouseDown(); 1294 } 1295 1296 void WebContentsImpl::HandleMouseUp() { 1297 if (delegate_) 1298 delegate_->HandleMouseUp(); 1299 } 1300 1301 void WebContentsImpl::HandlePointerActivate() { 1302 if (delegate_) 1303 delegate_->HandlePointerActivate(); 1304 } 1305 1306 void WebContentsImpl::HandleGestureBegin() { 1307 if (delegate_) 1308 delegate_->HandleGestureBegin(); 1309 } 1310 1311 void WebContentsImpl::HandleGestureEnd() { 1312 if (delegate_) 1313 delegate_->HandleGestureEnd(); 1314 } 1315 1316 void WebContentsImpl::ToggleFullscreenMode(bool enter_fullscreen) { 1317 // This method is being called to enter or leave renderer-initiated fullscreen 1318 // mode. Either way, make sure any existing fullscreen widget is shut down 1319 // first. 1320 RenderWidgetHostView* const widget_view = GetFullscreenRenderWidgetHostView(); 1321 if (widget_view) 1322 RenderWidgetHostImpl::From(widget_view->GetRenderWidgetHost())->Shutdown(); 1323 1324 if (delegate_) 1325 delegate_->ToggleFullscreenModeForTab(this, enter_fullscreen); 1326 1327 FOR_EACH_OBSERVER(WebContentsObserver, 1328 observers_, 1329 DidToggleFullscreenModeForTab(IsFullscreenForCurrentTab())); 1330 } 1331 1332 bool WebContentsImpl::IsFullscreenForCurrentTab() const { 1333 return delegate_ ? delegate_->IsFullscreenForTabOrPending(this) : false; 1334 } 1335 1336 void WebContentsImpl::RequestToLockMouse(bool user_gesture, 1337 bool last_unlocked_by_target) { 1338 if (delegate_) { 1339 delegate_->RequestToLockMouse(this, user_gesture, last_unlocked_by_target); 1340 } else { 1341 GotResponseToLockMouseRequest(false); 1342 } 1343 } 1344 1345 void WebContentsImpl::LostMouseLock() { 1346 if (delegate_) 1347 delegate_->LostMouseLock(); 1348 } 1349 1350 void WebContentsImpl::CreateNewWindow( 1351 int render_process_id, 1352 int route_id, 1353 int main_frame_route_id, 1354 const ViewHostMsg_CreateWindow_Params& params, 1355 SessionStorageNamespace* session_storage_namespace) { 1356 // We usually create the new window in the same BrowsingInstance (group of 1357 // script-related windows), by passing in the current SiteInstance. However, 1358 // if the opener is being suppressed (in a non-guest), we create a new 1359 // SiteInstance in its own BrowsingInstance. 1360 bool is_guest = BrowserPluginGuest::IsGuest(this); 1361 1362 // If the opener is to be suppressed, the new window can be in any process. 1363 // Since routing ids are process specific, we must not have one passed in 1364 // as argument here. 1365 DCHECK(!params.opener_suppressed || route_id == MSG_ROUTING_NONE); 1366 1367 scoped_refptr<SiteInstance> site_instance = 1368 params.opener_suppressed && !is_guest ? 1369 SiteInstance::CreateForURL(GetBrowserContext(), params.target_url) : 1370 GetSiteInstance(); 1371 1372 // A message to create a new window can only come from the active process for 1373 // this WebContentsImpl instance. If any other process sends the request, 1374 // it is invalid and the process must be terminated. 1375 if (GetRenderProcessHost()->GetID() != render_process_id) { 1376 base::ProcessHandle process_handle = 1377 RenderProcessHost::FromID(render_process_id)->GetHandle(); 1378 if (process_handle != base::kNullProcessHandle) { 1379 RecordAction( 1380 base::UserMetricsAction("Terminate_ProcessMismatch_CreateNewWindow")); 1381 base::KillProcess(process_handle, content::RESULT_CODE_KILLED, false); 1382 } 1383 return; 1384 } 1385 1386 // We must assign the SessionStorageNamespace before calling Init(). 1387 // 1388 // http://crbug.com/142685 1389 const std::string& partition_id = 1390 GetContentClient()->browser()-> 1391 GetStoragePartitionIdForSite(GetBrowserContext(), 1392 site_instance->GetSiteURL()); 1393 StoragePartition* partition = BrowserContext::GetStoragePartition( 1394 GetBrowserContext(), site_instance.get()); 1395 DOMStorageContextWrapper* dom_storage_context = 1396 static_cast<DOMStorageContextWrapper*>(partition->GetDOMStorageContext()); 1397 SessionStorageNamespaceImpl* session_storage_namespace_impl = 1398 static_cast<SessionStorageNamespaceImpl*>(session_storage_namespace); 1399 CHECK(session_storage_namespace_impl->IsFromContext(dom_storage_context)); 1400 1401 if (delegate_ && 1402 !delegate_->ShouldCreateWebContents(this, 1403 route_id, 1404 params.window_container_type, 1405 params.frame_name, 1406 params.target_url, 1407 partition_id, 1408 session_storage_namespace)) { 1409 if (route_id != MSG_ROUTING_NONE && 1410 !RenderViewHost::FromID(render_process_id, route_id)) { 1411 // If the embedder didn't create a WebContents for this route, we need to 1412 // delete the RenderView that had already been created. 1413 Send(new ViewMsg_Close(route_id)); 1414 } 1415 GetRenderViewHost()->GetProcess()->ResumeRequestsForView(route_id); 1416 GetRenderViewHost()->GetProcess()->ResumeRequestsForView( 1417 main_frame_route_id); 1418 return; 1419 } 1420 1421 // Create the new web contents. This will automatically create the new 1422 // WebContentsView. In the future, we may want to create the view separately. 1423 CreateParams create_params(GetBrowserContext(), site_instance.get()); 1424 create_params.routing_id = route_id; 1425 create_params.main_frame_routing_id = main_frame_route_id; 1426 create_params.opener = this; 1427 create_params.opener_suppressed = params.opener_suppressed; 1428 if (params.disposition == NEW_BACKGROUND_TAB) 1429 create_params.initially_hidden = true; 1430 1431 if (!is_guest) { 1432 create_params.context = view_->GetNativeView(); 1433 create_params.initial_size = GetContainerBounds().size(); 1434 } else { 1435 create_params.guest_instance_id = 1436 GetBrowserContext()->GetGuestManager()->GetNextInstanceID(); 1437 } 1438 WebContentsImpl* new_contents = static_cast<WebContentsImpl*>( 1439 WebContents::Create(create_params)); 1440 new_contents->GetController().SetSessionStorageNamespace( 1441 partition_id, 1442 session_storage_namespace); 1443 new_contents->RenderViewCreated(new_contents->GetRenderViewHost()); 1444 1445 // Save the window for later if we're not suppressing the opener (since it 1446 // will be shown immediately). 1447 if (!params.opener_suppressed) { 1448 if (!is_guest) { 1449 WebContentsView* new_view = new_contents->view_.get(); 1450 1451 // TODO(brettw): It seems bogus that we have to call this function on the 1452 // newly created object and give it one of its own member variables. 1453 new_view->CreateViewForWidget(new_contents->GetRenderViewHost()); 1454 } 1455 // Save the created window associated with the route so we can show it 1456 // later. 1457 DCHECK_NE(MSG_ROUTING_NONE, route_id); 1458 pending_contents_[route_id] = new_contents; 1459 AddDestructionObserver(new_contents); 1460 } 1461 1462 if (delegate_) { 1463 delegate_->WebContentsCreated( 1464 this, params.opener_render_frame_id, params.frame_name, 1465 params.target_url, new_contents); 1466 } 1467 1468 if (params.opener_suppressed) { 1469 // When the opener is suppressed, the original renderer cannot access the 1470 // new window. As a result, we need to show and navigate the window here. 1471 bool was_blocked = false; 1472 if (delegate_) { 1473 gfx::Rect initial_pos; 1474 delegate_->AddNewContents( 1475 this, new_contents, params.disposition, initial_pos, 1476 params.user_gesture, &was_blocked); 1477 } 1478 if (!was_blocked) { 1479 OpenURLParams open_params(params.target_url, 1480 Referrer(), 1481 CURRENT_TAB, 1482 PAGE_TRANSITION_LINK, 1483 true /* is_renderer_initiated */); 1484 open_params.user_gesture = params.user_gesture; 1485 new_contents->OpenURL(open_params); 1486 } 1487 } 1488 } 1489 1490 void WebContentsImpl::CreateNewWidget(int render_process_id, 1491 int route_id, 1492 blink::WebPopupType popup_type) { 1493 CreateNewWidget(render_process_id, route_id, false, popup_type); 1494 } 1495 1496 void WebContentsImpl::CreateNewFullscreenWidget(int render_process_id, 1497 int route_id) { 1498 CreateNewWidget(render_process_id, route_id, true, blink::WebPopupTypeNone); 1499 } 1500 1501 void WebContentsImpl::CreateNewWidget(int render_process_id, 1502 int route_id, 1503 bool is_fullscreen, 1504 blink::WebPopupType popup_type) { 1505 RenderProcessHost* process = GetRenderProcessHost(); 1506 // A message to create a new widget can only come from the active process for 1507 // this WebContentsImpl instance. If any other process sends the request, 1508 // it is invalid and the process must be terminated. 1509 if (process->GetID() != render_process_id) { 1510 base::ProcessHandle process_handle = 1511 RenderProcessHost::FromID(render_process_id)->GetHandle(); 1512 if (process_handle != base::kNullProcessHandle) { 1513 RecordAction( 1514 base::UserMetricsAction("Terminate_ProcessMismatch_CreateNewWidget")); 1515 base::KillProcess(process_handle, content::RESULT_CODE_KILLED, false); 1516 } 1517 return; 1518 } 1519 1520 RenderWidgetHostImpl* widget_host = 1521 new RenderWidgetHostImpl(this, process, route_id, IsHidden()); 1522 created_widgets_.insert(widget_host); 1523 1524 RenderWidgetHostViewBase* widget_view = 1525 static_cast<RenderWidgetHostViewBase*>( 1526 view_->CreateViewForPopupWidget(widget_host)); 1527 if (!widget_view) 1528 return; 1529 if (!is_fullscreen) { 1530 // Popups should not get activated. 1531 widget_view->SetPopupType(popup_type); 1532 } 1533 // Save the created widget associated with the route so we can show it later. 1534 pending_widget_views_[route_id] = widget_view; 1535 1536 #if defined(OS_MACOSX) 1537 // A RenderWidgetHostViewMac has lifetime scoped to the view. We'll retain it 1538 // to allow it to survive the trip without being hosted. 1539 base::mac::NSObjectRetain(widget_view->GetNativeView()); 1540 #endif 1541 } 1542 1543 void WebContentsImpl::ShowCreatedWindow(int route_id, 1544 WindowOpenDisposition disposition, 1545 const gfx::Rect& initial_pos, 1546 bool user_gesture) { 1547 WebContentsImpl* contents = GetCreatedWindow(route_id); 1548 if (contents) { 1549 WebContentsDelegate* delegate = GetDelegate(); 1550 if (delegate) { 1551 delegate->AddNewContents( 1552 this, contents, disposition, initial_pos, user_gesture, NULL); 1553 } 1554 } 1555 } 1556 1557 void WebContentsImpl::ShowCreatedWidget(int route_id, 1558 const gfx::Rect& initial_pos) { 1559 ShowCreatedWidget(route_id, false, initial_pos); 1560 } 1561 1562 void WebContentsImpl::ShowCreatedFullscreenWidget(int route_id) { 1563 ShowCreatedWidget(route_id, true, gfx::Rect()); 1564 } 1565 1566 void WebContentsImpl::ShowCreatedWidget(int route_id, 1567 bool is_fullscreen, 1568 const gfx::Rect& initial_pos) { 1569 RenderWidgetHostViewBase* widget_host_view = 1570 static_cast<RenderWidgetHostViewBase*>(GetCreatedWidget(route_id)); 1571 if (!widget_host_view) 1572 return; 1573 1574 RenderWidgetHostView* view = NULL; 1575 BrowserPluginGuest* guest = GetBrowserPluginGuest(); 1576 if (guest && guest->embedder_web_contents()) { 1577 view = guest->embedder_web_contents()->GetRenderWidgetHostView(); 1578 } else { 1579 view = GetRenderWidgetHostView(); 1580 } 1581 1582 if (is_fullscreen) { 1583 DCHECK_EQ(MSG_ROUTING_NONE, fullscreen_widget_routing_id_); 1584 fullscreen_widget_routing_id_ = route_id; 1585 if (delegate_ && delegate_->EmbedsFullscreenWidget()) { 1586 widget_host_view->InitAsChild(GetRenderWidgetHostView()->GetNativeView()); 1587 delegate_->ToggleFullscreenModeForTab(this, true); 1588 } else { 1589 widget_host_view->InitAsFullscreen(view); 1590 } 1591 FOR_EACH_OBSERVER(WebContentsObserver, 1592 observers_, 1593 DidShowFullscreenWidget(route_id)); 1594 if (!widget_host_view->HasFocus()) 1595 widget_host_view->Focus(); 1596 } else { 1597 widget_host_view->InitAsPopup(view, initial_pos); 1598 } 1599 1600 RenderWidgetHostImpl* render_widget_host_impl = 1601 RenderWidgetHostImpl::From(widget_host_view->GetRenderWidgetHost()); 1602 render_widget_host_impl->Init(); 1603 // Only allow privileged mouse lock for fullscreen render widget, which is 1604 // used to implement Pepper Flash fullscreen. 1605 render_widget_host_impl->set_allow_privileged_mouse_lock(is_fullscreen); 1606 1607 #if defined(OS_MACOSX) 1608 // A RenderWidgetHostViewMac has lifetime scoped to the view. Now that it's 1609 // properly embedded (or purposefully ignored) we can release the retain we 1610 // took in CreateNewWidget(). 1611 base::mac::NSObjectRelease(widget_host_view->GetNativeView()); 1612 #endif 1613 } 1614 1615 WebContentsImpl* WebContentsImpl::GetCreatedWindow(int route_id) { 1616 PendingContents::iterator iter = pending_contents_.find(route_id); 1617 1618 // Certain systems can block the creation of new windows. If we didn't succeed 1619 // in creating one, just return NULL. 1620 if (iter == pending_contents_.end()) { 1621 return NULL; 1622 } 1623 1624 WebContentsImpl* new_contents = iter->second; 1625 pending_contents_.erase(route_id); 1626 RemoveDestructionObserver(new_contents); 1627 1628 // Don't initialize the guest WebContents immediately. 1629 if (BrowserPluginGuest::IsGuest(new_contents)) 1630 return new_contents; 1631 1632 if (!new_contents->GetRenderProcessHost()->HasConnection() || 1633 !new_contents->GetRenderViewHost()->GetView()) 1634 return NULL; 1635 1636 // TODO(brettw): It seems bogus to reach into here and initialize the host. 1637 static_cast<RenderViewHostImpl*>(new_contents->GetRenderViewHost())->Init(); 1638 return new_contents; 1639 } 1640 1641 RenderWidgetHostView* WebContentsImpl::GetCreatedWidget(int route_id) { 1642 PendingWidgetViews::iterator iter = pending_widget_views_.find(route_id); 1643 if (iter == pending_widget_views_.end()) { 1644 DCHECK(false); 1645 return NULL; 1646 } 1647 1648 RenderWidgetHostView* widget_host_view = iter->second; 1649 pending_widget_views_.erase(route_id); 1650 1651 RenderWidgetHost* widget_host = widget_host_view->GetRenderWidgetHost(); 1652 if (!widget_host->GetProcess()->HasConnection()) { 1653 // The view has gone away or the renderer crashed. Nothing to do. 1654 return NULL; 1655 } 1656 1657 return widget_host_view; 1658 } 1659 1660 void WebContentsImpl::RequestMediaAccessPermission( 1661 const MediaStreamRequest& request, 1662 const MediaResponseCallback& callback) { 1663 if (delegate_) { 1664 delegate_->RequestMediaAccessPermission(this, request, callback); 1665 } else { 1666 callback.Run(MediaStreamDevices(), 1667 MEDIA_DEVICE_INVALID_STATE, 1668 scoped_ptr<MediaStreamUI>()); 1669 } 1670 } 1671 1672 SessionStorageNamespace* WebContentsImpl::GetSessionStorageNamespace( 1673 SiteInstance* instance) { 1674 return controller_.GetSessionStorageNamespace(instance); 1675 } 1676 1677 SessionStorageNamespaceMap WebContentsImpl::GetSessionStorageNamespaceMap() { 1678 return controller_.GetSessionStorageNamespaceMap(); 1679 } 1680 1681 FrameTree* WebContentsImpl::GetFrameTree() { 1682 return &frame_tree_; 1683 } 1684 1685 void WebContentsImpl::AccessibilityEventReceived( 1686 const std::vector<AXEventNotificationDetails>& details) { 1687 FOR_EACH_OBSERVER( 1688 WebContentsObserver, observers_, AccessibilityEventReceived(details)); 1689 } 1690 1691 void WebContentsImpl::OnShowValidationMessage( 1692 const gfx::Rect& anchor_in_root_view, 1693 const base::string16& main_text, 1694 const base::string16& sub_text) { 1695 if (delegate_) 1696 delegate_->ShowValidationMessage( 1697 this, anchor_in_root_view, main_text, sub_text); 1698 } 1699 1700 void WebContentsImpl::OnHideValidationMessage() { 1701 if (delegate_) 1702 delegate_->HideValidationMessage(this); 1703 } 1704 1705 void WebContentsImpl::OnMoveValidationMessage( 1706 const gfx::Rect& anchor_in_root_view) { 1707 if (delegate_) 1708 delegate_->MoveValidationMessage(this, anchor_in_root_view); 1709 } 1710 1711 void WebContentsImpl::DidSendScreenRects(RenderWidgetHostImpl* rwh) { 1712 if (browser_plugin_embedder_) 1713 browser_plugin_embedder_->DidSendScreenRects(); 1714 } 1715 1716 void WebContentsImpl::OnTouchEmulationEnabled(bool enabled) { 1717 touch_emulation_enabled_ = enabled; 1718 if (view_) 1719 view_->SetOverscrollControllerEnabled(CanOverscrollContent()); 1720 } 1721 1722 void WebContentsImpl::UpdatePreferredSize(const gfx::Size& pref_size) { 1723 const gfx::Size old_size = GetPreferredSize(); 1724 preferred_size_ = pref_size; 1725 OnPreferredSizeChanged(old_size); 1726 } 1727 1728 void WebContentsImpl::ResizeDueToAutoResize(const gfx::Size& new_size) { 1729 if (delegate_) 1730 delegate_->ResizeDueToAutoResize(this, new_size); 1731 } 1732 1733 WebContents* WebContentsImpl::OpenURL(const OpenURLParams& params) { 1734 if (!delegate_) 1735 return NULL; 1736 1737 WebContents* new_contents = delegate_->OpenURLFromTab(this, params); 1738 return new_contents; 1739 } 1740 1741 bool WebContentsImpl::Send(IPC::Message* message) { 1742 if (!GetRenderViewHost()) { 1743 delete message; 1744 return false; 1745 } 1746 1747 return GetRenderViewHost()->Send(message); 1748 } 1749 1750 bool WebContentsImpl::NavigateToPendingEntry( 1751 NavigationController::ReloadType reload_type) { 1752 FrameTreeNode* node = frame_tree_.root(); 1753 1754 // If we are using --site-per-process, we should navigate in the FrameTreeNode 1755 // specified in the pending entry. 1756 NavigationEntryImpl* pending_entry = 1757 NavigationEntryImpl::FromNavigationEntry(controller_.GetPendingEntry()); 1758 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess) && 1759 pending_entry->frame_tree_node_id() != -1) { 1760 node = frame_tree_.FindByID(pending_entry->frame_tree_node_id()); 1761 } 1762 1763 return node->navigator()->NavigateToPendingEntry( 1764 node->current_frame_host(), reload_type); 1765 } 1766 1767 void WebContentsImpl::RenderFrameForInterstitialPageCreated( 1768 RenderFrameHost* render_frame_host) { 1769 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 1770 RenderFrameForInterstitialPageCreated(render_frame_host)); 1771 } 1772 1773 void WebContentsImpl::AttachInterstitialPage( 1774 InterstitialPageImpl* interstitial_page) { 1775 DCHECK(interstitial_page); 1776 GetRenderManager()->set_interstitial_page(interstitial_page); 1777 1778 // Cancel any visible dialogs so that they don't interfere with the 1779 // interstitial. 1780 if (dialog_manager_) 1781 dialog_manager_->CancelActiveAndPendingDialogs(this); 1782 1783 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 1784 DidAttachInterstitialPage()); 1785 } 1786 1787 void WebContentsImpl::DetachInterstitialPage() { 1788 if (ShowingInterstitialPage()) 1789 GetRenderManager()->remove_interstitial_page(); 1790 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 1791 DidDetachInterstitialPage()); 1792 } 1793 1794 void WebContentsImpl::SetHistoryLengthAndPrune( 1795 const SiteInstance* site_instance, 1796 int history_length, 1797 int32 minimum_page_id) { 1798 // SetHistoryLengthAndPrune doesn't work when there are pending cross-site 1799 // navigations. Callers should ensure that this is the case. 1800 if (GetRenderManager()->pending_render_view_host()) { 1801 NOTREACHED(); 1802 return; 1803 } 1804 RenderViewHostImpl* rvh = GetRenderViewHostImpl(); 1805 if (!rvh) { 1806 NOTREACHED(); 1807 return; 1808 } 1809 if (site_instance && rvh->GetSiteInstance() != site_instance) { 1810 NOTREACHED(); 1811 return; 1812 } 1813 Send(new ViewMsg_SetHistoryLengthAndPrune(GetRoutingID(), 1814 history_length, 1815 minimum_page_id)); 1816 } 1817 1818 void WebContentsImpl::ReloadFocusedFrame(bool ignore_cache) { 1819 RenderFrameHost* focused_frame = GetFocusedFrame(); 1820 if (!focused_frame) 1821 return; 1822 1823 focused_frame->Send(new FrameMsg_Reload( 1824 focused_frame->GetRoutingID(), ignore_cache)); 1825 } 1826 1827 void WebContentsImpl::Undo() { 1828 RenderFrameHost* focused_frame = GetFocusedFrame(); 1829 if (!focused_frame) 1830 return; 1831 1832 focused_frame->Send(new InputMsg_Undo(focused_frame->GetRoutingID())); 1833 RecordAction(base::UserMetricsAction("Undo")); 1834 } 1835 1836 void WebContentsImpl::Redo() { 1837 RenderFrameHost* focused_frame = GetFocusedFrame(); 1838 if (!focused_frame) 1839 return; 1840 focused_frame->Send(new InputMsg_Redo(focused_frame->GetRoutingID())); 1841 RecordAction(base::UserMetricsAction("Redo")); 1842 } 1843 1844 void WebContentsImpl::Cut() { 1845 RenderFrameHost* focused_frame = GetFocusedFrame(); 1846 if (!focused_frame) 1847 return; 1848 1849 focused_frame->Send(new InputMsg_Cut(focused_frame->GetRoutingID())); 1850 RecordAction(base::UserMetricsAction("Cut")); 1851 } 1852 1853 void WebContentsImpl::Copy() { 1854 RenderFrameHost* focused_frame = GetFocusedFrame(); 1855 if (!focused_frame) 1856 return; 1857 1858 focused_frame->Send(new InputMsg_Copy(focused_frame->GetRoutingID())); 1859 RecordAction(base::UserMetricsAction("Copy")); 1860 } 1861 1862 void WebContentsImpl::CopyToFindPboard() { 1863 #if defined(OS_MACOSX) 1864 RenderFrameHost* focused_frame = GetFocusedFrame(); 1865 if (!focused_frame) 1866 return; 1867 1868 // Windows/Linux don't have the concept of a find pasteboard. 1869 focused_frame->Send( 1870 new InputMsg_CopyToFindPboard(focused_frame->GetRoutingID())); 1871 RecordAction(base::UserMetricsAction("CopyToFindPboard")); 1872 #endif 1873 } 1874 1875 void WebContentsImpl::Paste() { 1876 RenderFrameHost* focused_frame = GetFocusedFrame(); 1877 if (!focused_frame) 1878 return; 1879 1880 focused_frame->Send(new InputMsg_Paste(focused_frame->GetRoutingID())); 1881 RecordAction(base::UserMetricsAction("Paste")); 1882 } 1883 1884 void WebContentsImpl::PasteAndMatchStyle() { 1885 RenderFrameHost* focused_frame = GetFocusedFrame(); 1886 if (!focused_frame) 1887 return; 1888 1889 focused_frame->Send(new InputMsg_PasteAndMatchStyle( 1890 focused_frame->GetRoutingID())); 1891 RecordAction(base::UserMetricsAction("PasteAndMatchStyle")); 1892 } 1893 1894 void WebContentsImpl::Delete() { 1895 RenderFrameHost* focused_frame = GetFocusedFrame(); 1896 if (!focused_frame) 1897 return; 1898 1899 focused_frame->Send(new InputMsg_Delete(focused_frame->GetRoutingID())); 1900 RecordAction(base::UserMetricsAction("DeleteSelection")); 1901 } 1902 1903 void WebContentsImpl::SelectAll() { 1904 RenderFrameHost* focused_frame = GetFocusedFrame(); 1905 if (!focused_frame) 1906 return; 1907 1908 focused_frame->Send(new InputMsg_SelectAll(focused_frame->GetRoutingID())); 1909 RecordAction(base::UserMetricsAction("SelectAll")); 1910 } 1911 1912 void WebContentsImpl::Unselect() { 1913 RenderFrameHost* focused_frame = GetFocusedFrame(); 1914 if (!focused_frame) 1915 return; 1916 1917 focused_frame->Send(new InputMsg_Unselect(focused_frame->GetRoutingID())); 1918 RecordAction(base::UserMetricsAction("Unselect")); 1919 } 1920 1921 void WebContentsImpl::Replace(const base::string16& word) { 1922 RenderFrameHost* focused_frame = GetFocusedFrame(); 1923 if (!focused_frame) 1924 return; 1925 1926 focused_frame->Send(new InputMsg_Replace( 1927 focused_frame->GetRoutingID(), word)); 1928 } 1929 1930 void WebContentsImpl::ReplaceMisspelling(const base::string16& word) { 1931 RenderFrameHost* focused_frame = GetFocusedFrame(); 1932 if (!focused_frame) 1933 return; 1934 1935 focused_frame->Send(new InputMsg_ReplaceMisspelling( 1936 focused_frame->GetRoutingID(), word)); 1937 } 1938 1939 void WebContentsImpl::NotifyContextMenuClosed( 1940 const CustomContextMenuContext& context) { 1941 RenderFrameHost* focused_frame = GetFocusedFrame(); 1942 if (!focused_frame) 1943 return; 1944 1945 focused_frame->Send(new FrameMsg_ContextMenuClosed( 1946 focused_frame->GetRoutingID(), context)); 1947 } 1948 1949 void WebContentsImpl::ExecuteCustomContextMenuCommand( 1950 int action, const CustomContextMenuContext& context) { 1951 RenderFrameHost* focused_frame = GetFocusedFrame(); 1952 if (!focused_frame) 1953 return; 1954 1955 focused_frame->Send(new FrameMsg_CustomContextMenuAction( 1956 focused_frame->GetRoutingID(), context, action)); 1957 } 1958 1959 gfx::NativeView WebContentsImpl::GetNativeView() { 1960 return view_->GetNativeView(); 1961 } 1962 1963 gfx::NativeView WebContentsImpl::GetContentNativeView() { 1964 return view_->GetContentNativeView(); 1965 } 1966 1967 gfx::NativeWindow WebContentsImpl::GetTopLevelNativeWindow() { 1968 return view_->GetTopLevelNativeWindow(); 1969 } 1970 1971 gfx::Rect WebContentsImpl::GetViewBounds() { 1972 return view_->GetViewBounds(); 1973 } 1974 1975 gfx::Rect WebContentsImpl::GetContainerBounds() { 1976 gfx::Rect rv; 1977 view_->GetContainerBounds(&rv); 1978 return rv; 1979 } 1980 1981 DropData* WebContentsImpl::GetDropData() { 1982 return view_->GetDropData(); 1983 } 1984 1985 void WebContentsImpl::Focus() { 1986 view_->Focus(); 1987 } 1988 1989 void WebContentsImpl::SetInitialFocus() { 1990 view_->SetInitialFocus(); 1991 } 1992 1993 void WebContentsImpl::StoreFocus() { 1994 view_->StoreFocus(); 1995 } 1996 1997 void WebContentsImpl::RestoreFocus() { 1998 view_->RestoreFocus(); 1999 } 2000 2001 void WebContentsImpl::FocusThroughTabTraversal(bool reverse) { 2002 if (ShowingInterstitialPage()) { 2003 GetRenderManager()->interstitial_page()->FocusThroughTabTraversal(reverse); 2004 return; 2005 } 2006 GetRenderViewHostImpl()->SetInitialFocus(reverse); 2007 } 2008 2009 bool WebContentsImpl::ShowingInterstitialPage() const { 2010 return GetRenderManager()->interstitial_page() != NULL; 2011 } 2012 2013 InterstitialPage* WebContentsImpl::GetInterstitialPage() const { 2014 return GetRenderManager()->interstitial_page(); 2015 } 2016 2017 bool WebContentsImpl::IsSavable() { 2018 // WebKit creates Document object when MIME type is application/xhtml+xml, 2019 // so we also support this MIME type. 2020 return contents_mime_type_ == "text/html" || 2021 contents_mime_type_ == "text/xml" || 2022 contents_mime_type_ == "application/xhtml+xml" || 2023 contents_mime_type_ == "text/plain" || 2024 contents_mime_type_ == "text/css" || 2025 net::IsSupportedJavascriptMimeType(contents_mime_type_.c_str()); 2026 } 2027 2028 void WebContentsImpl::OnSavePage() { 2029 // If we can not save the page, try to download it. 2030 if (!IsSavable()) { 2031 RecordDownloadSource(INITIATED_BY_SAVE_PACKAGE_ON_NON_HTML); 2032 SaveFrame(GetURL(), Referrer()); 2033 return; 2034 } 2035 2036 Stop(); 2037 2038 // Create the save package and possibly prompt the user for the name to save 2039 // the page as. The user prompt is an asynchronous operation that runs on 2040 // another thread. 2041 save_package_ = new SavePackage(this); 2042 save_package_->GetSaveInfo(); 2043 } 2044 2045 // Used in automated testing to bypass prompting the user for file names. 2046 // Instead, the names and paths are hard coded rather than running them through 2047 // file name sanitation and extension / mime checking. 2048 bool WebContentsImpl::SavePage(const base::FilePath& main_file, 2049 const base::FilePath& dir_path, 2050 SavePageType save_type) { 2051 // Stop the page from navigating. 2052 Stop(); 2053 2054 save_package_ = new SavePackage(this, save_type, main_file, dir_path); 2055 return save_package_->Init(SavePackageDownloadCreatedCallback()); 2056 } 2057 2058 void WebContentsImpl::SaveFrame(const GURL& url, 2059 const Referrer& referrer) { 2060 if (!GetURL().is_valid()) 2061 return; 2062 bool is_main_frame = (url == GetURL()); 2063 2064 DownloadManager* dlm = 2065 BrowserContext::GetDownloadManager(GetBrowserContext()); 2066 if (!dlm) 2067 return; 2068 int64 post_id = -1; 2069 if (is_main_frame) { 2070 const NavigationEntry* entry = controller_.GetLastCommittedEntry(); 2071 if (entry) 2072 post_id = entry->GetPostID(); 2073 } 2074 scoped_ptr<DownloadUrlParameters> params( 2075 DownloadUrlParameters::FromWebContents(this, url)); 2076 params->set_referrer(referrer); 2077 params->set_post_id(post_id); 2078 params->set_prefer_cache(true); 2079 if (post_id >= 0) 2080 params->set_method("POST"); 2081 params->set_prompt(true); 2082 dlm->DownloadUrl(params.Pass()); 2083 } 2084 2085 void WebContentsImpl::GenerateMHTML( 2086 const base::FilePath& file, 2087 const base::Callback<void(int64)>& callback) { 2088 MHTMLGenerationManager::GetInstance()->SaveMHTML(this, file, callback); 2089 } 2090 2091 const std::string& WebContentsImpl::GetContentsMimeType() const { 2092 return contents_mime_type_; 2093 } 2094 2095 bool WebContentsImpl::WillNotifyDisconnection() const { 2096 return notify_disconnection_; 2097 } 2098 2099 void WebContentsImpl::SetOverrideEncoding(const std::string& encoding) { 2100 SetEncoding(encoding); 2101 Send(new ViewMsg_SetPageEncoding(GetRoutingID(), encoding)); 2102 } 2103 2104 void WebContentsImpl::ResetOverrideEncoding() { 2105 canonical_encoding_.clear(); 2106 Send(new ViewMsg_ResetPageEncodingToDefault(GetRoutingID())); 2107 } 2108 2109 RendererPreferences* WebContentsImpl::GetMutableRendererPrefs() { 2110 return &renderer_preferences_; 2111 } 2112 2113 void WebContentsImpl::Close() { 2114 Close(GetRenderViewHost()); 2115 } 2116 2117 void WebContentsImpl::DragSourceEndedAt(int client_x, int client_y, 2118 int screen_x, int screen_y, blink::WebDragOperation operation) { 2119 if (browser_plugin_embedder_.get()) 2120 browser_plugin_embedder_->DragSourceEndedAt(client_x, client_y, 2121 screen_x, screen_y, operation); 2122 if (GetRenderViewHost()) 2123 GetRenderViewHostImpl()->DragSourceEndedAt(client_x, client_y, 2124 screen_x, screen_y, operation); 2125 } 2126 2127 void WebContentsImpl::DidGetResourceResponseStart( 2128 const ResourceRequestDetails& details) { 2129 controller_.ssl_manager()->DidStartResourceResponse(details); 2130 2131 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2132 DidGetResourceResponseStart(details)); 2133 2134 // TODO(avi): Remove. http://crbug.com/170921 2135 NotificationService::current()->Notify( 2136 NOTIFICATION_RESOURCE_RESPONSE_STARTED, 2137 Source<WebContents>(this), 2138 Details<const ResourceRequestDetails>(&details)); 2139 } 2140 2141 void WebContentsImpl::DidGetRedirectForResourceRequest( 2142 RenderViewHost* render_view_host, 2143 const ResourceRedirectDetails& details) { 2144 controller_.ssl_manager()->DidReceiveResourceRedirect(details); 2145 2146 FOR_EACH_OBSERVER( 2147 WebContentsObserver, 2148 observers_, 2149 DidGetRedirectForResourceRequest(render_view_host, details)); 2150 2151 // TODO(avi): Remove. http://crbug.com/170921 2152 NotificationService::current()->Notify( 2153 NOTIFICATION_RESOURCE_RECEIVED_REDIRECT, 2154 Source<WebContents>(this), 2155 Details<const ResourceRedirectDetails>(&details)); 2156 } 2157 2158 void WebContentsImpl::SystemDragEnded() { 2159 if (GetRenderViewHost()) 2160 GetRenderViewHostImpl()->DragSourceSystemDragEnded(); 2161 if (delegate_) 2162 delegate_->DragEnded(); 2163 if (browser_plugin_embedder_.get()) 2164 browser_plugin_embedder_->SystemDragEnded(); 2165 } 2166 2167 void WebContentsImpl::UserGestureDone() { 2168 OnUserGesture(); 2169 } 2170 2171 void WebContentsImpl::SetClosedByUserGesture(bool value) { 2172 closed_by_user_gesture_ = value; 2173 } 2174 2175 bool WebContentsImpl::GetClosedByUserGesture() const { 2176 return closed_by_user_gesture_; 2177 } 2178 2179 int WebContentsImpl::GetZoomPercent(bool* enable_increment, 2180 bool* enable_decrement) const { 2181 *enable_decrement = *enable_increment = false; 2182 // Calculate the zoom percent from the factor. Round up to the nearest whole 2183 // number. 2184 int percent = static_cast<int>( 2185 ZoomLevelToZoomFactor(HostZoomMap::GetZoomLevel(this)) * 100 + 0.5); 2186 *enable_decrement = percent > minimum_zoom_percent_; 2187 *enable_increment = percent < maximum_zoom_percent_; 2188 return percent; 2189 } 2190 2191 void WebContentsImpl::ViewSource() { 2192 if (!delegate_) 2193 return; 2194 2195 NavigationEntry* entry = GetController().GetLastCommittedEntry(); 2196 if (!entry) 2197 return; 2198 2199 delegate_->ViewSourceForTab(this, entry->GetURL()); 2200 } 2201 2202 void WebContentsImpl::ViewFrameSource(const GURL& url, 2203 const PageState& page_state) { 2204 if (!delegate_) 2205 return; 2206 2207 delegate_->ViewSourceForFrame(this, url, page_state); 2208 } 2209 2210 int WebContentsImpl::GetMinimumZoomPercent() const { 2211 return minimum_zoom_percent_; 2212 } 2213 2214 int WebContentsImpl::GetMaximumZoomPercent() const { 2215 return maximum_zoom_percent_; 2216 } 2217 2218 gfx::Size WebContentsImpl::GetPreferredSize() const { 2219 return capturer_count_ == 0 ? preferred_size_ : preferred_size_for_capture_; 2220 } 2221 2222 bool WebContentsImpl::GotResponseToLockMouseRequest(bool allowed) { 2223 if (GetBrowserPluginGuest()) 2224 return GetBrowserPluginGuest()->LockMouse(allowed); 2225 2226 return GetRenderViewHost() ? 2227 GetRenderViewHostImpl()->GotResponseToLockMouseRequest(allowed) : false; 2228 } 2229 2230 bool WebContentsImpl::HasOpener() const { 2231 return opener_ != NULL; 2232 } 2233 2234 void WebContentsImpl::DidChooseColorInColorChooser(SkColor color) { 2235 if (!color_chooser_info_.get()) 2236 return; 2237 RenderFrameHost* rfh = RenderFrameHost::FromID( 2238 color_chooser_info_->render_process_id, 2239 color_chooser_info_->render_frame_id); 2240 if (!rfh) 2241 return; 2242 2243 rfh->Send(new FrameMsg_DidChooseColorResponse( 2244 rfh->GetRoutingID(), color_chooser_info_->identifier, color)); 2245 } 2246 2247 void WebContentsImpl::DidEndColorChooser() { 2248 if (!color_chooser_info_.get()) 2249 return; 2250 RenderFrameHost* rfh = RenderFrameHost::FromID( 2251 color_chooser_info_->render_process_id, 2252 color_chooser_info_->render_frame_id); 2253 if (!rfh) 2254 return; 2255 2256 rfh->Send(new FrameMsg_DidEndColorChooser( 2257 rfh->GetRoutingID(), color_chooser_info_->identifier)); 2258 color_chooser_info_.reset(); 2259 } 2260 2261 int WebContentsImpl::DownloadImage(const GURL& url, 2262 bool is_favicon, 2263 uint32_t max_bitmap_size, 2264 const ImageDownloadCallback& callback) { 2265 int id = StartDownload(GetMainFrame(), url, is_favicon, max_bitmap_size); 2266 image_download_map_[id] = callback; 2267 return id; 2268 } 2269 2270 bool WebContentsImpl::IsSubframe() const { 2271 return is_subframe_; 2272 } 2273 2274 void WebContentsImpl::Find(int request_id, 2275 const base::string16& search_text, 2276 const blink::WebFindOptions& options) { 2277 Send(new ViewMsg_Find(GetRoutingID(), request_id, search_text, options)); 2278 } 2279 2280 void WebContentsImpl::StopFinding(StopFindAction action) { 2281 Send(new ViewMsg_StopFinding(GetRoutingID(), action)); 2282 } 2283 2284 void WebContentsImpl::InsertCSS(const std::string& css) { 2285 GetMainFrame()->Send(new FrameMsg_CSSInsertRequest( 2286 GetMainFrame()->GetRoutingID(), css)); 2287 } 2288 2289 bool WebContentsImpl::FocusLocationBarByDefault() { 2290 NavigationEntry* entry = controller_.GetVisibleEntry(); 2291 if (entry && entry->GetURL() == GURL(url::kAboutBlankURL)) 2292 return true; 2293 return delegate_ && delegate_->ShouldFocusLocationBarByDefault(this); 2294 } 2295 2296 void WebContentsImpl::SetFocusToLocationBar(bool select_all) { 2297 if (delegate_) 2298 delegate_->SetFocusToLocationBar(select_all); 2299 } 2300 2301 void WebContentsImpl::DidStartProvisionalLoad( 2302 RenderFrameHostImpl* render_frame_host, 2303 int parent_routing_id, 2304 const GURL& validated_url, 2305 bool is_error_page, 2306 bool is_iframe_srcdoc) { 2307 bool is_main_frame = render_frame_host->frame_tree_node()->IsMainFrame(); 2308 2309 // Notify observers about the start of the provisional load. 2310 int render_frame_id = render_frame_host->GetRoutingID(); 2311 RenderViewHost* render_view_host = render_frame_host->render_view_host(); 2312 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2313 DidStartProvisionalLoadForFrame( 2314 render_frame_id, parent_routing_id, is_main_frame, 2315 validated_url, is_error_page, is_iframe_srcdoc, 2316 render_view_host)); 2317 2318 if (is_main_frame) { 2319 FOR_EACH_OBSERVER( 2320 WebContentsObserver, 2321 observers_, 2322 ProvisionalChangeToMainFrameUrl(validated_url, 2323 render_frame_host)); 2324 } 2325 } 2326 2327 void WebContentsImpl::DidFailProvisionalLoadWithError( 2328 RenderFrameHostImpl* render_frame_host, 2329 const FrameHostMsg_DidFailProvisionalLoadWithError_Params& params) { 2330 GURL validated_url(params.url); 2331 int render_frame_id = render_frame_host->GetRoutingID(); 2332 bool is_main_frame = render_frame_host->frame_tree_node()->IsMainFrame(); 2333 RenderViewHost* render_view_host = render_frame_host->render_view_host(); 2334 FOR_EACH_OBSERVER( 2335 WebContentsObserver, 2336 observers_, 2337 DidFailProvisionalLoad(render_frame_id, 2338 params.frame_unique_name, 2339 is_main_frame, 2340 validated_url, 2341 params.error_code, 2342 params.error_description, 2343 render_view_host)); 2344 } 2345 2346 void WebContentsImpl::DidFailLoadWithError( 2347 RenderFrameHostImpl* render_frame_host, 2348 const GURL& url, 2349 int error_code, 2350 const base::string16& error_description) { 2351 int render_frame_id = render_frame_host->GetRoutingID(); 2352 bool is_main_frame = render_frame_host->frame_tree_node()->IsMainFrame(); 2353 RenderViewHost* render_view_host = render_frame_host->render_view_host(); 2354 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2355 DidFailLoad(render_frame_id, url, is_main_frame, error_code, 2356 error_description, render_view_host)); 2357 } 2358 2359 void WebContentsImpl::NotifyChangedNavigationState( 2360 InvalidateTypes changed_flags) { 2361 NotifyNavigationStateChanged(changed_flags); 2362 } 2363 2364 void WebContentsImpl::AboutToNavigateRenderFrame( 2365 RenderFrameHostImpl* render_frame_host) { 2366 // Notify observers that we will navigate in this RenderView. 2367 RenderViewHost* render_view_host = render_frame_host->render_view_host(); 2368 FOR_EACH_OBSERVER( 2369 WebContentsObserver, 2370 observers_, 2371 AboutToNavigateRenderView(render_view_host)); 2372 } 2373 2374 void WebContentsImpl::DidStartNavigationToPendingEntry( 2375 RenderFrameHostImpl* render_frame_host, 2376 const GURL& url, 2377 NavigationController::ReloadType reload_type) { 2378 // Notify observers about navigation. 2379 FOR_EACH_OBSERVER( 2380 WebContentsObserver, 2381 observers_, 2382 DidStartNavigationToPendingEntry(url, reload_type)); 2383 } 2384 2385 void WebContentsImpl::RequestOpenURL(RenderFrameHostImpl* render_frame_host, 2386 const OpenURLParams& params) { 2387 int source_render_frame_id = render_frame_host->GetRoutingID(); 2388 WebContents* new_contents = OpenURL(params); 2389 2390 if (new_contents) { 2391 // Notify observers. 2392 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2393 DidOpenRequestedURL(new_contents, 2394 params.url, 2395 params.referrer, 2396 params.disposition, 2397 params.transition, 2398 source_render_frame_id)); 2399 } 2400 } 2401 2402 bool WebContentsImpl::ShouldPreserveAbortedURLs() { 2403 if (!delegate_) 2404 return false; 2405 return delegate_->ShouldPreserveAbortedURLs(this); 2406 } 2407 2408 void WebContentsImpl::DidRedirectProvisionalLoad( 2409 RenderFrameHostImpl* render_frame_host, 2410 const GURL& validated_target_url) { 2411 // Notify observers about the provisional change in the main frame URL. 2412 FOR_EACH_OBSERVER( 2413 WebContentsObserver, 2414 observers_, 2415 ProvisionalChangeToMainFrameUrl(validated_target_url, 2416 render_frame_host)); 2417 } 2418 2419 void WebContentsImpl::DidCommitProvisionalLoad( 2420 RenderFrameHostImpl* render_frame_host, 2421 const base::string16& frame_unique_name, 2422 bool is_main_frame, 2423 const GURL& url, 2424 PageTransition transition_type) { 2425 int render_frame_id = render_frame_host->GetRoutingID(); 2426 RenderViewHost* render_view_host = render_frame_host->render_view_host(); 2427 // Notify observers about the commit of the provisional load. 2428 FOR_EACH_OBSERVER( 2429 WebContentsObserver, 2430 observers_, 2431 DidCommitProvisionalLoadForFrame(render_frame_id, 2432 frame_unique_name, 2433 is_main_frame, 2434 url, 2435 transition_type, 2436 render_view_host)); 2437 } 2438 2439 void WebContentsImpl::DidNavigateMainFramePreCommit( 2440 const FrameHostMsg_DidCommitProvisionalLoad_Params& params) { 2441 // Ensure fullscreen mode is exited before committing the navigation to a 2442 // different page. The next page will not start out assuming it is in 2443 // fullscreen mode. 2444 if (controller_.IsURLInPageNavigation(params.url, 2445 params.was_within_same_page, 2446 NAVIGATION_TYPE_UNKNOWN)) { 2447 // No page change? Then, the renderer and browser can remain in fullscreen. 2448 return; 2449 } 2450 if (IsFullscreenForCurrentTab()) 2451 GetRenderViewHost()->ExitFullscreen(); 2452 DCHECK(!IsFullscreenForCurrentTab()); 2453 } 2454 2455 void WebContentsImpl::DidNavigateMainFramePostCommit( 2456 const LoadCommittedDetails& details, 2457 const FrameHostMsg_DidCommitProvisionalLoad_Params& params) { 2458 if (details.is_navigation_to_different_page()) { 2459 // Clear the status bubble. This is a workaround for a bug where WebKit 2460 // doesn't let us know that the cursor left an element during a 2461 // transition (this is also why the mouse cursor remains as a hand after 2462 // clicking on a link); see bugs 1184641 and 980803. We don't want to 2463 // clear the bubble when a user navigates to a named anchor in the same 2464 // page. 2465 UpdateTargetURL(details.entry->GetPageID(), GURL()); 2466 } 2467 2468 if (!details.is_in_page) { 2469 // Once the main frame is navigated, we're no longer considered to have 2470 // displayed insecure content. 2471 displayed_insecure_content_ = false; 2472 SSLManager::NotifySSLInternalStateChanged( 2473 GetController().GetBrowserContext()); 2474 } 2475 2476 // Notify observers about navigation. 2477 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2478 DidNavigateMainFrame(details, params)); 2479 2480 if (delegate_) 2481 delegate_->DidNavigateMainFramePostCommit(this); 2482 view_->SetOverscrollControllerEnabled(CanOverscrollContent()); 2483 } 2484 2485 void WebContentsImpl::DidNavigateAnyFramePostCommit( 2486 RenderFrameHostImpl* render_frame_host, 2487 const LoadCommittedDetails& details, 2488 const FrameHostMsg_DidCommitProvisionalLoad_Params& params) { 2489 // Now that something has committed, we don't need to track whether the 2490 // initial page has been accessed. 2491 has_accessed_initial_document_ = false; 2492 2493 // If we navigate off the page, close all JavaScript dialogs. 2494 if (dialog_manager_ && !details.is_in_page) 2495 dialog_manager_->CancelActiveAndPendingDialogs(this); 2496 2497 // Notify observers about navigation. 2498 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2499 DidNavigateAnyFrame(details, params)); 2500 } 2501 2502 void WebContentsImpl::SetMainFrameMimeType(const std::string& mime_type) { 2503 contents_mime_type_ = mime_type; 2504 } 2505 2506 bool WebContentsImpl::CanOverscrollContent() const { 2507 // Disable overscroll when touch emulation is on. See crbug.com/369938. 2508 if (touch_emulation_enabled_) 2509 return false; 2510 2511 if (delegate_) 2512 return delegate_->CanOverscrollContent(); 2513 2514 return false; 2515 } 2516 2517 void WebContentsImpl::OnThemeColorChanged(SkColor theme_color) { 2518 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2519 DidChangeThemeColor(theme_color)); 2520 } 2521 2522 void WebContentsImpl::OnDidLoadResourceFromMemoryCache( 2523 const GURL& url, 2524 const std::string& security_info, 2525 const std::string& http_method, 2526 const std::string& mime_type, 2527 ResourceType::Type resource_type) { 2528 base::StatsCounter cache("WebKit.CacheHit"); 2529 cache.Increment(); 2530 2531 // Send out a notification that we loaded a resource from our memory cache. 2532 int cert_id = 0; 2533 net::CertStatus cert_status = 0; 2534 int security_bits = -1; 2535 int connection_status = 0; 2536 SignedCertificateTimestampIDStatusList signed_certificate_timestamp_ids; 2537 DeserializeSecurityInfo(security_info, &cert_id, &cert_status, 2538 &security_bits, &connection_status, 2539 &signed_certificate_timestamp_ids); 2540 // TODO(alcutter,eranm): Pass signed_certificate_timestamp_ids into details 2541 LoadFromMemoryCacheDetails details( 2542 url, GetRenderProcessHost()->GetID(), cert_id, cert_status, http_method, 2543 mime_type, resource_type); 2544 2545 controller_.ssl_manager()->DidLoadFromMemoryCache(details); 2546 2547 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2548 DidLoadResourceFromMemoryCache(details)); 2549 2550 if (url.is_valid() && url.SchemeIsHTTPOrHTTPS()) { 2551 scoped_refptr<net::URLRequestContextGetter> request_context( 2552 resource_type == ResourceType::MEDIA ? 2553 GetBrowserContext()->GetMediaRequestContextForRenderProcess( 2554 GetRenderProcessHost()->GetID()) : 2555 GetBrowserContext()->GetRequestContextForRenderProcess( 2556 GetRenderProcessHost()->GetID())); 2557 BrowserThread::PostTask( 2558 BrowserThread::IO, 2559 FROM_HERE, 2560 base::Bind(&NotifyCacheOnIO, request_context, url, http_method)); 2561 } 2562 } 2563 2564 void WebContentsImpl::OnDidDisplayInsecureContent() { 2565 RecordAction(base::UserMetricsAction("SSL.DisplayedInsecureContent")); 2566 displayed_insecure_content_ = true; 2567 SSLManager::NotifySSLInternalStateChanged( 2568 GetController().GetBrowserContext()); 2569 } 2570 2571 void WebContentsImpl::OnDidRunInsecureContent( 2572 const std::string& security_origin, const GURL& target_url) { 2573 LOG(WARNING) << security_origin << " ran insecure content from " 2574 << target_url.possibly_invalid_spec(); 2575 RecordAction(base::UserMetricsAction("SSL.RanInsecureContent")); 2576 if (EndsWith(security_origin, kDotGoogleDotCom, false)) 2577 RecordAction(base::UserMetricsAction("SSL.RanInsecureContentGoogle")); 2578 controller_.ssl_manager()->DidRunInsecureContent(security_origin); 2579 displayed_insecure_content_ = true; 2580 SSLManager::NotifySSLInternalStateChanged( 2581 GetController().GetBrowserContext()); 2582 } 2583 2584 void WebContentsImpl::OnDocumentLoadedInFrame() { 2585 CHECK(render_frame_message_source_); 2586 CHECK(!render_view_message_source_); 2587 RenderFrameHostImpl* rfh = 2588 static_cast<RenderFrameHostImpl*>(render_frame_message_source_); 2589 2590 int render_frame_id = rfh->GetRoutingID(); 2591 RenderViewHost* render_view_host = rfh->render_view_host(); 2592 FOR_EACH_OBSERVER(WebContentsObserver, 2593 observers_, 2594 DocumentLoadedInFrame(render_frame_id, render_view_host)); 2595 } 2596 2597 void WebContentsImpl::OnDidFinishLoad( 2598 const GURL& url) { 2599 if (!render_frame_message_source_) { 2600 RecordAction(base::UserMetricsAction("BadMessageTerminate_RVD2")); 2601 GetRenderProcessHost()->ReceivedBadMessage(); 2602 return; 2603 } 2604 2605 GURL validated_url(url); 2606 RenderProcessHost* render_process_host = 2607 render_frame_message_source_->GetProcess(); 2608 render_process_host->FilterURL(false, &validated_url); 2609 2610 RenderFrameHostImpl* rfh = 2611 static_cast<RenderFrameHostImpl*>(render_frame_message_source_); 2612 int render_frame_id = rfh->GetRoutingID(); 2613 RenderViewHost* render_view_host = rfh->render_view_host(); 2614 bool is_main_frame = rfh->frame_tree_node()->IsMainFrame(); 2615 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2616 DidFinishLoad(render_frame_id, validated_url, 2617 is_main_frame, render_view_host)); 2618 } 2619 2620 void WebContentsImpl::OnDidStartLoading(bool to_different_document) { 2621 RenderFrameHostImpl* rfh = 2622 static_cast<RenderFrameHostImpl*>(render_frame_message_source_); 2623 int64 render_frame_id = rfh->frame_tree_node()->frame_tree_node_id(); 2624 2625 // It is possible to get multiple calls to OnDidStartLoading that don't have 2626 // corresponding calls to OnDidStopLoading: 2627 // - With "swappedout://" URLs, this happens when a RenderView gets swapped 2628 // out for a cross-process navigation, and it turns into a placeholder for 2629 // one being rendered in a different process. 2630 // - Also, there might be more than one RenderFrameHost sharing the same 2631 // FrameTreeNode (and thus sharing its ID) each sending a start. 2632 // - But in the future, once clamy@ moves navigation network requests to the 2633 // browser process, there's a good chance that callbacks about starting and 2634 // stopping will all be handled by the browser. When that happens, there 2635 // should no longer be a start/stop call imbalance. TODO(avi): When this 2636 // future arrives, update this code to not allow this case. 2637 DCHECK_GE(loading_frames_in_progress_, 0); 2638 if (loading_progresses_.find(render_frame_id) == loading_progresses_.end()) { 2639 if (loading_frames_in_progress_ == 0) 2640 DidStartLoading(rfh, to_different_document); 2641 ++loading_frames_in_progress_; 2642 } 2643 2644 loading_progresses_[render_frame_id] = kMinimumLoadingProgress; 2645 SendLoadProgressChanged(); 2646 } 2647 2648 void WebContentsImpl::OnDidStopLoading() { 2649 RenderFrameHostImpl* rfh = 2650 static_cast<RenderFrameHostImpl*>(render_frame_message_source_); 2651 int64 render_frame_id = rfh->frame_tree_node()->frame_tree_node_id(); 2652 2653 if (loading_progresses_.find(render_frame_id) != loading_progresses_.end()) { 2654 // Load stopped while we were still tracking load. Make sure we update 2655 // progress based on this frame's completion. 2656 loading_progresses_[render_frame_id] = 1.0; 2657 SendLoadProgressChanged(); 2658 // Then we clean-up our states. 2659 if (loading_total_progress_ == 1.0) 2660 ResetLoadProgressState(); 2661 } 2662 2663 // TODO(japhet): This should be a DCHECK, but the pdf plugin sometimes 2664 // calls DidStopLoading() without a matching DidStartLoading(). 2665 if (loading_frames_in_progress_ == 0) 2666 return; 2667 --loading_frames_in_progress_; 2668 if (loading_frames_in_progress_ == 0) 2669 DidStopLoading(rfh); 2670 } 2671 2672 void WebContentsImpl::OnDidChangeLoadProgress(double load_progress) { 2673 RenderFrameHostImpl* rfh = 2674 static_cast<RenderFrameHostImpl*>(render_frame_message_source_); 2675 int64 render_frame_id = rfh->frame_tree_node()->frame_tree_node_id(); 2676 2677 loading_progresses_[render_frame_id] = load_progress; 2678 2679 // We notify progress change immediately for the first and last updates. 2680 // Also, since the message loop may be pretty busy when a page is loaded, it 2681 // might not execute a posted task in a timely manner so we make sure to 2682 // immediately send progress report if enough time has passed. 2683 base::TimeDelta min_delay = 2684 base::TimeDelta::FromMilliseconds(kMinimumDelayBetweenLoadingUpdatesMS); 2685 if (load_progress == 1.0 || loading_last_progress_update_.is_null() || 2686 base::TimeTicks::Now() - loading_last_progress_update_ > min_delay) { 2687 // If there is a pending task to send progress, it is now obsolete. 2688 loading_weak_factory_.InvalidateWeakPtrs(); 2689 SendLoadProgressChanged(); 2690 if (loading_total_progress_ == 1.0) 2691 ResetLoadProgressState(); 2692 return; 2693 } 2694 2695 if (loading_weak_factory_.HasWeakPtrs()) 2696 return; 2697 2698 base::MessageLoop::current()->PostDelayedTask( 2699 FROM_HERE, 2700 base::Bind(&WebContentsImpl::SendLoadProgressChanged, 2701 loading_weak_factory_.GetWeakPtr()), 2702 min_delay); 2703 } 2704 2705 void WebContentsImpl::OnGoToEntryAtOffset(int offset) { 2706 if (!delegate_ || delegate_->OnGoToEntryOffset(offset)) 2707 controller_.GoToOffset(offset); 2708 } 2709 2710 void WebContentsImpl::OnUpdateZoomLimits(int minimum_percent, 2711 int maximum_percent) { 2712 minimum_zoom_percent_ = minimum_percent; 2713 maximum_zoom_percent_ = maximum_percent; 2714 } 2715 2716 void WebContentsImpl::OnEnumerateDirectory(int request_id, 2717 const base::FilePath& path) { 2718 if (!delegate_) 2719 return; 2720 2721 ChildProcessSecurityPolicyImpl* policy = 2722 ChildProcessSecurityPolicyImpl::GetInstance(); 2723 if (policy->CanReadFile(GetRenderProcessHost()->GetID(), path)) 2724 delegate_->EnumerateDirectory(this, request_id, path); 2725 } 2726 2727 void WebContentsImpl::OnRegisterProtocolHandler(const std::string& protocol, 2728 const GURL& url, 2729 const base::string16& title, 2730 bool user_gesture) { 2731 if (!delegate_) 2732 return; 2733 2734 ChildProcessSecurityPolicyImpl* policy = 2735 ChildProcessSecurityPolicyImpl::GetInstance(); 2736 if (policy->IsPseudoScheme(protocol)) 2737 return; 2738 2739 delegate_->RegisterProtocolHandler(this, protocol, url, user_gesture); 2740 } 2741 2742 void WebContentsImpl::OnFindReply(int request_id, 2743 int number_of_matches, 2744 const gfx::Rect& selection_rect, 2745 int active_match_ordinal, 2746 bool final_update) { 2747 if (delegate_) { 2748 delegate_->FindReply(this, request_id, number_of_matches, selection_rect, 2749 active_match_ordinal, final_update); 2750 } 2751 } 2752 2753 #if defined(OS_ANDROID) 2754 void WebContentsImpl::OnFindMatchRectsReply( 2755 int version, 2756 const std::vector<gfx::RectF>& rects, 2757 const gfx::RectF& active_rect) { 2758 if (delegate_) 2759 delegate_->FindMatchRectsReply(this, version, rects, active_rect); 2760 } 2761 2762 void WebContentsImpl::OnOpenDateTimeDialog( 2763 const ViewHostMsg_DateTimeDialogValue_Params& value) { 2764 date_time_chooser_->ShowDialog(ContentViewCore::FromWebContents(this), 2765 GetRenderViewHost(), 2766 value.dialog_type, 2767 value.dialog_value, 2768 value.minimum, 2769 value.maximum, 2770 value.step, 2771 value.suggestions); 2772 } 2773 2774 #endif 2775 2776 void WebContentsImpl::OnPepperPluginHung(int plugin_child_id, 2777 const base::FilePath& path, 2778 bool is_hung) { 2779 UMA_HISTOGRAM_COUNTS("Pepper.PluginHung", 1); 2780 2781 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2782 PluginHungStatusChanged(plugin_child_id, path, is_hung)); 2783 } 2784 2785 void WebContentsImpl::OnPluginCrashed(const base::FilePath& plugin_path, 2786 base::ProcessId plugin_pid) { 2787 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2788 PluginCrashed(plugin_path, plugin_pid)); 2789 } 2790 2791 void WebContentsImpl::OnDomOperationResponse(const std::string& json_string, 2792 int automation_id) { 2793 DomOperationNotificationDetails details(json_string, automation_id); 2794 NotificationService::current()->Notify( 2795 NOTIFICATION_DOM_OPERATION_RESPONSE, 2796 Source<WebContents>(this), 2797 Details<DomOperationNotificationDetails>(&details)); 2798 } 2799 2800 void WebContentsImpl::OnAppCacheAccessed(const GURL& manifest_url, 2801 bool blocked_by_policy) { 2802 // Notify observers about navigation. 2803 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2804 AppCacheAccessed(manifest_url, blocked_by_policy)); 2805 } 2806 2807 void WebContentsImpl::OnOpenColorChooser( 2808 int color_chooser_id, 2809 SkColor color, 2810 const std::vector<ColorSuggestion>& suggestions) { 2811 ColorChooser* new_color_chooser = delegate_ ? 2812 delegate_->OpenColorChooser(this, color, suggestions) : 2813 NULL; 2814 if (!new_color_chooser) 2815 return; 2816 if (color_chooser_info_.get()) 2817 color_chooser_info_->chooser->End(); 2818 2819 color_chooser_info_.reset(new ColorChooserInfo( 2820 render_frame_message_source_->GetProcess()->GetID(), 2821 render_frame_message_source_->GetRoutingID(), 2822 new_color_chooser, 2823 color_chooser_id)); 2824 } 2825 2826 void WebContentsImpl::OnEndColorChooser(int color_chooser_id) { 2827 if (color_chooser_info_ && 2828 color_chooser_id == color_chooser_info_->identifier) 2829 color_chooser_info_->chooser->End(); 2830 } 2831 2832 void WebContentsImpl::OnSetSelectedColorInColorChooser(int color_chooser_id, 2833 SkColor color) { 2834 if (color_chooser_info_ && 2835 color_chooser_id == color_chooser_info_->identifier) 2836 color_chooser_info_->chooser->SetSelectedColor(color); 2837 } 2838 2839 // This exists for render views that don't have a WebUI, but do have WebUI 2840 // bindings enabled. 2841 void WebContentsImpl::OnWebUISend(const GURL& source_url, 2842 const std::string& name, 2843 const base::ListValue& args) { 2844 if (delegate_) 2845 delegate_->WebUISend(this, source_url, name, args); 2846 } 2847 2848 void WebContentsImpl::OnRequestPpapiBrokerPermission( 2849 int routing_id, 2850 const GURL& url, 2851 const base::FilePath& plugin_path) { 2852 if (!delegate_) { 2853 OnPpapiBrokerPermissionResult(routing_id, false); 2854 return; 2855 } 2856 2857 if (!delegate_->RequestPpapiBrokerPermission( 2858 this, url, plugin_path, 2859 base::Bind(&WebContentsImpl::OnPpapiBrokerPermissionResult, 2860 base::Unretained(this), routing_id))) { 2861 NOTIMPLEMENTED(); 2862 OnPpapiBrokerPermissionResult(routing_id, false); 2863 } 2864 } 2865 2866 void WebContentsImpl::OnPpapiBrokerPermissionResult(int routing_id, 2867 bool result) { 2868 Send(new ViewMsg_PpapiBrokerPermissionResult(routing_id, result)); 2869 } 2870 2871 void WebContentsImpl::OnBrowserPluginMessage(const IPC::Message& message) { 2872 // This creates a BrowserPluginEmbedder, which handles all the BrowserPlugin 2873 // specific messages for this WebContents. This means that any message from 2874 // a BrowserPlugin prior to this will be ignored. 2875 // For more info, see comment above classes BrowserPluginEmbedder and 2876 // BrowserPluginGuest. 2877 CHECK(!browser_plugin_embedder_.get()); 2878 browser_plugin_embedder_.reset(BrowserPluginEmbedder::Create(this)); 2879 browser_plugin_embedder_->OnMessageReceived(message); 2880 } 2881 2882 void WebContentsImpl::OnDidDownloadImage( 2883 int id, 2884 int http_status_code, 2885 const GURL& image_url, 2886 const std::vector<SkBitmap>& bitmaps, 2887 const std::vector<gfx::Size>& original_bitmap_sizes) { 2888 if (bitmaps.size() != original_bitmap_sizes.size()) 2889 return; 2890 2891 ImageDownloadMap::iterator iter = image_download_map_.find(id); 2892 if (iter == image_download_map_.end()) { 2893 // Currently WebContents notifies us of ANY downloads so that it is 2894 // possible to get here. 2895 return; 2896 } 2897 if (!iter->second.is_null()) { 2898 iter->second.Run( 2899 id, http_status_code, image_url, bitmaps, original_bitmap_sizes); 2900 } 2901 image_download_map_.erase(id); 2902 } 2903 2904 void WebContentsImpl::OnUpdateFaviconURL( 2905 const std::vector<FaviconURL>& candidates) { 2906 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2907 DidUpdateFaviconURL(candidates)); 2908 } 2909 2910 void WebContentsImpl::OnMediaPlayingNotification(int64 player_cookie, 2911 bool has_video, 2912 bool has_audio) { 2913 // Chrome OS does its own detection of audio and video. 2914 #if !defined(OS_CHROMEOS) 2915 scoped_ptr<PowerSaveBlocker> blocker; 2916 if (has_video) { 2917 blocker = PowerSaveBlocker::Create( 2918 PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep, "Playing video"); 2919 #if defined(OS_ANDROID) 2920 static_cast<PowerSaveBlockerImpl*>(blocker.get()) 2921 ->InitDisplaySleepBlocker(GetView()->GetNativeView()); 2922 #endif 2923 } else if (has_audio) { 2924 blocker = PowerSaveBlocker::Create( 2925 PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, "Playing audio"); 2926 } 2927 2928 if (blocker) { 2929 power_save_blockers_[render_frame_message_source_][player_cookie] = 2930 blocker.release(); 2931 } 2932 #endif // !defined(OS_CHROMEOS) 2933 } 2934 2935 void WebContentsImpl::OnMediaPausedNotification(int64 player_cookie) { 2936 // Chrome OS does its own detection of audio and video. 2937 #if !defined(OS_CHROMEOS) 2938 delete power_save_blockers_[render_frame_message_source_][player_cookie]; 2939 power_save_blockers_[render_frame_message_source_].erase(player_cookie); 2940 #endif // !defined(OS_CHROMEOS) 2941 } 2942 2943 void WebContentsImpl::OnFirstVisuallyNonEmptyPaint() { 2944 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2945 DidFirstVisuallyNonEmptyPaint()); 2946 } 2947 2948 void WebContentsImpl::DidChangeVisibleSSLState() { 2949 if (delegate_) 2950 delegate_->VisibleSSLStateChanged(this); 2951 } 2952 2953 void WebContentsImpl::NotifyBeforeFormRepostWarningShow() { 2954 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 2955 BeforeFormRepostWarningShow()); 2956 } 2957 2958 2959 void WebContentsImpl::ActivateAndShowRepostFormWarningDialog() { 2960 Activate(); 2961 if (delegate_) 2962 delegate_->ShowRepostFormWarningDialog(this); 2963 } 2964 2965 bool WebContentsImpl::HasAccessedInitialDocument() { 2966 return has_accessed_initial_document_; 2967 } 2968 2969 // Notifies the RenderWidgetHost instance about the fact that the page is 2970 // loading, or done loading. 2971 void WebContentsImpl::SetIsLoading(RenderViewHost* render_view_host, 2972 bool is_loading, 2973 bool to_different_document, 2974 LoadNotificationDetails* details) { 2975 if (is_loading == is_loading_) 2976 return; 2977 2978 if (!is_loading) { 2979 load_state_ = net::LoadStateWithParam(net::LOAD_STATE_IDLE, 2980 base::string16()); 2981 load_state_host_.clear(); 2982 upload_size_ = 0; 2983 upload_position_ = 0; 2984 } 2985 2986 GetRenderManager()->SetIsLoading(is_loading); 2987 2988 is_loading_ = is_loading; 2989 waiting_for_response_ = is_loading; 2990 is_load_to_different_document_ = to_different_document; 2991 2992 if (delegate_) 2993 delegate_->LoadingStateChanged(this, to_different_document); 2994 NotifyNavigationStateChanged(INVALIDATE_TYPE_LOAD); 2995 2996 std::string url = (details ? details->url.possibly_invalid_spec() : "NULL"); 2997 if (is_loading) { 2998 TRACE_EVENT_ASYNC_BEGIN1("browser", "WebContentsImpl Loading", this, 2999 "URL", url); 3000 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 3001 DidStartLoading(render_view_host)); 3002 } else { 3003 TRACE_EVENT_ASYNC_END1("browser", "WebContentsImpl Loading", this, 3004 "URL", url); 3005 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 3006 DidStopLoading(render_view_host)); 3007 } 3008 3009 // TODO(avi): Remove. http://crbug.com/170921 3010 int type = is_loading ? NOTIFICATION_LOAD_START : NOTIFICATION_LOAD_STOP; 3011 NotificationDetails det = NotificationService::NoDetails(); 3012 if (details) 3013 det = Details<LoadNotificationDetails>(details); 3014 NotificationService::current()->Notify( 3015 type, Source<NavigationController>(&controller_), det); 3016 } 3017 3018 void WebContentsImpl::SelectRange(const gfx::Point& start, 3019 const gfx::Point& end) { 3020 RenderFrameHost* focused_frame = GetFocusedFrame(); 3021 if (!focused_frame) 3022 return; 3023 3024 focused_frame->Send( 3025 new InputMsg_SelectRange(focused_frame->GetRoutingID(), start, end)); 3026 } 3027 3028 void WebContentsImpl::UpdateMaxPageIDIfNecessary(RenderViewHost* rvh) { 3029 // If we are creating a RVH for a restored controller, then we need to make 3030 // sure the RenderView starts with a next_page_id_ larger than the number 3031 // of restored entries. This must be called before the RenderView starts 3032 // navigating (to avoid a race between the browser updating max_page_id and 3033 // the renderer updating next_page_id_). Because of this, we only call this 3034 // from CreateRenderView and allow that to notify the RenderView for us. 3035 int max_restored_page_id = controller_.GetMaxRestoredPageID(); 3036 if (max_restored_page_id > 3037 GetMaxPageIDForSiteInstance(rvh->GetSiteInstance())) 3038 UpdateMaxPageIDForSiteInstance(rvh->GetSiteInstance(), 3039 max_restored_page_id); 3040 } 3041 3042 bool WebContentsImpl::UpdateTitleForEntry(NavigationEntryImpl* entry, 3043 const base::string16& title) { 3044 // For file URLs without a title, use the pathname instead. In the case of a 3045 // synthesized title, we don't want the update to count toward the "one set 3046 // per page of the title to history." 3047 base::string16 final_title; 3048 bool explicit_set; 3049 if (entry && entry->GetURL().SchemeIsFile() && title.empty()) { 3050 final_title = base::UTF8ToUTF16(entry->GetURL().ExtractFileName()); 3051 explicit_set = false; // Don't count synthetic titles toward the set limit. 3052 } else { 3053 base::TrimWhitespace(title, base::TRIM_ALL, &final_title); 3054 explicit_set = true; 3055 } 3056 3057 // If a page is created via window.open and never navigated, 3058 // there will be no navigation entry. In this situation, 3059 // |page_title_when_no_navigation_entry_| will be used for page title. 3060 if (entry) { 3061 if (final_title == entry->GetTitle()) 3062 return false; // Nothing changed, don't bother. 3063 3064 entry->SetTitle(final_title); 3065 } else { 3066 if (page_title_when_no_navigation_entry_ == final_title) 3067 return false; // Nothing changed, don't bother. 3068 3069 page_title_when_no_navigation_entry_ = final_title; 3070 } 3071 3072 // Lastly, set the title for the view. 3073 view_->SetPageTitle(final_title); 3074 3075 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 3076 TitleWasSet(entry, explicit_set)); 3077 3078 // TODO(avi): Remove. http://crbug.com/170921 3079 std::pair<NavigationEntry*, bool> details = 3080 std::make_pair(entry, explicit_set); 3081 NotificationService::current()->Notify( 3082 NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED, 3083 Source<WebContents>(this), 3084 Details<std::pair<NavigationEntry*, bool> >(&details)); 3085 3086 return true; 3087 } 3088 3089 void WebContentsImpl::SendLoadProgressChanged() { 3090 loading_last_progress_update_ = base::TimeTicks::Now(); 3091 double progress = 0.0; 3092 int frame_count = 0; 3093 3094 for (LoadingProgressMap::iterator it = loading_progresses_.begin(); 3095 it != loading_progresses_.end(); 3096 ++it) { 3097 progress += it->second; 3098 ++frame_count; 3099 } 3100 if (frame_count == 0) 3101 return; 3102 progress /= frame_count; 3103 DCHECK(progress <= 1.0); 3104 3105 if (progress <= loading_total_progress_) 3106 return; 3107 loading_total_progress_ = progress; 3108 3109 if (delegate_) 3110 delegate_->LoadProgressChanged(this, progress); 3111 } 3112 3113 void WebContentsImpl::ResetLoadProgressState() { 3114 loading_progresses_.clear(); 3115 loading_total_progress_ = 0.0; 3116 loading_weak_factory_.InvalidateWeakPtrs(); 3117 loading_last_progress_update_ = base::TimeTicks(); 3118 } 3119 3120 void WebContentsImpl::NotifySwapped(RenderViewHost* old_host, 3121 RenderViewHost* new_host) { 3122 // After sending out a swap notification, we need to send a disconnect 3123 // notification so that clients that pick up a pointer to |this| can NULL the 3124 // pointer. See Bug 1230284. 3125 notify_disconnection_ = true; 3126 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 3127 RenderViewHostChanged(old_host, new_host)); 3128 3129 // TODO(avi): Remove. http://crbug.com/170921 3130 std::pair<RenderViewHost*, RenderViewHost*> details = 3131 std::make_pair(old_host, new_host); 3132 NotificationService::current()->Notify( 3133 NOTIFICATION_RENDER_VIEW_HOST_CHANGED, 3134 Source<WebContents>(this), 3135 Details<std::pair<RenderViewHost*, RenderViewHost*> >(&details)); 3136 3137 // Ensure that the associated embedder gets cleared after a RenderViewHost 3138 // gets swapped, so we don't reuse the same embedder next time a 3139 // RenderViewHost is attached to this WebContents. 3140 RemoveBrowserPluginEmbedder(); 3141 } 3142 3143 // TODO(avi): Remove this entire function because this notification is already 3144 // covered by two observer functions. http://crbug.com/170921 3145 void WebContentsImpl::NotifyDisconnected() { 3146 if (!notify_disconnection_) 3147 return; 3148 3149 notify_disconnection_ = false; 3150 NotificationService::current()->Notify( 3151 NOTIFICATION_WEB_CONTENTS_DISCONNECTED, 3152 Source<WebContents>(this), 3153 NotificationService::NoDetails()); 3154 } 3155 3156 void WebContentsImpl::NotifyNavigationEntryCommitted( 3157 const LoadCommittedDetails& load_details) { 3158 FOR_EACH_OBSERVER( 3159 WebContentsObserver, observers_, NavigationEntryCommitted(load_details)); 3160 } 3161 3162 bool WebContentsImpl::OnMessageReceived(RenderFrameHost* render_frame_host, 3163 const IPC::Message& message) { 3164 return OnMessageReceived(NULL, render_frame_host, message); 3165 } 3166 3167 const GURL& WebContentsImpl::GetMainFrameLastCommittedURL() const { 3168 return GetLastCommittedURL(); 3169 } 3170 3171 void WebContentsImpl::RenderFrameCreated(RenderFrameHost* render_frame_host) { 3172 // Note this is only for subframes, the notification for the main frame 3173 // happens in RenderViewCreated. 3174 FOR_EACH_OBSERVER(WebContentsObserver, 3175 observers_, 3176 RenderFrameCreated(render_frame_host)); 3177 } 3178 3179 void WebContentsImpl::RenderFrameDeleted(RenderFrameHost* render_frame_host) { 3180 ClearPowerSaveBlockers(render_frame_host); 3181 FOR_EACH_OBSERVER(WebContentsObserver, 3182 observers_, 3183 RenderFrameDeleted(render_frame_host)); 3184 } 3185 3186 void WebContentsImpl::WorkerCrashed(RenderFrameHost* render_frame_host) { 3187 if (delegate_) 3188 delegate_->WorkerCrashed(this); 3189 } 3190 3191 void WebContentsImpl::ShowContextMenu(RenderFrameHost* render_frame_host, 3192 const ContextMenuParams& params) { 3193 ContextMenuParams context_menu_params(params); 3194 // Allow WebContentsDelegates to handle the context menu operation first. 3195 if (GetBrowserPluginGuest()) { 3196 WebContentsViewGuest* view_guest = 3197 static_cast<WebContentsViewGuest*>(GetView()); 3198 context_menu_params = view_guest->ConvertContextMenuParams(params); 3199 } 3200 if (delegate_ && delegate_->HandleContextMenu(context_menu_params)) 3201 return; 3202 3203 render_view_host_delegate_view_->ShowContextMenu(render_frame_host, 3204 context_menu_params); 3205 } 3206 3207 void WebContentsImpl::RunJavaScriptMessage( 3208 RenderFrameHost* render_frame_host, 3209 const base::string16& message, 3210 const base::string16& default_prompt, 3211 const GURL& frame_url, 3212 JavaScriptMessageType javascript_message_type, 3213 IPC::Message* reply_msg) { 3214 // Suppress JavaScript dialogs when requested. Also suppress messages when 3215 // showing an interstitial as it's shown over the previous page and we don't 3216 // want the hidden page's dialogs to interfere with the interstitial. 3217 bool suppress_this_message = 3218 static_cast<RenderViewHostImpl*>(render_frame_host->GetRenderViewHost())-> 3219 IsSwappedOut() || 3220 ShowingInterstitialPage() || 3221 !delegate_ || 3222 delegate_->ShouldSuppressDialogs() || 3223 !delegate_->GetJavaScriptDialogManager(); 3224 3225 if (!suppress_this_message) { 3226 std::string accept_lang = GetContentClient()->browser()-> 3227 GetAcceptLangs(GetBrowserContext()); 3228 dialog_manager_ = delegate_->GetJavaScriptDialogManager(); 3229 dialog_manager_->RunJavaScriptDialog( 3230 this, 3231 frame_url.GetOrigin(), 3232 accept_lang, 3233 javascript_message_type, 3234 message, 3235 default_prompt, 3236 base::Bind(&WebContentsImpl::OnDialogClosed, 3237 base::Unretained(this), 3238 render_frame_host->GetProcess()->GetID(), 3239 render_frame_host->GetRoutingID(), 3240 reply_msg, 3241 false), 3242 &suppress_this_message); 3243 } 3244 3245 if (suppress_this_message) { 3246 // If we are suppressing messages, just reply as if the user immediately 3247 // pressed "Cancel", passing true to |dialog_was_suppressed|. 3248 OnDialogClosed(render_frame_host->GetProcess()->GetID(), 3249 render_frame_host->GetRoutingID(), reply_msg, 3250 true, false, base::string16()); 3251 } 3252 3253 // OnDialogClosed (two lines up) may have caused deletion of this object (see 3254 // http://crbug.com/288961 ). The only safe thing to do here is return. 3255 } 3256 3257 void WebContentsImpl::RunBeforeUnloadConfirm( 3258 RenderFrameHost* render_frame_host, 3259 const base::string16& message, 3260 bool is_reload, 3261 IPC::Message* reply_msg) { 3262 RenderFrameHostImpl* rfhi = 3263 static_cast<RenderFrameHostImpl*>(render_frame_host); 3264 RenderViewHostImpl* rvhi = 3265 static_cast<RenderViewHostImpl*>(render_frame_host->GetRenderViewHost()); 3266 if (delegate_) 3267 delegate_->WillRunBeforeUnloadConfirm(); 3268 3269 bool suppress_this_message = 3270 rvhi->rvh_state() != RenderViewHostImpl::STATE_DEFAULT || 3271 !delegate_ || 3272 delegate_->ShouldSuppressDialogs() || 3273 !delegate_->GetJavaScriptDialogManager(); 3274 if (suppress_this_message) { 3275 rfhi->JavaScriptDialogClosed(reply_msg, true, base::string16(), true); 3276 return; 3277 } 3278 3279 is_showing_before_unload_dialog_ = true; 3280 dialog_manager_ = delegate_->GetJavaScriptDialogManager(); 3281 dialog_manager_->RunBeforeUnloadDialog( 3282 this, message, is_reload, 3283 base::Bind(&WebContentsImpl::OnDialogClosed, base::Unretained(this), 3284 render_frame_host->GetProcess()->GetID(), 3285 render_frame_host->GetRoutingID(), reply_msg, 3286 false)); 3287 } 3288 3289 WebContents* WebContentsImpl::GetAsWebContents() { 3290 return this; 3291 } 3292 3293 bool WebContentsImpl::IsNeverVisible() { 3294 if (!delegate_) 3295 return false; 3296 return delegate_->IsNeverVisible(this); 3297 } 3298 3299 RenderViewHostDelegateView* WebContentsImpl::GetDelegateView() { 3300 return render_view_host_delegate_view_; 3301 } 3302 3303 RendererPreferences WebContentsImpl::GetRendererPrefs( 3304 BrowserContext* browser_context) const { 3305 return renderer_preferences_; 3306 } 3307 3308 gfx::Rect WebContentsImpl::GetRootWindowResizerRect() const { 3309 if (delegate_) 3310 return delegate_->GetRootWindowResizerRect(); 3311 return gfx::Rect(); 3312 } 3313 3314 void WebContentsImpl::RemoveBrowserPluginEmbedder() { 3315 if (browser_plugin_embedder_) 3316 browser_plugin_embedder_.reset(); 3317 } 3318 3319 void WebContentsImpl::RenderViewCreated(RenderViewHost* render_view_host) { 3320 // Don't send notifications if we are just creating a swapped-out RVH for 3321 // the opener chain. These won't be used for view-source or WebUI, so it's 3322 // ok to return early. 3323 if (static_cast<RenderViewHostImpl*>(render_view_host)->IsSwappedOut()) 3324 return; 3325 3326 if (delegate_) 3327 view_->SetOverscrollControllerEnabled(CanOverscrollContent()); 3328 3329 NotificationService::current()->Notify( 3330 NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, 3331 Source<WebContents>(this), 3332 Details<RenderViewHost>(render_view_host)); 3333 3334 // When we're creating views, we're still doing initial setup, so we always 3335 // use the pending Web UI rather than any possibly existing committed one. 3336 if (GetRenderManager()->pending_web_ui()) 3337 GetRenderManager()->pending_web_ui()->RenderViewCreated(render_view_host); 3338 3339 NavigationEntry* entry = controller_.GetPendingEntry(); 3340 if (entry && entry->IsViewSourceMode()) { 3341 // Put the renderer in view source mode. 3342 render_view_host->Send( 3343 new ViewMsg_EnableViewSourceMode(render_view_host->GetRoutingID())); 3344 } 3345 3346 view_->RenderViewCreated(render_view_host); 3347 3348 FOR_EACH_OBSERVER( 3349 WebContentsObserver, observers_, RenderViewCreated(render_view_host)); 3350 3351 // We tell the observers now instead of when the main RenderFrameHostImpl is 3352 // constructed because otherwise it would be too early (i.e. IPCs sent to the 3353 // frame would be dropped because it's not created yet). 3354 RenderFrameHost* main_frame = render_view_host->GetMainFrame(); 3355 FOR_EACH_OBSERVER( 3356 WebContentsObserver, observers_, RenderFrameCreated(main_frame)); 3357 } 3358 3359 void WebContentsImpl::RenderViewReady(RenderViewHost* rvh) { 3360 if (rvh != GetRenderViewHost()) { 3361 // Don't notify the world, since this came from a renderer in the 3362 // background. 3363 return; 3364 } 3365 3366 notify_disconnection_ = true; 3367 // TODO(avi): Remove. http://crbug.com/170921 3368 NotificationService::current()->Notify( 3369 NOTIFICATION_WEB_CONTENTS_CONNECTED, 3370 Source<WebContents>(this), 3371 NotificationService::NoDetails()); 3372 3373 bool was_crashed = IsCrashed(); 3374 SetIsCrashed(base::TERMINATION_STATUS_STILL_RUNNING, 0); 3375 3376 // Restore the focus to the tab (otherwise the focus will be on the top 3377 // window). 3378 if (was_crashed && !FocusLocationBarByDefault() && 3379 (!delegate_ || delegate_->ShouldFocusPageAfterCrash())) { 3380 view_->Focus(); 3381 } 3382 3383 FOR_EACH_OBSERVER(WebContentsObserver, observers_, RenderViewReady()); 3384 } 3385 3386 void WebContentsImpl::RenderViewTerminated(RenderViewHost* rvh, 3387 base::TerminationStatus status, 3388 int error_code) { 3389 if (rvh != GetRenderViewHost()) { 3390 // The pending page's RenderViewHost is gone. 3391 return; 3392 } 3393 3394 // Ensure fullscreen mode is exited in the |delegate_| since a crashed 3395 // renderer may not have made a clean exit. 3396 if (IsFullscreenForCurrentTab()) 3397 ToggleFullscreenMode(false); 3398 3399 // Cancel any visible dialogs so they are not left dangling over the sad tab. 3400 if (dialog_manager_) 3401 dialog_manager_->CancelActiveAndPendingDialogs(this); 3402 3403 if (delegate_) 3404 delegate_->HideValidationMessage(this); 3405 3406 SetIsLoading(rvh, false, true, NULL); 3407 NotifyDisconnected(); 3408 SetIsCrashed(status, error_code); 3409 3410 // Reset the loading progress. TODO(avi): What does it mean to have a 3411 // "renderer crash" when there is more than one renderer process serving a 3412 // webpage? Once this function is called at a more granular frame level, we 3413 // probably will need to more granularly reset the state here. 3414 ResetLoadProgressState(); 3415 loading_frames_in_progress_ = 0; 3416 3417 FOR_EACH_OBSERVER(WebContentsObserver, 3418 observers_, 3419 RenderProcessGone(GetCrashedStatus())); 3420 } 3421 3422 void WebContentsImpl::RenderViewDeleted(RenderViewHost* rvh) { 3423 FOR_EACH_OBSERVER(WebContentsObserver, observers_, RenderViewDeleted(rvh)); 3424 } 3425 3426 void WebContentsImpl::UpdateState(RenderViewHost* rvh, 3427 int32 page_id, 3428 const PageState& page_state) { 3429 // Ensure that this state update comes from either the active RVH or one of 3430 // the swapped out RVHs. We don't expect to hear from any other RVHs. 3431 // TODO(nasko): This should go through RenderFrameHost. 3432 // TODO(creis): We can't update state for cross-process subframes until we 3433 // have FrameNavigationEntries. Once we do, this should be a DCHECK. 3434 if (rvh != GetRenderViewHost() && 3435 !GetRenderManager()->IsRVHOnSwappedOutList( 3436 static_cast<RenderViewHostImpl*>(rvh))) 3437 return; 3438 3439 // We must be prepared to handle state updates for any page, these occur 3440 // when the user is scrolling and entering form data, as well as when we're 3441 // leaving a page, in which case our state may have already been moved to 3442 // the next page. The navigation controller will look up the appropriate 3443 // NavigationEntry and update it when it is notified via the delegate. 3444 3445 int entry_index = controller_.GetEntryIndexWithPageID( 3446 rvh->GetSiteInstance(), page_id); 3447 if (entry_index < 0) 3448 return; 3449 NavigationEntry* entry = controller_.GetEntryAtIndex(entry_index); 3450 3451 if (page_state == entry->GetPageState()) 3452 return; // Nothing to update. 3453 entry->SetPageState(page_state); 3454 controller_.NotifyEntryChanged(entry, entry_index); 3455 } 3456 3457 void WebContentsImpl::UpdateTargetURL(int32 page_id, const GURL& url) { 3458 if (delegate_) 3459 delegate_->UpdateTargetURL(this, page_id, url); 3460 } 3461 3462 void WebContentsImpl::Close(RenderViewHost* rvh) { 3463 #if defined(OS_MACOSX) 3464 // The UI may be in an event-tracking loop, such as between the 3465 // mouse-down and mouse-up in text selection or a button click. 3466 // Defer the close until after tracking is complete, so that we 3467 // don't free objects out from under the UI. 3468 // TODO(shess): This could get more fine-grained. For instance, 3469 // closing a tab in another window while selecting text in the 3470 // current window's Omnibox should be just fine. 3471 if (view_->IsEventTracking()) { 3472 view_->CloseTabAfterEventTracking(); 3473 return; 3474 } 3475 #endif 3476 3477 // Ignore this if it comes from a RenderViewHost that we aren't showing. 3478 if (delegate_ && rvh == GetRenderViewHost()) 3479 delegate_->CloseContents(this); 3480 } 3481 3482 void WebContentsImpl::SwappedOut(RenderFrameHost* rfh) { 3483 if (delegate_ && rfh->GetRenderViewHost() == GetRenderViewHost()) 3484 delegate_->SwappedOut(this); 3485 } 3486 3487 void WebContentsImpl::RequestMove(const gfx::Rect& new_bounds) { 3488 if (delegate_ && delegate_->IsPopupOrPanel(this)) 3489 delegate_->MoveContents(this, new_bounds); 3490 } 3491 3492 void WebContentsImpl::DidStartLoading(RenderFrameHost* render_frame_host, 3493 bool to_different_document) { 3494 SetIsLoading(render_frame_host->GetRenderViewHost(), true, 3495 to_different_document, NULL); 3496 } 3497 3498 void WebContentsImpl::DidStopLoading(RenderFrameHost* render_frame_host) { 3499 scoped_ptr<LoadNotificationDetails> details; 3500 3501 // Use the last committed entry rather than the active one, in case a 3502 // pending entry has been created. 3503 NavigationEntry* entry = controller_.GetLastCommittedEntry(); 3504 Navigator* navigator = frame_tree_.root()->navigator(); 3505 3506 // An entry may not exist for a stop when loading an initial blank page or 3507 // if an iframe injected by script into a blank page finishes loading. 3508 if (entry) { 3509 base::TimeDelta elapsed = 3510 base::TimeTicks::Now() - navigator->GetCurrentLoadStart(); 3511 3512 details.reset(new LoadNotificationDetails( 3513 entry->GetVirtualURL(), 3514 entry->GetTransitionType(), 3515 elapsed, 3516 &controller_, 3517 controller_.GetCurrentEntryIndex())); 3518 } 3519 3520 SetIsLoading(render_frame_host->GetRenderViewHost(), false, true, 3521 details.get()); 3522 } 3523 3524 void WebContentsImpl::DidCancelLoading() { 3525 controller_.DiscardNonCommittedEntries(); 3526 3527 // Update the URL display. 3528 NotifyNavigationStateChanged(INVALIDATE_TYPE_URL); 3529 } 3530 3531 void WebContentsImpl::DidAccessInitialDocument() { 3532 has_accessed_initial_document_ = true; 3533 3534 // We may have left a failed browser-initiated navigation in the address bar 3535 // to let the user edit it and try again. Clear it now that content might 3536 // show up underneath it. 3537 if (!IsLoading() && controller_.GetPendingEntry()) 3538 controller_.DiscardPendingEntry(); 3539 3540 // Update the URL display. 3541 NotifyNavigationStateChanged(content::INVALIDATE_TYPE_URL); 3542 } 3543 3544 void WebContentsImpl::DidDisownOpener(RenderFrameHost* render_frame_host) { 3545 if (opener_) { 3546 // Clear our opener so that future cross-process navigations don't have an 3547 // opener assigned. 3548 RemoveDestructionObserver(opener_); 3549 opener_ = NULL; 3550 } 3551 3552 // Notify all swapped out RenderViewHosts for this tab. This is important 3553 // in case we go back to them, or if another window in those processes tries 3554 // to access window.opener. 3555 GetRenderManager()->DidDisownOpener(render_frame_host->GetRenderViewHost()); 3556 } 3557 3558 void WebContentsImpl::DocumentOnLoadCompleted( 3559 RenderFrameHost* render_frame_host) { 3560 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 3561 DocumentOnLoadCompletedInMainFrame()); 3562 3563 // TODO(avi): Remove. http://crbug.com/170921 3564 NotificationService::current()->Notify( 3565 NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, 3566 Source<WebContents>(this), 3567 NotificationService::NoDetails()); 3568 } 3569 3570 void WebContentsImpl::UpdateTitle(RenderFrameHost* render_frame_host, 3571 int32 page_id, 3572 const base::string16& title, 3573 base::i18n::TextDirection title_direction) { 3574 RenderViewHost* rvh = render_frame_host->GetRenderViewHost(); 3575 3576 // If we have a title, that's a pretty good indication that we've started 3577 // getting useful data. 3578 SetNotWaitingForResponse(); 3579 3580 // Try to find the navigation entry, which might not be the current one. 3581 // For example, it might be from a pending RVH for the pending entry. 3582 NavigationEntryImpl* entry = controller_.GetEntryWithPageID( 3583 rvh->GetSiteInstance(), page_id); 3584 3585 // We can handle title updates when we don't have an entry in 3586 // UpdateTitleForEntry, but only if the update is from the current RVH. 3587 // TODO(avi): Change to make decisions based on the RenderFrameHost. 3588 if (!entry && rvh != GetRenderViewHost()) 3589 return; 3590 3591 // TODO(evan): make use of title_direction. 3592 // http://code.google.com/p/chromium/issues/detail?id=27094 3593 if (!UpdateTitleForEntry(entry, title)) 3594 return; 3595 3596 // Broadcast notifications when the UI should be updated. 3597 if (entry == controller_.GetEntryAtOffset(0)) 3598 NotifyNavigationStateChanged(INVALIDATE_TYPE_TITLE); 3599 } 3600 3601 void WebContentsImpl::UpdateEncoding(RenderFrameHost* render_frame_host, 3602 const std::string& encoding) { 3603 SetEncoding(encoding); 3604 } 3605 3606 void WebContentsImpl::DocumentAvailableInMainFrame( 3607 RenderViewHost* render_view_host) { 3608 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 3609 DocumentAvailableInMainFrame()); 3610 } 3611 void WebContentsImpl::RouteCloseEvent(RenderViewHost* rvh) { 3612 // Tell the active RenderViewHost to run unload handlers and close, as long 3613 // as the request came from a RenderViewHost in the same BrowsingInstance. 3614 // In most cases, we receive this from a swapped out RenderViewHost. 3615 // It is possible to receive it from one that has just been swapped in, 3616 // in which case we might as well deliver the message anyway. 3617 if (rvh->GetSiteInstance()->IsRelatedSiteInstance(GetSiteInstance())) 3618 GetRenderViewHost()->ClosePage(); 3619 } 3620 3621 void WebContentsImpl::RouteMessageEvent( 3622 RenderViewHost* rvh, 3623 const ViewMsg_PostMessage_Params& params) { 3624 // Only deliver the message to the active RenderViewHost if the request 3625 // came from a RenderViewHost in the same BrowsingInstance or if this 3626 // WebContents is dedicated to a browser plugin guest. 3627 // Note: This check means that an embedder could theoretically receive a 3628 // postMessage from anyone (not just its own guests). However, this is 3629 // probably not a risk for apps since other pages won't have references 3630 // to App windows. 3631 if (!rvh->GetSiteInstance()->IsRelatedSiteInstance(GetSiteInstance()) && 3632 !GetBrowserPluginGuest() && !GetBrowserPluginEmbedder()) 3633 return; 3634 3635 ViewMsg_PostMessage_Params new_params(params); 3636 3637 if (!params.message_port_ids.empty()) { 3638 MessagePortMessageFilter* message_port_message_filter = 3639 static_cast<RenderProcessHostImpl*>(GetRenderProcessHost()) 3640 ->message_port_message_filter(); 3641 message_port_message_filter->UpdateMessagePortsWithNewRoutes( 3642 params.message_port_ids, 3643 &new_params.new_routing_ids); 3644 } 3645 3646 // If there is a source_routing_id, translate it to the routing ID for 3647 // the equivalent swapped out RVH in the target process. If we need 3648 // to create a swapped out RVH for the source tab, we create its opener 3649 // chain as well, since those will also be accessible to the target page. 3650 if (new_params.source_routing_id != MSG_ROUTING_NONE) { 3651 // Try to look up the WebContents for the source page. 3652 WebContentsImpl* source_contents = NULL; 3653 RenderViewHostImpl* source_rvh = RenderViewHostImpl::FromID( 3654 rvh->GetProcess()->GetID(), params.source_routing_id); 3655 if (source_rvh) { 3656 source_contents = static_cast<WebContentsImpl*>( 3657 source_rvh->GetDelegate()->GetAsWebContents()); 3658 } 3659 3660 if (source_contents) { 3661 if (GetBrowserPluginGuest()) { 3662 // We create a swapped out RenderView for the embedder in the guest's 3663 // render process but we intentionally do not expose the embedder's 3664 // opener chain to it. 3665 new_params.source_routing_id = 3666 source_contents->CreateSwappedOutRenderView(GetSiteInstance()); 3667 } else { 3668 new_params.source_routing_id = 3669 source_contents->CreateOpenerRenderViews(GetSiteInstance()); 3670 } 3671 } else { 3672 // We couldn't find it, so don't pass a source frame. 3673 new_params.source_routing_id = MSG_ROUTING_NONE; 3674 } 3675 } 3676 3677 // In most cases, we receive this from a swapped out RenderViewHost. 3678 // It is possible to receive it from one that has just been swapped in, 3679 // in which case we might as well deliver the message anyway. 3680 Send(new ViewMsg_PostMessageEvent(GetRoutingID(), new_params)); 3681 } 3682 3683 bool WebContentsImpl::AddMessageToConsole(int32 level, 3684 const base::string16& message, 3685 int32 line_no, 3686 const base::string16& source_id) { 3687 if (!delegate_) 3688 return false; 3689 return delegate_->AddMessageToConsole(this, level, message, line_no, 3690 source_id); 3691 } 3692 3693 WebPreferences WebContentsImpl::GetWebkitPrefs() { 3694 // We want to base the page config off of the actual URL, rather than the 3695 // virtual URL. 3696 // TODO(nasko): Investigate how to remove the GetActiveEntry usage here, 3697 // as it is deprecated and can be out of sync with GetRenderViewHost(). 3698 GURL url = controller_.GetActiveEntry() 3699 ? controller_.GetActiveEntry()->GetURL() : GURL::EmptyGURL(); 3700 3701 return GetRenderManager()->current_host()->GetWebkitPrefs(url); 3702 } 3703 3704 int WebContentsImpl::CreateSwappedOutRenderView( 3705 SiteInstance* instance) { 3706 return GetRenderManager()->CreateRenderFrame(instance, MSG_ROUTING_NONE, 3707 true, true); 3708 } 3709 3710 void WebContentsImpl::OnUserGesture() { 3711 // Notify observers. 3712 FOR_EACH_OBSERVER(WebContentsObserver, observers_, DidGetUserGesture()); 3713 3714 ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get(); 3715 if (rdh) // NULL in unittests. 3716 rdh->OnUserGesture(this); 3717 } 3718 3719 void WebContentsImpl::OnIgnoredUIEvent() { 3720 // Notify observers. 3721 FOR_EACH_OBSERVER(WebContentsObserver, observers_, DidGetIgnoredUIEvent()); 3722 } 3723 3724 void WebContentsImpl::RendererUnresponsive(RenderViewHost* rvh, 3725 bool is_during_beforeunload, 3726 bool is_during_unload) { 3727 // Don't show hung renderer dialog for a swapped out RVH. 3728 if (rvh != GetRenderViewHost()) 3729 return; 3730 3731 RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>(rvh); 3732 3733 // Ignore renderer unresponsive event if debugger is attached to the tab 3734 // since the event may be a result of the renderer sitting on a breakpoint. 3735 // See http://crbug.com/65458 3736 if (DevToolsAgentHost::IsDebuggerAttached(this)) 3737 return; 3738 3739 if (is_during_beforeunload || is_during_unload) { 3740 // Hang occurred while firing the beforeunload/unload handler. 3741 // Pretend the handler fired so tab closing continues as if it had. 3742 rvhi->set_sudden_termination_allowed(true); 3743 3744 if (!GetRenderManager()->ShouldCloseTabOnUnresponsiveRenderer()) 3745 return; 3746 3747 // If the tab hangs in the beforeunload/unload handler there's really 3748 // nothing we can do to recover. If the hang is in the beforeunload handler, 3749 // pretend the beforeunload listeners have all fired and allow the delegate 3750 // to continue closing; the user will not have the option of cancelling the 3751 // close. Otherwise, pretend the unload listeners have all fired and close 3752 // the tab. 3753 bool close = true; 3754 if (is_during_beforeunload && delegate_) { 3755 delegate_->BeforeUnloadFired(this, true, &close); 3756 } 3757 if (close) 3758 Close(rvh); 3759 return; 3760 } 3761 3762 if (!GetRenderViewHostImpl() || !GetRenderViewHostImpl()->IsRenderViewLive()) 3763 return; 3764 3765 if (delegate_) 3766 delegate_->RendererUnresponsive(this); 3767 } 3768 3769 void WebContentsImpl::RendererResponsive(RenderViewHost* render_view_host) { 3770 if (delegate_) 3771 delegate_->RendererResponsive(this); 3772 } 3773 3774 void WebContentsImpl::LoadStateChanged( 3775 const GURL& url, 3776 const net::LoadStateWithParam& load_state, 3777 uint64 upload_position, 3778 uint64 upload_size) { 3779 load_state_ = load_state; 3780 upload_position_ = upload_position; 3781 upload_size_ = upload_size; 3782 load_state_host_ = net::IDNToUnicode(url.host(), 3783 GetContentClient()->browser()->GetAcceptLangs( 3784 GetBrowserContext())); 3785 if (load_state_.state == net::LOAD_STATE_READING_RESPONSE) 3786 SetNotWaitingForResponse(); 3787 if (IsLoading()) { 3788 NotifyNavigationStateChanged(INVALIDATE_TYPE_LOAD | INVALIDATE_TYPE_TAB); 3789 } 3790 } 3791 3792 void WebContentsImpl::BeforeUnloadFiredFromRenderManager( 3793 bool proceed, const base::TimeTicks& proceed_time, 3794 bool* proceed_to_fire_unload) { 3795 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 3796 BeforeUnloadFired(proceed_time)); 3797 if (delegate_) 3798 delegate_->BeforeUnloadFired(this, proceed, proceed_to_fire_unload); 3799 // Note: |this| might be deleted at this point. 3800 } 3801 3802 void WebContentsImpl::RenderProcessGoneFromRenderManager( 3803 RenderViewHost* render_view_host) { 3804 DCHECK(crashed_status_ != base::TERMINATION_STATUS_STILL_RUNNING); 3805 RenderViewTerminated(render_view_host, crashed_status_, crashed_error_code_); 3806 } 3807 3808 void WebContentsImpl::UpdateRenderViewSizeForRenderManager() { 3809 // TODO(brettw) this is a hack. See WebContentsView::SizeContents. 3810 gfx::Size size = GetSizeForNewRenderView(); 3811 // 0x0 isn't a valid window size (minimal window size is 1x1) but it may be 3812 // here during container initialization and normal window size will be set 3813 // later. In case of tab duplication this resizing to 0x0 prevents setting 3814 // normal size later so just ignore it. 3815 if (!size.IsEmpty()) 3816 view_->SizeContents(size); 3817 } 3818 3819 void WebContentsImpl::CancelModalDialogsForRenderManager() { 3820 // We need to cancel modal dialogs when doing a process swap, since the load 3821 // deferrer would prevent us from swapping out. 3822 if (dialog_manager_) 3823 dialog_manager_->CancelActiveAndPendingDialogs(this); 3824 } 3825 3826 void WebContentsImpl::NotifySwappedFromRenderManager(RenderViewHost* old_host, 3827 RenderViewHost* new_host) { 3828 NotifySwapped(old_host, new_host); 3829 3830 // Make sure the visible RVH reflects the new delegate's preferences. 3831 if (delegate_) 3832 view_->SetOverscrollControllerEnabled(CanOverscrollContent()); 3833 3834 view_->RenderViewSwappedIn(new_host); 3835 } 3836 3837 int WebContentsImpl::CreateOpenerRenderViewsForRenderManager( 3838 SiteInstance* instance) { 3839 if (!opener_) 3840 return MSG_ROUTING_NONE; 3841 3842 // Recursively create RenderViews for anything else in the opener chain. 3843 return opener_->CreateOpenerRenderViews(instance); 3844 } 3845 3846 int WebContentsImpl::CreateOpenerRenderViews(SiteInstance* instance) { 3847 int opener_route_id = MSG_ROUTING_NONE; 3848 3849 // If this tab has an opener, ensure it has a RenderView in the given 3850 // SiteInstance as well. 3851 if (opener_) 3852 opener_route_id = opener_->CreateOpenerRenderViews(instance); 3853 3854 // If any of the renderers (current, pending, or swapped out) for this 3855 // WebContents has the same SiteInstance, use it. 3856 if (GetRenderManager()->current_host()->GetSiteInstance() == instance) 3857 return GetRenderManager()->current_host()->GetRoutingID(); 3858 3859 if (GetRenderManager()->pending_render_view_host() && 3860 GetRenderManager()->pending_render_view_host()->GetSiteInstance() == 3861 instance) 3862 return GetRenderManager()->pending_render_view_host()->GetRoutingID(); 3863 3864 RenderViewHostImpl* rvh = GetRenderManager()->GetSwappedOutRenderViewHost( 3865 instance); 3866 if (rvh) 3867 return rvh->GetRoutingID(); 3868 3869 // Create a swapped out RenderView in the given SiteInstance if none exists, 3870 // setting its opener to the given route_id. Return the new view's route_id. 3871 return GetRenderManager()->CreateRenderFrame(instance, opener_route_id, 3872 true, true); 3873 } 3874 3875 NavigationControllerImpl& WebContentsImpl::GetControllerForRenderManager() { 3876 return GetController(); 3877 } 3878 3879 WebUIImpl* WebContentsImpl::CreateWebUIForRenderManager(const GURL& url) { 3880 return static_cast<WebUIImpl*>(CreateWebUI(url)); 3881 } 3882 3883 NavigationEntry* 3884 WebContentsImpl::GetLastCommittedNavigationEntryForRenderManager() { 3885 return controller_.GetLastCommittedEntry(); 3886 } 3887 3888 bool WebContentsImpl::CreateRenderViewForRenderManager( 3889 RenderViewHost* render_view_host, 3890 int opener_route_id, 3891 int proxy_routing_id, 3892 bool for_main_frame) { 3893 TRACE_EVENT0("browser", "WebContentsImpl::CreateRenderViewForRenderManager"); 3894 // Can be NULL during tests. 3895 RenderWidgetHostViewBase* rwh_view; 3896 // TODO(kenrb): RenderWidgetHostViewChildFrame special casing is temporary 3897 // until RenderWidgetHost is attached to RenderFrameHost. We need to special 3898 // case this because RWH is still a base class of RenderViewHost, and child 3899 // frame RWHVs are unique in that they do not have their own WebContents. 3900 if (!for_main_frame) { 3901 RenderWidgetHostViewChildFrame* rwh_view_child = 3902 new RenderWidgetHostViewChildFrame(render_view_host); 3903 rwh_view = rwh_view_child; 3904 } else { 3905 rwh_view = view_->CreateViewForWidget(render_view_host); 3906 } 3907 3908 // Now that the RenderView has been created, we need to tell it its size. 3909 if (rwh_view) 3910 rwh_view->SetSize(GetSizeForNewRenderView()); 3911 3912 // Make sure we use the correct starting page_id in the new RenderView. 3913 UpdateMaxPageIDIfNecessary(render_view_host); 3914 int32 max_page_id = 3915 GetMaxPageIDForSiteInstance(render_view_host->GetSiteInstance()); 3916 3917 if (!static_cast<RenderViewHostImpl*>( 3918 render_view_host)->CreateRenderView(base::string16(), 3919 opener_route_id, 3920 proxy_routing_id, 3921 max_page_id, 3922 created_with_opener_)) { 3923 return false; 3924 } 3925 3926 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 3927 // Force a ViewMsg_Resize to be sent, needed to make plugins show up on 3928 // linux. See crbug.com/83941. 3929 if (rwh_view) { 3930 if (RenderWidgetHost* render_widget_host = rwh_view->GetRenderWidgetHost()) 3931 render_widget_host->WasResized(); 3932 } 3933 #endif 3934 3935 return true; 3936 } 3937 3938 #if defined(OS_ANDROID) 3939 3940 base::android::ScopedJavaLocalRef<jobject> 3941 WebContentsImpl::GetJavaWebContents() { 3942 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 3943 3944 WebContentsAndroid* web_contents_android = 3945 static_cast<WebContentsAndroid*>(GetUserData(kWebContentsAndroidKey)); 3946 if (!web_contents_android) { 3947 web_contents_android = new WebContentsAndroid(this); 3948 SetUserData(kWebContentsAndroidKey, web_contents_android); 3949 } 3950 return web_contents_android->GetJavaObject(); 3951 } 3952 3953 bool WebContentsImpl::CreateRenderViewForInitialEmptyDocument() { 3954 return CreateRenderViewForRenderManager(GetRenderViewHost(), 3955 MSG_ROUTING_NONE, 3956 MSG_ROUTING_NONE, 3957 true); 3958 } 3959 3960 #elif defined(OS_MACOSX) 3961 3962 void WebContentsImpl::SetAllowOverlappingViews(bool overlapping) { 3963 view_->SetAllowOverlappingViews(overlapping); 3964 } 3965 3966 bool WebContentsImpl::GetAllowOverlappingViews() { 3967 return view_->GetAllowOverlappingViews(); 3968 } 3969 3970 void WebContentsImpl::SetOverlayView(WebContents* overlay, 3971 const gfx::Point& offset) { 3972 view_->SetOverlayView(static_cast<WebContentsImpl*>(overlay)->GetView(), 3973 offset); 3974 } 3975 3976 void WebContentsImpl::RemoveOverlayView() { 3977 view_->RemoveOverlayView(); 3978 } 3979 3980 #endif 3981 3982 void WebContentsImpl::OnDialogClosed(int render_process_id, 3983 int render_frame_id, 3984 IPC::Message* reply_msg, 3985 bool dialog_was_suppressed, 3986 bool success, 3987 const base::string16& user_input) { 3988 RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(render_process_id, 3989 render_frame_id); 3990 last_dialog_suppressed_ = dialog_was_suppressed; 3991 3992 if (is_showing_before_unload_dialog_ && !success) { 3993 // If a beforeunload dialog is canceled, we need to stop the throbber from 3994 // spinning, since we forced it to start spinning in Navigate. 3995 if (rfh) 3996 DidStopLoading(rfh); 3997 controller_.DiscardNonCommittedEntries(); 3998 3999 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 4000 BeforeUnloadDialogCancelled()); 4001 } 4002 4003 is_showing_before_unload_dialog_ = false; 4004 if (rfh) { 4005 rfh->JavaScriptDialogClosed(reply_msg, success, user_input, 4006 dialog_was_suppressed); 4007 } else { 4008 // Don't leak the sync IPC reply if the RFH or process is gone. 4009 delete reply_msg; 4010 } 4011 } 4012 4013 void WebContentsImpl::SetEncoding(const std::string& encoding) { 4014 if (encoding == last_reported_encoding_) 4015 return; 4016 last_reported_encoding_ = encoding; 4017 4018 canonical_encoding_ = GetContentClient()->browser()-> 4019 GetCanonicalEncodingNameByAliasName(encoding); 4020 } 4021 4022 void WebContentsImpl::CreateViewAndSetSizeForRVH(RenderViewHost* rvh) { 4023 RenderWidgetHostViewBase* rwh_view = view_->CreateViewForWidget(rvh); 4024 // Can be NULL during tests. 4025 if (rwh_view) 4026 rwh_view->SetSize(GetContainerBounds().size()); 4027 } 4028 4029 bool WebContentsImpl::IsHidden() { 4030 return capturer_count_ == 0 && !should_normally_be_visible_; 4031 } 4032 4033 RenderFrameHostManager* WebContentsImpl::GetRenderManager() const { 4034 return frame_tree_.root()->render_manager(); 4035 } 4036 4037 RenderViewHostImpl* WebContentsImpl::GetRenderViewHostImpl() { 4038 return static_cast<RenderViewHostImpl*>(GetRenderViewHost()); 4039 } 4040 4041 BrowserPluginGuest* WebContentsImpl::GetBrowserPluginGuest() const { 4042 return browser_plugin_guest_.get(); 4043 } 4044 4045 void WebContentsImpl::SetBrowserPluginGuest(BrowserPluginGuest* guest) { 4046 CHECK(!browser_plugin_guest_); 4047 browser_plugin_guest_.reset(guest); 4048 } 4049 4050 BrowserPluginEmbedder* WebContentsImpl::GetBrowserPluginEmbedder() const { 4051 return browser_plugin_embedder_.get(); 4052 } 4053 4054 void WebContentsImpl::ClearPowerSaveBlockers( 4055 RenderFrameHost* render_frame_host) { 4056 STLDeleteValues(&power_save_blockers_[render_frame_host]); 4057 power_save_blockers_.erase(render_frame_host); 4058 } 4059 4060 void WebContentsImpl::ClearAllPowerSaveBlockers() { 4061 for (PowerSaveBlockerMap::iterator i(power_save_blockers_.begin()); 4062 i != power_save_blockers_.end(); ++i) 4063 STLDeleteValues(&power_save_blockers_[i->first]); 4064 power_save_blockers_.clear(); 4065 } 4066 4067 gfx::Size WebContentsImpl::GetSizeForNewRenderView() { 4068 gfx::Size size; 4069 if (delegate_) 4070 size = delegate_->GetSizeForNewRenderView(this); 4071 if (size.IsEmpty()) 4072 size = GetContainerBounds().size(); 4073 return size; 4074 } 4075 4076 void WebContentsImpl::OnFrameRemoved( 4077 RenderViewHostImpl* render_view_host, 4078 int frame_routing_id) { 4079 FOR_EACH_OBSERVER(WebContentsObserver, observers_, 4080 FrameDetached(render_view_host, frame_routing_id)); 4081 } 4082 4083 void WebContentsImpl::OnPreferredSizeChanged(const gfx::Size& old_size) { 4084 if (!delegate_) 4085 return; 4086 const gfx::Size new_size = GetPreferredSize(); 4087 if (new_size != old_size) 4088 delegate_->UpdatePreferredSize(this, new_size); 4089 } 4090 4091 } // namespace content 4092