blob: 42be780a55cc001edd859c1f1b232d462f628fa3 [file] [log] [blame]
// Copyright 2017 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 CONTENT_PUBLIC_TEST_NAVIGATION_SIMULATOR_H_
#define CONTENT_PUBLIC_TEST_NAVIGATION_SIMULATOR_H_
#include <memory>
#include "base/callback.h"
#include "base/optional.h"
#include "content/public/browser/global_request_id.h"
#include "content/public/browser/navigation_throttle.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/referrer.h"
#include "content/public/test/navigation_simulator.h"
#include "net/base/host_port_pair.h"
#include "ui/base/page_transition_types.h"
class GURL;
namespace content {
class FrameTreeNode;
class NavigationHandle;
class NavigationHandleImpl;
class RenderFrameHost;
class TestRenderFrameHost;
class TestWebContents;
struct Referrer;
// An interface for simulating a navigation in unit tests. Currently this only
// supports renderer-initiated navigations.
// Note: this should not be used in browser tests.
// TODO(clamy): support browser-initiated navigations.
class NavigationSimulator : public WebContentsObserver {
public:
// Simulates a browser-initiated navigation to |url| started in
// |web_contents| from start to commit. Returns the RenderFrameHost that
// committed the navigation.
static RenderFrameHost* NavigateAndCommitFromBrowser(
WebContents* web_contents,
const GURL& url);
// Simulates a renderer-initiated navigation to |url| started in
// |render_frame_host| from start to commit. Returns the RenderFramehost that
// committed the navigation.
static RenderFrameHost* NavigateAndCommitFromDocument(
const GURL& original_url,
RenderFrameHost* render_frame_host);
// Simulates a failed browser-initiated navigation to |url| started in
// |web_contents| from start to commit. Returns the RenderFrameHost that
// committed the error page for the navigation, or nullptr if the navigation
// error did not result in an error page.
static RenderFrameHost* NavigateAndFailFromBrowser(WebContents* web_contents,
const GURL& url,
int net_error_code);
// Simulates a failed renderer-initiated navigation to |url| started in
// |render_frame_host| from start to commit. Returns the RenderFramehost that
// committed the error page for the navigation, or nullptr if the navigation
// error did not result in an error page.
static RenderFrameHost* NavigateAndFailFromDocument(
const GURL& original_url,
int net_error_code,
RenderFrameHost* render_frame_host);
// ---------------------------------------------------------------------------
// All the following methods should be used when more precise control over the
// navigation is needed.
// Creates a NavigationSimulator that will be used to simulate a
// browser-initiated navigation to |original_url| started in |contents|.
static std::unique_ptr<NavigationSimulator> CreateBrowserInitiated(
const GURL& original_url,
WebContents* contents);
// Creates a NavigationSimulator that will be used to simulate a
// renderer-initiated navigation to |original_url| started by
// |render_frame_host|.
static std::unique_ptr<NavigationSimulator> CreateRendererInitiated(
const GURL& original_url,
RenderFrameHost* render_frame_host);
~NavigationSimulator() override;
// --------------------------------------------------------------------------
// The following functions should be used to simulate events happening during
// a navigation.
//
// Example of usage for a successful renderer-initiated navigation:
// unique_ptr<NavigationSimulator> simulator =
// NavigationSimulator::CreateRendererInitiated(
// original_url, render_frame_host);
// simulator->SetTransition(ui::PAGE_TRANSITION_LINK);
// simulator->Start();
// for (GURL redirect_url : redirects)
// simulator->Redirect(redirect_url);
// simulator->Commit();
//
// Example of usage for a failed renderer-initiated navigation:
// unique_ptr<NavigationSimulator> simulator =
// NavigationSimulator::CreateRendererInitiated(
// original_url, render_frame_host);
// simulator->SetTransition(ui::PAGE_TRANSITION_LINK);
// simulator->Start();
// for (GURL redirect_url : redirects)
// simulator->Redirect(redirect_url);
// simulator->Fail(net::ERR_TIMED_OUT);
// simulator->CommitErrorPage();
//
// Example of usage for a same-page renderer-initiated navigation:
// unique_ptr<NavigationSimulator> simulator =
// NavigationSimulator::CreateRendererInitiated(
// original_url, render_frame_host);
// simulator->CommitSameDocument();
//
// Example of usage for a renderer-initiated navigation which is cancelled by
// a throttle upon redirecting. Note that registering the throttle is done
// elsewhere:
// unique_ptr<NavigationSimulator> simulator =
// NavigationSimulator::CreateRendererInitiated(
// original_url, render_frame_host);
// simulator->SetTransition(ui::PAGE_TRANSITION_LINK);
// simulator->Start();
// simulator->Redirect(redirect_url);
// EXPECT_EQ(NavigationThrottle::CANCEL,
// simulator->GetLastThrottleCheckResult());
// Simulates the start of the navigation.
virtual void Start();
// Simulates a redirect to |new_url| for the navigation.
virtual void Redirect(const GURL& new_url);
// Simulates the commit of the navigation in the RenderFrameHost.
virtual void Commit();
// Simulates the navigation failing with the error code |error_code|.
virtual void Fail(int error_code);
// Simulates the commit of an error page following a navigation failure.
virtual void CommitErrorPage();
// Simulates the commit of a same-document navigation, ie fragment navigations
// or pushState/popState navigations.
virtual void CommitSameDocument();
// Must be called after the simulated navigation or an error page has
// committed. Returns the RenderFrameHost the navigation committed in.
virtual RenderFrameHost* GetFinalRenderFrameHost();
// --------------------------------------------------------------------------
// The following functions are used to specify the parameters of the
// navigation.
// The following parameters are constant during the navigation and may only be
// specified before calling |Start|.
virtual void SetTransition(ui::PageTransition transition);
// The following parameters can change during redirects. They should be
// specified before calling |Start| if they need to apply to the navigation to
// the original url. Otherwise, they should be specified before calling
// |Redirect|.
virtual void SetReferrer(const Referrer& referrer);
// The following parameters can change at any point until the page fails or
// commits. They should be specified before calling |Fail| or |Commit|.
virtual void SetSocketAddress(const net::HostPortPair& socket_address);
// --------------------------------------------------------------------------
// Gets the last throttle check result computed by the navigation throttles.
// It is an error to call this before Start() is called.
virtual NavigationThrottle::ThrottleCheckResult GetLastThrottleCheckResult();
// Returns the NavigationHandle associated with the navigation being
// simulated. It is an error to call this before Start() or after the
// navigation has finished (successfully or not).
virtual NavigationHandle* GetNavigationHandle() const;
// Returns the GlobalRequestID for the simulated navigation request. Can be
// invoked after the navigation has completed. It is an error to call this
// before the simulated navigation has completed its WillProcessResponse
// callback.
content::GlobalRequestID GetGlobalRequestID() const;
// Allows the user of the NavigationSimulator to specify a callback that will
// be called if the navigation is deferred by a NavigationThrottle. This is
// used for testing deferring NavigationThrottles.
//
// Example usage:
// void CheckThrottleStateAndResume() {
// // Do some testing here.
// deferring_navigation_throttle->Resume();
// }
// unique_ptr<NavigationSimulator> simulator =
// NavigationSimulator::CreateRendererInitiated(
// original_url, render_frame_host);
// simulator->SetOnDeferCallback(base::Bind(&CheckThrottleStateAndResume));
// simulator->Start();
// simulator->Commit();
void SetOnDeferCallback(const base::Closure& on_defer_callback);
private:
NavigationSimulator(const GURL& original_url,
bool browser_initiated,
TestWebContents* web_contents,
TestRenderFrameHost* render_frame_host);
// WebContentsObserver:
void DidStartNavigation(NavigationHandle* navigation_handle) override;
void DidRedirectNavigation(NavigationHandle* navigation_handle) override;
void ReadyToCommitNavigation(NavigationHandle* navigation_handle) override;
void DidFinishNavigation(NavigationHandle* navigation_handle) override;
void OnWillStartRequest();
void OnWillRedirectRequest();
void OnWillProcessResponse();
// Simulates a browser-initiated navigation starting. Returns false if the
// navigation failed synchronously.
bool SimulateBrowserInitiatedStart();
// Simulates a renderer-initiated navigation starting. Returns false if the
// navigation failed synchronously.
bool SimulateRendererInitiatedStart();
// This method will block waiting for throttle checks to complete.
void WaitForThrottleChecksComplete();
// Sets |last_throttle_check_result_| and calls
// |throttle_checks_wait_closure_|.
void OnThrottleChecksComplete(NavigationThrottle::ThrottleCheckResult result);
// Helper method to set the OnThrottleChecksComplete callback on the
// NavigationHandle.
void PrepareCompleteCallbackOnHandle();
// Simulates the DidFailProvisionalLoad IPC following a NavigationThrottle
// cancelling the navigation.
// PlzNavigate: this is not needed.
void FailFromThrottleCheck(NavigationThrottle::ThrottleCheckResult result);
// Check if the navigation corresponds to a same-document navigation.
// PlzNavigate: only use on renderer-initiated navigations.
bool CheckIfSameDocument();
enum State {
INITIALIZATION,
STARTED,
FAILED,
FINISHED,
};
State state_ = INITIALIZATION;
// The WebContents in which the navigation is taking place.
TestWebContents* web_contents_;
// The renderer associated with this navigation.
// Note: this can initially be null for browser-initiated navigations.
TestRenderFrameHost* render_frame_host_;
FrameTreeNode* frame_tree_node_;
// The NavigationHandle associated with this navigation.
NavigationHandleImpl* handle_;
GURL navigation_url_;
net::HostPortPair socket_address_;
bool browser_initiated_;
bool same_document_ = false;
Referrer referrer_;
ui::PageTransition transition_;
// These are used to sanity check the content/public/ API calls emitted as
// part of the navigation.
int num_did_start_navigation_called_ = 0;
int num_will_start_request_called_ = 0;
int num_will_redirect_request_called_ = 0;
int num_did_redirect_navigation_called_ = 0;
int num_will_process_response_called_ = 0;
int num_ready_to_commit_called_ = 0;
int num_did_finish_navigation_called_ = 0;
// Holds the last ThrottleCheckResult calculated by the navigation's
// throttles. Will be unset before WillStartRequest is finished. Will be unset
// while throttles are being run, but before they finish.
base::Optional<NavigationThrottle::ThrottleCheckResult>
last_throttle_check_result_;
// GlobalRequestID for the associated NavigationHandle. Only valid after
// WillProcessResponse has been invoked on the NavigationHandle.
content::GlobalRequestID request_id_;
// Closure that is set when WaitForThrottleChecksComplete is called.
base::Closure throttle_checks_wait_closure_;
// Temporarily holds a closure that will be called on navigation deferral
// until the NavigationHandle for this navigation has been created.
base::Closure on_defer_callback_;
base::WeakPtrFactory<NavigationSimulator> weak_factory_;
};
} // namespace content
#endif // CONTENT_PUBLIC_TEST_NAVIGATION_SIMULATOR_H_