blob: 27083b052cc2203126bf2a976c47644d9a282e3b [file] [log] [blame]
// Copyright 2019 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_TEST_NAVIGATION_SIMULATOR_IMPL_H_
#define CONTENT_TEST_NAVIGATION_SIMULATOR_IMPL_H_
#include <memory>
#include <string>
#include "base/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_throttle.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/navigation_simulator.h"
#include "content/test/test_render_frame_host.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "net/base/host_port_pair.h"
#include "net/base/ip_endpoint.h"
#include "net/base/load_flags.h"
#include "net/dns/public/resolve_error_info.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/navigation/impression.h"
#include "third_party/blink/public/mojom/loader/mixed_content.mojom.h"
#include "third_party/blink/public/mojom/loader/referrer.mojom-forward.h"
#include "url/gurl.h"
namespace content {
class FrameTreeNode;
class NavigationRequest;
class TestRenderFrameHost;
class WebContentsImpl;
namespace mojom {
class NavigationClient;
}
class NavigationSimulatorImpl : public NavigationSimulator,
public WebContentsObserver {
public:
~NavigationSimulatorImpl() override;
// TODO(https://crbug.com/1131832): Remove `original_url` as it's not used.
static std::unique_ptr<NavigationSimulatorImpl> CreateBrowserInitiated(
const GURL& original_url,
WebContents* contents);
static std::unique_ptr<NavigationSimulatorImpl> CreateHistoryNavigation(
int offset,
WebContents* web_contents,
bool is_renderer_initiated);
// TODO(https://crbug.com/1131832): Remove `original_url` as it's not used.
static std::unique_ptr<NavigationSimulatorImpl> CreateRendererInitiated(
const GURL& original_url,
RenderFrameHost* render_frame_host);
static std::unique_ptr<NavigationSimulatorImpl> CreateFromPending(
NavigationController& controller);
// Creates a NavigationSimulator for an already-started navigation happening
// in `frame_tree_node`. Can be used to drive the navigation to completion.
static std::unique_ptr<NavigationSimulatorImpl> CreateFromPendingInFrame(
FrameTreeNode* frame_tree_node);
// NavigationSimulator implementation.
void Start() override;
void Redirect(const GURL& new_url) override;
void ReadyToCommit() override;
void Commit() override;
void AbortCommit() override;
void AbortFromRenderer() override;
void Fail(int error_code) override;
void CommitErrorPage() override;
void CommitSameDocument() override;
RenderFrameHost* GetFinalRenderFrameHost() override;
void Wait() override;
bool IsDeferred() override;
bool HasFailed() override;
void SetInitiatorFrame(RenderFrameHost* initiator_frame_host) override;
void SetTransition(ui::PageTransition transition) override;
void SetHasUserGesture(bool has_user_gesture) override;
void SetNavigationInputStart(base::TimeTicks navigation_input_start) override;
void SetReloadType(ReloadType reload_type) override;
void SetMethod(const std::string& method) override;
void SetIsFormSubmission(bool is_form_submission) override;
void SetReferrer(blink::mojom::ReferrerPtr referrer) override;
void SetSocketAddress(const net::IPEndPoint& remote_endpoint) override;
void SetWasFetchedViaCache(bool was_fetched_via_cache) override;
void SetIsSignedExchangeInnerResponse(
bool is_signed_exchange_inner_response) override;
void SetPermissionsPolicyHeader(
blink::ParsedPermissionsPolicy permissions_policy_header) override;
void SetContentsMimeType(const std::string& contents_mime_type) override;
void SetRedirectHeaders(
scoped_refptr<net::HttpResponseHeaders> redirect_headers) override;
void SetResponseHeaders(
scoped_refptr<net::HttpResponseHeaders> response_headers) override;
void SetResponseBody(
mojo::ScopedDataPipeConsumerHandle response_body) override;
void SetAutoAdvance(bool auto_advance) override;
void SetResolveErrorInfo(
const net::ResolveErrorInfo& resolve_error_info) override;
void SetSSLInfo(const net::SSLInfo& ssl_info) override;
void SetResponseDnsAliases(std::vector<std::string> aliases) override;
void SetEarlyHintsPreloadLinkHeaderReceived(bool received) override;
NavigationThrottle::ThrottleCheckResult GetLastThrottleCheckResult() override;
NavigationRequest* GetNavigationHandle() override;
content::GlobalRequestID GetGlobalRequestID() override;
void SetKeepLoading(bool keep_loading) override;
void StopLoading() override;
// Additional utilities usable only inside content/.
// This will do the very beginning of a navigation but stop before the
// beforeunload event response. Will leave the Simulator in a
// WAITING_BEFORE_UNLOAD state. We do not wait for beforeunload event when
// starting renderer-side, use solely for browser initiated navigations.
void BrowserInitiatedStartAndWaitBeforeUnload();
// Set LoadURLParams and make browser initiated navigations use
// LoadURLWithParams instead of LoadURL.
void SetLoadURLParams(NavigationController::LoadURLParams* load_url_params);
void set_should_check_main_world_csp(
network::mojom::CSPDisposition disposition) {
should_check_main_world_csp_ = disposition;
}
// Set DidCommit*Params history_list_was_cleared flag to `history_cleared`.
void set_history_list_was_cleared(bool history_cleared);
// Manually force the value of should_replace_current_entry flag in
// DidCommit*Params to `should_replace_current_entry`.
void set_should_replace_current_entry(bool should_replace_current_entry) {
should_replace_current_entry_ = should_replace_current_entry;
}
void set_http_connection_info(net::HttpResponseInfo::ConnectionInfo info) {
http_connection_info_ = info;
}
// Whether to drop the swap out ack of the previous RenderFrameHost during
// cross-process navigations. By default this is false, set to true if you
// want the old RenderFrameHost to be left in a pending swap out state.
void set_drop_unload_ack(bool drop_unload_ack) {
drop_unload_ack_ = drop_unload_ack;
}
// Whether to drop the BeforeUnloadCompleted of the current RenderFrameHost at
// the beginning of a browser-initiated navigation. By default this is false,
// set to true if you want to simulate the BeforeUnloadCompleted manually.
void set_block_invoking_before_unload_completed_callback(
bool block_invoking_before_unload_completed_callback) {
block_invoking_before_unload_completed_callback_ =
block_invoking_before_unload_completed_callback;
}
void set_page_state(const blink::PageState& page_state) {
page_state_ = page_state;
}
void set_origin(const url::Origin& origin) { origin_ = origin; }
void set_impression(const blink::Impression& impression) {
impression_ = impression;
}
void set_skip_service_worker(bool skip_service_worker) {
skip_service_worker_ = skip_service_worker;
}
void set_initiator_origin(
const absl::optional<url::Origin>& initiator_origin) {
initiator_origin_ = initiator_origin;
}
void set_request_headers(const std::string& headers) { headers_ = headers; }
void set_load_flags(int load_flags) { load_flags_ = load_flags; }
void set_mixed_content_context_type(
blink::mojom::MixedContentContextType mixed_content_context_type) {
mixed_content_context_type_ = mixed_content_context_type;
}
void set_searchable_form_url(const GURL& searchable_form_url) {
searchable_form_url_ = searchable_form_url;
}
void set_searchable_form_encoding(
const std::string& searchable_form_encoding) {
searchable_form_encoding_ = searchable_form_encoding;
}
void set_href_translate(const std::string& href_translate) {
href_translate_ = href_translate;
}
void set_request_context_type(
blink::mojom::RequestContextType request_context_type) {
request_context_type_ = request_context_type;
}
void set_insecure_request_policy(
blink::mojom::InsecureRequestPolicy insecure_request_policy) {
insecure_request_policy_ = insecure_request_policy;
}
void set_insecure_navigations_set(
const std::vector<uint32_t> insecure_navigations_set) {
insecure_navigations_set_ = insecure_navigations_set;
}
void set_has_potentially_trustworthy_unique_origin(
bool has_potentially_trustworthy_unique_origin) {
has_potentially_trustworthy_unique_origin_ =
has_potentially_trustworthy_unique_origin;
}
void set_supports_loading_mode_header(std::string value) {
supports_loading_mode_header_ = value;
}
private:
NavigationSimulatorImpl(const GURL& original_url,
bool browser_initiated,
WebContentsImpl* web_contents,
TestRenderFrameHost* render_frame_host);
// Adds a test navigation throttle to `request_` which sanity checks various
// callbacks have been properly called.
void RegisterTestThrottle();
// Initializes a NavigationSimulator from an existing NavigationRequest. This
// should only be needed if a navigation was started without a valid
// NavigationSimulator.
void InitializeFromStartedRequest(NavigationRequest* request);
// 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 StartComplete();
void RedirectComplete(int previous_num_will_redirect_request_called,
int previous_did_redirect_navigation_called);
void WillProcessResponseComplete();
void ReadyToCommitComplete();
void FailComplete(int error_code);
void OnWillStartRequest();
void OnWillRedirectRequest();
void OnWillFailRequest();
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 the navigation to reach the next
// NavigationThrottle phase of the navigation to complete
// (StartRequest|Redirect|Failed|ProcessResponse) if `auto_advance_`. This
// waits until *after* throttle checks are run (if the navigation requires
// throttle checks). If !`auto_advance_` this will just set up state for
// checking the result when the throttles end up finishing.
void MaybeWaitForThrottleChecksComplete(base::OnceClosure complete_closure);
// Like above but blocks waiting for the ReadyToCommit checks to complete.
void MaybeWaitForReadyToCommitCheckComplete();
// Sets `last_throttle_check_result_` and calls both the `wait_closure_` and
// the `throttle_checks_complete_closure_`, if they are set.
bool OnThrottleChecksComplete(NavigationThrottle::ThrottleCheckResult result);
// Helper method to set the OnThrottleChecksComplete callback on the
// NavigationRequest.
void PrepareCompleteCallbackOnRequest();
// Infers from internal parameters whether the navigation created a new
// entry.
bool DidCreateNewEntry(bool same_document, bool should_replace_current_entry);
// Set the navigation to be done towards the specified navigation controller
// offset. Typically -1 for back navigations or 1 for forward navigations.
void SetSessionHistoryOffset(int offset);
// Build DidCommitProvisionalLoadParams to commit the ongoing navigation,
// based on internal NavigationSimulator state and given parameters.
mojom::DidCommitProvisionalLoadParamsPtr BuildDidCommitProvisionalLoadParams(
bool same_document,
bool failed_navigation,
int last_http_status_code);
// Simulate the UnloadACK in the old RenderFrameHost if it was unloaded at the
// commit time.
void SimulateUnloadCompletionCallbackForPreviousFrameIfNeeded(
RenderFrameHostImpl* previous_frame);
// Certain navigations can skip throttle checks:
// - same-document navigations
// - about:blank navigations
// - navigations not handled by the network stack
// - page activations like prerendering and back-forward cache.
bool NeedsThrottleChecks() const;
// Whether the navigation performs CommitDeferringCondition checks before
// committing. i.e. if it goes through the full
// WillStartRequest->WillProcessResponse->etc.->Commit phases. This includes
// all navigations that require throttle checks plus page activations like
// prerendering/BFCache.
bool NeedsPreCommitChecks() const;
// See comments of `force_before_unload_for_browser_initiated_` for details
// on what this does.
void AddBeforeUnloadHandlerIfNecessary();
enum State {
INITIALIZATION,
WAITING_BEFORE_UNLOAD,
STARTED,
READY_TO_COMMIT,
FAILED,
FINISHED,
};
State state_ = INITIALIZATION;
// The WebContents in which the navigation is taking place.
// IMPORTANT: Because NavigationSimulator is used outside content/ where we
// sometimes use WebContentsImpl and not TestWebContents, this cannot be
// assumed to cast properly to TestWebContents.
raw_ptr<WebContentsImpl> web_contents_;
// The renderer associated with this navigation.
// Note: this can initially be null for browser-initiated navigations.
raw_ptr<TestRenderFrameHost> render_frame_host_;
raw_ptr<FrameTreeNode> frame_tree_node_;
// The NavigationRequest associated with this navigation.
raw_ptr<NavigationRequest> request_;
// Note: additional parameters to modify the navigation should be properly
// initialized (if needed) in InitializeFromStartedRequest.
GURL navigation_url_;
net::IPEndPoint remote_endpoint_;
bool was_fetched_via_cache_ = false;
bool is_signed_exchange_inner_response_ = false;
std::string initial_method_;
bool is_form_submission_ = false;
bool browser_initiated_;
bool same_document_ = false;
TestRenderFrameHost::LoadingScenario loading_scenario_ =
TestRenderFrameHost::LoadingScenario::kOther;
blink::mojom::ReferrerPtr referrer_;
raw_ptr<RenderFrameHost> initiator_frame_host_ = nullptr;
ui::PageTransition transition_;
ReloadType reload_type_ = ReloadType::NONE;
int session_history_offset_ = 0;
bool has_user_gesture_ = true;
base::TimeTicks navigation_input_start_;
mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
browser_interface_broker_receiver_;
std::string contents_mime_type_;
scoped_refptr<net::HttpResponseHeaders> redirect_headers_;
scoped_refptr<net::HttpResponseHeaders> response_headers_;
blink::ParsedPermissionsPolicy permissions_policy_header_;
mojo::ScopedDataPipeConsumerHandle response_body_;
network::mojom::CSPDisposition should_check_main_world_csp_ =
network::mojom::CSPDisposition::CHECK;
net::HttpResponseInfo::ConnectionInfo http_connection_info_ =
net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN;
net::ResolveErrorInfo resolve_error_info_ = net::ResolveErrorInfo(net::OK);
absl::optional<net::SSLInfo> ssl_info_;
absl::optional<blink::PageState> page_state_;
absl::optional<url::Origin> origin_;
absl::optional<blink::Impression> impression_;
int64_t post_id_ = -1;
bool skip_service_worker_ = false;
absl::optional<url::Origin> initiator_origin_;
std::string headers_;
int load_flags_ = net::LOAD_NORMAL;
blink::mojom::MixedContentContextType mixed_content_context_type_ =
blink::mojom::MixedContentContextType::kBlockable;
GURL searchable_form_url_;
std::string searchable_form_encoding_;
std::string href_translate_;
blink::mojom::RequestContextType request_context_type_ =
blink::mojom::RequestContextType::LOCATION;
blink::mojom::InsecureRequestPolicy insecure_request_policy_ =
blink::mojom::InsecureRequestPolicy::kLeaveInsecureRequestsAlone;
std::vector<uint32_t> insecure_navigations_set_;
bool has_potentially_trustworthy_unique_origin_ = false;
// Any DNS aliases, as read from CNAME records, for the request URL that
// would be in the network::mojom::URLResponseHead. The alias chain order
// is preserved in reverse, from canonical name (i.e. address record name)
// through to query name.
std::vector<std::string> response_dns_aliases_;
bool auto_advance_ = true;
bool drop_unload_ack_ = false;
bool block_invoking_before_unload_completed_callback_ = false;
bool keep_loading_ = false;
// Generic params structure used for fully customized browser initiated
// navigation requests. Only valid if explicitely provided.
raw_ptr<NavigationController::LoadURLParams> load_url_params_;
bool history_list_was_cleared_ = false;
bool should_replace_current_entry_ = false;
bool was_aborted_prior_to_ready_to_commit_ = false;
bool early_hints_preload_link_header_received_ = false;
std::string supports_loading_mode_header_;
absl::optional<bool> was_prerendered_page_activation_;
// 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_will_fail_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.
absl::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 MaybeWaitForThrottleChecksComplete is called.
// Called in OnThrottleChecksComplete.
base::OnceClosure throttle_checks_complete_closure_;
// Closure that is called in OnThrottleChecksComplete if we are waiting on the
// result. Calling this will quit the nested run loop.
base::OnceClosure wait_closure_;
// Closure that is called when DidStartNavigation is called.
base::OnceClosure did_start_navigation_closure_;
// This member simply ensures that we do not disconnect the NavigationClient
// interface, as it would be interpreted as a cancellation coming from the
// renderer process side. This member interface will never be bound.
mojo::PendingAssociatedReceiver<mojom::NavigationClient>
navigation_client_receiver_;
// If true, the RenderFrameHost is told there is a before-unload handler. This
// is done for compat, as this code and consuming code was written assuming
// navigations would always result in querying for before-unload handlers even
// if one wasn't present.
const bool force_before_unload_for_browser_initiated_;
base::WeakPtrFactory<NavigationSimulatorImpl> weak_factory_{this};
};
} // namespace content
#endif // CONTENT_TEST_NAVIGATION_SIMULATOR_IMPL_H_