summaryrefslogtreecommitdiffstats
path: root/components/error_page
diff options
context:
space:
mode:
authorhashimoto <hashimoto@chromium.org>2014-10-14 20:56:53 -0700
committerCommit bot <commit-bot@chromium.org>2014-10-15 03:57:40 +0000
commit9b160e24e7f967dda04cac13d393124ba173a0e8 (patch)
tree8ab2a11f55aa1886b158c636349b774e65580eb2 /components/error_page
parent3517bb6c601efad861dddcf01a16e2c68bf927c7 (diff)
downloadchromium_src-9b160e24e7f967dda04cac13d393124ba173a0e8.zip
chromium_src-9b160e24e7f967dda04cac13d393124ba173a0e8.tar.gz
chromium_src-9b160e24e7f967dda04cac13d393124ba173a0e8.tar.bz2
Componentize NetErrorHelperCore
Create a new component error_page which will host NetErrorHelper to make it usable from binaries other than chrome. Move NetErrorHelperCore and net_error_info.{cc,h} to components/error_page. Split LocalizedError::ErrorPageParams to components/error_page/common/error_page_params. Move string resources to error_page_strings.grdp Fix GYP, GN, DEPS. Copy OWNERS from chrome/renderer/net to components/error_page. BUG=398173 TEST=build TBR=sky@chromium.org for +ui/base in DEPS Review URL: https://codereview.chromium.org/570253002 Cr-Commit-Position: refs/heads/master@{#299647}
Diffstat (limited to 'components/error_page')
-rw-r--r--components/error_page/OWNERS2
-rw-r--r--components/error_page/common/BUILD.gn17
-rw-r--r--components/error_page/common/error_page_params.cc20
-rw-r--r--components/error_page/common/error_page_params.h45
-rw-r--r--components/error_page/common/net_error_info.cc46
-rw-r--r--components/error_page/common/net_error_info.h92
-rw-r--r--components/error_page/renderer/BUILD.gn21
-rw-r--r--components/error_page/renderer/DEPS7
-rw-r--r--components/error_page/renderer/net_error_helper_core.cc952
-rw-r--r--components/error_page/renderer/net_error_helper_core.h271
-rw-r--r--components/error_page/renderer/net_error_helper_core_unittest.cc2436
11 files changed, 3909 insertions, 0 deletions
diff --git a/components/error_page/OWNERS b/components/error_page/OWNERS
new file mode 100644
index 0000000..a479fb9
--- /dev/null
+++ b/components/error_page/OWNERS
@@ -0,0 +1,2 @@
+mmenke@chromium.org
+ttuttle@chromium.org
diff --git a/components/error_page/common/BUILD.gn b/components/error_page/common/BUILD.gn
new file mode 100644
index 0000000..b3c5b88
--- /dev/null
+++ b/components/error_page/common/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("common") {
+ sources = [
+ "error_page_params.cc",
+ "error_page_params.h",
+ "net_error_info.cc",
+ "net_error_info.h",
+ ]
+
+ deps = [
+ "//base",
+ "//url",
+ ]
+} \ No newline at end of file
diff --git a/components/error_page/common/error_page_params.cc b/components/error_page/common/error_page_params.cc
new file mode 100644
index 0000000..a6d3c42
--- /dev/null
+++ b/components/error_page/common/error_page_params.cc
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/error_page/common/error_page_params.h"
+
+#include "base/values.h"
+
+namespace error_page {
+
+ErrorPageParams::ErrorPageParams()
+ : suggest_reload(false),
+ reload_tracking_id(-1),
+ search_tracking_id(-1) {
+}
+
+ErrorPageParams::~ErrorPageParams() {
+}
+
+} // namespace error_page
diff --git a/components/error_page/common/error_page_params.h b/components/error_page/common/error_page_params.h
new file mode 100644
index 0000000..c0b2203
--- /dev/null
+++ b/components/error_page/common/error_page_params.h
@@ -0,0 +1,45 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_NET_ERROR_COMMON_ERROR_PAGE_PARAMS_H_
+#define COMPONENTS_NET_ERROR_COMMON_ERROR_PAGE_PARAMS_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "url/gurl.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace error_page {
+
+// Optional parameters that affect the display of an error page.
+struct ErrorPageParams {
+ ErrorPageParams();
+ ~ErrorPageParams();
+
+ // Overrides whether reloading is suggested.
+ bool suggest_reload;
+ int reload_tracking_id;
+
+ // Overrides default suggestions. Each entry must be a DictionaryValuethat
+ // contains a "header" entry. A "body" entry may optionally be specified.
+ // JSTemplate evaluation will be applied when added to the DOM. If NULL, the
+ // default suggestions will be used.
+ scoped_ptr<base::ListValue> override_suggestions;
+
+ // Prefix to prepend to search terms. Search box is only shown if this is
+ // a valid url. The search terms will be appended to the end of this URL to
+ // conduct a search.
+ GURL search_url;
+ // Default search terms. Ignored if |search_url| is invalid.
+ std::string search_terms;
+ int search_tracking_id;
+};
+
+} // namespace error_page
+
+#endif // COMPONENTS_NET_ERROR_COMMON_ERROR_PAGE_PARAMS_H_
diff --git a/components/error_page/common/net_error_info.cc b/components/error_page/common/net_error_info.cc
new file mode 100644
index 0000000..d47e7e5
--- /dev/null
+++ b/components/error_page/common/net_error_info.cc
@@ -0,0 +1,46 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/error_page/common/net_error_info.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+
+namespace chrome_common_net {
+
+const char kDnsProbeErrorDomain[] = "dnsprobe";
+
+const char* DnsProbeStatusToString(int status) {
+ switch (status) {
+ case DNS_PROBE_POSSIBLE:
+ return "DNS_PROBE_POSSIBLE";
+ case DNS_PROBE_NOT_RUN:
+ return "DNS_PROBE_NOT_RUN";
+ case DNS_PROBE_STARTED:
+ return "DNS_PROBE_STARTED";
+ case DNS_PROBE_FINISHED_INCONCLUSIVE:
+ return "DNS_PROBE_FINISHED_INCONCLUSIVE";
+ case DNS_PROBE_FINISHED_NO_INTERNET:
+ return "DNS_PROBE_FINISHED_NO_INTERNET";
+ case DNS_PROBE_FINISHED_BAD_CONFIG:
+ return "DNS_PROBE_FINISHED_BAD_CONFIG";
+ case DNS_PROBE_FINISHED_NXDOMAIN:
+ return "DNS_PROBE_FINISHED_NXDOMAIN";
+ default:
+ NOTREACHED();
+ return "";
+ }
+}
+
+bool DnsProbeStatusIsFinished(DnsProbeStatus status) {
+ return status >= DNS_PROBE_FINISHED_INCONCLUSIVE &&
+ status < DNS_PROBE_MAX;
+}
+
+void RecordEvent(NetworkErrorPageEvent event) {
+ UMA_HISTOGRAM_ENUMERATION("Net.ErrorPageCounts", event,
+ NETWORK_ERROR_PAGE_EVENT_MAX);
+}
+
+} // namespace chrome_common_net
diff --git a/components/error_page/common/net_error_info.h b/components/error_page/common/net_error_info.h
new file mode 100644
index 0000000..35433ff
--- /dev/null
+++ b/components/error_page/common/net_error_info.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_ERROR_PAGE_COMMON_NET_ERROR_INFO_H_
+#define COMPONENTS_ERROR_PAGE_COMMON_NET_ERROR_INFO_H_
+
+// TODO(hashimoto): Change this to namespace error_page.
+namespace chrome_common_net {
+
+// Network error page events. Used for UMA statistics.
+enum NetworkErrorPageEvent {
+ NETWORK_ERROR_PAGE_SHOWN, // Error pages shown.
+
+ NETWORK_ERROR_PAGE_RELOAD_BUTTON_SHOWN, // Reload buttons shown.
+ NETWORK_ERROR_PAGE_RELOAD_BUTTON_CLICKED, // Reload button clicked.
+ NETWORK_ERROR_PAGE_RELOAD_BUTTON_ERROR, // Reload button clicked
+ // -> error.
+
+ NETWORK_ERROR_PAGE_LOAD_STALE_BUTTON_SHOWN, // Load stale buttons shown.
+ NETWORK_ERROR_PAGE_LOAD_STALE_BUTTON_CLICKED, // Load stale button clicked.
+ NETWORK_ERROR_PAGE_LOAD_STALE_BUTTON_ERROR, // Load stale buttons -> error.
+
+ NETWORK_ERROR_PAGE_MORE_BUTTON_CLICKED, // More button clicked.
+
+ NETWORK_ERROR_PAGE_BROWSER_INITIATED_RELOAD, // Reload from browser.
+
+ NETWORK_ERROR_PAGE_EVENT_MAX,
+};
+
+// The status of a DNS probe.
+//
+// The DNS_PROBE_FINISHED_* values are used in histograms, so:
+// 1. FINISHED_UNKNOWN must remain the first FINISHED_* value.
+// 2. FINISHED_* values must not be rearranged relative to FINISHED_UNKNOWN.
+// 3. New FINISHED_* values must be inserted at the end.
+// 4. New non-FINISHED_* values cannot be inserted.
+enum DnsProbeStatus {
+ // A DNS probe may be run for this error page. (This status is only used on
+ // the renderer side before it's received a status update from the browser.)
+ DNS_PROBE_POSSIBLE,
+
+ // A DNS probe will not be run for this error page. (This happens if the
+ // user has the "Use web service to resolve navigation errors" preference
+ // turned off, or if probes are disabled by the field trial.)
+ DNS_PROBE_NOT_RUN,
+
+ // A DNS probe has been started for this error page. The renderer should
+ // expect to receive another IPC with one of the FINISHED statuses once the
+ // probe has finished (as long as the error page is still loaded).
+ DNS_PROBE_STARTED,
+
+ // A DNS probe has finished with one of the following results:
+
+ // The probe was inconclusive.
+ DNS_PROBE_FINISHED_INCONCLUSIVE,
+
+ // There's no internet connection.
+ DNS_PROBE_FINISHED_NO_INTERNET,
+
+ // The DNS configuration is wrong, or the servers are down or broken.
+ DNS_PROBE_FINISHED_BAD_CONFIG,
+
+ // The DNS servers are working fine, so the domain must not exist.
+ DNS_PROBE_FINISHED_NXDOMAIN,
+
+ DNS_PROBE_MAX
+};
+
+// Returns a string representing |status|. It should be simply the name of
+// the value as a string, but don't rely on that. This is presented to the
+// user as part of the DNS error page (as the error code, at the bottom),
+// and is also used in some verbose log messages.
+//
+// The function will NOTREACHED() and return an empty string if given an int
+// that does not match a value in DnsProbeStatus (or if it is DNS_PROBE_MAX,
+// which is not a real status).
+const char* DnsProbeStatusToString(int status);
+
+// Returns true if |status| is one of the DNS_PROBE_FINISHED_* statuses.
+bool DnsProbeStatusIsFinished(DnsProbeStatus status);
+
+// Record specific error page events.
+void RecordEvent(NetworkErrorPageEvent event);
+
+// The error domain used to pass DNS probe statuses to the localized error
+// code.
+extern const char kDnsProbeErrorDomain[];
+
+} // namespace chrome_common_net
+
+#endif // COMPONENTS_ERROR_PAGE_COMMON_NET_ERROR_INFO_H_
diff --git a/components/error_page/renderer/BUILD.gn b/components/error_page/renderer/BUILD.gn
new file mode 100644
index 0000000..96fe7ff
--- /dev/null
+++ b/components/error_page/renderer/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("renderer") {
+ sources = [
+ "net_error_helper_core.cc",
+ "net_error_helper_core.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/error_page/common",
+ "//components/strings",
+ "//content/public/common",
+ "//net",
+ "//third_party/WebKit/public:blink",
+ "//ui/base",
+ "//url",
+ ]
+} \ No newline at end of file
diff --git a/components/error_page/renderer/DEPS b/components/error_page/renderer/DEPS
new file mode 100644
index 0000000..5d271a9
--- /dev/null
+++ b/components/error_page/renderer/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+ "+content/public/common",
+ "+grit/components_strings.h",
+ "+net",
+ "+third_party/WebKit/public/platform",
+ "+ui/base",
+]
diff --git a/components/error_page/renderer/net_error_helper_core.cc b/components/error_page/renderer/net_error_helper_core.cc
new file mode 100644
index 0000000..93f943e
--- /dev/null
+++ b/components/error_page/renderer/net_error_helper_core.cc
@@ -0,0 +1,952 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/error_page/renderer/net_error_helper_core.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/i18n/rtl.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_value_converter.h"
+#include "base/json/json_writer.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "components/error_page/common/error_page_params.h"
+#include "content/public/common/url_constants.h"
+#include "grit/components_strings.h"
+#include "net/base/escape.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/WebURLError.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+namespace error_page {
+
+namespace {
+
+struct CorrectionTypeToResourceTable {
+ int resource_id;
+ const char* correction_type;
+};
+
+const CorrectionTypeToResourceTable kCorrectionResourceTable[] = {
+ {IDS_ERRORPAGES_SUGGESTION_VISIT_GOOGLE_CACHE, "cachedPage"},
+ // "reloadPage" is has special handling.
+ {IDS_ERRORPAGES_SUGGESTION_CORRECTED_URL, "urlCorrection"},
+ {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL, "siteDomain"},
+ {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL, "host"},
+ {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL, "sitemap"},
+ {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL, "pathParentFolder"},
+ // "siteSearchQuery" is not yet supported.
+ // TODO(mmenke): Figure out what format "siteSearchQuery" uses for its
+ // suggestions.
+ // "webSearchQuery" has special handling.
+ {IDS_ERRORPAGES_SUGGESTION_ALTERNATE_URL, "contentOverlap"},
+ {IDS_ERRORPAGES_SUGGESTION_CORRECTED_URL, "emphasizedUrlCorrection"},
+};
+
+struct NavigationCorrection {
+ NavigationCorrection() : is_porn(false), is_soft_porn(false) {
+ }
+
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<NavigationCorrection>* converter) {
+ converter->RegisterStringField("correctionType",
+ &NavigationCorrection::correction_type);
+ converter->RegisterStringField("urlCorrection",
+ &NavigationCorrection::url_correction);
+ converter->RegisterStringField("clickType",
+ &NavigationCorrection::click_type);
+ converter->RegisterStringField("clickData",
+ &NavigationCorrection::click_data);
+ converter->RegisterBoolField("isPorn", &NavigationCorrection::is_porn);
+ converter->RegisterBoolField("isSoftPorn",
+ &NavigationCorrection::is_soft_porn);
+ }
+
+ std::string correction_type;
+ std::string url_correction;
+ std::string click_type;
+ std::string click_data;
+ bool is_porn;
+ bool is_soft_porn;
+};
+
+struct NavigationCorrectionResponse {
+ std::string event_id;
+ std::string fingerprint;
+ ScopedVector<NavigationCorrection> corrections;
+
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<NavigationCorrectionResponse>* converter) {
+ converter->RegisterStringField("result.eventId",
+ &NavigationCorrectionResponse::event_id);
+ converter->RegisterStringField("result.fingerprint",
+ &NavigationCorrectionResponse::fingerprint);
+ converter->RegisterRepeatedMessage(
+ "result.UrlCorrections",
+ &NavigationCorrectionResponse::corrections);
+ }
+};
+
+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) {
+ return error.domain.utf8() == net::kErrorDomain &&
+ (error.reason == net::ERR_NAME_NOT_RESOLVED ||
+ error.reason == net::ERR_NAME_RESOLUTION_FAILED);
+}
+
+GURL SanitizeURL(const GURL& url) {
+ GURL::Replacements remove_params;
+ remove_params.ClearUsername();
+ remove_params.ClearPassword();
+ remove_params.ClearQuery();
+ remove_params.ClearRef();
+ return url.ReplaceComponents(remove_params);
+}
+
+// Sanitizes and formats a URL for upload to the error correction service.
+std::string PrepareUrlForUpload(const GURL& url) {
+ // TODO(yuusuke): Change to net::FormatUrl when Link Doctor becomes
+ // unicode-capable.
+ std::string spec_to_send = SanitizeURL(url).spec();
+
+ // Notify navigation correction service of the url truncation by sending of
+ // "?" at the end.
+ if (url.has_query())
+ spec_to_send.append("?");
+ return spec_to_send;
+}
+
+// Given a WebURLError, returns true if the FixURL service should be used
+// for that error. Also sets |error_param| to the string that should be sent to
+// the FixURL service to identify the error type.
+bool ShouldUseFixUrlServiceForError(const blink::WebURLError& error,
+ std::string* error_param) {
+ error_param->clear();
+
+ // Don't use the correction service for HTTPS (for privacy reasons).
+ GURL unreachable_url(error.unreachableURL);
+ if (GURL(unreachable_url).SchemeIsSecure())
+ return false;
+
+ std::string domain = error.domain.utf8();
+ if (domain == "http" && error.reason == 404) {
+ *error_param = "http404";
+ return true;
+ }
+ if (IsDnsError(error)) {
+ *error_param = "dnserror";
+ return true;
+ }
+ if (domain == net::kErrorDomain &&
+ (error.reason == net::ERR_CONNECTION_FAILED ||
+ error.reason == net::ERR_CONNECTION_REFUSED ||
+ error.reason == net::ERR_ADDRESS_UNREACHABLE ||
+ error.reason == net::ERR_CONNECTION_TIMED_OUT)) {
+ *error_param = "connectionFailure";
+ return true;
+ }
+ return false;
+}
+
+// Creates a request body for use with the fixurl service. Sets parameters
+// shared by all types of requests to the service. |correction_params| must
+// contain the parameters specific to the actual request type.
+std::string CreateRequestBody(
+ const std::string& method,
+ const std::string& error_param,
+ const NetErrorHelperCore::NavigationCorrectionParams& correction_params,
+ scoped_ptr<base::DictionaryValue> params_dict) {
+ // Set params common to all request types.
+ params_dict->SetString("key", correction_params.api_key);
+ params_dict->SetString("clientName", "chrome");
+ params_dict->SetString("error", error_param);
+
+ if (!correction_params.language.empty())
+ params_dict->SetString("language", correction_params.language);
+
+ if (!correction_params.country_code.empty())
+ params_dict->SetString("originCountry", correction_params.country_code);
+
+ base::DictionaryValue request_dict;
+ request_dict.SetString("method", method);
+ request_dict.SetString("apiVersion", "v1");
+ request_dict.Set("params", params_dict.release());
+
+ std::string request_body;
+ bool success = base::JSONWriter::Write(&request_dict, &request_body);
+ DCHECK(success);
+ return request_body;
+}
+
+// If URL correction information should be retrieved remotely for a main frame
+// load that failed with |error|, returns true and sets
+// |correction_request_body| to be the body for the correction request.
+std::string CreateFixUrlRequestBody(
+ const blink::WebURLError& error,
+ const NetErrorHelperCore::NavigationCorrectionParams& correction_params) {
+ std::string error_param;
+ bool result = ShouldUseFixUrlServiceForError(error, &error_param);
+ DCHECK(result);
+
+ // TODO(mmenke): Investigate open sourcing the relevant protocol buffers and
+ // using those directly instead.
+ scoped_ptr<base::DictionaryValue> params(new base::DictionaryValue());
+ params->SetString("urlQuery", PrepareUrlForUpload(error.unreachableURL));
+ return CreateRequestBody("linkdoctor.fixurl.fixurl", error_param,
+ correction_params, params.Pass());
+}
+
+std::string CreateClickTrackingUrlRequestBody(
+ const blink::WebURLError& error,
+ const NetErrorHelperCore::NavigationCorrectionParams& correction_params,
+ const NavigationCorrectionResponse& response,
+ const NavigationCorrection& correction) {
+ std::string error_param;
+ bool result = ShouldUseFixUrlServiceForError(error, &error_param);
+ DCHECK(result);
+
+ scoped_ptr<base::DictionaryValue> params(new base::DictionaryValue());
+
+ params->SetString("originalUrlQuery",
+ PrepareUrlForUpload(error.unreachableURL));
+
+ params->SetString("clickedUrlCorrection", correction.url_correction);
+ params->SetString("clickType", correction.click_type);
+ params->SetString("clickData", correction.click_data);
+
+ params->SetString("eventId", response.event_id);
+ params->SetString("fingerprint", response.fingerprint);
+
+ return CreateRequestBody("linkdoctor.fixurl.clicktracking", error_param,
+ correction_params, params.Pass());
+}
+
+base::string16 FormatURLForDisplay(const GURL& url, bool is_rtl,
+ const std::string accept_languages) {
+ // Translate punycode into UTF8, unescape UTF8 URLs.
+ base::string16 url_for_display(net::FormatUrl(
+ url, accept_languages, net::kFormatUrlOmitNothing,
+ net::UnescapeRule::NORMAL, NULL, NULL, NULL));
+ // URLs are always LTR.
+ if (is_rtl)
+ base::i18n::WrapStringWithLTRFormatting(&url_for_display);
+ return url_for_display;
+}
+
+scoped_ptr<NavigationCorrectionResponse> ParseNavigationCorrectionResponse(
+ const std::string raw_response) {
+ // TODO(mmenke): Open source related protocol buffers and use them directly.
+ scoped_ptr<base::Value> parsed(base::JSONReader::Read(raw_response));
+ scoped_ptr<NavigationCorrectionResponse> response(
+ new NavigationCorrectionResponse());
+ base::JSONValueConverter<NavigationCorrectionResponse> converter;
+ if (!parsed || !converter.Convert(*parsed, response.get()))
+ response.reset();
+ return response.Pass();
+}
+
+scoped_ptr<ErrorPageParams> CreateErrorPageParams(
+ const NavigationCorrectionResponse& response,
+ const blink::WebURLError& error,
+ const NetErrorHelperCore::NavigationCorrectionParams& correction_params,
+ const std::string& accept_languages,
+ bool is_rtl) {
+ // Version of URL for display in suggestions. It has to be sanitized first
+ // because any received suggestions will be relative to the sanitized URL.
+ base::string16 original_url_for_display =
+ FormatURLForDisplay(SanitizeURL(GURL(error.unreachableURL)), is_rtl,
+ accept_languages);
+
+ scoped_ptr<ErrorPageParams> params(new ErrorPageParams());
+ params->override_suggestions.reset(new base::ListValue());
+ scoped_ptr<base::ListValue> parsed_corrections(new base::ListValue());
+ for (ScopedVector<NavigationCorrection>::const_iterator it =
+ response.corrections.begin();
+ it != response.corrections.end(); ++it) {
+ // Doesn't seem like a good idea to show these.
+ if ((*it)->is_porn || (*it)->is_soft_porn)
+ continue;
+
+ int tracking_id = it - response.corrections.begin();
+
+ if ((*it)->correction_type == "reloadPage") {
+ params->suggest_reload = true;
+ params->reload_tracking_id = tracking_id;
+ continue;
+ }
+
+ if ((*it)->correction_type == "webSearchQuery") {
+ // If there are mutliple searches suggested, use the first suggestion.
+ if (params->search_terms.empty()) {
+ params->search_url = correction_params.search_url;
+ params->search_terms = (*it)->url_correction;
+ params->search_tracking_id = tracking_id;
+ }
+ continue;
+ }
+
+ // Allow reload page and web search query to be empty strings, but not
+ // links.
+ if ((*it)->url_correction.empty())
+ continue;
+ size_t correction_index;
+ for (correction_index = 0;
+ correction_index < arraysize(kCorrectionResourceTable);
+ ++correction_index) {
+ if ((*it)->correction_type !=
+ kCorrectionResourceTable[correction_index].correction_type) {
+ continue;
+ }
+ base::DictionaryValue* suggest = new base::DictionaryValue();
+ suggest->SetString("header",
+ l10n_util::GetStringUTF16(
+ kCorrectionResourceTable[correction_index].resource_id));
+ suggest->SetString("urlCorrection", (*it)->url_correction);
+ suggest->SetString(
+ "urlCorrectionForDisplay",
+ FormatURLForDisplay(GURL((*it)->url_correction), is_rtl,
+ accept_languages));
+ suggest->SetString("originalUrlForDisplay", original_url_for_display);
+ suggest->SetInteger("trackingId", tracking_id);
+ params->override_suggestions->Append(suggest);
+ break;
+ }
+ }
+
+ if (params->override_suggestions->empty() && !params->search_url.is_valid())
+ params.reset();
+ return params.Pass();
+}
+
+void ReportAutoReloadSuccess(const blink::WebURLError& error, size_t count) {
+ if (error.domain.utf8() != net::kErrorDomain)
+ return;
+ UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtSuccess",
+ -error.reason,
+ net::GetAllErrorCodesForUma());
+ UMA_HISTOGRAM_COUNTS("Net.AutoReload.CountAtSuccess",
+ static_cast<base::HistogramBase::Sample>(count));
+ if (count == 1) {
+ UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtFirstSuccess",
+ -error.reason,
+ net::GetAllErrorCodesForUma());
+ }
+}
+
+void ReportAutoReloadFailure(const blink::WebURLError& error, size_t count) {
+ if (error.domain.utf8() != net::kErrorDomain)
+ return;
+ UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtStop",
+ -error.reason,
+ net::GetAllErrorCodesForUma());
+ UMA_HISTOGRAM_COUNTS("Net.AutoReload.CountAtStop",
+ static_cast<base::HistogramBase::Sample>(count));
+}
+
+} // namespace
+
+struct NetErrorHelperCore::ErrorPageInfo {
+ ErrorPageInfo(blink::WebURLError error, bool was_failed_post)
+ : error(error),
+ was_failed_post(was_failed_post),
+ needs_dns_updates(false),
+ needs_load_navigation_corrections(false),
+ reload_button_in_page(false),
+ load_stale_button_in_page(false),
+ is_finished_loading(false),
+ auto_reload_triggered(false) {
+ }
+
+ // Information about the failed page load.
+ blink::WebURLError error;
+ bool was_failed_post;
+
+ // Information about the status of the error page.
+
+ // True if a page is a DNS error page and has not yet received a final DNS
+ // probe status.
+ bool needs_dns_updates;
+
+ // True if a blank page was loaded, and navigation corrections need to be
+ // loaded to generate the real error page.
+ bool needs_load_navigation_corrections;
+
+ // Navigation correction service paramers, which will be used in response to
+ // certain types of network errors. They are all stored here in case they
+ // change over the course of displaying the error page.
+ scoped_ptr<NetErrorHelperCore::NavigationCorrectionParams>
+ navigation_correction_params;
+
+ scoped_ptr<NavigationCorrectionResponse> navigation_correction_response;
+
+ // All the navigation corrections that have been clicked, for tracking
+ // purposes.
+ std::set<int> clicked_corrections;
+
+ // Track if specific buttons are included in an error page, for statistics.
+ bool reload_button_in_page;
+ bool load_stale_button_in_page;
+
+ // True if a page has completed loading, at which point it can receive
+ // updates.
+ bool is_finished_loading;
+
+ // True if the auto-reload timer has fired and a reload is or has been in
+ // flight.
+ bool auto_reload_triggered;
+};
+
+NetErrorHelperCore::NavigationCorrectionParams::NavigationCorrectionParams() {
+}
+
+NetErrorHelperCore::NavigationCorrectionParams::~NavigationCorrectionParams() {
+}
+
+bool NetErrorHelperCore::IsReloadableError(
+ const NetErrorHelperCore::ErrorPageInfo& info) {
+ return info.error.domain.utf8() == net::kErrorDomain &&
+ info.error.reason != net::ERR_ABORTED &&
+ !info.was_failed_post;
+}
+
+NetErrorHelperCore::NetErrorHelperCore(Delegate* delegate,
+ bool auto_reload_enabled,
+ bool auto_reload_visible_only,
+ bool is_visible)
+ : delegate_(delegate),
+ last_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE),
+ auto_reload_enabled_(auto_reload_enabled),
+ auto_reload_visible_only_(auto_reload_visible_only),
+ auto_reload_timer_(new base::Timer(false, false)),
+ auto_reload_paused_(false),
+ uncommitted_load_started_(false),
+ // TODO(ellyjones): Make online_ accurate at object creation.
+ online_(true),
+ visible_(is_visible),
+ auto_reload_count_(0),
+ navigation_from_button_(NO_BUTTON) {
+}
+
+NetErrorHelperCore::~NetErrorHelperCore() {
+ if (committed_error_page_info_ &&
+ committed_error_page_info_->auto_reload_triggered) {
+ ReportAutoReloadFailure(committed_error_page_info_->error,
+ auto_reload_count_);
+ }
+}
+
+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_->needs_load_navigation_corrections = false;
+ if (pending_error_page_info_)
+ pending_error_page_info_->needs_load_navigation_corrections = false;
+ delegate_->CancelFetchNavigationCorrections();
+ auto_reload_timer_->Stop();
+ auto_reload_paused_ = false;
+}
+
+void NetErrorHelperCore::OnStop() {
+ if (committed_error_page_info_ &&
+ committed_error_page_info_->auto_reload_triggered) {
+ ReportAutoReloadFailure(committed_error_page_info_->error,
+ auto_reload_count_);
+ }
+ CancelPendingFetches();
+ uncommitted_load_started_ = false;
+ auto_reload_count_ = 0;
+}
+
+void NetErrorHelperCore::OnWasShown() {
+ visible_ = true;
+ if (!auto_reload_visible_only_)
+ return;
+ if (auto_reload_paused_)
+ MaybeStartAutoReloadTimer();
+}
+
+void NetErrorHelperCore::OnWasHidden() {
+ visible_ = false;
+ if (!auto_reload_visible_only_)
+ return;
+ PauseAutoReloadTimer();
+}
+
+void NetErrorHelperCore::OnStartLoad(FrameType frame_type, PageType page_type) {
+ if (frame_type != MAIN_FRAME)
+ return;
+
+ uncommitted_load_started_ = true;
+
+ // 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)
+ CancelPendingFetches();
+}
+
+void NetErrorHelperCore::OnCommitLoad(FrameType frame_type, const GURL& url) {
+ if (frame_type != MAIN_FRAME)
+ return;
+
+ // uncommitted_load_started_ could already be false, since RenderFrameImpl
+ // calls OnCommitLoad once for each in-page navigation (like a fragment
+ // change) with no corresponding OnStartLoad.
+ uncommitted_load_started_ = false;
+
+ // Track if an error occurred due to a page button press.
+ // This isn't perfect; if (for instance), the server is slow responding
+ // to a request generated from the page reload button, and the user hits
+ // the browser reload button, this code will still believe the
+ // result is from the page reload button.
+ if (committed_error_page_info_ && pending_error_page_info_ &&
+ navigation_from_button_ != NO_BUTTON &&
+ committed_error_page_info_->error.unreachableURL ==
+ pending_error_page_info_->error.unreachableURL) {
+ DCHECK(navigation_from_button_ == RELOAD_BUTTON ||
+ navigation_from_button_ == LOAD_STALE_BUTTON);
+ chrome_common_net::RecordEvent(
+ navigation_from_button_ == RELOAD_BUTTON ?
+ chrome_common_net::NETWORK_ERROR_PAGE_RELOAD_BUTTON_ERROR :
+ chrome_common_net::NETWORK_ERROR_PAGE_LOAD_STALE_BUTTON_ERROR);
+ }
+ navigation_from_button_ = NO_BUTTON;
+
+ if (committed_error_page_info_ && !pending_error_page_info_ &&
+ committed_error_page_info_->auto_reload_triggered) {
+ const blink::WebURLError& error = committed_error_page_info_->error;
+ const GURL& error_url = error.unreachableURL;
+ if (url == error_url)
+ ReportAutoReloadSuccess(error, auto_reload_count_);
+ else if (url != GURL(content::kUnreachableWebDataURL))
+ ReportAutoReloadFailure(error, auto_reload_count_);
+ }
+
+ committed_error_page_info_.reset(pending_error_page_info_.release());
+}
+
+void NetErrorHelperCore::OnFinishLoad(FrameType frame_type) {
+ if (frame_type != MAIN_FRAME)
+ return;
+
+ if (!committed_error_page_info_) {
+ auto_reload_count_ = 0;
+ return;
+ }
+
+ committed_error_page_info_->is_finished_loading = true;
+
+ chrome_common_net::RecordEvent(chrome_common_net::NETWORK_ERROR_PAGE_SHOWN);
+ if (committed_error_page_info_->reload_button_in_page) {
+ chrome_common_net::RecordEvent(
+ chrome_common_net::NETWORK_ERROR_PAGE_RELOAD_BUTTON_SHOWN);
+ }
+ if (committed_error_page_info_->load_stale_button_in_page) {
+ chrome_common_net::RecordEvent(
+ chrome_common_net::NETWORK_ERROR_PAGE_LOAD_STALE_BUTTON_SHOWN);
+ }
+
+ delegate_->EnablePageHelperFunctions();
+
+ if (committed_error_page_info_->needs_load_navigation_corrections) {
+ // If there is another pending error page load, |fix_url| should have been
+ // cleared.
+ DCHECK(!pending_error_page_info_);
+ DCHECK(!committed_error_page_info_->needs_dns_updates);
+ delegate_->FetchNavigationCorrections(
+ committed_error_page_info_->navigation_correction_params->url,
+ CreateFixUrlRequestBody(
+ committed_error_page_info_->error,
+ *committed_error_page_info_->navigation_correction_params));
+ } else if (auto_reload_enabled_ &&
+ IsReloadableError(*committed_error_page_info_)) {
+ MaybeStartAutoReloadTimer();
+ }
+
+ if (!committed_error_page_info_->needs_dns_updates ||
+ last_probe_status_ == chrome_common_net::DNS_PROBE_POSSIBLE) {
+ return;
+ }
+ DVLOG(1) << "Error page finished loading; sending saved status.";
+ UpdateErrorPage();
+}
+
+void NetErrorHelperCore::GetErrorHTML(
+ FrameType frame_type,
+ const blink::WebURLError& error,
+ bool is_failed_post,
+ std::string* error_html) {
+ if (frame_type == MAIN_FRAME) {
+ // If navigation corrections were needed before, that should have been
+ // cancelled earlier by starting a new page load (Which has now failed).
+ DCHECK(!committed_error_page_info_ ||
+ !committed_error_page_info_->needs_load_navigation_corrections);
+
+ pending_error_page_info_.reset(new ErrorPageInfo(error, is_failed_post));
+ pending_error_page_info_->navigation_correction_params.reset(
+ new NavigationCorrectionParams(navigation_correction_params_));
+ GetErrorHtmlForMainFrame(pending_error_page_info_.get(), error_html);
+ } else {
+ // These values do not matter, as error pages in iframes hide the buttons.
+ bool reload_button_in_page;
+ bool load_stale_button_in_page;
+
+ delegate_->GenerateLocalizedErrorPage(
+ error, is_failed_post, scoped_ptr<ErrorPageParams>(),
+ &reload_button_in_page, &load_stale_button_in_page,
+ error_html);
+ }
+}
+
+void NetErrorHelperCore::OnNetErrorInfo(
+ chrome_common_net::DnsProbeStatus status) {
+ DCHECK_NE(chrome_common_net::DNS_PROBE_POSSIBLE, status);
+
+ last_probe_status_ = status;
+
+ if (!committed_error_page_info_ ||
+ !committed_error_page_info_->needs_dns_updates ||
+ !committed_error_page_info_->is_finished_loading) {
+ return;
+ }
+
+ UpdateErrorPage();
+}
+
+void NetErrorHelperCore::OnSetNavigationCorrectionInfo(
+ const GURL& navigation_correction_url,
+ const std::string& language,
+ const std::string& country_code,
+ const std::string& api_key,
+ const GURL& search_url) {
+ navigation_correction_params_.url = navigation_correction_url;
+ navigation_correction_params_.language = language;
+ navigation_correction_params_.country_code = country_code;
+ navigation_correction_params_.api_key = api_key;
+ navigation_correction_params_.search_url = search_url;
+}
+
+void NetErrorHelperCore::GetErrorHtmlForMainFrame(
+ ErrorPageInfo* pending_error_page_info,
+ std::string* error_html) {
+ std::string error_param;
+ blink::WebURLError error = pending_error_page_info->error;
+
+ if (pending_error_page_info->navigation_correction_params &&
+ pending_error_page_info->navigation_correction_params->url.is_valid() &&
+ ShouldUseFixUrlServiceForError(error, &error_param)) {
+ pending_error_page_info->needs_load_navigation_corrections = true;
+ return;
+ }
+
+ if (IsDnsError(pending_error_page_info->error)) {
+ // The last probe status needs to be reset if this is a DNS error. This
+ // means that if a DNS error page is committed but has not yet finished
+ // loading, a DNS probe status scheduled to be sent to it may be thrown
+ // out, but since the new error page should trigger a new DNS probe, it
+ // will just get the results for the next page load.
+ last_probe_status_ = chrome_common_net::DNS_PROBE_POSSIBLE;
+ pending_error_page_info->needs_dns_updates = true;
+ error = GetUpdatedError(error);
+ }
+
+ delegate_->GenerateLocalizedErrorPage(
+ error, pending_error_page_info->was_failed_post,
+ scoped_ptr<ErrorPageParams>(),
+ &pending_error_page_info->reload_button_in_page,
+ &pending_error_page_info->load_stale_button_in_page,
+ error_html);
+}
+
+void NetErrorHelperCore::UpdateErrorPage() {
+ DCHECK(committed_error_page_info_->needs_dns_updates);
+ DCHECK(committed_error_page_info_->is_finished_loading);
+ DCHECK_NE(chrome_common_net::DNS_PROBE_POSSIBLE, last_probe_status_);
+
+ UMA_HISTOGRAM_ENUMERATION("DnsProbe.ErrorPageUpdateStatus",
+ last_probe_status_,
+ chrome_common_net::DNS_PROBE_MAX);
+ // Every status other than DNS_PROBE_POSSIBLE and DNS_PROBE_STARTED is a
+ // final status code. Once one is reached, the page does not need further
+ // updates.
+ if (last_probe_status_ != chrome_common_net::DNS_PROBE_STARTED)
+ committed_error_page_info_->needs_dns_updates = false;
+
+ // There is no need to worry about the button display statistics here because
+ // the presentation of the reload and load stale buttons can't be changed
+ // by a DNS error update.
+ delegate_->UpdateErrorPage(
+ GetUpdatedError(committed_error_page_info_->error),
+ committed_error_page_info_->was_failed_post);
+}
+
+void NetErrorHelperCore::OnNavigationCorrectionsFetched(
+ const std::string& corrections,
+ const std::string& accept_languages,
+ bool is_rtl) {
+ // Loading suggestions only starts when a blank error page finishes loading,
+ // and is cancelled with a new load.
+ DCHECK(!pending_error_page_info_);
+ DCHECK(committed_error_page_info_->is_finished_loading);
+ DCHECK(committed_error_page_info_->needs_load_navigation_corrections);
+ DCHECK(committed_error_page_info_->navigation_correction_params);
+
+ pending_error_page_info_.reset(
+ new ErrorPageInfo(committed_error_page_info_->error,
+ committed_error_page_info_->was_failed_post));
+ pending_error_page_info_->navigation_correction_response =
+ ParseNavigationCorrectionResponse(corrections);
+
+ std::string error_html;
+ scoped_ptr<ErrorPageParams> params;
+ if (pending_error_page_info_->navigation_correction_response) {
+ // Copy navigation correction parameters used for the request, so tracking
+ // requests can still be sent if the configuration changes.
+ pending_error_page_info_->navigation_correction_params.reset(
+ new NavigationCorrectionParams(
+ *committed_error_page_info_->navigation_correction_params));
+ params = CreateErrorPageParams(
+ *pending_error_page_info_->navigation_correction_response,
+ pending_error_page_info_->error,
+ *pending_error_page_info_->navigation_correction_params,
+ accept_languages, is_rtl);
+ delegate_->GenerateLocalizedErrorPage(
+ pending_error_page_info_->error,
+ pending_error_page_info_->was_failed_post,
+ params.Pass(),
+ &pending_error_page_info_->reload_button_in_page,
+ &pending_error_page_info_->load_stale_button_in_page,
+ &error_html);
+ } else {
+ // Since |navigation_correction_params| in |pending_error_page_info_| is
+ // NULL, this won't trigger another attempt to load corrections.
+ GetErrorHtmlForMainFrame(pending_error_page_info_.get(), &error_html);
+ }
+
+ // TODO(mmenke): Once the new API is in place, look into replacing this
+ // double page load by just updating the error page, like DNS
+ // probes do.
+ delegate_->LoadErrorPageInMainFrame(
+ error_html,
+ pending_error_page_info_->error.unreachableURL);
+}
+
+blink::WebURLError NetErrorHelperCore::GetUpdatedError(
+ const blink::WebURLError& error) const {
+ // If a probe didn't run or wasn't conclusive, restore the original error.
+ if (last_probe_status_ == chrome_common_net::DNS_PROBE_NOT_RUN ||
+ last_probe_status_ ==
+ chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE) {
+ return error;
+ }
+
+ blink::WebURLError updated_error;
+ updated_error.domain = blink::WebString::fromUTF8(
+ chrome_common_net::kDnsProbeErrorDomain);
+ updated_error.reason = last_probe_status_;
+ updated_error.unreachableURL = error.unreachableURL;
+ updated_error.staleCopyInCache = error.staleCopyInCache;
+
+ return updated_error;
+}
+
+void NetErrorHelperCore::Reload() {
+ if (!committed_error_page_info_) {
+ return;
+ }
+ delegate_->ReloadPage();
+}
+
+bool NetErrorHelperCore::MaybeStartAutoReloadTimer() {
+ if (!committed_error_page_info_ ||
+ !committed_error_page_info_->is_finished_loading ||
+ pending_error_page_info_ ||
+ uncommitted_load_started_) {
+ return false;
+ }
+
+ StartAutoReloadTimer();
+ return true;
+}
+
+void NetErrorHelperCore::StartAutoReloadTimer() {
+ DCHECK(committed_error_page_info_);
+ DCHECK(IsReloadableError(*committed_error_page_info_));
+
+ committed_error_page_info_->auto_reload_triggered = true;
+
+ if (!online_ || (!visible_ && auto_reload_visible_only_)) {
+ auto_reload_paused_ = true;
+ return;
+ }
+
+ auto_reload_paused_ = false;
+ base::TimeDelta delay = GetAutoReloadTime(auto_reload_count_);
+ auto_reload_timer_->Stop();
+ auto_reload_timer_->Start(FROM_HERE, delay,
+ base::Bind(&NetErrorHelperCore::AutoReloadTimerFired,
+ base::Unretained(this)));
+}
+
+void NetErrorHelperCore::AutoReloadTimerFired() {
+ auto_reload_count_++;
+ Reload();
+}
+
+void NetErrorHelperCore::PauseAutoReloadTimer() {
+ if (!auto_reload_timer_->IsRunning())
+ return;
+ DCHECK(committed_error_page_info_);
+ DCHECK(!auto_reload_paused_);
+ DCHECK(committed_error_page_info_->auto_reload_triggered);
+ auto_reload_timer_->Stop();
+ auto_reload_paused_ = true;
+}
+
+void NetErrorHelperCore::NetworkStateChanged(bool online) {
+ bool was_online = online_;
+ online_ = online;
+ if (!was_online && online) {
+ // Transitioning offline -> online
+ if (auto_reload_paused_)
+ MaybeStartAutoReloadTimer();
+ } else if (was_online && !online) {
+ // Transitioning online -> offline
+ if (auto_reload_timer_->IsRunning())
+ auto_reload_count_ = 0;
+ PauseAutoReloadTimer();
+ }
+}
+
+bool NetErrorHelperCore::ShouldSuppressErrorPage(FrameType frame_type,
+ const GURL& url) {
+ // Don't suppress child frame errors.
+ if (frame_type != MAIN_FRAME)
+ return false;
+
+ if (!auto_reload_enabled_)
+ 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;
+
+ // If the error page wasn't reloadable, display it.
+ if (!IsReloadableError(*committed_error_page_info_))
+ return false;
+
+ // If |auto_reload_timer_| is still running or is paused, this error page
+ // isn't from an auto reload.
+ if (auto_reload_timer_->IsRunning() || auto_reload_paused_)
+ return false;
+
+ // If the error page was reloadable, and the timer isn't running or paused, an
+ // auto-reload has already been triggered.
+ DCHECK(committed_error_page_info_->auto_reload_triggered);
+
+ GURL error_url = committed_error_page_info_->error.unreachableURL;
+ // TODO(ellyjones): also plumb the error code down to CCRC and check that
+ if (error_url != url)
+ return false;
+
+ // Suppressed an error-page load; the previous uncommitted load was the error
+ // page load starting, so forget about it.
+ uncommitted_load_started_ = false;
+
+ // The first iteration of the timer is started by OnFinishLoad calling
+ // MaybeStartAutoReloadTimer, but since error pages for subsequent loads are
+ // suppressed in this function, subsequent iterations of the timer have to be
+ // started here.
+ MaybeStartAutoReloadTimer();
+ return true;
+}
+
+void NetErrorHelperCore::ExecuteButtonPress(Button button) {
+ switch (button) {
+ case RELOAD_BUTTON:
+ chrome_common_net::RecordEvent(
+ chrome_common_net::NETWORK_ERROR_PAGE_RELOAD_BUTTON_CLICKED);
+ navigation_from_button_ = RELOAD_BUTTON;
+ Reload();
+ return;
+ case LOAD_STALE_BUTTON:
+ chrome_common_net::RecordEvent(
+ chrome_common_net::NETWORK_ERROR_PAGE_LOAD_STALE_BUTTON_CLICKED);
+ navigation_from_button_ = LOAD_STALE_BUTTON;
+ delegate_->LoadPageFromCache(
+ committed_error_page_info_->error.unreachableURL);
+ return;
+ case MORE_BUTTON:
+ // Visual effects on page are handled in Javascript code.
+ chrome_common_net::RecordEvent(
+ chrome_common_net::NETWORK_ERROR_PAGE_MORE_BUTTON_CLICKED);
+ return;
+ case NO_BUTTON:
+ NOTREACHED();
+ return;
+ }
+}
+
+void NetErrorHelperCore::TrackClick(int tracking_id) {
+ // It's technically possible for |navigation_correction_params| to be NULL but
+ // for |navigation_correction_response| not to be NULL, if the paramters
+ // changed between loading the original error page and loading the error page
+ if (!committed_error_page_info_ ||
+ !committed_error_page_info_->navigation_correction_response) {
+ return;
+ }
+
+ NavigationCorrectionResponse* response =
+ committed_error_page_info_->navigation_correction_response.get();
+
+ // |tracking_id| is less than 0 when the error page was not generated by the
+ // navigation correction service. |tracking_id| should never be greater than
+ // the array size, but best to be safe, since it contains data from a remote
+ // site, though none of that data should make it into Javascript callbacks.
+ if (tracking_id < 0 ||
+ static_cast<size_t>(tracking_id) >= response->corrections.size()) {
+ return;
+ }
+
+ // Only report a clicked link once.
+ if (committed_error_page_info_->clicked_corrections.count(tracking_id))
+ return;
+
+ committed_error_page_info_->clicked_corrections.insert(tracking_id);
+ std::string request_body = CreateClickTrackingUrlRequestBody(
+ committed_error_page_info_->error,
+ *committed_error_page_info_->navigation_correction_params,
+ *response,
+ *response->corrections[tracking_id]);
+ delegate_->SendTrackingRequest(
+ committed_error_page_info_->navigation_correction_params->url,
+ request_body);
+}
+
+} // namespace error_page
diff --git a/components/error_page/renderer/net_error_helper_core.h b/components/error_page/renderer/net_error_helper_core.h
new file mode 100644
index 0000000..ec00d33
--- /dev/null
+++ b/components/error_page/renderer/net_error_helper_core.h
@@ -0,0 +1,271 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_ERROR_PAGE_RENDERER_NET_ERROR_HELPER_CORE_H_
+#define COMPONENTS_ERROR_PAGE_RENDERER_NET_ERROR_HELPER_CORE_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "components/error_page/common/net_error_info.h"
+#include "url/gurl.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace blink {
+struct WebURLError;
+}
+
+namespace error_page {
+
+struct ErrorPageParams;
+
+// Class that contains the logic for how the NetErrorHelper. This allows for
+// testing the logic without a RenderView or WebFrame, which are difficult to
+// mock, and for testing races which are impossible to reliably reproduce
+// with real RenderViews or WebFrames.
+class NetErrorHelperCore {
+ public:
+ enum FrameType {
+ MAIN_FRAME,
+ SUB_FRAME,
+ };
+
+ enum PageType {
+ NON_ERROR_PAGE,
+ ERROR_PAGE,
+ };
+
+ enum Button {
+ NO_BUTTON,
+ RELOAD_BUTTON,
+ LOAD_STALE_BUTTON,
+ MORE_BUTTON,
+ };
+
+ // The Delegate handles all interaction with the RenderView, WebFrame, and
+ // the network, as well as the generation of error pages.
+ class Delegate {
+ public:
+ // Generates an error page's HTML for the given error.
+ virtual void GenerateLocalizedErrorPage(
+ const blink::WebURLError& error,
+ bool is_failed_post,
+ scoped_ptr<error_page::ErrorPageParams> params,
+ bool* reload_button_shown,
+ bool* load_stale_button_shown,
+ std::string* html) const = 0;
+
+ // Loads the given HTML in the main frame for use as an error page.
+ virtual void LoadErrorPageInMainFrame(const std::string& html,
+ const GURL& failed_url) = 0;
+
+ // Create extra Javascript bindings in the error page.
+ virtual void EnablePageHelperFunctions() = 0;
+
+ // Updates the currently displayed error page with a new error code. The
+ // currently displayed error page must have finished loading, and must have
+ // been generated by a call to GenerateLocalizedErrorPage.
+ virtual void UpdateErrorPage(const blink::WebURLError& error,
+ bool is_failed_post) = 0;
+
+ // Fetches an error page and calls into OnErrorPageFetched when done. Any
+ // previous fetch must either be canceled or finished before calling. Can't
+ // be called synchronously after a previous fetch completes.
+ virtual void FetchNavigationCorrections(
+ const GURL& navigation_correction_url,
+ const std::string& navigation_correction_request_body) = 0;
+
+ // Cancels fetching navigation corrections. Does nothing if no fetch is
+ // ongoing.
+ virtual void CancelFetchNavigationCorrections() = 0;
+
+ // Sends an HTTP request used to track which link on the page was clicked to
+ // the navigation correction service.
+ virtual void SendTrackingRequest(
+ const GURL& tracking_url,
+ const std::string& tracking_request_body) = 0;
+
+ // Starts a reload of the page in the observed frame.
+ virtual void ReloadPage() = 0;
+
+ // Load the original page from cache.
+ virtual void LoadPageFromCache(const GURL& page_url) = 0;
+
+ protected:
+ virtual ~Delegate() {}
+ };
+
+ struct NavigationCorrectionParams {
+ NavigationCorrectionParams();
+ ~NavigationCorrectionParams();
+
+ // URL used both for getting the suggestions and tracking clicks.
+ GURL url;
+
+ std::string language;
+ std::string country_code;
+ std::string api_key;
+ GURL search_url;
+ };
+
+ NetErrorHelperCore(Delegate* delegate,
+ bool auto_reload_enabled,
+ bool auto_reload_visible_only,
+ bool is_visible);
+ ~NetErrorHelperCore();
+
+ // Examines |frame| and |error| to see if this is an error worthy of a DNS
+ // probe. If it is, initializes |error_strings| based on |error|,
+ // |is_failed_post|, and |locale| with suitable strings and returns true.
+ // If not, returns false, in which case the caller should look up error
+ // strings directly using LocalizedError::GetNavigationErrorStrings.
+ //
+ // Updates the NetErrorHelper with the assumption the page will be loaded
+ // immediately.
+ void GetErrorHTML(FrameType frame_type,
+ const blink::WebURLError& error,
+ bool is_failed_post,
+ std::string* error_html);
+
+ // These methods handle tracking the actual state of the page.
+ void OnStartLoad(FrameType frame_type, PageType page_type);
+ void OnCommitLoad(FrameType frame_type, const GURL& url);
+ void OnFinishLoad(FrameType frame_type);
+ void OnStop();
+ void OnWasShown();
+ void OnWasHidden();
+
+ void CancelPendingFetches();
+
+ // Called when an error page have has been retrieved over the network. |html|
+ // must be an empty string on error.
+ void OnNavigationCorrectionsFetched(const std::string& corrections,
+ const std::string& accept_languages,
+ bool is_rtl);
+
+ // Notifies |this| that network error information from the browser process
+ // has been received.
+ void OnNetErrorInfo(chrome_common_net::DnsProbeStatus status);
+
+ void OnSetNavigationCorrectionInfo(const GURL& navigation_correction_url,
+ const std::string& language,
+ const std::string& country_code,
+ const std::string& api_key,
+ const GURL& search_url);
+ // Notifies |this| that the network's online status changed.
+ // 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 NetworkStateChanged(bool online);
+
+ int auto_reload_count() const { return auto_reload_count_; }
+
+ bool ShouldSuppressErrorPage(FrameType frame_type, const GURL& url);
+
+ void set_timer_for_testing(scoped_ptr<base::Timer> timer) {
+ auto_reload_timer_.reset(timer.release());
+ }
+
+ // Execute the effect of pressing the specified button.
+ // Note that the visual effects of the 'MORE' button are taken
+ // care of in JavaScript.
+ void ExecuteButtonPress(Button button);
+
+ // Reports to the correction service that the link with the given tracking
+ // ID was clicked. Only pages generated with information from the service
+ // have links with tracking IDs. Duplicate requests from the same page with
+ // the same tracking ID are ignored.
+ void TrackClick(int tracking_id);
+
+ private:
+ struct ErrorPageInfo;
+
+ // Gets HTML for a main frame error page. Depending on
+ // |pending_error_page_info|, may use the navigation correction service, or
+ // show a DNS probe error page. May modify |pending_error_page_info|.
+ void GetErrorHtmlForMainFrame(ErrorPageInfo* pending_error_page_info,
+ std::string* error_html);
+
+ // Updates the currently displayed error page with a new error based on the
+ // most recently received DNS probe result. The page must have finished
+ // loading before this is called.
+ void UpdateErrorPage();
+
+ blink::WebURLError GetUpdatedError(const blink::WebURLError& error) const;
+
+ void Reload();
+ bool MaybeStartAutoReloadTimer();
+ void StartAutoReloadTimer();
+ void AutoReloadTimerFired();
+ void PauseAutoReloadTimer();
+
+ static bool IsReloadableError(const ErrorPageInfo& info);
+
+ Delegate* delegate_;
+
+ // The last DnsProbeStatus received from the browser.
+ chrome_common_net::DnsProbeStatus last_probe_status_;
+
+ // Information for the provisional / "pre-provisional" error page. NULL when
+ // there's no page pending, or the pending page is not an error page.
+ scoped_ptr<ErrorPageInfo> pending_error_page_info_;
+
+ // Information for the committed error page. NULL when the committed page is
+ // not an error page.
+ scoped_ptr<ErrorPageInfo> committed_error_page_info_;
+
+ NavigationCorrectionParams navigation_correction_params_;
+
+ // True if auto-reload is enabled at all.
+ const bool auto_reload_enabled_;
+
+ // True if auto-reload should only run when the observed frame is visible.
+ const bool auto_reload_visible_only_;
+
+ // Timer used to wait for auto-reload attempts.
+ scoped_ptr<base::Timer> auto_reload_timer_;
+
+ // True if the auto-reload timer would be running but is waiting for an
+ // offline->online network transition.
+ bool auto_reload_paused_;
+
+ // True if there is an uncommitted-but-started load, error page or not. This
+ // is used to inhibit starting auto-reload when an error page finishes, in
+ // case this happens:
+ // Error page starts
+ // Error page commits
+ // Non-error page starts
+ // Error page finishes
+ bool uncommitted_load_started_;
+
+ // Is the browser online?
+ bool online_;
+
+ // Is the RenderFrame this object is observing visible?
+ bool visible_;
+
+ int auto_reload_count_;
+
+ // This value is set only when a navigation has been initiated from
+ // the error page. It is used to detect when such navigations result
+ // in errors.
+ Button navigation_from_button_;
+};
+
+} // namespace error_page
+
+#endif // COMPONENTS_ERROR_PAGE_RENDERER_NET_ERROR_HELPER_CORE_H_
diff --git a/components/error_page/renderer/net_error_helper_core_unittest.cc b/components/error_page/renderer/net_error_helper_core_unittest.cc
new file mode 100644
index 0000000..88cdcc3
--- /dev/null
+++ b/components/error_page/renderer/net_error_helper_core_unittest.cc
@@ -0,0 +1,2436 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/error_page/renderer/net_error_helper_core.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/histogram_tester.h"
+#include "base/timer/mock_timer.h"
+#include "base/timer/timer.h"
+#include "base/values.h"
+#include "components/error_page/common/error_page_params.h"
+#include "components/error_page/common/net_error_info.h"
+#include "content/public/common/url_constants.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebURLError.h"
+#include "url/gurl.h"
+
+namespace error_page {
+namespace {
+
+using blink::WebURLError;
+using chrome_common_net::DnsProbeStatus;
+using chrome_common_net::DnsProbeStatusToString;
+using error_page::ErrorPageParams;
+
+const char kFailedUrl[] = "http://failed/";
+const char kFailedHttpsUrl[] = "https://failed/";
+
+const char kNavigationCorrectionUrl[] = "http://navigation.corrections/";
+const char kLanguage[] = "en";
+const char kCountry[] = "us";
+const char kApiKey[] = "api_key";
+const char kSearchUrl[] = "http://www.google.com/search";
+
+const char kSuggestedSearchTerms[] = "Happy Goats";
+const char kNavigationCorrectionEventId[] = "#007";
+const char kNavigationCorrectionFingerprint[] = "RandumStuff";
+
+struct NavigationCorrection {
+ const char* correction_type;
+ const char* url_correction;
+ const char* click_type;
+ const char* click_data;
+ bool is_porn;
+ bool is_soft_porn;
+
+ base::Value* ToValue() const {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetString("correctionType", correction_type);
+ dict->SetString("urlCorrection", url_correction);
+ dict->SetString("clickType", click_type);
+ dict->SetString("clickData", click_data);
+ dict->SetBoolean("isPorn", is_porn);
+ dict->SetBoolean("isSoftPorn", is_soft_porn);
+ return dict;
+ }
+};
+
+const NavigationCorrection kDefaultCorrections[] = {
+ {"reloadPage", kFailedUrl, "rld", "data1", false, false},
+ {"urlCorrection", "http://somewhere_else/", "btn", "data2", false, false},
+ {"contentOverlap", "http://pony_island/", "btn", "data3", false, false},
+
+ // Porn should be ignored.
+ {"emphasizedUrlCorrection", "http://porn/", "btn", "data4", true, false},
+ {"sitemap", "http://more_porn/", "btn", "data5", false, true},
+
+ {"webSearchQuery", kSuggestedSearchTerms, "frm", "data6", false, false},
+};
+
+std::string SuggestionsToResponse(const NavigationCorrection* corrections,
+ int num_corrections) {
+ base::ListValue* url_corrections = new base::ListValue();
+ for (int i = 0; i < num_corrections; ++i)
+ url_corrections->Append(corrections[i].ToValue());
+
+ scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
+ response->Set("result.UrlCorrections", url_corrections);
+ response->SetString("result.eventId", kNavigationCorrectionEventId);
+ response->SetString("result.fingerprint", kNavigationCorrectionFingerprint);
+
+ std::string json;
+ base::JSONWriter::Write(response.get(), &json);
+ return json;
+}
+
+testing::AssertionResult StringValueEquals(
+ const base::DictionaryValue& dict,
+ const std::string& key,
+ const std::string& expected_value) {
+ std::string actual_value;
+ if (!dict.GetString(key, &actual_value))
+ return testing::AssertionFailure() << key << " not found.";
+ if (actual_value != expected_value) {
+ return testing::AssertionFailure()
+ << "actual: " << actual_value << "\n expected: " << expected_value;
+ }
+ return testing::AssertionSuccess();
+}
+
+// Creates a string from an error that is used as a mock locally generated
+// error page for that error.
+std::string ErrorToString(const WebURLError& error, bool is_failed_post) {
+ return base::StringPrintf("(%s, %s, %i, %s)",
+ error.unreachableURL.string().utf8().c_str(),
+ error.domain.utf8().c_str(), error.reason,
+ is_failed_post ? "POST" : "NOT POST");
+}
+
+WebURLError ProbeError(DnsProbeStatus status) {
+ WebURLError error;
+ error.unreachableURL = GURL(kFailedUrl);
+ error.domain = blink::WebString::fromUTF8(
+ chrome_common_net::kDnsProbeErrorDomain);
+ error.reason = status;
+ return error;
+}
+
+WebURLError NetError(net::Error net_error) {
+ WebURLError error;
+ error.unreachableURL = GURL(kFailedUrl);
+ error.domain = blink::WebString::fromUTF8(net::kErrorDomain);
+ error.reason = net_error;
+ return error;
+}
+
+// Convenience functions that create an error string for a non-POST request.
+
+std::string ProbeErrorString(DnsProbeStatus status) {
+ return ErrorToString(ProbeError(status), false);
+}
+
+std::string NetErrorString(net::Error net_error) {
+ return ErrorToString(NetError(net_error), false);
+}
+
+class NetErrorHelperCoreTest : public testing::Test,
+ public NetErrorHelperCore::Delegate {
+ public:
+ NetErrorHelperCoreTest() : timer_(NULL),
+ update_count_(0),
+ error_html_update_count_(0),
+ reload_count_(0),
+ load_stale_count_(0),
+ enable_page_helper_functions_count_(0),
+ default_url_(GURL(kFailedUrl)),
+ error_url_(GURL(content::kUnreachableWebDataURL)),
+ tracking_request_count_(0) {
+ SetUpCore(false, false, true);
+ }
+
+ virtual ~NetErrorHelperCoreTest() {
+ // No test finishes while an error page is being fetched.
+ EXPECT_FALSE(is_url_being_fetched());
+ }
+
+ virtual void SetUp() override {
+ base::StatisticsRecorder::Initialize();
+ }
+
+ void SetUpCore(bool auto_reload_enabled,
+ bool auto_reload_visible_only,
+ bool visible) {
+ // The old value of timer_, if any, will be freed by the old core_ being
+ // destructed, since core_ takes ownership of the timer.
+ timer_ = new base::MockTimer(false, false);
+ core_.reset(new NetErrorHelperCore(this,
+ auto_reload_enabled,
+ auto_reload_visible_only,
+ visible));
+ core_->set_timer_for_testing(scoped_ptr<base::Timer>(timer_));
+ }
+
+ NetErrorHelperCore* core() { return core_.get(); }
+
+ const GURL& url_being_fetched() const { return url_being_fetched_; }
+ bool is_url_being_fetched() const { return !url_being_fetched_.is_empty(); }
+
+ int reload_count() const {
+ return reload_count_;
+ }
+
+ int load_stale_count() const {
+ return load_stale_count_;
+ }
+
+ const GURL& load_stale_url() const {
+ return load_stale_url_;
+ }
+
+ const GURL& default_url() const {
+ return default_url_;
+ }
+
+ const GURL& error_url() const {
+ return error_url_;
+ }
+
+ int enable_page_helper_functions_count() const {
+ return enable_page_helper_functions_count_;
+ }
+
+ const std::string& last_update_string() const { return last_update_string_; }
+ int update_count() const { return update_count_; }
+
+ const std::string& last_error_html() const { return last_error_html_; }
+ int error_html_update_count() const { return error_html_update_count_; }
+
+ const ErrorPageParams* last_error_page_params() const {
+ return last_error_page_params_.get();
+ }
+
+ const GURL& last_tracking_url() const { return last_tracking_url_; }
+ const std::string& last_tracking_request_body() const {
+ return last_tracking_request_body_;
+ }
+ int tracking_request_count() const { return tracking_request_count_; }
+
+ base::MockTimer* timer() { return timer_; }
+
+ void NavigationCorrectionsLoadSuccess(
+ const NavigationCorrection* corrections, int num_corrections) {
+ NavigationCorrectionsLoadFinished(
+ SuggestionsToResponse(corrections, num_corrections));
+ }
+
+ void NavigationCorrectionsLoadFailure() {
+ NavigationCorrectionsLoadFinished("");
+ }
+
+ void NavigationCorrectionsLoadFinished(const std::string& result) {
+ url_being_fetched_ = GURL();
+ core()->OnNavigationCorrectionsFetched(result, "en", false);
+ }
+
+ void DoErrorLoad(net::Error error) {
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(error), false, &html);
+ EXPECT_FALSE(html.empty());
+ EXPECT_EQ(NetErrorString(error), html);
+
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME,
+ error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ }
+
+ void DoSuccessLoad() {
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, default_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ }
+
+ void DoDnsProbe(chrome_common_net::DnsProbeStatus final_status) {
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(final_status);
+ }
+
+ void EnableNavigationCorrections() {
+ SetNavigationCorrectionURL(GURL(kNavigationCorrectionUrl));
+ }
+
+ void DisableNavigationCorrections() {
+ SetNavigationCorrectionURL(GURL());
+ }
+
+ void ExpectDefaultNavigationCorrections() const {
+ // Checks that the last error page params correspond to kDefaultSuggestions.
+ ASSERT_TRUE(last_error_page_params());
+ EXPECT_TRUE(last_error_page_params()->suggest_reload);
+ EXPECT_EQ(2u, last_error_page_params()->override_suggestions->GetSize());
+ EXPECT_EQ(GURL(kSearchUrl), last_error_page_params()->search_url);
+ EXPECT_EQ(kSuggestedSearchTerms, last_error_page_params()->search_terms);
+ }
+
+ private:
+ void SetNavigationCorrectionURL(const GURL& navigation_correction_url) {
+ core()->OnSetNavigationCorrectionInfo(navigation_correction_url,
+ kLanguage, kCountry, kApiKey,
+ GURL(kSearchUrl));
+ }
+
+ // NetErrorHelperCore::Delegate implementation:
+ virtual void GenerateLocalizedErrorPage(const WebURLError& error,
+ bool is_failed_post,
+ scoped_ptr<ErrorPageParams> params,
+ bool* reload_button_shown,
+ bool* load_stale_button_shown,
+ std::string* html) const override {
+ last_error_page_params_.reset(params.release());
+ *reload_button_shown = false;
+ *load_stale_button_shown = false;
+ *html = ErrorToString(error, is_failed_post);
+ }
+
+ virtual void LoadErrorPageInMainFrame(const std::string& html,
+ const GURL& failed_url) override {
+ error_html_update_count_++;
+ last_error_html_ = html;
+ }
+
+ virtual void EnablePageHelperFunctions() override {
+ enable_page_helper_functions_count_++;
+ }
+
+ virtual void UpdateErrorPage(const WebURLError& error,
+ bool is_failed_post) override {
+ update_count_++;
+ last_error_page_params_.reset(NULL);
+ last_error_html_ = ErrorToString(error, is_failed_post);
+ }
+
+ virtual void FetchNavigationCorrections(
+ const GURL& navigation_correction_url,
+ const std::string& navigation_correction_request_body) override {
+ EXPECT_TRUE(url_being_fetched_.is_empty());
+ EXPECT_TRUE(request_body_.empty());
+ EXPECT_EQ(GURL(kNavigationCorrectionUrl), navigation_correction_url);
+
+ url_being_fetched_ = navigation_correction_url;
+ request_body_ = navigation_correction_request_body;
+
+ // Check the body of the request.
+
+ base::JSONReader reader;
+ scoped_ptr<base::Value> parsed_body(reader.Read(
+ navigation_correction_request_body));
+ ASSERT_TRUE(parsed_body);
+ base::DictionaryValue* dict = NULL;
+ ASSERT_TRUE(parsed_body->GetAsDictionary(&dict));
+
+ EXPECT_TRUE(StringValueEquals(*dict, "params.urlQuery", kFailedUrl));
+ EXPECT_TRUE(StringValueEquals(*dict, "params.language", kLanguage));
+ EXPECT_TRUE(StringValueEquals(*dict, "params.originCountry", kCountry));
+ EXPECT_TRUE(StringValueEquals(*dict, "params.key", kApiKey));
+ }
+
+ virtual void CancelFetchNavigationCorrections() override {
+ url_being_fetched_ = GURL();
+ request_body_.clear();
+ }
+
+ virtual void ReloadPage() override {
+ reload_count_++;
+ }
+
+ virtual void LoadPageFromCache(const GURL& error_url) override {
+ load_stale_count_++;
+ load_stale_url_ = error_url;
+ }
+
+ virtual void SendTrackingRequest(
+ const GURL& tracking_url,
+ const std::string& tracking_request_body) override {
+ last_tracking_url_ = tracking_url;
+ last_tracking_request_body_ = tracking_request_body;
+ tracking_request_count_++;
+
+ // Check the body of the request.
+
+ base::JSONReader reader;
+ scoped_ptr<base::Value> parsed_body(reader.Read(tracking_request_body));
+ ASSERT_TRUE(parsed_body);
+ base::DictionaryValue* dict = NULL;
+ ASSERT_TRUE(parsed_body->GetAsDictionary(&dict));
+
+ EXPECT_TRUE(StringValueEquals(*dict, "params.originalUrlQuery",
+ kFailedUrl));
+ EXPECT_TRUE(StringValueEquals(*dict, "params.language", kLanguage));
+ EXPECT_TRUE(StringValueEquals(*dict, "params.originCountry", kCountry));
+ EXPECT_TRUE(StringValueEquals(*dict, "params.key", kApiKey));
+ }
+
+ base::MockTimer* timer_;
+
+ scoped_ptr<NetErrorHelperCore> core_;
+
+ GURL url_being_fetched_;
+ std::string request_body_;
+
+ // Contains the information passed to the last call to UpdateErrorPage, as a
+ // string.
+ std::string last_update_string_;
+ // Number of times |last_update_string_| has been changed.
+ int update_count_;
+
+ // Contains the HTML set by the last call to LoadErrorPageInMainFrame.
+ std::string last_error_html_;
+ // Number of times |last_error_html_| has been changed.
+ int error_html_update_count_;
+
+ // Mutable because GenerateLocalizedErrorPage is const.
+ mutable scoped_ptr<ErrorPageParams> last_error_page_params_;
+
+ int reload_count_;
+ int load_stale_count_;
+ GURL load_stale_url_;
+
+ int enable_page_helper_functions_count_;
+
+ const GURL default_url_;
+ const GURL error_url_;
+
+ GURL last_tracking_url_;
+ std::string last_tracking_request_body_;
+ int tracking_request_count_;
+};
+
+//------------------------------------------------------------------------------
+// Basic tests that don't update the error page for probes or load navigation
+// corrections.
+//------------------------------------------------------------------------------
+
+TEST_F(NetErrorHelperCoreTest, Null) {
+}
+
+TEST_F(NetErrorHelperCoreTest, SuccessfulPageLoad) {
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, default_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+TEST_F(NetErrorHelperCoreTest, SuccessfulPageLoadWithNavigationCorrections) {
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, default_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+TEST_F(NetErrorHelperCoreTest, MainFrameNonDnsError) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_CONNECTION_RESET), false, &html);
+ // Should have returned a local error page.
+ EXPECT_FALSE(html.empty());
+ EXPECT_EQ(NetErrorString(net::ERR_CONNECTION_RESET), html);
+
+ // Error page loads.
+ EXPECT_EQ(0, enable_page_helper_functions_count());
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+ EXPECT_EQ(1, enable_page_helper_functions_count());
+}
+
+TEST_F(NetErrorHelperCoreTest, MainFrameNonDnsErrorWithCorrections) {
+ EnableNavigationCorrections();
+
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_CONNECTION_RESET), false, &html);
+ // Should have returned a local error page.
+ EXPECT_FALSE(html.empty());
+ EXPECT_EQ(NetErrorString(net::ERR_CONNECTION_RESET), html);
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+// Much like above tests, but with a bunch of spurious DNS status messages that
+// should have no effect.
+TEST_F(NetErrorHelperCoreTest, MainFrameNonDnsErrorSpuriousStatus) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_CONNECTION_RESET),
+ false, &html);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+
+ // Should have returned a local error page.
+ EXPECT_FALSE(html.empty());
+ EXPECT_EQ(NetErrorString(net::ERR_CONNECTION_RESET), html);
+
+ // Error page loads.
+
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+TEST_F(NetErrorHelperCoreTest, SubFrameDnsError) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::SUB_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::SUB_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page.
+ EXPECT_EQ(NetErrorString(net::ERR_NAME_NOT_RESOLVED), html);
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::SUB_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::SUB_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::SUB_FRAME);
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+TEST_F(NetErrorHelperCoreTest, SubFrameDnsErrorWithCorrections) {
+ EnableNavigationCorrections();
+
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::SUB_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::SUB_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page.
+ EXPECT_EQ(NetErrorString(net::ERR_NAME_NOT_RESOLVED), html);
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::SUB_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::SUB_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::SUB_FRAME);
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+// Much like above tests, but with a bunch of spurious DNS status messages that
+// should have no effect.
+TEST_F(NetErrorHelperCoreTest, SubFrameDnsErrorSpuriousStatus) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::SUB_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::SUB_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+
+ // Should have returned a local error page.
+ EXPECT_EQ(NetErrorString(net::ERR_NAME_NOT_RESOLVED), html);
+
+ // Error page loads.
+
+ core()->OnStartLoad(NetErrorHelperCore::SUB_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+
+ core()->OnCommitLoad(NetErrorHelperCore::SUB_FRAME, error_url());
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+
+ core()->OnFinishLoad(NetErrorHelperCore::SUB_FRAME);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+//------------------------------------------------------------------------------
+// Tests for updating the error page in response to DNS probe results. None
+// of these have navigation corrections enabled.
+//------------------------------------------------------------------------------
+
+// Test case where the error page finishes loading before receiving any DNS
+// probe messages.
+TEST_F(NetErrorHelperCoreTest, FinishedBeforeProbe) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page indicating a probe may run.
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE), html);
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(0, update_count());
+
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ EXPECT_EQ(1, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_STARTED),
+ last_error_html());
+
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN),
+ last_error_html());
+
+ // Any other probe updates should be ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+// Same as above, but the probe is not run.
+TEST_F(NetErrorHelperCoreTest, FinishedBeforeProbeNotRun) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page indicating a probe may run.
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE), html);
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(0, update_count());
+
+ // When the not run status arrives, the page should revert to the normal dns
+ // error page.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_NOT_RUN);
+ EXPECT_EQ(1, update_count());
+ EXPECT_EQ(NetErrorString(net::ERR_NAME_NOT_RESOLVED), last_error_html());
+
+ // Any other probe updates should be ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(1, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+// Same as above, but the probe result is inconclusive.
+TEST_F(NetErrorHelperCoreTest, FinishedBeforeProbeInconclusive) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page indicating a probe may run.
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE), html);
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(0, update_count());
+
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ EXPECT_EQ(1, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_STARTED),
+ last_error_html());
+
+ // When the inconclusive status arrives, the page should revert to the normal
+ // dns error page.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(NetErrorString(net::ERR_NAME_NOT_RESOLVED), last_error_html());
+
+ // Any other probe updates should be ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_INCONCLUSIVE);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+// Same as above, but the probe result is no internet.
+TEST_F(NetErrorHelperCoreTest, FinishedBeforeProbeNoInternet) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page indicating a probe may run.
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE), html);
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(0, update_count());
+
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ EXPECT_EQ(1, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_STARTED),
+ last_error_html());
+
+ // When the inconclusive status arrives, the page should revert to the normal
+ // dns error page.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET),
+ last_error_html());
+
+ // Any other probe updates should be ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+// Same as above, but the probe result is bad config.
+TEST_F(NetErrorHelperCoreTest, FinishedBeforeProbeBadConfig) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page indicating a probe may run.
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE), html);
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(0, update_count());
+
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ EXPECT_EQ(1, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_STARTED),
+ last_error_html());
+
+ // When the inconclusive status arrives, the page should revert to the normal
+ // dns error page.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_BAD_CONFIG);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_FINISHED_BAD_CONFIG),
+ last_error_html());
+
+ // Any other probe updates should be ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_BAD_CONFIG);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+// Test case where the error page finishes loading after receiving the start
+// DNS probe message.
+TEST_F(NetErrorHelperCoreTest, FinishedAfterStartProbe) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page indicating a probe may run.
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE), html);
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+
+ // Nothing should be done when a probe status comes in before loading
+ // finishes.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ EXPECT_EQ(0, update_count());
+
+ // When loading finishes, however, the buffered probe status should be sent
+ // to the page.
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(1, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_STARTED),
+ last_error_html());
+
+ // Should update the page again when the probe result comes in.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN),
+ last_error_html());
+
+ // Any other probe updates should be ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_NOT_RUN);
+ EXPECT_EQ(2, update_count());
+}
+
+// Test case where the error page finishes loading before receiving any DNS
+// probe messages and the request is a POST.
+TEST_F(NetErrorHelperCoreTest, FinishedBeforeProbePost) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ true, &html);
+ // Should have returned a local error page indicating a probe may run.
+ EXPECT_EQ(ErrorToString(
+ ProbeError(chrome_common_net::DNS_PROBE_POSSIBLE),
+ true),
+ html);
+
+ // Error page loads.
+ EXPECT_EQ(0, enable_page_helper_functions_count());
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(1, enable_page_helper_functions_count());
+
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ EXPECT_EQ(1, update_count());
+ EXPECT_EQ(ErrorToString(
+ ProbeError(chrome_common_net::DNS_PROBE_STARTED), true),
+ last_error_html());
+
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(ErrorToString(
+ ProbeError(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN),
+ true),
+ last_error_html());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+// Test case where the probe finishes before the page is committed.
+TEST_F(NetErrorHelperCoreTest, ProbeFinishesEarly) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page indicating a probe may run.
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE), html);
+
+ // Error page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+
+ // Nothing should be done when the probe statuses come in before loading
+ // finishes.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(0, update_count());
+
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ EXPECT_EQ(0, update_count());
+
+ // When loading finishes, however, the buffered probe status should be sent
+ // to the page.
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(1, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN),
+ last_error_html());
+
+ // Any other probe updates should be ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(1, update_count());
+}
+
+// Test case where one error page loads completely before a new navigation
+// results in another error page. Probes are run for both pages.
+TEST_F(NetErrorHelperCoreTest, TwoErrorsWithProbes) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page indicating a probe may run.
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE), html);
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ // Probe results come in.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN),
+ last_error_html());
+
+ // The process starts again.
+
+ // Normal page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page indicating a probe may run.
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE), html);
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(2, update_count());
+
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ EXPECT_EQ(3, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_STARTED),
+ last_error_html());
+
+ // The probe returns a different result this time.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET);
+ EXPECT_EQ(4, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET),
+ last_error_html());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+// Test case where one error page loads completely before a new navigation
+// results in another error page. Probe results for the first probe are only
+// received after the second load starts, but before it commits.
+TEST_F(NetErrorHelperCoreTest, TwoErrorsWithProbesAfterSecondStarts) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page indicating a probe may run.
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE), html);
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ // The process starts again.
+
+ // Normal page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page indicating a probe may run.
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE), html);
+
+ // Error page starts to load.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+
+ // Probe results come in, and the first page is updated.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN),
+ last_error_html());
+
+ // Second page finishes loading, and is updated using the same probe result.
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(3, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN),
+ last_error_html());
+
+ // Other probe results should be ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET);
+ EXPECT_EQ(3, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+// Same as above, but a new page is loaded before the error page commits.
+TEST_F(NetErrorHelperCoreTest, ErrorPageLoadInterrupted) {
+ // Original page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and an error page is requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page indicating a probe may run.
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE), html);
+
+ // Error page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ // Probe statuses come in, but should be ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(0, update_count());
+
+ // A new navigation begins while the error page is loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // And fails.
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ // Should have returned a local error page indicating a probe may run.
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE), html);
+
+ // Error page finishes loading.
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ // Probe results come in.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ EXPECT_EQ(1, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_STARTED),
+ last_error_html());
+
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET),
+ last_error_html());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+//------------------------------------------------------------------------------
+// Navigation correction tests.
+//------------------------------------------------------------------------------
+
+// Check that corrections are not used for HTTPS URLs.
+TEST_F(NetErrorHelperCoreTest, NoCorrectionsForHttps) {
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // The HTTPS page fails to load.
+ std::string html;
+ blink::WebURLError error = NetError(net::ERR_NAME_NOT_RESOLVED);
+ error.unreachableURL = GURL(kFailedHttpsUrl);
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME, error, false, &html);
+
+ blink::WebURLError probe_error =
+ ProbeError(chrome_common_net::DNS_PROBE_POSSIBLE);
+ probe_error.unreachableURL = GURL(kFailedHttpsUrl);
+ EXPECT_EQ(ErrorToString(probe_error, false), html);
+ EXPECT_FALSE(is_url_being_fetched());
+ EXPECT_FALSE(last_error_page_params());
+
+ // The blank page loads, no error page is loaded.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_FALSE(is_url_being_fetched());
+ EXPECT_FALSE(last_error_page_params());
+
+ // Page is updated in response to DNS probes as normal.
+ EXPECT_EQ(0, update_count());
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(2, update_count());
+ EXPECT_FALSE(last_error_page_params());
+ blink::WebURLError final_probe_error =
+ ProbeError(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ final_probe_error.unreachableURL = GURL(kFailedHttpsUrl);
+ EXPECT_EQ(ErrorToString(final_probe_error, false), last_error_html());
+}
+
+// The blank page loads, then the navigation corrections request succeeds and is
+// loaded. Then the probe results come in.
+TEST_F(NetErrorHelperCoreTest, CorrectionsReceivedBeforeProbe) {
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+ EXPECT_FALSE(is_url_being_fetched());
+ EXPECT_FALSE(last_error_page_params());
+
+ // The blank page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+
+ // Corrections retrieval starts when the error page finishes loading.
+ EXPECT_FALSE(is_url_being_fetched());
+ EXPECT_FALSE(last_error_page_params());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_TRUE(is_url_being_fetched());
+ EXPECT_FALSE(last_error_page_params());
+
+ // Corrections are retrieved.
+ NavigationCorrectionsLoadSuccess(kDefaultCorrections,
+ arraysize(kDefaultCorrections));
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(NetErrorString(net::ERR_NAME_NOT_RESOLVED), last_error_html());
+ ExpectDefaultNavigationCorrections();
+ EXPECT_FALSE(is_url_being_fetched());
+
+ // Corrections load.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ // Any probe statuses should be ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(1, error_html_update_count());
+}
+
+// The blank page finishes loading, then probe results come in, and then
+// the navigation corrections request succeeds.
+TEST_F(NetErrorHelperCoreTest, CorrectionsRetrievedAfterProbes) {
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_TRUE(is_url_being_fetched());
+ EXPECT_FALSE(last_error_page_params());
+
+ // Probe statuses should be ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+ EXPECT_FALSE(last_error_page_params());
+
+ // Corrections are retrieved.
+ EXPECT_TRUE(is_url_being_fetched());
+ NavigationCorrectionsLoadSuccess(kDefaultCorrections,
+ arraysize(kDefaultCorrections));
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(NetErrorString(net::ERR_NAME_NOT_RESOLVED), last_error_html());
+ ExpectDefaultNavigationCorrections();
+ EXPECT_FALSE(is_url_being_fetched());
+
+ // Corrections load.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(0, update_count());
+}
+
+// The corrections request fails and then the error page loads for an error that
+// does not trigger DNS probes.
+TEST_F(NetErrorHelperCoreTest, CorrectionsFailLoadNoProbes) {
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_CONNECTION_FAILED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ // Corrections request fails, final error page is shown.
+ EXPECT_TRUE(is_url_being_fetched());
+ NavigationCorrectionsLoadFailure();
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(last_error_html(), NetErrorString(net::ERR_CONNECTION_FAILED));
+ EXPECT_FALSE(is_url_being_fetched());
+ EXPECT_EQ(0, update_count());
+ EXPECT_FALSE(last_error_page_params());
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ // If probe statuses come in last from another page load, they should be
+ // ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(1, error_html_update_count());
+}
+
+// The corrections request fails and then the error page loads before probe
+// results are received.
+TEST_F(NetErrorHelperCoreTest, CorrectionsFailLoadBeforeProbe) {
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ // Corrections request fails, probe pending page shown.
+ EXPECT_TRUE(is_url_being_fetched());
+ NavigationCorrectionsLoadFailure();
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(last_error_html(),
+ ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE));
+ EXPECT_FALSE(is_url_being_fetched());
+ EXPECT_EQ(0, update_count());
+
+ // Probe page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ // Probe statuses comes in, and page is updated.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ EXPECT_EQ(1, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_STARTED),
+ last_error_html());
+
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN),
+ last_error_html());
+
+ // The commit results in sending a second probe status, which is ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(1, error_html_update_count());
+}
+
+// The corrections request fails after receiving probe results.
+TEST_F(NetErrorHelperCoreTest, CorrectionsFailAfterProbe) {
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ // Results come in, but end up being ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(0, update_count());
+
+ // Corrections request fails, probe pending page shown.
+ EXPECT_TRUE(is_url_being_fetched());
+ NavigationCorrectionsLoadFailure();
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(last_error_html(),
+ ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE));
+ EXPECT_FALSE(is_url_being_fetched());
+ EXPECT_EQ(0, update_count());
+
+ // Probe page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ // Probe statuses comes in, and page is updated.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(1, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN),
+ last_error_html());
+ EXPECT_EQ(1, error_html_update_count());
+}
+
+// An error page load that would normally load correction is interrupted
+// by a new navigation before the blank page commits.
+TEST_F(NetErrorHelperCoreTest, CorrectionsInterruptedBeforeCommit) {
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page starts loading.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+
+ // A new page load starts.
+ EXPECT_FALSE(is_url_being_fetched());
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // A new page load interrupts the original load.
+ EXPECT_FALSE(is_url_being_fetched());
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ EXPECT_FALSE(is_url_being_fetched());
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, default_url());
+ EXPECT_FALSE(is_url_being_fetched());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ EXPECT_FALSE(is_url_being_fetched());
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+// An error page load that would normally load corrections is interrupted
+// by a new navigation before the blank page finishes loading.
+TEST_F(NetErrorHelperCoreTest, CorrectionsInterruptedBeforeLoad) {
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page starts loading and is committed.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+
+ // A new page load interrupts the original load.
+ EXPECT_FALSE(is_url_being_fetched());
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ EXPECT_FALSE(is_url_being_fetched());
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, default_url());
+ EXPECT_FALSE(is_url_being_fetched());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ EXPECT_FALSE(is_url_being_fetched());
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(0, error_html_update_count());
+}
+
+// The corrections request is cancelled due to a new navigation. The new
+// navigation fails and then loads corrections successfully.
+TEST_F(NetErrorHelperCoreTest, CorrectionsInterrupted) {
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_TRUE(is_url_being_fetched());
+
+ // Results come in, but end up being ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(0, update_count());
+
+ // A new load appears!
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ EXPECT_FALSE(is_url_being_fetched());
+
+ // It fails, and corrections are requested again once a blank page is loaded.
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ EXPECT_FALSE(is_url_being_fetched());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_TRUE(is_url_being_fetched());
+
+ // Corrections request succeeds.
+ NavigationCorrectionsLoadSuccess(kDefaultCorrections,
+ arraysize(kDefaultCorrections));
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(NetErrorString(net::ERR_NAME_NOT_RESOLVED), last_error_html());
+ ExpectDefaultNavigationCorrections();
+ EXPECT_FALSE(is_url_being_fetched());
+
+ // Probe statuses come in, and are ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(0, update_count());
+}
+
+// The corrections request is cancelled due to call to Stop(). The cross
+// process navigation is cancelled, and then a new load fails and tries to load
+// corrections again, unsuccessfully.
+TEST_F(NetErrorHelperCoreTest, CorrectionsStopped) {
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ EXPECT_TRUE(is_url_being_fetched());
+ core()->OnStop();
+ EXPECT_FALSE(is_url_being_fetched());
+
+ // Results come in, but end up being ignored.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(0, update_count());
+
+ // Cross process navigation must have been cancelled, and a new load appears!
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested again.
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page loads again.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_TRUE(is_url_being_fetched());
+
+ // Corrections request fails, probe pending page shown.
+ NavigationCorrectionsLoadFailure();
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(last_error_html(),
+ ProbeErrorString(chrome_common_net::DNS_PROBE_POSSIBLE));
+ EXPECT_FALSE(is_url_being_fetched());
+
+ // Probe page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ // Probe statuses comes in, and page is updated.
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_STARTED);
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_STARTED),
+ last_error_html());
+ EXPECT_EQ(1, update_count());
+
+ core()->OnNetErrorInfo(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ EXPECT_EQ(2, update_count());
+ EXPECT_EQ(ProbeErrorString(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN),
+ last_error_html());
+ EXPECT_EQ(1, error_html_update_count());
+}
+
+// Check the case corrections are disabled while the blank page (Loaded
+// before the corrections page) is being loaded.
+TEST_F(NetErrorHelperCoreTest, CorrectionsDisabledBeforeFetch) {
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ // Corrections is disabled.
+ DisableNavigationCorrections();
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_TRUE(is_url_being_fetched());
+ EXPECT_FALSE(last_error_page_params());
+
+ // Corrections are retrieved.
+ NavigationCorrectionsLoadSuccess(kDefaultCorrections,
+ arraysize(kDefaultCorrections));
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(NetErrorString(net::ERR_NAME_NOT_RESOLVED), last_error_html());
+ EXPECT_FALSE(is_url_being_fetched());
+ ExpectDefaultNavigationCorrections();
+
+ // Corrections load.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(0, update_count());
+}
+
+// Check the case corrections is disabled while fetching the corrections for
+// a failed page load.
+TEST_F(NetErrorHelperCoreTest, CorrectionsDisabledDuringFetch) {
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_TRUE(is_url_being_fetched());
+ EXPECT_FALSE(last_error_page_params());
+
+ // Corrections are disabled.
+ DisableNavigationCorrections();
+
+ // Corrections are retrieved.
+ NavigationCorrectionsLoadSuccess(kDefaultCorrections,
+ arraysize(kDefaultCorrections));
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(NetErrorString(net::ERR_NAME_NOT_RESOLVED), last_error_html());
+ EXPECT_FALSE(is_url_being_fetched());
+ ExpectDefaultNavigationCorrections();
+
+ // Corrections load.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(0, update_count());
+}
+
+// Checks corrections are is used when there are no search suggestions.
+TEST_F(NetErrorHelperCoreTest, CorrectionsWithoutSearch) {
+ const NavigationCorrection kCorrections[] = {
+ {"urlCorrection", "http://somewhere_else/", "btn", "data", false, false},
+ };
+
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_TRUE(is_url_being_fetched());
+ EXPECT_FALSE(last_error_page_params());
+
+ // Corrections are retrieved.
+ NavigationCorrectionsLoadSuccess(kCorrections, arraysize(kCorrections));
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(NetErrorString(net::ERR_NAME_NOT_RESOLVED), last_error_html());
+ EXPECT_FALSE(is_url_being_fetched());
+
+ // Check params.
+ ASSERT_TRUE(last_error_page_params());
+ EXPECT_FALSE(last_error_page_params()->suggest_reload);
+ EXPECT_EQ(1u, last_error_page_params()->override_suggestions->GetSize());
+ EXPECT_FALSE(last_error_page_params()->search_url.is_valid());
+ EXPECT_EQ("", last_error_page_params()->search_terms);
+
+ // Corrections load.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(0, update_count());
+}
+
+// Checks corrections are used when there are only search suggestions.
+TEST_F(NetErrorHelperCoreTest, CorrectionsOnlySearchSuggestion) {
+ const NavigationCorrection kCorrections[] = {
+ {"webSearchQuery", kSuggestedSearchTerms, "frm", "data", false, false},
+ };
+
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_TRUE(is_url_being_fetched());
+ EXPECT_FALSE(last_error_page_params());
+
+ // Corrections are retrieved.
+ NavigationCorrectionsLoadSuccess(kCorrections, arraysize(kCorrections));
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(NetErrorString(net::ERR_NAME_NOT_RESOLVED), last_error_html());
+ EXPECT_FALSE(is_url_being_fetched());
+
+ // Check params.
+ ASSERT_TRUE(last_error_page_params());
+ EXPECT_FALSE(last_error_page_params()->suggest_reload);
+ EXPECT_EQ(0u, last_error_page_params()->override_suggestions->GetSize());
+ EXPECT_EQ(GURL(kSearchUrl), last_error_page_params()->search_url);
+ EXPECT_EQ(kSuggestedSearchTerms, last_error_page_params()->search_terms);
+
+ // Corrections load.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(0, update_count());
+}
+
+// The correction service returns a non-JSON result.
+TEST_F(NetErrorHelperCoreTest, CorrectionServiceReturnsNonJsonResult) {
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_CONNECTION_FAILED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ // Corrections request fails, final error page is shown.
+ EXPECT_TRUE(is_url_being_fetched());
+ NavigationCorrectionsLoadFinished("Weird Response");
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(last_error_html(), NetErrorString(net::ERR_CONNECTION_FAILED));
+ EXPECT_FALSE(is_url_being_fetched());
+ EXPECT_EQ(0, update_count());
+ EXPECT_FALSE(last_error_page_params());
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+}
+
+// The correction service returns a JSON result that isn't a valid list of
+// corrections.
+TEST_F(NetErrorHelperCoreTest, CorrectionServiceReturnsInvalidJsonResult) {
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails, and corrections are requested.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_CONNECTION_FAILED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+
+ // The blank page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ // Corrections request fails, final error page is shown.
+ EXPECT_TRUE(is_url_being_fetched());
+ NavigationCorrectionsLoadFinished("{\"result\": 42}");
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(last_error_html(), NetErrorString(net::ERR_CONNECTION_FAILED));
+ EXPECT_FALSE(is_url_being_fetched());
+ EXPECT_EQ(0, update_count());
+ EXPECT_FALSE(last_error_page_params());
+
+ // Error page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+}
+
+TEST_F(NetErrorHelperCoreTest, CorrectionClickTracking) {
+ // Go through the standard navigation correction steps.
+
+ // Original page starts loading.
+ EnableNavigationCorrections();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+
+ // It fails.
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_NAME_NOT_RESOLVED),
+ false, &html);
+ EXPECT_TRUE(html.empty());
+ EXPECT_FALSE(is_url_being_fetched());
+ EXPECT_FALSE(last_error_page_params());
+
+ // The blank page loads.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+
+ // Corrections retrieval starts when the error page finishes loading.
+ EXPECT_FALSE(is_url_being_fetched());
+ EXPECT_FALSE(last_error_page_params());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_TRUE(is_url_being_fetched());
+ EXPECT_FALSE(last_error_page_params());
+
+ // Corrections are retrieved.
+ NavigationCorrectionsLoadSuccess(kDefaultCorrections,
+ arraysize(kDefaultCorrections));
+ EXPECT_EQ(1, error_html_update_count());
+ EXPECT_EQ(NetErrorString(net::ERR_NAME_NOT_RESOLVED), last_error_html());
+ ExpectDefaultNavigationCorrections();
+ EXPECT_FALSE(is_url_being_fetched());
+
+ // Corrections load.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ EXPECT_EQ(0, tracking_request_count());
+
+ // Invalid clicks should be ignored.
+ core()->TrackClick(-1);
+ core()->TrackClick(arraysize(kDefaultCorrections));
+ EXPECT_EQ(0, tracking_request_count());
+
+ for (size_t i = 0; i < arraysize(kDefaultCorrections); ++i) {
+ // Skip links that do not appear in the page.
+ if (kDefaultCorrections[i].is_porn || kDefaultCorrections[i].is_soft_porn)
+ continue;
+
+ int old_tracking_request_count = tracking_request_count();
+ core()->TrackClick(i);
+ EXPECT_EQ(old_tracking_request_count + 1, tracking_request_count());
+ EXPECT_EQ(GURL(kNavigationCorrectionUrl), last_tracking_url());
+
+ // Make sure all expected strings appear in output.
+ EXPECT_TRUE(last_tracking_request_body().find(
+ kDefaultCorrections[i].url_correction));
+ EXPECT_TRUE(last_tracking_request_body().find(
+ kDefaultCorrections[i].click_data));
+ EXPECT_TRUE(last_tracking_request_body().find(
+ kDefaultCorrections[i].click_type));
+ EXPECT_TRUE(last_tracking_request_body().find(
+ kNavigationCorrectionEventId));
+ EXPECT_TRUE(last_tracking_request_body().find(
+ kNavigationCorrectionFingerprint));
+ }
+
+ // Make sure duplicate tracking requests are ignored.
+ for (size_t i = 0; i < arraysize(kDefaultCorrections); ++i) {
+ // Skip links that do not appear in the page.
+ if (kDefaultCorrections[i].is_porn || kDefaultCorrections[i].is_soft_porn)
+ continue;
+
+ int old_tracking_request_count = tracking_request_count();
+ core()->TrackClick(i);
+ EXPECT_EQ(old_tracking_request_count, tracking_request_count());
+ }
+
+ EXPECT_EQ(0, update_count());
+ EXPECT_EQ(1, error_html_update_count());
+}
+
+TEST_F(NetErrorHelperCoreTest, AutoReloadDisabled) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+
+ EXPECT_FALSE(timer()->IsRunning());
+ EXPECT_EQ(0, reload_count());
+}
+
+class NetErrorHelperCoreAutoReloadTest : public NetErrorHelperCoreTest {
+ public:
+ virtual void SetUp() {
+ NetErrorHelperCoreTest::SetUp();
+ SetUpCore(true, false, true);
+ }
+};
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, Succeeds) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+
+ EXPECT_TRUE(timer()->IsRunning());
+ EXPECT_EQ(0, reload_count());
+
+ timer()->Fire();
+ EXPECT_FALSE(timer()->IsRunning());
+ EXPECT_EQ(1, reload_count());
+
+ DoSuccessLoad();
+
+ EXPECT_FALSE(timer()->IsRunning());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, Retries) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+
+ EXPECT_TRUE(timer()->IsRunning());
+ base::TimeDelta first_delay = timer()->GetCurrentDelay();
+ EXPECT_EQ(0, reload_count());
+
+ timer()->Fire();
+ EXPECT_FALSE(timer()->IsRunning());
+ EXPECT_EQ(1, reload_count());
+
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+
+ EXPECT_TRUE(timer()->IsRunning());
+ EXPECT_GT(timer()->GetCurrentDelay(), first_delay);
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, StopsTimerOnStop) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ EXPECT_TRUE(timer()->IsRunning());
+ core()->OnStop();
+ EXPECT_FALSE(timer()->IsRunning());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, StopsLoadingOnStop) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ EXPECT_EQ(0, core()->auto_reload_count());
+ timer()->Fire();
+ EXPECT_EQ(1, core()->auto_reload_count());
+ EXPECT_EQ(1, reload_count());
+ core()->OnStop();
+ EXPECT_FALSE(timer()->IsRunning());
+ EXPECT_EQ(0, core()->auto_reload_count());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, StopsOnOtherLoadStart) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ EXPECT_TRUE(timer()->IsRunning());
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ EXPECT_FALSE(timer()->IsRunning());
+ EXPECT_EQ(0, core()->auto_reload_count());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, ResetsCountOnSuccess) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ base::TimeDelta delay = timer()->GetCurrentDelay();
+ EXPECT_EQ(0, core()->auto_reload_count());
+ timer()->Fire();
+ EXPECT_EQ(1, core()->auto_reload_count());
+ EXPECT_EQ(1, reload_count());
+ DoSuccessLoad();
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ EXPECT_EQ(0, core()->auto_reload_count());
+ EXPECT_EQ(timer()->GetCurrentDelay(), delay);
+ timer()->Fire();
+ EXPECT_EQ(1, core()->auto_reload_count());
+ EXPECT_EQ(2, reload_count());
+ DoSuccessLoad();
+ EXPECT_EQ(0, core()->auto_reload_count());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, RestartsOnOnline) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ base::TimeDelta delay = timer()->GetCurrentDelay();
+ timer()->Fire();
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ EXPECT_TRUE(timer()->IsRunning());
+ EXPECT_NE(delay, timer()->GetCurrentDelay());
+ core()->NetworkStateChanged(false);
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->NetworkStateChanged(true);
+ EXPECT_TRUE(timer()->IsRunning());
+ EXPECT_EQ(delay, timer()->GetCurrentDelay());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, DoesNotStartOnOnline) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ timer()->Fire();
+ DoSuccessLoad();
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->NetworkStateChanged(true);
+ EXPECT_FALSE(timer()->IsRunning());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, DoesNotStartOffline) {
+ core()->NetworkStateChanged(false);
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->NetworkStateChanged(true);
+ EXPECT_TRUE(timer()->IsRunning());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, DoesNotRestartOnOnlineAfterStop) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ timer()->Fire();
+ core()->OnStop();
+ core()->NetworkStateChanged(true);
+ EXPECT_FALSE(timer()->IsRunning());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, WithDnsProbes) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ DoDnsProbe(chrome_common_net::DNS_PROBE_FINISHED_NXDOMAIN);
+ timer()->Fire();
+ EXPECT_EQ(1, reload_count());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, ExponentialBackoffLevelsOff) {
+ base::TimeDelta previous = base::TimeDelta::FromMilliseconds(0);
+ const int kMaxTries = 50;
+ int tries = 0;
+ for (tries = 0; tries < kMaxTries; tries++) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ EXPECT_TRUE(timer()->IsRunning());
+ if (previous == timer()->GetCurrentDelay())
+ break;
+ previous = timer()->GetCurrentDelay();
+ timer()->Fire();
+ }
+
+ EXPECT_LT(tries, kMaxTries);
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, SlowError) {
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_CONNECTION_RESET), false, &html);
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ EXPECT_FALSE(timer()->IsRunning());
+ // Start a new non-error page load.
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ EXPECT_FALSE(timer()->IsRunning());
+ // Finish the error page load.
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_FALSE(timer()->IsRunning());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, OnlineSlowError) {
+ core()->NetworkStateChanged(false);
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_CONNECTION_RESET), false, &html);
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->NetworkStateChanged(true);
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->NetworkStateChanged(false);
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->NetworkStateChanged(true);
+ EXPECT_TRUE(timer()->IsRunning());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, OnlinePendingError) {
+ core()->NetworkStateChanged(false);
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_CONNECTION_RESET), false, &html);
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->NetworkStateChanged(true);
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->NetworkStateChanged(false);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->NetworkStateChanged(true);
+ EXPECT_TRUE(timer()->IsRunning());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, OnlinePartialErrorReplacement) {
+ core()->NetworkStateChanged(false);
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ std::string html;
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_CONNECTION_RESET), false, &html);
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ core()->GetErrorHTML(NetErrorHelperCore::MAIN_FRAME,
+ NetError(net::ERR_CONNECTION_RESET), false, &html);
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::ERROR_PAGE);
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->NetworkStateChanged(true);
+ EXPECT_FALSE(timer()->IsRunning());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, ShouldSuppressNonReloadableErrorPage) {
+ DoErrorLoad(net::ERR_ABORTED);
+ EXPECT_FALSE(core()->ShouldSuppressErrorPage(NetErrorHelperCore::MAIN_FRAME,
+ GURL(kFailedUrl)));
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, ShouldSuppressErrorPage) {
+ // Set up the environment to test ShouldSuppressErrorPage: auto-reload is
+ // enabled, an error page is loaded, and the auto-reload callback is running.
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ timer()->Fire();
+
+ EXPECT_FALSE(core()->ShouldSuppressErrorPage(NetErrorHelperCore::SUB_FRAME,
+ GURL(kFailedUrl)));
+ EXPECT_FALSE(core()->ShouldSuppressErrorPage(NetErrorHelperCore::MAIN_FRAME,
+ GURL("http://some.other.url")));
+ EXPECT_TRUE(core()->ShouldSuppressErrorPage(NetErrorHelperCore::MAIN_FRAME,
+ GURL(kFailedUrl)));
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, HiddenAndShown) {
+ SetUpCore(true, true, true);
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ EXPECT_TRUE(timer()->IsRunning());
+ core()->OnWasHidden();
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->OnWasShown();
+ EXPECT_TRUE(timer()->IsRunning());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, HiddenWhileOnline) {
+ SetUpCore(true, true, true);
+ core()->NetworkStateChanged(false);
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->OnWasHidden();
+ core()->NetworkStateChanged(true);
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->NetworkStateChanged(false);
+ core()->OnWasShown();
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->NetworkStateChanged(true);
+ EXPECT_TRUE(timer()->IsRunning());
+ core()->NetworkStateChanged(false);
+ core()->OnWasHidden();
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->NetworkStateChanged(true);
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->OnWasShown();
+ EXPECT_TRUE(timer()->IsRunning());
+}
+
+TEST_F(NetErrorHelperCoreAutoReloadTest, ShownWhileNotReloading) {
+ SetUpCore(true, true, false);
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ EXPECT_FALSE(timer()->IsRunning());
+ core()->OnWasShown();
+ EXPECT_TRUE(timer()->IsRunning());
+}
+
+class NetErrorHelperCoreHistogramTest
+ : public NetErrorHelperCoreAutoReloadTest {
+ public:
+ virtual void SetUp() override {
+ NetErrorHelperCoreAutoReloadTest::SetUp();
+ }
+
+ static const char kCountAtStop[];
+ static const char kErrorAtStop[];
+ static const char kCountAtSuccess[];
+ static const char kErrorAtSuccess[];
+ static const char kErrorAtFirstSuccess[];
+
+ protected:
+ base::HistogramTester histogram_tester_;
+};
+
+const char NetErrorHelperCoreHistogramTest::kCountAtStop[] =
+ "Net.AutoReload.CountAtStop";
+const char NetErrorHelperCoreHistogramTest::kErrorAtStop[] =
+ "Net.AutoReload.ErrorAtStop";
+const char NetErrorHelperCoreHistogramTest::kCountAtSuccess[] =
+ "Net.AutoReload.CountAtSuccess";
+const char NetErrorHelperCoreHistogramTest::kErrorAtSuccess[] =
+ "Net.AutoReload.ErrorAtSuccess";
+const char NetErrorHelperCoreHistogramTest::kErrorAtFirstSuccess[] =
+ "Net.AutoReload.ErrorAtFirstSuccess";
+
+// Test that the success histograms are updated when auto-reload succeeds at the
+// first attempt, and that the failure histograms are not updated.
+TEST_F(NetErrorHelperCoreHistogramTest, SuccessAtFirstAttempt) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ timer()->Fire();
+ DoSuccessLoad();
+
+ // All of CountAtSuccess, ErrorAtSuccess, and ErrorAtFirstSuccess should
+ // reflect this successful load. The failure histograms should be unchanged.
+ histogram_tester_.ExpectTotalCount(kCountAtSuccess, 1);
+ histogram_tester_.ExpectTotalCount(kErrorAtSuccess, 1);
+ histogram_tester_.ExpectTotalCount(kErrorAtFirstSuccess, 1);
+ histogram_tester_.ExpectTotalCount(kCountAtStop, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtStop, 0);
+}
+
+// Test that the success histograms are updated when auto-reload succeeds but
+// not on the first attempt, and that the first-success histogram is not
+// updated.
+TEST_F(NetErrorHelperCoreHistogramTest, SuccessAtSecondAttempt) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ timer()->Fire();
+ EXPECT_TRUE(core()->ShouldSuppressErrorPage(NetErrorHelperCore::MAIN_FRAME,
+ default_url()));
+// DoErrorLoad(net::ERR_CONNECTION_RESET);
+ timer()->Fire();
+ DoSuccessLoad();
+
+ // CountAtSuccess and ErrorAtSuccess should reflect this successful load, but
+ // not ErrorAtFirstSuccess since it wasn't a first success.
+ histogram_tester_.ExpectTotalCount(kCountAtSuccess, 1);
+ histogram_tester_.ExpectTotalCount(kErrorAtSuccess, 1);
+ histogram_tester_.ExpectTotalCount(kErrorAtFirstSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kCountAtStop, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtStop, 0);
+}
+
+// Test that a user stop (caused by the user pressing the 'Stop' button)
+// registers as an auto-reload failure if an auto-reload attempt is in flight.
+// Note that "user stop" is also caused by a cross-process navigation, for which
+// the browser process will send an OnStop to the old process.
+TEST_F(NetErrorHelperCoreHistogramTest, UserStop) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ timer()->Fire();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ core()->OnStop();
+
+ // CountAtStop and ErrorAtStop should reflect the failure.
+ histogram_tester_.ExpectTotalCount(kCountAtSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtFirstSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kCountAtStop, 1);
+ histogram_tester_.ExpectTotalCount(kErrorAtStop, 1);
+}
+
+// Test that a user stop (caused by the user pressing the 'Stop' button)
+// registers as an auto-reload failure even if an auto-reload attempt has not
+// been launched yet (i.e., if the timer is running, but no reload is in
+// flight), because this means auto-reload didn't successfully replace the error
+// page.
+TEST_F(NetErrorHelperCoreHistogramTest, OtherPageLoaded) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ core()->OnStop();
+
+ histogram_tester_.ExpectTotalCount(kCountAtSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtFirstSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kCountAtStop, 1);
+ histogram_tester_.ExpectTotalCount(kErrorAtStop, 1);
+}
+
+// Test that a commit of a different URL (caused by the user navigating to a
+// different page) with an auto-reload attempt in flight registers as an
+// auto-reload failure.
+TEST_F(NetErrorHelperCoreHistogramTest, OtherPageLoadedAfterTimerFires) {
+ const GURL kTestUrl("https://anotherurl");
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ timer()->Fire();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME,
+ kTestUrl);
+ core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
+
+ histogram_tester_.ExpectTotalCount(kCountAtSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtFirstSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kCountAtStop, 1);
+ histogram_tester_.ExpectTotalCount(kErrorAtStop, 1);
+}
+
+// Test that a commit of the same URL with an auto-reload attempt in flight
+// registers as an auto-reload success.
+TEST_F(NetErrorHelperCoreHistogramTest, SamePageLoadedAfterTimerFires) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ timer()->Fire();
+ DoSuccessLoad();
+
+ histogram_tester_.ExpectTotalCount(kCountAtSuccess, 1);
+ histogram_tester_.ExpectTotalCount(kErrorAtSuccess, 1);
+ histogram_tester_.ExpectTotalCount(kErrorAtFirstSuccess, 1);
+ histogram_tester_.ExpectTotalCount(kCountAtStop, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtStop, 0);
+}
+
+TEST_F(NetErrorHelperCoreHistogramTest, SamePageLoadedAfterLoadStarts) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ timer()->Fire();
+ // Autoreload attempt starts
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ // User does a manual reload
+ DoSuccessLoad();
+
+ histogram_tester_.ExpectTotalCount(kCountAtSuccess, 1);
+ histogram_tester_.ExpectTotalCount(kErrorAtSuccess, 1);
+ histogram_tester_.ExpectTotalCount(kErrorAtFirstSuccess, 1);
+ histogram_tester_.ExpectTotalCount(kCountAtStop, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtStop, 0);
+}
+
+// In this test case, the user presses the reload button manually after an
+// auto-reload fails and the error page is suppressed.
+TEST_F(NetErrorHelperCoreHistogramTest, ErrorPageLoadedAfterTimerFires) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ timer()->Fire();
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ EXPECT_TRUE(core()->ShouldSuppressErrorPage(NetErrorHelperCore::MAIN_FRAME,
+ default_url()));
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+
+ histogram_tester_.ExpectTotalCount(kCountAtSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtFirstSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kCountAtStop, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtStop, 0);
+}
+
+TEST_F(NetErrorHelperCoreHistogramTest, SuccessPageLoadedBeforeTimerFires) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
+ NetErrorHelperCore::NON_ERROR_PAGE);
+ core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME,
+ GURL(kFailedHttpsUrl));
+
+ histogram_tester_.ExpectTotalCount(kCountAtSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kErrorAtFirstSuccess, 0);
+ histogram_tester_.ExpectTotalCount(kCountAtStop, 1);
+ histogram_tester_.ExpectTotalCount(kErrorAtStop, 1);
+}
+
+
+TEST_F(NetErrorHelperCoreTest, ExplicitReloadSucceeds) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ EXPECT_EQ(0, reload_count());
+ core()->ExecuteButtonPress(NetErrorHelperCore::RELOAD_BUTTON);
+ EXPECT_EQ(1, reload_count());
+}
+
+TEST_F(NetErrorHelperCoreTest, ExplicitLoadStaleSucceeds) {
+ DoErrorLoad(net::ERR_CONNECTION_RESET);
+ EXPECT_EQ(0, load_stale_count());
+ core()->ExecuteButtonPress(NetErrorHelperCore::LOAD_STALE_BUTTON);
+ EXPECT_EQ(1, load_stale_count());
+ EXPECT_EQ(GURL(kFailedUrl), load_stale_url());
+}
+
+} // namespace
+} // namespace error_page