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