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