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