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.
5 #include "chrome/browser/ui/omnibox/alternate_nav_url_fetcher.h"
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"
22 using content::NavigationController
;
24 AlternateNavURLFetcher::AlternateNavURLFetcher(
25 const GURL
& alternate_nav_url
)
26 : alternate_nav_url_(alternate_nav_url
),
29 navigated_to_entry_(false) {
30 registrar_
.Add(this, content::NOTIFICATION_NAV_ENTRY_PENDING
,
31 content::NotificationService::AllSources());
34 AlternateNavURLFetcher::~AlternateNavURLFetcher() {
37 void AlternateNavURLFetcher::Observe(
39 const content::NotificationSource
& source
,
40 const content::NotificationDetails
& details
) {
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
) {
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
>(
56 StartFetch(controller
);
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!
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.
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!
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
));
97 DCHECK_EQ(NOT_STARTED
, state_
);
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);
108 void AlternateNavURLFetcher::SetStatusFromURLFetch(
110 const net::URLRequestStatus
& status
,
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
;
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(
126 IntranetRedirectDetector::RedirectOrigin(),
127 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES
);
128 state_
= same_domain_or_host
? FAILED
: SUCCEEDED
;
135 void AlternateNavURLFetcher::ShowInfobarIfPossible() {
136 if (navigated_to_entry_
&& (state_
== SUCCEEDED
)) {
137 AlternateNavInfoBarDelegate::Create(
138 InfoBarService::FromWebContents(controller_
->GetWebContents()),
140 } else if (state_
!= FAILED
) {