Chromium Code Reviews| Index: content/test/navigation_simulator_impl.cc |
| diff --git a/content/test/navigation_simulator_impl.cc b/content/test/navigation_simulator_impl.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..9bb164eea1c68ef63d4afbf35679d8c44559fc6a |
| --- /dev/null |
| +++ b/content/test/navigation_simulator_impl.cc |
| @@ -0,0 +1,444 @@ |
| +// 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. |
| + |
| +#include "content/test/navigation_simulator_impl.h" |
| + |
| +#include "content/browser/frame_host/navigation_handle_impl.h" |
| +#include "content/browser/frame_host/navigation_request.h" |
| +#include "content/common/frame_messages.h" |
| +#include "content/public/browser/navigation_throttle.h" |
| +#include "content/public/browser/web_contents.h" |
| +#include "content/public/common/browser_side_navigation_policy.h" |
| +#include "content/test/test_navigation_url_loader.h" |
| +#include "content/test/test_render_frame_host.h" |
| +#include "net/url_request/redirect_info.h" |
| + |
| +namespace content { |
| + |
| +namespace { |
| + |
| +class NavigationThrottleCallCounter : public NavigationThrottle { |
|
nasko
2017/02/16 18:47:59
This class doesn't seem to count anything, so the
clamy
2017/02/17 17:34:52
Indeed. Renamed it.
|
| + public: |
| + NavigationThrottleCallCounter(NavigationHandle* handle, |
| + const base::Closure& on_will_start_request, |
| + const base::Closure& on_will_redirect_request, |
| + const base::Closure& on_will_process_response) |
| + : NavigationThrottle(handle), |
| + on_will_start_request_(on_will_start_request), |
| + on_will_redirect_request_(on_will_redirect_request), |
| + on_will_process_response_(on_will_process_response) {} |
| + |
| + NavigationThrottle::ThrottleCheckResult WillStartRequest() override { |
| + on_will_start_request_.Run(); |
| + return NavigationThrottle::PROCEED; |
| + } |
| + |
| + NavigationThrottle::ThrottleCheckResult WillRedirectRequest() override { |
| + on_will_redirect_request_.Run(); |
| + return NavigationThrottle::PROCEED; |
| + } |
| + |
| + NavigationThrottle::ThrottleCheckResult WillProcessResponse() override { |
| + on_will_process_response_.Run(); |
| + return NavigationThrottle::PROCEED; |
| + } |
| + |
| + private: |
| + base::Closure on_will_start_request_; |
| + base::Closure on_will_redirect_request_; |
| + base::Closure on_will_process_response_; |
| +}; |
| + |
| +} // namespace |
| + |
| +NavigationSimulatorImpl::NavigationSimulatorImpl( |
| + const GURL& original_url, |
| + TestRenderFrameHost* render_frame_host) |
| + : WebContentsObserver(WebContents::FromRenderFrameHost(render_frame_host)), |
| + state_(INITIALIZATION), |
|
nasko
2017/02/16 18:47:59
With the newest C++11 goodies, we can initialize t
clamy
2017/02/17 17:34:52
Done.
|
| + render_frame_host_(render_frame_host), |
| + handle_(nullptr), |
| + navigation_url_(original_url), |
| + transition_(ui::PAGE_TRANSITION_LINK), |
| + num_did_start_navigation_called_(0), |
| + num_will_start_request_called_(0), |
| + num_will_redirect_request_called_(0), |
| + num_did_redirect_navigation_called_(0), |
| + num_will_process_response_called_(0), |
| + num_ready_to_commit_called_(0), |
| + num_did_finish_navigation_called_(0), |
| + weak_factory_(this) { |
| + if (render_frame_host->GetParent()) { |
| + if (!render_frame_host->frame_tree_node()->has_committed_real_load()) |
| + transition_ = ui::PAGE_TRANSITION_AUTO_SUBFRAME; |
| + else |
| + transition_ = ui::PAGE_TRANSITION_MANUAL_SUBFRAME; |
| + } |
| +} |
| + |
| +NavigationSimulatorImpl::~NavigationSimulatorImpl() {} |
| + |
| +void NavigationSimulatorImpl::Start() { |
| + CHECK(state_ == INITIALIZATION) |
| + << "NavigationSimulator::Start should only be called once."; |
| + state_ = STARTED; |
| + |
| + if (IsBrowserSideNavigationEnabled()) { |
| + render_frame_host_->SendRendererInitiatedNavigationRequest(navigation_url_, |
| + true); |
| + NavigationRequest* request = |
| + render_frame_host_->frame_tree_node()->navigation_request(); |
| + DCHECK(request); |
| + handle_ = request->navigation_handle(); |
| + } else { |
| + render_frame_host_->OnMessageReceived( |
| + FrameHostMsg_DidStartLoading(render_frame_host_->GetRoutingID(), true)); |
| + render_frame_host_->OnMessageReceived(FrameHostMsg_DidStartProvisionalLoad( |
| + render_frame_host_->GetRoutingID(), navigation_url_, |
| + std::vector<GURL>(), base::TimeTicks::Now())); |
| + handle_ = render_frame_host_->navigation_handle(); |
| + handle_->WillStartRequest( |
| + "GET", scoped_refptr<content::ResourceRequestBodyImpl>(), referrer_, |
| + true /* user_gesture */, transition_, false /* is_external_protocol */, |
| + REQUEST_CONTEXT_TYPE_LOCATION, |
| + blink::WebMixedContentContextType::NotMixedContent, |
| + base::Callback<void(NavigationThrottle::ThrottleCheckResult)>()); |
| + } |
| + |
| + CHECK(handle_); |
| + |
| + // Make sure all NavigationThrottles have run. |
| + // TODO(clamy): provide a non auto-advance mode if needed. |
| + while (handle_->state_for_testing() == NavigationHandleImpl::DEFERRING_START) |
| + handle_->Resume(); |
| + |
| + CHECK_EQ(1, num_did_start_navigation_called_); |
| + CHECK_EQ(1, num_will_start_request_called_); |
| +} |
| + |
| +void NavigationSimulatorImpl::Redirect(const GURL& new_url) { |
| + CHECK(state_ == STARTED) << "NavigationSimulatorImpl::Redirect should be " |
| + "called after Start but before Fail or Commit"; |
| + CHECK_EQ(0, num_did_finish_navigation_called_) |
| + << "NavigationSimulatorImpl::Redirect cannot be called after the " |
| + "navigation has finished"; |
| + |
| + navigation_url_ = new_url; |
| + |
| + int previous_num_will_redirect_request_called = |
| + num_will_redirect_request_called_; |
| + int previous_did_redirect_navigation_called = |
| + num_did_redirect_navigation_called_; |
| + |
| + if (IsBrowserSideNavigationEnabled()) { |
| + NavigationRequest* request = |
| + render_frame_host_->frame_tree_node()->navigation_request(); |
| + TestNavigationURLLoader* url_loader = |
| + static_cast<TestNavigationURLLoader*>(request->loader_for_testing()); |
| + CHECK(url_loader); |
| + |
| + net::RedirectInfo redirect_info; |
| + redirect_info.status_code = 302; |
| + redirect_info.new_method = "GET"; |
| + redirect_info.new_url = new_url; |
| + redirect_info.new_first_party_for_cookies = new_url; |
| + redirect_info.new_referrer = referrer_.url.spec(); |
| + redirect_info.new_referrer_policy = |
| + Referrer::ReferrerPolicyForUrlRequest(referrer_); |
| + |
| + url_loader->CallOnRequestRedirected( |
| + redirect_info, scoped_refptr<ResourceResponse>(new ResourceResponse)); |
| + } else { |
| + handle_->WillRedirectRequest( |
| + new_url, "GET", referrer_.url, false /* is_external_protocol */, |
| + scoped_refptr<net::HttpResponseHeaders>(), |
| + net::HttpResponseInfo::ConnectionInfo(), |
| + base::Callback<void(NavigationThrottle::ThrottleCheckResult)>()); |
| + } |
| + |
| + // Make sure all NavigationThrottles have run. |
| + // TODO(clamy): provide a non auto-advance mode if needed. |
| + while (handle_->state_for_testing() == |
| + NavigationHandleImpl::DEFERRING_REDIRECT) { |
| + handle_->Resume(); |
| + } |
| + |
| + CHECK_EQ(previous_num_will_redirect_request_called + 1, |
| + num_will_redirect_request_called_); |
| + CHECK_EQ(previous_did_redirect_navigation_called + 1, |
| + num_did_redirect_navigation_called_); |
| +} |
| + |
| +void NavigationSimulatorImpl::Commit() { |
| + CHECK_EQ(STARTED, state_) << "NavigationSimulatorImpl::Commit can only be " |
| + "called once, and should be called after Start " |
| + "has been called"; |
| + CHECK_EQ(0, num_did_finish_navigation_called_) |
| + << "NavigationSimulatorImpl::Commit cannot be called after the " |
| + "navigation has finished"; |
| + |
| + if (IsBrowserSideNavigationEnabled() && |
| + render_frame_host_->frame_tree_node()->navigation_request()) { |
| + render_frame_host_->PrepareForCommit(); |
| + } |
| + |
| + // Call NavigationHandle::WillProcessResponse if needed. |
| + if (handle_->state_for_testing() < |
| + NavigationHandleImpl::WILL_PROCESS_RESPONSE) { |
| + handle_->WillProcessResponse( |
| + render_frame_host_, scoped_refptr<net::HttpResponseHeaders>(), |
| + net::HttpResponseInfo::ConnectionInfo(), SSLStatus(), GlobalRequestID(), |
| + false /* should_replace_current_entry */, false /* is_download */, |
| + false /* is_stream */, base::Closure(), |
| + base::Callback<void(NavigationThrottle::ThrottleCheckResult)>()); |
| + } |
| + |
| + // Make sure all NavigationThrottles have run. |
| + // TODO(clamy): provide a non auto-advance mode if needed. |
| + while (handle_->state_for_testing() == |
| + NavigationHandleImpl::DEFERRING_RESPONSE) { |
| + handle_->Resume(); |
| + } |
| + |
| + CHECK_EQ(1, num_will_process_response_called_); |
| + CHECK_EQ(1, num_ready_to_commit_called_); |
| + |
| + // Update the RenderFrameHost now that we know which RenderFrameHost will |
| + // commit the navigation. |
| + TestRenderFrameHost* new_render_frame_host = |
| + static_cast<TestRenderFrameHost*>(handle_->GetRenderFrameHost()); |
| + if (!IsBrowserSideNavigationEnabled() && |
| + new_render_frame_host != render_frame_host_) { |
| + CHECK(handle_->is_transferring()); |
| + // Simulate the renderer transfer. |
| + new_render_frame_host->OnMessageReceived(FrameHostMsg_DidStartLoading( |
| + new_render_frame_host->GetRoutingID(), true)); |
| + new_render_frame_host->OnMessageReceived( |
| + FrameHostMsg_DidStartProvisionalLoad( |
| + new_render_frame_host->GetRoutingID(), navigation_url_, |
| + std::vector<GURL>(), base::TimeTicks::Now())); |
| + CHECK(!handle_->is_transferring()); |
| + } |
| + render_frame_host_ = |
| + static_cast<TestRenderFrameHost*>(handle_->GetRenderFrameHost()); |
|
nasko
2017/02/16 18:47:59
Why do we need to call this again? Wouldn't assign
clamy
2017/02/17 17:34:52
Done.
|
| + |
| + FrameHostMsg_DidCommitProvisionalLoad_Params params; |
| + params.nav_entry_id = 0; |
| + params.url = navigation_url_; |
| + params.origin = url::Origin(navigation_url_); |
| + params.transition = transition_; |
| + params.should_update_history = true; |
| + params.did_create_new_entry = |
| + render_frame_host_->GetParent() || |
| + render_frame_host_->frame_tree_node()->has_committed_real_load(); |
| + params.gesture = NavigationGestureUser; |
| + params.contents_mime_type = "text/html"; |
| + params.method = "GET"; |
| + params.http_status_code = 200; |
| + params.socket_address.set_host("2001:db8::1"); |
| + params.socket_address.set_port(80); |
| + params.history_list_was_cleared = false; |
| + params.original_request_url = navigation_url_; |
| + params.was_within_same_page = false; |
| + params.page_state = |
| + PageState::CreateForTesting(navigation_url_, false, nullptr, nullptr); |
| + |
| + render_frame_host_->SendNavigateWithParams(¶ms); |
| + |
| + state_ = FINISHED; |
| + |
| + CHECK_EQ(1, num_did_finish_navigation_called_); |
| +} |
| + |
| +void NavigationSimulatorImpl::Fail(int error_code) { |
| + CHECK_EQ(STARTED, state_) << "NavigationSimulatorImpl::Fail can only be " |
| + "called once, and should be called after Start " |
| + "has been called"; |
| + CHECK_EQ(0, num_did_finish_navigation_called_) |
| + << "NavigationSimulatorImpl::Fail cannot be called after the " |
| + "navigation has finished"; |
| + |
| + state_ = FAILED; |
| + |
| + bool should_result_in_error_page = error_code != net::ERR_ABORTED; |
| + if (IsBrowserSideNavigationEnabled()) { |
| + NavigationRequest* request = |
| + render_frame_host_->frame_tree_node()->navigation_request(); |
| + CHECK(request); |
| + TestNavigationURLLoader* url_loader = |
| + static_cast<TestNavigationURLLoader*>(request->loader_for_testing()); |
| + CHECK(url_loader); |
| + url_loader->SimulateError(error_code); |
| + } else { |
| + FrameHostMsg_DidFailProvisionalLoadWithError_Params error_params; |
| + error_params.error_code = error_code; |
| + error_params.url = navigation_url_; |
| + render_frame_host_->OnMessageReceived( |
| + FrameHostMsg_DidFailProvisionalLoadWithError( |
| + render_frame_host_->GetRoutingID(), error_params)); |
| + if (!should_result_in_error_page) { |
| + render_frame_host_->OnMessageReceived( |
| + FrameHostMsg_DidStopLoading(render_frame_host_->GetRoutingID())); |
| + } |
| + } |
| + |
| + if (IsBrowserSideNavigationEnabled()) { |
| + CHECK_EQ(1, num_ready_to_commit_called_); |
| + if (should_result_in_error_page) { |
| + // Update the RenderFrameHost now that we know which RenderFrameHost will |
| + // commit the error page. |
| + render_frame_host_ = |
| + static_cast<TestRenderFrameHost*>(handle_->GetRenderFrameHost()); |
| + } |
| + } |
| + |
| + if (should_result_in_error_page) |
| + CHECK_EQ(0, num_did_finish_navigation_called_); |
| + else |
| + CHECK_EQ(1, num_did_finish_navigation_called_); |
| +} |
| + |
| +void NavigationSimulatorImpl::CommitErrorPage() { |
| + CHECK_EQ(FAILED, state_) |
| + << "NavigationSimulatorImpl::CommitErrorPage can only be " |
| + "called once, and should be called after Fail " |
| + "has been called"; |
| + CHECK_EQ(0, num_did_finish_navigation_called_) |
| + << "NavigationSimulatorImpl::CommitErrorPage cannot be called after the " |
| + "navigation has finished"; |
| + |
| + GURL error_url = GURL(kUnreachableWebDataURL); |
| + render_frame_host_->OnMessageReceived(FrameHostMsg_DidStartProvisionalLoad( |
| + render_frame_host_->GetRoutingID(), error_url, std::vector<GURL>(), |
| + base::TimeTicks::Now())); |
| + FrameHostMsg_DidCommitProvisionalLoad_Params params; |
| + params.nav_entry_id = 0; |
| + params.did_create_new_entry = true; |
| + params.url = navigation_url_; |
| + params.transition = transition_; |
| + params.was_within_same_page = false; |
| + params.url_is_unreachable = true; |
| + params.page_state = |
| + PageState::CreateForTesting(navigation_url_, false, nullptr, nullptr); |
| + render_frame_host_->SendNavigateWithParams(¶ms); |
| + |
| + state_ = FINISHED; |
| + |
| + CHECK_EQ(1, num_did_finish_navigation_called_); |
| +} |
| + |
| +void NavigationSimulatorImpl::CommitSamePage() { |
| + CHECK_EQ(INITIALIZATION, state_) |
| + << "NavigationSimulatorImpl::CommitErrorPage should be the only " |
| + "navigation event function called on the NavigationSimulator"; |
| + |
| + render_frame_host_->OnMessageReceived( |
| + FrameHostMsg_DidStartLoading(render_frame_host_->GetRoutingID(), false)); |
| + |
| + FrameHostMsg_DidCommitProvisionalLoad_Params params; |
| + params.nav_entry_id = 0; |
| + params.url = navigation_url_; |
| + params.origin = url::Origin(navigation_url_); |
| + params.transition = transition_; |
| + params.should_update_history = true; |
|
nasko
2017/02/16 18:47:59
Is this set to true for same page?
clamy
2017/02/17 17:34:52
I think so. See https://cs.chromium.org/chromium/s
|
| + params.did_create_new_entry = |
|
nasko
2017/02/16 18:47:59
Why would we create a new entry for same page navi
clamy
2017/02/17 17:34:52
Done.
|
| + render_frame_host_->GetParent() || |
| + render_frame_host_->frame_tree_node()->has_committed_real_load(); |
| + params.gesture = NavigationGestureUser; |
| + params.contents_mime_type = "text/html"; |
| + params.method = "GET"; |
| + params.http_status_code = 200; |
| + params.socket_address.set_host("2001:db8::1"); |
| + params.socket_address.set_port(80); |
| + params.history_list_was_cleared = false; |
| + params.original_request_url = navigation_url_; |
| + params.was_within_same_page = true; |
| + params.page_state = |
| + PageState::CreateForTesting(navigation_url_, false, nullptr, nullptr); |
| + |
| + render_frame_host_->SendNavigateWithParams(¶ms); |
| + |
| + render_frame_host_->OnMessageReceived( |
| + FrameHostMsg_DidStopLoading(render_frame_host_->GetRoutingID())); |
| + |
| + state_ = FINISHED; |
| + |
| + CHECK_EQ(1, num_did_start_navigation_called_); |
| + CHECK_EQ(0, num_will_start_request_called_); |
| + CHECK_EQ(0, num_will_process_response_called_); |
| + CHECK_EQ(0, num_ready_to_commit_called_); |
| + CHECK_EQ(1, num_did_finish_navigation_called_); |
| +} |
| + |
| +void NavigationSimulatorImpl::SetTransition(ui::PageTransition transition) { |
| + CHECK_EQ(INITIALIZATION, state_) |
| + << "The transition cannot be set after the navigation has started"; |
| + transition_ = transition; |
| +} |
| + |
| +void NavigationSimulatorImpl::SetReferrer(const Referrer& referrer) { |
| + CHECK_LE(state_, STARTED) << "The referrer cannot be set after the " |
| + "navigation has committed or has failed"; |
| + referrer_ = referrer; |
| +} |
| + |
| +void NavigationSimulatorImpl::DidStartNavigation( |
| + NavigationHandle* navigation_handle) { |
| + // Check if this navigation is the one we're simulating. |
| + if (state_ != STARTED) |
| + return; |
| + |
| + if (navigation_handle->GetURL() != navigation_url_) |
| + return; |
| + |
| + NavigationHandleImpl* handle = |
| + static_cast<NavigationHandleImpl*>(navigation_handle); |
| + |
| + if (handle->frame_tree_node() != render_frame_host_->frame_tree_node()) |
| + return; |
| + |
| + num_did_start_navigation_called_++; |
| + |
| + // Add a throttle to count NavigationThrottle calls count. |
| + handle->RegisterThrottleForTesting( |
| + base::MakeUnique<NavigationThrottleCallCounter>( |
| + handle, base::Bind(&NavigationSimulatorImpl::OnWillStartRequest, |
| + weak_factory_.GetWeakPtr()), |
| + base::Bind(&NavigationSimulatorImpl::OnWillRedirectRequest, |
| + weak_factory_.GetWeakPtr()), |
| + base::Bind(&NavigationSimulatorImpl::OnWillProcessResponse, |
| + weak_factory_.GetWeakPtr()))); |
| +} |
| + |
| +void NavigationSimulatorImpl::DidRedirectNavigation( |
| + NavigationHandle* navigation_handle) { |
| + if (navigation_handle == handle_) |
| + num_did_redirect_navigation_called_++; |
| +} |
| + |
| +void NavigationSimulatorImpl::ReadyToCommitNavigation( |
| + NavigationHandle* navigation_handle) { |
| + if (navigation_handle == handle_) |
| + num_ready_to_commit_called_++; |
| +} |
| + |
| +void NavigationSimulatorImpl::DidFinishNavigation( |
| + NavigationHandle* navigation_handle) { |
| + if (navigation_handle == handle_) |
| + num_did_finish_navigation_called_++; |
| +} |
| + |
| +void NavigationSimulatorImpl::OnWillStartRequest() { |
| + num_will_start_request_called_++; |
| +} |
| + |
| +void NavigationSimulatorImpl::OnWillRedirectRequest() { |
| + num_will_redirect_request_called_++; |
| +} |
| + |
| +void NavigationSimulatorImpl::OnWillProcessResponse() { |
| + num_will_process_response_called_++; |
| +} |
| + |
| +} // namespace content |