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.
4
5 #include "chrome/browser/net/net_error_tab_helper.h"
6
7 #include "base/bind.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 "net/base/net_errors.h"
18
19 using chrome_common_net::DnsProbeStatus;
20 using chrome_common_net::DnsProbeStatusToString;
21 using content::BrowserContext;
22 using content::BrowserThread;
23 using content::PageTransition;
24 using content::RenderViewHost;
25 using content::WebContents;
26 using content::WebContentsObserver;
27
28 DEFINE_WEB_CONTENTS_USER_DATA_KEY(chrome_browser_net::NetErrorTabHelper);
29
30 namespace chrome_browser_net {
31
32 namespace {
33
34 static NetErrorTabHelper::TestingState testing_state_ =
35 NetErrorTabHelper::TESTING_DEFAULT;
36
37 // Returns whether |net_error| is a DNS-related error (and therefore whether
38 // the tab helper should start a DNS probe after receiving it.)
IsDnsError(int net_error)39 bool IsDnsError(int net_error) {
40 return net_error == net::ERR_NAME_NOT_RESOLVED ||
41 net_error == net::ERR_NAME_RESOLUTION_FAILED;
42 }
43
OnDnsProbeFinishedOnIOThread(const base::Callback<void (DnsProbeStatus)> & callback,DnsProbeStatus result)44 void OnDnsProbeFinishedOnIOThread(
45 const base::Callback<void(DnsProbeStatus)>& callback,
46 DnsProbeStatus result) {
47 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
48
49 BrowserThread::PostTask(
50 BrowserThread::UI,
51 FROM_HERE,
52 base::Bind(callback, result));
53 }
54
55 // Can only access g_browser_process->io_thread() from the browser thread,
56 // so have to pass it in to the callback instead of dereferencing it here.
StartDnsProbeOnIOThread(const base::Callback<void (DnsProbeStatus)> & callback,IOThread * io_thread)57 void StartDnsProbeOnIOThread(
58 const base::Callback<void(DnsProbeStatus)>& callback,
59 IOThread* io_thread) {
60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
61
62 DnsProbeService* probe_service =
63 io_thread->globals()->dns_probe_service.get();
64
65 probe_service->ProbeDns(base::Bind(&OnDnsProbeFinishedOnIOThread, callback));
66 }
67
68 } // namespace
69
~NetErrorTabHelper()70 NetErrorTabHelper::~NetErrorTabHelper() {
71 }
72
73 // static
set_state_for_testing(TestingState state)74 void NetErrorTabHelper::set_state_for_testing(TestingState state) {
75 testing_state_ = state;
76 }
77
DidStartProvisionalLoadForFrame(int64 frame_id,int64 parent_frame_id,bool is_main_frame,const GURL & validated_url,bool is_error_page,bool is_iframe_srcdoc,RenderViewHost * render_view_host)78 void NetErrorTabHelper::DidStartProvisionalLoadForFrame(
79 int64 frame_id,
80 int64 parent_frame_id,
81 bool is_main_frame,
82 const GURL& validated_url,
83 bool is_error_page,
84 bool is_iframe_srcdoc,
85 RenderViewHost* render_view_host) {
86 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
87
88 if (!is_main_frame)
89 return;
90
91 is_error_page_ = is_error_page;
92 }
93
DidCommitProvisionalLoadForFrame(int64 frame_id,const base::string16 & frame_unique_name,bool is_main_frame,const GURL & url,PageTransition transition_type,RenderViewHost * render_view_host)94 void NetErrorTabHelper::DidCommitProvisionalLoadForFrame(
95 int64 frame_id,
96 const base::string16& frame_unique_name,
97 bool is_main_frame,
98 const GURL& url,
99 PageTransition transition_type,
100 RenderViewHost* render_view_host) {
101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
102
103 if (!is_main_frame)
104 return;
105
106 // Resend status every time an error page commits; this is somewhat spammy,
107 // but ensures that the status will make it to the real error page, even if
108 // the link doctor loads a blank intermediate page or the tab switches
109 // renderer processes.
110 if (is_error_page_ && dns_error_active_) {
111 dns_error_page_committed_ = true;
112 DVLOG(1) << "Committed error page; resending status.";
113 SendInfo();
114 } else {
115 dns_error_active_ = false;
116 dns_error_page_committed_ = false;
117 }
118 }
119
DidFailProvisionalLoad(int64 frame_id,const base::string16 & frame_unique_name,bool is_main_frame,const GURL & validated_url,int error_code,const base::string16 & error_description,RenderViewHost * render_view_host)120 void NetErrorTabHelper::DidFailProvisionalLoad(
121 int64 frame_id,
122 const base::string16& frame_unique_name,
123 bool is_main_frame,
124 const GURL& validated_url,
125 int error_code,
126 const base::string16& error_description,
127 RenderViewHost* render_view_host) {
128 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
129
130 if (!is_main_frame)
131 return;
132
133 if (IsDnsError(error_code)) {
134 dns_error_active_ = true;
135 OnMainFrameDnsError();
136 }
137 }
138
NetErrorTabHelper(WebContents * contents)139 NetErrorTabHelper::NetErrorTabHelper(WebContents* contents)
140 : WebContentsObserver(contents),
141 weak_factory_(this),
142 is_error_page_(false),
143 dns_error_active_(false),
144 dns_error_page_committed_(false),
145 dns_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE) {
146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
147
148 // If this helper is under test, it won't have a WebContents.
149 if (contents)
150 InitializePref(contents);
151 }
152
OnMainFrameDnsError()153 void NetErrorTabHelper::OnMainFrameDnsError() {
154 if (ProbesAllowed()) {
155 // Don't start more than one probe at a time.
156 if (dns_probe_status_ != chrome_common_net::DNS_PROBE_STARTED) {
157 StartDnsProbe();
158 dns_probe_status_ = chrome_common_net::DNS_PROBE_STARTED;
159 }
160 } else {
161 dns_probe_status_ = chrome_common_net::DNS_PROBE_NOT_RUN;
162 }
163 }
164
StartDnsProbe()165 void NetErrorTabHelper::StartDnsProbe() {
166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
167 DCHECK(dns_error_active_);
168 DCHECK_NE(chrome_common_net::DNS_PROBE_STARTED, dns_probe_status_);
169
170 DVLOG(1) << "Starting DNS probe.";
171
172 BrowserThread::PostTask(
173 BrowserThread::IO,
174 FROM_HERE,
175 base::Bind(&StartDnsProbeOnIOThread,
176 base::Bind(&NetErrorTabHelper::OnDnsProbeFinished,
177 weak_factory_.GetWeakPtr()),
178 g_browser_process->io_thread()));
179 }
180
OnDnsProbeFinished(DnsProbeStatus result)181 void NetErrorTabHelper::OnDnsProbeFinished(DnsProbeStatus result) {
182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
183 DCHECK_EQ(chrome_common_net::DNS_PROBE_STARTED, dns_probe_status_);
184 DCHECK(chrome_common_net::DnsProbeStatusIsFinished(result));
185
186 DVLOG(1) << "Finished DNS probe with result "
187 << DnsProbeStatusToString(result) << ".";
188
189 dns_probe_status_ = result;
190
191 if (dns_error_page_committed_)
192 SendInfo();
193 }
194
InitializePref(WebContents * contents)195 void NetErrorTabHelper::InitializePref(WebContents* contents) {
196 DCHECK(contents);
197
198 BrowserContext* browser_context = contents->GetBrowserContext();
199 Profile* profile = Profile::FromBrowserContext(browser_context);
200 resolve_errors_with_web_service_.Init(
201 prefs::kAlternateErrorPagesEnabled,
202 profile->GetPrefs());
203 }
204
ProbesAllowed() const205 bool NetErrorTabHelper::ProbesAllowed() const {
206 if (testing_state_ != TESTING_DEFAULT)
207 return testing_state_ == TESTING_FORCE_ENABLED;
208
209 // TODO(ttuttle): Disable on mobile?
210 return *resolve_errors_with_web_service_;
211 }
212
SendInfo()213 void NetErrorTabHelper::SendInfo() {
214 DCHECK_NE(chrome_common_net::DNS_PROBE_POSSIBLE, dns_probe_status_);
215 DCHECK(dns_error_page_committed_);
216
217 DVLOG(1) << "Sending status " << DnsProbeStatusToString(dns_probe_status_);
218 Send(new ChromeViewMsg_NetErrorInfo(routing_id(), dns_probe_status_));
219
220 if (!dns_probe_status_snoop_callback_.is_null())
221 dns_probe_status_snoop_callback_.Run(dns_probe_status_);
222 }
223
224 } // namespace chrome_browser_net
225