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 "components/url_formatter/url_formatter.h"
13 #include "content/common/navigation_params.h"
14 #include "content/public/common/content_constants.h"
15 #include "content/public/common/url_constants.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 scoped_ptr
<NavigationEntryImpl::TreeNode
>
48 NavigationEntryImpl::TreeNode::CloneAndReplace(
49 FrameTreeNode
* frame_tree_node
,
50 FrameNavigationEntry
* frame_navigation_entry
) const {
51 if (frame_tree_node
&& MatchesFrame(frame_tree_node
)) {
52 // Replace this node in the cloned tree and prune its children.
53 return make_scoped_ptr(
54 new NavigationEntryImpl::TreeNode(frame_navigation_entry
));
57 // Clone the tree using a copy of the FrameNavigationEntry, without sharing.
58 // TODO(creis): Share FNEs unless it's for another tab.
59 scoped_ptr
<NavigationEntryImpl::TreeNode
> copy(
60 new NavigationEntryImpl::TreeNode(frame_entry
->Clone()));
62 // Recursively clone the children.
63 for (auto& child
: children
) {
64 copy
->children
.push_back(
65 child
->CloneAndReplace(frame_tree_node
, frame_navigation_entry
));
71 scoped_ptr
<NavigationEntry
> NavigationEntry::Create() {
72 return make_scoped_ptr(new NavigationEntryImpl()).Pass();
75 NavigationEntryImpl
* NavigationEntryImpl::FromNavigationEntry(
76 NavigationEntry
* entry
) {
77 return static_cast<NavigationEntryImpl
*>(entry
);
80 const NavigationEntryImpl
* NavigationEntryImpl::FromNavigationEntry(
81 const NavigationEntry
* entry
) {
82 return static_cast<const NavigationEntryImpl
*>(entry
);
85 scoped_ptr
<NavigationEntryImpl
> NavigationEntryImpl::FromNavigationEntry(
86 scoped_ptr
<NavigationEntry
> entry
) {
87 return make_scoped_ptr(FromNavigationEntry(entry
.release()));
90 NavigationEntryImpl::NavigationEntryImpl()
91 : NavigationEntryImpl(nullptr, -1, GURL(), Referrer(), base::string16(),
92 ui::PAGE_TRANSITION_LINK
, false) {
95 NavigationEntryImpl::NavigationEntryImpl(SiteInstanceImpl
* instance
,
98 const Referrer
& referrer
,
99 const base::string16
& title
,
100 ui::PageTransition transition_type
,
101 bool is_renderer_initiated
)
102 : frame_tree_(new TreeNode(
103 new FrameNavigationEntry(-1, -1, -1, instance
, url
, referrer
))),
104 unique_id_(GetUniqueIDInConstructor()),
105 bindings_(kInvalidBindings
),
106 page_type_(PAGE_TYPE_NORMAL
),
107 update_virtual_url_with_url_(false),
110 transition_type_(transition_type
),
111 has_post_data_(false),
113 restore_type_(RESTORE_NONE
),
114 is_overriding_user_agent_(false),
115 http_status_code_(0),
116 is_renderer_initiated_(is_renderer_initiated
),
117 should_replace_entry_(false),
118 should_clear_history_list_(false),
119 can_load_local_resources_(false),
120 frame_tree_node_id_(-1) {
121 #if defined(OS_ANDROID)
122 has_user_gesture_
= false;
126 NavigationEntryImpl::~NavigationEntryImpl() {
129 int NavigationEntryImpl::GetUniqueID() const {
133 PageType
NavigationEntryImpl::GetPageType() const {
137 void NavigationEntryImpl::SetURL(const GURL
& url
) {
138 frame_tree_
->frame_entry
->set_url(url
);
139 cached_display_title_
.clear();
142 const GURL
& NavigationEntryImpl::GetURL() const {
143 return frame_tree_
->frame_entry
->url();
146 void NavigationEntryImpl::SetBaseURLForDataURL(const GURL
& url
) {
147 base_url_for_data_url_
= url
;
150 const GURL
& NavigationEntryImpl::GetBaseURLForDataURL() const {
151 return base_url_for_data_url_
;
154 void NavigationEntryImpl::SetReferrer(const Referrer
& referrer
) {
155 frame_tree_
->frame_entry
->set_referrer(referrer
);
158 const Referrer
& NavigationEntryImpl::GetReferrer() const {
159 return frame_tree_
->frame_entry
->referrer();
162 void NavigationEntryImpl::SetVirtualURL(const GURL
& url
) {
163 virtual_url_
= (url
== GetURL()) ? GURL() : url
;
164 cached_display_title_
.clear();
167 const GURL
& NavigationEntryImpl::GetVirtualURL() const {
168 return virtual_url_
.is_empty() ? GetURL() : virtual_url_
;
171 void NavigationEntryImpl::SetTitle(const base::string16
& title
) {
173 cached_display_title_
.clear();
176 const base::string16
& NavigationEntryImpl::GetTitle() const {
180 void NavigationEntryImpl::SetPageState(const PageState
& state
) {
181 frame_tree_
->frame_entry
->set_page_state(state
);
184 const PageState
& NavigationEntryImpl::GetPageState() const {
185 return frame_tree_
->frame_entry
->page_state();
188 void NavigationEntryImpl::SetPageID(int page_id
) {
192 int32
NavigationEntryImpl::GetPageID() const {
196 void NavigationEntryImpl::set_site_instance(SiteInstanceImpl
* site_instance
) {
197 // TODO(creis): Update all callers and remove this method.
198 frame_tree_
->frame_entry
->set_site_instance(site_instance
);
201 void NavigationEntryImpl::set_source_site_instance(
202 SiteInstanceImpl
* source_site_instance
) {
203 source_site_instance_
= source_site_instance
;
206 void NavigationEntryImpl::SetBindings(int bindings
) {
207 // Ensure this is set to a valid value, and that it stays the same once set.
208 CHECK_NE(bindings
, kInvalidBindings
);
209 CHECK(bindings_
== kInvalidBindings
|| bindings_
== bindings
);
210 bindings_
= bindings
;
213 const base::string16
& NavigationEntryImpl::GetTitleForDisplay(
214 const std::string
& languages
) const {
215 // Most pages have real titles. Don't even bother caching anything if this is
220 // More complicated cases will use the URLs as the title. This result we will
221 // cache since it's more complicated to compute.
222 if (!cached_display_title_
.empty())
223 return cached_display_title_
;
225 // Use the virtual URL first if any, and fall back on using the real URL.
226 base::string16 title
;
227 if (!virtual_url_
.is_empty()) {
228 title
= url_formatter::FormatUrl(virtual_url_
, languages
);
229 } else if (!GetURL().is_empty()) {
230 title
= url_formatter::FormatUrl(GetURL(), languages
);
233 // For file:// URLs use the filename as the title, not the full path.
234 if (GetURL().SchemeIsFile()) {
235 // It is necessary to ignore the reference and query parameters or else
236 // looking for slashes might accidentally return one of those values. See
237 // https://crbug.com/503003.
238 base::string16::size_type refpos
= title
.find('#');
239 base::string16::size_type querypos
= title
.find('?');
240 base::string16::size_type lastpos
;
241 if (refpos
== base::string16::npos
)
243 else if (querypos
== base::string16::npos
)
246 lastpos
= (refpos
< querypos
) ? refpos
: querypos
;
247 base::string16::size_type slashpos
= title
.rfind('/', lastpos
);
248 if (slashpos
!= base::string16::npos
)
249 title
= title
.substr(slashpos
+ 1);
252 gfx::ElideString(title
, kMaxTitleChars
, &cached_display_title_
);
253 return cached_display_title_
;
256 bool NavigationEntryImpl::IsViewSourceMode() const {
257 return virtual_url_
.SchemeIs(kViewSourceScheme
);
260 void NavigationEntryImpl::SetTransitionType(
261 ui::PageTransition transition_type
) {
262 transition_type_
= transition_type
;
265 ui::PageTransition
NavigationEntryImpl::GetTransitionType() const {
266 return transition_type_
;
269 const GURL
& NavigationEntryImpl::GetUserTypedURL() const {
270 return user_typed_url_
;
273 void NavigationEntryImpl::SetHasPostData(bool has_post_data
) {
274 has_post_data_
= has_post_data
;
277 bool NavigationEntryImpl::GetHasPostData() const {
278 return has_post_data_
;
281 void NavigationEntryImpl::SetPostID(int64 post_id
) {
285 int64
NavigationEntryImpl::GetPostID() const {
289 void NavigationEntryImpl::SetBrowserInitiatedPostData(
290 const base::RefCountedMemory
* data
) {
291 browser_initiated_post_data_
= data
;
294 const base::RefCountedMemory
*
295 NavigationEntryImpl::GetBrowserInitiatedPostData() const {
296 return browser_initiated_post_data_
.get();
300 const FaviconStatus
& NavigationEntryImpl::GetFavicon() const {
304 FaviconStatus
& NavigationEntryImpl::GetFavicon() {
308 const SSLStatus
& NavigationEntryImpl::GetSSL() const {
312 SSLStatus
& NavigationEntryImpl::GetSSL() {
316 void NavigationEntryImpl::SetOriginalRequestURL(const GURL
& original_url
) {
317 original_request_url_
= original_url
;
320 const GURL
& NavigationEntryImpl::GetOriginalRequestURL() const {
321 return original_request_url_
;
324 void NavigationEntryImpl::SetIsOverridingUserAgent(bool override
) {
325 is_overriding_user_agent_
= override
;
328 bool NavigationEntryImpl::GetIsOverridingUserAgent() const {
329 return is_overriding_user_agent_
;
332 void NavigationEntryImpl::SetTimestamp(base::Time timestamp
) {
333 timestamp_
= timestamp
;
336 base::Time
NavigationEntryImpl::GetTimestamp() const {
340 void NavigationEntryImpl::SetHttpStatusCode(int http_status_code
) {
341 http_status_code_
= http_status_code
;
344 int NavigationEntryImpl::GetHttpStatusCode() const {
345 return http_status_code_
;
348 void NavigationEntryImpl::SetRedirectChain(
349 const std::vector
<GURL
>& redirect_chain
) {
350 redirect_chain_
= redirect_chain
;
353 const std::vector
<GURL
>& NavigationEntryImpl::GetRedirectChain() const {
354 return redirect_chain_
;
357 bool NavigationEntryImpl::IsRestored() const {
358 return restore_type_
!= RESTORE_NONE
;
361 void NavigationEntryImpl::SetCanLoadLocalResources(bool allow
) {
362 can_load_local_resources_
= allow
;
365 bool NavigationEntryImpl::GetCanLoadLocalResources() const {
366 return can_load_local_resources_
;
369 void NavigationEntryImpl::SetExtraData(const std::string
& key
,
370 const base::string16
& data
) {
371 extra_data_
[key
] = data
;
374 bool NavigationEntryImpl::GetExtraData(const std::string
& key
,
375 base::string16
* data
) const {
376 std::map
<std::string
, base::string16
>::const_iterator iter
=
377 extra_data_
.find(key
);
378 if (iter
== extra_data_
.end())
380 *data
= iter
->second
;
384 void NavigationEntryImpl::ClearExtraData(const std::string
& key
) {
385 extra_data_
.erase(key
);
388 scoped_ptr
<NavigationEntryImpl
> NavigationEntryImpl::Clone() const {
389 return NavigationEntryImpl::CloneAndReplace(nullptr, nullptr);
392 scoped_ptr
<NavigationEntryImpl
> NavigationEntryImpl::CloneAndReplace(
393 FrameTreeNode
* frame_tree_node
,
394 FrameNavigationEntry
* frame_navigation_entry
) const {
395 scoped_ptr
<NavigationEntryImpl
> copy
=
396 make_scoped_ptr(new NavigationEntryImpl());
398 // TODO(creis): Only share the same FrameNavigationEntries if cloning within
401 frame_tree_
->CloneAndReplace(frame_tree_node
, frame_navigation_entry
);
403 // Copy most state over, unless cleared in ResetForCommit.
404 // Don't copy unique_id_, otherwise it won't be unique.
405 copy
->bindings_
= bindings_
;
406 copy
->page_type_
= page_type_
;
407 copy
->virtual_url_
= virtual_url_
;
408 copy
->update_virtual_url_with_url_
= update_virtual_url_with_url_
;
409 copy
->title_
= title_
;
410 copy
->favicon_
= favicon_
;
411 copy
->page_id_
= page_id_
;
413 copy
->transition_type_
= transition_type_
;
414 copy
->user_typed_url_
= user_typed_url_
;
415 copy
->has_post_data_
= has_post_data_
;
416 copy
->post_id_
= post_id_
;
417 copy
->restore_type_
= restore_type_
;
418 copy
->original_request_url_
= original_request_url_
;
419 copy
->is_overriding_user_agent_
= is_overriding_user_agent_
;
420 copy
->timestamp_
= timestamp_
;
421 copy
->http_status_code_
= http_status_code_
;
422 // ResetForCommit: browser_initiated_post_data_
423 copy
->screenshot_
= screenshot_
;
424 copy
->extra_headers_
= extra_headers_
;
425 // ResetForCommit: source_site_instance_
426 copy
->base_url_for_data_url_
= base_url_for_data_url_
;
427 // ResetForCommit: is_renderer_initiated_
428 copy
->cached_display_title_
= cached_display_title_
;
429 // ResetForCommit: transferred_global_request_id_
430 // ResetForCommit: should_replace_entry_
431 copy
->redirect_chain_
= redirect_chain_
;
432 // ResetForCommit: should_clear_history_list_
433 // ResetForCommit: frame_tree_node_id_
434 // ResetForCommit: intent_received_timestamp_
435 copy
->extra_data_
= extra_data_
;
440 CommonNavigationParams
NavigationEntryImpl::ConstructCommonNavigationParams(
441 const GURL
& dest_url
,
442 const Referrer
& dest_referrer
,
443 const FrameNavigationEntry
& frame_entry
,
444 FrameMsg_Navigate_Type::Value navigation_type
) const {
445 FrameMsg_UILoadMetricsReportType::Value report_type
=
446 FrameMsg_UILoadMetricsReportType::NO_REPORT
;
447 base::TimeTicks ui_timestamp
= base::TimeTicks();
448 #if defined(OS_ANDROID)
449 if (!intent_received_timestamp().is_null())
450 report_type
= FrameMsg_UILoadMetricsReportType::REPORT_INTENT
;
451 ui_timestamp
= intent_received_timestamp();
454 return CommonNavigationParams(
455 dest_url
, dest_referrer
, GetTransitionType(), navigation_type
,
456 !IsViewSourceMode(), should_replace_entry(), ui_timestamp
, report_type
,
457 GetBaseURLForDataURL(), GetHistoryURLForDataURL());
460 StartNavigationParams
NavigationEntryImpl::ConstructStartNavigationParams()
462 std::vector
<unsigned char> browser_initiated_post_data
;
463 if (GetBrowserInitiatedPostData()) {
464 browser_initiated_post_data
.assign(
465 GetBrowserInitiatedPostData()->front(),
466 GetBrowserInitiatedPostData()->front() +
467 GetBrowserInitiatedPostData()->size());
470 return StartNavigationParams(GetHasPostData(), extra_headers(),
471 browser_initiated_post_data
,
472 #if defined(OS_ANDROID)
475 transferred_global_request_id().child_id
,
476 transferred_global_request_id().request_id
);
479 RequestNavigationParams
NavigationEntryImpl::ConstructRequestNavigationParams(
480 const FrameNavigationEntry
& frame_entry
,
481 base::TimeTicks navigation_start
,
482 bool is_same_document_history_load
,
483 bool has_committed_real_load
,
484 bool intended_as_new_entry
,
485 int pending_history_list_offset
,
486 int current_history_list_offset
,
487 int current_history_list_length
) const {
488 // Set the redirect chain to the navigation's redirects, unless returning to a
489 // completed navigation (whose previous redirects don't apply).
490 std::vector
<GURL
> redirects
;
491 if (ui::PageTransitionIsNewNavigation(GetTransitionType())) {
492 redirects
= GetRedirectChain();
495 int pending_offset_to_send
= pending_history_list_offset
;
496 int current_offset_to_send
= current_history_list_offset
;
497 int current_length_to_send
= current_history_list_length
;
498 if (should_clear_history_list()) {
499 // Set the history list related parameters to the same values a
500 // NavigationController would return before its first navigation. This will
501 // fully clear the RenderView's view of the session history.
502 pending_offset_to_send
= -1;
503 current_offset_to_send
= -1;
504 current_length_to_send
= 0;
506 return RequestNavigationParams(
507 GetIsOverridingUserAgent(), navigation_start
, redirects
,
508 GetCanLoadLocalResources(), base::Time::Now(), frame_entry
.page_state(),
509 GetPageID(), GetUniqueID(), is_same_document_history_load
,
510 has_committed_real_load
, intended_as_new_entry
, pending_offset_to_send
,
511 current_offset_to_send
, current_length_to_send
,
512 should_clear_history_list());
515 void NavigationEntryImpl::ResetForCommit() {
516 // Any state that only matters when a navigation entry is pending should be
518 // TODO(creis): This state should be moved to NavigationRequest once
519 // PlzNavigate is enabled.
520 SetBrowserInitiatedPostData(nullptr);
521 set_source_site_instance(nullptr);
522 set_is_renderer_initiated(false);
523 set_transferred_global_request_id(GlobalRequestID());
524 set_should_replace_entry(false);
526 set_should_clear_history_list(false);
527 set_frame_tree_node_id(-1);
529 #if defined(OS_ANDROID)
530 // Reset the time stamp so that the metrics are not reported if this entry is
531 // loaded again in the future.
532 set_intent_received_timestamp(base::TimeTicks());
536 void NavigationEntryImpl::AddOrUpdateFrameEntry(FrameTreeNode
* frame_tree_node
,
537 int64 item_sequence_number
,
538 int64 document_sequence_number
,
539 SiteInstanceImpl
* site_instance
,
541 const Referrer
& referrer
,
542 const PageState
& page_state
) {
543 // We should already have a TreeNode for the parent node by the time this node
544 // commits. Find it first.
545 DCHECK(frame_tree_node
->parent());
546 NavigationEntryImpl::TreeNode
* parent_node
=
547 FindFrameEntry(frame_tree_node
->parent());
549 // The renderer should not send a commit for a subframe before its parent.
550 // TODO(creis): Kill the renderer if we get here.
554 // Now check whether we have a TreeNode for the node itself.
555 int frame_tree_node_id
= frame_tree_node
->frame_tree_node_id();
556 for (TreeNode
* child
: parent_node
->children
) {
557 if (child
->frame_entry
->frame_tree_node_id() == frame_tree_node_id
) {
558 // Update the existing FrameNavigationEntry (e.g., for replaceState).
559 child
->frame_entry
->UpdateEntry(item_sequence_number
,
560 document_sequence_number
, site_instance
,
561 url
, referrer
, page_state
);
566 // No entry exists yet, so create a new one.
567 // Unordered list, since we expect to look up entries by frame sequence number
569 FrameNavigationEntry
* frame_entry
= new FrameNavigationEntry(
570 frame_tree_node_id
, item_sequence_number
, document_sequence_number
,
571 site_instance
, url
, referrer
);
572 frame_entry
->set_page_state(page_state
);
573 parent_node
->children
.push_back(
574 new NavigationEntryImpl::TreeNode(frame_entry
));
577 FrameNavigationEntry
* NavigationEntryImpl::GetFrameEntry(
578 FrameTreeNode
* frame_tree_node
) const {
579 NavigationEntryImpl::TreeNode
* tree_node
= FindFrameEntry(frame_tree_node
);
580 return tree_node
? tree_node
->frame_entry
.get() : nullptr;
583 void NavigationEntryImpl::SetScreenshotPNGData(
584 scoped_refptr
<base::RefCountedBytes
> png_data
) {
585 screenshot_
= png_data
;
586 if (screenshot_
.get())
587 UMA_HISTOGRAM_MEMORY_KB("Overscroll.ScreenshotSize", screenshot_
->size());
590 GURL
NavigationEntryImpl::GetHistoryURLForDataURL() const {
591 return GetBaseURLForDataURL().is_empty() ? GURL() : GetVirtualURL();
594 NavigationEntryImpl::TreeNode
* NavigationEntryImpl::FindFrameEntry(
595 FrameTreeNode
* frame_tree_node
) const {
596 NavigationEntryImpl::TreeNode
* node
= nullptr;
597 std::queue
<NavigationEntryImpl::TreeNode
*> work_queue
;
598 work_queue
.push(root_node());
599 while (!work_queue
.empty()) {
600 node
= work_queue
.front();
602 if (node
->MatchesFrame(frame_tree_node
)) {
603 // Only the root TreeNode should have a FTN ID of -1.
604 DCHECK_IMPLIES(node
->frame_entry
->frame_tree_node_id() == -1,
605 node
== root_node());
608 // Enqueue any children and keep looking.
609 for (auto& child
: node
->children
)
610 work_queue
.push(child
);
615 } // namespace content