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