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