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 "components/sessions/serialized_navigation_entry.h"
7 #include "base/pickle.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "content/public/browser/favicon_status.h"
10 #include "content/public/browser/navigation_controller.h"
11 #include "content/public/browser/navigation_entry.h"
12 #include "sync/protocol/session_specifics.pb.h"
13 #include "sync/util/time.h"
14 #include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
16 using content::NavigationEntry
;
20 const char kSearchTermsKey
[] = "search_terms";
22 SerializedNavigationEntry::SerializedNavigationEntry()
25 transition_type_(content::PAGE_TRANSITION_TYPED
),
26 has_post_data_(false),
28 is_overriding_user_agent_(false),
31 blocked_state_(STATE_INVALID
) {}
33 SerializedNavigationEntry::~SerializedNavigationEntry() {}
36 SerializedNavigationEntry
SerializedNavigationEntry::FromNavigationEntry(
38 const NavigationEntry
& entry
) {
39 SerializedNavigationEntry navigation
;
40 navigation
.index_
= index
;
41 navigation
.unique_id_
= entry
.GetUniqueID();
42 navigation
.referrer_
= entry
.GetReferrer();
43 navigation
.virtual_url_
= entry
.GetVirtualURL();
44 navigation
.title_
= entry
.GetTitle();
45 navigation
.page_state_
= entry
.GetPageState();
46 navigation
.transition_type_
= entry
.GetTransitionType();
47 navigation
.has_post_data_
= entry
.GetHasPostData();
48 navigation
.post_id_
= entry
.GetPostID();
49 navigation
.original_request_url_
= entry
.GetOriginalRequestURL();
50 navigation
.is_overriding_user_agent_
= entry
.GetIsOverridingUserAgent();
51 navigation
.timestamp_
= entry
.GetTimestamp();
52 navigation
.is_restored_
= entry
.IsRestored();
53 // If you want to navigate a named frame in Chrome, you will first need to
54 // add support for persisting it. It is currently only used for layout tests.
55 CHECK(entry
.GetFrameToNavigate().empty());
56 entry
.GetExtraData(kSearchTermsKey
, &navigation
.search_terms_
);
57 if (entry
.GetFavicon().valid
)
58 navigation
.favicon_url_
= entry
.GetFavicon().url
;
59 navigation
.http_status_code_
= entry
.GetHttpStatusCode();
60 navigation
.redirect_chain_
= entry
.GetRedirectChain();
65 SerializedNavigationEntry
SerializedNavigationEntry::FromSyncData(
67 const sync_pb::TabNavigation
& sync_data
) {
68 SerializedNavigationEntry navigation
;
69 navigation
.index_
= index
;
70 navigation
.unique_id_
= sync_data
.unique_id();
71 navigation
.referrer_
= content::Referrer(
72 GURL(sync_data
.referrer()),
73 static_cast<blink::WebReferrerPolicy
>(sync_data
.referrer_policy()));
74 navigation
.virtual_url_
= GURL(sync_data
.virtual_url());
75 navigation
.title_
= base::UTF8ToUTF16(sync_data
.title());
76 navigation
.page_state_
=
77 content::PageState::CreateFromEncodedData(sync_data
.state());
79 uint32 transition
= 0;
80 if (sync_data
.has_page_transition()) {
81 switch (sync_data
.page_transition()) {
82 case sync_pb::SyncEnums_PageTransition_LINK
:
83 transition
= content::PAGE_TRANSITION_LINK
;
85 case sync_pb::SyncEnums_PageTransition_TYPED
:
86 transition
= content::PAGE_TRANSITION_TYPED
;
88 case sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK
:
89 transition
= content::PAGE_TRANSITION_AUTO_BOOKMARK
;
91 case sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME
:
92 transition
= content::PAGE_TRANSITION_AUTO_SUBFRAME
;
94 case sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME
:
95 transition
= content::PAGE_TRANSITION_MANUAL_SUBFRAME
;
97 case sync_pb::SyncEnums_PageTransition_GENERATED
:
98 transition
= content::PAGE_TRANSITION_GENERATED
;
100 case sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL
:
101 transition
= content::PAGE_TRANSITION_AUTO_TOPLEVEL
;
103 case sync_pb::SyncEnums_PageTransition_FORM_SUBMIT
:
104 transition
= content::PAGE_TRANSITION_FORM_SUBMIT
;
106 case sync_pb::SyncEnums_PageTransition_RELOAD
:
107 transition
= content::PAGE_TRANSITION_RELOAD
;
109 case sync_pb::SyncEnums_PageTransition_KEYWORD
:
110 transition
= content::PAGE_TRANSITION_KEYWORD
;
112 case sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED
:
114 content::PAGE_TRANSITION_KEYWORD_GENERATED
;
117 transition
= content::PAGE_TRANSITION_LINK
;
122 if (sync_data
.has_redirect_type()) {
123 switch (sync_data
.redirect_type()) {
124 case sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT
:
125 transition
|= content::PAGE_TRANSITION_CLIENT_REDIRECT
;
127 case sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT
:
128 transition
|= content::PAGE_TRANSITION_SERVER_REDIRECT
;
132 if (sync_data
.navigation_forward_back())
133 transition
|= content::PAGE_TRANSITION_FORWARD_BACK
;
134 if (sync_data
.navigation_from_address_bar())
135 transition
|= content::PAGE_TRANSITION_FROM_ADDRESS_BAR
;
136 if (sync_data
.navigation_home_page())
137 transition
|= content::PAGE_TRANSITION_HOME_PAGE
;
138 if (sync_data
.navigation_chain_start())
139 transition
|= content::PAGE_TRANSITION_CHAIN_START
;
140 if (sync_data
.navigation_chain_end())
141 transition
|= content::PAGE_TRANSITION_CHAIN_END
;
143 navigation
.transition_type_
=
144 static_cast<content::PageTransition
>(transition
);
146 navigation
.timestamp_
= base::Time();
147 navigation
.search_terms_
= base::UTF8ToUTF16(sync_data
.search_terms());
148 if (sync_data
.has_favicon_url())
149 navigation
.favicon_url_
= GURL(sync_data
.favicon_url());
151 navigation
.http_status_code_
= sync_data
.http_status_code();
153 navigation
.Sanitize();
155 navigation
.is_restored_
= true;
162 // Helper used by SerializedNavigationEntry::WriteToPickle(). It writes |str| to
163 // |pickle|, if and only if |str| fits within (|max_bytes| -
164 // |*bytes_written|). |bytes_written| is incremented to reflect the
167 // TODO(akalin): Unify this with the same function in
168 // base_session_service.cc.
169 void WriteStringToPickle(Pickle
* pickle
,
172 const std::string
& str
) {
173 int num_bytes
= str
.size() * sizeof(char);
174 if (*bytes_written
+ num_bytes
< max_bytes
) {
175 *bytes_written
+= num_bytes
;
176 pickle
->WriteString(str
);
178 pickle
->WriteString(std::string());
182 // base::string16 version of WriteStringToPickle.
184 // TODO(akalin): Unify this, too.
185 void WriteString16ToPickle(Pickle
* pickle
,
188 const base::string16
& str
) {
189 int num_bytes
= str
.size() * sizeof(base::char16
);
190 if (*bytes_written
+ num_bytes
< max_bytes
) {
191 *bytes_written
+= num_bytes
;
192 pickle
->WriteString16(str
);
194 pickle
->WriteString16(base::string16());
198 // A mask used for arbitrary boolean values needed to represent a
199 // NavigationEntry. Currently only contains HAS_POST_DATA.
201 // NOTE(akalin): We may want to just serialize |has_post_data_|
202 // directly. Other bools (|is_overriding_user_agent_|) haven't been
203 // added to this mask.
220 // type_mask (has_post_data_)
222 // original_request_url_
223 // is_overriding_user_agent_
228 void SerializedNavigationEntry::WriteToPickle(int max_size
,
229 Pickle
* pickle
) const {
230 pickle
->WriteInt(index_
);
232 int bytes_written
= 0;
234 WriteStringToPickle(pickle
, &bytes_written
, max_size
,
235 virtual_url_
.spec());
237 WriteString16ToPickle(pickle
, &bytes_written
, max_size
, title_
);
239 content::PageState page_state
= page_state_
;
241 page_state
= page_state
.RemovePasswordData();
243 WriteStringToPickle(pickle
, &bytes_written
, max_size
,
244 page_state
.ToEncodedData());
246 pickle
->WriteInt(transition_type_
);
248 const int type_mask
= has_post_data_
? HAS_POST_DATA
: 0;
249 pickle
->WriteInt(type_mask
);
252 pickle
, &bytes_written
, max_size
,
253 referrer_
.url
.is_valid() ? referrer_
.url
.spec() : std::string());
255 pickle
->WriteInt(referrer_
.policy
);
257 // Save info required to override the user agent.
259 pickle
, &bytes_written
, max_size
,
260 original_request_url_
.is_valid() ?
261 original_request_url_
.spec() : std::string());
262 pickle
->WriteBool(is_overriding_user_agent_
);
263 pickle
->WriteInt64(timestamp_
.ToInternalValue());
265 WriteString16ToPickle(pickle
, &bytes_written
, max_size
, search_terms_
);
267 pickle
->WriteInt(http_status_code_
);
270 bool SerializedNavigationEntry::ReadFromPickle(PickleIterator
* iterator
) {
271 *this = SerializedNavigationEntry();
272 std::string virtual_url_spec
, page_state_data
;
273 int transition_type_int
= 0;
274 if (!iterator
->ReadInt(&index_
) ||
275 !iterator
->ReadString(&virtual_url_spec
) ||
276 !iterator
->ReadString16(&title_
) ||
277 !iterator
->ReadString(&page_state_data
) ||
278 !iterator
->ReadInt(&transition_type_int
))
280 virtual_url_
= GURL(virtual_url_spec
);
281 page_state_
= content::PageState::CreateFromEncodedData(page_state_data
);
282 transition_type_
= static_cast<content::PageTransition
>(transition_type_int
);
284 // type_mask did not always exist in the written stream. As such, we
285 // don't fail if it can't be read.
287 bool has_type_mask
= iterator
->ReadInt(&type_mask
);
290 has_post_data_
= type_mask
& HAS_POST_DATA
;
291 // the "referrer" property was added after type_mask to the written
292 // stream. As such, we don't fail if it can't be read.
293 std::string referrer_spec
;
294 if (!iterator
->ReadString(&referrer_spec
))
295 referrer_spec
= std::string();
296 // The "referrer policy" property was added even later, so we fall back to
297 // the default policy if the property is not present.
299 blink::WebReferrerPolicy policy
;
300 if (iterator
->ReadInt(&policy_int
))
301 policy
= static_cast<blink::WebReferrerPolicy
>(policy_int
);
303 policy
= blink::WebReferrerPolicyDefault
;
304 referrer_
= content::Referrer(GURL(referrer_spec
), policy
);
306 // If the original URL can't be found, leave it empty.
307 std::string original_request_url_spec
;
308 if (!iterator
->ReadString(&original_request_url_spec
))
309 original_request_url_spec
= std::string();
310 original_request_url_
= GURL(original_request_url_spec
);
312 // Default to not overriding the user agent if we don't have info.
313 if (!iterator
->ReadBool(&is_overriding_user_agent_
))
314 is_overriding_user_agent_
= false;
316 int64 timestamp_internal_value
= 0;
317 if (iterator
->ReadInt64(×tamp_internal_value
)) {
318 timestamp_
= base::Time::FromInternalValue(timestamp_internal_value
);
320 timestamp_
= base::Time();
323 // If the search terms field can't be found, leave it empty.
324 if (!iterator
->ReadString16(&search_terms_
))
325 search_terms_
.clear();
327 if (!iterator
->ReadInt(&http_status_code_
))
328 http_status_code_
= 0;
338 scoped_ptr
<NavigationEntry
> SerializedNavigationEntry::ToNavigationEntry(
340 content::BrowserContext
* browser_context
) const {
341 scoped_ptr
<NavigationEntry
> entry(
342 content::NavigationController::CreateNavigationEntry(
345 // Use a transition type of reload so that we don't incorrectly
346 // increase the typed count.
347 content::PAGE_TRANSITION_RELOAD
,
349 // The extra headers are not sync'ed across sessions.
353 entry
->SetTitle(title_
);
354 entry
->SetPageState(page_state_
);
355 entry
->SetPageID(page_id
);
356 entry
->SetHasPostData(has_post_data_
);
357 entry
->SetPostID(post_id_
);
358 entry
->SetOriginalRequestURL(original_request_url_
);
359 entry
->SetIsOverridingUserAgent(is_overriding_user_agent_
);
360 entry
->SetTimestamp(timestamp_
);
361 entry
->SetExtraData(kSearchTermsKey
, search_terms_
);
362 entry
->SetHttpStatusCode(http_status_code_
);
363 entry
->SetRedirectChain(redirect_chain_
);
365 // These fields should have default values.
366 DCHECK_EQ(STATE_INVALID
, blocked_state_
);
367 DCHECK_EQ(0u, content_pack_categories_
.size());
372 // TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well?
373 // See http://crbug.com/67068.
374 sync_pb::TabNavigation
SerializedNavigationEntry::ToSyncData() const {
375 sync_pb::TabNavigation sync_data
;
376 sync_data
.set_virtual_url(virtual_url_
.spec());
377 sync_data
.set_referrer(referrer_
.url
.spec());
378 sync_data
.set_referrer_policy(referrer_
.policy
);
379 sync_data
.set_title(base::UTF16ToUTF8(title_
));
381 // Page transition core.
382 COMPILE_ASSERT(content::PAGE_TRANSITION_LAST_CORE
==
383 content::PAGE_TRANSITION_KEYWORD_GENERATED
,
384 PageTransitionCoreBounds
);
385 switch (PageTransitionStripQualifier(transition_type_
)) {
386 case content::PAGE_TRANSITION_LINK
:
387 sync_data
.set_page_transition(
388 sync_pb::SyncEnums_PageTransition_LINK
);
390 case content::PAGE_TRANSITION_TYPED
:
391 sync_data
.set_page_transition(
392 sync_pb::SyncEnums_PageTransition_TYPED
);
394 case content::PAGE_TRANSITION_AUTO_BOOKMARK
:
395 sync_data
.set_page_transition(
396 sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK
);
398 case content::PAGE_TRANSITION_AUTO_SUBFRAME
:
399 sync_data
.set_page_transition(
400 sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME
);
402 case content::PAGE_TRANSITION_MANUAL_SUBFRAME
:
403 sync_data
.set_page_transition(
404 sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME
);
406 case content::PAGE_TRANSITION_GENERATED
:
407 sync_data
.set_page_transition(
408 sync_pb::SyncEnums_PageTransition_GENERATED
);
410 case content::PAGE_TRANSITION_AUTO_TOPLEVEL
:
411 sync_data
.set_page_transition(
412 sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL
);
414 case content::PAGE_TRANSITION_FORM_SUBMIT
:
415 sync_data
.set_page_transition(
416 sync_pb::SyncEnums_PageTransition_FORM_SUBMIT
);
418 case content::PAGE_TRANSITION_RELOAD
:
419 sync_data
.set_page_transition(
420 sync_pb::SyncEnums_PageTransition_RELOAD
);
422 case content::PAGE_TRANSITION_KEYWORD
:
423 sync_data
.set_page_transition(
424 sync_pb::SyncEnums_PageTransition_KEYWORD
);
426 case content::PAGE_TRANSITION_KEYWORD_GENERATED
:
427 sync_data
.set_page_transition(
428 sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED
);
434 // Page transition qualifiers.
435 if (PageTransitionIsRedirect(transition_type_
)) {
436 if (transition_type_
& content::PAGE_TRANSITION_CLIENT_REDIRECT
) {
437 sync_data
.set_redirect_type(
438 sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT
);
439 } else if (transition_type_
& content::PAGE_TRANSITION_SERVER_REDIRECT
) {
440 sync_data
.set_redirect_type(
441 sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT
);
444 sync_data
.set_navigation_forward_back(
445 (transition_type_
& content::PAGE_TRANSITION_FORWARD_BACK
) != 0);
446 sync_data
.set_navigation_from_address_bar(
447 (transition_type_
& content::PAGE_TRANSITION_FROM_ADDRESS_BAR
) != 0);
448 sync_data
.set_navigation_home_page(
449 (transition_type_
& content::PAGE_TRANSITION_HOME_PAGE
) != 0);
450 sync_data
.set_navigation_chain_start(
451 (transition_type_
& content::PAGE_TRANSITION_CHAIN_START
) != 0);
452 sync_data
.set_navigation_chain_end(
453 (transition_type_
& content::PAGE_TRANSITION_CHAIN_END
) != 0);
455 sync_data
.set_unique_id(unique_id_
);
456 sync_data
.set_timestamp_msec(syncer::TimeToProtoTime(timestamp_
));
457 // The full-resolution timestamp works as a global ID.
458 sync_data
.set_global_id(timestamp_
.ToInternalValue());
460 sync_data
.set_search_terms(base::UTF16ToUTF8(search_terms_
));
462 sync_data
.set_http_status_code(http_status_code_
);
464 if (favicon_url_
.is_valid())
465 sync_data
.set_favicon_url(favicon_url_
.spec());
467 if (blocked_state_
!= STATE_INVALID
) {
468 sync_data
.set_blocked_state(
469 static_cast<sync_pb::TabNavigation_BlockedState
>(blocked_state_
));
472 for (std::set
<std::string
>::const_iterator it
=
473 content_pack_categories_
.begin();
474 it
!= content_pack_categories_
.end(); ++it
) {
475 sync_data
.add_content_pack_categories(*it
);
478 // Copy all redirect chain entries except the last URL (which should match
480 if (redirect_chain_
.size() > 1) { // Single entry chains have no redirection.
481 size_t last_entry
= redirect_chain_
.size() - 1;
482 for (size_t i
= 0; i
< last_entry
; i
++) {
483 sync_pb::NavigationRedirect
* navigation_redirect
=
484 sync_data
.add_navigation_redirect();
485 navigation_redirect
->set_url(redirect_chain_
[i
].spec());
487 // If the last URL didn't match the virtual_url, record it separately.
488 if (sync_data
.virtual_url() != redirect_chain_
[last_entry
].spec()) {
489 sync_data
.set_last_navigation_redirect_url(
490 redirect_chain_
[last_entry
].spec());
494 sync_data
.set_is_restored(is_restored_
);
500 std::vector
<NavigationEntry
*> SerializedNavigationEntry::ToNavigationEntries(
501 const std::vector
<SerializedNavigationEntry
>& navigations
,
502 content::BrowserContext
* browser_context
) {
504 std::vector
<NavigationEntry
*> entries
;
505 for (std::vector
<SerializedNavigationEntry
>::const_iterator
506 it
= navigations
.begin(); it
!= navigations
.end(); ++it
) {
508 it
->ToNavigationEntry(page_id
, browser_context
).release());
514 void SerializedNavigationEntry::Sanitize() {
515 content::Referrer new_referrer
=
516 content::Referrer::SanitizeForRequest(virtual_url_
, referrer_
);
518 // No need to compare the policy, as it doesn't change during
519 // sanitization. If there has been a change, the referrer needs to be
520 // stripped from the page state as well.
521 if (referrer_
.url
!= new_referrer
.url
) {
522 referrer_
= content::Referrer();
523 page_state_
= page_state_
.RemoveReferrer();
527 } // namespace sessions