Chromium Code Reviews| Index: content/public/test/navigation_simulator.cc |
| diff --git a/content/public/test/navigation_simulator.cc b/content/public/test/navigation_simulator.cc |
| index 9cc1399bcf7da6050a8b26b6c019fdb51174dec9..309378dbb79bd4fbaabaefd513eef6dda1393000 100644 |
| --- a/content/public/test/navigation_simulator.cc |
| +++ b/content/public/test/navigation_simulator.cc |
| @@ -4,7 +4,9 @@ |
| #include "content/public/test/navigation_simulator.h" |
| -#include "content/browser/frame_host/navigation_handle_impl.h" |
| +#include "base/bind.h" |
| +#include "base/memory/ptr_util.h" |
| +#include "base/run_loop.h" |
| #include "content/browser/frame_host/navigation_request.h" |
| #include "content/common/frame_messages.h" |
| #include "content/public/browser/navigation_throttle.h" |
| @@ -61,6 +63,7 @@ void NavigationSimulator::NavigateAndCommitFromDocument( |
| NavigationSimulator simulator( |
| original_url, static_cast<TestRenderFrameHost*>(render_frame_host)); |
| simulator.Commit(); |
| + simulator.WaitForThrottleChecksComplete(); |
| } |
| // static |
| @@ -101,7 +104,7 @@ NavigationSimulator::NavigationSimulator(const GURL& original_url, |
| NavigationSimulator::~NavigationSimulator() {} |
| -void NavigationSimulator::Start() { |
| +NavigationThrottle::ThrottleCheckResult NavigationSimulator::Start() { |
| CHECK(state_ == INITIALIZATION) |
| << "NavigationSimulator::Start should only be called once."; |
| state_ = STARTED; |
| @@ -123,7 +126,9 @@ void NavigationSimulator::Start() { |
| render_frame_host_->GetRoutingID(), common_params, begin_params)); |
| NavigationRequest* request = |
| render_frame_host_->frame_tree_node()->navigation_request(); |
| - DCHECK(request); |
| + // The request failed synchronously. |
| + if (!request) |
| + return last_throttle_check_result_.value(); |
| DCHECK_EQ(handle_, request->navigation_handle()); |
| } else { |
| render_frame_host_->OnMessageReceived( |
| @@ -131,27 +136,32 @@ void NavigationSimulator::Start() { |
| render_frame_host_->OnMessageReceived(FrameHostMsg_DidStartProvisionalLoad( |
| render_frame_host_->GetRoutingID(), navigation_url_, |
| std::vector<GURL>(), base::TimeTicks::Now())); |
| + DCHECK(handle_); |
| DCHECK_EQ(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)>()); |
| + GenerateCompleteCallback()); |
| } |
| - |
| CHECK(handle_); |
| + return RunOrStoreThrottleCompleteCallback(base::Bind( |
| + &NavigationSimulator::FinishStart, weak_factory_.GetWeakPtr())); |
| +} |
| - // 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(); |
| - |
| +void NavigationSimulator::FinishStart( |
| + NavigationThrottle::ThrottleCheckResult result) { |
| CHECK_EQ(1, num_did_start_navigation_called_); |
| - CHECK_EQ(1, num_will_start_request_called_); |
| + if (result == NavigationThrottle::PROCEED) { |
| + CHECK_EQ(1, num_will_start_request_called_); |
| + } else { |
| + state_ = FAILED; |
| + } |
| } |
| -void NavigationSimulator::Redirect(const GURL& new_url) { |
| +NavigationThrottle::ThrottleCheckResult NavigationSimulator::Redirect( |
| + const GURL& new_url) { |
| CHECK(state_ <= STARTED) << "NavigationSimulator::Redirect should be " |
| "called before Fail or Commit"; |
| CHECK_EQ(0, num_did_finish_navigation_called_) |
| @@ -159,7 +169,7 @@ void NavigationSimulator::Redirect(const GURL& new_url) { |
| "navigation has finished"; |
| if (state_ == INITIALIZATION) |
| - Start(); |
| + StartAndExpectToProceed(); |
| navigation_url_ = new_url; |
| @@ -168,9 +178,12 @@ void NavigationSimulator::Redirect(const GURL& new_url) { |
| int previous_did_redirect_navigation_called = |
| num_did_redirect_navigation_called_; |
| + last_throttle_check_result_.reset(); |
| if (IsBrowserSideNavigationEnabled()) { |
| NavigationRequest* request = |
| render_frame_host_->frame_tree_node()->navigation_request(); |
| + request->set_on_checks_complete_callback_for_testing( |
| + GenerateCompleteCallback()); |
| TestNavigationURLLoader* url_loader = |
| static_cast<TestNavigationURLLoader*>(request->loader_for_testing()); |
| CHECK(url_loader); |
| @@ -190,24 +203,29 @@ void NavigationSimulator::Redirect(const GURL& new_url) { |
| handle_->WillRedirectRequest( |
| new_url, "GET", referrer_.url, false /* is_external_protocol */, |
| scoped_refptr<net::HttpResponseHeaders>(), |
| - net::HttpResponseInfo::ConnectionInfo(), |
| - base::Callback<void(NavigationThrottle::ThrottleCheckResult)>()); |
| + net::HttpResponseInfo::ConnectionInfo(), GenerateCompleteCallback()); |
| } |
| + return RunOrStoreThrottleCompleteCallback(base::Bind( |
| + &NavigationSimulator::FinishRedirect, weak_factory_.GetWeakPtr(), |
| + previous_num_will_redirect_request_called, |
| + previous_did_redirect_navigation_called)); |
| +} |
| - // 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(); |
| +void NavigationSimulator::FinishRedirect( |
| + int previous_num_will_redirect_request_called, |
| + int previous_did_redirect_navigation_called, |
| + NavigationThrottle::ThrottleCheckResult result) { |
| + if (result == NavigationThrottle::PROCEED) { |
| + 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_); |
| + } else { |
| + state_ = FAILED; |
| } |
| - |
| - 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 NavigationSimulator::Commit() { |
| +NavigationThrottle::ThrottleCheckResult NavigationSimulator::Commit() { |
| CHECK_LE(state_, STARTED) << "NavigationSimulator::Commit can only be " |
| "called once, and cannot be called after " |
| "NavigationSimulator::Fail"; |
| @@ -216,31 +234,39 @@ void NavigationSimulator::Commit() { |
| "navigation has finished"; |
| if (state_ == INITIALIZATION) |
| - Start(); |
| + StartAndExpectToProceed(); |
| - if (IsBrowserSideNavigationEnabled() && |
| - render_frame_host_->frame_tree_node()->navigation_request()) { |
| - render_frame_host_->PrepareForCommit(); |
| + last_throttle_check_result_.reset(); |
| + if (IsBrowserSideNavigationEnabled()) { |
| + NavigationRequest* request = |
| + render_frame_host_->frame_tree_node()->navigation_request(); |
| + if (request) { |
| + request->set_on_checks_complete_callback_for_testing( |
| + GenerateCompleteCallback()); |
| + render_frame_host_->PrepareForCommit(); |
| + } |
| } |
| // Call NavigationHandle::WillProcessResponse if needed. |
| - if (handle_->state_for_testing() < |
| - NavigationHandleImpl::WILL_PROCESS_RESPONSE) { |
| + // Note that the handle's state can be CANCELING if a throttle cancelled it |
| + // synchronously in PrepareForCommit. |
| + if (handle_->state_for_testing() < NavigationHandleImpl::CANCELING) { |
| 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)>()); |
| + false /* is_stream */, base::Closure(), GenerateCompleteCallback()); |
| } |
| + return RunOrStoreThrottleCompleteCallback(base::Bind( |
| + &NavigationSimulator::FinishCommit, weak_factory_.GetWeakPtr())); |
| +} |
| - // 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(); |
| +void NavigationSimulator::FinishCommit( |
| + NavigationThrottle::ThrottleCheckResult result) { |
| + if (result != NavigationThrottle::PROCEED) { |
| + state_ = FAILED; |
| + return; |
| } |
| - |
| CHECK_EQ(1, num_will_process_response_called_); |
| CHECK_EQ(1, num_ready_to_commit_called_); |
| @@ -310,7 +336,7 @@ void NavigationSimulator::Fail(int error_code) { |
| "navigation has finished"; |
| if (state_ == INITIALIZATION) |
| - Start(); |
| + StartAndExpectToProceed(); |
| state_ = FAILED; |
| @@ -446,6 +472,17 @@ void NavigationSimulator::SetReferrer(const Referrer& referrer) { |
| referrer_ = referrer; |
| } |
| +NavigationThrottle::ThrottleCheckResult |
| +NavigationSimulator::WaitForThrottleChecksComplete() { |
| + if (!last_throttle_check_result_) { |
| + base::RunLoop run_loop; |
| + throttle_checks_wait_closure_ = run_loop.QuitClosure(); |
| + run_loop.Run(); |
| + throttle_checks_wait_closure_.Reset(); |
| + } |
| + return last_throttle_check_result_.value(); |
| +} |
| + |
| void NavigationSimulator::DidStartNavigation( |
| NavigationHandle* navigation_handle) { |
| // Check if this navigation is the one we're simulating. |
| @@ -475,6 +512,15 @@ void NavigationSimulator::DidStartNavigation( |
| weak_factory_.GetWeakPtr()), |
| base::Bind(&NavigationSimulator::OnWillProcessResponse, |
| weak_factory_.GetWeakPtr()))); |
| + |
| + if (IsBrowserSideNavigationEnabled()) { |
|
clamy
2017/03/06 15:22:15
What do you think of the previous suggestion to st
Charlie Harrison
2017/03/06 20:18:43
Done.
|
| + NavigationRequest* request = |
| + render_frame_host_->frame_tree_node()->navigation_request(); |
| + if (request) { |
| + request->set_on_checks_complete_callback_for_testing( |
| + GenerateCompleteCallback()); |
| + } |
| + } |
| } |
| void NavigationSimulator::DidRedirectNavigation( |
| @@ -507,4 +553,55 @@ void NavigationSimulator::OnWillProcessResponse() { |
| num_will_process_response_called_++; |
| } |
| +// These methods are a little subtle. Overall, the design constraints are: |
| +// 1. Synchronous throttle checks should not cause subsequent |
| +// WaitForThrottleChecksComplete to block. |
| +// |
| +// 2. |complete_callback_| is called after all synchronous code is executed in |
| +// response to the throttle checks. This point is important because in some |
| +// cases (e.g. cancellation), the OnThrottleChecksComplete callback that the |
| +// NavigationRequest/NavigationHandle holds on to will be called before things |
| +// like navigation tear down and DidFinishNavigation. This code ensures that |
| +// |complete_callback_| is called after that occurs, either synchronously in |
| +// RunOrStoreThrottleCompleteCallback, or asynchronously in |
| +// OnThrottleChecksComplete. |
| +NavigationThrottle::ThrottleCheckResult |
| +NavigationSimulator::RunOrStoreThrottleCompleteCallback( |
| + ThrottleChecksFinishedCallback finish_callback) { |
| + // If completed the checks synchronously, call Finish* here. Else, save the |
| + // callback to call asynchronously when the checks finish. |
| + if (last_throttle_check_result_) { |
| + finish_callback.Run(last_throttle_check_result_.value()); |
| + return last_throttle_check_result_.value(); |
| + } |
| + complete_callback_ = std::move(finish_callback); |
| + return NavigationThrottle::DEFER; |
| +} |
| + |
| +void NavigationSimulator::OnThrottleChecksComplete( |
| + NavigationThrottle::ThrottleCheckResult result) { |
| + // For synchronous throttle checks, set this here, and read from it downstream |
| + // in RunOrStoreThrottleCompleteCallback. |
| + DCHECK(!last_throttle_check_result_); |
| + last_throttle_check_result_ = result; |
| + if (!complete_callback_) |
| + return; |
| + BrowserThread::PostTaskAndReply( |
| + BrowserThread::UI, FROM_HERE, |
| + base::Bind(std::move(complete_callback_), result), |
| + throttle_checks_wait_closure_); |
| +} |
| + |
| +ThrottleChecksFinishedCallback NavigationSimulator::GenerateCompleteCallback() { |
| + return base::Bind(&NavigationSimulator::OnThrottleChecksComplete, |
| + weak_factory_.GetWeakPtr()); |
| +} |
| + |
| +void NavigationSimulator::StartAndExpectToProceed() { |
| + NavigationThrottle::ThrottleCheckResult result = Start(); |
| + if (result == NavigationThrottle::DEFER) |
| + result = WaitForThrottleChecksComplete(); |
| + CHECK_EQ(NavigationThrottle::PROCEED, result); |
| +} |
| + |
| } // namespace content |