• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AlternateNavURLFetcher(const GURL & alternate_nav_url)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 
~AlternateNavURLFetcher()33 AlternateNavURLFetcher::~AlternateNavURLFetcher() {}
34 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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 
OnURLFetchComplete(const URLFetcher * source,const GURL & url,const net::URLRequestStatus & status,int response_code,const ResponseCookies & cookies,const std::string & data)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 
GetIcon() const101 SkBitmap* AlternateNavURLFetcher::GetIcon() const {
102   return ResourceBundle::GetSharedInstance().GetBitmapNamed(
103       IDR_INFOBAR_ALT_NAV_URL);
104 }
105 
GetInfoBarType() const106 InfoBarDelegate::Type AlternateNavURLFetcher::GetInfoBarType() const {
107   return PAGE_ACTION_TYPE;
108 }
109 
GetMessageTextWithOffset(size_t * link_offset) const110 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 
GetLinkText() const117 string16 AlternateNavURLFetcher::GetLinkText() const {
118   return UTF8ToUTF16(alternate_nav_url_.spec());
119 }
120 
LinkClicked(WindowOpenDisposition disposition)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 
InfoBarClosed()134 void AlternateNavURLFetcher::InfoBarClosed() {
135   delete this;
136 }
137 
SetStatusFromURLFetch(const GURL & url,const net::URLRequestStatus & status,int response_code)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 
ShowInfobarIfPossible()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