1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/frame_host/interstitial_page_impl.h" 6 7 #include <vector> 8 9 #include "base/bind.h" 10 #include "base/compiler_specific.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "base/threading/thread.h" 15 #include "content/browser/dom_storage/dom_storage_context_wrapper.h" 16 #include "content/browser/dom_storage/session_storage_namespace_impl.h" 17 #include "content/browser/frame_host/interstitial_page_navigator_impl.h" 18 #include "content/browser/frame_host/navigation_controller_impl.h" 19 #include "content/browser/frame_host/navigation_entry_impl.h" 20 #include "content/browser/loader/resource_dispatcher_host_impl.h" 21 #include "content/browser/renderer_host/render_process_host_impl.h" 22 #include "content/browser/renderer_host/render_view_host_delegate_view.h" 23 #include "content/browser/renderer_host/render_view_host_factory.h" 24 #include "content/browser/renderer_host/render_view_host_impl.h" 25 #include "content/browser/renderer_host/render_widget_host_view_base.h" 26 #include "content/browser/site_instance_impl.h" 27 #include "content/browser/web_contents/web_contents_impl.h" 28 #include "content/browser/web_contents/web_contents_view.h" 29 #include "content/common/frame_messages.h" 30 #include "content/common/view_messages.h" 31 #include "content/public/browser/browser_context.h" 32 #include "content/public/browser/browser_thread.h" 33 #include "content/public/browser/content_browser_client.h" 34 #include "content/public/browser/dom_operation_notification_details.h" 35 #include "content/public/browser/interstitial_page_delegate.h" 36 #include "content/public/browser/invalidate_type.h" 37 #include "content/public/browser/notification_service.h" 38 #include "content/public/browser/notification_source.h" 39 #include "content/public/browser/storage_partition.h" 40 #include "content/public/browser/user_metrics.h" 41 #include "content/public/browser/web_contents_delegate.h" 42 #include "content/public/common/bindings_policy.h" 43 #include "net/base/escape.h" 44 #include "net/url_request/url_request_context_getter.h" 45 #include "ui/base/page_transition_types.h" 46 47 using blink::WebDragOperation; 48 using blink::WebDragOperationsMask; 49 50 namespace content { 51 namespace { 52 53 void ResourceRequestHelper(ResourceDispatcherHostImpl* rdh, 54 int process_id, 55 int render_view_host_id, 56 ResourceRequestAction action) { 57 switch (action) { 58 case BLOCK: 59 rdh->BlockRequestsForRoute(process_id, render_view_host_id); 60 break; 61 case RESUME: 62 rdh->ResumeBlockedRequestsForRoute(process_id, render_view_host_id); 63 break; 64 case CANCEL: 65 rdh->CancelBlockedRequestsForRoute(process_id, render_view_host_id); 66 break; 67 default: 68 NOTREACHED(); 69 } 70 } 71 72 } // namespace 73 74 class InterstitialPageImpl::InterstitialPageRVHDelegateView 75 : public RenderViewHostDelegateView { 76 public: 77 explicit InterstitialPageRVHDelegateView(InterstitialPageImpl* page); 78 79 // RenderViewHostDelegateView implementation: 80 #if defined(OS_MACOSX) || defined(OS_ANDROID) 81 virtual void ShowPopupMenu(RenderFrameHost* render_frame_host, 82 const gfx::Rect& bounds, 83 int item_height, 84 double item_font_size, 85 int selected_item, 86 const std::vector<MenuItem>& items, 87 bool right_aligned, 88 bool allow_multiple_selection) OVERRIDE; 89 virtual void HidePopupMenu() OVERRIDE; 90 #endif 91 virtual void StartDragging(const DropData& drop_data, 92 WebDragOperationsMask operations_allowed, 93 const gfx::ImageSkia& image, 94 const gfx::Vector2d& image_offset, 95 const DragEventSourceInfo& event_info) OVERRIDE; 96 virtual void UpdateDragCursor(WebDragOperation operation) OVERRIDE; 97 virtual void GotFocus() OVERRIDE; 98 virtual void TakeFocus(bool reverse) OVERRIDE; 99 virtual void OnFindReply(int request_id, 100 int number_of_matches, 101 const gfx::Rect& selection_rect, 102 int active_match_ordinal, 103 bool final_update); 104 105 private: 106 InterstitialPageImpl* interstitial_page_; 107 108 DISALLOW_COPY_AND_ASSIGN(InterstitialPageRVHDelegateView); 109 }; 110 111 112 // We keep a map of the various blocking pages shown as the UI tests need to 113 // be able to retrieve them. 114 typedef std::map<WebContents*, InterstitialPageImpl*> InterstitialPageMap; 115 static InterstitialPageMap* g_web_contents_to_interstitial_page; 116 117 // Initializes g_web_contents_to_interstitial_page in a thread-safe manner. 118 // Should be called before accessing g_web_contents_to_interstitial_page. 119 static void InitInterstitialPageMap() { 120 if (!g_web_contents_to_interstitial_page) 121 g_web_contents_to_interstitial_page = new InterstitialPageMap; 122 } 123 124 InterstitialPage* InterstitialPage::Create(WebContents* web_contents, 125 bool new_navigation, 126 const GURL& url, 127 InterstitialPageDelegate* delegate) { 128 return new InterstitialPageImpl( 129 web_contents, 130 static_cast<RenderWidgetHostDelegate*>( 131 static_cast<WebContentsImpl*>(web_contents)), 132 new_navigation, url, delegate); 133 } 134 135 InterstitialPage* InterstitialPage::GetInterstitialPage( 136 WebContents* web_contents) { 137 InitInterstitialPageMap(); 138 InterstitialPageMap::const_iterator iter = 139 g_web_contents_to_interstitial_page->find(web_contents); 140 if (iter == g_web_contents_to_interstitial_page->end()) 141 return NULL; 142 143 return iter->second; 144 } 145 146 InterstitialPageImpl::InterstitialPageImpl( 147 WebContents* web_contents, 148 RenderWidgetHostDelegate* render_widget_host_delegate, 149 bool new_navigation, 150 const GURL& url, 151 InterstitialPageDelegate* delegate) 152 : WebContentsObserver(web_contents), 153 web_contents_(web_contents), 154 controller_(static_cast<NavigationControllerImpl*>( 155 &web_contents->GetController())), 156 render_widget_host_delegate_(render_widget_host_delegate), 157 url_(url), 158 new_navigation_(new_navigation), 159 should_discard_pending_nav_entry_(new_navigation), 160 reload_on_dont_proceed_(false), 161 enabled_(true), 162 action_taken_(NO_ACTION), 163 render_view_host_(NULL), 164 // TODO(nasko): The InterstitialPageImpl will need to provide its own 165 // NavigationControllerImpl to the Navigator, which is separate from 166 // the WebContents one, so we can enforce no navigation policy here. 167 // While we get the code to a point to do this, pass NULL for it. 168 // TODO(creis): We will also need to pass delegates for the RVHM as we 169 // start to use it. 170 frame_tree_(new InterstitialPageNavigatorImpl(this, controller_), 171 this, this, this, 172 static_cast<WebContentsImpl*>(web_contents)), 173 original_child_id_(web_contents->GetRenderProcessHost()->GetID()), 174 original_rvh_id_(web_contents->GetRenderViewHost()->GetRoutingID()), 175 should_revert_web_contents_title_(false), 176 web_contents_was_loading_(false), 177 resource_dispatcher_host_notified_(false), 178 rvh_delegate_view_(new InterstitialPageRVHDelegateView(this)), 179 create_view_(true), 180 delegate_(delegate), 181 weak_ptr_factory_(this) { 182 InitInterstitialPageMap(); 183 // It would be inconsistent to create an interstitial with no new navigation 184 // (which is the case when the interstitial was triggered by a sub-resource on 185 // a page) when we have a pending entry (in the process of loading a new top 186 // frame). 187 DCHECK(new_navigation || !web_contents->GetController().GetPendingEntry()); 188 } 189 190 InterstitialPageImpl::~InterstitialPageImpl() { 191 } 192 193 void InterstitialPageImpl::Show() { 194 if (!enabled()) 195 return; 196 197 // If an interstitial is already showing or about to be shown, close it before 198 // showing the new one. 199 // Be careful not to take an action on the old interstitial more than once. 200 InterstitialPageMap::const_iterator iter = 201 g_web_contents_to_interstitial_page->find(web_contents_); 202 if (iter != g_web_contents_to_interstitial_page->end()) { 203 InterstitialPageImpl* interstitial = iter->second; 204 if (interstitial->action_taken_ != NO_ACTION) { 205 interstitial->Hide(); 206 } else { 207 // If we are currently showing an interstitial page for which we created 208 // a transient entry and a new interstitial is shown as the result of a 209 // new browser initiated navigation, then that transient entry has already 210 // been discarded and a new pending navigation entry created. 211 // So we should not discard that new pending navigation entry. 212 // See http://crbug.com/9791 213 if (new_navigation_ && interstitial->new_navigation_) 214 interstitial->should_discard_pending_nav_entry_= false; 215 interstitial->DontProceed(); 216 } 217 } 218 219 // Block the resource requests for the render view host while it is hidden. 220 TakeActionOnResourceDispatcher(BLOCK); 221 // We need to be notified when the RenderViewHost is destroyed so we can 222 // cancel the blocked requests. We cannot do that on 223 // NOTIFY_WEB_CONTENTS_DESTROYED as at that point the RenderViewHost has 224 // already been destroyed. 225 notification_registrar_.Add( 226 this, NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED, 227 Source<RenderWidgetHost>(controller_->delegate()->GetRenderViewHost())); 228 229 // Update the g_web_contents_to_interstitial_page map. 230 iter = g_web_contents_to_interstitial_page->find(web_contents_); 231 DCHECK(iter == g_web_contents_to_interstitial_page->end()); 232 (*g_web_contents_to_interstitial_page)[web_contents_] = this; 233 234 if (new_navigation_) { 235 NavigationEntryImpl* entry = new NavigationEntryImpl; 236 entry->SetURL(url_); 237 entry->SetVirtualURL(url_); 238 entry->set_page_type(PAGE_TYPE_INTERSTITIAL); 239 240 // Give delegates a chance to set some states on the navigation entry. 241 delegate_->OverrideEntry(entry); 242 243 controller_->SetTransientEntry(entry); 244 } 245 246 DCHECK(!render_view_host_); 247 render_view_host_ = static_cast<RenderViewHostImpl*>(CreateRenderViewHost()); 248 render_view_host_->AttachToFrameTree(); 249 CreateWebContentsView(); 250 251 std::string data_url = "data:text/html;charset=utf-8," + 252 net::EscapePath(delegate_->GetHTMLContents()); 253 frame_tree_.root()->current_frame_host()->NavigateToURL(GURL(data_url)); 254 255 notification_registrar_.Add(this, NOTIFICATION_NAV_ENTRY_PENDING, 256 Source<NavigationController>(controller_)); 257 } 258 259 void InterstitialPageImpl::Hide() { 260 // We may have already been hidden, and are just waiting to be deleted. 261 // We can't check for enabled() here, because some callers have already 262 // called Disable. 263 if (!render_view_host_) 264 return; 265 266 Disable(); 267 268 RenderWidgetHostView* old_view = 269 controller_->delegate()->GetRenderViewHost()->GetView(); 270 if (controller_->delegate()->GetInterstitialPage() == this && 271 old_view && 272 !old_view->IsShowing() && 273 !controller_->delegate()->IsHidden()) { 274 // Show the original RVH since we're going away. Note it might not exist if 275 // the renderer crashed while the interstitial was showing. 276 // Note that it is important that we don't call Show() if the view is 277 // already showing. That would result in bad things (unparented HWND on 278 // Windows for example) happening. 279 old_view->Show(); 280 } 281 282 // If the focus was on the interstitial, let's keep it to the page. 283 // (Note that in unit-tests the RVH may not have a view). 284 if (render_view_host_->GetView() && 285 render_view_host_->GetView()->HasFocus() && 286 controller_->delegate()->GetRenderViewHost()->GetView()) { 287 controller_->delegate()->GetRenderViewHost()->GetView()->Focus(); 288 } 289 290 // Delete this and call Shutdown on the RVH asynchronously, as we may have 291 // been called from a RVH delegate method, and we can't delete the RVH out 292 // from under itself. 293 base::MessageLoop::current()->PostNonNestableTask( 294 FROM_HERE, 295 base::Bind(&InterstitialPageImpl::Shutdown, 296 weak_ptr_factory_.GetWeakPtr())); 297 render_view_host_ = NULL; 298 frame_tree_.ResetForMainFrameSwap(); 299 controller_->delegate()->DetachInterstitialPage(); 300 // Let's revert to the original title if necessary. 301 NavigationEntry* entry = controller_->GetVisibleEntry(); 302 if (!new_navigation_ && should_revert_web_contents_title_) { 303 entry->SetTitle(original_web_contents_title_); 304 controller_->delegate()->NotifyNavigationStateChanged( 305 INVALIDATE_TYPE_TITLE); 306 } 307 308 InterstitialPageMap::iterator iter = 309 g_web_contents_to_interstitial_page->find(web_contents_); 310 DCHECK(iter != g_web_contents_to_interstitial_page->end()); 311 if (iter != g_web_contents_to_interstitial_page->end()) 312 g_web_contents_to_interstitial_page->erase(iter); 313 314 // Clear the WebContents pointer, because it may now be deleted. 315 // This signifies that we are in the process of shutting down. 316 web_contents_ = NULL; 317 } 318 319 void InterstitialPageImpl::Observe( 320 int type, 321 const NotificationSource& source, 322 const NotificationDetails& details) { 323 switch (type) { 324 case NOTIFICATION_NAV_ENTRY_PENDING: 325 // We are navigating away from the interstitial (the user has typed a URL 326 // in the location bar or clicked a bookmark). Make sure clicking on the 327 // interstitial will have no effect. Also cancel any blocked requests 328 // on the ResourceDispatcherHost. Note that when we get this notification 329 // the RenderViewHost has not yet navigated so we'll unblock the 330 // RenderViewHost before the resource request for the new page we are 331 // navigating arrives in the ResourceDispatcherHost. This ensures that 332 // request won't be blocked if the same RenderViewHost was used for the 333 // new navigation. 334 Disable(); 335 TakeActionOnResourceDispatcher(CANCEL); 336 break; 337 case NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED: 338 if (action_taken_ == NO_ACTION) { 339 // The RenderViewHost is being destroyed (as part of the tab being 340 // closed); make sure we clear the blocked requests. 341 RenderViewHost* rvh = static_cast<RenderViewHost*>( 342 static_cast<RenderViewHostImpl*>( 343 RenderWidgetHostImpl::From( 344 Source<RenderWidgetHost>(source).ptr()))); 345 DCHECK(rvh->GetProcess()->GetID() == original_child_id_ && 346 rvh->GetRoutingID() == original_rvh_id_); 347 TakeActionOnResourceDispatcher(CANCEL); 348 } 349 break; 350 default: 351 NOTREACHED(); 352 } 353 } 354 355 void InterstitialPageImpl::NavigationEntryCommitted( 356 const LoadCommittedDetails& load_details) { 357 OnNavigatingAwayOrTabClosing(); 358 } 359 360 void InterstitialPageImpl::WebContentsDestroyed() { 361 OnNavigatingAwayOrTabClosing(); 362 } 363 364 bool InterstitialPageImpl::OnMessageReceived( 365 const IPC::Message& message, 366 RenderFrameHost* render_frame_host) { 367 return OnMessageReceived(message); 368 } 369 370 bool InterstitialPageImpl::OnMessageReceived(RenderFrameHost* render_frame_host, 371 const IPC::Message& message) { 372 return OnMessageReceived(message); 373 } 374 375 bool InterstitialPageImpl::OnMessageReceived(RenderViewHost* render_view_host, 376 const IPC::Message& message) { 377 return OnMessageReceived(message); 378 } 379 380 bool InterstitialPageImpl::OnMessageReceived(const IPC::Message& message) { 381 382 bool handled = true; 383 IPC_BEGIN_MESSAGE_MAP(InterstitialPageImpl, message) 384 IPC_MESSAGE_HANDLER(FrameHostMsg_DomOperationResponse, 385 OnDomOperationResponse) 386 IPC_MESSAGE_UNHANDLED(handled = false) 387 IPC_END_MESSAGE_MAP() 388 389 return handled; 390 } 391 392 void InterstitialPageImpl::RenderFrameCreated( 393 RenderFrameHost* render_frame_host) { 394 // Note this is only for subframes in the interstitial, the notification for 395 // the main frame happens in RenderViewCreated. 396 controller_->delegate()->RenderFrameForInterstitialPageCreated( 397 render_frame_host); 398 } 399 400 void InterstitialPageImpl::UpdateTitle( 401 RenderFrameHost* render_frame_host, 402 int32 page_id, 403 const base::string16& title, 404 base::i18n::TextDirection title_direction) { 405 if (!enabled()) 406 return; 407 408 RenderViewHost* render_view_host = render_frame_host->GetRenderViewHost(); 409 DCHECK(render_view_host == render_view_host_); 410 NavigationEntry* entry = controller_->GetVisibleEntry(); 411 if (!entry) { 412 // Crash reports from the field indicate this can be NULL. 413 // This is unexpected as InterstitialPages constructed with the 414 // new_navigation flag set to true create a transient navigation entry 415 // (that is returned as the active entry). And the only case so far of 416 // interstitial created with that flag set to false is with the 417 // SafeBrowsingBlockingPage, when the resource triggering the interstitial 418 // is a sub-resource, meaning the main page has already been loaded and a 419 // navigation entry should have been created. 420 NOTREACHED(); 421 return; 422 } 423 424 // If this interstitial is shown on an existing navigation entry, we'll need 425 // to remember its title so we can revert to it when hidden. 426 if (!new_navigation_ && !should_revert_web_contents_title_) { 427 original_web_contents_title_ = entry->GetTitle(); 428 should_revert_web_contents_title_ = true; 429 } 430 // TODO(evan): make use of title_direction. 431 // http://code.google.com/p/chromium/issues/detail?id=27094 432 entry->SetTitle(title); 433 controller_->delegate()->NotifyNavigationStateChanged(INVALIDATE_TYPE_TITLE); 434 } 435 436 AccessibilityMode InterstitialPageImpl::GetAccessibilityMode() const { 437 if (web_contents_) 438 return static_cast<WebContentsImpl*>(web_contents_)->GetAccessibilityMode(); 439 else 440 return AccessibilityModeOff; 441 } 442 443 RenderViewHostDelegateView* InterstitialPageImpl::GetDelegateView() { 444 return rvh_delegate_view_.get(); 445 } 446 447 const GURL& InterstitialPageImpl::GetMainFrameLastCommittedURL() const { 448 return url_; 449 } 450 451 void InterstitialPageImpl::RenderViewTerminated( 452 RenderViewHost* render_view_host, 453 base::TerminationStatus status, 454 int error_code) { 455 // Our renderer died. This should not happen in normal cases. 456 // If we haven't already started shutdown, just dismiss the interstitial. 457 // We cannot check for enabled() here, because we may have called Disable 458 // without calling Hide. 459 if (render_view_host_) 460 DontProceed(); 461 } 462 463 void InterstitialPageImpl::DidNavigate( 464 RenderViewHost* render_view_host, 465 const FrameHostMsg_DidCommitProvisionalLoad_Params& params) { 466 // A fast user could have navigated away from the page that triggered the 467 // interstitial while the interstitial was loading, that would have disabled 468 // us. In that case we can dismiss ourselves. 469 if (!enabled()) { 470 DontProceed(); 471 return; 472 } 473 if (ui::PageTransitionCoreTypeIs(params.transition, 474 ui::PAGE_TRANSITION_AUTO_SUBFRAME)) { 475 // No need to handle navigate message from iframe in the interstitial page. 476 return; 477 } 478 479 // The RenderViewHost has loaded its contents, we can show it now. 480 if (!controller_->delegate()->IsHidden()) 481 render_view_host_->GetView()->Show(); 482 controller_->delegate()->AttachInterstitialPage(this); 483 484 RenderWidgetHostView* rwh_view = 485 controller_->delegate()->GetRenderViewHost()->GetView(); 486 487 // The RenderViewHost may already have crashed before we even get here. 488 if (rwh_view) { 489 // If the page has focus, focus the interstitial. 490 if (rwh_view->HasFocus()) 491 Focus(); 492 493 // Hide the original RVH since we're showing the interstitial instead. 494 rwh_view->Hide(); 495 } 496 497 // Notify the tab we are not loading so the throbber is stopped. It also 498 // causes a WebContentsObserver::DidStopLoading callback that the 499 // AutomationProvider (used by the UI tests) expects to consider a navigation 500 // as complete. Without this, navigating in a UI test to a URL that triggers 501 // an interstitial would hang. 502 web_contents_was_loading_ = controller_->delegate()->IsLoading(); 503 controller_->delegate()->SetIsLoading( 504 controller_->delegate()->GetRenderViewHost(), false, true, NULL); 505 } 506 507 RendererPreferences InterstitialPageImpl::GetRendererPrefs( 508 BrowserContext* browser_context) const { 509 delegate_->OverrideRendererPrefs(&renderer_preferences_); 510 return renderer_preferences_; 511 } 512 513 WebPreferences InterstitialPageImpl::ComputeWebkitPrefs() { 514 if (!enabled()) 515 return WebPreferences(); 516 517 return render_view_host_->ComputeWebkitPrefs(url_); 518 } 519 520 void InterstitialPageImpl::RenderWidgetDeleted( 521 RenderWidgetHostImpl* render_widget_host) { 522 // TODO(creis): Remove this method once we verify the shutdown path is sane. 523 CHECK(!web_contents_); 524 } 525 526 bool InterstitialPageImpl::PreHandleKeyboardEvent( 527 const NativeWebKeyboardEvent& event, 528 bool* is_keyboard_shortcut) { 529 if (!enabled()) 530 return false; 531 return render_widget_host_delegate_->PreHandleKeyboardEvent( 532 event, is_keyboard_shortcut); 533 } 534 535 void InterstitialPageImpl::HandleKeyboardEvent( 536 const NativeWebKeyboardEvent& event) { 537 if (enabled()) 538 render_widget_host_delegate_->HandleKeyboardEvent(event); 539 } 540 541 #if defined(OS_WIN) 542 gfx::NativeViewAccessible 543 InterstitialPageImpl::GetParentNativeViewAccessible() { 544 if (web_contents_) { 545 WebContentsImpl* wci = static_cast<WebContentsImpl*>(web_contents_); 546 return wci->GetParentNativeViewAccessible(); 547 } 548 return NULL; 549 } 550 #endif 551 552 WebContents* InterstitialPageImpl::web_contents() const { 553 return web_contents_; 554 } 555 556 RenderViewHost* InterstitialPageImpl::CreateRenderViewHost() { 557 if (!enabled()) 558 return NULL; 559 560 // Interstitial pages don't want to share the session storage so we mint a 561 // new one. 562 BrowserContext* browser_context = web_contents()->GetBrowserContext(); 563 scoped_refptr<SiteInstance> site_instance = 564 SiteInstance::Create(browser_context); 565 DOMStorageContextWrapper* dom_storage_context = 566 static_cast<DOMStorageContextWrapper*>( 567 BrowserContext::GetStoragePartition( 568 browser_context, site_instance.get())->GetDOMStorageContext()); 569 session_storage_namespace_ = 570 new SessionStorageNamespaceImpl(dom_storage_context); 571 572 // Use the RenderViewHost from our FrameTree. 573 frame_tree_.root()->render_manager()->Init( 574 browser_context, site_instance.get(), MSG_ROUTING_NONE, MSG_ROUTING_NONE); 575 return frame_tree_.root()->current_frame_host()->render_view_host(); 576 } 577 578 WebContentsView* InterstitialPageImpl::CreateWebContentsView() { 579 if (!enabled() || !create_view_) 580 return NULL; 581 WebContentsView* wcv = 582 static_cast<WebContentsImpl*>(web_contents())->GetView(); 583 RenderWidgetHostViewBase* view = 584 wcv->CreateViewForWidget(render_view_host_); 585 render_view_host_->SetView(view); 586 render_view_host_->AllowBindings(BINDINGS_POLICY_DOM_AUTOMATION); 587 588 int32 max_page_id = web_contents()-> 589 GetMaxPageIDForSiteInstance(render_view_host_->GetSiteInstance()); 590 render_view_host_->CreateRenderView(base::string16(), 591 MSG_ROUTING_NONE, 592 MSG_ROUTING_NONE, 593 max_page_id, 594 false); 595 controller_->delegate()->RenderFrameForInterstitialPageCreated( 596 frame_tree_.root()->current_frame_host()); 597 view->SetSize(web_contents()->GetContainerBounds().size()); 598 // Don't show the interstitial until we have navigated to it. 599 view->Hide(); 600 return wcv; 601 } 602 603 void InterstitialPageImpl::Proceed() { 604 // Don't repeat this if we are already shutting down. We cannot check for 605 // enabled() here, because we may have called Disable without calling Hide. 606 if (!render_view_host_) 607 return; 608 609 if (action_taken_ != NO_ACTION) { 610 NOTREACHED(); 611 return; 612 } 613 Disable(); 614 action_taken_ = PROCEED_ACTION; 615 616 // Resumes the throbber, if applicable. 617 if (web_contents_was_loading_) 618 controller_->delegate()->SetIsLoading( 619 controller_->delegate()->GetRenderViewHost(), true, true, NULL); 620 621 // If this is a new navigation, the old page is going away, so we cancel any 622 // blocked requests for it. If it is not a new navigation, then it means the 623 // interstitial was shown as a result of a resource loading in the page. 624 // Since the user wants to proceed, we'll let any blocked request go through. 625 if (new_navigation_) 626 TakeActionOnResourceDispatcher(CANCEL); 627 else 628 TakeActionOnResourceDispatcher(RESUME); 629 630 // No need to hide if we are a new navigation, we'll get hidden when the 631 // navigation is committed. 632 if (!new_navigation_) { 633 Hide(); 634 delegate_->OnProceed(); 635 return; 636 } 637 638 delegate_->OnProceed(); 639 } 640 641 void InterstitialPageImpl::DontProceed() { 642 // Don't repeat this if we are already shutting down. We cannot check for 643 // enabled() here, because we may have called Disable without calling Hide. 644 if (!render_view_host_) 645 return; 646 DCHECK(action_taken_ != DONT_PROCEED_ACTION); 647 648 Disable(); 649 action_taken_ = DONT_PROCEED_ACTION; 650 651 // If this is a new navigation, we are returning to the original page, so we 652 // resume blocked requests for it. If it is not a new navigation, then it 653 // means the interstitial was shown as a result of a resource loading in the 654 // page and we won't return to the original page, so we cancel blocked 655 // requests in that case. 656 if (new_navigation_) 657 TakeActionOnResourceDispatcher(RESUME); 658 else 659 TakeActionOnResourceDispatcher(CANCEL); 660 661 if (should_discard_pending_nav_entry_) { 662 // Since no navigation happens we have to discard the transient entry 663 // explicitely. Note that by calling DiscardNonCommittedEntries() we also 664 // discard the pending entry, which is what we want, since the navigation is 665 // cancelled. 666 controller_->DiscardNonCommittedEntries(); 667 } 668 669 if (reload_on_dont_proceed_) 670 controller_->Reload(true); 671 672 Hide(); 673 delegate_->OnDontProceed(); 674 } 675 676 void InterstitialPageImpl::CancelForNavigation() { 677 // The user is trying to navigate away. We should unblock the renderer and 678 // disable the interstitial, but keep it visible until the navigation 679 // completes. 680 Disable(); 681 // If this interstitial was shown for a new navigation, allow any navigations 682 // on the original page to resume (e.g., subresource requests, XHRs, etc). 683 // Otherwise, cancel the pending, possibly dangerous navigations. 684 if (new_navigation_) 685 TakeActionOnResourceDispatcher(RESUME); 686 else 687 TakeActionOnResourceDispatcher(CANCEL); 688 } 689 690 void InterstitialPageImpl::SetSize(const gfx::Size& size) { 691 if (!enabled()) 692 return; 693 #if !defined(OS_MACOSX) 694 // When a tab is closed, we might be resized after our view was NULLed 695 // (typically if there was an info-bar). 696 if (render_view_host_->GetView()) 697 render_view_host_->GetView()->SetSize(size); 698 #else 699 // TODO(port): Does Mac need to SetSize? 700 NOTIMPLEMENTED(); 701 #endif 702 } 703 704 void InterstitialPageImpl::Focus() { 705 // Focus the native window. 706 if (!enabled()) 707 return; 708 render_view_host_->GetView()->Focus(); 709 } 710 711 void InterstitialPageImpl::FocusThroughTabTraversal(bool reverse) { 712 if (!enabled()) 713 return; 714 render_view_host_->SetInitialFocus(reverse); 715 } 716 717 RenderWidgetHostView* InterstitialPageImpl::GetView() { 718 return render_view_host_->GetView(); 719 } 720 721 RenderViewHost* InterstitialPageImpl::GetRenderViewHostForTesting() const { 722 return render_view_host_; 723 } 724 725 #if defined(OS_ANDROID) 726 RenderViewHost* InterstitialPageImpl::GetRenderViewHost() const { 727 return render_view_host_; 728 } 729 #endif 730 731 InterstitialPageDelegate* InterstitialPageImpl::GetDelegateForTesting() { 732 return delegate_.get(); 733 } 734 735 void InterstitialPageImpl::DontCreateViewForTesting() { 736 create_view_ = false; 737 } 738 739 gfx::Rect InterstitialPageImpl::GetRootWindowResizerRect() const { 740 return gfx::Rect(); 741 } 742 743 void InterstitialPageImpl::CreateNewWindow( 744 int render_process_id, 745 int route_id, 746 int main_frame_route_id, 747 const ViewHostMsg_CreateWindow_Params& params, 748 SessionStorageNamespace* session_storage_namespace) { 749 NOTREACHED() << "InterstitialPage does not support showing popups yet."; 750 } 751 752 void InterstitialPageImpl::CreateNewWidget(int render_process_id, 753 int route_id, 754 blink::WebPopupType popup_type) { 755 NOTREACHED() << "InterstitialPage does not support showing drop-downs yet."; 756 } 757 758 void InterstitialPageImpl::CreateNewFullscreenWidget(int render_process_id, 759 int route_id) { 760 NOTREACHED() 761 << "InterstitialPage does not support showing full screen popups."; 762 } 763 764 void InterstitialPageImpl::ShowCreatedWindow(int route_id, 765 WindowOpenDisposition disposition, 766 const gfx::Rect& initial_pos, 767 bool user_gesture) { 768 NOTREACHED() << "InterstitialPage does not support showing popups yet."; 769 } 770 771 void InterstitialPageImpl::ShowCreatedWidget(int route_id, 772 const gfx::Rect& initial_pos) { 773 NOTREACHED() << "InterstitialPage does not support showing drop-downs yet."; 774 } 775 776 void InterstitialPageImpl::ShowCreatedFullscreenWidget(int route_id) { 777 NOTREACHED() 778 << "InterstitialPage does not support showing full screen popups."; 779 } 780 781 SessionStorageNamespace* InterstitialPageImpl::GetSessionStorageNamespace( 782 SiteInstance* instance) { 783 return session_storage_namespace_.get(); 784 } 785 786 FrameTree* InterstitialPageImpl::GetFrameTree() { 787 return &frame_tree_; 788 } 789 790 void InterstitialPageImpl::Disable() { 791 enabled_ = false; 792 } 793 794 void InterstitialPageImpl::Shutdown() { 795 delete this; 796 } 797 798 void InterstitialPageImpl::OnNavigatingAwayOrTabClosing() { 799 if (action_taken_ == NO_ACTION) { 800 // We are navigating away from the interstitial or closing a tab with an 801 // interstitial. Default to DontProceed(). We don't just call Hide as 802 // subclasses will almost certainly override DontProceed to do some work 803 // (ex: close pending connections). 804 DontProceed(); 805 } else { 806 // User decided to proceed and either the navigation was committed or 807 // the tab was closed before that. 808 Hide(); 809 } 810 } 811 812 void InterstitialPageImpl::TakeActionOnResourceDispatcher( 813 ResourceRequestAction action) { 814 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)) << 815 "TakeActionOnResourceDispatcher should be called on the main thread."; 816 817 if (action == CANCEL || action == RESUME) { 818 if (resource_dispatcher_host_notified_) 819 return; 820 resource_dispatcher_host_notified_ = true; 821 } 822 823 // The tab might not have a render_view_host if it was closed (in which case, 824 // we have taken care of the blocked requests when processing 825 // NOTIFY_RENDER_WIDGET_HOST_DESTROYED. 826 // Also we need to test there is a ResourceDispatcherHostImpl, as when unit- 827 // tests we don't have one. 828 RenderViewHostImpl* rvh = RenderViewHostImpl::FromID(original_child_id_, 829 original_rvh_id_); 830 if (!rvh || !ResourceDispatcherHostImpl::Get()) 831 return; 832 833 BrowserThread::PostTask( 834 BrowserThread::IO, 835 FROM_HERE, 836 base::Bind( 837 &ResourceRequestHelper, 838 ResourceDispatcherHostImpl::Get(), 839 original_child_id_, 840 original_rvh_id_, 841 action)); 842 } 843 844 void InterstitialPageImpl::OnDomOperationResponse( 845 const std::string& json_string, 846 int automation_id) { 847 // Needed by test code. 848 DomOperationNotificationDetails details(json_string, automation_id); 849 NotificationService::current()->Notify( 850 NOTIFICATION_DOM_OPERATION_RESPONSE, 851 Source<WebContents>(web_contents()), 852 Details<DomOperationNotificationDetails>(&details)); 853 854 if (!enabled()) 855 return; 856 delegate_->CommandReceived(details.json); 857 } 858 859 860 InterstitialPageImpl::InterstitialPageRVHDelegateView:: 861 InterstitialPageRVHDelegateView(InterstitialPageImpl* page) 862 : interstitial_page_(page) { 863 } 864 865 #if defined(OS_MACOSX) || defined(OS_ANDROID) 866 void InterstitialPageImpl::InterstitialPageRVHDelegateView::ShowPopupMenu( 867 RenderFrameHost* render_frame_host, 868 const gfx::Rect& bounds, 869 int item_height, 870 double item_font_size, 871 int selected_item, 872 const std::vector<MenuItem>& items, 873 bool right_aligned, 874 bool allow_multiple_selection) { 875 NOTREACHED() << "InterstitialPage does not support showing popup menus."; 876 } 877 878 void InterstitialPageImpl::InterstitialPageRVHDelegateView::HidePopupMenu() { 879 NOTREACHED() << "InterstitialPage does not support showing popup menus."; 880 } 881 #endif 882 883 void InterstitialPageImpl::InterstitialPageRVHDelegateView::StartDragging( 884 const DropData& drop_data, 885 WebDragOperationsMask allowed_operations, 886 const gfx::ImageSkia& image, 887 const gfx::Vector2d& image_offset, 888 const DragEventSourceInfo& event_info) { 889 interstitial_page_->render_view_host_->DragSourceSystemDragEnded(); 890 DVLOG(1) << "InterstitialPage does not support dragging yet."; 891 } 892 893 void InterstitialPageImpl::InterstitialPageRVHDelegateView::UpdateDragCursor( 894 WebDragOperation) { 895 NOTREACHED() << "InterstitialPage does not support dragging yet."; 896 } 897 898 void InterstitialPageImpl::InterstitialPageRVHDelegateView::GotFocus() { 899 WebContents* web_contents = interstitial_page_->web_contents(); 900 if (web_contents && web_contents->GetDelegate()) 901 web_contents->GetDelegate()->WebContentsFocused(web_contents); 902 } 903 904 void InterstitialPageImpl::InterstitialPageRVHDelegateView::TakeFocus( 905 bool reverse) { 906 if (!interstitial_page_->web_contents()) 907 return; 908 WebContentsImpl* web_contents = 909 static_cast<WebContentsImpl*>(interstitial_page_->web_contents()); 910 if (!web_contents->GetDelegateView()) 911 return; 912 913 web_contents->GetDelegateView()->TakeFocus(reverse); 914 } 915 916 void InterstitialPageImpl::InterstitialPageRVHDelegateView::OnFindReply( 917 int request_id, int number_of_matches, const gfx::Rect& selection_rect, 918 int active_match_ordinal, bool final_update) { 919 } 920 921 } // namespace content 922