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