OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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/renderer/net/net_error_helper_core.h" | 5 #include "chrome/renderer/net/net_error_helper_core.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/callback.h" |
| 11 #include "base/location.h" |
9 #include "base/metrics/histogram.h" | 12 #include "base/metrics/histogram.h" |
10 #include "chrome/common/localized_error.h" | 13 #include "chrome/common/localized_error.h" |
11 #include "net/base/escape.h" | 14 #include "net/base/escape.h" |
12 #include "net/base/net_errors.h" | 15 #include "net/base/net_errors.h" |
13 #include "third_party/WebKit/public/platform/WebString.h" | 16 #include "third_party/WebKit/public/platform/WebString.h" |
14 #include "third_party/WebKit/public/platform/WebURLError.h" | 17 #include "third_party/WebKit/public/platform/WebURLError.h" |
15 #include "url/gurl.h" | 18 #include "url/gurl.h" |
16 | 19 |
17 namespace { | 20 namespace { |
18 | 21 |
| 22 base::TimeDelta GetAutoReloadTime(size_t reload_count) { |
| 23 static const int kDelaysMs[] = { |
| 24 0, 5000, 30000, 60000, 300000, 600000, 1800000 |
| 25 }; |
| 26 if (reload_count >= arraysize(kDelaysMs)) |
| 27 reload_count = arraysize(kDelaysMs) - 1; |
| 28 return base::TimeDelta::FromMilliseconds(kDelaysMs[reload_count]); |
| 29 } |
| 30 |
19 // Returns whether |net_error| is a DNS-related error (and therefore whether | 31 // Returns whether |net_error| is a DNS-related error (and therefore whether |
20 // the tab helper should start a DNS probe after receiving it.) | 32 // the tab helper should start a DNS probe after receiving it.) |
21 bool IsDnsError(const blink::WebURLError& error) { | 33 bool IsDnsError(const blink::WebURLError& error) { |
22 return error.domain.utf8() == net::kErrorDomain && | 34 return error.domain.utf8() == net::kErrorDomain && |
23 (error.reason == net::ERR_NAME_NOT_RESOLVED || | 35 (error.reason == net::ERR_NAME_NOT_RESOLVED || |
24 error.reason == net::ERR_NAME_RESOLUTION_FAILED); | 36 error.reason == net::ERR_NAME_RESOLUTION_FAILED); |
25 } | 37 } |
26 | 38 |
27 // If an alternate error page should be retrieved remotely for a main frame load | 39 // If an alternate error page should be retrieved remotely for a main frame load |
28 // that failed with |error|, returns true and sets |error_page_url| to the URL | 40 // that failed with |error|, returns true and sets |error_page_url| to the URL |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 // valid URL. Request will be issued when the error page finishes loading. | 121 // valid URL. Request will be issued when the error page finishes loading. |
110 // This is done on load complete to ensure that there are two complete loads | 122 // This is done on load complete to ensure that there are two complete loads |
111 // for tests to wait for. | 123 // for tests to wait for. |
112 GURL alternate_error_page_url; | 124 GURL alternate_error_page_url; |
113 | 125 |
114 // True if a page has completed loading, at which point it can receive | 126 // True if a page has completed loading, at which point it can receive |
115 // updates. | 127 // updates. |
116 bool is_finished_loading; | 128 bool is_finished_loading; |
117 }; | 129 }; |
118 | 130 |
| 131 bool NetErrorHelperCore::IsReloadableError( |
| 132 const NetErrorHelperCore::ErrorPageInfo& info) { |
| 133 return info.error.domain.utf8() == net::kErrorDomain && |
| 134 info.error.reason != net::ERR_ABORTED && |
| 135 !info.was_failed_post; |
| 136 } |
| 137 |
119 NetErrorHelperCore::NetErrorHelperCore(Delegate* delegate) | 138 NetErrorHelperCore::NetErrorHelperCore(Delegate* delegate) |
120 : delegate_(delegate), | 139 : delegate_(delegate), |
121 last_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE) { | 140 last_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE), |
| 141 auto_reload_enabled_(false), |
| 142 auto_reload_timer_(new MockableOneShotTimer()), |
| 143 online_(true), |
| 144 auto_reload_count_(0), |
| 145 can_auto_reload_page_(false) { |
122 } | 146 } |
123 | 147 |
124 NetErrorHelperCore::~NetErrorHelperCore() { | 148 NetErrorHelperCore::~NetErrorHelperCore() { |
125 } | 149 } |
126 | 150 |
| 151 void NetErrorHelperCore::CancelPendingFetches() { |
| 152 // Cancel loading the alternate error page, and prevent any pending error page |
| 153 // load from starting a new error page load. Swapping in the error page when |
| 154 // it's finished loading could abort the navigation, otherwise. |
| 155 if (committed_error_page_info_ && can_auto_reload_page_) { |
| 156 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtStop", |
| 157 committed_error_page_info_->error.reason, |
| 158 net::GetAllErrorCodesForUma()); |
| 159 UMA_HISTOGRAM_COUNTS("Net.AutoReload.CountAtStop", auto_reload_count_); |
| 160 } |
| 161 if (committed_error_page_info_) { |
| 162 committed_error_page_info_->alternate_error_page_url = GURL(); |
| 163 } |
| 164 if (pending_error_page_info_) { |
| 165 pending_error_page_info_->alternate_error_page_url = GURL(); |
| 166 } |
| 167 delegate_->CancelFetchErrorPage(); |
| 168 auto_reload_timer_->Stop(); |
| 169 } |
| 170 |
127 void NetErrorHelperCore::OnStop() { | 171 void NetErrorHelperCore::OnStop() { |
128 // On stop, cancel loading the alternate error page, and prevent any pending | 172 CancelPendingFetches(); |
129 // error page load from starting a new error page load. Swapping in the error | 173 auto_reload_count_ = 0; |
130 // page when it's finished loading could abort the navigation, otherwise. | 174 can_auto_reload_page_ = false; |
131 if (committed_error_page_info_) | |
132 committed_error_page_info_->alternate_error_page_url = GURL(); | |
133 if (pending_error_page_info_) | |
134 pending_error_page_info_->alternate_error_page_url = GURL(); | |
135 delegate_->CancelFetchErrorPage(); | |
136 } | 175 } |
137 | 176 |
138 void NetErrorHelperCore::OnStartLoad(FrameType frame_type, PageType page_type) { | 177 void NetErrorHelperCore::OnStartLoad(FrameType frame_type, PageType page_type) { |
139 if (frame_type != MAIN_FRAME) | 178 if (frame_type != MAIN_FRAME) |
140 return; | 179 return; |
141 | 180 |
142 // If there's no pending error page information associated with the page load, | 181 // If there's no pending error page information associated with the page load, |
143 // or the new page is not an error page, then reset pending error page state. | 182 // or the new page is not an error page, then reset pending error page state. |
144 if (!pending_error_page_info_ || page_type != ERROR_PAGE) { | 183 if (!pending_error_page_info_ || page_type != ERROR_PAGE) { |
145 OnStop(); | 184 CancelPendingFetches(); |
| 185 // If a non-error load is starting, don't try to auto-reload while it is in |
| 186 // flight. |
| 187 can_auto_reload_page_ = false; |
| 188 } else { |
| 189 // If an error load is starting, the resulting error page is autoreloadable. |
| 190 can_auto_reload_page_ = true; |
146 } | 191 } |
147 } | 192 } |
148 | 193 |
149 void NetErrorHelperCore::OnCommitLoad(FrameType frame_type) { | 194 void NetErrorHelperCore::OnCommitLoad(FrameType frame_type) { |
150 if (frame_type != MAIN_FRAME) | 195 if (frame_type != MAIN_FRAME) |
151 return; | 196 return; |
152 | 197 |
| 198 if (committed_error_page_info_ && !pending_error_page_info_ && |
| 199 can_auto_reload_page_) { |
| 200 int reason = committed_error_page_info_->error.reason; |
| 201 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtSuccess", |
| 202 reason, |
| 203 net::GetAllErrorCodesForUma()); |
| 204 UMA_HISTOGRAM_COUNTS("Net.AutoReload.CountAtSuccess", auto_reload_count_); |
| 205 if (auto_reload_count_ == 1) { |
| 206 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtFirstSuccess", |
| 207 reason, |
| 208 net::GetAllErrorCodesForUma()); |
| 209 } |
| 210 } |
| 211 |
153 committed_error_page_info_.reset(pending_error_page_info_.release()); | 212 committed_error_page_info_.reset(pending_error_page_info_.release()); |
154 } | 213 } |
155 | 214 |
156 void NetErrorHelperCore::OnFinishLoad(FrameType frame_type) { | 215 void NetErrorHelperCore::OnFinishLoad(FrameType frame_type) { |
157 if (frame_type != MAIN_FRAME || !committed_error_page_info_) | 216 if (frame_type != MAIN_FRAME) |
158 return; | 217 return; |
159 | 218 |
| 219 if (!committed_error_page_info_) { |
| 220 auto_reload_count_ = 0; |
| 221 return; |
| 222 } |
| 223 |
160 committed_error_page_info_->is_finished_loading = true; | 224 committed_error_page_info_->is_finished_loading = true; |
161 | 225 |
162 if (committed_error_page_info_->alternate_error_page_url.is_valid()) { | 226 if (committed_error_page_info_->alternate_error_page_url.is_valid()) { |
163 // If there is another pending error page load, | 227 // If there is another pending error page load, |
164 // |replace_with_alternate_error_page| should have been set to false. | 228 // |replace_with_alternate_error_page| should have been set to false. |
165 DCHECK(!pending_error_page_info_); | 229 DCHECK(!pending_error_page_info_); |
166 DCHECK(!committed_error_page_info_->needs_dns_updates); | 230 DCHECK(!committed_error_page_info_->needs_dns_updates); |
167 GURL error_page_url; | 231 GURL error_page_url; |
168 delegate_->FetchErrorPage( | 232 delegate_->FetchErrorPage( |
169 committed_error_page_info_->alternate_error_page_url); | 233 committed_error_page_info_->alternate_error_page_url); |
| 234 } else if (auto_reload_enabled_ && |
| 235 IsReloadableError(*committed_error_page_info_)) { |
| 236 can_auto_reload_page_ = true; |
| 237 MaybeStartAutoReloadTimer(); |
170 } | 238 } |
171 | 239 |
172 if (!committed_error_page_info_->needs_dns_updates || | 240 if (!committed_error_page_info_->needs_dns_updates || |
173 last_probe_status_ == chrome_common_net::DNS_PROBE_POSSIBLE) { | 241 last_probe_status_ == chrome_common_net::DNS_PROBE_POSSIBLE) { |
174 return; | 242 return; |
175 } | 243 } |
176 DVLOG(1) << "Error page finished loading; sending saved status."; | 244 DVLOG(1) << "Error page finished loading; sending saved status."; |
177 UpdateErrorPage(); | 245 UpdateErrorPage(); |
178 } | 246 } |
179 | 247 |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
310 | 378 |
311 blink::WebURLError updated_error; | 379 blink::WebURLError updated_error; |
312 updated_error.domain = blink::WebString::fromUTF8( | 380 updated_error.domain = blink::WebString::fromUTF8( |
313 chrome_common_net::kDnsProbeErrorDomain); | 381 chrome_common_net::kDnsProbeErrorDomain); |
314 updated_error.reason = last_probe_status_; | 382 updated_error.reason = last_probe_status_; |
315 updated_error.unreachableURL = error.unreachableURL; | 383 updated_error.unreachableURL = error.unreachableURL; |
316 updated_error.staleCopyInCache = error.staleCopyInCache; | 384 updated_error.staleCopyInCache = error.staleCopyInCache; |
317 | 385 |
318 return updated_error; | 386 return updated_error; |
319 } | 387 } |
| 388 |
| 389 void NetErrorHelperCore::Reload() { |
| 390 if (!committed_error_page_info_) { |
| 391 return; |
| 392 } |
| 393 delegate_->ReloadPage(); |
| 394 } |
| 395 |
| 396 bool NetErrorHelperCore::MaybeStartAutoReloadTimer() { |
| 397 if (!committed_error_page_info_ || !can_auto_reload_page_) |
| 398 return false; |
| 399 |
| 400 DCHECK(IsReloadableError(*committed_error_page_info_)); |
| 401 |
| 402 if (!online_) |
| 403 return false; |
| 404 |
| 405 StartAutoReloadTimer(); |
| 406 return true; |
| 407 } |
| 408 |
| 409 void NetErrorHelperCore::StartAutoReloadTimer() { |
| 410 DCHECK(committed_error_page_info_); |
| 411 DCHECK(can_auto_reload_page_); |
| 412 base::TimeDelta delay = GetAutoReloadTime(auto_reload_count_); |
| 413 auto_reload_count_++; |
| 414 auto_reload_timer_->Stop(); |
| 415 auto_reload_timer_->Start(FROM_HERE, delay, |
| 416 base::Bind(&NetErrorHelperCore::Reload, |
| 417 base::Unretained(this))); |
| 418 } |
| 419 |
| 420 void NetErrorHelperCore::NetworkStateChanged(bool online) { |
| 421 online_ = online; |
| 422 if (auto_reload_timer_->IsRunning()) { |
| 423 DCHECK(committed_error_page_info_); |
| 424 // If there's an existing timer running, stop it and reset the retry count. |
| 425 auto_reload_timer_->Stop(); |
| 426 auto_reload_count_ = 0; |
| 427 } |
| 428 |
| 429 // If the network state changed to online, maybe start auto-reloading again. |
| 430 if (online) |
| 431 MaybeStartAutoReloadTimer(); |
| 432 } |
| 433 |
| 434 bool NetErrorHelperCore::ShouldSuppressErrorPage(FrameType frame_type, |
| 435 const GURL& url) { |
| 436 // Don't suppress child frame errors. |
| 437 if (frame_type != MAIN_FRAME) |
| 438 return false; |
| 439 |
| 440 // If |auto_reload_timer_| is still running, this error page isn't from an |
| 441 // auto reload. |
| 442 if (auto_reload_timer_->IsRunning()) |
| 443 return false; |
| 444 |
| 445 // If there's no committed error page, this error page wasn't from an auto |
| 446 // reload. |
| 447 if (!committed_error_page_info_ || !can_auto_reload_page_) |
| 448 return false; |
| 449 |
| 450 GURL error_url = committed_error_page_info_->error.unreachableURL; |
| 451 if (error_url != url) |
| 452 return false; |
| 453 |
| 454 MaybeStartAutoReloadTimer(); |
| 455 return true; |
| 456 } |
OLD | NEW |