1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/test/navigation_simulator_impl.h"
6
7 #include <utility>
8 #include "base/bind.h"
9 #include "base/debug/stack_trace.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/run_loop.h"
12 #include "content/browser/renderer_host/debug_urls.h"
13 #include "content/browser/renderer_host/frame_tree_node.h"
14 #include "content/browser/renderer_host/navigation_entry_impl.h"
15 #include "content/browser/renderer_host/navigation_request.h"
16 #include "content/browser/renderer_host/render_frame_host_impl.h"
17 #include "content/browser/web_contents/web_contents_impl.h"
18 #include "content/common/content_navigation_policy.h"
19 #include "content/common/frame_messages.h"
20 #include "content/common/navigation_params.h"
21 #include "content/common/navigation_params_utils.h"
22 #include "content/public/browser/web_contents.h"
23 #include "content/public/common/navigation_policy.h"
24 #include "content/public/common/url_utils.h"
25 #include "content/test/test_navigation_url_loader.h"
26 #include "content/test/test_render_frame_host.h"
27 #include "content/test/test_web_contents.h"
28 #include "ipc/ipc_message.h"
29 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
30 #include "mojo/public/cpp/bindings/pending_remote.h"
31 #include "net/base/load_flags.h"
32 #include "net/url_request/redirect_info.h"
33 #include "services/metrics/public/cpp/ukm_recorder.h"
34
35 namespace content {
36
37 namespace {
38
39 class NavigationThrottleCallbackRunner : public NavigationThrottle {
40 public:
NavigationThrottleCallbackRunner(NavigationHandle * handle,base::OnceClosure on_will_start_request,const base::RepeatingClosure & on_will_redirect_request,base::OnceClosure on_will_fail_request,base::OnceClosure on_will_process_response)41 NavigationThrottleCallbackRunner(
42 NavigationHandle* handle,
43 base::OnceClosure on_will_start_request,
44 const base::RepeatingClosure& on_will_redirect_request,
45 base::OnceClosure on_will_fail_request,
46 base::OnceClosure on_will_process_response)
47 : NavigationThrottle(handle),
48 on_will_start_request_(std::move(on_will_start_request)),
49 on_will_redirect_request_(on_will_redirect_request),
50 on_will_fail_request_(std::move(on_will_fail_request)),
51 on_will_process_response_(std::move(on_will_process_response)) {}
52
WillStartRequest()53 NavigationThrottle::ThrottleCheckResult WillStartRequest() override {
54 std::move(on_will_start_request_).Run();
55 return NavigationThrottle::PROCEED;
56 }
57
WillRedirectRequest()58 NavigationThrottle::ThrottleCheckResult WillRedirectRequest() override {
59 on_will_redirect_request_.Run();
60 return NavigationThrottle::PROCEED;
61 }
62
WillFailRequest()63 NavigationThrottle::ThrottleCheckResult WillFailRequest() override {
64 std::move(on_will_fail_request_).Run();
65 return NavigationThrottle::PROCEED;
66 }
67
WillProcessResponse()68 NavigationThrottle::ThrottleCheckResult WillProcessResponse() override {
69 std::move(on_will_process_response_).Run();
70 return NavigationThrottle::PROCEED;
71 }
72
GetNameForLogging()73 const char* GetNameForLogging() override {
74 return "NavigationThrottleCallbackRunner";
75 }
76
77 private:
78 base::OnceClosure on_will_start_request_;
79 base::RepeatingClosure on_will_redirect_request_;
80 base::OnceClosure on_will_fail_request_;
81 base::OnceClosure on_will_process_response_;
82 };
83
84 int64_t g_unique_identifier = 0;
85
GetFrameTreeNodeForPendingEntry(WebContentsImpl * contents)86 FrameTreeNode* GetFrameTreeNodeForPendingEntry(WebContentsImpl* contents) {
87 NavigationEntryImpl* pending_entry =
88 contents->GetController().GetPendingEntry();
89 int frame_tree_node_id = pending_entry->frame_tree_node_id();
90 FrameTree* frame_tree = contents->GetFrameTree();
91 if (frame_tree_node_id == FrameTreeNode::kFrameTreeNodeInvalidId)
92 return frame_tree->root();
93 return frame_tree->FindByID(frame_tree_node_id);
94 }
95
96 } // namespace
97
98 // static
NavigateAndCommitFromBrowser(WebContents * web_contents,const GURL & url)99 RenderFrameHost* NavigationSimulator::NavigateAndCommitFromBrowser(
100 WebContents* web_contents,
101 const GURL& url) {
102 auto simulator =
103 NavigationSimulatorImpl::CreateBrowserInitiated(url, web_contents);
104 simulator->Commit();
105 return simulator->GetFinalRenderFrameHost();
106 }
107
108 // static
Reload(WebContents * web_contents)109 RenderFrameHost* NavigationSimulator::Reload(WebContents* web_contents) {
110 NavigationEntry* entry =
111 web_contents->GetController().GetLastCommittedEntry();
112 CHECK(entry);
113 auto simulator = NavigationSimulatorImpl::CreateBrowserInitiated(
114 entry->GetURL(), web_contents);
115 simulator->SetReloadType(ReloadType::NORMAL);
116 simulator->Commit();
117 return simulator->GetFinalRenderFrameHost();
118 }
119
120 // static
GoBack(WebContents * web_contents)121 RenderFrameHost* NavigationSimulator::GoBack(WebContents* web_contents) {
122 return GoToOffset(web_contents, -1);
123 }
124
125 // static
GoForward(WebContents * web_contents)126 RenderFrameHost* NavigationSimulator::GoForward(WebContents* web_contents) {
127 return GoToOffset(web_contents, 1);
128 }
129
130 // static
GoToOffset(WebContents * web_contents,int offset)131 RenderFrameHost* NavigationSimulator::GoToOffset(WebContents* web_contents,
132 int offset) {
133 auto simulator =
134 NavigationSimulatorImpl::CreateHistoryNavigation(offset, web_contents);
135 simulator->Commit();
136 return simulator->GetFinalRenderFrameHost();
137 }
138
139 // static
NavigateAndCommitFromDocument(const GURL & original_url,RenderFrameHost * render_frame_host)140 RenderFrameHost* NavigationSimulator::NavigateAndCommitFromDocument(
141 const GURL& original_url,
142 RenderFrameHost* render_frame_host) {
143 auto simulator = NavigationSimulator::CreateRendererInitiated(
144 original_url, render_frame_host);
145 simulator->Commit();
146 return simulator->GetFinalRenderFrameHost();
147 }
148
149 // static
NavigateAndFailFromBrowser(WebContents * web_contents,const GURL & url,int net_error_code)150 RenderFrameHost* NavigationSimulator::NavigateAndFailFromBrowser(
151 WebContents* web_contents,
152 const GURL& url,
153 int net_error_code) {
154 auto simulator =
155 NavigationSimulator::CreateBrowserInitiated(url, web_contents);
156 simulator->Fail(net_error_code);
157 if (net_error_code == net::ERR_ABORTED)
158 return nullptr;
159 simulator->CommitErrorPage();
160 return simulator->GetFinalRenderFrameHost();
161 }
162
163 // static
ReloadAndFail(WebContents * web_contents,int net_error_code)164 RenderFrameHost* NavigationSimulator::ReloadAndFail(WebContents* web_contents,
165 int net_error_code) {
166 NavigationEntry* entry =
167 web_contents->GetController().GetLastCommittedEntry();
168 CHECK(entry);
169 auto simulator = NavigationSimulator::CreateBrowserInitiated(entry->GetURL(),
170 web_contents);
171 simulator->SetReloadType(ReloadType::NORMAL);
172 simulator->Fail(net_error_code);
173 if (net_error_code == net::ERR_ABORTED)
174 return nullptr;
175 simulator->CommitErrorPage();
176 return simulator->GetFinalRenderFrameHost();
177 }
178
179 // static
GoBackAndFail(WebContents * web_contents,int net_error_code)180 RenderFrameHost* NavigationSimulator::GoBackAndFail(WebContents* web_contents,
181 int net_error_code) {
182 return GoToOffsetAndFail(web_contents, -1, net_error_code);
183 }
184
185 // static
GoToOffsetAndFail(WebContents * web_contents,int offset,int net_error_code)186 RenderFrameHost* NavigationSimulator::GoToOffsetAndFail(
187 WebContents* web_contents,
188 int offset,
189 int net_error_code) {
190 auto simulator =
191 NavigationSimulator::CreateHistoryNavigation(offset, web_contents);
192 simulator->Fail(net_error_code);
193 if (net_error_code == net::ERR_ABORTED)
194 return nullptr;
195 simulator->CommitErrorPage();
196 return simulator->GetFinalRenderFrameHost();
197 }
198
199 // static
NavigateAndFailFromDocument(const GURL & original_url,int net_error_code,RenderFrameHost * render_frame_host)200 RenderFrameHost* NavigationSimulator::NavigateAndFailFromDocument(
201 const GURL& original_url,
202 int net_error_code,
203 RenderFrameHost* render_frame_host) {
204 auto simulator = NavigationSimulator::CreateRendererInitiated(
205 original_url, render_frame_host);
206 simulator->Fail(net_error_code);
207 if (net_error_code == net::ERR_ABORTED)
208 return nullptr;
209 simulator->CommitErrorPage();
210 return simulator->GetFinalRenderFrameHost();
211 }
212
213 // static
214 std::unique_ptr<NavigationSimulator>
CreateBrowserInitiated(const GURL & original_url,WebContents * web_contents)215 NavigationSimulator::CreateBrowserInitiated(const GURL& original_url,
216 WebContents* web_contents) {
217 return NavigationSimulatorImpl::CreateBrowserInitiated(original_url,
218 web_contents);
219 }
220
221 // static
222 std::unique_ptr<NavigationSimulatorImpl>
CreateBrowserInitiated(const GURL & original_url,WebContents * web_contents)223 NavigationSimulatorImpl::CreateBrowserInitiated(const GURL& original_url,
224 WebContents* web_contents) {
225 return std::unique_ptr<NavigationSimulatorImpl>(new NavigationSimulatorImpl(
226 original_url, true /* browser_initiated */,
227 static_cast<WebContentsImpl*>(web_contents), nullptr));
228 }
229
230 // static
231 std::unique_ptr<NavigationSimulator>
CreateHistoryNavigation(int offset,WebContents * web_contents)232 NavigationSimulator::CreateHistoryNavigation(int offset,
233 WebContents* web_contents) {
234 return NavigationSimulatorImpl::CreateHistoryNavigation(offset, web_contents);
235 }
236
237 // static
238 std::unique_ptr<NavigationSimulatorImpl>
CreateHistoryNavigation(int offset,WebContents * web_contents)239 NavigationSimulatorImpl::CreateHistoryNavigation(int offset,
240 WebContents* web_contents) {
241 auto simulator =
242 NavigationSimulatorImpl::CreateBrowserInitiated(GURL(), web_contents);
243 simulator->SetSessionHistoryOffset(offset);
244 return simulator;
245 }
246
247 // static
248 std::unique_ptr<NavigationSimulator>
CreateRendererInitiated(const GURL & original_url,RenderFrameHost * render_frame_host)249 NavigationSimulator::CreateRendererInitiated(
250 const GURL& original_url,
251 RenderFrameHost* render_frame_host) {
252 return NavigationSimulatorImpl::CreateRendererInitiated(original_url,
253 render_frame_host);
254 }
255
256 // static
257 std::unique_ptr<NavigationSimulatorImpl>
CreateRendererInitiated(const GURL & original_url,RenderFrameHost * render_frame_host)258 NavigationSimulatorImpl::CreateRendererInitiated(
259 const GURL& original_url,
260 RenderFrameHost* render_frame_host) {
261 return std::unique_ptr<NavigationSimulatorImpl>(new NavigationSimulatorImpl(
262 original_url, false /* browser_initiated */,
263 static_cast<WebContentsImpl*>(
264 WebContents::FromRenderFrameHost(render_frame_host)),
265 static_cast<TestRenderFrameHost*>(render_frame_host)));
266 }
267
268 // static
CreateFromPending(WebContents * contents)269 std::unique_ptr<NavigationSimulator> NavigationSimulator::CreateFromPending(
270 WebContents* contents) {
271 return NavigationSimulatorImpl::CreateFromPending(contents);
272 }
273
274 // static
275 std::unique_ptr<NavigationSimulatorImpl>
CreateFromPending(WebContents * contents)276 NavigationSimulatorImpl::CreateFromPending(WebContents* contents) {
277 WebContentsImpl* contents_impl = static_cast<WebContentsImpl*>(contents);
278
279 FrameTreeNode* frame_tree_node =
280 GetFrameTreeNodeForPendingEntry(contents_impl);
281 return NavigationSimulatorImpl::CreateFromPendingInFrame(frame_tree_node);
282 }
283
284 // static
285 std::unique_ptr<NavigationSimulatorImpl>
CreateFromPendingInFrame(FrameTreeNode * frame_tree_node)286 NavigationSimulatorImpl::CreateFromPendingInFrame(
287 FrameTreeNode* frame_tree_node) {
288 CHECK(frame_tree_node);
289 TestRenderFrameHost* test_frame_host =
290 static_cast<TestRenderFrameHost*>(frame_tree_node->current_frame_host());
291 CHECK(test_frame_host);
292 NavigationRequest* request = frame_tree_node->navigation_request();
293 // It is possible to not have a NavigationRequest in the frame tree node if
294 // it did not go to the network (such as about:blank). In that case it is
295 // already in the RenderFrameHost.
296 if (!request)
297 request = test_frame_host->navigation_requests().begin()->second.get();
298 CHECK(request);
299
300 // Simulate the BeforeUnload completion callback if needed.
301 if (request->state() == NavigationRequest::WAITING_FOR_RENDERER_RESPONSE)
302 test_frame_host->SimulateBeforeUnloadCompleted(true /* proceed */);
303
304 auto simulator = base::WrapUnique(new NavigationSimulatorImpl(
305 GURL(), request->browser_initiated(),
306 WebContentsImpl::FromFrameTreeNode(frame_tree_node), test_frame_host));
307 simulator->frame_tree_node_ = frame_tree_node;
308 simulator->InitializeFromStartedRequest(request);
309 return simulator;
310 }
311
NavigationSimulatorImpl(const GURL & original_url,bool browser_initiated,WebContentsImpl * web_contents,TestRenderFrameHost * render_frame_host)312 NavigationSimulatorImpl::NavigationSimulatorImpl(
313 const GURL& original_url,
314 bool browser_initiated,
315 WebContentsImpl* web_contents,
316 TestRenderFrameHost* render_frame_host)
317 : WebContentsObserver(web_contents),
318 web_contents_(web_contents),
319 render_frame_host_(render_frame_host),
320 frame_tree_node_(render_frame_host
321 ? render_frame_host->frame_tree_node()
322 : web_contents->GetMainFrame()->frame_tree_node()),
323 request_(nullptr),
324 original_url_(original_url),
325 navigation_url_(original_url),
326 initial_method_("GET"),
327 browser_initiated_(browser_initiated),
328 referrer_(blink::mojom::Referrer::New()),
329 transition_(browser_initiated ? ui::PAGE_TRANSITION_TYPED
330 : ui::PAGE_TRANSITION_LINK),
331 contents_mime_type_("text/html"),
332 load_url_params_(nullptr) {
333 net::IPAddress address;
334 CHECK(address.AssignFromIPLiteral("2001:db8::1"));
335 remote_endpoint_ = net::IPEndPoint(address, 80);
336
337 // For renderer-initiated navigation, the RenderFrame must be initialized. Do
338 // it if it hasn't happened yet.
339 if (!browser_initiated)
340 render_frame_host->InitializeRenderFrameIfNeeded();
341
342 if (render_frame_host && render_frame_host->GetParent()) {
343 if (!render_frame_host->frame_tree_node()->has_committed_real_load())
344 transition_ = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
345 else
346 transition_ = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
347 }
348
349 mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
350 stub_interface_provider;
351 interface_provider_receiver_ =
352 stub_interface_provider.InitWithNewPipeAndPassReceiver();
353 browser_interface_broker_receiver_ =
354 mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>()
355 .InitWithNewPipeAndPassReceiver();
356 }
357
~NavigationSimulatorImpl()358 NavigationSimulatorImpl::~NavigationSimulatorImpl() {}
359
SetIsPostWithId(int64_t post_id)360 void NavigationSimulatorImpl::SetIsPostWithId(int64_t post_id) {
361 post_id_ = post_id;
362 SetMethod("POST");
363 }
364
InitializeFromStartedRequest(NavigationRequest * request)365 void NavigationSimulatorImpl::InitializeFromStartedRequest(
366 NavigationRequest* request) {
367 CHECK(request);
368 request_ = request;
369 CHECK(request_->IsNavigationStarted());
370 CHECK_EQ(web_contents_, request_->GetDelegate());
371 CHECK(render_frame_host_);
372 CHECK_EQ(frame_tree_node_, request_->frame_tree_node());
373 state_ = STARTED;
374 original_url_ = request->commit_params().original_url;
375 navigation_url_ = request_->GetURL();
376 // |remote_endpoint_| cannot be inferred from the request.
377 // |initial_method_| cannot be set after the request has started.
378 browser_initiated_ = request_->browser_initiated();
379 // |same_document_| should always be false here.
380 referrer_ = request_->common_params().referrer.Clone();
381 transition_ = request_->GetPageTransition();
382 // |reload_type_| cannot be set after the request has started.
383 // |session_history_offset_| cannot be set after the request has started.
384 has_user_gesture_ = request_->HasUserGesture();
385 // |contents_mime_type_| cannot be inferred from the request.
386
387 // Add a throttle to count NavigationThrottle calls count. Bump
388 // num_did_start_navigation to account for the fact that the navigation handle
389 // has already been created.
390 num_did_start_navigation_called_++;
391 RegisterTestThrottle(request);
392 PrepareCompleteCallbackOnRequest();
393 }
394
RegisterTestThrottle(NavigationRequest * request)395 void NavigationSimulatorImpl::RegisterTestThrottle(NavigationRequest* request) {
396 request->RegisterThrottleForTesting(
397 std::make_unique<NavigationThrottleCallbackRunner>(
398 request,
399 base::BindOnce(&NavigationSimulatorImpl::OnWillStartRequest,
400 weak_factory_.GetWeakPtr()),
401 base::BindRepeating(&NavigationSimulatorImpl::OnWillRedirectRequest,
402 weak_factory_.GetWeakPtr()),
403 base::BindOnce(&NavigationSimulatorImpl::OnWillFailRequest,
404 weak_factory_.GetWeakPtr()),
405 base::BindOnce(&NavigationSimulatorImpl::OnWillProcessResponse,
406 weak_factory_.GetWeakPtr())));
407 }
408
Start()409 void NavigationSimulatorImpl::Start() {
410 CHECK(state_ == INITIALIZATION || state_ == WAITING_BEFORE_UNLOAD)
411 << "NavigationSimulatorImpl::Start should only be called once.";
412
413 if (browser_initiated_) {
414 if (!SimulateBrowserInitiatedStart())
415 return;
416 } else {
417 if (!SimulateRendererInitiatedStart())
418 return;
419 }
420 state_ = STARTED;
421
422 CHECK(request_);
423 if (IsRendererDebugURL(navigation_url_))
424 return;
425
426 if (same_document_ || !IsURLHandledByNetworkStack(navigation_url_) ||
427 navigation_url_.IsAboutBlank()) {
428 CHECK_EQ(1, num_did_start_navigation_called_);
429 return;
430 }
431
432 MaybeWaitForThrottleChecksComplete(base::BindOnce(
433 &NavigationSimulatorImpl::StartComplete, weak_factory_.GetWeakPtr()));
434 }
435
StartComplete()436 void NavigationSimulatorImpl::StartComplete() {
437 CHECK_EQ(1, num_did_start_navigation_called_);
438 if (GetLastThrottleCheckResult().action() == NavigationThrottle::PROCEED) {
439 CHECK_EQ(1, num_will_start_request_called_);
440 } else {
441 state_ = FAILED;
442 }
443 }
444
Redirect(const GURL & new_url)445 void NavigationSimulatorImpl::Redirect(const GURL& new_url) {
446 CHECK_LE(state_, STARTED) << "NavigationSimulatorImpl::Redirect should be "
447 "called before Fail or Commit";
448 CHECK_EQ(0, num_did_finish_navigation_called_)
449 << "NavigationSimulatorImpl::Redirect cannot be called after the "
450 "navigation has finished";
451
452 if (state_ < STARTED) {
453 Start();
454 if (state_ == FAILED)
455 return;
456 }
457
458 navigation_url_ = new_url;
459
460 int previous_num_will_redirect_request_called =
461 num_will_redirect_request_called_;
462 int previous_did_redirect_navigation_called =
463 num_did_redirect_navigation_called_;
464
465 PrepareCompleteCallbackOnRequest();
466 NavigationRequest* request = frame_tree_node_->navigation_request();
467 CHECK(request) << "Trying to redirect a navigation that does not go to the "
468 "network stack.";
469
470 TestNavigationURLLoader* url_loader =
471 static_cast<TestNavigationURLLoader*>(request->loader_for_testing());
472 CHECK(url_loader);
473
474 net::RedirectInfo redirect_info;
475 redirect_info.status_code = 302;
476 redirect_info.new_method = "GET";
477 redirect_info.new_url = new_url;
478 redirect_info.new_site_for_cookies = net::SiteForCookies::FromUrl(new_url);
479 redirect_info.new_referrer = referrer_->url.spec();
480 redirect_info.new_referrer_policy =
481 Referrer::ReferrerPolicyForUrlRequest(referrer_->policy);
482 auto response = network::mojom::URLResponseHead::New();
483 response->connection_info = http_connection_info_;
484 response->ssl_info = ssl_info_;
485
486 url_loader->CallOnRequestRedirected(redirect_info, std::move(response));
487
488 MaybeWaitForThrottleChecksComplete(base::BindOnce(
489 &NavigationSimulatorImpl::RedirectComplete, weak_factory_.GetWeakPtr(),
490 previous_num_will_redirect_request_called,
491 previous_did_redirect_navigation_called));
492 }
493
RedirectComplete(int previous_num_will_redirect_request_called,int previous_did_redirect_navigation_called)494 void NavigationSimulatorImpl::RedirectComplete(
495 int previous_num_will_redirect_request_called,
496 int previous_did_redirect_navigation_called) {
497 if (GetLastThrottleCheckResult().action() == NavigationThrottle::PROCEED) {
498 CHECK_EQ(previous_num_will_redirect_request_called + 1,
499 num_will_redirect_request_called_);
500 CHECK_EQ(previous_did_redirect_navigation_called + 1,
501 num_did_redirect_navigation_called_);
502 } else {
503 state_ = FAILED;
504 }
505 }
506
ReadyToCommit()507 void NavigationSimulatorImpl::ReadyToCommit() {
508 CHECK_LE(state_, STARTED)
509 << "NavigationSimulatorImpl::ReadyToCommit can only "
510 "be called once, and cannot be called after "
511 "NavigationSimulatorImpl::Fail";
512 CHECK_EQ(0, num_did_finish_navigation_called_)
513 << "NavigationSimulatorImpl::ReadyToCommit cannot be called after the "
514 "navigation has finished";
515
516 if (state_ < STARTED) {
517 if (block_invoking_before_unload_completed_callback_ &&
518 state_ == WAITING_BEFORE_UNLOAD) {
519 // The user should have simulated the BeforeUnloadCompleted by themselves.
520 // Finish the initialization and skip the Start simulation.
521 InitializeFromStartedRequest(request_);
522 } else {
523 Start();
524 if (state_ == FAILED)
525 return;
526 }
527 }
528
529 if (!response_headers_) {
530 response_headers_ =
531 base::MakeRefCounted<net::HttpResponseHeaders>(std::string());
532 }
533 response_headers_->SetHeader("Content-Type", contents_mime_type_);
534 PrepareCompleteCallbackOnRequest();
535 if (frame_tree_node_->navigation_request()) {
536 static_cast<TestRenderFrameHost*>(frame_tree_node_->current_frame_host())
537 ->PrepareForCommitDeprecatedForNavigationSimulator(
538 remote_endpoint_, was_fetched_via_cache_,
539 is_signed_exchange_inner_response_, http_connection_info_,
540 ssl_info_, response_headers_);
541 }
542
543 // Synchronous failure can cause the navigation to finish here.
544 if (!request_) {
545 state_ = FAILED;
546 return;
547 }
548
549 bool needs_throttle_checks = !same_document_ &&
550 !navigation_url_.IsAboutBlank() &&
551 IsURLHandledByNetworkStack(navigation_url_);
552 auto complete_closure =
553 base::BindOnce(&NavigationSimulatorImpl::ReadyToCommitComplete,
554 weak_factory_.GetWeakPtr(), needs_throttle_checks);
555 if (needs_throttle_checks) {
556 MaybeWaitForThrottleChecksComplete(std::move(complete_closure));
557 return;
558 }
559 std::move(complete_closure).Run();
560 }
561
ReadyToCommitComplete(bool ran_throttles)562 void NavigationSimulatorImpl::ReadyToCommitComplete(bool ran_throttles) {
563 if (ran_throttles) {
564 if (GetLastThrottleCheckResult().action() != NavigationThrottle::PROCEED) {
565 state_ = FAILED;
566 return;
567 }
568 CHECK_EQ(1, num_will_process_response_called_);
569 CHECK_EQ(1, num_ready_to_commit_called_);
570 }
571
572 request_id_ = request_->GetGlobalRequestID();
573
574 // Update the RenderFrameHost now that we know which RenderFrameHost will
575 // commit the navigation.
576 render_frame_host_ =
577 static_cast<TestRenderFrameHost*>(request_->GetRenderFrameHost());
578 state_ = READY_TO_COMMIT;
579 }
580
Commit()581 void NavigationSimulatorImpl::Commit() {
582 CHECK_LE(state_, READY_TO_COMMIT)
583 << "NavigationSimulatorImpl::Commit can only "
584 "be called once, and cannot be called "
585 "after NavigationSimulatorImpl::Fail";
586 CHECK_EQ(0, num_did_finish_navigation_called_)
587 << "NavigationSimulatorImpl::Commit cannot be called after the "
588 "navigation "
589 "has finished";
590
591 if (state_ < READY_TO_COMMIT) {
592 ReadyToCommit();
593 if (state_ == FAILED || state_ == FINISHED)
594 return;
595 }
596
597 // Keep a pointer to the current RenderFrameHost that may be pending deletion
598 // after commit.
599 RenderFrameHostImpl* previous_rfh =
600 render_frame_host_->frame_tree_node()->current_frame_host();
601
602 // RenderDocument: Do not dispatch UnloadACK if the navigation was committed
603 // in the same SiteInstance. This has already been dispatched during the
604 // navigation in the renderer process.
605 if (previous_rfh->GetSiteInstance() == render_frame_host_->GetSiteInstance())
606 drop_unload_ack_ = true;
607
608 // If the frame is not alive we do not displatch Unload ACK. CommitPending()
609 // may be called immediately and delete the old RenderFrameHost, so we need to
610 // record that now while we can still access the object.
611 if (!previous_rfh->IsRenderFrameLive())
612 drop_unload_ack_ = true;
613
614 if (same_document_) {
615 interface_provider_receiver_.reset();
616 browser_interface_broker_receiver_.reset();
617 }
618
619 auto params = BuildDidCommitProvisionalLoadParams(
620 same_document_ /* same_document */, false /* failed_navigation */);
621 render_frame_host_->SimulateCommitProcessed(
622 request_, std::move(params), std::move(interface_provider_receiver_),
623 std::move(browser_interface_broker_receiver_), same_document_);
624
625 if (previous_rfh)
626 SimulateUnloadCompletionCallbackForPreviousFrameIfNeeded(previous_rfh);
627
628 loading_scenario_ =
629 TestRenderFrameHost::LoadingScenario::NewDocumentNavigation;
630 state_ = FINISHED;
631 if (!keep_loading_)
632 StopLoading();
633
634 if (!IsRendererDebugURL(navigation_url_))
635 CHECK_EQ(1, num_did_finish_navigation_called_);
636 }
637
AbortCommit()638 void NavigationSimulatorImpl::AbortCommit() {
639 CHECK_LE(state_, FAILED)
640 << "NavigationSimulatorImpl::AbortCommit cannot be called after "
641 "NavigationSimulatorImpl::Commit or "
642 "NavigationSimulatorImpl::CommitErrorPage.";
643 if (state_ < READY_TO_COMMIT) {
644 ReadyToCommit();
645 if (state_ == FINISHED)
646 return;
647 }
648
649 CHECK(render_frame_host_)
650 << "NavigationSimulatorImpl::AbortCommit can only be "
651 "called for navigations that commit.";
652 render_frame_host_->AbortCommit(request_);
653
654 state_ = FINISHED;
655 StopLoading();
656
657 CHECK_EQ(1, num_did_finish_navigation_called_);
658 }
659
AbortFromRenderer()660 void NavigationSimulatorImpl::AbortFromRenderer() {
661 CHECK(!browser_initiated_)
662 << "NavigationSimulatorImpl::AbortFromRenderer cannot be called for "
663 "browser-initiated navigation.";
664 CHECK_LE(state_, FAILED)
665 << "NavigationSimulatorImpl::AbortFromRenderer cannot be called after "
666 "NavigationSimulatorImpl::Commit or "
667 "NavigationSimulatorImpl::CommitErrorPage.";
668
669 was_aborted_ = true;
670 request_->RendererAbortedNavigationForTesting();
671 state_ = FINISHED;
672
673 CHECK_EQ(1, num_did_finish_navigation_called_);
674 }
675
Fail(int error_code)676 void NavigationSimulatorImpl::Fail(int error_code) {
677 CHECK_LE(state_, STARTED) << "NavigationSimulatorImpl::Fail can only be "
678 "called once, and cannot be called after "
679 "NavigationSimulatorImpl::ReadyToCommit";
680 CHECK_EQ(0, num_did_finish_navigation_called_)
681 << "NavigationSimulatorImpl::Fail cannot be called after the "
682 "navigation has finished";
683 CHECK(!IsRendererDebugURL(navigation_url_));
684
685 if (state_ == INITIALIZATION)
686 Start();
687
688 state_ = FAILED;
689
690 PrepareCompleteCallbackOnRequest();
691 CHECK(request_);
692 TestNavigationURLLoader* url_loader =
693 static_cast<TestNavigationURLLoader*>(request_->loader_for_testing());
694 CHECK(url_loader);
695 network::URLLoaderCompletionStatus status(error_code);
696 status.resolve_error_info = resolve_error_info_;
697 status.ssl_info = ssl_info_;
698 url_loader->SimulateErrorWithStatus(status);
699
700 auto complete_closure =
701 base::BindOnce(&NavigationSimulatorImpl::FailComplete,
702 weak_factory_.GetWeakPtr(), error_code);
703 if (error_code != net::ERR_ABORTED) {
704 MaybeWaitForThrottleChecksComplete(std::move(complete_closure));
705 return;
706 }
707 std::move(complete_closure).Run();
708 }
709
FailComplete(int error_code)710 void NavigationSimulatorImpl::FailComplete(int error_code) {
711 bool should_result_in_error_page = error_code != net::ERR_ABORTED;
712 if (error_code != net::ERR_ABORTED) {
713 NavigationThrottle::ThrottleCheckResult result =
714 GetLastThrottleCheckResult();
715 if (result.action() == NavigationThrottle::CANCEL ||
716 result.action() == NavigationThrottle::CANCEL_AND_IGNORE) {
717 should_result_in_error_page = false;
718 }
719 }
720
721 if (should_result_in_error_page) {
722 // TODO(clamy): Check that ReadyToCommit has been called once, once the test
723 // architecture of NavigationRequest vs NavigationHandle has been clarified.
724 // Currently, when auto-advance is off, this function will be called before
725 // NavigationRequest::CommitErrorPage which is the one that triggers the
726 // call to observers.
727 CHECK_EQ(0, num_did_finish_navigation_called_);
728 // Update the RenderFrameHost now that we know which RenderFrameHost will
729 // commit the error page.
730 render_frame_host_ =
731 static_cast<TestRenderFrameHost*>(request_->GetRenderFrameHost());
732 }
733 }
734
CommitErrorPage()735 void NavigationSimulatorImpl::CommitErrorPage() {
736 CHECK_EQ(FAILED, state_)
737 << "NavigationSimulatorImpl::CommitErrorPage can only be "
738 "called once, and should be called after Fail "
739 "has been called";
740 CHECK_EQ(0, num_did_finish_navigation_called_)
741 << "NavigationSimulatorImpl::CommitErrorPage cannot be called after the "
742 "navigation has finished";
743
744 // Keep a pointer to the current RenderFrameHost that may be pending deletion
745 // after commit.
746 // RenderDocument: The |previous_rfh| might also be immediately deleted after
747 // commit, because it has already run its unload handler.
748 RenderFrameHostImpl* previous_rfh =
749 render_frame_host_->frame_tree_node()->current_frame_host();
750
751 // RenderDocument: Do not dispatch UnloadACK if the navigation was committed
752 // in the same SiteInstance. This has already been dispatched during the
753 // navigation in the renderer process.
754 if (previous_rfh->GetSiteInstance() == render_frame_host_->GetSiteInstance())
755 drop_unload_ack_ = true;
756
757 // If the frame is not alive we do not displatch Unload ACK. CommitPending()
758 // may be called immediately and delete the old RenderFrameHost, so we need to
759 // record that now while we can still access the object.
760 if (!previous_rfh->IsRenderFrameLive())
761 drop_unload_ack_ = true;
762
763 auto params = BuildDidCommitProvisionalLoadParams(
764 false /* same_document */, true /* failed_navigation */);
765 render_frame_host_->SimulateCommitProcessed(
766 request_, std::move(params), std::move(interface_provider_receiver_),
767 std::move(browser_interface_broker_receiver_), false /* same_document */);
768
769 SimulateUnloadCompletionCallbackForPreviousFrameIfNeeded(previous_rfh);
770
771 state_ = FINISHED;
772 if (!keep_loading_)
773 StopLoading();
774
775 CHECK_EQ(1, num_did_finish_navigation_called_);
776 }
777
CommitSameDocument()778 void NavigationSimulatorImpl::CommitSameDocument() {
779 if (!browser_initiated_) {
780 CHECK_EQ(INITIALIZATION, state_)
781 << "NavigationSimulatorImpl::CommitSameDocument should be the only "
782 "navigation event function called on the NavigationSimulatorImpl";
783 } else {
784 // This function is intended for same document navigations initiating from
785 // the renderer. For regular same document navigations simply use Commit().
786 Commit();
787 return;
788 }
789
790 auto params = BuildDidCommitProvisionalLoadParams(
791 true /* same_document */, false /* failed_navigation */);
792
793 interface_provider_receiver_.reset();
794 browser_interface_broker_receiver_.reset();
795
796 render_frame_host_->SimulateCommitProcessed(
797 request_, std::move(params),
798 mojo::NullReceiver() /* interface_provider_receiver_ */,
799 mojo::NullReceiver() /* browser_interface_broker_receiver */,
800 true /* same_document */);
801
802 // Same-document commits should never hit network-related stages of committing
803 // a navigation.
804 CHECK_EQ(0, num_will_start_request_called_);
805 CHECK_EQ(0, num_will_process_response_called_);
806 CHECK_EQ(0, num_ready_to_commit_called_);
807
808 if (num_did_finish_navigation_called_ == 0) {
809 // Fail the navigation if it results in a process kill (e.g. see
810 // NavigatorTestWithBrowserSideNavigation.CrossSiteClaimWithinPage test).
811 state_ = FAILED;
812 return;
813 }
814 loading_scenario_ =
815 TestRenderFrameHost::LoadingScenario::kSameDocumentNavigation;
816 state_ = FINISHED;
817 if (!keep_loading_)
818 StopLoading();
819
820 CHECK_EQ(1, num_did_start_navigation_called_);
821 CHECK_EQ(1, num_did_finish_navigation_called_);
822 }
823
SetInitiatorFrame(RenderFrameHost * initiator_frame_host)824 void NavigationSimulatorImpl::SetInitiatorFrame(
825 RenderFrameHost* initiator_frame_host) {
826 // Browser-initiated navigations are not associated with an initiator frame.
827 CHECK(!browser_initiated_);
828 CHECK(initiator_frame_host);
829
830 // TODO(https://crbug.com/1072790): Support cross-process initiators here by
831 // using NavigationRequest::CreateBrowserInitiated() (like
832 // RenderFrameProxyHost does) for the navigation.
833 CHECK_EQ(render_frame_host_->GetProcess(), initiator_frame_host->GetProcess())
834 << "The initiator frame must belong to the same process as the frame you "
835 "are navigating";
836 initiator_frame_host_ = initiator_frame_host;
837 }
838
SetTransition(ui::PageTransition transition)839 void NavigationSimulatorImpl::SetTransition(ui::PageTransition transition) {
840 if (frame_tree_node_ && !frame_tree_node_->IsMainFrame()) {
841 // Subframe case. The subframe page transition is only set at commit time in
842 // the navigation code, so it can be modified later in time.
843 CHECK(PageTransitionCoreTypeIs(transition,
844 ui::PAGE_TRANSITION_AUTO_SUBFRAME) ||
845 PageTransitionCoreTypeIs(transition,
846 ui::PAGE_TRANSITION_MANUAL_SUBFRAME))
847 << "The transition type is not appropriate for a subframe";
848 } else {
849 CHECK_EQ(INITIALIZATION, state_)
850 << "The transition cannot be set after the navigation has started";
851 CHECK_EQ(ReloadType::NONE, reload_type_)
852 << "The transition cannot be specified for reloads";
853 CHECK_EQ(0, session_history_offset_)
854 << "The transition cannot be specified for back/forward navigations";
855 }
856 transition_ = transition;
857 }
858
SetHasUserGesture(bool has_user_gesture)859 void NavigationSimulatorImpl::SetHasUserGesture(bool has_user_gesture) {
860 CHECK_EQ(INITIALIZATION, state_) << "The has_user_gesture parameter cannot "
861 "be set after the navigation has started";
862 has_user_gesture_ = has_user_gesture;
863 }
864
SetReloadType(ReloadType reload_type)865 void NavigationSimulatorImpl::SetReloadType(ReloadType reload_type) {
866 CHECK_EQ(INITIALIZATION, state_) << "The reload_type parameter cannot "
867 "be set after the navigation has started";
868 CHECK(browser_initiated_) << "The reload_type parameter can only be set for "
869 "browser-intiated navigations";
870 CHECK_EQ(0, session_history_offset_)
871 << "The reload_type parameter cannot be set for "
872 "session history navigations";
873 reload_type_ = reload_type;
874 if (reload_type_ != ReloadType::NONE)
875 transition_ = ui::PAGE_TRANSITION_RELOAD;
876 }
877
SetMethod(const std::string & method)878 void NavigationSimulatorImpl::SetMethod(const std::string& method) {
879 CHECK_EQ(INITIALIZATION, state_) << "The method parameter cannot "
880 "be set after the navigation has started";
881 initial_method_ = method;
882 }
883
SetIsFormSubmission(bool is_form_submission)884 void NavigationSimulatorImpl::SetIsFormSubmission(bool is_form_submission) {
885 CHECK_EQ(INITIALIZATION, state_) << "The form submission parameter cannot "
886 "be set after the navigation has started";
887 is_form_submission_ = is_form_submission;
888 }
889
SetWasInitiatedByLinkClick(bool was_initiated_by_link_click)890 void NavigationSimulatorImpl::SetWasInitiatedByLinkClick(
891 bool was_initiated_by_link_click) {
892 CHECK_EQ(INITIALIZATION, state_) << "The form submission parameter cannot "
893 "be set after the navigation has started";
894 was_initiated_by_link_click_ = was_initiated_by_link_click;
895 }
896
SetReferrer(blink::mojom::ReferrerPtr referrer)897 void NavigationSimulatorImpl::SetReferrer(blink::mojom::ReferrerPtr referrer) {
898 CHECK_LE(state_, STARTED) << "The referrer cannot be set after the "
899 "navigation has committed or has failed";
900 referrer_ = std::move(referrer);
901 }
902
SetSocketAddress(const net::IPEndPoint & remote_endpoint)903 void NavigationSimulatorImpl::SetSocketAddress(
904 const net::IPEndPoint& remote_endpoint) {
905 CHECK_LE(state_, STARTED) << "The socket address cannot be set after the "
906 "navigation has committed or failed";
907 remote_endpoint_ = remote_endpoint;
908 }
909
SetWasFetchedViaCache(bool was_fetched_via_cache)910 void NavigationSimulatorImpl::SetWasFetchedViaCache(
911 bool was_fetched_via_cache) {
912 CHECK_LE(state_, STARTED) << "The was_fetched_via_cache flag cannot be set "
913 "after the navigation has committed or failed";
914 was_fetched_via_cache_ = was_fetched_via_cache;
915 }
916
SetIsSignedExchangeInnerResponse(bool is_signed_exchange_inner_response)917 void NavigationSimulatorImpl::SetIsSignedExchangeInnerResponse(
918 bool is_signed_exchange_inner_response) {
919 CHECK_LE(state_, STARTED) << "The signed exchange flag cannot be set after "
920 "the navigation has committed or failed";
921 is_signed_exchange_inner_response_ = is_signed_exchange_inner_response;
922 }
923
SetInterfaceProviderReceiver(mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver)924 void NavigationSimulatorImpl::SetInterfaceProviderReceiver(
925 mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver) {
926 CHECK_LE(state_, STARTED) << "The InterfaceProvider cannot be set "
927 "after the navigation has committed or failed";
928 CHECK(receiver.is_valid());
929 interface_provider_receiver_ = std::move(receiver);
930 }
931
SetContentsMimeType(const std::string & contents_mime_type)932 void NavigationSimulatorImpl::SetContentsMimeType(
933 const std::string& contents_mime_type) {
934 CHECK_LE(state_, STARTED) << "The contents mime type cannot be set after the "
935 "navigation has committed or failed";
936 contents_mime_type_ = contents_mime_type;
937 }
938
SetResponseHeaders(scoped_refptr<net::HttpResponseHeaders> response_headers)939 void NavigationSimulatorImpl::SetResponseHeaders(
940 scoped_refptr<net::HttpResponseHeaders> response_headers) {
941 CHECK_LE(state_, STARTED) << "The response headers cannot be set after the "
942 "navigation has committed or failed";
943 response_headers_ = response_headers;
944 }
945
SetLoadURLParams(NavigationController::LoadURLParams * load_url_params)946 void NavigationSimulatorImpl::SetLoadURLParams(
947 NavigationController::LoadURLParams* load_url_params) {
948 load_url_params_ = load_url_params;
949
950 // Make sure the internal attributes of NavigationSimulatorImpl match the
951 // LoadURLParams that is going to be sent.
952 referrer_ = blink::mojom::Referrer::New(load_url_params->referrer.url,
953 load_url_params->referrer.policy);
954 transition_ = load_url_params->transition_type;
955 }
956
SetAutoAdvance(bool auto_advance)957 void NavigationSimulatorImpl::SetAutoAdvance(bool auto_advance) {
958 auto_advance_ = auto_advance;
959 }
960
SetResolveErrorInfo(const net::ResolveErrorInfo & resolve_error_info)961 void NavigationSimulatorImpl::SetResolveErrorInfo(
962 const net::ResolveErrorInfo& resolve_error_info) {
963 resolve_error_info_ = resolve_error_info;
964 }
965
SetSSLInfo(const net::SSLInfo & ssl_info)966 void NavigationSimulatorImpl::SetSSLInfo(const net::SSLInfo& ssl_info) {
967 ssl_info_ = ssl_info;
968 }
969
970 NavigationThrottle::ThrottleCheckResult
GetLastThrottleCheckResult()971 NavigationSimulatorImpl::GetLastThrottleCheckResult() {
972 return last_throttle_check_result_.value();
973 }
974
GetNavigationHandle()975 NavigationRequest* NavigationSimulatorImpl::GetNavigationHandle() {
976 CHECK_GE(state_, STARTED);
977 return request_;
978 }
979
GetGlobalRequestID()980 content::GlobalRequestID NavigationSimulatorImpl::GetGlobalRequestID() {
981 CHECK_GT(state_, STARTED) << "The GlobalRequestID is not available until "
982 "after the navigation has completed "
983 "WillProcessResponse";
984 return request_id_;
985 }
986
BrowserInitiatedStartAndWaitBeforeUnload()987 void NavigationSimulatorImpl::BrowserInitiatedStartAndWaitBeforeUnload() {
988 if (reload_type_ != ReloadType::NONE) {
989 web_contents_->GetController().Reload(reload_type_,
990 false /*check_for_repost */);
991 } else if (session_history_offset_) {
992 web_contents_->GetController().GoToOffset(session_history_offset_);
993 } else {
994 if (load_url_params_) {
995 web_contents_->GetController().LoadURLWithParams(*load_url_params_);
996 load_url_params_ = nullptr;
997 } else {
998 NavigationController::LoadURLParams load_url_params(navigation_url_);
999 load_url_params.referrer = Referrer(*referrer_);
1000 load_url_params.transition_type = transition_;
1001 if (initial_method_ == "POST")
1002 load_url_params.load_type = NavigationController::LOAD_TYPE_HTTP_POST;
1003
1004 web_contents_->GetController().LoadURLWithParams(load_url_params);
1005 }
1006 }
1007
1008 frame_tree_node_ = GetFrameTreeNodeForPendingEntry(web_contents_);
1009 CHECK(frame_tree_node_);
1010 render_frame_host_ =
1011 static_cast<TestRenderFrameHost*>(frame_tree_node_->current_frame_host());
1012
1013 // The navigation url might have been rewritten by the NavigationController.
1014 // Update it.
1015 navigation_url_ = web_contents_->GetController().GetPendingEntry()->GetURL();
1016
1017 state_ = WAITING_BEFORE_UNLOAD;
1018 }
1019
DidStartNavigation(NavigationHandle * navigation_handle)1020 void NavigationSimulatorImpl::DidStartNavigation(
1021 NavigationHandle* navigation_handle) {
1022 // Check if this navigation is the one we're simulating.
1023 if (request_)
1024 return;
1025
1026 NavigationRequest* request = NavigationRequest::From(navigation_handle);
1027
1028 if (request->frame_tree_node() != frame_tree_node_)
1029 return;
1030
1031 request_ = request;
1032 num_did_start_navigation_called_++;
1033
1034 // Add a throttle to count NavigationThrottle calls count.
1035 RegisterTestThrottle(request);
1036 PrepareCompleteCallbackOnRequest();
1037 }
1038
DidRedirectNavigation(NavigationHandle * navigation_handle)1039 void NavigationSimulatorImpl::DidRedirectNavigation(
1040 NavigationHandle* navigation_handle) {
1041 if (request_ == navigation_handle)
1042 num_did_redirect_navigation_called_++;
1043 }
1044
ReadyToCommitNavigation(NavigationHandle * navigation_handle)1045 void NavigationSimulatorImpl::ReadyToCommitNavigation(
1046 NavigationHandle* navigation_handle) {
1047 if (request_ && navigation_handle == request_)
1048 num_ready_to_commit_called_++;
1049 }
1050
DidFinishNavigation(NavigationHandle * navigation_handle)1051 void NavigationSimulatorImpl::DidFinishNavigation(
1052 NavigationHandle* navigation_handle) {
1053 NavigationRequest* request = NavigationRequest::From(navigation_handle);
1054 if (request == request_) {
1055 num_did_finish_navigation_called_++;
1056 if (navigation_handle->IsServedFromBackForwardCache()) {
1057 // Back-forward cache navigations commit and finish synchronously, unlike
1058 // all other navigations, which wait for a reply from the renderer.
1059 // The |state_| is normally updated to 'FINISHED' when we simulate a
1060 // renderer reply at the end of the NavigationSimulatorImpl::Commit()
1061 // function, but we have not reached this stage yet.
1062 // Set |state_| to FINISHED to ensure that we would not try to simulate
1063 // navigation commit for the second time.
1064 RenderFrameHostImpl* previous_rfh = RenderFrameHostImpl::FromID(
1065 navigation_handle->GetPreviousRenderFrameHostId());
1066 CHECK(previous_rfh) << "Previous RenderFrameHost should not be destroyed "
1067 "without a Unload_ACK";
1068
1069 // If the frame is not alive we do not displatch Unload ACK.
1070 // CommitPending() may be called immediately and delete the old
1071 // RenderFrameHost, so we need to record that now while we can still
1072 // access the object.
1073 if (!previous_rfh->IsRenderFrameLive())
1074 drop_unload_ack_ = true;
1075 SimulateUnloadCompletionCallbackForPreviousFrameIfNeeded(previous_rfh);
1076 state_ = FINISHED;
1077 }
1078 request_ = nullptr;
1079 if (was_aborted_)
1080 CHECK_EQ(net::ERR_ABORTED, request->GetNetErrorCode());
1081 }
1082 }
1083
OnWillStartRequest()1084 void NavigationSimulatorImpl::OnWillStartRequest() {
1085 num_will_start_request_called_++;
1086 }
1087
OnWillRedirectRequest()1088 void NavigationSimulatorImpl::OnWillRedirectRequest() {
1089 num_will_redirect_request_called_++;
1090 }
1091
OnWillFailRequest()1092 void NavigationSimulatorImpl::OnWillFailRequest() {
1093 num_will_fail_request_called_++;
1094 }
1095
OnWillProcessResponse()1096 void NavigationSimulatorImpl::OnWillProcessResponse() {
1097 num_will_process_response_called_++;
1098 }
1099
SimulateBrowserInitiatedStart()1100 bool NavigationSimulatorImpl::SimulateBrowserInitiatedStart() {
1101 if (state_ == INITIALIZATION)
1102 BrowserInitiatedStartAndWaitBeforeUnload();
1103
1104 // Simulate the BeforeUnload completion callback if needed.
1105 NavigationRequest* request = frame_tree_node_->navigation_request();
1106 if (request &&
1107 request->state() == NavigationRequest::WAITING_FOR_RENDERER_RESPONSE) {
1108 if (block_invoking_before_unload_completed_callback_) {
1109 // Since we do not simulate the BeforeUnloadCompleted, DidStartNavigation
1110 // will not have been called, and |request_| will not be properly set. Do
1111 // it manually.
1112 request_ = request;
1113 return false;
1114 }
1115 render_frame_host_->SimulateBeforeUnloadCompleted(true /* proceed */);
1116 }
1117
1118 // Note: WillStartRequest checks can destroy the request synchronously, or
1119 // this can be a navigation that doesn't need a network request and that was
1120 // passed directly to a RenderFrameHost for commit.
1121 request =
1122 web_contents_->GetMainFrame()->frame_tree_node()->navigation_request();
1123 if (!request) {
1124 if (IsRendererDebugURL(navigation_url_)) {
1125 // We don't create NavigationRequests nor NavigationHandles for a
1126 // navigation to a renderer-debug URL. Instead, the URL is passed to the
1127 // current RenderFrameHost so that the renderer process can handle it.
1128 CHECK(!request_);
1129 CHECK(web_contents_->GetMainFrame()->is_loading());
1130
1131 // A navigation to a renderer-debug URL cannot commit. Simulate the
1132 // renderer process aborting it.
1133 render_frame_host_ =
1134 static_cast<TestRenderFrameHost*>(web_contents_->GetMainFrame());
1135 StopLoading();
1136 state_ = FAILED;
1137 return false;
1138 } else if (request_ &&
1139 web_contents_->GetMainFrame()->navigation_request() ==
1140 request_) {
1141 CHECK(!IsURLHandledByNetworkStack(request_->common_params().url));
1142 return true;
1143 } else if (web_contents_->GetMainFrame()
1144 ->same_document_navigation_request() &&
1145 web_contents_->GetMainFrame()
1146 ->same_document_navigation_request() == request_) {
1147 CHECK(request_->IsSameDocument());
1148 same_document_ = true;
1149 return true;
1150 }
1151 return false;
1152 }
1153
1154 CHECK_EQ(request_, request);
1155 return true;
1156 }
1157
SimulateRendererInitiatedStart()1158 bool NavigationSimulatorImpl::SimulateRendererInitiatedStart() {
1159 mojom::BeginNavigationParamsPtr begin_params =
1160 mojom::BeginNavigationParams::New(
1161 initiator_frame_host_ ? initiator_frame_host_->GetRoutingID()
1162 : MSG_ROUTING_NONE /* initiator_routing_id */,
1163 std::string() /* headers */, net::LOAD_NORMAL,
1164 false /* skip_service_worker */,
1165 blink::mojom::RequestContextType::HYPERLINK,
1166 network::mojom::RequestDestination::kDocument,
1167 blink::WebMixedContentContextType::kBlockable, is_form_submission_,
1168 was_initiated_by_link_click_, GURL() /* searchable_form_url */,
1169 std::string() /* searchable_form_encoding */,
1170 GURL() /* client_side_redirect_url */,
1171 base::nullopt /* detools_initiator_info */,
1172 false /* force_ignore_site_for_cookies */,
1173 nullptr /* trust_token_params */, impression_,
1174 base::TimeTicks() /* renderer_before_unload_start */,
1175 base::TimeTicks() /* renderer_before_unload_end */);
1176 auto common_params = CreateCommonNavigationParams();
1177 common_params->navigation_start = base::TimeTicks::Now();
1178 common_params->url = navigation_url_;
1179 common_params->initiator_origin = url::Origin();
1180 common_params->method = initial_method_;
1181 common_params->referrer = referrer_.Clone();
1182 common_params->transition = transition_;
1183 common_params->navigation_type =
1184 PageTransitionCoreTypeIs(transition_, ui::PAGE_TRANSITION_RELOAD)
1185 ? mojom::NavigationType::RELOAD
1186 : mojom::NavigationType::DIFFERENT_DOCUMENT;
1187 common_params->has_user_gesture = has_user_gesture_;
1188 common_params->initiator_csp_info = mojom::InitiatorCSPInfo::New(
1189 should_check_main_world_csp_,
1190 std::vector<network::mojom::ContentSecurityPolicyPtr>(), nullptr);
1191
1192 mojo::PendingAssociatedRemote<mojom::NavigationClient>
1193 navigation_client_remote;
1194 navigation_client_receiver_ =
1195 navigation_client_remote.InitWithNewEndpointAndPassReceiver();
1196 render_frame_host_->frame_host_receiver_for_testing().impl()->BeginNavigation(
1197 std::move(common_params), std::move(begin_params), mojo::NullRemote(),
1198 std::move(navigation_client_remote), mojo::NullRemote());
1199
1200 NavigationRequest* request =
1201 render_frame_host_->frame_tree_node()->navigation_request();
1202
1203 // The request failed synchronously.
1204 if (!request)
1205 return false;
1206
1207 CHECK_EQ(request_, request);
1208 return true;
1209 }
1210
MaybeWaitForThrottleChecksComplete(base::OnceClosure complete_closure)1211 void NavigationSimulatorImpl::MaybeWaitForThrottleChecksComplete(
1212 base::OnceClosure complete_closure) {
1213 // If last_throttle_check_result_ is set, then throttle checks completed
1214 // synchronously.
1215 if (last_throttle_check_result_) {
1216 std::move(complete_closure).Run();
1217 return;
1218 }
1219
1220 throttle_checks_complete_closure_ = std::move(complete_closure);
1221 if (auto_advance_)
1222 Wait();
1223 }
1224
Wait()1225 void NavigationSimulatorImpl::Wait() {
1226 CHECK(!wait_closure_);
1227 if (!IsDeferred())
1228 return;
1229 base::RunLoop run_loop;
1230 wait_closure_ = run_loop.QuitClosure();
1231 run_loop.Run();
1232 }
1233
OnThrottleChecksComplete(NavigationThrottle::ThrottleCheckResult result)1234 bool NavigationSimulatorImpl::OnThrottleChecksComplete(
1235 NavigationThrottle::ThrottleCheckResult result) {
1236 CHECK(!last_throttle_check_result_);
1237 last_throttle_check_result_ = result;
1238 if (wait_closure_)
1239 std::move(wait_closure_).Run();
1240 if (throttle_checks_complete_closure_)
1241 std::move(throttle_checks_complete_closure_).Run();
1242 return false;
1243 }
1244
PrepareCompleteCallbackOnRequest()1245 void NavigationSimulatorImpl::PrepareCompleteCallbackOnRequest() {
1246 last_throttle_check_result_.reset();
1247 request_->set_complete_callback_for_testing(
1248 base::BindOnce(&NavigationSimulatorImpl::OnThrottleChecksComplete,
1249 base::Unretained(this)));
1250 }
1251
GetFinalRenderFrameHost()1252 RenderFrameHost* NavigationSimulatorImpl::GetFinalRenderFrameHost() {
1253 CHECK_GE(state_, READY_TO_COMMIT);
1254 return render_frame_host_;
1255 }
1256
IsDeferred()1257 bool NavigationSimulatorImpl::IsDeferred() {
1258 return !throttle_checks_complete_closure_.is_null();
1259 }
1260
CheckIfSameDocument()1261 bool NavigationSimulatorImpl::CheckIfSameDocument() {
1262 // This approach to determining whether a navigation is to be treated as
1263 // same document is not robust, as it will not handle pushState type
1264 // navigation. Do not use elsewhere!
1265
1266 // First we need a valid document that is not an error page.
1267 if (!render_frame_host_->GetLastCommittedURL().is_valid() ||
1268 render_frame_host_->last_commit_was_error_page()) {
1269 return false;
1270 }
1271
1272 // Exclude reloads.
1273 if (ui::PageTransitionCoreTypeIs(transition_, ui::PAGE_TRANSITION_RELOAD)) {
1274 return false;
1275 }
1276
1277 // A browser-initiated navigation to the exact same url in the address bar is
1278 // not a same document navigation.
1279 if (browser_initiated_ &&
1280 render_frame_host_->GetLastCommittedURL() == navigation_url_) {
1281 return false;
1282 }
1283
1284 // Finally, the navigation url and the last committed url should match,
1285 // except for the fragment.
1286 GURL url_copy(navigation_url_);
1287 url::Replacements<char> replacements;
1288 replacements.ClearRef();
1289 return url_copy.ReplaceComponents(replacements) ==
1290 render_frame_host_->GetLastCommittedURL().ReplaceComponents(
1291 replacements);
1292 }
1293
DidCreateNewEntry()1294 bool NavigationSimulatorImpl::DidCreateNewEntry() {
1295 if (did_create_new_entry_.has_value())
1296 return did_create_new_entry_.value();
1297 if (ui::PageTransitionCoreTypeIs(transition_,
1298 ui::PAGE_TRANSITION_AUTO_SUBFRAME))
1299 return false;
1300 if (reload_type_ != ReloadType::NONE ||
1301 (request_ && NavigationTypeUtils::IsReload(
1302 request_->common_params().navigation_type))) {
1303 return false;
1304 }
1305 if (session_history_offset_ ||
1306 (request_ && NavigationTypeUtils::IsHistory(
1307 request_->common_params().navigation_type))) {
1308 return false;
1309 }
1310 if (request_ && (request_->common_params().navigation_type ==
1311 mojom::NavigationType::RESTORE ||
1312 request_->common_params().navigation_type ==
1313 mojom::NavigationType::RESTORE_WITH_POST)) {
1314 return false;
1315 }
1316
1317 return true;
1318 }
1319
SetSessionHistoryOffset(int session_history_offset)1320 void NavigationSimulatorImpl::SetSessionHistoryOffset(
1321 int session_history_offset) {
1322 CHECK(session_history_offset);
1323 session_history_offset_ = session_history_offset;
1324 transition_ =
1325 ui::PageTransitionFromInt(transition_ | ui::PAGE_TRANSITION_FORWARD_BACK);
1326 }
1327
set_did_create_new_entry(bool did_create_new_entry)1328 void NavigationSimulatorImpl::set_did_create_new_entry(
1329 bool did_create_new_entry) {
1330 did_create_new_entry_ = did_create_new_entry;
1331 }
1332
set_history_list_was_cleared(bool history_cleared)1333 void NavigationSimulatorImpl::set_history_list_was_cleared(
1334 bool history_cleared) {
1335 history_list_was_cleared_ = history_cleared;
1336 }
1337
1338 std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params>
BuildDidCommitProvisionalLoadParams(bool same_document,bool failed_navigation)1339 NavigationSimulatorImpl::BuildDidCommitProvisionalLoadParams(
1340 bool same_document,
1341 bool failed_navigation) {
1342 std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params> params =
1343 std::make_unique<FrameHostMsg_DidCommitProvisionalLoad_Params>();
1344 params->url = navigation_url_;
1345 params->original_request_url = original_url_;
1346 params->referrer = Referrer(*referrer_);
1347 params->contents_mime_type = contents_mime_type_;
1348 params->transition = transition_;
1349 params->gesture =
1350 has_user_gesture_ ? NavigationGestureUser : NavigationGestureAuto;
1351 params->history_list_was_cleared = history_list_was_cleared_;
1352 params->did_create_new_entry = DidCreateNewEntry();
1353 params->should_replace_current_entry = should_replace_current_entry_;
1354 params->navigation_token = request_
1355 ? request_->commit_params().navigation_token
1356 : base::UnguessableToken::Create();
1357 params->post_id = post_id_;
1358
1359 if (intended_as_new_entry_.has_value())
1360 params->intended_as_new_entry = intended_as_new_entry_.value();
1361
1362 if (failed_navigation) {
1363 // Note: Error pages must commit in a unique origin. So it is left unset.
1364 params->url_is_unreachable = true;
1365 } else {
1366 params->origin = origin_.value_or(url::Origin::Create(navigation_url_));
1367 params->redirects.push_back(navigation_url_);
1368 params->method = request_ ? request_->common_params().method : "GET";
1369 params->http_status_code = 200;
1370 params->should_update_history = true;
1371 }
1372
1373 CHECK(same_document || request_);
1374 params->nav_entry_id = request_ ? request_->nav_entry_id() : 0;
1375
1376 // Simulate Blink assigning a item sequence number and document sequence
1377 // number to the navigation.
1378 params->item_sequence_number = ++g_unique_identifier;
1379 if (same_document) {
1380 FrameNavigationEntry* current_entry =
1381 web_contents_->GetController().GetLastCommittedEntry()->GetFrameEntry(
1382 frame_tree_node_);
1383 params->document_sequence_number =
1384 current_entry->document_sequence_number();
1385 } else {
1386 params->document_sequence_number = ++g_unique_identifier;
1387 }
1388
1389 // Simulate embedding token creation.
1390 if (!same_document)
1391 params->embedding_token = base::UnguessableToken::Create();
1392
1393 params->page_state = page_state_.value_or(
1394 blink::PageState::CreateForTestingWithSequenceNumbers(
1395 navigation_url_, params->item_sequence_number,
1396 params->document_sequence_number));
1397
1398 return params;
1399 }
1400
SetKeepLoading(bool keep_loading)1401 void NavigationSimulatorImpl::SetKeepLoading(bool keep_loading) {
1402 keep_loading_ = keep_loading;
1403 }
1404
StopLoading()1405 void NavigationSimulatorImpl::StopLoading() {
1406 CHECK(render_frame_host_);
1407 render_frame_host_->SimulateLoadingCompleted(loading_scenario_);
1408 }
1409
FailLoading(const GURL & url,int error_code)1410 void NavigationSimulatorImpl::FailLoading(const GURL& url, int error_code) {
1411 CHECK(render_frame_host_);
1412 render_frame_host_->DidFailLoadWithError(url, error_code);
1413 }
1414
1415 void NavigationSimulatorImpl::
SimulateUnloadCompletionCallbackForPreviousFrameIfNeeded(RenderFrameHostImpl * previous_rfh)1416 SimulateUnloadCompletionCallbackForPreviousFrameIfNeeded(
1417 RenderFrameHostImpl* previous_rfh) {
1418 // Do not dispatch FrameHostMsg_Unload_ACK if the navigation was committed in
1419 // the same RenderFrameHost.
1420 if (previous_rfh == render_frame_host_)
1421 return;
1422 if (drop_unload_ack_)
1423 return;
1424 // The previous RenderFrameHost is not live, we will not attempt to unload
1425 // it.
1426 if (!previous_rfh->IsRenderFrameLive())
1427 return;
1428 // The previous RenderFrameHost entered the back-forward cache and hasn't been
1429 // requested to unload. The browser process do not expect
1430 // FrameHostMsg_Unload_ACK.
1431 if (previous_rfh->IsInBackForwardCache())
1432 return;
1433 previous_rfh->OnMessageReceived(
1434 FrameHostMsg_Unload_ACK(previous_rfh->GetRoutingID()));
1435 }
1436
1437 } // namespace content
1438