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