Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / ui / omnibox / chrome_omnibox_navigation_observer.cc
bloba156a77775235a891080b3db1e5beec3b7724f4c
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/chrome_omnibox_navigation_observer.h"
7 #include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
8 #include "chrome/browser/infobars/infobar_service.h"
9 #include "chrome/browser/intranet_redirect_detector.h"
10 #include "chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.h"
11 #include "components/omnibox/browser/shortcuts_backend.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/render_frame_host.h"
19 #include "content/public/browser/web_contents.h"
20 #include "net/base/load_flags.h"
21 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
22 #include "net/url_request/url_fetcher.h"
23 #include "net/url_request/url_request.h"
26 // Helpers --------------------------------------------------------------------
28 namespace {
30 // HTTP 2xx, 401, and 407 all indicate that the target address exists.
31 bool ResponseCodeIndicatesSuccess(int response_code) {
32 return ((response_code / 100) == 2) || (response_code == 401) ||
33 (response_code == 407);
36 // Returns true if |final_url| doesn't represent an ISP hijack of
37 // |original_url|, based on the IntranetRedirectDetector's RedirectOrigin().
38 bool IsValidNavigation(const GURL& original_url, const GURL& final_url) {
39 const GURL& redirect_url(IntranetRedirectDetector::RedirectOrigin());
40 return !redirect_url.is_valid() ||
41 net::registry_controlled_domains::SameDomainOrHost(
42 original_url, final_url,
43 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES) ||
44 !net::registry_controlled_domains::SameDomainOrHost(
45 final_url, redirect_url,
46 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
49 } // namespace
51 // ChromeOmniboxNavigationObserver --------------------------------------------
53 ChromeOmniboxNavigationObserver::ChromeOmniboxNavigationObserver(
54 Profile* profile,
55 const base::string16& text,
56 const AutocompleteMatch& match,
57 const AutocompleteMatch& alternate_nav_match)
58 : text_(text),
59 match_(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_ = 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 ChromeOmniboxNavigationObserver::~ChromeOmniboxNavigationObserver() {}
78 void ChromeOmniboxNavigationObserver::OnSuccessfulNavigation() {
79 if (shortcuts_backend_.get())
80 shortcuts_backend_->AddOrUpdateShortcut(text_, match_);
83 bool ChromeOmniboxNavigationObserver::HasSeenPendingLoad() const {
84 return load_state_ != LOAD_NOT_SEEN;
87 void ChromeOmniboxNavigationObserver::Observe(
88 int type,
89 const content::NotificationSource& source,
90 const content::NotificationDetails& details) {
91 DCHECK_EQ(content::NOTIFICATION_NAV_ENTRY_PENDING, type);
93 // It's possible for an attempted omnibox navigation to cause the extensions
94 // system to synchronously navigate an extension background page. Not only is
95 // this navigation not the one we want to observe, the associated WebContents
96 // is invisible and has no InfoBarService, so trying to show an infobar in it
97 // later will crash. Just ignore this navigation and keep listening.
98 content::NavigationController* controller =
99 content::Source<content::NavigationController>(source).ptr();
100 content::WebContents* web_contents = controller->GetWebContents();
101 if (!InfoBarService::FromWebContents(web_contents))
102 return;
104 // Ignore navgiations to the wrong URL.
105 // This shouldn't actually happen, but right now it's possible because the
106 // prerenderer doesn't properly notify us when it swaps in a prerendered page.
107 // Plus, the swap-in can trigger instant to kick off a new background
108 // prerender, which we _do_ get notified about. Once crbug.com/247848 is
109 // fixed, this conditional should be able to be replaced with a [D]CHECK;
110 // until then we ignore the incorrect navigation (and will be torn down
111 // without having received the correct notification).
112 if (match_.destination_url !=
113 content::Details<content::NavigationEntry>(details)->GetVirtualURL())
114 return;
116 registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_PENDING,
117 content::NotificationService::AllSources());
118 if (fetcher_) {
119 fetcher_->SetRequestContext(
120 controller->GetBrowserContext()->GetRequestContext());
122 WebContentsObserver::Observe(web_contents);
123 // DidStartNavigationToPendingEntry() will be called for this load as well.
126 void ChromeOmniboxNavigationObserver::DidStartNavigationToPendingEntry(
127 const GURL& url,
128 content::NavigationController::ReloadType reload_type) {
129 if (load_state_ == LOAD_NOT_SEEN) {
130 load_state_ = LOAD_PENDING;
131 if (fetcher_)
132 fetcher_->Start();
133 } else {
134 delete this;
138 void ChromeOmniboxNavigationObserver::DidFailProvisionalLoad(
139 content::RenderFrameHost* render_frame_host,
140 const GURL& validated_url,
141 int error_code,
142 const base::string16& error_description,
143 bool was_ignored_by_handler) {
144 if ((load_state_ != LOAD_COMMITTED) && !render_frame_host->GetParent())
145 delete this;
148 void ChromeOmniboxNavigationObserver::NavigationEntryCommitted(
149 const content::LoadCommittedDetails& load_details) {
150 load_state_ = LOAD_COMMITTED;
151 if (ResponseCodeIndicatesSuccess(load_details.http_status_code) &&
152 IsValidNavigation(match_.destination_url,
153 load_details.entry->GetVirtualURL()))
154 OnSuccessfulNavigation();
155 if (!fetcher_ || (fetch_state_ != FETCH_NOT_COMPLETE))
156 OnAllLoadingFinished(); // deletes |this|!
159 void ChromeOmniboxNavigationObserver::WebContentsDestroyed() {
160 delete this;
163 void ChromeOmniboxNavigationObserver::OnURLFetchComplete(
164 const net::URLFetcher* source) {
165 DCHECK_EQ(fetcher_.get(), source);
166 const net::URLRequestStatus& status = source->GetStatus();
167 int response_code = source->GetResponseCode();
168 fetch_state_ =
169 (status.is_success() && ResponseCodeIndicatesSuccess(response_code)) ||
170 ((status.status() == net::URLRequestStatus::CANCELED) &&
171 ((response_code / 100) == 3) &&
172 IsValidNavigation(alternate_nav_match_.destination_url,
173 source->GetURL()))
174 ? FETCH_SUCCEEDED
175 : FETCH_FAILED;
176 if (load_state_ == LOAD_COMMITTED)
177 OnAllLoadingFinished(); // deletes |this|!
180 void ChromeOmniboxNavigationObserver::OnAllLoadingFinished() {
181 if (fetch_state_ == FETCH_SUCCEEDED) {
182 AlternateNavInfoBarDelegate::Create(
183 web_contents(), text_, alternate_nav_match_, match_.destination_url);
185 delete this;