Adding instrumentation to locate the source of jankiness.
[chromium-blink-merge.git] / chrome / browser / net / net_error_tab_helper.cc
blob82d2fe1948096590ef9d0bae61d95caf7d955bdb
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/net/net_error_tab_helper.h"
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/prefs/pref_service.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/io_thread.h"
12 #include "chrome/browser/net/dns_probe_service.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/common/pref_names.h"
15 #include "chrome/common/render_messages.h"
16 #include "components/error_page/common/net_error_info.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/render_frame_host.h"
19 #include "net/base/net_errors.h"
21 using chrome_common_net::DnsProbeStatus;
22 using chrome_common_net::DnsProbeStatusToString;
23 using content::BrowserContext;
24 using content::BrowserThread;
25 using ui::PageTransition;
26 using content::RenderViewHost;
27 using content::WebContents;
28 using content::WebContentsObserver;
30 DEFINE_WEB_CONTENTS_USER_DATA_KEY(chrome_browser_net::NetErrorTabHelper);
32 namespace chrome_browser_net {
34 namespace {
36 static NetErrorTabHelper::TestingState testing_state_ =
37 NetErrorTabHelper::TESTING_DEFAULT;
39 // Returns whether |net_error| is a DNS-related error (and therefore whether
40 // the tab helper should start a DNS probe after receiving it.)
41 bool IsDnsError(int net_error) {
42 return net_error == net::ERR_NAME_NOT_RESOLVED ||
43 net_error == net::ERR_NAME_RESOLUTION_FAILED;
46 void OnDnsProbeFinishedOnIOThread(
47 const base::Callback<void(DnsProbeStatus)>& callback,
48 DnsProbeStatus result) {
49 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
51 BrowserThread::PostTask(
52 BrowserThread::UI,
53 FROM_HERE,
54 base::Bind(callback, result));
57 // Can only access g_browser_process->io_thread() from the browser thread,
58 // so have to pass it in to the callback instead of dereferencing it here.
59 void StartDnsProbeOnIOThread(
60 const base::Callback<void(DnsProbeStatus)>& callback,
61 IOThread* io_thread) {
62 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
64 DnsProbeService* probe_service =
65 io_thread->globals()->dns_probe_service.get();
67 probe_service->ProbeDns(base::Bind(&OnDnsProbeFinishedOnIOThread, callback));
70 } // namespace
72 NetErrorTabHelper::~NetErrorTabHelper() {
75 // static
76 void NetErrorTabHelper::set_state_for_testing(TestingState state) {
77 testing_state_ = state;
80 void NetErrorTabHelper::DidStartNavigationToPendingEntry(
81 const GURL& url,
82 content::NavigationController::ReloadType reload_type) {
83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
85 if (!is_error_page_)
86 return;
88 // Only record reloads.
89 if (reload_type != content::NavigationController::NO_RELOAD) {
90 chrome_common_net::RecordEvent(
91 chrome_common_net::NETWORK_ERROR_PAGE_BROWSER_INITIATED_RELOAD);
95 void NetErrorTabHelper::DidStartProvisionalLoadForFrame(
96 content::RenderFrameHost* render_frame_host,
97 const GURL& validated_url,
98 bool is_error_page,
99 bool is_iframe_srcdoc) {
100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
102 if (render_frame_host->GetParent())
103 return;
105 is_error_page_ = is_error_page;
108 void NetErrorTabHelper::DidCommitProvisionalLoadForFrame(
109 content::RenderFrameHost* render_frame_host,
110 const GURL& url,
111 PageTransition transition_type) {
112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
114 if (render_frame_host->GetParent())
115 return;
117 // Resend status every time an error page commits; this is somewhat spammy,
118 // but ensures that the status will make it to the real error page, even if
119 // the link doctor loads a blank intermediate page or the tab switches
120 // renderer processes.
121 if (is_error_page_ && dns_error_active_) {
122 dns_error_page_committed_ = true;
123 DVLOG(1) << "Committed error page; resending status.";
124 SendInfo();
125 } else {
126 dns_error_active_ = false;
127 dns_error_page_committed_ = false;
131 void NetErrorTabHelper::DidFailProvisionalLoad(
132 content::RenderFrameHost* render_frame_host,
133 const GURL& validated_url,
134 int error_code,
135 const base::string16& error_description) {
136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
138 if (render_frame_host->GetParent())
139 return;
141 if (IsDnsError(error_code)) {
142 dns_error_active_ = true;
143 OnMainFrameDnsError();
147 NetErrorTabHelper::NetErrorTabHelper(WebContents* contents)
148 : WebContentsObserver(contents),
149 is_error_page_(false),
150 dns_error_active_(false),
151 dns_error_page_committed_(false),
152 dns_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE),
153 weak_factory_(this) {
154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
156 // If this helper is under test, it won't have a WebContents.
157 if (contents)
158 InitializePref(contents);
161 void NetErrorTabHelper::OnMainFrameDnsError() {
162 if (ProbesAllowed()) {
163 // Don't start more than one probe at a time.
164 if (dns_probe_status_ != chrome_common_net::DNS_PROBE_STARTED) {
165 StartDnsProbe();
166 dns_probe_status_ = chrome_common_net::DNS_PROBE_STARTED;
168 } else {
169 dns_probe_status_ = chrome_common_net::DNS_PROBE_NOT_RUN;
173 void NetErrorTabHelper::StartDnsProbe() {
174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
175 DCHECK(dns_error_active_);
176 DCHECK_NE(chrome_common_net::DNS_PROBE_STARTED, dns_probe_status_);
178 DVLOG(1) << "Starting DNS probe.";
180 BrowserThread::PostTask(
181 BrowserThread::IO,
182 FROM_HERE,
183 base::Bind(&StartDnsProbeOnIOThread,
184 base::Bind(&NetErrorTabHelper::OnDnsProbeFinished,
185 weak_factory_.GetWeakPtr()),
186 g_browser_process->io_thread()));
189 void NetErrorTabHelper::OnDnsProbeFinished(DnsProbeStatus result) {
190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
191 DCHECK_EQ(chrome_common_net::DNS_PROBE_STARTED, dns_probe_status_);
192 DCHECK(chrome_common_net::DnsProbeStatusIsFinished(result));
194 DVLOG(1) << "Finished DNS probe with result "
195 << DnsProbeStatusToString(result) << ".";
197 dns_probe_status_ = result;
199 if (dns_error_page_committed_)
200 SendInfo();
203 void NetErrorTabHelper::InitializePref(WebContents* contents) {
204 DCHECK(contents);
206 BrowserContext* browser_context = contents->GetBrowserContext();
207 Profile* profile = Profile::FromBrowserContext(browser_context);
208 resolve_errors_with_web_service_.Init(
209 prefs::kAlternateErrorPagesEnabled,
210 profile->GetPrefs());
213 bool NetErrorTabHelper::ProbesAllowed() const {
214 if (testing_state_ != TESTING_DEFAULT)
215 return testing_state_ == TESTING_FORCE_ENABLED;
217 // TODO(ttuttle): Disable on mobile?
218 return *resolve_errors_with_web_service_;
221 void NetErrorTabHelper::SendInfo() {
222 DCHECK_NE(chrome_common_net::DNS_PROBE_POSSIBLE, dns_probe_status_);
223 DCHECK(dns_error_page_committed_);
225 DVLOG(1) << "Sending status " << DnsProbeStatusToString(dns_probe_status_);
226 content::RenderFrameHost* rfh = web_contents()->GetMainFrame();
227 rfh->Send(new ChromeViewMsg_NetErrorInfo(rfh->GetRoutingID(),
228 dns_probe_status_));
230 if (!dns_probe_status_snoop_callback_.is_null())
231 dns_probe_status_snoop_callback_.Run(dns_probe_status_);
234 } // namespace chrome_browser_net