1 // Copyright 2013 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/browser/renderer_host/navigator.h"
6
7 #include <utility>
8
9 #include "base/check_op.h"
10 #include "base/debug/dump_without_crashing.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/notreached.h"
13 #include "base/strings/string_util.h"
14 #include "base/time/time.h"
15 #include "content/browser/child_process_security_policy_impl.h"
16 #include "content/browser/renderer_host/debug_urls.h"
17 #include "content/browser/renderer_host/frame_tree.h"
18 #include "content/browser/renderer_host/frame_tree_node.h"
19 #include "content/browser/renderer_host/navigation_controller_impl.h"
20 #include "content/browser/renderer_host/navigation_entry_impl.h"
21 #include "content/browser/renderer_host/navigation_request.h"
22 #include "content/browser/renderer_host/navigation_request_info.h"
23 #include "content/browser/renderer_host/navigator_delegate.h"
24 #include "content/browser/renderer_host/render_frame_host_impl.h"
25 #include "content/browser/renderer_host/render_view_host_impl.h"
26 #include "content/browser/site_instance_impl.h"
27 #include "content/browser/web_package/prefetched_signed_exchange_cache.h"
28 #include "content/browser/web_package/web_bundle_handle_tracker.h"
29 #include "content/browser/webui/web_ui_controller_factory_registry.h"
30 #include "content/browser/webui/web_ui_impl.h"
31 #include "content/common/frame_messages.h"
32 #include "content/common/inter_process_time_ticks_converter.h"
33 #include "content/common/navigation_params.h"
34 #include "content/common/navigation_params_utils.h"
35 #include "content/public/browser/browser_context.h"
36 #include "content/public/browser/content_browser_client.h"
37 #include "content/public/browser/global_request_id.h"
38 #include "content/public/browser/invalidate_type.h"
39 #include "content/public/browser/navigation_controller.h"
40 #include "content/public/browser/navigation_details.h"
41 #include "content/public/browser/page_navigator.h"
42 #include "content/public/browser/render_view_host.h"
43 #include "content/public/browser/restore_type.h"
44 #include "content/public/common/bindings_policy.h"
45 #include "content/public/common/content_client.h"
46 #include "content/public/common/content_constants.h"
47 #include "content/public/common/navigation_policy.h"
48 #include "content/public/common/url_utils.h"
49 #include "net/base/net_errors.h"
50 #include "services/metrics/public/cpp/ukm_builders.h"
51 #include "services/metrics/public/cpp/ukm_recorder.h"
52 #include "services/metrics/public/cpp/ukm_source_id.h"
53 #include "services/network/public/mojom/url_loader_factory.mojom.h"
54 #include "url/gurl.h"
55 #include "url/url_util.h"
56
57 namespace content {
58
59 struct Navigator::NavigationMetricsData {
NavigationMetricsDatacontent::Navigator::NavigationMetricsData60 NavigationMetricsData(base::TimeTicks start_time,
61 GURL url,
62 ukm::SourceId ukm_source_id,
63 bool is_browser_initiated_before_unload,
64 RestoreType restore_type)
65 : start_time_(start_time),
66 url_(url),
67 ukm_source_id_(ukm_source_id),
68 is_browser_initiated_before_unload_(
69 is_browser_initiated_before_unload) {
70 is_restoring_from_last_session_ =
71 (restore_type == RestoreType::LAST_SESSION_EXITED_CLEANLY ||
72 restore_type == RestoreType::LAST_SESSION_CRASHED);
73 }
74
75 base::TimeTicks start_time_;
76 GURL url_;
77 ukm::SourceId ukm_source_id_;
78 bool is_browser_initiated_before_unload_;
79 bool is_restoring_from_last_session_;
80 base::TimeTicks url_job_start_time_;
81 base::TimeDelta before_unload_delay_;
82
83 // Timestamps before_unload_(start|end)_ give the time it took to run
84 // beforeunloads dispatched from the browser process. For browser-initated
85 // navigations this includes all frames (all beforeunload handlers on a page).
86 // For renderer-initated navigations this just includes OOPIFs since local
87 // beforeunloads will have been run in the renderer before dispatching the
88 // navigation IPC.
89 base::Optional<base::TimeTicks> before_unload_start_;
90 base::Optional<base::TimeTicks> before_unload_end_;
91
92 // Time at which the browser process received a navigation request and
93 // dispatched beforeunloads to the renderer.
94 base::Optional<base::TimeTicks> before_unload_sent_;
95
96 // Timestamps renderer_before_unload_(start|end)_ give the time it took to run
97 // beforeunloads for local frames in a renderer-initiated navigation, prior to
98 // notifying the browser process about the navigation.
99 base::Optional<base::TimeTicks> renderer_before_unload_start_;
100 base::Optional<base::TimeTicks> renderer_before_unload_end_;
101 };
102
Navigator(NavigationControllerImpl * navigation_controller,NavigatorDelegate * delegate)103 Navigator::Navigator(NavigationControllerImpl* navigation_controller,
104 NavigatorDelegate* delegate)
105 : controller_(navigation_controller), delegate_(delegate) {}
106
107 Navigator::~Navigator() = default;
108
109 // static
CheckWebUIRendererDoesNotDisplayNormalURL(RenderFrameHostImpl * render_frame_host,const UrlInfo & url_info,bool is_renderer_initiated_check)110 bool Navigator::CheckWebUIRendererDoesNotDisplayNormalURL(
111 RenderFrameHostImpl* render_frame_host,
112 const UrlInfo& url_info,
113 bool is_renderer_initiated_check) {
114 const GURL& url = url_info.url;
115 // In single process mode, everything runs in the same process, so the checks
116 // below are irrelevant.
117 if (RenderProcessHost::run_renderer_in_process())
118 return true;
119
120 ChildProcessSecurityPolicyImpl* security_policy =
121 ChildProcessSecurityPolicyImpl::GetInstance();
122 ProcessLock process_lock =
123 security_policy->GetProcessLock(render_frame_host->GetProcess()->GetID());
124
125 // In the case of error page process, any URL is allowed to commit.
126 if (process_lock == ProcessLock::CreateForErrorPage())
127 return true;
128
129 bool frame_has_bindings = ((render_frame_host->GetEnabledBindings() &
130 kWebUIBindingsPolicyMask) != 0);
131 bool is_allowed_in_web_ui_renderer =
132 WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI(
133 render_frame_host->GetProcess()->GetBrowserContext(), url);
134
135 // Embedders might disable locking for WebUI URLs, which is bad idea, however
136 // this method should take this into account.
137 SiteInstanceImpl* site_instance = render_frame_host->GetSiteInstance();
138 SiteInfo site_info = site_instance->DeriveSiteInfo(url_info);
139 bool should_lock_process =
140 site_info.ShouldLockProcessToSite(site_instance->GetIsolationContext());
141
142 // If the |render_frame_host| has any WebUI bindings, disallow URLs that are
143 // not allowed in a WebUI renderer process.
144 if (frame_has_bindings) {
145 // The process itself must have WebUI bit in the security policy.
146 // Otherwise it indicates that there is a bug in browser process logic and
147 // the browser process must be terminated.
148 // TODO(nasko): Convert to CHECK() once it is confirmed this is not
149 // violated in reality.
150 if (!security_policy->HasWebUIBindings(
151 render_frame_host->GetProcess()->GetID())) {
152 base::debug::DumpWithoutCrashing();
153 }
154
155 // Check whether the process must be locked and if so that the process lock
156 // is indeed in place.
157 if (should_lock_process && !process_lock.is_locked_to_site())
158 return false;
159
160 // There must be a WebUI on the frame.
161 if (!render_frame_host->web_ui())
162 return false;
163
164 // The |url| must be allowed in a WebUI process if the frame has WebUI.
165 if (!is_allowed_in_web_ui_renderer) {
166 // If this method is called in response to IPC message from the renderer
167 // process, it should be terminated, otherwise it is a bug in the
168 // navigation logic and the browser process should be terminated to avoid
169 // exposing users to security issues.
170 if (is_renderer_initiated_check)
171 return false;
172
173 CHECK(false);
174 }
175 }
176
177 // If |url| is one that is allowed in WebUI renderer process, ensure that its
178 // origin is either opaque or matches the origin of the process lock.
179 if (is_allowed_in_web_ui_renderer) {
180 url::Origin url_origin = url::Origin::Create(url.GetOrigin());
181
182 // Verify |url| matches the origin of the process lock, if one is in place.
183 if (should_lock_process) {
184 if (!url_origin.opaque() && !process_lock.MatchesOrigin(url_origin))
185 return false;
186 }
187 }
188
189 return true;
190 }
191
192 // A renderer-initiated navigation should be ignored iff a) there is an ongoing
193 // request b) which is browser initiated and c) the renderer request is not
194 // user-initiated.
195 // static
ShouldIgnoreIncomingRendererRequest(const NavigationRequest * ongoing_navigation_request,bool has_user_gesture)196 bool Navigator::ShouldIgnoreIncomingRendererRequest(
197 const NavigationRequest* ongoing_navigation_request,
198 bool has_user_gesture) {
199 return ongoing_navigation_request &&
200 ongoing_navigation_request->browser_initiated() && !has_user_gesture;
201 }
202
GetDelegate()203 NavigatorDelegate* Navigator::GetDelegate() {
204 return delegate_;
205 }
206
GetController()207 NavigationController* Navigator::GetController() {
208 return controller_;
209 }
210
DidFailLoadWithError(RenderFrameHostImpl * render_frame_host,const GURL & url,int error_code)211 void Navigator::DidFailLoadWithError(RenderFrameHostImpl* render_frame_host,
212 const GURL& url,
213 int error_code) {
214 if (delegate_) {
215 delegate_->DidFailLoadWithError(render_frame_host, url, error_code);
216 }
217 }
218
StartHistoryNavigationInNewSubframe(RenderFrameHostImpl * render_frame_host,mojo::PendingAssociatedRemote<mojom::NavigationClient> * navigation_client)219 bool Navigator::StartHistoryNavigationInNewSubframe(
220 RenderFrameHostImpl* render_frame_host,
221 mojo::PendingAssociatedRemote<mojom::NavigationClient>* navigation_client) {
222 return controller_->StartHistoryNavigationInNewSubframe(render_frame_host,
223 navigation_client);
224 }
225
DidNavigate(RenderFrameHostImpl * render_frame_host,const FrameHostMsg_DidCommitProvisionalLoad_Params & params,std::unique_ptr<NavigationRequest> navigation_request,bool was_within_same_document)226 void Navigator::DidNavigate(
227 RenderFrameHostImpl* render_frame_host,
228 const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
229 std::unique_ptr<NavigationRequest> navigation_request,
230 bool was_within_same_document) {
231 DCHECK(navigation_request);
232 FrameTreeNode* frame_tree_node = render_frame_host->frame_tree_node();
233 FrameTree* frame_tree = frame_tree_node->frame_tree();
234 base::WeakPtr<RenderFrameHostImpl> old_frame_host =
235 frame_tree_node->render_manager()->current_frame_host()->GetWeakPtr();
236
237 bool is_same_document_navigation = controller_->IsURLSameDocumentNavigation(
238 params.url, params.origin, was_within_same_document, render_frame_host);
239 // If a frame claims the navigation was same-document, it must be the current
240 // frame, not a pending one.
241 if (is_same_document_navigation &&
242 render_frame_host != old_frame_host.get()) {
243 bad_message::ReceivedBadMessage(render_frame_host->GetProcess(),
244 bad_message::NI_IN_PAGE_NAVIGATION);
245 is_same_document_navigation = false;
246 }
247 // At this point we have already chosen a SiteInstance for this navigation, so
248 // set |origin_requests_isolation| = false in the conversion to UrlInfo below.
249 const UrlInfo url_info(params.url, false /* origin_requests_isolation */);
250 bool is_cross_document_same_site_navigation =
251 !is_same_document_navigation &&
252 old_frame_host->IsNavigationSameSite(
253 url_info, render_frame_host->GetSiteInstance()
254 ->GetCoopCoepCrossOriginIsolatedInfo());
255
256 if (is_cross_document_same_site_navigation) {
257 UMA_HISTOGRAM_BOOLEAN(
258 "BackForwardCache.ProactiveSameSiteBISwap.SameSiteNavigationDidSwap",
259 navigation_request->did_same_site_proactive_browsing_instance_swap());
260 }
261
262 if (navigation_request->did_same_site_proactive_browsing_instance_swap()) {
263 // If we did a same-site cross-BrowsingInstance main frame navigation, we
264 // might be introducing a web-observable behavior change if we need to
265 // unload the old frame (if we can't store the page in the back-forward
266 // cache), because on normal same-site navigations the unloading of the old
267 // RenderFrameHost happens before commit. We're measuring how often this
268 // case happens to determine the risk of this change.
269 DCHECK(old_frame_host.get());
270 DCHECK_NE(old_frame_host.get(), render_frame_host);
271 DCHECK(frame_tree_node->IsMainFrame());
272 DCHECK(!old_frame_host->GetSiteInstance()->IsRelatedSiteInstance(
273 render_frame_host->GetSiteInstance()));
274 DCHECK(is_cross_document_same_site_navigation);
275 bool can_store_in_back_forward_cache =
276 controller_->GetBackForwardCache().CanStorePageNow(
277 old_frame_host.get());
278 UMA_HISTOGRAM_BOOLEAN(
279 "BackForwardCache.ProactiveSameSiteBISwap.EligibilityDuringCommit",
280 can_store_in_back_forward_cache);
281 UMA_HISTOGRAM_BOOLEAN(
282 "BackForwardCache.ProactiveSameSiteBISwap.UnloadRunsAfterCommit",
283 !can_store_in_back_forward_cache &&
284 old_frame_host->UnloadHandlerExistsInSameSiteInstanceSubtree());
285 }
286
287 if (auto& old_page_info = navigation_request->commit_params().old_page_info) {
288 // This is a same-site main-frame navigation where we did a proactive
289 // BrowsingInstance swap but we're reusing the old page's process, and we
290 // have dispatched the pagehide and visibilitychange handlers of the old
291 // page when we committed the new page.
292 auto* page_lifecycle_state_manager =
293 old_frame_host->render_view_host()->GetPageLifecycleStateManager();
294 page_lifecycle_state_manager->DidSetPagehideDispatchDuringNewPageCommit(
295 std::move(old_page_info->new_lifecycle_state_for_old_page));
296 }
297
298 if (ui::PageTransitionIsMainFrame(params.transition)) {
299 if (delegate_) {
300 // Run tasks that must execute just before the commit.
301 delegate_->DidNavigateMainFramePreCommit(is_same_document_navigation);
302 }
303 }
304
305 // DidNavigateFrame() must be called before replicating the new origin and
306 // other properties to proxies. This is because it destroys the subframes of
307 // the frame we're navigating from, which might trigger those subframes to
308 // run unload handlers. Those unload handlers should still see the old
309 // frame's origin. See https://crbug.com/825283.
310 frame_tree_node->render_manager()->DidNavigateFrame(
311 render_frame_host, params.gesture == NavigationGestureUser,
312 is_same_document_navigation,
313 navigation_request->coop_status()
314 .require_browsing_instance_swap() /* clear_proxies_on_commit */,
315 navigation_request->commit_params().frame_policy);
316
317 // Save the new page's origin and other properties, and replicate them to
318 // proxies, including the proxy created in DidNavigateFrame() to replace the
319 // old frame in cross-process navigation cases.
320 frame_tree_node->SetCurrentOrigin(
321 params.origin, params.has_potentially_trustworthy_unique_origin);
322 frame_tree_node->SetInsecureRequestPolicy(params.insecure_request_policy);
323 frame_tree_node->SetInsecureNavigationsSet(params.insecure_navigations_set);
324
325 // Save the activation status of the previous page here before it gets reset
326 // in FrameTreeNode::ResetForNavigation.
327 bool previous_document_was_activated =
328 frame_tree->root()->HasStickyUserActivation();
329
330 if (!is_same_document_navigation) {
331 // Navigating to a new location means a new, fresh set of http headers
332 // and/or <meta> elements - we need to reset CSP and Feature Policy.
333 // However, if the navigation is restoring the given |render_frame_host|
334 // from back-forward cache, it does not change the document in the given
335 // RenderFrameHost and the existing Content Security Policy should be kept.
336 if (!navigation_request->IsServedFromBackForwardCache())
337 render_frame_host->ResetContentSecurityPolicies();
338
339 auto reset_result = frame_tree_node->ResetForNavigation(
340 navigation_request->IsServedFromBackForwardCache());
341
342 // |old_frame_host| might get immediately deleted after the DidNavigateFrame
343 // call above, so use weak pointer here.
344 if (old_frame_host && old_frame_host->IsInBackForwardCache()) {
345 if (reset_result.changed_frame_policy) {
346 old_frame_host->EvictFromBackForwardCacheWithReason(
347 BackForwardCacheMetrics::NotRestoredReason::
348 kFrameTreeNodeStateReset);
349 }
350 }
351 }
352
353 // Update the site of the SiteInstance if it doesn't have one yet, unless
354 // assigning a site is not necessary for this URL. In that case, the
355 // SiteInstance can still be considered unused until a navigation to a real
356 // page.
357 SiteInstanceImpl* site_instance = render_frame_host->GetSiteInstance();
358 if (!site_instance->HasSite() &&
359 SiteInstanceImpl::ShouldAssignSiteForURL(url_info.url)) {
360 site_instance->ConvertToDefaultOrSetSite(url_info);
361 }
362
363 // Need to update MIME type here because it's referred to in
364 // UpdateNavigationCommands() called by RendererDidNavigate() to
365 // determine whether or not to enable the encoding menu.
366 // It's updated only for the main frame. For a subframe,
367 // RenderView::UpdateURL does not set params.contents_mime_type.
368 // (see http://code.google.com/p/chromium/issues/detail?id=2929 )
369 // TODO(jungshik): Add a test for the encoding menu to avoid
370 // regressing it again.
371 // TODO(nasko): Verify the correctness of the above comment, since some of the
372 // code doesn't exist anymore. Also, move this code in the
373 // PageTransitionIsMainFrame code block above.
374 if (ui::PageTransitionIsMainFrame(params.transition) && delegate_) {
375 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
376 render_frame_host->GetRenderViewHost());
377 rvh->SetContentsMimeType(params.contents_mime_type);
378 }
379
380 int old_entry_count = controller_->GetEntryCount();
381 LoadCommittedDetails details;
382 bool did_navigate = controller_->RendererDidNavigate(
383 render_frame_host, params, &details, is_same_document_navigation,
384 previous_document_was_activated, navigation_request.get());
385
386 // If the history length and/or offset changed, update other renderers in the
387 // FrameTree.
388 if (old_entry_count != controller_->GetEntryCount() ||
389 details.previous_entry_index !=
390 controller_->GetLastCommittedEntryIndex()) {
391 frame_tree->root()->render_manager()->ExecutePageBroadcastMethod(
392 base::BindRepeating(
393 [](int history_offset, int history_count, RenderViewHostImpl* rvh) {
394 if (auto& broadcast = rvh->GetAssociatedPageBroadcast())
395 broadcast->SetHistoryOffsetAndLength(history_offset,
396 history_count);
397 },
398 controller_->GetLastCommittedEntryIndex(),
399 controller_->GetEntryCount()),
400 site_instance);
401 }
402
403 // Back-forward cache navigations do not create a new document.
404 //
405 // |was_within_same_document| (controlled by the renderer) also needs to be
406 // considered: in some cases, the browser and renderer can disagree. While
407 // this is usually a bad message kill, there are some situations where this
408 // can legitimately happen. When a new frame is created (e.g. with
409 // <iframe src="...">), the initial about:blank document doesn't have a
410 // corresponding entry in the browser process. As a result, the browser
411 // process incorrectly determines that the navigation is cross-document when
412 // in reality it's same-document.
413 //
414 // TODO(crbug/1099264): Remove |was_within_same_document| from this logic
415 // once all same-document navigations have a NavigationEntry. Once this
416 // happens there should be no cases where the browser and renderer
417 // legitimately disagree as described above.
418 bool did_create_new_document =
419 !navigation_request->IsServedFromBackForwardCache() &&
420 !is_same_document_navigation && !was_within_same_document;
421
422 render_frame_host->DidNavigate(params, navigation_request.get(),
423 did_create_new_document);
424
425 // Send notification about committed provisional loads. This notification is
426 // different from the NAV_ENTRY_COMMITTED notification which doesn't include
427 // the actual URL navigated to and isn't sent for AUTO_SUBFRAME navigations.
428 if (details.type != NAVIGATION_TYPE_NAV_IGNORE && delegate_) {
429 DCHECK_EQ(!render_frame_host->GetParent(),
430 did_navigate ? details.is_main_frame : false);
431 navigation_request->DidCommitNavigation(params, did_navigate,
432 details.did_replace_entry,
433 details.previous_url, details.type);
434 navigation_request.reset();
435 }
436
437 if (!did_navigate)
438 return; // No navigation happened.
439
440 // DO NOT ADD MORE STUFF TO THIS FUNCTION! Your component should either listen
441 // for the appropriate notification (best) or you can add it to
442 // DidNavigateMainFramePostCommit / DidNavigateAnyFramePostCommit (only if
443 // necessary, please).
444
445 // TODO(carlosk): Move this out.
446 RecordNavigationMetrics(details, params, site_instance);
447
448 // Run post-commit tasks.
449 if (delegate_) {
450 if (details.is_main_frame) {
451 delegate_->DidNavigateMainFramePostCommit(render_frame_host, details,
452 params);
453 }
454
455 delegate_->DidNavigateAnyFramePostCommit(render_frame_host, details,
456 params);
457 }
458 }
459
Navigate(std::unique_ptr<NavigationRequest> request,ReloadType reload_type,RestoreType restore_type)460 void Navigator::Navigate(std::unique_ptr<NavigationRequest> request,
461 ReloadType reload_type,
462 RestoreType restore_type) {
463 TRACE_EVENT0("browser,navigation", "Navigator::Navigate");
464 TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
465 "navigation,rail", "NavigationTiming navigationStart",
466 TRACE_EVENT_SCOPE_GLOBAL, request->common_params().navigation_start);
467
468 // Save destination url, as it is needed for
469 // DidStartNavigationToPendingEntry and request could be destroyed after
470 // BeginNavigation below.
471 GURL dest_url = request->common_params().url;
472 FrameTreeNode* frame_tree_node = request->frame_tree_node();
473
474 navigation_data_ = std::make_unique<NavigationMetricsData>(
475 request->common_params().navigation_start, request->common_params().url,
476 frame_tree_node->current_frame_host()->GetPageUkmSourceId(),
477 true /* is_browser_initiated_before_unload */, restore_type);
478
479 // Check if the BeforeUnload event needs to execute before assigning the
480 // NavigationRequest to the FrameTreeNode. Assigning it to the FrameTreeNode
481 // has the side effect of initializing the current RenderFrameHost, which will
482 // return that it should execute the BeforeUnload event (even though we don't
483 // need to wait for it in the case of a brand new RenderFrameHost).
484 //
485 // We don't want to dispatch a beforeunload handler if
486 // is_history_navigation_in_new_child is true. This indicates a newly created
487 // child frame which does not have a beforeunload handler.
488 bool should_dispatch_beforeunload =
489 !NavigationTypeUtils::IsSameDocument(
490 request->common_params().navigation_type) &&
491 !request->common_params().is_history_navigation_in_new_child_frame &&
492 frame_tree_node->current_frame_host()->ShouldDispatchBeforeUnload(
493 false /* check_subframes_only */);
494
495 int nav_entry_id = request->nav_entry_id();
496 bool is_pending_entry =
497 controller_->GetPendingEntry() &&
498 (nav_entry_id == controller_->GetPendingEntry()->GetUniqueID());
499 frame_tree_node->CreatedNavigationRequest(std::move(request));
500 DCHECK(frame_tree_node->navigation_request());
501
502 // Have the current renderer execute its beforeunload event if needed. If it
503 // is not needed then NavigationRequest::BeginNavigation should be directly
504 // called instead.
505 if (should_dispatch_beforeunload) {
506 frame_tree_node->navigation_request()->SetWaitingForRendererResponse();
507 frame_tree_node->current_frame_host()->DispatchBeforeUnload(
508 RenderFrameHostImpl::BeforeUnloadType::BROWSER_INITIATED_NAVIGATION,
509 reload_type != ReloadType::NONE);
510 } else {
511 frame_tree_node->navigation_request()->BeginNavigation();
512 // WARNING: The NavigationRequest might have been destroyed in
513 // BeginNavigation(). Do not use |frame_tree_node->navigation_request()|
514 // after this point without null checking it first.
515 }
516
517 // Make sure no code called via RFH::Navigate clears the pending entry.
518 if (is_pending_entry)
519 CHECK_EQ(nav_entry_id, controller_->GetPendingEntry()->GetUniqueID());
520 }
521
RequestOpenURL(RenderFrameHostImpl * render_frame_host,const GURL & url,const GlobalFrameRoutingId & initiator_routing_id,const base::Optional<url::Origin> & initiator_origin,const scoped_refptr<network::ResourceRequestBody> & post_body,const std::string & extra_headers,const Referrer & referrer,WindowOpenDisposition disposition,bool should_replace_current_entry,bool user_gesture,blink::TriggeringEventInfo triggering_event_info,const std::string & href_translate,scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,const base::Optional<Impression> & impression)522 void Navigator::RequestOpenURL(
523 RenderFrameHostImpl* render_frame_host,
524 const GURL& url,
525 const GlobalFrameRoutingId& initiator_routing_id,
526 const base::Optional<url::Origin>& initiator_origin,
527 const scoped_refptr<network::ResourceRequestBody>& post_body,
528 const std::string& extra_headers,
529 const Referrer& referrer,
530 WindowOpenDisposition disposition,
531 bool should_replace_current_entry,
532 bool user_gesture,
533 blink::TriggeringEventInfo triggering_event_info,
534 const std::string& href_translate,
535 scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
536 const base::Optional<Impression>& impression) {
537 // Note: This can be called for subframes (even when OOPIFs are not possible)
538 // if the disposition calls for a different window.
539
540 // Only the current RenderFrameHost should be sending an OpenURL request.
541 // Pending RenderFrameHost should know where it is navigating and pending
542 // deletion RenderFrameHost shouldn't be trying to navigate.
543 if (render_frame_host !=
544 render_frame_host->frame_tree_node()->current_frame_host()) {
545 return;
546 }
547
548 SiteInstance* current_site_instance = render_frame_host->GetSiteInstance();
549
550 // TODO(creis): Pass the redirect_chain into this method to support client
551 // redirects. http://crbug.com/311721.
552 std::vector<GURL> redirect_chain;
553
554 int frame_tree_node_id = FrameTreeNode::kFrameTreeNodeInvalidId;
555
556 // Send the navigation to the current FrameTreeNode if it's destined for a
557 // subframe in the current tab. We'll assume it's for the main frame
558 // (possibly of a new or different WebContents) otherwise.
559 if (disposition == WindowOpenDisposition::CURRENT_TAB &&
560 render_frame_host->GetParent()) {
561 frame_tree_node_id =
562 render_frame_host->frame_tree_node()->frame_tree_node_id();
563 }
564
565 OpenURLParams params(url, referrer, frame_tree_node_id, disposition,
566 ui::PAGE_TRANSITION_LINK,
567 true /* is_renderer_initiated */);
568 params.post_data = post_body;
569 params.extra_headers = extra_headers;
570 if (redirect_chain.size() > 0)
571 params.redirect_chain = redirect_chain;
572 params.should_replace_current_entry = should_replace_current_entry;
573 params.user_gesture = user_gesture;
574 params.triggering_event_info = triggering_event_info;
575 params.initiator_origin = initiator_origin;
576 params.initiator_routing_id = initiator_routing_id;
577
578 // RequestOpenURL is used only for local frames, so we can get here only if
579 // the navigation is initiated by a frame in the same SiteInstance as this
580 // frame. Note that navigations on RenderFrameProxies do not use
581 // RequestOpenURL and go through NavigateFromFrameProxy instead.
582 params.source_site_instance = current_site_instance;
583
584 params.source_render_frame_id = render_frame_host->GetRoutingID();
585 params.source_render_process_id = render_frame_host->GetProcess()->GetID();
586
587 if (render_frame_host->web_ui()) {
588 // Note that we hide the referrer for Web UI pages. We don't really want
589 // web sites to see a referrer of "chrome://blah" (and some chrome: URLs
590 // might have search terms or other stuff we don't want to send to the
591 // site), so we send no referrer.
592 params.referrer = Referrer();
593
594 // Navigations in Web UI pages count as browser-initiated navigations.
595 params.is_renderer_initiated = false;
596 }
597
598 params.blob_url_loader_factory = std::move(blob_url_loader_factory);
599 params.href_translate = href_translate;
600 params.impression = impression;
601
602 if (delegate_)
603 delegate_->OpenURL(params);
604 }
605
NavigateFromFrameProxy(RenderFrameHostImpl * render_frame_host,const GURL & url,const GlobalFrameRoutingId & initiator_routing_id,const url::Origin & initiator_origin,SiteInstance * source_site_instance,const Referrer & referrer,ui::PageTransition page_transition,bool should_replace_current_entry,NavigationDownloadPolicy download_policy,const std::string & method,scoped_refptr<network::ResourceRequestBody> post_body,const std::string & extra_headers,scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,bool has_user_gesture,const base::Optional<Impression> & impression)606 void Navigator::NavigateFromFrameProxy(
607 RenderFrameHostImpl* render_frame_host,
608 const GURL& url,
609 const GlobalFrameRoutingId& initiator_routing_id,
610 const url::Origin& initiator_origin,
611 SiteInstance* source_site_instance,
612 const Referrer& referrer,
613 ui::PageTransition page_transition,
614 bool should_replace_current_entry,
615 NavigationDownloadPolicy download_policy,
616 const std::string& method,
617 scoped_refptr<network::ResourceRequestBody> post_body,
618 const std::string& extra_headers,
619 scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
620 bool has_user_gesture,
621 const base::Optional<Impression>& impression) {
622 // |method != "POST"| should imply absence of |post_body|.
623 if (method != "POST" && post_body) {
624 NOTREACHED();
625 post_body = nullptr;
626 }
627
628 // Allow the delegate to cancel the transfer.
629 if (!delegate_->ShouldTransferNavigation(
630 render_frame_host->frame_tree_node()->IsMainFrame()))
631 return;
632
633 // TODO(creis): Determine if this transfer started as a browser-initiated
634 // navigation. See https://crbug.com/495161.
635 bool is_renderer_initiated = true;
636 Referrer referrer_to_use(referrer);
637 if (render_frame_host->web_ui()) {
638 // Note that we hide the referrer for Web UI pages. We don't really want
639 // web sites to see a referrer of "chrome://blah" (and some chrome: URLs
640 // might have search terms or other stuff we don't want to send to the
641 // site), so we send no referrer.
642 referrer_to_use = Referrer();
643
644 // Navigations in Web UI pages count as browser-initiated navigations.
645 is_renderer_initiated = false;
646 }
647
648 if (is_renderer_initiated &&
649 ShouldIgnoreIncomingRendererRequest(
650 render_frame_host->frame_tree_node()->navigation_request(),
651 has_user_gesture)) {
652 return;
653 }
654
655 controller_->NavigateFromFrameProxy(
656 render_frame_host, url, initiator_routing_id, initiator_origin,
657 is_renderer_initiated, source_site_instance, referrer_to_use,
658 page_transition, should_replace_current_entry, download_policy, method,
659 post_body, extra_headers, std::move(blob_url_loader_factory), impression);
660 }
661
BeforeUnloadCompleted(FrameTreeNode * frame_tree_node,bool proceed,const base::TimeTicks & proceed_time)662 void Navigator::BeforeUnloadCompleted(FrameTreeNode* frame_tree_node,
663 bool proceed,
664 const base::TimeTicks& proceed_time) {
665 DCHECK(frame_tree_node);
666
667 NavigationRequest* navigation_request = frame_tree_node->navigation_request();
668
669 // The NavigationRequest may have been canceled while the renderer was
670 // executing the BeforeUnload event.
671 if (!navigation_request)
672 return;
673
674 // If the user chose not to proceed, cancel the ongoing navigation.
675 // Note: it might be a new navigation, and not the one that triggered the
676 // sending of the BeforeUnload IPC in the first place. However, the
677 // BeforeUnload where the user asked not to proceed will have taken place
678 // after the navigation started. The last user input should be respected, and
679 // the navigation cancelled anyway.
680 if (!proceed) {
681 CancelNavigation(frame_tree_node);
682 return;
683 }
684
685 // The browser-initiated NavigationRequest that triggered the sending of the
686 // BeforeUnload IPC might have been replaced by a renderer-initiated one while
687 // the BeforeUnload event executed in the renderer. In that case, the request
688 // will already have begun, so there is no need to start it again.
689 if (navigation_request->state() >
690 NavigationRequest::WAITING_FOR_RENDERER_RESPONSE) {
691 DCHECK(navigation_request->from_begin_navigation());
692 return;
693 }
694
695 // Update the navigation start: it should be when it was determined that the
696 // navigation will proceed.
697 navigation_request->set_navigation_start_time(proceed_time);
698
699 DCHECK_EQ(NavigationRequest::WAITING_FOR_RENDERER_RESPONSE,
700 navigation_request->state());
701
702 // Send the request to the IO thread.
703 navigation_request->BeginNavigation();
704 // DO NOT USE |navigation_request| BEYOND THIS POINT. It might have been
705 // destroyed in BeginNavigation().
706 // See https://crbug.com/770157.
707 }
708
OnBeginNavigation(FrameTreeNode * frame_tree_node,mojom::CommonNavigationParamsPtr common_params,mojom::BeginNavigationParamsPtr begin_params,scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,mojo::PendingAssociatedRemote<mojom::NavigationClient> navigation_client,mojo::PendingRemote<blink::mojom::NavigationInitiator> navigation_initiator,scoped_refptr<PrefetchedSignedExchangeCache> prefetched_signed_exchange_cache,std::unique_ptr<WebBundleHandleTracker> web_bundle_handle_tracker)709 void Navigator::OnBeginNavigation(
710 FrameTreeNode* frame_tree_node,
711 mojom::CommonNavigationParamsPtr common_params,
712 mojom::BeginNavigationParamsPtr begin_params,
713 scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
714 mojo::PendingAssociatedRemote<mojom::NavigationClient> navigation_client,
715 mojo::PendingRemote<blink::mojom::NavigationInitiator> navigation_initiator,
716 scoped_refptr<PrefetchedSignedExchangeCache>
717 prefetched_signed_exchange_cache,
718 std::unique_ptr<WebBundleHandleTracker> web_bundle_handle_tracker) {
719 // TODO(clamy): the url sent by the renderer should be validated with
720 // FilterURL.
721 // This is a renderer-initiated navigation.
722 DCHECK(frame_tree_node);
723
724 if (common_params->is_history_navigation_in_new_child_frame) {
725 // Try to find a FrameNavigationEntry that matches this frame instead, based
726 // on the frame's unique name. If this can't be found, fall back to the
727 // default path below.
728 if (frame_tree_node->navigator().StartHistoryNavigationInNewSubframe(
729 frame_tree_node->current_frame_host(), &navigation_client)) {
730 return;
731 }
732 }
733
734 NavigationRequest* ongoing_navigation_request =
735 frame_tree_node->navigation_request();
736
737 // Client redirects during the initial history navigation of a child frame
738 // should take precedence over the history navigation (despite being renderer-
739 // initiated). See https://crbug.com/348447 and https://crbug.com/691168.
740 if (ongoing_navigation_request &&
741 ongoing_navigation_request->common_params()
742 .is_history_navigation_in_new_child_frame) {
743 // Preemptively clear this local pointer before deleting the request.
744 ongoing_navigation_request = nullptr;
745 frame_tree_node->ResetNavigationRequest(false);
746 }
747
748 // Verify this navigation has precedence.
749 if (ShouldIgnoreIncomingRendererRequest(ongoing_navigation_request,
750 common_params->has_user_gesture)) {
751 return;
752 }
753
754 NavigationEntryImpl* navigation_entry =
755 GetNavigationEntryForRendererInitiatedNavigation(*common_params,
756 frame_tree_node);
757 const bool override_user_agent =
758 delegate_ &&
759 delegate_->ShouldOverrideUserAgentForRendererInitiatedNavigation();
760 frame_tree_node->CreatedNavigationRequest(
761 NavigationRequest::CreateRendererInitiated(
762 frame_tree_node, navigation_entry, std::move(common_params),
763 std::move(begin_params), controller_->GetLastCommittedEntryIndex(),
764 controller_->GetEntryCount(), override_user_agent,
765 std::move(blob_url_loader_factory), std::move(navigation_client),
766 std::move(navigation_initiator),
767 std::move(prefetched_signed_exchange_cache),
768 std::move(web_bundle_handle_tracker)));
769 NavigationRequest* navigation_request = frame_tree_node->navigation_request();
770
771 navigation_data_ = std::make_unique<NavigationMetricsData>(
772 navigation_request->common_params().navigation_start,
773 navigation_request->common_params().url,
774 frame_tree_node->current_frame_host()->GetPageUkmSourceId(),
775 false /* is_browser_initiated_before_unload */, RestoreType::NONE);
776
777 LogRendererInitiatedBeforeUnloadTime(
778 navigation_request->begin_params()->before_unload_start,
779 navigation_request->begin_params()->before_unload_end);
780
781 // This frame has already run beforeunload before it sent this IPC. See if
782 // any of its cross-process subframes also need to run beforeunload. If so,
783 // delay the navigation until beforeunload completion callbacks are invoked on
784 // those frames.
785 DCHECK(!NavigationTypeUtils::IsSameDocument(
786 navigation_request->common_params().navigation_type));
787 bool should_dispatch_beforeunload =
788 frame_tree_node->current_frame_host()->ShouldDispatchBeforeUnload(
789 true /* check_subframes_only */);
790 if (should_dispatch_beforeunload) {
791 frame_tree_node->navigation_request()->SetWaitingForRendererResponse();
792 frame_tree_node->current_frame_host()->DispatchBeforeUnload(
793 RenderFrameHostImpl::BeforeUnloadType::RENDERER_INITIATED_NAVIGATION,
794 NavigationTypeUtils::IsReload(
795 navigation_request->common_params().navigation_type));
796 return;
797 }
798
799 // For main frames, NavigationHandle will be created after the call to
800 // |DidStartMainFrameNavigation|, so it receives the most up to date pending
801 // entry from the NavigationController.
802 navigation_request->BeginNavigation();
803 // DO NOT USE |navigation_request| BEYOND THIS POINT. It might have been
804 // destroyed in BeginNavigation().
805 // See https://crbug.com/770157.
806 }
807
RestartNavigationAsCrossDocument(std::unique_ptr<NavigationRequest> navigation_request)808 void Navigator::RestartNavigationAsCrossDocument(
809 std::unique_ptr<NavigationRequest> navigation_request) {
810 FrameTreeNode* frame_tree_node = navigation_request->frame_tree_node();
811 // Don't restart the navigation if there is already another ongoing navigation
812 // in the FrameTreeNode.
813 if (frame_tree_node->navigation_request())
814 return;
815
816 navigation_request->ResetForCrossDocumentRestart();
817 frame_tree_node->CreatedNavigationRequest(std::move(navigation_request));
818 frame_tree_node->navigation_request()->BeginNavigation();
819 // DO NOT USE THE NAVIGATION REQUEST BEYOND THIS POINT. It might have been
820 // destroyed in BeginNavigation().
821 // See https://crbug.com/770157.
822 }
823
CancelNavigation(FrameTreeNode * frame_tree_node)824 void Navigator::CancelNavigation(FrameTreeNode* frame_tree_node) {
825 if (frame_tree_node->navigation_request())
826 frame_tree_node->navigation_request()->set_net_error(net::ERR_ABORTED);
827 frame_tree_node->ResetNavigationRequest(false);
828 if (frame_tree_node->IsMainFrame())
829 navigation_data_.reset();
830 }
831
LogResourceRequestTime(base::TimeTicks timestamp,const GURL & url)832 void Navigator::LogResourceRequestTime(base::TimeTicks timestamp,
833 const GURL& url) {
834 if (navigation_data_ && navigation_data_->url_ == url) {
835 navigation_data_->url_job_start_time_ = timestamp;
836 UMA_HISTOGRAM_TIMES(
837 "Navigation.TimeToURLJobStart",
838 navigation_data_->url_job_start_time_ - navigation_data_->start_time_);
839 }
840 }
841
LogBeforeUnloadTime(base::TimeTicks renderer_before_unload_start_time,base::TimeTicks renderer_before_unload_end_time,base::TimeTicks before_unload_sent_time)842 void Navigator::LogBeforeUnloadTime(
843 base::TimeTicks renderer_before_unload_start_time,
844 base::TimeTicks renderer_before_unload_end_time,
845 base::TimeTicks before_unload_sent_time) {
846 if (!navigation_data_)
847 return;
848
849 // Only stores the beforeunload delay if we're tracking a browser initiated
850 // navigation and it happened later than the navigation request.
851 if (navigation_data_->is_browser_initiated_before_unload_ &&
852 renderer_before_unload_start_time > navigation_data_->start_time_) {
853 navigation_data_->before_unload_delay_ =
854 renderer_before_unload_end_time - renderer_before_unload_start_time;
855 }
856 // LogBeforeUnloadTime is called once for each cross-process frame. Once all
857 // beforeunloads complete, the timestamps in navigation_data will be the
858 // timestamps of the beforeunload that blocked the navigation the longest.
859 if (!base::TimeTicks::IsConsistentAcrossProcesses()) {
860 // These timestamps come directly from the renderer so they might need to be
861 // converted to local time stamps.
862 InterProcessTimeTicksConverter converter(
863 LocalTimeTicks::FromTimeTicks(before_unload_sent_time),
864 LocalTimeTicks::FromTimeTicks(base::TimeTicks::Now()),
865 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_start_time),
866 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time));
867 LocalTimeTicks converted_renderer_before_unload_start =
868 converter.ToLocalTimeTicks(
869 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_start_time));
870 LocalTimeTicks converted_renderer_before_unload_end =
871 converter.ToLocalTimeTicks(
872 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time));
873 navigation_data_->before_unload_start_ =
874 converted_renderer_before_unload_start.ToTimeTicks();
875 navigation_data_->before_unload_end_ =
876 converted_renderer_before_unload_end.ToTimeTicks();
877 } else {
878 navigation_data_->before_unload_start_ = renderer_before_unload_start_time;
879 navigation_data_->before_unload_end_ = renderer_before_unload_end_time;
880 }
881 navigation_data_->before_unload_sent_ = before_unload_sent_time;
882 }
883
LogRendererInitiatedBeforeUnloadTime(base::TimeTicks renderer_before_unload_start_time,base::TimeTicks renderer_before_unload_end_time)884 void Navigator::LogRendererInitiatedBeforeUnloadTime(
885 base::TimeTicks renderer_before_unload_start_time,
886 base::TimeTicks renderer_before_unload_end_time) {
887 DCHECK(navigation_data_);
888
889 if (renderer_before_unload_start_time == base::TimeTicks() ||
890 renderer_before_unload_end_time == base::TimeTicks())
891 return;
892
893 if (!base::TimeTicks::IsConsistentAcrossProcesses()) {
894 // These timestamps come directly from the renderer so they might need to be
895 // converted to local time stamps.
896 InterProcessTimeTicksConverter converter(
897 LocalTimeTicks::FromTimeTicks(base::TimeTicks()),
898 LocalTimeTicks::FromTimeTicks(base::TimeTicks::Now()),
899 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_start_time),
900 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time));
901 LocalTimeTicks converted_renderer_before_unload_start =
902 converter.ToLocalTimeTicks(
903 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_start_time));
904 LocalTimeTicks converted_renderer_before_unload_end =
905 converter.ToLocalTimeTicks(
906 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time));
907 navigation_data_->renderer_before_unload_start_ =
908 converted_renderer_before_unload_start.ToTimeTicks();
909 navigation_data_->renderer_before_unload_end_ =
910 converted_renderer_before_unload_end.ToTimeTicks();
911 } else {
912 navigation_data_->renderer_before_unload_start_ =
913 renderer_before_unload_start_time;
914 navigation_data_->renderer_before_unload_end_ =
915 renderer_before_unload_end_time;
916 }
917 }
918
RecordNavigationMetrics(const LoadCommittedDetails & details,const FrameHostMsg_DidCommitProvisionalLoad_Params & params,SiteInstance * site_instance)919 void Navigator::RecordNavigationMetrics(
920 const LoadCommittedDetails& details,
921 const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
922 SiteInstance* site_instance) {
923 DCHECK(site_instance->HasProcess());
924
925 if (!details.is_main_frame || !navigation_data_ ||
926 navigation_data_->url_job_start_time_.is_null() ||
927 navigation_data_->url_ != params.original_request_url) {
928 return;
929 }
930
931 ukm::builders::Unload builder(navigation_data_->ukm_source_id_);
932
933 if (navigation_data_->is_browser_initiated_before_unload_) {
934 base::TimeDelta time_to_commit =
935 base::TimeTicks::Now() - navigation_data_->start_time_;
936 UMA_HISTOGRAM_TIMES("Navigation.TimeToCommit", time_to_commit);
937
938 time_to_commit -= navigation_data_->before_unload_delay_;
939 base::TimeDelta time_to_network = navigation_data_->url_job_start_time_ -
940 navigation_data_->start_time_ -
941 navigation_data_->before_unload_delay_;
942 if (navigation_data_->is_restoring_from_last_session_) {
943 UMA_HISTOGRAM_TIMES(
944 "Navigation.TimeToCommit_SessionRestored_BeforeUnloadDiscounted",
945 time_to_commit);
946 UMA_HISTOGRAM_TIMES(
947 "Navigation.TimeToURLJobStart_SessionRestored_BeforeUnloadDiscounted",
948 time_to_network);
949 navigation_data_.reset();
950 return;
951 }
952 bool navigation_created_new_renderer_process =
953 site_instance->GetProcess()->GetInitTimeForNavigationMetrics() >
954 navigation_data_->start_time_;
955 if (navigation_created_new_renderer_process) {
956 UMA_HISTOGRAM_TIMES(
957 "Navigation.TimeToCommit_NewRenderer_BeforeUnloadDiscounted",
958 time_to_commit);
959 UMA_HISTOGRAM_TIMES(
960 "Navigation.TimeToURLJobStart_NewRenderer_BeforeUnloadDiscounted",
961 time_to_network);
962 } else {
963 UMA_HISTOGRAM_TIMES(
964 "Navigation.TimeToCommit_ExistingRenderer_BeforeUnloadDiscounted",
965 time_to_commit);
966 UMA_HISTOGRAM_TIMES(
967 "Navigation.TimeToURLJobStart_ExistingRenderer_"
968 "BeforeUnloadDiscounted",
969 time_to_network);
970 }
971 if (navigation_data_->before_unload_start_ &&
972 navigation_data_->before_unload_end_) {
973 builder.SetBeforeUnloadDuration(
974 (navigation_data_->before_unload_end_.value() -
975 navigation_data_->before_unload_start_.value())
976 .InMilliseconds());
977 }
978 } else {
979 if (navigation_data_->renderer_before_unload_start_ &&
980 navigation_data_->renderer_before_unload_end_) {
981 base::TimeDelta before_unload_duration =
982 navigation_data_->renderer_before_unload_end_.value() -
983 navigation_data_->renderer_before_unload_start_.value();
984
985 // If we had to dispatch beforeunload handlers for OOPIFs from the
986 // browser, add those into the beforeunload duration as they contributed
987 // to the total beforeunload latency.
988 if (navigation_data_->before_unload_sent_) {
989 before_unload_duration +=
990 navigation_data_->before_unload_end_.value() -
991 navigation_data_->before_unload_start_.value();
992 }
993 builder.SetBeforeUnloadDuration(before_unload_duration.InMilliseconds());
994 }
995 }
996
997 // Records the queuing duration of the beforeunload sent from the browser to
998 // the frame that blocked the navigation the longest. This can happen in a
999 // renderer or browser initiated navigation and could mean a long queuing time
1000 // blocked the navigation or a long beforeunload. Records nothing if none were
1001 // sent.
1002 if (navigation_data_->before_unload_sent_) {
1003 builder.SetBeforeUnloadQueueingDuration(
1004 (navigation_data_->before_unload_start_.value() -
1005 navigation_data_->before_unload_sent_.value())
1006 .InMilliseconds());
1007 }
1008
1009 builder.Record(ukm::UkmRecorder::Get());
1010 navigation_data_.reset();
1011 }
1012
1013 NavigationEntryImpl*
GetNavigationEntryForRendererInitiatedNavigation(const mojom::CommonNavigationParams & common_params,FrameTreeNode * frame_tree_node)1014 Navigator::GetNavigationEntryForRendererInitiatedNavigation(
1015 const mojom::CommonNavigationParams& common_params,
1016 FrameTreeNode* frame_tree_node) {
1017 if (!frame_tree_node->IsMainFrame())
1018 return nullptr;
1019
1020 // If there is no browser-initiated pending entry for this navigation and it
1021 // is not for the error URL, create a pending entry and ensure the address bar
1022 // updates accordingly. We don't know the referrer or extra headers at this
1023 // point, but the referrer will be set properly upon commit. This does not
1024 // set the SiteInstance for the pending entry, because it may change
1025 // before the URL commits.
1026 NavigationEntryImpl* pending_entry = controller_->GetPendingEntry();
1027 bool has_browser_initiated_pending_entry =
1028 pending_entry && !pending_entry->is_renderer_initiated();
1029 if (has_browser_initiated_pending_entry)
1030 return nullptr;
1031
1032 // A pending navigation entry is created in OnBeginNavigation(). The renderer
1033 // sends a provisional load notification after that. We don't want to create
1034 // a duplicate navigation entry here.
1035 bool renderer_provisional_load_to_pending_url =
1036 pending_entry && pending_entry->is_renderer_initiated() &&
1037 (pending_entry->GetURL() == common_params.url);
1038 if (renderer_provisional_load_to_pending_url)
1039 return nullptr;
1040
1041 // Since GetNavigationEntryForRendererInitiatedNavigation is called from
1042 // OnBeginNavigation, we can assume that no frame proxies are involved and
1043 // therefore that |current_site_instance| is also the |source_site_instance|.
1044 SiteInstance* current_site_instance =
1045 frame_tree_node->current_frame_host()->GetSiteInstance();
1046 SiteInstance* source_site_instance = current_site_instance;
1047
1048 std::unique_ptr<NavigationEntryImpl> entry =
1049 NavigationEntryImpl::FromNavigationEntry(
1050 NavigationControllerImpl::CreateNavigationEntry(
1051 common_params.url, content::Referrer(),
1052 common_params.initiator_origin, source_site_instance,
1053 ui::PAGE_TRANSITION_LINK, true /* is_renderer_initiated */,
1054 std::string() /* extra_headers */,
1055 controller_->GetBrowserContext(),
1056 nullptr /* blob_url_loader_factory */,
1057 common_params.should_replace_current_entry,
1058 controller_->GetWebContents()));
1059 entry->set_reload_type(NavigationRequest::NavigationTypeToReloadType(
1060 common_params.navigation_type));
1061
1062 controller_->SetPendingEntry(std::move(entry));
1063 if (delegate_)
1064 delegate_->NotifyChangedNavigationState(content::INVALIDATE_TYPE_URL);
1065
1066 return controller_->GetPendingEntry();
1067 }
1068
1069 } // namespace content
1070