Move error page auto-reload to shared browser code
This CL moves Chrome's renderer-side error page auto-reload behavior
implementation to a new browser-side NetErrorAutoReloader helper within
the error_page component. Correpsonding tests are rewritten as browser
tests within components_browsertests.
Bug: 1098578
Change-Id: I42e7af0a3d23364d5065e474312f35620299ff54
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2302927
Reviewed-by: Matt Menke <[email protected]>
Reviewed-by: Avi Drissman <[email protected]>
Reviewed-by: Nasko Oskov <[email protected]>
Reviewed-by: Carlos IL <[email protected]>
Commit-Queue: Ken Rockot <[email protected]>
Cr-Commit-Position: refs/heads/master@{#791935}
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 81935b1..9960de7 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -110,6 +110,7 @@
"+components/embedder_support",
"+components/encrypted_messages",
"+components/enterprise",
+ "+components/error_page/content/browser",
"+components/exo",
"+components/external_intents",
"+components/favicon_base",
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 2cfdc073..fc12a1f 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -207,6 +207,7 @@
#include "components/dom_distiller/core/url_constants.h"
#include "components/embedder_support/switches.h"
#include "components/error_page/common/error_page_switches.h"
+#include "components/error_page/content/browser/net_error_auto_reloader.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "components/feature_engagement/public/feature_list.h"
#include "components/google/core/common/google_switches.h"
@@ -1119,6 +1120,18 @@
}
}
+bool IsErrorPageAutoReloadEnabled() {
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
+ if (command_line.HasSwitch(switches::kEnableAutomation))
+ return false;
+ if (command_line.HasSwitch(switches::kEnableAutoReload))
+ return true;
+ if (command_line.HasSwitch(switches::kDisableAutoReload))
+ return false;
+ return true;
+}
+
} // namespace
// Generate a pseudo-random permutation of the following brand/version pairs:
@@ -2041,16 +2054,6 @@
namespace {
-bool IsAutoReloadEnabled() {
- const base::CommandLine& browser_command_line =
- *base::CommandLine::ForCurrentProcess();
- if (browser_command_line.HasSwitch(switches::kEnableAutoReload))
- return true;
- if (browser_command_line.HasSwitch(switches::kDisableAutoReload))
- return false;
- return true;
-}
-
void MaybeAppendBlinkSettingsSwitchForFieldTrial(
const base::CommandLine& browser_command_line,
base::CommandLine* command_line) {
@@ -2302,9 +2305,6 @@
}
}
- if (IsAutoReloadEnabled())
- command_line->AppendSwitch(switches::kEnableAutoReload);
-
MaybeAppendBlinkSettingsSwitchForFieldTrial(browser_command_line,
command_line);
@@ -4111,6 +4111,12 @@
handle, std::make_unique<ChromeSecurityBlockingPageFactory>()),
&throttles);
+ if (IsErrorPageAutoReloadEnabled() && handle->IsInMainFrame()) {
+ MaybeAddThrottle(
+ error_page::NetErrorAutoReloader::MaybeCreateThrottleFor(handle),
+ &throttles);
+ }
+
return throttles;
}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 99c75f9..20e948f3 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -420,6 +420,7 @@
"//components/embedder_support",
"//components/encrypted_messages:encrypted_message_proto",
"//components/enterprise",
+ "//components/error_page/content/browser",
"//components/favicon/content",
"//components/favicon/core",
"//components/feature_engagement",
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 16541dd..5db905d 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -1231,14 +1231,6 @@
content::RenderFrame* render_frame,
const GURL& url,
int error_code) {
- // Unit tests for ChromeContentRendererClient pass a NULL RenderFrame here.
- // Unfortunately it's very difficult to construct a mock RenderView, so skip
- // this functionality in this case.
- if (render_frame && NetErrorHelper::Get(render_frame)
- ->ShouldSuppressErrorPage(url, error_code)) {
- return true;
- }
-
// Do not flash an error page if the Instant new tab page fails to load.
bool is_instant_ntp = false;
#if !defined(OS_ANDROID)
diff --git a/chrome/renderer/net/net_error_helper.cc b/chrome/renderer/net/net_error_helper.cc
index 68af1dd2..747497b6 100644
--- a/chrome/renderer/net/net_error_helper.cc
+++ b/chrome/renderer/net/net_error_helper.cc
@@ -81,12 +81,6 @@
namespace {
-NetErrorHelperCore::PageType GetLoadingPageType(const GURL& url) {
- if (!url.is_valid() || url.spec() != kUnreachableWebDataURL)
- return NetErrorHelperCore::NON_ERROR_PAGE;
- return NetErrorHelperCore::ERROR_PAGE;
-}
-
NetErrorHelperCore::FrameType GetFrameType(RenderFrame* render_frame) {
if (render_frame->IsMainFrame())
return NetErrorHelperCore::MAIN_FRAME;
@@ -123,24 +117,16 @@
NetErrorHelper::NetErrorHelper(RenderFrame* render_frame)
: RenderFrameObserver(render_frame),
content::RenderFrameObserverTracker<NetErrorHelper>(render_frame) {
- RenderThread::Get()->AddObserver(this);
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- bool auto_reload_enabled =
- command_line->HasSwitch(switches::kEnableAutoReload);
// TODO(mmenke): Consider only creating a NetErrorHelperCore for main frames.
// subframes don't need any of the NetErrorHelperCore's extra logic.
- core_.reset(new NetErrorHelperCore(this,
- auto_reload_enabled,
- !render_frame->IsHidden()));
+ core_ = std::make_unique<NetErrorHelperCore>(this);
render_frame->GetAssociatedInterfaceRegistry()->AddInterface(
base::BindRepeating(&NetErrorHelper::OnNetworkDiagnosticsClientRequest,
base::Unretained(this)));
}
-NetErrorHelper::~NetErrorHelper() {
- RenderThread::Get()->RemoveObserver(this);
-}
+NetErrorHelper::~NetErrorHelper() = default;
void NetErrorHelper::ButtonPressed(NetErrorHelperCore::Button button) {
core_->ExecuteButtonPress(button);
@@ -179,12 +165,6 @@
return interface;
}
-void NetErrorHelper::DidStartNavigation(
- const GURL& url,
- base::Optional<blink::WebNavigationType> navigation_type) {
- core_->OnStartLoad(GetFrameType(render_frame()), GetLoadingPageType(url));
-}
-
void NetErrorHelper::DidCommitProvisionalLoad(ui::PageTransition transition) {
// Invalidate weak pointers from old error page controllers. If loading a new
// error page, the controller has not yet been attached, so this won't affect
@@ -200,26 +180,10 @@
core_->OnFinishLoad(GetFrameType(render_frame()));
}
-void NetErrorHelper::OnStop() {
- core_->OnStop();
-}
-
-void NetErrorHelper::WasShown() {
- core_->OnWasShown();
-}
-
-void NetErrorHelper::WasHidden() {
- core_->OnWasHidden();
-}
-
void NetErrorHelper::OnDestruct() {
delete this;
}
-void NetErrorHelper::NetworkStateChanged(bool enabled) {
- core_->NetworkStateChanged(enabled);
-}
-
void NetErrorHelper::PrepareErrorPage(const error_page::Error& error,
bool is_failed_post,
std::string* error_html) {
@@ -227,11 +191,6 @@
error_html);
}
-bool NetErrorHelper::ShouldSuppressErrorPage(const GURL& url, int error_code) {
- return core_->ShouldSuppressErrorPage(GetFrameType(render_frame()), url,
- error_code);
-}
-
std::unique_ptr<network::ResourceRequest> NetErrorHelper::CreatePostRequest(
const GURL& url) const {
auto resource_request = std::make_unique<network::ResourceRequest>();
diff --git a/chrome/renderer/net/net_error_helper.h b/chrome/renderer/net/net_error_helper.h
index fbc2ce1..7fde1e1 100644
--- a/chrome/renderer/net/net_error_helper.h
+++ b/chrome/renderer/net/net_error_helper.h
@@ -23,7 +23,6 @@
#include "components/security_interstitials/core/controller_client.h"
#include "content/public/renderer/render_frame_observer.h"
#include "content/public/renderer/render_frame_observer_tracker.h"
-#include "content/public/renderer/render_thread_observer.h"
#include "mojo/public/cpp/bindings/associated_receiver_set.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
@@ -44,7 +43,6 @@
class NetErrorHelper
: public content::RenderFrameObserver,
public content::RenderFrameObserverTracker<NetErrorHelper>,
- public content::RenderThreadObserver,
public NetErrorHelperCore::Delegate,
public NetErrorPageController::Delegate,
public security_interstitials::SecurityInterstitialPageController::
@@ -71,19 +69,10 @@
GetInterface() override;
// RenderFrameObserver implementation.
- void DidStartNavigation(
- const GURL& url,
- base::Optional<blink::WebNavigationType> navigation_type) override;
void DidCommitProvisionalLoad(ui::PageTransition transition) override;
void DidFinishLoad() override;
- void OnStop() override;
- void WasShown() override;
- void WasHidden() override;
void OnDestruct() override;
- // RenderThreadObserver implementation.
- void NetworkStateChanged(bool online) override;
-
// Sets values in |pending_error_page_info_|. If |error_html| is not null, it
// initializes |error_html| with the HTML of an error page in response to
// |error|. Updates internals state with the assumption the page will be
@@ -92,10 +81,6 @@
bool is_failed_post,
std::string* error_html);
- // Returns whether a load for |url| with |error_code| in the |frame| the
- // NetErrorHelper is attached to should have its error page suppressed.
- bool ShouldSuppressErrorPage(const GURL& url, int error_code);
-
private:
// Returns ResourceRequest filled with |url|. It has request_initiator from
// the frame origin and origin header with "null" for a unique origin.
diff --git a/chrome/renderer/net/net_error_helper_core.cc b/chrome/renderer/net/net_error_helper_core.cc
index 488ae78..8e5edea0 100644
--- a/chrome/renderer/net/net_error_helper_core.cc
+++ b/chrome/renderer/net/net_error_helper_core.cc
@@ -22,14 +22,6 @@
namespace {
-base::TimeDelta GetAutoReloadTime(size_t reload_count) {
- static const int kDelaysMs[] = {0, 5000, 30000, 60000,
- 300000, 600000, 1800000};
- if (reload_count >= base::size(kDelaysMs))
- reload_count = base::size(kDelaysMs) - 1;
- return base::TimeDelta::FromMilliseconds(kDelaysMs[reload_count]);
-}
-
// Returns whether |error| is a DNS-related error (and therefore whether
// the tab helper should start a DNS probe after receiving it).
bool IsNetDnsError(const error_page::Error& error) {
@@ -41,11 +33,7 @@
struct NetErrorHelperCore::ErrorPageInfo {
ErrorPageInfo(error_page::Error error, bool was_failed_post)
- : error(error),
- was_failed_post(was_failed_post),
- needs_dns_updates(false),
- is_finished_loading(false),
- auto_reload_triggered(false) {}
+ : error(error), was_failed_post(was_failed_post) {}
// Information about the failed page load.
error_page::Error error;
@@ -55,70 +43,21 @@
// True if a page is a DNS error page and has not yet received a final DNS
// probe status.
- bool needs_dns_updates;
+ bool needs_dns_updates = false;
bool dns_probe_complete = false;
// 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;
+ bool is_finished_loading = false;
error_page::LocalizedError::PageState page_state;
};
-bool NetErrorHelperCore::IsReloadableError(
- const NetErrorHelperCore::ErrorPageInfo& info) {
- GURL url = info.error.url();
- return info.error.domain() == error_page::Error::kNetErrorDomain &&
- info.error.reason() != net::ERR_ABORTED &&
- // For now, net::ERR_UNKNOWN_URL_SCHEME is only being displayed on
- // Chrome for Android.
- info.error.reason() != net::ERR_UNKNOWN_URL_SCHEME &&
- // Do not trigger if the server rejects a client certificate.
- // https://crbug.com/431387
- !net::IsClientCertificateError(info.error.reason()) &&
- // Some servers reject client certificates with a generic
- // handshake_failure alert.
- // https://crbug.com/431387
- info.error.reason() != net::ERR_SSL_PROTOCOL_ERROR &&
- // Do not trigger for blacklisted URLs.
- // https://crbug.com/803839
- // Do not trigger for requests that were blocked by the browser itself.
- !net::IsRequestBlockedError(info.error.reason()) &&
- !info.was_failed_post &&
- // Do not trigger for this error code because it is used by Chrome
- // while an auth prompt is being displayed.
- info.error.reason() != net::ERR_INVALID_AUTH_CREDENTIALS &&
- // Don't auto-reload non-http/https schemas.
- // https://crbug.com/471713
- url.SchemeIsHTTPOrHTTPS() &&
- // Don't auto reload if the error was a secure DNS network error, since
- // the reload may interfere with the captive portal probe state.
- // TODO(crbug.com/1016164): Explore how to allow reloads for secure DNS
- // network errors without interfering with the captive portal probe
- // state.
- !info.error.resolve_error_info().is_secure_network_error;
-}
-
-NetErrorHelperCore::NetErrorHelperCore(Delegate* delegate,
- bool auto_reload_enabled,
- bool is_visible)
+NetErrorHelperCore::NetErrorHelperCore(Delegate* delegate)
: delegate_(delegate),
last_probe_status_(error_page::DNS_PROBE_POSSIBLE),
can_show_network_diagnostics_dialog_(false),
- auto_reload_enabled_(auto_reload_enabled),
- auto_reload_timer_(new base::OneShotTimer()),
- auto_reload_paused_(false),
- auto_reload_in_flight_(false),
- uncommitted_load_started_(false),
- online_(content::RenderThread::Get()->IsOnline()),
- visible_(is_visible),
- auto_reload_count_(0),
- navigation_from_button_(NO_BUTTON),
- custom_error_page_(false)
+ navigation_from_button_(NO_BUTTON)
#if defined(OS_ANDROID)
,
page_auto_fetcher_helper_(
@@ -129,60 +68,10 @@
NetErrorHelperCore::~NetErrorHelperCore() = default;
-void NetErrorHelperCore::CancelPendingAutoReload() {
- auto_reload_timer_->Stop();
- auto_reload_paused_ = false;
-}
-
-void NetErrorHelperCore::OnStop() {
- CancelPendingAutoReload();
- uncommitted_load_started_ = false;
- auto_reload_count_ = 0;
- auto_reload_in_flight_ = false;
-}
-
-void NetErrorHelperCore::OnWasShown() {
- visible_ = true;
- if (auto_reload_paused_)
- MaybeStartAutoReloadTimer();
-}
-
-void NetErrorHelperCore::OnWasHidden() {
- visible_ = false;
- 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) {
- CancelPendingAutoReload();
- } else {
- // Halt auto-reload if it's currently scheduled. OnFinishLoad will trigger
- // auto-reload if appropriate.
- PauseAutoReloadTimer();
- }
-}
-
void NetErrorHelperCore::OnCommitLoad(FrameType frame_type, const GURL& url) {
if (frame_type != MAIN_FRAME)
return;
- // If a page is committing, either it's an error page and autoreload will be
- // started again below, or it's a success page and we need to clear autoreload
- // state.
- auto_reload_in_flight_ = false;
-
- // 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;
-
#if defined(OS_ANDROID)
// Don't need this state. It will be refreshed if another error page is
// loaded.
@@ -244,24 +133,17 @@
if (frame_type != MAIN_FRAME)
return;
- if (!committed_error_page_info_) {
- auto_reload_count_ = 0;
+ if (!committed_error_page_info_)
return;
- }
+
committed_error_page_info_->is_finished_loading = true;
RecordEvent(error_page::NETWORK_ERROR_PAGE_SHOWN);
delegate_->SetIsShowingDownloadButton(
committed_error_page_info_->page_state.download_button_shown);
-
delegate_->EnablePageHelperFunctions();
- if (auto_reload_enabled_ && IsReloadableError(*committed_error_page_info_) &&
- !custom_error_page_) {
- MaybeStartAutoReloadTimer();
- }
-
DVLOG(1) << "Error page finished loading; sending saved status.";
if (committed_error_page_info_->needs_dns_updates) {
if (last_probe_status_ != error_page::DNS_PROBE_POSSIBLE)
@@ -278,16 +160,11 @@
if (frame_type == MAIN_FRAME) {
pending_error_page_info_.reset(new ErrorPageInfo(error, is_failed_post));
PrepareErrorPageForMainFrame(pending_error_page_info_.get(), error_html);
- } else {
- if (error_html) {
- custom_error_page_ = false;
- delegate_->GenerateLocalizedErrorPage(
- error, is_failed_post,
- false /* No diagnostics dialogs allowed for subframes. */, nullptr,
- error_html);
- } else {
- custom_error_page_ = true;
- }
+ } else if (error_html) {
+ delegate_->GenerateLocalizedErrorPage(
+ error, is_failed_post,
+ false /* No diagnostics dialogs allowed for subframes. */, nullptr,
+ error_html);
}
}
@@ -336,12 +213,9 @@
error = GetUpdatedError(*pending_error_page_info);
}
if (error_html) {
- custom_error_page_ = false;
pending_error_page_info->page_state = delegate_->GenerateLocalizedErrorPage(
error, pending_error_page_info->was_failed_post,
can_show_network_diagnostics_dialog_, nullptr, error_html);
- } else {
- custom_error_page_ = true;
}
}
@@ -391,110 +265,6 @@
delegate_->ReloadFrame();
}
-bool NetErrorHelperCore::MaybeStartAutoReloadTimer() {
- // Automation tools expect to be in control of reloads.
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableAutomation)) {
- return false;
- }
-
- 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_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::BindOnce(&NetErrorHelperCore::AutoReloadTimerFired,
- base::Unretained(this)));
-}
-
-void NetErrorHelperCore::AutoReloadTimerFired() {
- // AutoReloadTimerFired only runs if:
- // 1. StartAutoReloadTimer was previously called, which requires that
- // committed_error_page_info_ is populated;
- // 2. No other page load has started since (1), since OnStartLoad stops the
- // auto-reload timer.
- DCHECK(committed_error_page_info_);
-
- auto_reload_count_++;
- auto_reload_in_flight_ = true;
- 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,
- int error_code) {
- // Don't suppress child frame errors.
- if (frame_type != MAIN_FRAME)
- return false;
-
- // If there's no auto reload attempt in flight, this error page didn't come
- // from auto reload, so don't suppress it.
- if (!auto_reload_in_flight_)
- return false;
-
- // Even with auto_reload_in_flight_ error page may not come from
- // the auto reload when proceeding from error CERT_AUTHORITY_INVALID
- // to error INVALID_AUTH_CREDENTIALS, so do not suppress the error page
- // for the new error code.
- if (committed_error_page_info_ &&
- committed_error_page_info_->error.reason() != error_code)
- return false;
-
- uncommitted_load_started_ = false;
- // This serves to terminate the auto-reload in flight attempt. If
- // ShouldSuppressErrorPage is called, the auto-reload yielded an error, which
- // means the request was already sent.
- auto_reload_in_flight_ = false;
- MaybeStartAutoReloadTimer();
- return true;
-}
-
#if defined(OS_ANDROID)
void NetErrorHelperCore::SetPageAutoFetcherHelperForTesting(
std::unique_ptr<PageAutoFetcherHelper> page_auto_fetcher_helper) {
diff --git a/chrome/renderer/net/net_error_helper_core.h b/chrome/renderer/net/net_error_helper_core.h
index e9f1d7e..b2befb0 100644
--- a/chrome/renderer/net/net_error_helper_core.h
+++ b/chrome/renderer/net/net_error_helper_core.h
@@ -121,9 +121,7 @@
virtual ~Delegate() {}
};
- NetErrorHelperCore(Delegate* delegate,
- bool auto_reload_enabled,
- bool is_visible);
+ explicit NetErrorHelperCore(Delegate* delegate);
~NetErrorHelperCore();
// Sets values in |pending_error_page_info_|. If |error_html| is not null, it
@@ -136,12 +134,8 @@
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 CancelPendingAutoReload();
@@ -158,29 +152,6 @@
// synced preferences.
void OnEasterEggHighScoreReceived(int high_score);
- // 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,
- int error_code);
-
- void set_timer_for_testing(std::unique_ptr<base::OneShotTimer> timer) {
- auto_reload_timer_ = std::move(timer);
- }
-
#if defined(OS_ANDROID)
void SetPageAutoFetcherHelperForTesting(
std::unique_ptr<PageAutoFetcherHelper> page_auto_fetcher_helper);
@@ -227,14 +198,8 @@
error_page::Error GetUpdatedError(const ErrorPageInfo& error_info) const;
void Reload();
- bool MaybeStartAutoReloadTimer();
- void StartAutoReloadTimer();
- void AutoReloadTimerFired();
- void PauseAutoReloadTimer();
- static bool IsReloadableError(const ErrorPageInfo& info);
-
- Delegate* delegate_;
+ Delegate* const delegate_;
// The last DnsProbeStatus received from the browser.
error_page::DnsProbeStatus last_probe_status_;
@@ -249,45 +214,11 @@
bool can_show_network_diagnostics_dialog_;
- // True if auto-reload is enabled at all.
- const bool auto_reload_enabled_;
-
- // Timer used to wait for auto-reload attempts.
- std::unique_ptr<base::OneShotTimer> 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_;
-
- // Whether an auto-reload-initiated Reload() attempt is in flight.
- bool auto_reload_in_flight_;
-
- // 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_;
- // True if the current error page is displaying custom HTML (e.g.
- // security interstitials).
- bool custom_error_page_;
-
#if defined(OS_ANDROID)
AvailableOfflineContentHelper available_content_helper_;
std::unique_ptr<PageAutoFetcherHelper> page_auto_fetcher_helper_;
diff --git a/chrome/renderer/net/net_error_helper_core_unittest.cc b/chrome/renderer/net/net_error_helper_core_unittest.cc
index 359a67a..e9dbc64 100644
--- a/chrome/renderer/net/net_error_helper_core_unittest.cc
+++ b/chrome/renderer/net/net_error_helper_core_unittest.cc
@@ -21,8 +21,6 @@
#include "base/test/bind_test_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
-#include "base/timer/mock_timer.h"
-#include "base/timer/timer.h"
#include "build/build_config.h"
#include "chrome/common/available_offline_content.mojom.h"
#include "chrome/renderer/net/available_offline_content_helper.h"
@@ -90,30 +88,18 @@
public NetErrorHelperCore::Delegate {
public:
NetErrorHelperCoreTest()
- : timer_(nullptr),
- update_count_(0),
+ : update_count_(0),
reload_count_(0),
diagnose_error_count_(0),
download_count_(0),
list_visible_by_prefs_(true),
enable_page_helper_functions_count_(0),
default_url_(GURL(kFailedUrl)),
- error_url_(GURL(content::kUnreachableWebDataURL)) {
- SetUpCore(false, true);
- }
+ error_url_(GURL(content::kUnreachableWebDataURL)) {}
~NetErrorHelperCoreTest() override = default;
- void SetUpCore(bool auto_reload_enabled,
- 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::MockOneShotTimer();
- core_.reset(new NetErrorHelperCore(this, auto_reload_enabled, visible));
- core_->set_timer_for_testing(base::WrapUnique(timer_));
- }
-
- NetErrorHelperCore* core() { return core_.get(); }
+ NetErrorHelperCore* core() { return &core_; }
int reload_count() const { return reload_count_; }
@@ -170,14 +156,10 @@
}
#endif
- base::MockOneShotTimer* timer() { return timer_; }
-
base::test::TaskEnvironment* task_environment() { return &task_environment_; }
content::MockRenderThread* render_thread() { return &render_thread_; }
void DoErrorLoadOfURL(net::Error error, const GURL& url) {
- core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
- NetErrorHelperCore::NON_ERROR_PAGE);
std::string html;
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetErrorForURL(error, url),
@@ -185,8 +167,6 @@
EXPECT_FALSE(html.empty());
EXPECT_EQ(NetErrorStringForURL(error, url), html);
- core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
- NetErrorHelperCore::ERROR_PAGE);
core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
}
@@ -195,23 +175,7 @@
DoErrorLoadOfURL(error, GURL(kFailedUrl));
}
- void DoErrorLoadWithCustomErrorPage(net::Error error) {
- core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
- NetErrorHelperCore::NON_ERROR_PAGE);
- // Custom error pages pass nullptr to PrepareErrorPage, since they set the
- // error HTML on their own.
- core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
- NetErrorForURL(error, GURL(kFailedUrl)),
- false /* is_failed_post */, nullptr);
- 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);
}
@@ -239,6 +203,8 @@
std::string* html) const override {
last_can_show_network_diagnostics_dialog_ =
can_show_network_diagnostics_dialog;
+
+ CHECK_NE(html, nullptr);
*html = ErrorToString(error, is_failed_post);
return GetPageState();
@@ -290,12 +256,10 @@
content::RenderFrame* GetRenderFrame() override { return nullptr; }
- base::MockOneShotTimer* timer_;
-
base::test::TaskEnvironment task_environment_;
content::MockRenderThread render_thread_;
- std::unique_ptr<NetErrorHelperCore> core_;
+ NetErrorHelperCore core_{this};
// Contains the information passed to the last call to UpdateErrorPage, as a
// string.
@@ -338,19 +302,13 @@
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());
}
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.
+ // An error page is requested.
std::string html;
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_CONNECTION_RESET),
@@ -361,8 +319,6 @@
// 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());
@@ -372,13 +328,9 @@
// 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(error_page::DNS_PROBE_FINISHED_NXDOMAIN);
-
- // It fails, and an error page is requested.
+ // Loading fails, and an error page is requested.
std::string html;
+ core()->OnNetErrorInfo(error_page::DNS_PROBE_FINISHED_NXDOMAIN);
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_CONNECTION_RESET),
false /* is_failed_post */, &html);
@@ -390,8 +342,6 @@
// Error page loads.
- core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
- NetErrorHelperCore::ERROR_PAGE);
core()->OnNetErrorInfo(error_page::DNS_PROBE_FINISHED_NXDOMAIN);
core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
@@ -403,12 +353,19 @@
EXPECT_EQ(0, update_count());
}
-TEST_F(NetErrorHelperCoreTest, SubFrameDnsError) {
- // Original page starts loading.
- core()->OnStartLoad(NetErrorHelperCore::SUB_FRAME,
- NetErrorHelperCore::NON_ERROR_PAGE);
+TEST_F(NetErrorHelperCoreTest, SubFrameErrorWithCustomErrorPage) {
+ // Loading fails, and an error page is requested. |error_html| is null
+ // indicating a custom error page. Calls below should not crash.
+ core()->PrepareErrorPage(
+ NetErrorHelperCore::SUB_FRAME, NetError(net::ERR_NAME_NOT_RESOLVED),
+ false /* is_failed_post */, nullptr /* error_html */);
+ core()->OnCommitLoad(NetErrorHelperCore::SUB_FRAME, error_url());
+ core()->OnFinishLoad(NetErrorHelperCore::SUB_FRAME);
+ EXPECT_EQ(0, update_count());
+}
- // It fails, and an error page is requested.
+TEST_F(NetErrorHelperCoreTest, SubFrameDnsError) {
+ // Loading fails, and an error page is requested.
std::string html;
core()->PrepareErrorPage(NetErrorHelperCore::SUB_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
@@ -417,8 +374,6 @@
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());
@@ -427,13 +382,9 @@
// 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(error_page::DNS_PROBE_FINISHED_NXDOMAIN);
-
- // It fails, and an error page is requested.
+ // Loading fails, and an error page is requested.
std::string html;
+ core()->OnNetErrorInfo(error_page::DNS_PROBE_FINISHED_NXDOMAIN);
core()->PrepareErrorPage(NetErrorHelperCore::SUB_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
false /* is_failed_post */, &html);
@@ -444,8 +395,6 @@
// Error page loads.
- core()->OnStartLoad(NetErrorHelperCore::SUB_FRAME,
- NetErrorHelperCore::ERROR_PAGE);
core()->OnNetErrorInfo(error_page::DNS_PROBE_FINISHED_NXDOMAIN);
core()->OnCommitLoad(NetErrorHelperCore::SUB_FRAME, error_url());
@@ -464,11 +413,7 @@
// 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.
+ // Loading fails, and an error page is requested.
std::string html;
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
@@ -477,8 +422,6 @@
EXPECT_EQ(ProbeErrorString(error_page::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());
@@ -500,11 +443,7 @@
// 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.
+ // Loading fails, and an error page is requested.
std::string html;
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
@@ -513,8 +452,6 @@
EXPECT_EQ(ProbeErrorString(error_page::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());
@@ -533,11 +470,7 @@
// 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.
+ // Loading fails, and an error page is requested.
std::string html;
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
@@ -546,8 +479,6 @@
EXPECT_EQ(ProbeErrorString(error_page::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());
@@ -571,11 +502,7 @@
TEST_F(NetErrorHelperCoreTest, FinishedBeforeProbeNoInternet) {
base::HistogramTester histogram_tester_;
- // Original page starts loading.
- core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
- NetErrorHelperCore::NON_ERROR_PAGE);
-
- // It fails, and an error page is requested.
+ // Loading fails, and an error page is requested.
std::string html;
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
@@ -584,8 +511,6 @@
EXPECT_EQ(ProbeErrorString(error_page::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());
@@ -611,14 +536,10 @@
// Perform a second error page load, and confirm that the previous load
// doesn't affect the result.
- core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
- NetErrorHelperCore::NON_ERROR_PAGE);
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
false /* is_failed_post */, &html);
EXPECT_EQ(ProbeErrorString(error_page::DNS_PROBE_POSSIBLE), html);
- core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
- NetErrorHelperCore::ERROR_PAGE);
core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
core()->OnNetErrorInfo(error_page::DNS_PROBE_STARTED);
@@ -634,11 +555,7 @@
// 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.
+ // Loading fails, and an error page is requested.
std::string html;
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
@@ -647,8 +564,6 @@
EXPECT_EQ(ProbeErrorString(error_page::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());
@@ -672,11 +587,7 @@
// 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.
+ // Loading fails, and an error page is requested.
std::string html;
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
@@ -685,8 +596,6 @@
EXPECT_EQ(ProbeErrorString(error_page::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
@@ -714,11 +623,7 @@
// 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.
+ // Loading fails, and an error page is requested.
std::string html;
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
@@ -729,8 +634,6 @@
// 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());
@@ -750,11 +653,7 @@
// 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.
+ // Loading fails, and an error page is requested.
std::string html;
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
@@ -762,12 +661,8 @@
// Should have returned a local error page indicating a probe may run.
EXPECT_EQ(ProbeErrorString(error_page::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.
+ // Error page starts loading. Nothing should be done when the probe statuses
+ // come in before loading finishes.
core()->OnNetErrorInfo(error_page::DNS_PROBE_STARTED);
core()->OnNetErrorInfo(error_page::DNS_PROBE_FINISHED_NXDOMAIN);
EXPECT_EQ(0, update_count());
@@ -791,11 +686,7 @@
// 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.
+ // Loading fails, and an error page is requested.
std::string html;
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
@@ -804,8 +695,6 @@
EXPECT_EQ(ProbeErrorString(error_page::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);
@@ -818,11 +707,7 @@
// 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.
+ // Loading fails, and an error page is requested.
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
false /* is_failed_post */, &html);
@@ -830,8 +715,6 @@
EXPECT_EQ(ProbeErrorString(error_page::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());
@@ -851,11 +734,7 @@
// 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.
+ // Loading fails, and an error page is requested.
std::string html;
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
@@ -864,29 +743,20 @@
EXPECT_EQ(ProbeErrorString(error_page::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.
+ // Loading fails, and an error page is requested.
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
false /* is_failed_post */, &html);
// Should have returned a local error page indicating a probe may run.
EXPECT_EQ(ProbeErrorString(error_page::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.
+ // Error page starts to load. Probe results come in, and the first page is
+ // updated.
core()->OnNetErrorInfo(error_page::DNS_PROBE_STARTED);
core()->OnNetErrorInfo(error_page::DNS_PROBE_FINISHED_NXDOMAIN);
EXPECT_EQ(2, update_count());
@@ -907,11 +777,7 @@
// 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.
+ // Loading fails, and an error page is requested.
std::string html;
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
@@ -919,19 +785,12 @@
// Should have returned a local error page indicating a probe may run.
EXPECT_EQ(ProbeErrorString(error_page::DNS_PROBE_POSSIBLE), html);
- // Error page starts loading.
- core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
- NetErrorHelperCore::ERROR_PAGE);
- // Probe statuses come in, but should be ignored.
+ // Error page starts loading. Probe statuses come in, but should be ignored.
core()->OnNetErrorInfo(error_page::DNS_PROBE_STARTED);
core()->OnNetErrorInfo(error_page::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.
+ // A new navigation fails while the error page is loading.
core()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
NetError(net::ERR_NAME_NOT_RESOLVED),
false /* is_failed_post */, &html);
@@ -953,404 +812,6 @@
last_error_html());
}
-//------------------------------------------------------------------------------
-// Autoreload tests.
-//------------------------------------------------------------------------------
-
-TEST_F(NetErrorHelperCoreTest, AutoReloadDisabled) {
- DoErrorLoad(net::ERR_CONNECTION_RESET);
-
- EXPECT_FALSE(timer()->IsRunning());
- EXPECT_EQ(0, reload_count());
-}
-
-class NetErrorHelperCoreAutoReloadTest : public NetErrorHelperCoreTest {
- public:
- void SetUp() override {
- NetErrorHelperCoreTest::SetUp();
- SetUpCore(true, 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());
-}
-
-// This is a regression test for http://crbug.com/881208.
-TEST_F(NetErrorHelperCoreAutoReloadTest, StopsOnErrorLoadCommit) {
- DoErrorLoad(net::ERR_CONNECTION_RESET);
- EXPECT_TRUE(timer()->IsRunning());
-
- // Simulate manually reloading the error page while the timer is still
- // running.
- std::string html;
- core()->PrepareErrorPage(
- NetErrorHelperCore::MAIN_FRAME,
- NetErrorForURL(net::ERR_CONNECTION_RESET, GURL(kFailedUrl)),
- false /* is_failed_post */, &html);
-
- core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
- NetErrorHelperCore::ERROR_PAGE);
- EXPECT_FALSE(timer()->IsRunning());
- core()->OnCommitLoad(NetErrorHelperCore::MAIN_FRAME, error_url());
- core()->OnFinishLoad(NetErrorHelperCore::MAIN_FRAME);
- EXPECT_TRUE(timer()->IsRunning());
-}
-
-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(error_page::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()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
- NetError(net::ERR_CONNECTION_RESET),
- false /* is_failed_post */, &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()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
- NetError(net::ERR_CONNECTION_RESET),
- false /* is_failed_post */, &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()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
- NetError(net::ERR_CONNECTION_RESET),
- false /* is_failed_post */, &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()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
- NetError(net::ERR_CONNECTION_RESET),
- false /* is_failed_post */, &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()->PrepareErrorPage(NetErrorHelperCore::MAIN_FRAME,
- NetError(net::ERR_CONNECTION_RESET),
- false /* is_failed_post */, &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), net::ERR_ABORTED));
-
- DoErrorLoad(net::ERR_UNKNOWN_URL_SCHEME);
- EXPECT_FALSE(core()->ShouldSuppressErrorPage(NetErrorHelperCore::MAIN_FRAME,
- GURL(kFailedUrl),
- net::ERR_UNKNOWN_URL_SCHEME));
-}
-
-TEST_F(NetErrorHelperCoreAutoReloadTest,
- ShouldSuppressErrorPageForDifferentError) {
- DoErrorLoad(net::ERR_CERT_AUTHORITY_INVALID);
- EXPECT_FALSE(core()->ShouldSuppressErrorPage(
- NetErrorHelperCore::MAIN_FRAME, GURL(kFailedUrl),
- net::ERR_INVALID_AUTH_CREDENTIALS));
-}
-
-TEST_F(NetErrorHelperCoreAutoReloadTest, DoesNotReload) {
- DoErrorLoad(net::ERR_ABORTED);
- EXPECT_FALSE(timer()->IsRunning());
-
- DoErrorLoad(net::ERR_UNKNOWN_URL_SCHEME);
- EXPECT_FALSE(timer()->IsRunning());
-
- DoErrorLoad(net::ERR_SSL_PROTOCOL_ERROR);
- EXPECT_FALSE(timer()->IsRunning());
-
- DoErrorLoad(net::ERR_BLOCKED_BY_ADMINISTRATOR);
- EXPECT_FALSE(timer()->IsRunning());
-
- DoErrorLoad(net::ERR_BAD_SSL_CLIENT_AUTH_CERT);
- EXPECT_FALSE(timer()->IsRunning());
-
- DoErrorLoadOfURL(net::ERR_ACCESS_DENIED, GURL("data://some-data-here"));
- EXPECT_FALSE(timer()->IsRunning());
-
- DoErrorLoadOfURL(net::ERR_ACCESS_DENIED, GURL("chrome-extension://foo"));
- EXPECT_FALSE(timer()->IsRunning());
-}
-
-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();
-
- // Sub-frame load.
- EXPECT_FALSE(core()->ShouldSuppressErrorPage(NetErrorHelperCore::SUB_FRAME,
- GURL(kFailedUrl),
- net::ERR_CONNECTION_RESET));
- EXPECT_TRUE(core()->ShouldSuppressErrorPage(NetErrorHelperCore::MAIN_FRAME,
- GURL(kFailedUrl),
- net::ERR_CONNECTION_RESET));
- // No auto-reload attempt in flight.
- EXPECT_FALSE(core()->ShouldSuppressErrorPage(NetErrorHelperCore::MAIN_FRAME,
- GURL(kFailedUrl),
- net::ERR_CONNECTION_RESET));
-}
-
-TEST_F(NetErrorHelperCoreAutoReloadTest, HiddenAndShown) {
- SetUpCore(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);
- 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, false);
- DoErrorLoad(net::ERR_CONNECTION_RESET);
- EXPECT_FALSE(timer()->IsRunning());
- core()->OnWasShown();
- EXPECT_TRUE(timer()->IsRunning());
-}
-
-TEST_F(NetErrorHelperCoreAutoReloadTest, ManualReloadShowsError) {
- SetUpCore(true, true);
- DoErrorLoad(net::ERR_CONNECTION_RESET);
- core()->OnStartLoad(NetErrorHelperCore::MAIN_FRAME,
- NetErrorHelperCore::ERROR_PAGE);
- EXPECT_FALSE(core()->ShouldSuppressErrorPage(NetErrorHelperCore::MAIN_FRAME,
- GURL(kFailedUrl),
- net::ERR_CONNECTION_RESET));
-}
-
-TEST_F(NetErrorHelperCoreAutoReloadTest,
- AutoReloadDisabledForCustomErrorPages) {
- // This error code would normally trigger auto-reloads, but shouldn't if we
- // are showing a custom error page (e.g. an interstitial.).
- DoErrorLoadWithCustomErrorPage(net::ERR_CONNECTION_RESET);
-
- EXPECT_FALSE(timer()->IsRunning());
- EXPECT_EQ(0, reload_count());
-}
-
-TEST_F(NetErrorHelperCoreAutoReloadTest,
- AutoReloadEnabledAfterCustomErrorPage) {
- DoErrorLoadWithCustomErrorPage(net::ERR_CONNECTION_RESET);
- EXPECT_FALSE(timer()->IsRunning());
- EXPECT_EQ(0, reload_count());
- DoErrorLoad(net::ERR_CONNECTION_RESET);
- EXPECT_TRUE(timer()->IsRunning());
-}
-
TEST_F(NetErrorHelperCoreTest, ExplicitReloadSucceeds) {
DoErrorLoad(net::ERR_CONNECTION_RESET);
EXPECT_EQ(0, reload_count());
diff --git a/components/BUILD.gn b/components/BUILD.gn
index fa79211..23f86db 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -592,6 +592,7 @@
"//components/dom_distiller/content/browser",
"//components/dom_distiller/core",
"//components/dom_distiller/core:test_support",
+ "//components/error_page/content/browser:browser_tests",
"//components/metrics:content",
"//components/offline_pages/content/renovations",
"//components/offline_pages/core/renovations",
diff --git a/components/error_page/content/browser/BUILD.gn b/components/error_page/content/browser/BUILD.gn
new file mode 100644
index 0000000..f39ebf6
--- /dev/null
+++ b/components/error_page/content/browser/BUILD.gn
@@ -0,0 +1,43 @@
+# Copyright 2020 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("browser") {
+ public = [ "net_error_auto_reloader.h" ]
+
+ sources = [ "net_error_auto_reloader.cc" ]
+
+ public_deps = [
+ "//base",
+ "//content/public/browser",
+ "//services/network/public/cpp",
+ "//services/network/public/mojom",
+ ]
+
+ deps = [
+ "//net",
+ "//url",
+ ]
+}
+
+source_set("browser_tests") {
+ testonly = true
+
+ defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+
+ sources = [ "net_error_auto_reloader_browsertest.cc" ]
+
+ deps = [
+ ":browser",
+ "//base",
+ "//base/test:test_support",
+ "//content/public/browser",
+ "//content/shell:content_shell_lib",
+ "//content/test:browsertest_support",
+ "//content/test:test_support",
+ "//net",
+ "//services/network:test_support",
+ "//testing/gtest",
+ "//url",
+ ]
+}
diff --git a/components/error_page/content/browser/DEPS b/components/error_page/content/browser/DEPS
new file mode 100644
index 0000000..3e18f370
--- /dev/null
+++ b/components/error_page/content/browser/DEPS
@@ -0,0 +1,14 @@
+include_rules = [
+ "+content/public/browser",
+ "+net/base",
+ "+services/network/public",
+]
+
+specific_include_rules = {
+ ".*_browsertest\.cc": [
+ "+content/public/test",
+ "+content/shell/browser/shell.h",
+ "+content/shell/browser/shell_content_browser_client.h",
+ "+services/network/test",
+ ]
+}
diff --git a/components/error_page/content/browser/net_error_auto_reloader.cc b/components/error_page/content/browser/net_error_auto_reloader.cc
new file mode 100644
index 0000000..6699bbb
--- /dev/null
+++ b/components/error_page/content/browser/net_error_auto_reloader.cc
@@ -0,0 +1,318 @@
+// Copyright 2020 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/content/browser/net_error_auto_reloader.h"
+
+#include <algorithm>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle.h"
+#include "content/public/browser/network_service_instance.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/net_errors.h"
+#include "url/gurl.h"
+
+namespace error_page {
+
+namespace {
+
+bool ShouldAutoReload(content::NavigationHandle* handle) {
+ DCHECK(handle->HasCommitted());
+ const int net_error = handle->GetNetErrorCode();
+ return handle->IsErrorPage() && !handle->IsCustomErrorPage() &&
+ net_error != net::OK && !handle->IsPost() &&
+ // For now, net::ERR_UNKNOWN_URL_SCHEME is only being displayed on
+ // Chrome for Android.
+ net_error != net::ERR_UNKNOWN_URL_SCHEME &&
+ // Do not trigger if the server rejects a client certificate.
+ // https://crbug.com/431387
+ !net::IsClientCertificateError(net_error) &&
+ // Some servers reject client certificates with a generic
+ // handshake_failure alert.
+ // https://crbug.com/431387
+ net_error != net::ERR_SSL_PROTOCOL_ERROR &&
+ // Do not trigger for blocklisted URLs.
+ // https://crbug.com/803839
+ // Do not trigger for requests that were blocked by the browser itself.
+ !net::IsRequestBlockedError(net_error) &&
+ // Do not trigger for this error code because it is used by Chrome
+ // while an auth prompt is being displayed.
+ net_error != net::ERR_INVALID_AUTH_CREDENTIALS &&
+ // Don't auto-reload non-http/https schemas.
+ // https://crbug.com/471713
+ handle->GetURL().SchemeIsHTTPOrHTTPS() &&
+ // Don't auto reload if the error was a secure DNS network error, since
+ // the reload may interfere with the captive portal probe state.
+ // TODO(crbug.com/1016164): Explore how to allow reloads for secure DNS
+ // network errors without interfering with the captive portal probe
+ // state.
+ !handle->GetResolveErrorInfo().is_secure_network_error;
+}
+
+base::TimeDelta GetNextReloadDelay(size_t reload_count) {
+ static const int kDelaysMs[] = {1000, 5000, 30000, 60000,
+ 300000, 600000, 1800000};
+ return base::TimeDelta::FromMilliseconds(
+ kDelaysMs[std::min(reload_count, base::size(kDelaysMs) - 1)]);
+}
+
+// Helper to block a navigation that would result in re-committing the same
+// error page a tab is already displaying.
+class IgnoreDuplicateErrorThrottle : public content::NavigationThrottle {
+ public:
+ using ShouldSuppressCallback =
+ base::OnceCallback<bool(content::NavigationHandle*)>;
+
+ IgnoreDuplicateErrorThrottle(content::NavigationHandle* handle,
+ ShouldSuppressCallback should_suppress)
+ : content::NavigationThrottle(handle),
+ should_suppress_(std::move(should_suppress)) {
+ DCHECK(should_suppress_);
+ }
+ IgnoreDuplicateErrorThrottle(const IgnoreDuplicateErrorThrottle&) = delete;
+ IgnoreDuplicateErrorThrottle& operator=(const IgnoreDuplicateErrorThrottle&) =
+ delete;
+ ~IgnoreDuplicateErrorThrottle() override = default;
+
+ // content::NavigationThrottle:
+ content::NavigationThrottle::ThrottleCheckResult WillFailRequest() override {
+ DCHECK(should_suppress_);
+ if (std::move(should_suppress_).Run(navigation_handle()))
+ return content::NavigationThrottle::ThrottleAction::CANCEL;
+ return content::NavigationThrottle::ThrottleAction::PROCEED;
+ }
+
+ const char* GetNameForLogging() override {
+ return "IgnoreDuplicateErrorThrottle";
+ }
+
+ private:
+ ShouldSuppressCallback should_suppress_;
+};
+
+} // namespace
+
+NetErrorAutoReloader::ErrorPageInfo::ErrorPageInfo(const GURL& url,
+ net::Error error)
+ : url(url), error(error) {}
+
+NetErrorAutoReloader::ErrorPageInfo::~ErrorPageInfo() = default;
+
+NetErrorAutoReloader::NetErrorAutoReloader(content::WebContents* web_contents)
+ : content::WebContentsObserver(web_contents),
+ connection_tracker_(content::GetNetworkConnectionTracker()) {
+ connection_tracker_->AddNetworkConnectionObserver(this);
+
+ network::mojom::ConnectionType connection_type;
+ if (connection_tracker_->GetConnectionType(
+ &connection_type,
+ base::BindOnce(&NetErrorAutoReloader::SetInitialConnectionType,
+ weak_ptr_factory_.GetWeakPtr()))) {
+ SetInitialConnectionType(connection_type);
+ }
+}
+
+NetErrorAutoReloader::~NetErrorAutoReloader() {
+ // NOTE: Tests may call `DisableConnectionChangeObservationForTesting` to null
+ // this out.
+ if (connection_tracker_)
+ connection_tracker_->RemoveNetworkConnectionObserver(this);
+}
+
+// static
+std::unique_ptr<content::NavigationThrottle>
+NetErrorAutoReloader::MaybeCreateThrottleFor(
+ content::NavigationHandle* handle) {
+ // Note that `CreateForWebContents` is a no-op if `contents` already has a
+ // NetErrorAutoReloader. See WebContentsUserData.
+ content::WebContents* contents = handle->GetWebContents();
+ CreateForWebContents(contents);
+ return FromWebContents(contents)->MaybeCreateThrottle(handle);
+}
+
+void NetErrorAutoReloader::DidStartNavigation(
+ content::NavigationHandle* handle) {
+ if (!handle->IsInMainFrame())
+ return;
+
+ // Suppress automatic reload as long as any navigations are pending.
+ PauseAutoReloadTimerIfRunning();
+ pending_navigations_.insert(handle);
+}
+
+void NetErrorAutoReloader::DidFinishNavigation(
+ content::NavigationHandle* handle) {
+ if (!handle->IsInMainFrame())
+ return;
+
+ pending_navigations_.erase(handle);
+ if (!handle->HasCommitted()) {
+ // This navigation was cancelled and not committed. If there are still other
+ // pending navigations, or we aren't sitting on a error page which allows
+ // auto-reload, there's nothing to do.
+ if (!pending_navigations_.empty() || !current_reloadable_error_page_info_)
+ return;
+
+ // The last pending navigation was just cancelled and we're sitting on an
+ // error page which allows auto-reload. Schedule the next auto-reload
+ // attempt.
+ is_auto_reload_in_progress_ = false;
+ ScheduleNextAutoReload();
+ return;
+ }
+
+ if (!ShouldAutoReload(handle)) {
+ // We've committed something that doesn't support auto-reload. Reset
+ // all auto-reload state so nothing interesting happens until another
+ // error page navigation is committed.
+ Reset();
+ return;
+ }
+
+ // This heuristic isn't perfect but it should be good enough: if the new
+ // commit is not a reload, or if it's an error page with an error code
+ // different from what we had previously committed, we treat it as a new
+ // error and thus reset our tracking state.
+ net::Error net_error = handle->GetNetErrorCode();
+ if (handle->GetReloadType() == content::ReloadType::NONE ||
+ !current_reloadable_error_page_info_ ||
+ net_error != current_reloadable_error_page_info_->error) {
+ Reset();
+ current_reloadable_error_page_info_ =
+ ErrorPageInfo(handle->GetURL(), net_error);
+ }
+
+ // We only schedule a reload if there are no other pending navigations.
+ // If there are and they end up getting terminated without a commit, we
+ // will schedule the next auto-reload at that time.
+ if (pending_navigations_.empty())
+ ScheduleNextAutoReload();
+}
+
+void NetErrorAutoReloader::NavigationStopped() {
+ // Stopping navigation or loading cancels all pending auto-reload behavior
+ // until the next time a new error page is committed. Note that a stop during
+ // navigation will also result in a DidFinishNavigation with a failed
+ // navigation and an error code of ERR_ABORTED. However stops can also occur
+ // after an error page commits but before it finishes loading, and we want to
+ // catch those cases too.
+ Reset();
+}
+
+void NetErrorAutoReloader::OnVisibilityChanged(content::Visibility visibility) {
+ if (!IsWebContentsVisible()) {
+ PauseAutoReloadTimerIfRunning();
+ } else if (pending_navigations_.empty()) {
+ ResumeAutoReloadIfPaused();
+ }
+}
+
+void NetErrorAutoReloader::OnConnectionChanged(
+ network::mojom::ConnectionType type) {
+ is_online_ = (type != network::mojom::ConnectionType::CONNECTION_NONE);
+ if (!is_online_) {
+ PauseAutoReloadTimerIfRunning();
+ } else if (pending_navigations_.empty()) {
+ ResumeAutoReloadIfPaused();
+ }
+}
+
+// static
+base::TimeDelta NetErrorAutoReloader::GetNextReloadDelayForTesting(
+ size_t reload_count) {
+ return GetNextReloadDelay(reload_count);
+}
+
+void NetErrorAutoReloader::DisableConnectionChangeObservationForTesting() {
+ if (connection_tracker_) {
+ connection_tracker_->RemoveNetworkConnectionObserver(this);
+ connection_tracker_ = nullptr;
+ }
+}
+
+void NetErrorAutoReloader::SetInitialConnectionType(
+ network::mojom::ConnectionType type) {
+ // NOTE: Tests may call `DisableConnectionChangeObservationForTesting` to null
+ // this out.
+ if (connection_tracker_)
+ OnConnectionChanged(type);
+}
+
+bool NetErrorAutoReloader::IsWebContentsVisible() {
+ return web_contents()->GetVisibility() != content::Visibility::HIDDEN;
+}
+
+void NetErrorAutoReloader::Reset() {
+ next_reload_timer_.reset();
+ num_reloads_for_current_error_ = 0;
+ is_auto_reload_in_progress_ = false;
+ current_reloadable_error_page_info_.reset();
+}
+
+void NetErrorAutoReloader::PauseAutoReloadTimerIfRunning() {
+ next_reload_timer_.reset();
+}
+
+void NetErrorAutoReloader::ResumeAutoReloadIfPaused() {
+ if (current_reloadable_error_page_info_ && !next_reload_timer_)
+ ScheduleNextAutoReload();
+}
+
+void NetErrorAutoReloader::ScheduleNextAutoReload() {
+ DCHECK(current_reloadable_error_page_info_);
+ if (!is_online_ || !IsWebContentsVisible())
+ return;
+
+ // Note that Unretained is safe here because base::OneShotTimer will never
+ // run its callback once destructed.
+ next_reload_timer_.emplace();
+ next_reload_timer_->Start(
+ FROM_HERE, GetNextReloadDelay(num_reloads_for_current_error_),
+ base::BindOnce(&NetErrorAutoReloader::ReloadMainFrame,
+ base::Unretained(this)));
+}
+
+void NetErrorAutoReloader::ReloadMainFrame() {
+ DCHECK(current_reloadable_error_page_info_);
+ if (!is_online_ || !IsWebContentsVisible())
+ return;
+
+ ++num_reloads_for_current_error_;
+ is_auto_reload_in_progress_ = true;
+ web_contents()->GetMainFrame()->Reload();
+}
+
+std::unique_ptr<content::NavigationThrottle>
+NetErrorAutoReloader::MaybeCreateThrottle(content::NavigationHandle* handle) {
+ DCHECK(handle->IsInMainFrame());
+ if (!current_reloadable_error_page_info_ ||
+ current_reloadable_error_page_info_->url != handle->GetURL() ||
+ !is_auto_reload_in_progress_) {
+ return nullptr;
+ }
+
+ return std::make_unique<IgnoreDuplicateErrorThrottle>(
+ handle, base::BindOnce(&NetErrorAutoReloader::ShouldSuppressErrorPage,
+ base::Unretained(this)));
+}
+
+bool NetErrorAutoReloader::ShouldSuppressErrorPage(
+ content::NavigationHandle* handle) {
+ // We already verified these conditions when the throttle was created, but now
+ // that the throttle is about to fail its navigation, we double-check in case
+ // another navigation has committed in the interim.
+ if (!current_reloadable_error_page_info_ ||
+ current_reloadable_error_page_info_->url != handle->GetURL() ||
+ current_reloadable_error_page_info_->error != handle->GetNetErrorCode()) {
+ return false;
+ }
+
+ return true;
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(NetErrorAutoReloader)
+
+} // namespace error_page
diff --git a/components/error_page/content/browser/net_error_auto_reloader.h b/components/error_page/content/browser/net_error_auto_reloader.h
new file mode 100644
index 0000000..f7ee52a8
--- /dev/null
+++ b/components/error_page/content/browser/net_error_auto_reloader.h
@@ -0,0 +1,115 @@
+// Copyright 2020 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_CONTENT_BROWSER_NET_ERROR_AUTO_RELOADER_H_
+#define COMPONENTS_ERROR_PAGE_CONTENT_BROWSER_NET_ERROR_AUTO_RELOADER_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <set>
+
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "net/base/net_errors.h"
+#include "services/network/public/cpp/network_connection_tracker.h"
+#include "services/network/public/mojom/network_change_manager.mojom.h"
+#include "url/gurl.h"
+
+namespace content {
+class NavigationHandle;
+class NavigationThrottle;
+class WebContents;
+} // namespace content
+
+namespace error_page {
+
+// This class implements support for automatic reload attempts with backoff
+// whenever a WebContents' main frame lands on common network error pages. To
+// use this behavior as a Content embedder, simply call the static
+// `MaybeCreateNavigationThrottle()` method from within your implementation of
+// ContentBrowserClient::CreateThrottlesForNavigation.
+class NetErrorAutoReloader
+ : public content::WebContentsObserver,
+ public content::WebContentsUserData<NetErrorAutoReloader>,
+ public network::NetworkConnectionTracker::NetworkConnectionObserver {
+ public:
+ NetErrorAutoReloader(const NetErrorAutoReloader&) = delete;
+ NetErrorAutoReloader& operator=(const NetErrorAutoReloader&) = delete;
+ ~NetErrorAutoReloader() override;
+
+ // Maybe installs a throttle for the given navigation, lazily initializing the
+ // appropriate WebContents' NetErrorAutoReloader instance if necessary. For
+ // embedders wanting to use NetErrorAutoReload's behavior, it's sufficient to
+ // call this from ContentBrowserClient::CreateThrottlesForNavigation for each
+ // navigation processed.
+ static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
+ content::NavigationHandle* handle);
+
+ // content::WebContentsObserver:
+ void DidStartNavigation(content::NavigationHandle* handle) override;
+ void DidFinishNavigation(content::NavigationHandle* handle) override;
+ void NavigationStopped() override;
+ void OnVisibilityChanged(content::Visibility visibility) override;
+
+ // network::NetworkConnectionTracker::NetworkConnectionObserver:
+ void OnConnectionChanged(network::mojom::ConnectionType type) override;
+
+ // Returns the delay applied when scheduling the next auto-reload of a page
+ // after it's already been auto-reloaded `reload_count` times.
+ static base::TimeDelta GetNextReloadDelayForTesting(size_t reload_count);
+
+ // Permanently unsubscribes this object from receiving OnConnectionChanged
+ // notifications. Used in tests which want to drive this behavior explicitly.
+ void DisableConnectionChangeObservationForTesting();
+
+ // Returns the timer used internally to schedule the next auto-reload task,
+ // or null if no auto-reload task is currently scheduled.
+ base::Optional<base::OneShotTimer>& next_reload_timer_for_testing() {
+ return next_reload_timer_;
+ }
+
+ private:
+ friend class content::WebContentsUserData<NetErrorAutoReloader>;
+
+ explicit NetErrorAutoReloader(content::WebContents* web_contents);
+
+ void SetInitialConnectionType(network::mojom::ConnectionType type);
+ bool IsWebContentsVisible();
+ void Reset();
+ void PauseAutoReloadTimerIfRunning();
+ void ResumeAutoReloadIfPaused();
+ void ScheduleNextAutoReload();
+ void ReloadMainFrame();
+ std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottle(
+ content::NavigationHandle* handle);
+ bool ShouldSuppressErrorPage(content::NavigationHandle* handle);
+
+ struct ErrorPageInfo {
+ ErrorPageInfo(const GURL& url, net::Error error);
+ ~ErrorPageInfo();
+
+ GURL url;
+ net::Error error;
+ };
+
+ network::NetworkConnectionTracker* connection_tracker_;
+ bool is_online_ = true;
+ std::set<content::NavigationHandle*> pending_navigations_;
+ base::Optional<base::OneShotTimer> next_reload_timer_;
+ base::Optional<ErrorPageInfo> current_reloadable_error_page_info_;
+ size_t num_reloads_for_current_error_ = 0;
+ bool is_auto_reload_in_progress_ = false;
+ base::WeakPtrFactory<NetErrorAutoReloader> weak_ptr_factory_{this};
+
+ WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+} // namespace error_page
+
+#endif // COMPONENTS_ERROR_PAGE_CONTENT_BROWSER_NET_ERROR_AUTO_RELOADER_H_
diff --git a/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc b/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc
new file mode 100644
index 0000000..7925d93
--- /dev/null
+++ b/components/error_page/content/browser/net_error_auto_reloader_browsertest.cc
@@ -0,0 +1,538 @@
+// Copyright 2020 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/content/browser/net_error_auto_reloader.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/optional.h"
+#include "base/test/bind_test_util.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/test_navigation_throttle.h"
+#include "content/public/test/test_navigation_throttle_inserter.h"
+#include "content/public/test/url_loader_interceptor.h"
+#include "content/shell/browser/shell.h"
+#include "content/shell/browser/shell_content_browser_client.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace error_page {
+namespace {
+
+// Helper which intercepts all requests for a given URL and terminates them with
+// a given net error code. Interception affects most browser requests globally
+// (tests here are concerned only with main-frame navigation requests, which are
+// covered) and persists from construction time until destruction time.
+class NetErrorUrlInterceptor {
+ public:
+ NetErrorUrlInterceptor(GURL url, net::Error error)
+ : url_(std::move(url)),
+ interceptor_(base::BindLambdaForTesting(
+ [this,
+ error](content::URLLoaderInterceptor::RequestParams* params) {
+ if (params->url_request.url != url_)
+ return false;
+ network::URLLoaderCompletionStatus status;
+ status.error_code = error;
+ params->client->OnComplete(status);
+ return true;
+ })) {}
+ ~NetErrorUrlInterceptor() = default;
+
+ private:
+ const GURL url_;
+ const content::URLLoaderInterceptor interceptor_;
+};
+
+// Helper to intercept all navigations with a failure using custom error page
+// contents. As long as an instance of this class exists, navigations will land
+// on its custom error page.
+class CustomErrorPageThrottleInserter {
+ public:
+ CustomErrorPageThrottleInserter(content::WebContents* web_contents,
+ net::Error error,
+ std::string error_page_contents)
+ : throttle_inserter_(
+ web_contents,
+ base::BindLambdaForTesting(
+ [error, error_page_contents](content::NavigationHandle* handle)
+ -> std::unique_ptr<content::NavigationThrottle> {
+ auto throttle =
+ std::make_unique<content::TestNavigationThrottle>(handle);
+ throttle->SetResponse(
+ content::TestNavigationThrottle::WILL_START_REQUEST,
+ content::TestNavigationThrottle::SYNCHRONOUS,
+ content::NavigationThrottle::ThrottleCheckResult(
+ content::NavigationThrottle::CANCEL, error,
+ error_page_contents));
+ return throttle;
+ })) {}
+ ~CustomErrorPageThrottleInserter() = default;
+
+ private:
+ const content::TestNavigationThrottleInserter throttle_inserter_;
+};
+
+// Helper to intercept and defer the first navigation initiated after
+// construction. Allows a test to wait for both request start and deferral, as
+// well as request completion after cancellation.
+class DeferNextNavigationThrottleInserter
+ : public content::WebContentsObserver {
+ public:
+ class DeferringThrottle : public content::NavigationThrottle {
+ public:
+ explicit DeferringThrottle(content::NavigationHandle* handle,
+ base::OnceClosure callback)
+ : NavigationThrottle(handle), callback_(std::move(callback)) {}
+
+ ~DeferringThrottle() override = default;
+
+ void Cancel() { CancelDeferredNavigation(CANCEL); }
+
+ // content::NavigationThrottle:
+ ThrottleCheckResult WillStartRequest() override {
+ std::move(callback_).Run();
+ return DEFER;
+ }
+ const char* GetNameForLogging() override { return "DeferringThrottle"; }
+
+ private:
+ base::OnceClosure callback_;
+ };
+
+ explicit DeferNextNavigationThrottleInserter(
+ content::WebContents* web_contents)
+ : content::WebContentsObserver(web_contents),
+ throttle_inserter_(
+ web_contents,
+ base::BindRepeating(
+ &DeferNextNavigationThrottleInserter::MaybeCreateThrottle,
+ base::Unretained(this))) {}
+ ~DeferNextNavigationThrottleInserter() override = default;
+
+ void WaitForNextNavigationToBeDeferred() { defer_wait_loop_.Run(); }
+
+ void CancelAndWaitForNavigationToFinish() {
+ throttle_->Cancel();
+ finish_wait_loop_.Run();
+ }
+
+ // content::WebContentsObserver:
+ void DidFinishNavigation(content::NavigationHandle* handle) override {
+ DCHECK(throttle_);
+ if (handle == throttle_->navigation_handle())
+ finish_wait_loop_.Quit();
+ }
+
+ private:
+ std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottle(
+ content::NavigationHandle* handle) {
+ if (throttle_)
+ return nullptr;
+
+ auto throttle = std::make_unique<DeferringThrottle>(
+ handle, defer_wait_loop_.QuitClosure());
+ throttle_ = throttle.get();
+ return throttle;
+ }
+
+ const content::TestNavigationThrottleInserter throttle_inserter_;
+ DeferringThrottle* throttle_ = nullptr;
+ base::RunLoop defer_wait_loop_;
+ base::RunLoop finish_wait_loop_;
+};
+
+base::TimeDelta GetDelayForReloadCount(size_t count) {
+ return NetErrorAutoReloader::GetNextReloadDelayForTesting(count);
+}
+
+class NetErrorAutoReloaderBrowserTest : public content::ContentBrowserTest {
+ public:
+ void SetUpOnMainThread() override {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ error_page::NetErrorAutoReloader::CreateForWebContents(
+ shell()->web_contents());
+
+ // Start online by default in all tests.
+ GetAutoReloader()->DisableConnectionChangeObservationForTesting();
+ GetAutoReloader()->OnConnectionChanged(
+ network::mojom::ConnectionType::CONNECTION_WIFI);
+
+ content::ShellContentBrowserClient::Get()
+ ->set_create_throttles_for_navigation_callback(base::BindRepeating(
+ [](content::NavigationHandle* handle)
+ -> std::vector<std::unique_ptr<content::NavigationThrottle>> {
+ std::vector<std::unique_ptr<content::NavigationThrottle>>
+ throttles;
+ auto throttle =
+ NetErrorAutoReloader::MaybeCreateThrottleFor(handle);
+ if (throttle)
+ throttles.push_back(std::move(throttle));
+ return throttles;
+ }));
+ }
+
+ NetErrorAutoReloader* GetAutoReloader() {
+ return error_page::NetErrorAutoReloader::FromWebContents(
+ shell()->web_contents());
+ }
+
+ // Returns the time-delay of the currently scheduled auto-reload task, if one
+ // is scheduled. If no auto-reload is scheduled, this returns null.
+ base::Optional<base::TimeDelta> GetCurrentAutoReloadDelay() {
+ const base::Optional<base::OneShotTimer>& timer =
+ GetAutoReloader()->next_reload_timer_for_testing();
+ if (!timer)
+ return base::nullopt;
+ return timer->GetCurrentDelay();
+ }
+
+ // Forces the currently scheduled auto-reload task to execute immediately.
+ // This allows tests to force normal forward-progress of the delayed reload
+ // logic without waiting a long time or painfully messing around with mock
+ // time in browser tests.
+ void ForceScheduledAutoReloadNow() {
+ base::Optional<base::OneShotTimer>& timer =
+ GetAutoReloader()->next_reload_timer_for_testing();
+ if (timer && timer->IsRunning())
+ timer->FireNow();
+ }
+
+ GURL GetTestUrl() { return embedded_test_server()->GetURL("/empty.html"); }
+
+ // Helper used by all tests to perform navigations, whether successful or
+ // intercepted for simulated failure. Note that this asynchronously initiates
+ // the navigation and then waits only for the *navigation* to finish; this is
+ // in contrast to common test utilities which wait for loading to finish. It
+ // matters because most of NetErrorAutoReloader's interesting behavior is
+ // triggered at navigation completion and tests may want to observe the
+ // immediate side effects, such as the scheduling of an auto-reload timer.
+ //
+ // Return true if the navigation was successful, or false if it failed.
+ bool NavigateMainFrame(const GURL& url) WARN_UNUSED_RESULT {
+ content::TestNavigationManager navigation(shell()->web_contents(), url);
+ shell()->web_contents()->GetController().LoadURL(
+ url, content::Referrer(), ui::PAGE_TRANSITION_TYPED,
+ /*extra_headers=*/std::string());
+ navigation.WaitForNavigationFinished();
+ return navigation.was_successful();
+ }
+
+ void SimulateNetworkGoingOnline() {
+ GetAutoReloader()->OnConnectionChanged(
+ network::mojom::ConnectionType::CONNECTION_WIFI);
+ }
+
+ void SimulateNetworkGoingOffline() {
+ GetAutoReloader()->OnConnectionChanged(
+ network::mojom::ConnectionType::CONNECTION_NONE);
+ }
+};
+
+// A successful navigation results in no auto-reload being scheduled.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest, NoError) {
+ EXPECT_TRUE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+}
+
+// A normal error page triggers a scheduled reload.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest, ErrorSchedulesReload) {
+ NetErrorUrlInterceptor interceptor(GetTestUrl(), net::ERR_CONNECTION_RESET);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(GetDelayForReloadCount(0), GetCurrentAutoReloadDelay());
+}
+
+// A successful auto-reload operation will behave like any successful navigation
+// and not schedule subsequent reloads.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest, ErrorRecovery) {
+ auto interceptor = std::make_unique<NetErrorUrlInterceptor>(
+ GetTestUrl(), net::ERR_CONNECTION_RESET);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(GetDelayForReloadCount(0), GetCurrentAutoReloadDelay());
+ interceptor.reset();
+
+ // Force the scheduled auto-reload once interception is cancelled, and observe
+ // a successful navigation.
+ content::TestNavigationManager navigation(shell()->web_contents(),
+ GetTestUrl());
+ ForceScheduledAutoReloadNow();
+ navigation.WaitForNavigationFinished();
+ EXPECT_TRUE(navigation.was_successful());
+
+ // No new auto-reload scheduled.
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+}
+
+// An auto-reload that fails in the same way as the original navigation will
+// result in another reload being scheduled with an increased delay.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest, ReloadDelayBackoff) {
+ auto interceptor = std::make_unique<NetErrorUrlInterceptor>(
+ GetTestUrl(), net::ERR_CONNECTION_RESET);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(GetDelayForReloadCount(0), GetCurrentAutoReloadDelay());
+
+ // Force the scheduled auto-reload to run while still intercepting the
+ // navigation request and forcing it to fail with the same error. Observe that
+ // the navigation fails again and that a new auto-reload task has been
+ // scheduled with an appropriately increased delay. Note that these auto-
+ // reload navigations are also expected not to commit, since we suppress
+ // committing a new error page navigation when it corresponds to the same
+ // error code as the currently committed error page.
+ {
+ content::TestNavigationManager navigation(shell()->web_contents(),
+ GetTestUrl());
+ ForceScheduledAutoReloadNow();
+ navigation.WaitForNavigationFinished();
+ EXPECT_FALSE(navigation.was_committed());
+ EXPECT_EQ(GetDelayForReloadCount(1), GetCurrentAutoReloadDelay());
+ }
+
+ // Do that one more time, for good measure. Note that we expect the scheduled
+ // auto-reload delay to change again here, still with no new commit.
+ {
+ content::TestNavigationManager navigation(shell()->web_contents(),
+ GetTestUrl());
+ ForceScheduledAutoReloadNow();
+ navigation.WaitForNavigationFinished();
+ EXPECT_FALSE(navigation.was_committed());
+ EXPECT_EQ(GetDelayForReloadCount(2), GetCurrentAutoReloadDelay());
+ interceptor.reset();
+ }
+
+ // Finally, let the next reload succeed and verify successful navigation with
+ // no subsequent auto-reload scheduling.
+ {
+ content::TestNavigationManager navigation(shell()->web_contents(),
+ GetTestUrl());
+ ForceScheduledAutoReloadNow();
+ navigation.WaitForNavigationFinished();
+ EXPECT_TRUE(navigation.was_successful());
+ }
+}
+
+// If an auto-reload results in a different network error, it's treated as a new
+// navigation and the auto-reload delay backoff is reset.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest,
+ ResetOnAutoReloadWithNewError) {
+ {
+ NetErrorUrlInterceptor interceptor(GetTestUrl(), net::ERR_CONNECTION_RESET);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(GetDelayForReloadCount(0), GetCurrentAutoReloadDelay());
+ }
+
+ // Force the scheduled auto-reload to run while still intercepting the
+ // navigation request and forcing it to fail with a new error. Observe that
+ // the navigation fails again but that the new auto-reload task has been
+ // scheduled as if it was the first failure, since the error code is
+ // different.
+ {
+ NetErrorUrlInterceptor interceptor(GetTestUrl(), net::ERR_ACCESS_DENIED);
+ content::TestNavigationManager navigation(shell()->web_contents(),
+ GetTestUrl());
+ ForceScheduledAutoReloadNow();
+ navigation.WaitForNavigationFinished();
+ EXPECT_TRUE(navigation.was_committed());
+ EXPECT_FALSE(navigation.was_successful());
+ EXPECT_EQ(GetDelayForReloadCount(0), GetCurrentAutoReloadDelay());
+ }
+}
+
+// An explicitly stopped navigation from an error page does not trigger
+// auto-reload to restart.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest, StopCancelsAutoReload) {
+ NetErrorUrlInterceptor interceptor(GetTestUrl(), net::ERR_CONNECTION_RESET);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(GetDelayForReloadCount(0), GetCurrentAutoReloadDelay());
+
+ // Start the navigation and simulate stoppage via user action (i.e.
+ // `WebContents::Stop`). This should cancel the previously scheduled
+ // auto-reload timer without scheduling a new one.
+ content::TestNavigationManager navigation(shell()->web_contents(),
+ GetTestUrl());
+ shell()->web_contents()->GetController().LoadURL(
+ GetTestUrl(), content::Referrer(), ui::PAGE_TRANSITION_TYPED,
+ /*extra_headers=*/std::string());
+ EXPECT_TRUE(navigation.WaitForRequestStart());
+ shell()->web_contents()->Stop();
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+}
+
+// Various specific types of network-layer errors do not trigger auto-reload.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest,
+ NoAutoReloadOnUnsupportedNetworkErrors) {
+ {
+ NetErrorUrlInterceptor interceptor(GetTestUrl(),
+ net::ERR_UNKNOWN_URL_SCHEME);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+ }
+ {
+ NetErrorUrlInterceptor interceptor(GetTestUrl(),
+ net::ERR_BAD_SSL_CLIENT_AUTH_CERT);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+ }
+ {
+ NetErrorUrlInterceptor interceptor(GetTestUrl(),
+ net::ERR_SSL_PROTOCOL_ERROR);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+ }
+ {
+ NetErrorUrlInterceptor interceptor(GetTestUrl(),
+ net::ERR_BLOCKED_BY_ADMINISTRATOR);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+ }
+ {
+ NetErrorUrlInterceptor interceptor(GetTestUrl(),
+ net::ERR_INVALID_AUTH_CREDENTIALS);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+ }
+}
+
+// Only HTTP and HTTPS navigation error pages activate auto-reload.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest,
+ NoAutoReloadWithoutHttpOrHttps) {
+ {
+ const GURL kTestDataUrl{"data://whatever"};
+ NetErrorUrlInterceptor interceptor(kTestDataUrl, net::ERR_ACCESS_DENIED);
+ EXPECT_FALSE(NavigateMainFrame(kTestDataUrl));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+ }
+
+ {
+ const GURL kTestFileUrl{"file://whatever"};
+ NetErrorUrlInterceptor interceptor(kTestFileUrl, net::ERR_ACCESS_DENIED);
+ EXPECT_FALSE(NavigateMainFrame(kTestFileUrl));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+ }
+}
+
+// Custom error pages do not activate auto-reload.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest,
+ NoAutoReloadWithCustomErrorPage) {
+ CustomErrorPageThrottleInserter custom_error_page(
+ shell()->web_contents(), net::ERR_ACCESS_DENIED,
+ "<html><body><strong>NONE SHALL PASS!</strong></body></html>");
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+}
+
+// Starting a new navigation cancels any pending auto-reload.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest,
+ NavigationCancelsAutoReload) {
+ // Force an error to initiate auto-reload.
+ auto interceptor = std::make_unique<NetErrorUrlInterceptor>(
+ GetTestUrl(), net::ERR_CONNECTION_RESET);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(GetDelayForReloadCount(0), GetCurrentAutoReloadDelay());
+ interceptor.reset();
+
+ // Start a new navigation before the reload task can run. Reload should be
+ // cancelled. Note that we wait only for the request to start and be deferred
+ // here before verifying the auto-reload cancellation.
+ DeferNextNavigationThrottleInserter deferrer(shell()->web_contents());
+ shell()->web_contents()->GetController().LoadURL(
+ GetTestUrl(), content::Referrer(), ui::PAGE_TRANSITION_TYPED,
+ /*extra_headers=*/std::string());
+ deferrer.WaitForNextNavigationToBeDeferred();
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+
+ // Now cancel the deferred navigation and observe that auto-reload for the
+ // error page is rescheduled.
+ deferrer.CancelAndWaitForNavigationToFinish();
+ EXPECT_EQ(GetDelayForReloadCount(0), GetCurrentAutoReloadDelay());
+}
+
+// An error page while offline does not trigger auto-reload.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest,
+ NoAutoReloadWhileOffline) {
+ SimulateNetworkGoingOffline();
+
+ // This would normally schedule an auto-reload, but we're offline.
+ NetErrorUrlInterceptor interceptor(GetTestUrl(), net::ERR_CONNECTION_RESET);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+}
+
+// If the browser comes online while sitting at an error page that supports
+// auto-reload, a new auto-reload task should be scheduled.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest,
+ AutoReloadWhenBrowserComesOnline) {
+ SimulateNetworkGoingOffline();
+
+ // This would normally schedule an auto-reload, but we're offline.
+ NetErrorUrlInterceptor interceptor(GetTestUrl(), net::ERR_CONNECTION_RESET);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+
+ SimulateNetworkGoingOnline();
+ EXPECT_EQ(GetDelayForReloadCount(0), GetCurrentAutoReloadDelay());
+}
+
+// If the browser comes online while sitting at non-error page, auto-reload is
+// not scheduled.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest,
+ NoAutoReloadOnNonErrorPageWhenBrowserComesOnline) {
+ EXPECT_TRUE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+
+ SimulateNetworkGoingOffline();
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+
+ SimulateNetworkGoingOnline();
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+}
+
+// Auto-reload is not scheduled when the WebContents are hidden.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest,
+ NoAutoReloadWhenContentsHidden) {
+ shell()->web_contents()->WasHidden();
+
+ // This would normally schedule an auto-reload, but we're offline.
+ NetErrorUrlInterceptor interceptor(GetTestUrl(), net::ERR_CONNECTION_RESET);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+}
+
+// If the WebContents becomes visible while sitting at an error page that
+// supports auto-reload, a new auto-reload task should be scheduled.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest,
+ AutoReloadWhenContentsBecomeVisible) {
+ shell()->web_contents()->WasHidden();
+
+ // This would normally schedule an auto-reload, but we're offline.
+ NetErrorUrlInterceptor interceptor(GetTestUrl(), net::ERR_CONNECTION_RESET);
+ EXPECT_FALSE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+
+ shell()->web_contents()->WasShown();
+ EXPECT_EQ(GetDelayForReloadCount(0), GetCurrentAutoReloadDelay());
+}
+
+// If the WebContents becomes visible while sitting at non-error page,
+// auto-reload is not scheduled.
+IN_PROC_BROWSER_TEST_F(NetErrorAutoReloaderBrowserTest,
+ NoAutoReloadOnNonErrorPageWhenContentsBecomeVisible) {
+ EXPECT_TRUE(NavigateMainFrame(GetTestUrl()));
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+
+ shell()->web_contents()->WasHidden();
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+
+ shell()->web_contents()->WasShown();
+ EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay());
+}
+
+} // namespace
+} // namespace error_page
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 7ef7730..c4e8c37 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -1473,7 +1473,7 @@
base::debug::DumpWithoutCrashing();
}
- ReadyToCommitNavigation(false /* is_error */);
+ ReadyToCommitNavigation(CommitPageType::kNonErrorPage);
CommitNavigation();
return;
}
@@ -3100,7 +3100,8 @@
}
sandbox_flags_to_commit_ = ComputeSandboxFlagsToCommit();
- ReadyToCommitNavigation(true);
+ ReadyToCommitNavigation(error_page_content ? CommitPageType::kCustomErrorPage
+ : CommitPageType::kErrorPage);
render_frame_host_->FailedNavigation(this, *common_params_, *commit_params_,
has_stale_copy_in_cache_, net_error_,
error_page_content);
@@ -3797,7 +3798,7 @@
// commit. Inform observers that the navigation is now ready to commit,
// unless it is not set to commit (204/205s/downloads).
if (render_frame_host_)
- ReadyToCommitNavigation(false);
+ ReadyToCommitNavigation(CommitPageType::kNonErrorPage);
// The call above might block on showing a user dialog. The interaction of
// the user with this dialog might result in the WebContents owning this
@@ -4160,10 +4161,11 @@
!IsForMhtmlSubframe();
}
-void NavigationRequest::ReadyToCommitNavigation(bool is_error) {
+void NavigationRequest::ReadyToCommitNavigation(CommitPageType type) {
EnterChildTraceEvent("ReadyToCommitNavigation", this);
SetState(READY_TO_COMMIT);
+ committed_page_type_ = type;
ready_to_commit_time_ = base::TimeTicks::Now();
RestartCommitTimeout();
@@ -4186,7 +4188,7 @@
// Record metrics for the time it takes to get to this state from the
// beginning of the navigation.
- if (!IsSameDocument() && !is_error) {
+ if (!IsSameDocument() && type == CommitPageType::kNonErrorPage) {
is_same_process_ =
render_frame_host_->GetProcess()->GetID() ==
frame_tree_node_->current_frame_host()->GetProcess()->GetID();
@@ -4408,6 +4410,11 @@
return state_ == DID_COMMIT_ERROR_PAGE;
}
+bool NavigationRequest::IsCustomErrorPage() {
+ return state_ == DID_COMMIT_ERROR_PAGE &&
+ committed_page_type_ == CommitPageType::kCustomErrorPage;
+}
+
net::HttpResponseInfo::ConnectionInfo NavigationRequest::GetConnectionInfo() {
return response() ? response()->connection_info
: net::HttpResponseInfo::ConnectionInfo();
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index 2bf304e..e4a57cff 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -294,6 +294,7 @@
bool IsSameDocument() override;
bool HasCommitted() override;
bool IsErrorPage() override;
+ bool IsCustomErrorPage() override;
bool HasSubframeNavigationEntryCommitted() override;
bool DidReplaceEntry() override;
bool ShouldUpdateHistory() override;
@@ -979,7 +980,12 @@
// Called when the navigation is ready to be committed. This will update the
// |state_| and inform the delegate.
- void ReadyToCommitNavigation(bool is_error);
+ enum class CommitPageType {
+ kNonErrorPage,
+ kErrorPage,
+ kCustomErrorPage,
+ };
+ void ReadyToCommitNavigation(CommitPageType type);
// Called if READY_TO_COMMIT -> COMMIT state transition takes an unusually
// long time.
@@ -1119,6 +1125,9 @@
int bindings_;
bool entry_overrides_ua_ = false;
+ // Indicates what type of error page is about to be committed, if any.
+ CommitPageType committed_page_type_ = CommitPageType::kNonErrorPage;
+
// Set to true if SetIsOverridingUserAgent() is called.
bool was_set_overriding_user_agent_called_ = false;
diff --git a/content/public/browser/navigation_handle.h b/content/public/browser/navigation_handle.h
index 8a1d8de..7facdef 100644
--- a/content/public/browser/navigation_handle.h
+++ b/content/public/browser/navigation_handle.h
@@ -230,6 +230,11 @@
// GetNetErrorCode will be net::OK.
virtual bool IsErrorPage() = 0;
+ // Indicates whether navigation committed a custom error page (i.e. an error
+ // page that provides its own HTML.) IsErrorPage is always true if this is
+ // true, but the inverse doesn't hold.
+ virtual bool IsCustomErrorPage() = 0;
+
// Not all committed subframe navigations (i.e., !IsInMainFrame &&
// HasCommitted) end up causing a change of the current NavigationEntry. For
// example, some users of NavigationHandle may want to ignore the initial
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 2b8475b..ee2a32f 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -2937,7 +2937,8 @@
void TestNavigationManager::DidFinishNavigation(NavigationHandle* handle) {
if (handle != request_)
return;
- was_successful_ = handle->HasCommitted() && !handle->IsErrorPage();
+ was_committed_ = handle->HasCommitted();
+ was_successful_ = was_committed_ && !handle->IsErrorPage();
current_state_ = NavigationState::FINISHED;
navigation_paused_ = false;
request_ = nullptr;
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 83514d8..0bb59f7 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -1496,6 +1496,9 @@
NavigationHandle* GetNavigationHandle();
// Whether the navigation successfully committed.
+ bool was_committed() const { return was_committed_; }
+
+ // Whether the navigation successfully committed and was not an error page.
bool was_successful() const { return was_successful_; }
// Allows nestable tasks when running a message loop in the Wait* functions.
@@ -1541,6 +1544,7 @@
bool navigation_paused_;
NavigationState current_state_;
NavigationState desired_state_;
+ bool was_committed_ = false;
bool was_successful_ = false;
base::OnceClosure quit_closure_;
base::RunLoop::Type message_loop_type_ = base::RunLoop::Type::kDefault;
diff --git a/content/public/test/mock_navigation_handle.h b/content/public/test/mock_navigation_handle.h
index 7149f13..eec48d6 100644
--- a/content/public/test/mock_navigation_handle.h
+++ b/content/public/test/mock_navigation_handle.h
@@ -70,6 +70,7 @@
}
bool HasCommitted() override { return has_committed_; }
bool IsErrorPage() override { return is_error_page_; }
+ bool IsCustomErrorPage() override { return false; }
MOCK_METHOD0(HasSubframeNavigationEntryCommitted, bool());
MOCK_METHOD0(DidReplaceEntry, bool());
MOCK_METHOD0(ShouldUpdateHistory, bool());