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.
5 #include "content/browser/frame_host/navigation_entry_impl.h"
9 #include "base/metrics/histogram.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "content/common/navigation_params.h"
13 #include "content/public/common/content_constants.h"
14 #include "content/public/common/url_constants.h"
15 #include "net/base/net_util.h"
16 #include "ui/gfx/text_elider.h"
18 // Use this to get a new unique ID for a NavigationEntry during construction.
19 // The returned ID is guaranteed to be nonzero (which is the "no ID" indicator).
20 static int GetUniqueIDInConstructor() {
21 static int unique_id_counter
= 0;
22 return ++unique_id_counter
;
27 int NavigationEntryImpl::kInvalidBindings
= -1;
29 NavigationEntryImpl::TreeNode::TreeNode(FrameNavigationEntry
* frame_entry
)
30 : frame_entry(frame_entry
) {
33 NavigationEntryImpl::TreeNode::~TreeNode() {
36 bool NavigationEntryImpl::TreeNode::MatchesFrame(
37 FrameTreeNode
* frame_tree_node
) const {
38 if (frame_tree_node
->frame_tree_node_id() ==
39 frame_entry
->frame_tree_node_id())
42 // For now, we set the root FNE's FrameTreeNode ID to -1.
43 return frame_tree_node
->IsMainFrame() &&
44 frame_entry
->frame_tree_node_id() == -1;
47 NavigationEntryImpl::TreeNode
* NavigationEntryImpl::TreeNode::Clone() const {
48 // Clone the tree using a copy of the FrameNavigationEntry, without sharing.
49 NavigationEntryImpl::TreeNode
* copy
=
50 new NavigationEntryImpl::TreeNode(frame_entry
->Clone());
52 // TODO(creis): Clone children once we add them.
56 NavigationEntry
* NavigationEntry::Create() {
57 return new NavigationEntryImpl();
60 NavigationEntryImpl
* NavigationEntryImpl::FromNavigationEntry(
61 NavigationEntry
* entry
) {
62 return static_cast<NavigationEntryImpl
*>(entry
);
65 const NavigationEntryImpl
* NavigationEntryImpl::FromNavigationEntry(
66 const NavigationEntry
* entry
) {
67 return static_cast<const NavigationEntryImpl
*>(entry
);
70 NavigationEntryImpl::NavigationEntryImpl()
71 : NavigationEntryImpl(nullptr, -1, GURL(), Referrer(), base::string16(),
72 ui::PAGE_TRANSITION_LINK
, false) {
75 NavigationEntryImpl::NavigationEntryImpl(SiteInstanceImpl
* instance
,
78 const Referrer
& referrer
,
79 const base::string16
& title
,
80 ui::PageTransition transition_type
,
81 bool is_renderer_initiated
)
83 new TreeNode(new FrameNavigationEntry(-1, instance
, url
, referrer
))),
84 unique_id_(GetUniqueIDInConstructor()),
85 bindings_(kInvalidBindings
),
86 page_type_(PAGE_TYPE_NORMAL
),
87 update_virtual_url_with_url_(false),
90 transition_type_(transition_type
),
91 has_post_data_(false),
93 restore_type_(RESTORE_NONE
),
94 is_overriding_user_agent_(false),
96 is_renderer_initiated_(is_renderer_initiated
),
97 should_replace_entry_(false),
98 should_clear_history_list_(false),
99 can_load_local_resources_(false),
100 frame_tree_node_id_(-1) {
103 NavigationEntryImpl::~NavigationEntryImpl() {
106 int NavigationEntryImpl::GetUniqueID() const {
110 PageType
NavigationEntryImpl::GetPageType() const {
114 void NavigationEntryImpl::SetURL(const GURL
& url
) {
115 frame_tree_
->frame_entry
->set_url(url
);
116 cached_display_title_
.clear();
119 const GURL
& NavigationEntryImpl::GetURL() const {
120 return frame_tree_
->frame_entry
->url();
123 void NavigationEntryImpl::SetBaseURLForDataURL(const GURL
& url
) {
124 base_url_for_data_url_
= url
;
127 const GURL
& NavigationEntryImpl::GetBaseURLForDataURL() const {
128 return base_url_for_data_url_
;
131 void NavigationEntryImpl::SetReferrer(const Referrer
& referrer
) {
132 frame_tree_
->frame_entry
->set_referrer(referrer
);
135 const Referrer
& NavigationEntryImpl::GetReferrer() const {
136 return frame_tree_
->frame_entry
->referrer();
139 void NavigationEntryImpl::SetVirtualURL(const GURL
& url
) {
140 virtual_url_
= (url
== GetURL()) ? GURL() : url
;
141 cached_display_title_
.clear();
144 const GURL
& NavigationEntryImpl::GetVirtualURL() const {
145 return virtual_url_
.is_empty() ? GetURL() : virtual_url_
;
148 void NavigationEntryImpl::SetTitle(const base::string16
& title
) {
150 cached_display_title_
.clear();
153 const base::string16
& NavigationEntryImpl::GetTitle() const {
157 void NavigationEntryImpl::SetPageState(const PageState
& state
) {
158 frame_tree_
->frame_entry
->set_page_state(state
);
161 const PageState
& NavigationEntryImpl::GetPageState() const {
162 return frame_tree_
->frame_entry
->page_state();
165 void NavigationEntryImpl::SetPageID(int page_id
) {
169 int32
NavigationEntryImpl::GetPageID() const {
173 void NavigationEntryImpl::set_site_instance(SiteInstanceImpl
* site_instance
) {
174 // TODO(creis): Update all callers and remove this method.
175 frame_tree_
->frame_entry
->set_site_instance(site_instance
);
178 void NavigationEntryImpl::set_source_site_instance(
179 SiteInstanceImpl
* source_site_instance
) {
180 source_site_instance_
= source_site_instance
;
183 void NavigationEntryImpl::SetBindings(int bindings
) {
184 // Ensure this is set to a valid value, and that it stays the same once set.
185 CHECK_NE(bindings
, kInvalidBindings
);
186 CHECK(bindings_
== kInvalidBindings
|| bindings_
== bindings
);
187 bindings_
= bindings
;
190 const base::string16
& NavigationEntryImpl::GetTitleForDisplay(
191 const std::string
& languages
) const {
192 // Most pages have real titles. Don't even bother caching anything if this is
197 // More complicated cases will use the URLs as the title. This result we will
198 // cache since it's more complicated to compute.
199 if (!cached_display_title_
.empty())
200 return cached_display_title_
;
202 // Use the virtual URL first if any, and fall back on using the real URL.
203 base::string16 title
;
204 if (!virtual_url_
.is_empty()) {
205 title
= net::FormatUrl(virtual_url_
, languages
);
206 } else if (!GetURL().is_empty()) {
207 title
= net::FormatUrl(GetURL(), languages
);
210 // For file:// URLs use the filename as the title, not the full path.
211 if (GetURL().SchemeIsFile()) {
212 base::string16::size_type slashpos
= title
.rfind('/');
213 if (slashpos
!= base::string16::npos
)
214 title
= title
.substr(slashpos
+ 1);
217 gfx::ElideString(title
, kMaxTitleChars
, &cached_display_title_
);
218 return cached_display_title_
;
221 bool NavigationEntryImpl::IsViewSourceMode() const {
222 return virtual_url_
.SchemeIs(kViewSourceScheme
);
225 void NavigationEntryImpl::SetTransitionType(
226 ui::PageTransition transition_type
) {
227 transition_type_
= transition_type
;
230 ui::PageTransition
NavigationEntryImpl::GetTransitionType() const {
231 return transition_type_
;
234 const GURL
& NavigationEntryImpl::GetUserTypedURL() const {
235 return user_typed_url_
;
238 void NavigationEntryImpl::SetHasPostData(bool has_post_data
) {
239 has_post_data_
= has_post_data
;
242 bool NavigationEntryImpl::GetHasPostData() const {
243 return has_post_data_
;
246 void NavigationEntryImpl::SetPostID(int64 post_id
) {
250 int64
NavigationEntryImpl::GetPostID() const {
254 void NavigationEntryImpl::SetBrowserInitiatedPostData(
255 const base::RefCountedMemory
* data
) {
256 browser_initiated_post_data_
= data
;
259 const base::RefCountedMemory
*
260 NavigationEntryImpl::GetBrowserInitiatedPostData() const {
261 return browser_initiated_post_data_
.get();
265 const FaviconStatus
& NavigationEntryImpl::GetFavicon() const {
269 FaviconStatus
& NavigationEntryImpl::GetFavicon() {
273 const SSLStatus
& NavigationEntryImpl::GetSSL() const {
277 SSLStatus
& NavigationEntryImpl::GetSSL() {
281 void NavigationEntryImpl::SetOriginalRequestURL(const GURL
& original_url
) {
282 original_request_url_
= original_url
;
285 const GURL
& NavigationEntryImpl::GetOriginalRequestURL() const {
286 return original_request_url_
;
289 void NavigationEntryImpl::SetIsOverridingUserAgent(bool override
) {
290 is_overriding_user_agent_
= override
;
293 bool NavigationEntryImpl::GetIsOverridingUserAgent() const {
294 return is_overriding_user_agent_
;
297 void NavigationEntryImpl::SetTimestamp(base::Time timestamp
) {
298 timestamp_
= timestamp
;
301 base::Time
NavigationEntryImpl::GetTimestamp() const {
305 void NavigationEntryImpl::SetHttpStatusCode(int http_status_code
) {
306 http_status_code_
= http_status_code
;
309 int NavigationEntryImpl::GetHttpStatusCode() const {
310 return http_status_code_
;
313 void NavigationEntryImpl::SetRedirectChain(
314 const std::vector
<GURL
>& redirect_chain
) {
315 redirect_chain_
= redirect_chain
;
318 const std::vector
<GURL
>& NavigationEntryImpl::GetRedirectChain() const {
319 return redirect_chain_
;
322 bool NavigationEntryImpl::IsRestored() const {
323 return restore_type_
!= RESTORE_NONE
;
326 void NavigationEntryImpl::SetCanLoadLocalResources(bool allow
) {
327 can_load_local_resources_
= allow
;
330 bool NavigationEntryImpl::GetCanLoadLocalResources() const {
331 return can_load_local_resources_
;
334 void NavigationEntryImpl::SetExtraData(const std::string
& key
,
335 const base::string16
& data
) {
336 extra_data_
[key
] = data
;
339 bool NavigationEntryImpl::GetExtraData(const std::string
& key
,
340 base::string16
* data
) const {
341 std::map
<std::string
, base::string16
>::const_iterator iter
=
342 extra_data_
.find(key
);
343 if (iter
== extra_data_
.end())
345 *data
= iter
->second
;
349 void NavigationEntryImpl::ClearExtraData(const std::string
& key
) {
350 extra_data_
.erase(key
);
353 NavigationEntryImpl
* NavigationEntryImpl::Clone() const {
354 NavigationEntryImpl
* copy
= new NavigationEntryImpl();
356 // TODO(creis): Only share the same FrameNavigationEntries if cloning within
358 copy
->frame_tree_
.reset(frame_tree_
->Clone());
360 // Copy most state over, unless cleared in ResetForCommit.
361 // Don't copy unique_id_, otherwise it won't be unique.
362 copy
->bindings_
= bindings_
;
363 copy
->page_type_
= page_type_
;
364 copy
->virtual_url_
= virtual_url_
;
365 copy
->update_virtual_url_with_url_
= update_virtual_url_with_url_
;
366 copy
->title_
= title_
;
367 copy
->favicon_
= favicon_
;
368 copy
->page_id_
= page_id_
;
370 copy
->transition_type_
= transition_type_
;
371 copy
->user_typed_url_
= user_typed_url_
;
372 copy
->has_post_data_
= has_post_data_
;
373 copy
->post_id_
= post_id_
;
374 copy
->restore_type_
= restore_type_
;
375 copy
->original_request_url_
= original_request_url_
;
376 copy
->is_overriding_user_agent_
= is_overriding_user_agent_
;
377 copy
->timestamp_
= timestamp_
;
378 copy
->http_status_code_
= http_status_code_
;
379 // ResetForCommit: browser_initiated_post_data_
380 copy
->screenshot_
= screenshot_
;
381 copy
->extra_headers_
= extra_headers_
;
382 // ResetForCommit: source_site_instance_
383 copy
->base_url_for_data_url_
= base_url_for_data_url_
;
384 // ResetForCommit: is_renderer_initiated_
385 copy
->cached_display_title_
= cached_display_title_
;
386 // ResetForCommit: transferred_global_request_id_
387 // ResetForCommit: should_replace_entry_
388 copy
->redirect_chain_
= redirect_chain_
;
389 // ResetForCommit: should_clear_history_list_
390 // ResetForCommit: frame_tree_node_id_
391 // ResetForCommit: intent_received_timestamp_
392 copy
->extra_data_
= extra_data_
;
397 CommonNavigationParams
NavigationEntryImpl::ConstructCommonNavigationParams(
398 FrameMsg_Navigate_Type::Value navigation_type
) const {
399 FrameMsg_UILoadMetricsReportType::Value report_type
=
400 FrameMsg_UILoadMetricsReportType::NO_REPORT
;
401 base::TimeTicks ui_timestamp
= base::TimeTicks();
402 #if defined(OS_ANDROID)
403 if (!intent_received_timestamp().is_null())
404 report_type
= FrameMsg_UILoadMetricsReportType::REPORT_INTENT
;
405 ui_timestamp
= intent_received_timestamp();
408 return CommonNavigationParams(
409 GetURL(), GetReferrer(), GetTransitionType(), navigation_type
,
410 !IsViewSourceMode(), ui_timestamp
, report_type
, GetBaseURLForDataURL(),
411 GetHistoryURLForDataURL());
414 StartNavigationParams
NavigationEntryImpl::ConstructStartNavigationParams()
416 std::vector
<unsigned char> browser_initiated_post_data
;
417 if (GetBrowserInitiatedPostData()) {
418 browser_initiated_post_data
.assign(
419 GetBrowserInitiatedPostData()->front(),
420 GetBrowserInitiatedPostData()->front() +
421 GetBrowserInitiatedPostData()->size());
424 return StartNavigationParams(
425 GetHasPostData(), extra_headers(), browser_initiated_post_data
,
426 should_replace_entry(), transferred_global_request_id().child_id
,
427 transferred_global_request_id().request_id
);
430 RequestNavigationParams
NavigationEntryImpl::ConstructRequestNavigationParams(
431 base::TimeTicks navigation_start
,
432 bool has_committed_real_load
,
433 bool intended_as_new_entry
,
434 int pending_history_list_offset
,
435 int current_history_list_offset
,
436 int current_history_list_length
) const {
437 // Set the redirect chain to the navigation's redirects, unless returning to a
438 // completed navigation (whose previous redirects don't apply).
439 std::vector
<GURL
> redirects
;
440 if (ui::PageTransitionIsNewNavigation(GetTransitionType())) {
441 redirects
= GetRedirectChain();
444 int pending_offset_to_send
= pending_history_list_offset
;
445 int current_offset_to_send
= current_history_list_offset
;
446 int current_length_to_send
= current_history_list_length
;
447 if (should_clear_history_list()) {
448 // Set the history list related parameters to the same values a
449 // NavigationController would return before its first navigation. This will
450 // fully clear the RenderView's view of the session history.
451 pending_offset_to_send
= -1;
452 current_offset_to_send
= -1;
453 current_length_to_send
= 0;
455 return RequestNavigationParams(
456 GetIsOverridingUserAgent(), navigation_start
, redirects
,
457 GetCanLoadLocalResources(), base::Time::Now(), GetPageState(),
458 GetPageID(), GetUniqueID(), has_committed_real_load
,
459 intended_as_new_entry
, pending_offset_to_send
, current_offset_to_send
,
460 current_length_to_send
, should_clear_history_list());
463 void NavigationEntryImpl::ResetForCommit() {
464 // Any state that only matters when a navigation entry is pending should be
466 // TODO(creis): This state should be moved to NavigationRequest once
467 // PlzNavigate is enabled.
468 SetBrowserInitiatedPostData(nullptr);
469 set_source_site_instance(nullptr);
470 set_is_renderer_initiated(false);
471 set_transferred_global_request_id(GlobalRequestID());
472 set_should_replace_entry(false);
474 set_should_clear_history_list(false);
475 set_frame_tree_node_id(-1);
477 #if defined(OS_ANDROID)
478 // Reset the time stamp so that the metrics are not reported if this entry is
479 // loaded again in the future.
480 set_intent_received_timestamp(base::TimeTicks());
484 void NavigationEntryImpl::AddOrUpdateFrameEntry(FrameTreeNode
* frame_tree_node
,
485 SiteInstanceImpl
* site_instance
,
487 const Referrer
& referrer
,
488 const PageState
& page_state
) {
489 // We should already have a TreeNode for the parent node by the time this node
490 // commits. Find it first.
491 DCHECK(frame_tree_node
->parent());
492 NavigationEntryImpl::TreeNode
* parent_node
=
493 FindFrameEntry(frame_tree_node
->parent());
495 // The renderer should not send a commit for a subframe before its parent.
496 // TODO(creis): This can currently happen because we don't yet clone the
497 // FrameNavigationEntry tree on manual subframe navigations. Once that's
498 // added, we should kill the renderer if we get here.
502 // Now check whether we have a TreeNode for the node itself.
503 int frame_tree_node_id
= frame_tree_node
->frame_tree_node_id();
504 for (TreeNode
* child
: parent_node
->children
) {
505 if (child
->frame_entry
->frame_tree_node_id() == frame_tree_node_id
) {
506 // Update the existing FrameNavigationEntry.
507 child
->frame_entry
->UpdateEntry(site_instance
, url
, referrer
, page_state
);
512 // No entry exists yet, so create a new one unless it's for about:blank.
513 // Unordered list, since we expect to look up entries by frame sequence number
515 if (url
== GURL(url::kAboutBlankURL
))
517 FrameNavigationEntry
* frame_entry
= new FrameNavigationEntry(
518 frame_tree_node_id
, site_instance
, url
, referrer
);
519 frame_entry
->set_page_state(page_state
);
520 parent_node
->children
.push_back(
521 new NavigationEntryImpl::TreeNode(frame_entry
));
524 bool NavigationEntryImpl::HasFrameEntry(FrameTreeNode
* frame_tree_node
) const {
525 return FindFrameEntry(frame_tree_node
) != nullptr;
528 void NavigationEntryImpl::SetScreenshotPNGData(
529 scoped_refptr
<base::RefCountedBytes
> png_data
) {
530 screenshot_
= png_data
;
531 if (screenshot_
.get())
532 UMA_HISTOGRAM_MEMORY_KB("Overscroll.ScreenshotSize", screenshot_
->size());
535 GURL
NavigationEntryImpl::GetHistoryURLForDataURL() const {
536 return GetBaseURLForDataURL().is_empty() ? GURL() : GetVirtualURL();
539 NavigationEntryImpl::TreeNode
* NavigationEntryImpl::FindFrameEntry(
540 FrameTreeNode
* frame_tree_node
) const {
541 NavigationEntryImpl::TreeNode
* node
= nullptr;
542 std::queue
<NavigationEntryImpl::TreeNode
*> work_queue
;
543 work_queue
.push(root_node());
544 while (!work_queue
.empty()) {
545 node
= work_queue
.front();
547 if (node
->MatchesFrame(frame_tree_node
)) {
548 // Only the root TreeNode should have a FTN ID of -1.
549 DCHECK_IMPLIES(node
->frame_entry
->frame_tree_node_id() == -1,
550 node
== root_node());
553 // Enqueue any children and keep looking.
554 for (auto& child
: node
->children
)
555 work_queue
.push(child
);
560 } // namespace content