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