Index: chrome/renderer/net/net_error_helper_core.cc |
diff --git a/chrome/renderer/net/net_error_helper_core.cc b/chrome/renderer/net/net_error_helper_core.cc |
index 55cb771c9e50b103835ff0cf013367e5c7c39bff..92d319b30274571e2c339bbe39281f30432923c4 100644 |
--- a/chrome/renderer/net/net_error_helper_core.cc |
+++ b/chrome/renderer/net/net_error_helper_core.cc |
@@ -6,6 +6,9 @@ |
#include <string> |
+#include "base/bind.h" |
+#include "base/callback.h" |
+#include "base/location.h" |
#include "base/metrics/histogram.h" |
#include "chrome/common/localized_error.h" |
#include "net/base/escape.h" |
@@ -16,6 +19,15 @@ |
namespace { |
+base::TimeDelta GetAutoReloadTime(size_t reload_count) { |
+ static const int kDelaysMs[] = { |
+ 0, 5000, 30000, 60000, 300000, 600000, 1800000 |
+ }; |
+ if (reload_count >= arraysize(kDelaysMs)) |
+ reload_count = arraysize(kDelaysMs) - 1; |
+ return base::TimeDelta::FromMilliseconds(kDelaysMs[reload_count]); |
+} |
+ |
// Returns whether |net_error| is a DNS-related error (and therefore whether |
// the tab helper should start a DNS probe after receiving it.) |
bool IsDnsError(const blink::WebURLError& error) { |
@@ -24,6 +36,11 @@ bool IsDnsError(const blink::WebURLError& error) { |
error.reason == net::ERR_NAME_RESOLUTION_FAILED); |
} |
+bool IsReloadableError(const blink::WebURLError& error) { |
+ return error.domain.utf8() == net::kErrorDomain && |
+ error.reason != net::ERR_ABORTED; |
+} |
+ |
// If an alternate error page should be retrieved remotely for a main frame load |
// that failed with |error|, returns true and sets |error_page_url| to the URL |
// of the remote error page. |
@@ -116,23 +133,45 @@ struct NetErrorHelperCore::ErrorPageInfo { |
bool is_finished_loading; |
}; |
-NetErrorHelperCore::NetErrorHelperCore(Delegate* delegate) |
+NetErrorHelperCore::NetErrorHelperCore(Delegate* delegate, |
+ scoped_ptr<MockableOneShotTimer> reload_timer) |
: delegate_(delegate), |
- last_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE) { |
+ last_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE), |
+ auto_reload_enabled_(false), |
+ auto_reload_timer_(reload_timer.Pass()), |
+ auto_reload_count_(0), |
+ auto_reload_pending_(false), |
+ online_(true) { |
+ LOG(ERROR) << "NetErrorHelperCore: allocated"; |
} |
NetErrorHelperCore::~NetErrorHelperCore() { |
} |
-void NetErrorHelperCore::OnStop() { |
- // On stop, cancel loading the alternate error page, and prevent any pending |
- // error page load from starting a new error page load. Swapping in the error |
- // page when it's finished loading could abort the navigation, otherwise. |
- if (committed_error_page_info_) |
+void NetErrorHelperCore::CancelPendingFetches() { |
+ // Cancel loading the alternate error page, and prevent any pending error page |
+ // load from starting a new error page load. Swapping in the error page when |
+ // it's finished loading could abort the navigation, otherwise. |
+ if (committed_error_page_info_) { |
committed_error_page_info_->alternate_error_page_url = GURL(); |
+ } |
if (pending_error_page_info_) |
pending_error_page_info_->alternate_error_page_url = GURL(); |
delegate_->CancelFetchErrorPage(); |
+ auto_reload_timer_->Stop(); |
+} |
+ |
+void NetErrorHelperCore::OnStop() { |
+ CancelPendingFetches(); |
+ if (auto_reload_pending_) { |
+ HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtStop", |
+ committed_error_page_info_->error.reason, |
+ net::GetAllErrorCodesForUma()); |
+ HISTOGRAM_COUNTS("Net.AutoReload.CountAtStop", |
+ auto_reload_count_); |
+ } |
+ auto_reload_count_ = 0; |
+ auto_reload_pending_ = false; |
} |
void NetErrorHelperCore::OnStartLoad(FrameType frame_type, PageType page_type) { |
@@ -142,7 +181,7 @@ void NetErrorHelperCore::OnStartLoad(FrameType frame_type, PageType page_type) { |
// If there's no pending error page information associated with the page load, |
// or the new page is not an error page, then reset pending error page state. |
if (!pending_error_page_info_ || page_type != ERROR_PAGE) { |
- OnStop(); |
+ CancelPendingFetches(); |
} |
} |
@@ -150,12 +189,32 @@ void NetErrorHelperCore::OnCommitLoad(FrameType frame_type) { |
if (frame_type != MAIN_FRAME) |
return; |
+ if (committed_error_page_info_ && !pending_error_page_info_ && |
+ auto_reload_pending_) { |
+ int reason = committed_error_page_info_->error.reason; |
+ HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtSuccess", |
+ reason, |
+ net::GetAllErrorCodesForUma()); |
+ if (auto_reload_count_ == 1) |
+ HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtFirstSuccess", |
+ reason, |
+ net::GetAllErrorCodesForUma()); |
+ } |
+ |
committed_error_page_info_.reset(pending_error_page_info_.release()); |
} |
void NetErrorHelperCore::OnFinishLoad(FrameType frame_type) { |
- if (frame_type != MAIN_FRAME || !committed_error_page_info_) |
+ if (frame_type != MAIN_FRAME) |
+ return; |
+ |
+ if (!committed_error_page_info_) { |
+ if (auto_reload_pending_) |
+ HISTOGRAM_COUNTS("Net.AutoReload.CountAtSuccess", auto_reload_count_); |
Randy Smith (Not in Mondays)
2014/02/26 00:07:38
Suggestion: I'd be inclined to move this histogram
Elly Fong-Jones
2014/03/03 19:31:07
Done.
|
+ auto_reload_count_ = 0; |
+ auto_reload_pending_ = false; |
return; |
+ } |
committed_error_page_info_->is_finished_loading = true; |
@@ -167,6 +226,9 @@ void NetErrorHelperCore::OnFinishLoad(FrameType frame_type) { |
GURL error_page_url; |
delegate_->FetchErrorPage( |
committed_error_page_info_->alternate_error_page_url); |
+ } else if (auto_reload_enabled_) { |
+ auto_reload_pending_ = true; |
+ MaybeStartAutoReloadTimer(); |
} |
if (!committed_error_page_info_->needs_dns_updates || |
@@ -316,3 +378,85 @@ blink::WebURLError NetErrorHelperCore::GetUpdatedError( |
return updated_error; |
} |
+ |
+void NetErrorHelperCore::Reload() { |
+ if (!committed_error_page_info_) { |
+ return; |
+ } |
+ delegate_->ReloadPage(); |
+} |
+ |
+bool NetErrorHelperCore::MaybeStartAutoReloadTimer() { |
+ if (!committed_error_page_info_) |
+ return false; |
+ |
+ if (!IsReloadableError(committed_error_page_info_->error)) |
+ return false; |
+ |
+ if (committed_error_page_info_->was_failed_post) |
+ return false; |
+ |
+ if (!online_) |
+ return false; |
+ |
+ if (!auto_reload_pending_) |
+ return false; |
+ |
+ StartAutoReloadTimer(); |
+ return true; |
+} |
+ |
+void NetErrorHelperCore::StartAutoReloadTimer() { |
+ base::TimeDelta delay = GetAutoReloadTime(auto_reload_count_); |
+ auto_reload_count_++; |
+ auto_reload_timer_->Stop(); |
+ auto_reload_timer_->Start(FROM_HERE, delay, |
+ base::Bind(&NetErrorHelperCore::Reload, |
+ base::Unretained(this))); |
+} |
+ |
+// Handler for NetworkStateChanged notification from the browser process. If the |
+// network state changes to online, this method is responsible for starting the |
+// auto-reload process. |
+// |
+// Warning: if there are many tabs sitting at an error page, this handler will |
+// be run at the same time for each of their top-level renderframes, which can |
+// cause many requests to be started at the same time. There's no current |
+// protection against this kind of "reload storm". |
+// |
+// TODO(rdsmith): prevent the reload storm. |
+void NetErrorHelperCore::NetworkStateChanged(bool online) { |
+ online_ = online; |
+ if (auto_reload_timer_->IsRunning()) { |
+ // If there's an existing timer running, stop it and reset the retry count. |
+ auto_reload_timer_->Stop(); |
+ auto_reload_count_ = 0; |
+ } |
+ |
+ if (online) |
+ // If we just got online, maybe start auto-reloading again. |
+ MaybeStartAutoReloadTimer(); |
mmenke
2014/02/25 22:09:52
Use braces for multi-line if blocks, or put the co
Elly Fong-Jones
2014/03/03 19:31:07
Done.
|
+} |
+ |
+bool NetErrorHelperCore::ShouldSuppressErrorPage(const GURL& url) { |
+ // Don't suppress errors if not auto-reloading. |
+ if (!auto_reload_pending_) |
+ return false; |
+ |
+ // If auto_reload_timer_ is still running, this error page isn't from an auto |
+ // reload. |
+ if (auto_reload_timer_->IsRunning()) |
+ return false; |
+ |
+ // If there's no committed error page, this error page wasn't from an auto |
+ // reload. |
+ if (!committed_error_page_info_) |
+ return false; |
+ |
+ GURL error_url = committed_error_page_info_->error.unreachableURL; |
+ if (error_url != url) |
+ return false; |
+ |
+ MaybeStartAutoReloadTimer(); |
+ return true; |
+} |