OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/net/net_error_tab_helper.h" | 5 #include "chrome/browser/net/net_error_tab_helper.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/metrics/field_trial.h" | 8 #include "base/metrics/field_trial.h" |
9 #include "base/prefs/pref_service.h" | 9 #include "base/prefs/pref_service.h" |
10 #include "chrome/browser/browser_process.h" | 10 #include "chrome/browser/browser_process.h" |
11 #include "chrome/browser/io_thread.h" | 11 #include "chrome/browser/io_thread.h" |
12 #include "chrome/browser/net/dns_probe_service.h" | 12 #include "chrome/browser/net/dns_probe_service.h" |
13 #include "chrome/browser/profiles/profile.h" | 13 #include "chrome/browser/profiles/profile.h" |
14 #include "chrome/common/net/net_error_info.h" | 14 #include "chrome/common/net/net_error_info.h" |
15 #include "chrome/common/pref_names.h" | 15 #include "chrome/common/pref_names.h" |
16 #include "chrome/common/render_messages.h" | 16 #include "chrome/common/render_messages.h" |
17 #include "content/public/browser/browser_thread.h" | 17 #include "content/public/browser/browser_thread.h" |
18 #include "net/base/net_errors.h" | 18 #include "net/base/net_errors.h" |
19 | 19 |
20 using base::FieldTrialList; | 20 using base::FieldTrialList; |
21 using chrome_common_net::DnsProbeStatus; | 21 using chrome_common_net::DnsProbeResult; |
22 using chrome_common_net::DnsProbeStatusToString; | |
23 using content::BrowserContext; | 22 using content::BrowserContext; |
24 using content::BrowserThread; | 23 using content::BrowserThread; |
25 using content::PageTransition; | 24 using content::PageTransition; |
26 using content::RenderViewHost; | 25 using content::RenderViewHost; |
27 using content::WebContents; | 26 using content::WebContents; |
28 using content::WebContentsObserver; | 27 using content::WebContentsObserver; |
29 | 28 |
30 DEFINE_WEB_CONTENTS_USER_DATA_KEY(chrome_browser_net::NetErrorTabHelper); | 29 DEFINE_WEB_CONTENTS_USER_DATA_KEY(chrome_browser_net::NetErrorTabHelper); |
31 | 30 |
32 namespace chrome_browser_net { | 31 namespace chrome_browser_net { |
33 | 32 |
34 namespace { | 33 namespace { |
35 | 34 |
| 35 const char kDnsProbeFieldTrialName[] = "DnsProbe-Enable"; |
| 36 const char kDnsProbeFieldTrialEnableGroupName[] = "enable"; |
| 37 |
36 static NetErrorTabHelper::TestingState testing_state_ = | 38 static NetErrorTabHelper::TestingState testing_state_ = |
37 NetErrorTabHelper::TESTING_DEFAULT; | 39 NetErrorTabHelper::TESTING_DEFAULT; |
38 | 40 |
39 // Returns whether |net_error| is a DNS-related error (and therefore whether | 41 // Returns whether |net_error| is a DNS-related error (and therefore whether |
40 // the tab helper should start a DNS probe after receiving it.) | 42 // the tab helper should start a DNS probe after receiving it.) |
41 bool IsDnsError(int net_error) { | 43 bool IsDnsError(int net_error) { |
42 return net_error == net::ERR_NAME_NOT_RESOLVED || | 44 return net_error == net::ERR_NAME_NOT_RESOLVED || |
43 net_error == net::ERR_NAME_RESOLUTION_FAILED; | 45 net_error == net::ERR_NAME_RESOLUTION_FAILED; |
44 } | 46 } |
45 | 47 |
| 48 bool GetEnabledByTrial() { |
| 49 return (FieldTrialList::FindFullName(kDnsProbeFieldTrialName) |
| 50 == kDnsProbeFieldTrialEnableGroupName); |
| 51 } |
| 52 |
| 53 NetErrorTracker::FrameType GetFrameType(bool is_main_frame) { |
| 54 return is_main_frame ? NetErrorTracker::FRAME_MAIN |
| 55 : NetErrorTracker::FRAME_SUB; |
| 56 } |
| 57 |
| 58 NetErrorTracker::PageType GetPageType(bool is_error_page) { |
| 59 return is_error_page ? NetErrorTracker::PAGE_ERROR |
| 60 : NetErrorTracker::PAGE_NORMAL; |
| 61 } |
| 62 |
| 63 NetErrorTracker::ErrorType GetErrorType(int net_error) { |
| 64 return IsDnsError(net_error) ? NetErrorTracker::ERROR_DNS |
| 65 : NetErrorTracker::ERROR_OTHER; |
| 66 } |
| 67 |
46 void OnDnsProbeFinishedOnIOThread( | 68 void OnDnsProbeFinishedOnIOThread( |
47 const base::Callback<void(DnsProbeStatus)>& callback, | 69 const base::Callback<void(DnsProbeResult)>& callback, |
48 DnsProbeStatus result) { | 70 DnsProbeResult result) { |
49 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
50 | 72 |
| 73 DVLOG(1) << "DNS probe finished with result " << result; |
| 74 |
51 BrowserThread::PostTask( | 75 BrowserThread::PostTask( |
52 BrowserThread::UI, | 76 BrowserThread::UI, |
53 FROM_HERE, | 77 FROM_HERE, |
54 base::Bind(callback, result)); | 78 base::Bind(callback, result)); |
55 } | 79 } |
56 | 80 |
57 // Can only access g_browser_process->io_thread() from the browser thread, | 81 // We 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. | 82 // so we have to pass it in to the callback instead of dereferencing it here. |
59 void StartDnsProbeOnIOThread( | 83 void StartDnsProbeOnIOThread( |
60 const base::Callback<void(DnsProbeStatus)>& callback, | 84 const base::Callback<void(DnsProbeResult)>& callback, |
61 IOThread* io_thread) { | 85 IOThread* io_thread) { |
62 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 86 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
63 | 87 |
| 88 DVLOG(1) << "Starting DNS probe"; |
| 89 |
64 DnsProbeService* probe_service = | 90 DnsProbeService* probe_service = |
65 io_thread->globals()->dns_probe_service.get(); | 91 io_thread->globals()->dns_probe_service.get(); |
66 | 92 |
67 probe_service->ProbeDns(base::Bind(&OnDnsProbeFinishedOnIOThread, callback)); | 93 probe_service->ProbeDns(base::Bind(&OnDnsProbeFinishedOnIOThread, callback)); |
68 } | 94 } |
69 | 95 |
70 } // namespace | 96 } // namespace |
71 | 97 |
72 NetErrorTabHelper::~NetErrorTabHelper() { | 98 NetErrorTabHelper::~NetErrorTabHelper() { |
73 } | 99 } |
74 | 100 |
75 // static | 101 // static |
76 void NetErrorTabHelper::set_state_for_testing(TestingState state) { | 102 void NetErrorTabHelper::set_state_for_testing(TestingState state) { |
77 testing_state_ = state; | 103 testing_state_ = state; |
78 } | 104 } |
79 | 105 |
80 void NetErrorTabHelper::DidStartProvisionalLoadForFrame( | 106 void NetErrorTabHelper::DidStartProvisionalLoadForFrame( |
81 int64 frame_id, | 107 int64 frame_id, |
82 int64 parent_frame_id, | 108 int64 parent_frame_id, |
83 bool is_main_frame, | 109 bool is_main_frame, |
84 const GURL& validated_url, | 110 const GURL& validated_url, |
85 bool is_error_page, | 111 bool is_error_page, |
86 bool is_iframe_srcdoc, | 112 bool is_iframe_srcdoc, |
87 RenderViewHost* render_view_host) { | 113 RenderViewHost* render_view_host) { |
88 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
89 | 115 |
90 if (!is_main_frame) | 116 tracker_.OnStartProvisionalLoad(GetFrameType(is_main_frame), |
91 return; | 117 GetPageType(is_error_page)); |
92 | |
93 is_error_page_ = is_error_page; | |
94 } | 118 } |
95 | 119 |
96 void NetErrorTabHelper::DidCommitProvisionalLoadForFrame( | 120 void NetErrorTabHelper::DidCommitProvisionalLoadForFrame( |
97 int64 frame_id, | 121 int64 frame_id, |
98 bool is_main_frame, | 122 bool is_main_frame, |
99 const GURL& url, | 123 const GURL& url, |
100 PageTransition transition_type, | 124 PageTransition transition_type, |
101 RenderViewHost* render_view_host) { | 125 RenderViewHost* render_view_host) { |
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
103 | 127 |
104 if (!is_main_frame) | 128 tracker_.OnCommitProvisionalLoad(GetFrameType(is_main_frame)); |
105 return; | |
106 | |
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."; | |
114 SendInfo(); | |
115 } else { | |
116 dns_error_active_ = false; | |
117 dns_error_page_committed_ = false; | |
118 } | |
119 } | 129 } |
120 | 130 |
121 void NetErrorTabHelper::DidFailProvisionalLoad( | 131 void NetErrorTabHelper::DidFailProvisionalLoad( |
122 int64 frame_id, | 132 int64 frame_id, |
123 bool is_main_frame, | 133 bool is_main_frame, |
124 const GURL& validated_url, | 134 const GURL& validated_url, |
125 int error_code, | 135 int error_code, |
126 const string16& error_description, | 136 const string16& error_description, |
127 RenderViewHost* render_view_host) { | 137 RenderViewHost* render_view_host) { |
128 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
129 | 139 |
130 if (!is_main_frame) | 140 tracker_.OnFailProvisionalLoad(GetFrameType(is_main_frame), |
131 return; | 141 GetErrorType(error_code)); |
| 142 } |
132 | 143 |
133 if (IsDnsError(error_code)) { | 144 void NetErrorTabHelper::DidFinishLoad( |
134 dns_error_active_ = true; | 145 int64 frame_id, |
135 OnMainFrameDnsError(); | 146 const GURL& validated_url, |
136 } | 147 bool is_main_frame, |
| 148 RenderViewHost* render_view_host) { |
| 149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 150 |
| 151 tracker_.OnFinishLoad(GetFrameType(is_main_frame)); |
137 } | 152 } |
138 | 153 |
139 NetErrorTabHelper::NetErrorTabHelper(WebContents* contents) | 154 NetErrorTabHelper::NetErrorTabHelper(WebContents* contents) |
140 : WebContentsObserver(contents), | 155 : WebContentsObserver(contents), |
141 weak_factory_(this), | 156 weak_factory_(this), |
142 is_error_page_(false), | 157 tracker_(base::Bind(&NetErrorTabHelper::TrackerCallback, |
143 dns_error_active_(false), | 158 weak_factory_.GetWeakPtr())), |
144 dns_error_page_committed_(false), | 159 dns_error_page_state_(NetErrorTracker::DNS_ERROR_PAGE_NONE), |
145 dns_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE), | 160 dns_probe_state_(DNS_PROBE_NONE), |
146 enabled_by_trial_(chrome_common_net::DnsProbesEnabledByFieldTrial()) { | 161 enabled_by_trial_(GetEnabledByTrial()) { |
147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
148 | 163 |
149 // If this helper is under test, it won't have a WebContents. | 164 InitializePref(contents); |
150 if (contents) | |
151 InitializePref(contents); | |
152 } | 165 } |
153 | 166 |
154 void NetErrorTabHelper::OnMainFrameDnsError() { | 167 void NetErrorTabHelper::TrackerCallback( |
155 if (ProbesAllowed()) { | 168 NetErrorTracker::DnsErrorPageState state) { |
156 // Don't start more than one probe at a time. | 169 dns_error_page_state_ = state; |
157 if (dns_probe_status_ != chrome_common_net::DNS_PROBE_STARTED) { | 170 |
158 StartDnsProbe(); | 171 MaybePostStartDnsProbeTask(); |
159 dns_probe_status_ = chrome_common_net::DNS_PROBE_STARTED; | 172 MaybeSendInfo(); |
160 } | 173 } |
161 } else { | 174 |
162 dns_probe_status_ = chrome_common_net::DNS_PROBE_NOT_RUN; | 175 void NetErrorTabHelper::MaybePostStartDnsProbeTask() { |
| 176 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 177 |
| 178 if (dns_error_page_state_ != NetErrorTracker::DNS_ERROR_PAGE_NONE && |
| 179 dns_probe_state_ != DNS_PROBE_STARTED && |
| 180 ProbesAllowed()) { |
| 181 BrowserThread::PostTask( |
| 182 BrowserThread::IO, |
| 183 FROM_HERE, |
| 184 base::Bind(&StartDnsProbeOnIOThread, |
| 185 base::Bind(&NetErrorTabHelper::OnDnsProbeFinished, |
| 186 weak_factory_.GetWeakPtr()), |
| 187 g_browser_process->io_thread())); |
| 188 dns_probe_state_ = DNS_PROBE_STARTED; |
163 } | 189 } |
164 } | 190 } |
165 | 191 |
166 void NetErrorTabHelper::StartDnsProbe() { | 192 void NetErrorTabHelper::OnDnsProbeFinished(DnsProbeResult result) { |
167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
168 DCHECK(dns_error_active_); | 194 DCHECK_EQ(DNS_PROBE_STARTED, dns_probe_state_); |
169 DCHECK_NE(chrome_common_net::DNS_PROBE_STARTED, dns_probe_status_); | |
170 | 195 |
171 DVLOG(1) << "Starting DNS probe."; | 196 dns_probe_result_ = result; |
| 197 dns_probe_state_ = DNS_PROBE_FINISHED; |
172 | 198 |
173 BrowserThread::PostTask( | 199 MaybeSendInfo(); |
174 BrowserThread::IO, | |
175 FROM_HERE, | |
176 base::Bind(&StartDnsProbeOnIOThread, | |
177 base::Bind(&NetErrorTabHelper::OnDnsProbeFinished, | |
178 weak_factory_.GetWeakPtr()), | |
179 g_browser_process->io_thread())); | |
180 } | 200 } |
181 | 201 |
182 void NetErrorTabHelper::OnDnsProbeFinished(DnsProbeStatus result) { | 202 void NetErrorTabHelper::MaybeSendInfo() { |
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 203 if (dns_error_page_state_ == NetErrorTracker::DNS_ERROR_PAGE_LOADED && |
184 DCHECK_EQ(chrome_common_net::DNS_PROBE_STARTED, dns_probe_status_); | 204 dns_probe_state_ == DNS_PROBE_FINISHED) { |
185 DCHECK(chrome_common_net::DnsProbeStatusIsFinished(result)); | 205 DVLOG(1) << "Sending result " << dns_probe_result_ << " to renderer"; |
186 | 206 Send(new ChromeViewMsg_NetErrorInfo(routing_id(), dns_probe_result_)); |
187 DVLOG(1) << "Finished DNS probe with result " | 207 dns_probe_state_ = DNS_PROBE_NONE; |
188 << DnsProbeStatusToString(result) << "."; | 208 } |
189 | |
190 dns_probe_status_ = result; | |
191 | |
192 if (dns_error_page_committed_) | |
193 SendInfo(); | |
194 } | 209 } |
195 | 210 |
196 void NetErrorTabHelper::InitializePref(WebContents* contents) { | 211 void NetErrorTabHelper::InitializePref(WebContents* contents) { |
197 DCHECK(contents); | 212 DCHECK(contents); |
198 | 213 |
199 BrowserContext* browser_context = contents->GetBrowserContext(); | 214 BrowserContext* browser_context = contents->GetBrowserContext(); |
200 Profile* profile = Profile::FromBrowserContext(browser_context); | 215 Profile* profile = Profile::FromBrowserContext(browser_context); |
201 resolve_errors_with_web_service_.Init( | 216 resolve_errors_with_web_service_.Init( |
202 prefs::kAlternateErrorPagesEnabled, | 217 prefs::kAlternateErrorPagesEnabled, |
203 profile->GetPrefs()); | 218 profile->GetPrefs()); |
204 } | 219 } |
205 | 220 |
206 bool NetErrorTabHelper::ProbesAllowed() const { | 221 bool NetErrorTabHelper::ProbesAllowed() const { |
207 if (testing_state_ != TESTING_DEFAULT) | 222 if (testing_state_ != TESTING_DEFAULT) |
208 return testing_state_ == TESTING_FORCE_ENABLED; | 223 return testing_state_ == TESTING_FORCE_ENABLED; |
209 | 224 |
210 // TODO(ttuttle): Disable on mobile? | 225 // TODO(ttuttle): Disable on mobile? |
211 return enabled_by_trial_ && *resolve_errors_with_web_service_; | 226 return enabled_by_trial_ && *resolve_errors_with_web_service_; |
212 } | 227 } |
213 | 228 |
214 void NetErrorTabHelper::SendInfo() { | |
215 DCHECK_NE(chrome_common_net::DNS_PROBE_POSSIBLE, dns_probe_status_); | |
216 DCHECK(dns_error_page_committed_); | |
217 | |
218 DVLOG(1) << "Sending status " << DnsProbeStatusToString(dns_probe_status_); | |
219 Send(new ChromeViewMsg_NetErrorInfo(routing_id(), dns_probe_status_)); | |
220 | |
221 if (!dns_probe_status_snoop_callback_.is_null()) | |
222 dns_probe_status_snoop_callback_.Run(dns_probe_status_); | |
223 } | |
224 | |
225 } // namespace chrome_browser_net | 229 } // namespace chrome_browser_net |
OLD | NEW |