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"
8 #include "base/prefs/pref_service.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/io_thread.h"
11 #include "chrome/browser/net/dns_probe_service.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/common/net/net_error_info.h"
14 #include "chrome/common/pref_names.h"
15 #include "chrome/common/render_messages.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/render_frame_host.h"
18 #include "net/base/net_errors.h"
20 using chrome_common_net::DnsProbeStatus
;
21 using chrome_common_net::DnsProbeStatusToString
;
22 using content::BrowserContext
;
23 using content::BrowserThread
;
24 using content::PageTransition
;
25 using content::RenderViewHost
;
26 using content::WebContents
;
27 using content::WebContentsObserver
;
29 DEFINE_WEB_CONTENTS_USER_DATA_KEY(chrome_browser_net::NetErrorTabHelper
);
31 namespace chrome_browser_net
{
35 static NetErrorTabHelper::TestingState testing_state_
=
36 NetErrorTabHelper::TESTING_DEFAULT
;
38 // Returns whether |net_error| is a DNS-related error (and therefore whether
39 // the tab helper should start a DNS probe after receiving it.)
40 bool IsDnsError(int net_error
) {
41 return net_error
== net::ERR_NAME_NOT_RESOLVED
||
42 net_error
== net::ERR_NAME_RESOLUTION_FAILED
;
45 void OnDnsProbeFinishedOnIOThread(
46 const base::Callback
<void(DnsProbeStatus
)>& callback
,
47 DnsProbeStatus result
) {
48 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
50 BrowserThread::PostTask(
53 base::Bind(callback
, result
));
56 // Can only access g_browser_process->io_thread() from the browser thread,
57 // so have to pass it in to the callback instead of dereferencing it here.
58 void StartDnsProbeOnIOThread(
59 const base::Callback
<void(DnsProbeStatus
)>& callback
,
60 IOThread
* io_thread
) {
61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
63 DnsProbeService
* probe_service
=
64 io_thread
->globals()->dns_probe_service
.get();
66 probe_service
->ProbeDns(base::Bind(&OnDnsProbeFinishedOnIOThread
, callback
));
71 NetErrorTabHelper::~NetErrorTabHelper() {
75 void NetErrorTabHelper::set_state_for_testing(TestingState state
) {
76 testing_state_
= state
;
79 void NetErrorTabHelper::DidStartProvisionalLoadForFrame(
81 int64 parent_frame_id
,
83 const GURL
& validated_url
,
85 bool is_iframe_srcdoc
,
86 RenderViewHost
* render_view_host
) {
87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
92 is_error_page_
= is_error_page
;
95 void NetErrorTabHelper::DidCommitProvisionalLoadForFrame(
97 const base::string16
& frame_unique_name
,
100 PageTransition transition_type
,
101 RenderViewHost
* render_view_host
) {
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
107 // Resend status every time an error page commits; this is somewhat spammy,
108 // but ensures that the status will make it to the real error page, even if
109 // the link doctor loads a blank intermediate page or the tab switches
110 // renderer processes.
111 if (is_error_page_
&& dns_error_active_
) {
112 dns_error_page_committed_
= true;
113 DVLOG(1) << "Committed error page; resending status.";
116 dns_error_active_
= false;
117 dns_error_page_committed_
= false;
121 void NetErrorTabHelper::DidFailProvisionalLoad(
123 const base::string16
& frame_unique_name
,
125 const GURL
& validated_url
,
127 const base::string16
& error_description
,
128 RenderViewHost
* render_view_host
) {
129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
134 if (IsDnsError(error_code
)) {
135 dns_error_active_
= true;
136 OnMainFrameDnsError();
140 NetErrorTabHelper::NetErrorTabHelper(WebContents
* contents
)
141 : WebContentsObserver(contents
),
143 is_error_page_(false),
144 dns_error_active_(false),
145 dns_error_page_committed_(false),
146 dns_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE
) {
147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
149 // If this helper is under test, it won't have a WebContents.
151 InitializePref(contents
);
154 void NetErrorTabHelper::OnMainFrameDnsError() {
155 if (ProbesAllowed()) {
156 // Don't start more than one probe at a time.
157 if (dns_probe_status_
!= chrome_common_net::DNS_PROBE_STARTED
) {
159 dns_probe_status_
= chrome_common_net::DNS_PROBE_STARTED
;
162 dns_probe_status_
= chrome_common_net::DNS_PROBE_NOT_RUN
;
166 void NetErrorTabHelper::StartDnsProbe() {
167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
168 DCHECK(dns_error_active_
);
169 DCHECK_NE(chrome_common_net::DNS_PROBE_STARTED
, dns_probe_status_
);
171 DVLOG(1) << "Starting DNS probe.";
173 BrowserThread::PostTask(
176 base::Bind(&StartDnsProbeOnIOThread
,
177 base::Bind(&NetErrorTabHelper::OnDnsProbeFinished
,
178 weak_factory_
.GetWeakPtr()),
179 g_browser_process
->io_thread()));
182 void NetErrorTabHelper::OnDnsProbeFinished(DnsProbeStatus result
) {
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
184 DCHECK_EQ(chrome_common_net::DNS_PROBE_STARTED
, dns_probe_status_
);
185 DCHECK(chrome_common_net::DnsProbeStatusIsFinished(result
));
187 DVLOG(1) << "Finished DNS probe with result "
188 << DnsProbeStatusToString(result
) << ".";
190 dns_probe_status_
= result
;
192 if (dns_error_page_committed_
)
196 void NetErrorTabHelper::InitializePref(WebContents
* contents
) {
199 BrowserContext
* browser_context
= contents
->GetBrowserContext();
200 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
201 resolve_errors_with_web_service_
.Init(
202 prefs::kAlternateErrorPagesEnabled
,
203 profile
->GetPrefs());
206 bool NetErrorTabHelper::ProbesAllowed() const {
207 if (testing_state_
!= TESTING_DEFAULT
)
208 return testing_state_
== TESTING_FORCE_ENABLED
;
210 // TODO(ttuttle): Disable on mobile?
211 return *resolve_errors_with_web_service_
;
214 void NetErrorTabHelper::SendInfo() {
215 DCHECK_NE(chrome_common_net::DNS_PROBE_POSSIBLE
, dns_probe_status_
);
216 DCHECK(dns_error_page_committed_
);
218 DVLOG(1) << "Sending status " << DnsProbeStatusToString(dns_probe_status_
);
219 content::RenderFrameHost
* rfh
= web_contents()->GetMainFrame();
220 rfh
->Send(new ChromeViewMsg_NetErrorInfo(rfh
->GetRoutingID(),
223 if (!dns_probe_status_snoop_callback_
.is_null())
224 dns_probe_status_snoop_callback_
.Run(dns_probe_status_
);
227 } // namespace chrome_browser_net