Home | History | Annotate | Download | only in omnibox
      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 "chrome/browser/ui/omnibox/alternate_nav_url_fetcher.h"
      6 
      7 #include "chrome/browser/chrome_notification_types.h"
      8 #include "chrome/browser/infobars/infobar_service.h"
      9 #include "chrome/browser/intranet_redirect_detector.h"
     10 #include "chrome/browser/profiles/profile.h"
     11 #include "chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.h"
     12 #include "content/public/browser/navigation_controller.h"
     13 #include "content/public/browser/notification_service.h"
     14 #include "content/public/browser/render_process_host.h"
     15 #include "content/public/browser/render_view_host.h"
     16 #include "content/public/browser/web_contents.h"
     17 #include "net/base/load_flags.h"
     18 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
     19 #include "net/url_request/url_fetcher.h"
     20 #include "net/url_request/url_request.h"
     21 
     22 using content::NavigationController;
     23 
     24 AlternateNavURLFetcher::AlternateNavURLFetcher(
     25     const GURL& alternate_nav_url)
     26     : alternate_nav_url_(alternate_nav_url),
     27       controller_(NULL),
     28       state_(NOT_STARTED),
     29       navigated_to_entry_(false) {
     30   registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_PENDING,
     31                  content::NotificationService::AllSources());
     32 }
     33 
     34 AlternateNavURLFetcher::~AlternateNavURLFetcher() {
     35 }
     36 
     37 void AlternateNavURLFetcher::Observe(
     38     int type,
     39     const content::NotificationSource& source,
     40     const content::NotificationDetails& details) {
     41   switch (type) {
     42     case content::NOTIFICATION_NAV_ENTRY_PENDING: {
     43       // If we've already received a notification for the same controller, we
     44       // should delete ourselves as that indicates that the page is being
     45       // re-loaded so this instance is now stale.
     46       NavigationController* controller =
     47           content::Source<NavigationController>(source).ptr();
     48       if (controller_ == controller) {
     49         delete this;
     50       } else if (!controller_) {
     51         // Start listening for the commit notification.
     52         DCHECK(controller->GetPendingEntry());
     53         registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
     54                        content::Source<NavigationController>(
     55                           controller));
     56         StartFetch(controller);
     57       }
     58       break;
     59     }
     60 
     61     case content::NOTIFICATION_NAV_ENTRY_COMMITTED:
     62       // The page was navigated, we can show the infobar now if necessary.
     63       registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
     64                         content::Source<NavigationController>(controller_));
     65       navigated_to_entry_ = true;
     66       ShowInfobarIfPossible();
     67       // WARNING: |this| may be deleted!
     68       break;
     69 
     70     case content::NOTIFICATION_WEB_CONTENTS_DESTROYED:
     71       // We have been closed. In order to prevent the URLFetcher from trying to
     72       // access the controller that will be invalid, we delete ourselves.
     73       // This deletes the URLFetcher and insures its callback won't be called.
     74       delete this;
     75       break;
     76 
     77     default:
     78       NOTREACHED();
     79   }
     80 }
     81 
     82 void AlternateNavURLFetcher::OnURLFetchComplete(
     83     const net::URLFetcher* source) {
     84   DCHECK_EQ(fetcher_.get(), source);
     85   SetStatusFromURLFetch(
     86       source->GetURL(), source->GetStatus(), source->GetResponseCode());
     87   ShowInfobarIfPossible();
     88   // WARNING: |this| may be deleted!
     89 }
     90 
     91 void AlternateNavURLFetcher::StartFetch(NavigationController* controller) {
     92   controller_ = controller;
     93   content::WebContents* web_contents = controller_->GetWebContents();
     94   registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
     95                  content::Source<content::WebContents>(web_contents));
     96 
     97   DCHECK_EQ(NOT_STARTED, state_);
     98   state_ = IN_PROGRESS;
     99   fetcher_.reset(net::URLFetcher::Create(GURL(alternate_nav_url_),
    100                                          net::URLFetcher::HEAD, this));
    101   fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
    102   fetcher_->SetRequestContext(
    103       controller_->GetBrowserContext()->GetRequestContext());
    104   fetcher_->SetStopOnRedirect(true);
    105   fetcher_->Start();
    106 }
    107 
    108 void AlternateNavURLFetcher::SetStatusFromURLFetch(
    109     const GURL& url,
    110     const net::URLRequestStatus& status,
    111     int response_code) {
    112   if (status.is_success()) {
    113     // HTTP 2xx, 401, and 407 all indicate that the target address exists.
    114     state_ = (((response_code / 100) == 2) || (response_code == 401) ||
    115         (response_code == 407)) ? SUCCEEDED : FAILED;
    116   } else {
    117     // If we got HTTP 3xx, we'll have auto-canceled; treat this as evidence the
    118     // target address exists as long as we're not redirected to a common
    119     // location every time, lest we annoy the user with infobars on everything
    120     // they type.  See comments on IntranetRedirectDetector.
    121     if (status.status() == net::URLRequestStatus::CANCELED &&
    122         (response_code / 100) == 3) {
    123       const bool same_domain_or_host =
    124           net::registry_controlled_domains::SameDomainOrHost(
    125               url,
    126               IntranetRedirectDetector::RedirectOrigin(),
    127               net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
    128       state_ = same_domain_or_host ? FAILED : SUCCEEDED;
    129     } else {
    130       state_ = FAILED;
    131     }
    132   }
    133 }
    134 
    135 void AlternateNavURLFetcher::ShowInfobarIfPossible() {
    136   if (navigated_to_entry_ && (state_ == SUCCEEDED)) {
    137     AlternateNavInfoBarDelegate::Create(
    138         InfoBarService::FromWebContents(controller_->GetWebContents()),
    139         alternate_nav_url_);
    140   } else if (state_ != FAILED) {
    141     return;
    142   }
    143   delete this;
    144 }
    145