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