| // 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 "base/run_loop.h" |
| #include "content/public/browser/global_request_id.h" |
| #include "content/public/browser/navigation_throttle.h" |
| #include "content/public/browser/reload_type.h" |
| #include "content/public/browser/web_contents_observer.h" |
| #include "content/public/common/referrer.h" |
| #include "mojo/public/cpp/bindings/associated_interface_request.h" |
| #include "net/base/host_port_pair.h" |
| #include "services/service_manager/public/cpp/interface_provider.h" |
| #include "ui/base/page_transition_types.h" |
| |
| class GURL; |
| |
| namespace content { |
| |
| class FrameTreeNode; |
| class MockNavigationClientImpl; |
| class NavigationHandle; |
| class NavigationHandleImpl; |
| class NavigationRequest; |
| class RenderFrameHost; |
| class TestRenderFrameHost; |
| struct Referrer; |
| |
| namespace mojom { |
| class NavigationClient; |
| } |
| |
| // An interface for simulating a navigation in unit tests. Supports both |
| // renderer and browser-initiated navigations. |
| // Note: this should not be used in browser tests. |
| 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 the page reloading. Returns the RenderFrameHost that committed |
| // the navigation. |
| static RenderFrameHost* Reload(WebContents* web_contents); |
| |
| // Simulates a back navigation from start to commit. Returns the |
| // RenderFrameHost that committed the navigation. |
| static RenderFrameHost* GoBack(WebContents* web_contents); |
| |
| // Simulates a forward navigation from start to commit. Returns the |
| // RenderFrameHost that committed the navigation. |
| static RenderFrameHost* GoForward(WebContents* web_contents); |
| |
| // Simulates a navigation to the given offset of the web_contents navigation |
| // controller, from start to finish. |
| static RenderFrameHost* GoToOffset(WebContents* web_contents, int offset); |
| |
| // 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 the page reloading and failing. 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* ReloadAndFail(WebContents* web_contents, |
| int net_error_code); |
| |
| // Simulates a failed back navigation. 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* GoBackAndFail(WebContents* web_contents, |
| int net_error_code); |
| |
| // TODO(clamy, ahemery): Add GoForwardAndFail() if it becomes needed. |
| |
| // Simulates a failed offset navigation. 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* GoToOffsetAndFail(WebContents* web_contents, |
| int offset, |
| 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 history |
| // navigation to one of the |web_contents|'s navigation controller |offset|. |
| // E.g. offset -1 for back navigations and 1 for forward navigations. |
| static std::unique_ptr<NavigationSimulator> CreateHistoryNavigation( |
| int offset, |
| WebContents* web_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); |
| |
| // Creates a NavigationSimulator for an already-started browser initiated |
| // navigation via LoadURL / Reload / GoToOffset. Can be used to drive the |
| // navigation to completion. |
| static std::unique_ptr<NavigationSimulator> CreateFromPendingBrowserInitiated( |
| WebContents* contents); |
| |
| ~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 receiving the navigation response and choosing a final |
| // RenderFrameHost to commit it. |
| virtual void ReadyToCommit(); |
| |
| // Simulates the commit of the navigation in the RenderFrameHost. |
| virtual void Commit(); |
| |
| // Simulates the commit of a navigation or an error page aborting. |
| virtual void AbortCommit(); |
| |
| // 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(); |
| |
| // Only used if AutoAdvance is turned off. Will wait until the current stage |
| // of the navigation is complete. |
| void Wait(); |
| |
| // Returns true if the navigation is deferred waiting for navigation throttles |
| // to complete. |
| bool IsDeferred(); |
| |
| // -------------------------------------------------------------------------- |
| |
| // 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); |
| virtual void SetHasUserGesture(bool has_user_gesture); |
| // Note: ReloadType should only be specified for browser-initiated |
| // navigations. |
| void SetReloadType(ReloadType reload_type); |
| |
| // Sets the HTTP method for the navigation. |
| void SetMethod(const std::string& method); |
| |
| // 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); |
| |
| // Sets the InterfaceProvider interface request to pass in as an argument to |
| // DidCommitProvisionalLoad for cross-document navigations. If not called, |
| // a stub will be passed in (which will never receive any interface requests). |
| // |
| // This interface connection would normally be created by the RenderFrame, |
| // with the client end bound to |remote_interfaces_| to allow the new document |
| // to access services exposed by the RenderFrameHost. |
| virtual void SetInterfaceProviderRequest( |
| service_manager::mojom::InterfaceProviderRequest request); |
| |
| // Provides the contents mime type to be set at commit. It should be |
| // specified before calling |Commit|. |
| virtual void SetContentsMimeType(const std::string& contents_mime_type); |
| |
| // Whether or not the NavigationSimulator automatically advances the |
| // navigation past the stage requested (e.g. through asynchronous |
| // NavigationThrottles). Defaults to true. Useful for testing throttles which |
| // defer the navigation. |
| // |
| // If the test sets this to false, it should follow up any calls that result |
| // in throttles deferring the navigation with a call to Wait(). |
| virtual void SetAutoAdvance(bool auto_advance); |
| |
| // -------------------------------------------------------------------------- |
| |
| // 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; |
| |
| private: |
| NavigationSimulator(const GURL& original_url, |
| bool browser_initiated, |
| WebContentsImpl* web_contents, |
| TestRenderFrameHost* render_frame_host); |
| |
| // Adds a test navigation throttle to |handle| which sanity checks various |
| // callbacks have been properly called. |
| void RegisterTestThrottle(NavigationHandle* handle); |
| |
| // 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 ReadyToCommitComplete(bool ran_throttles); |
| 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 throttle checks to complete if |
| // |auto_advance_|. Otherwise will just set up state for checking the result |
| // when the throttles end up finishing. |
| void MaybeWaitForThrottleChecksComplete(base::OnceClosure complete_closure); |
| |
| // Sets |last_throttle_check_result_| and calls both the |
| // |wait_closure_| and the |throttle_checks_complete_closure_|, if they are |
| // set. |
| void OnThrottleChecksComplete(NavigationThrottle::ThrottleCheckResult result); |
| |
| // Helper method to set the OnThrottleChecksComplete callback on the |
| // NavigationHandle. |
| void PrepareCompleteCallbackOnHandle(); |
| |
| // Check if the navigation corresponds to a same-document navigation. |
| // Only use on renderer-initiated navigations. |
| bool CheckIfSameDocument(); |
| |
| // Infers from internal parameters whether the navigation created a new |
| // entry. |
| bool DidCreateNewEntry(); |
| |
| // 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); |
| |
| // Only used when PerNavigationMojoInterface is enabled. |
| void StoreNavigationClientRequest( |
| mojo::AssociatedInterfaceRequest<mojom::NavigationClient> |
| navigation_client_request); |
| |
| enum State { |
| INITIALIZATION, |
| STARTED, |
| READY_TO_COMMIT, |
| FAILED, |
| FINISHED, |
| }; |
| |
| State state_ = INITIALIZATION; |
| |
| // The WebContents in which the navigation is taking place. |
| WebContentsImpl* 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_; |
| |
| // Note: additional parameters to modify the navigation should be properly |
| // initialized (if needed) in InitializeFromStartedRequest. |
| GURL navigation_url_; |
| net::HostPortPair socket_address_; |
| std::string initial_method_; |
| bool browser_initiated_; |
| bool same_document_ = false; |
| Referrer referrer_; |
| ui::PageTransition transition_; |
| ReloadType reload_type_ = ReloadType::NONE; |
| int session_history_offset_ = 0; |
| bool has_user_gesture_ = true; |
| service_manager::mojom::InterfaceProviderRequest interface_provider_request_; |
| std::string contents_mime_type_; |
| |
| bool auto_advance_ = true; |
| |
| // 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. |
| 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 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_; |
| |
| // A mock NavigationClient implementation that is used because we do not |
| // actually have a renderer. The navigations would be instantly aborted if |
| // this was not kept alive. |
| // Only used when PerNavigationMojoInterface is enabled. |
| std::unique_ptr<MockNavigationClientImpl> navigation_client_impl_; |
| |
| base::WeakPtrFactory<NavigationSimulator> weak_factory_; |
| }; |
| |
| } // namespace content |
| |
| #endif // CONTENT_PUBLIC_TEST_NAVIGATION_SIMULATOR_H_ |