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/omnibox_navigation_observer.h"
7 #include "chrome/browser/autocomplete/shortcuts_backend.h"
8 #include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
9 #include "chrome/browser/infobars/infobar_service.h"
10 #include "chrome/browser/intranet_redirect_detector.h"
11 #include "chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.h"
12 #include "content/public/browser/browser_context.h"
13 #include "content/public/browser/navigation_controller.h"
14 #include "content/public/browser/navigation_details.h"
15 #include "content/public/browser/navigation_entry.h"
16 #include "content/public/browser/notification_service.h"
17 #include "content/public/browser/notification_types.h"
18 #include "content/public/browser/web_contents.h"
19 #include "net/base/load_flags.h"
20 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
21 #include "net/url_request/url_fetcher.h"
22 #include "net/url_request/url_request.h"
25 // Helpers --------------------------------------------------------------------
29 // HTTP 2xx, 401, and 407 all indicate that the target address exists.
30 bool ResponseCodeIndicatesSuccess(int response_code
) {
31 return ((response_code
/ 100) == 2) || (response_code
== 401) ||
32 (response_code
== 407);
35 // Returns true if |final_url| doesn't represent an ISP hijack of
36 // |original_url|, based on the IntranetRedirectDetector's RedirectOrigin().
37 bool IsValidNavigation(const GURL
& original_url
, const GURL
& final_url
) {
38 const GURL
& redirect_url(IntranetRedirectDetector::RedirectOrigin());
39 return !redirect_url
.is_valid() ||
40 net::registry_controlled_domains::SameDomainOrHost(
41 original_url
, final_url
,
42 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES
) ||
43 !net::registry_controlled_domains::SameDomainOrHost(
44 final_url
, redirect_url
,
45 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES
);
51 // OmniboxNavigationObserver --------------------------------------------------
53 OmniboxNavigationObserver::OmniboxNavigationObserver(
55 const base::string16
& text
,
56 const AutocompleteMatch
& match
,
57 const AutocompleteMatch
& alternate_nav_match
)
60 alternate_nav_match_(alternate_nav_match
),
61 shortcuts_backend_(ShortcutsBackendFactory::GetForProfile(profile
)),
62 load_state_(LOAD_NOT_SEEN
),
63 fetch_state_(FETCH_NOT_COMPLETE
) {
64 if (alternate_nav_match_
.destination_url
.is_valid()) {
65 fetcher_
.reset(net::URLFetcher::Create(alternate_nav_match_
.destination_url
,
66 net::URLFetcher::HEAD
, this));
67 fetcher_
->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES
);
68 fetcher_
->SetStopOnRedirect(true);
70 // We need to start by listening to AllSources, since we don't know which tab
71 // the navigation might occur in.
72 registrar_
.Add(this, content::NOTIFICATION_NAV_ENTRY_PENDING
,
73 content::NotificationService::AllSources());
76 OmniboxNavigationObserver::~OmniboxNavigationObserver() {
79 void OmniboxNavigationObserver::OnSuccessfulNavigation() {
80 if (shortcuts_backend_
)
81 shortcuts_backend_
->AddOrUpdateShortcut(text_
, match_
);
84 void OmniboxNavigationObserver::Observe(
86 const content::NotificationSource
& source
,
87 const content::NotificationDetails
& details
) {
88 DCHECK_EQ(content::NOTIFICATION_NAV_ENTRY_PENDING
, type
);
89 registrar_
.Remove(this, content::NOTIFICATION_NAV_ENTRY_PENDING
,
90 content::NotificationService::AllSources());
91 content::NavigationController
* controller
=
92 content::Source
<content::NavigationController
>(source
).ptr();
94 fetcher_
->SetRequestContext(
95 controller
->GetBrowserContext()->GetRequestContext());
97 WebContentsObserver::Observe(controller
->GetWebContents());
98 // DidStartNavigationToPendingEntry() will be called for this load as well.
101 void OmniboxNavigationObserver::NavigationEntryCommitted(
102 const content::LoadCommittedDetails
& load_details
) {
103 load_state_
= LOAD_COMMITTED
;
104 if (ResponseCodeIndicatesSuccess(load_details
.http_status_code
) &&
105 IsValidNavigation(match_
.destination_url
, load_details
.entry
->GetURL()))
106 OnSuccessfulNavigation();
107 if (!fetcher_
|| (fetch_state_
!= FETCH_NOT_COMPLETE
))
108 OnAllLoadingFinished(); // deletes |this|!
111 void OmniboxNavigationObserver::WebContentsDestroyed() {
115 void OmniboxNavigationObserver::DidStartNavigationToPendingEntry(
117 content::NavigationController::ReloadType reload_type
) {
118 if (load_state_
== LOAD_NOT_SEEN
) {
119 load_state_
= LOAD_PENDING
;
127 void OmniboxNavigationObserver::OnURLFetchComplete(
128 const net::URLFetcher
* source
) {
129 DCHECK_EQ(fetcher_
.get(), source
);
130 const net::URLRequestStatus
& status
= source
->GetStatus();
131 int response_code
= source
->GetResponseCode();
133 (status
.is_success() && ResponseCodeIndicatesSuccess(response_code
)) ||
134 ((status
.status() == net::URLRequestStatus::CANCELED
) &&
135 ((response_code
/ 100) == 3) &&
136 IsValidNavigation(alternate_nav_match_
.destination_url
,
138 FETCH_SUCCEEDED
: FETCH_FAILED
;
139 if (load_state_
== LOAD_COMMITTED
)
140 OnAllLoadingFinished(); // deletes |this|!
143 void OmniboxNavigationObserver::OnAllLoadingFinished() {
144 if (fetch_state_
== FETCH_SUCCEEDED
) {
145 AlternateNavInfoBarDelegate::Create(
146 web_contents(), text_
, alternate_nav_match_
, match_
.destination_url
);