Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2011 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/alternate_nav_url_fetcher.h"
      6 
      7 #include "base/utf_string_conversions.h"
      8 #include "chrome/browser/intranet_redirect_detector.h"
      9 #include "chrome/browser/profiles/profile.h"
     10 #include "content/browser/tab_contents/navigation_controller.h"
     11 #include "content/browser/tab_contents/navigation_entry.h"
     12 #include "content/browser/tab_contents/tab_contents.h"
     13 #include "content/common/notification_service.h"
     14 #include "grit/generated_resources.h"
     15 #include "grit/theme_resources.h"
     16 #include "net/base/registry_controlled_domain.h"
     17 #include "net/url_request/url_request.h"
     18 #include "ui/base/l10n/l10n_util.h"
     19 #include "ui/base/resource/resource_bundle.h"
     20 
     21 AlternateNavURLFetcher::AlternateNavURLFetcher(
     22     const GURL& alternate_nav_url)
     23     : LinkInfoBarDelegate(NULL),
     24       alternate_nav_url_(alternate_nav_url),
     25       controller_(NULL),
     26       state_(NOT_STARTED),
     27       navigated_to_entry_(false),
     28       infobar_contents_(NULL) {
     29   registrar_.Add(this, NotificationType::NAV_ENTRY_PENDING,
     30                  NotificationService::AllSources());
     31 }
     32 
     33 AlternateNavURLFetcher::~AlternateNavURLFetcher() {}
     34 
     35 void AlternateNavURLFetcher::Observe(NotificationType type,
     36                                      const NotificationSource& source,
     37                                      const NotificationDetails& details) {
     38   switch (type.value) {
     39     case NotificationType::NAV_ENTRY_PENDING:
     40       // If we've already received a notification for the same controller, we
     41       // should delete ourselves as that indicates that the page is being
     42       // re-loaded so this instance is now stale.
     43       // http://crbug.com/43378
     44       if (!infobar_contents_ &&
     45           controller_ == Source<NavigationController>(source).ptr()) {
     46         delete this;
     47       } else if (!controller_) {
     48         controller_ = Source<NavigationController>(source).ptr();
     49         DCHECK(controller_->pending_entry());
     50 
     51         // Start listening for the commit notification. We also need to listen
     52         // for the tab close command since that means the load will never
     53         // commit!
     54         registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
     55                        Source<NavigationController>(controller_));
     56         registrar_.Add(this, NotificationType::TAB_CLOSED,
     57                        Source<NavigationController>(controller_));
     58 
     59         DCHECK_EQ(NOT_STARTED, state_);
     60         state_ = IN_PROGRESS;
     61         fetcher_.reset(new URLFetcher(GURL(alternate_nav_url_),
     62                                       URLFetcher::HEAD, this));
     63         fetcher_->set_request_context(
     64             controller_->profile()->GetRequestContext());
     65         fetcher_->Start();
     66       }
     67       break;
     68 
     69     case NotificationType::NAV_ENTRY_COMMITTED:
     70       // The page was navigated, we can show the infobar now if necessary.
     71       registrar_.Remove(this, NotificationType::NAV_ENTRY_COMMITTED,
     72                         Source<NavigationController>(controller_));
     73       navigated_to_entry_ = true;
     74       ShowInfobarIfPossible();
     75       break;
     76 
     77     case NotificationType::TAB_CLOSED:
     78       // We have been closed. In order to prevent the URLFetcher from trying to
     79       // access the controller that will be invalid, we delete ourselves.
     80       // This deletes the URLFetcher and insures its callback won't be called.
     81       delete this;
     82       break;
     83 
     84     default:
     85       NOTREACHED();
     86   }
     87 }
     88 
     89 void AlternateNavURLFetcher::OnURLFetchComplete(
     90     const URLFetcher* source,
     91     const GURL& url,
     92     const net::URLRequestStatus& status,
     93     int response_code,
     94     const ResponseCookies& cookies,
     95     const std::string& data) {
     96   DCHECK_EQ(fetcher_.get(), source);
     97   SetStatusFromURLFetch(url, status, response_code);
     98   ShowInfobarIfPossible();
     99 }
    100 
    101 SkBitmap* AlternateNavURLFetcher::GetIcon() const {
    102   return ResourceBundle::GetSharedInstance().GetBitmapNamed(
    103       IDR_INFOBAR_ALT_NAV_URL);
    104 }
    105 
    106 InfoBarDelegate::Type AlternateNavURLFetcher::GetInfoBarType() const {
    107   return PAGE_ACTION_TYPE;
    108 }
    109 
    110 string16 AlternateNavURLFetcher::GetMessageTextWithOffset(
    111     size_t* link_offset) const {
    112   const string16 label = l10n_util::GetStringFUTF16(
    113       IDS_ALTERNATE_NAV_URL_VIEW_LABEL, string16(), link_offset);
    114   return label;
    115 }
    116 
    117 string16 AlternateNavURLFetcher::GetLinkText() const {
    118   return UTF8ToUTF16(alternate_nav_url_.spec());
    119 }
    120 
    121 bool AlternateNavURLFetcher::LinkClicked(WindowOpenDisposition disposition) {
    122   infobar_contents_->OpenURL(
    123       alternate_nav_url_, GURL(), disposition,
    124       // Pretend the user typed this URL, so that navigating to
    125       // it will be the default action when it's typed again in
    126       // the future.
    127       PageTransition::TYPED);
    128 
    129   // We should always close, even if the navigation did not occur within this
    130   // TabContents.
    131   return true;
    132 }
    133 
    134 void AlternateNavURLFetcher::InfoBarClosed() {
    135   delete this;
    136 }
    137 
    138 void AlternateNavURLFetcher::SetStatusFromURLFetch(
    139     const GURL& url,
    140     const net::URLRequestStatus& status,
    141     int response_code) {
    142   if (!status.is_success() ||
    143       // HTTP 2xx, 401, and 407 all indicate that the target address exists.
    144       (((response_code / 100) != 2) &&
    145        (response_code != 401) && (response_code != 407)) ||
    146       // Fail if we're redirected to a common location.
    147       // This happens for ISPs/DNS providers/etc. who return
    148       // provider-controlled pages to arbitrary user navigation attempts.
    149       // Because this can result in infobars on large fractions of user
    150       // searches, we don't show automatic infobars for these.  Note that users
    151       // can still choose to explicitly navigate to or search for pages in
    152       // these domains, and can still get infobars for cases that wind up on
    153       // other domains (e.g. legit intranet sites), we're just trying to avoid
    154       // erroneously harassing the user with our own UI prompts.
    155       net::RegistryControlledDomainService::SameDomainOrHost(url,
    156           IntranetRedirectDetector::RedirectOrigin())) {
    157     state_ = FAILED;
    158     return;
    159   }
    160   state_ = SUCCEEDED;
    161 }
    162 
    163 void AlternateNavURLFetcher::ShowInfobarIfPossible() {
    164   if (!navigated_to_entry_ || state_ != SUCCEEDED) {
    165     if (state_ == FAILED)
    166       delete this;
    167     return;
    168   }
    169 
    170   infobar_contents_ = controller_->tab_contents();
    171   StoreActiveEntryUniqueID(infobar_contents_);
    172   infobar_contents_->AddInfoBar(this);
    173 }
    174