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/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/net/net_error_diagnostics_dialog.h"
14 #include "chrome/browser/platform_util.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/common/pref_names.h"
17 #include "chrome/common/render_messages.h"
18 #include "components/error_page/common/net_error_info.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/render_frame_host.h"
21 #include "content/public/browser/render_view_host.h"
22 #include "ipc/ipc_message_macros.h"
23 #include "net/base/net_errors.h"
26 using content::BrowserContext
;
27 using content::BrowserThread
;
28 using content::RenderViewHost
;
29 using content::WebContents
;
30 using content::WebContentsObserver
;
31 using error_page::DnsProbeStatus
;
32 using error_page::DnsProbeStatusToString
;
33 using ui::PageTransition
;
35 DEFINE_WEB_CONTENTS_USER_DATA_KEY(chrome_browser_net::NetErrorTabHelper
);
37 namespace chrome_browser_net
{
41 static NetErrorTabHelper::TestingState testing_state_
=
42 NetErrorTabHelper::TESTING_DEFAULT
;
44 // Returns whether |net_error| is a DNS-related error (and therefore whether
45 // the tab helper should start a DNS probe after receiving it.)
46 bool IsDnsError(int net_error
) {
47 return net_error
== net::ERR_NAME_NOT_RESOLVED
||
48 net_error
== net::ERR_NAME_RESOLUTION_FAILED
;
51 void OnDnsProbeFinishedOnIOThread(
52 const base::Callback
<void(DnsProbeStatus
)>& callback
,
53 DnsProbeStatus result
) {
54 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
56 BrowserThread::PostTask(
59 base::Bind(callback
, result
));
62 // Can only access g_browser_process->io_thread() from the browser thread,
63 // so have to pass it in to the callback instead of dereferencing it here.
64 void StartDnsProbeOnIOThread(
65 const base::Callback
<void(DnsProbeStatus
)>& callback
,
66 IOThread
* io_thread
) {
67 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
69 DnsProbeService
* probe_service
=
70 io_thread
->globals()->dns_probe_service
.get();
72 probe_service
->ProbeDns(base::Bind(&OnDnsProbeFinishedOnIOThread
, callback
));
77 NetErrorTabHelper::~NetErrorTabHelper() {
81 void NetErrorTabHelper::set_state_for_testing(TestingState state
) {
82 testing_state_
= state
;
85 void NetErrorTabHelper::RenderViewCreated(
86 content::RenderViewHost
* render_view_host
) {
87 content::RenderFrameHost
* render_frame_host
=
88 render_view_host
->GetMainFrame();
89 render_frame_host
->Send(new ChromeViewMsg_SetCanShowNetworkDiagnosticsDialog(
90 render_frame_host
->GetRoutingID(),
91 CanShowNetworkDiagnosticsDialog()));
94 void NetErrorTabHelper::DidStartNavigationToPendingEntry(
96 content::NavigationController::ReloadType reload_type
) {
97 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
102 // Only record reloads.
103 if (reload_type
!= content::NavigationController::NO_RELOAD
) {
104 error_page::RecordEvent(
105 error_page::NETWORK_ERROR_PAGE_BROWSER_INITIATED_RELOAD
);
109 void NetErrorTabHelper::DidStartProvisionalLoadForFrame(
110 content::RenderFrameHost
* render_frame_host
,
111 const GURL
& validated_url
,
113 bool is_iframe_srcdoc
) {
114 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
116 if (render_frame_host
->GetParent())
119 is_error_page_
= is_error_page
;
122 void NetErrorTabHelper::DidCommitProvisionalLoadForFrame(
123 content::RenderFrameHost
* render_frame_host
,
125 PageTransition transition_type
) {
126 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
128 if (render_frame_host
->GetParent())
131 // Resend status every time an error page commits; this is somewhat spammy,
132 // but ensures that the status will make it to the real error page, even if
133 // the link doctor loads a blank intermediate page or the tab switches
134 // renderer processes.
135 if (is_error_page_
&& dns_error_active_
) {
136 dns_error_page_committed_
= true;
137 DVLOG(1) << "Committed error page; resending status.";
140 dns_error_active_
= false;
141 dns_error_page_committed_
= false;
145 void NetErrorTabHelper::DidFailProvisionalLoad(
146 content::RenderFrameHost
* render_frame_host
,
147 const GURL
& validated_url
,
149 const base::string16
& error_description
,
150 bool was_ignored_by_handler
) {
151 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
153 if (render_frame_host
->GetParent())
156 if (IsDnsError(error_code
)) {
157 dns_error_active_
= true;
158 OnMainFrameDnsError();
162 bool NetErrorTabHelper::OnMessageReceived(
163 const IPC::Message
& message
,
164 content::RenderFrameHost
* render_frame_host
) {
166 IPC_BEGIN_MESSAGE_MAP(NetErrorTabHelper
, message
)
167 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RunNetworkDiagnostics
,
168 RunNetworkDiagnostics
)
169 IPC_MESSAGE_UNHANDLED(handled
= false)
170 IPC_END_MESSAGE_MAP()
175 NetErrorTabHelper::NetErrorTabHelper(WebContents
* contents
)
176 : WebContentsObserver(contents
),
177 is_error_page_(false),
178 dns_error_active_(false),
179 dns_error_page_committed_(false),
180 dns_probe_status_(error_page::DNS_PROBE_POSSIBLE
),
181 weak_factory_(this) {
182 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
184 // If this helper is under test, it won't have a WebContents.
186 InitializePref(contents
);
189 void NetErrorTabHelper::OnMainFrameDnsError() {
190 if (ProbesAllowed()) {
191 // Don't start more than one probe at a time.
192 if (dns_probe_status_
!= error_page::DNS_PROBE_STARTED
) {
194 dns_probe_status_
= error_page::DNS_PROBE_STARTED
;
197 dns_probe_status_
= error_page::DNS_PROBE_NOT_RUN
;
201 void NetErrorTabHelper::StartDnsProbe() {
202 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
203 DCHECK(dns_error_active_
);
204 DCHECK_NE(error_page::DNS_PROBE_STARTED
, dns_probe_status_
);
206 DVLOG(1) << "Starting DNS probe.";
208 BrowserThread::PostTask(
211 base::Bind(&StartDnsProbeOnIOThread
,
212 base::Bind(&NetErrorTabHelper::OnDnsProbeFinished
,
213 weak_factory_
.GetWeakPtr()),
214 g_browser_process
->io_thread()));
217 void NetErrorTabHelper::OnDnsProbeFinished(DnsProbeStatus result
) {
218 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
219 DCHECK_EQ(error_page::DNS_PROBE_STARTED
, dns_probe_status_
);
220 DCHECK(error_page::DnsProbeStatusIsFinished(result
));
222 DVLOG(1) << "Finished DNS probe with result "
223 << DnsProbeStatusToString(result
) << ".";
225 dns_probe_status_
= result
;
227 if (dns_error_page_committed_
)
231 void NetErrorTabHelper::InitializePref(WebContents
* contents
) {
234 BrowserContext
* browser_context
= contents
->GetBrowserContext();
235 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
236 resolve_errors_with_web_service_
.Init(
237 prefs::kAlternateErrorPagesEnabled
,
238 profile
->GetPrefs());
241 bool NetErrorTabHelper::ProbesAllowed() const {
242 if (testing_state_
!= TESTING_DEFAULT
)
243 return testing_state_
== TESTING_FORCE_ENABLED
;
245 // TODO(ttuttle): Disable on mobile?
246 return *resolve_errors_with_web_service_
;
249 void NetErrorTabHelper::SendInfo() {
250 DCHECK_NE(error_page::DNS_PROBE_POSSIBLE
, dns_probe_status_
);
251 DCHECK(dns_error_page_committed_
);
253 DVLOG(1) << "Sending status " << DnsProbeStatusToString(dns_probe_status_
);
254 content::RenderFrameHost
* rfh
= web_contents()->GetMainFrame();
255 rfh
->Send(new ChromeViewMsg_NetErrorInfo(rfh
->GetRoutingID(),
258 if (!dns_probe_status_snoop_callback_
.is_null())
259 dns_probe_status_snoop_callback_
.Run(dns_probe_status_
);
262 void NetErrorTabHelper::RunNetworkDiagnostics(const GURL
& url
) {
263 // Only run diagnostics on HTTP or HTTPS URLs. Shouldn't receive URLs with
264 // any other schemes, but the renderer is not trusted.
265 if (!url
.is_valid() || !url
.SchemeIsHTTPOrHTTPS())
267 // Sanitize URL prior to running diagnostics on it.
268 RunNetworkDiagnosticsHelper(url
.GetOrigin().spec());
271 void NetErrorTabHelper::RunNetworkDiagnosticsHelper(
272 const std::string
& sanitized_url
) {
273 ShowNetworkDiagnosticsDialog(web_contents(), sanitized_url
);
276 } // namespace chrome_browser_net