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),
30 blocked_state_(STATE_INVALID
) {}
32 SerializedNavigationEntry::~SerializedNavigationEntry() {}
35 SerializedNavigationEntry
SerializedNavigationEntry::FromNavigationEntry(
37 const NavigationEntry
& entry
) {
38 SerializedNavigationEntry navigation
;
39 navigation
.index_
= index
;
40 navigation
.unique_id_
= entry
.GetUniqueID();
41 navigation
.referrer_
= entry
.GetReferrer();
42 navigation
.virtual_url_
= entry
.GetVirtualURL();
43 navigation
.title_
= entry
.GetTitle();
44 navigation
.page_state_
= entry
.GetPageState();
45 navigation
.transition_type_
= entry
.GetTransitionType();
46 navigation
.has_post_data_
= entry
.GetHasPostData();
47 navigation
.post_id_
= entry
.GetPostID();
48 navigation
.original_request_url_
= entry
.GetOriginalRequestURL();
49 navigation
.is_overriding_user_agent_
= entry
.GetIsOverridingUserAgent();
50 navigation
.timestamp_
= entry
.GetTimestamp();
51 // If you want to navigate a named frame in Chrome, you will first need to
52 // add support for persisting it. It is currently only used for layout tests.
53 CHECK(entry
.GetFrameToNavigate().empty());
54 entry
.GetExtraData(kSearchTermsKey
, &navigation
.search_terms_
);
55 if (entry
.GetFavicon().valid
)
56 navigation
.favicon_url_
= entry
.GetFavicon().url
;
57 navigation
.http_status_code_
= entry
.GetHttpStatusCode();
62 SerializedNavigationEntry
SerializedNavigationEntry::FromSyncData(
64 const sync_pb::TabNavigation
& sync_data
) {
65 SerializedNavigationEntry navigation
;
66 navigation
.index_
= index
;
67 navigation
.unique_id_
= sync_data
.unique_id();
68 navigation
.referrer_
=
69 content::Referrer(GURL(sync_data
.referrer()),
70 WebKit::WebReferrerPolicyDefault
);
71 navigation
.virtual_url_
= GURL(sync_data
.virtual_url());
72 navigation
.title_
= UTF8ToUTF16(sync_data
.title());
73 navigation
.page_state_
=
74 content::PageState::CreateFromEncodedData(sync_data
.state());
76 uint32 transition
= 0;
77 if (sync_data
.has_page_transition()) {
78 switch (sync_data
.page_transition()) {
79 case sync_pb::SyncEnums_PageTransition_LINK
:
80 transition
= content::PAGE_TRANSITION_LINK
;
82 case sync_pb::SyncEnums_PageTransition_TYPED
:
83 transition
= content::PAGE_TRANSITION_TYPED
;
85 case sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK
:
86 transition
= content::PAGE_TRANSITION_AUTO_BOOKMARK
;
88 case sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME
:
89 transition
= content::PAGE_TRANSITION_AUTO_SUBFRAME
;
91 case sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME
:
92 transition
= content::PAGE_TRANSITION_MANUAL_SUBFRAME
;
94 case sync_pb::SyncEnums_PageTransition_GENERATED
:
95 transition
= content::PAGE_TRANSITION_GENERATED
;
97 case sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL
:
98 transition
= content::PAGE_TRANSITION_AUTO_TOPLEVEL
;
100 case sync_pb::SyncEnums_PageTransition_FORM_SUBMIT
:
101 transition
= content::PAGE_TRANSITION_FORM_SUBMIT
;
103 case sync_pb::SyncEnums_PageTransition_RELOAD
:
104 transition
= content::PAGE_TRANSITION_RELOAD
;
106 case sync_pb::SyncEnums_PageTransition_KEYWORD
:
107 transition
= content::PAGE_TRANSITION_KEYWORD
;
109 case sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED
:
111 content::PAGE_TRANSITION_KEYWORD_GENERATED
;
114 transition
= content::PAGE_TRANSITION_LINK
;
119 if (sync_data
.has_redirect_type()) {
120 switch (sync_data
.redirect_type()) {
121 case sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT
:
122 transition
|= content::PAGE_TRANSITION_CLIENT_REDIRECT
;
124 case sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT
:
125 transition
|= content::PAGE_TRANSITION_SERVER_REDIRECT
;
129 if (sync_data
.navigation_forward_back())
130 transition
|= content::PAGE_TRANSITION_FORWARD_BACK
;
131 if (sync_data
.navigation_from_address_bar())
132 transition
|= content::PAGE_TRANSITION_FROM_ADDRESS_BAR
;
133 if (sync_data
.navigation_home_page())
134 transition
|= content::PAGE_TRANSITION_HOME_PAGE
;
135 if (sync_data
.navigation_chain_start())
136 transition
|= content::PAGE_TRANSITION_CHAIN_START
;
137 if (sync_data
.navigation_chain_end())
138 transition
|= content::PAGE_TRANSITION_CHAIN_END
;
140 navigation
.transition_type_
=
141 static_cast<content::PageTransition
>(transition
);
143 navigation
.timestamp_
= base::Time();
144 navigation
.search_terms_
= UTF8ToUTF16(sync_data
.search_terms());
145 if (sync_data
.has_favicon_url())
146 navigation
.favicon_url_
= GURL(sync_data
.favicon_url());
148 navigation
.http_status_code_
= sync_data
.http_status_code();
150 // We shouldn't sync session data for managed users down at the moment.
151 DCHECK(!sync_data
.has_blocked_state());
152 DCHECK_EQ(0, sync_data
.content_pack_categories_size());
159 // Helper used by SerializedNavigationEntry::WriteToPickle(). It writes |str| to
160 // |pickle|, if and only if |str| fits within (|max_bytes| -
161 // |*bytes_written|). |bytes_written| is incremented to reflect the
164 // TODO(akalin): Unify this with the same function in
165 // base_session_service.cc.
166 void WriteStringToPickle(Pickle
* pickle
,
169 const std::string
& str
) {
170 int num_bytes
= str
.size() * sizeof(char);
171 if (*bytes_written
+ num_bytes
< max_bytes
) {
172 *bytes_written
+= num_bytes
;
173 pickle
->WriteString(str
);
175 pickle
->WriteString(std::string());
179 // string16 version of WriteStringToPickle.
181 // TODO(akalin): Unify this, too.
182 void WriteString16ToPickle(Pickle
* pickle
,
185 const string16
& str
) {
186 int num_bytes
= str
.size() * sizeof(char16
);
187 if (*bytes_written
+ num_bytes
< max_bytes
) {
188 *bytes_written
+= num_bytes
;
189 pickle
->WriteString16(str
);
191 pickle
->WriteString16(string16());
195 // A mask used for arbitrary boolean values needed to represent a
196 // NavigationEntry. Currently only contains HAS_POST_DATA.
198 // NOTE(akalin): We may want to just serialize |has_post_data_|
199 // directly. Other bools (|is_overriding_user_agent_|) haven't been
200 // added to this mask.
217 // type_mask (has_post_data_)
219 // original_request_url_
220 // is_overriding_user_agent_
225 void SerializedNavigationEntry::WriteToPickle(int max_size
,
226 Pickle
* pickle
) const {
227 pickle
->WriteInt(index_
);
229 int bytes_written
= 0;
231 WriteStringToPickle(pickle
, &bytes_written
, max_size
,
232 virtual_url_
.spec());
234 WriteString16ToPickle(pickle
, &bytes_written
, max_size
, title_
);
236 content::PageState page_state
= page_state_
;
238 page_state
= page_state
.RemovePasswordData();
240 WriteStringToPickle(pickle
, &bytes_written
, max_size
,
241 page_state
.ToEncodedData());
243 pickle
->WriteInt(transition_type_
);
245 const int type_mask
= has_post_data_
? HAS_POST_DATA
: 0;
246 pickle
->WriteInt(type_mask
);
249 pickle
, &bytes_written
, max_size
,
250 referrer_
.url
.is_valid() ? referrer_
.url
.spec() : std::string());
252 pickle
->WriteInt(referrer_
.policy
);
254 // Save info required to override the user agent.
256 pickle
, &bytes_written
, max_size
,
257 original_request_url_
.is_valid() ?
258 original_request_url_
.spec() : std::string());
259 pickle
->WriteBool(is_overriding_user_agent_
);
260 pickle
->WriteInt64(timestamp_
.ToInternalValue());
262 WriteString16ToPickle(pickle
, &bytes_written
, max_size
, search_terms_
);
264 pickle
->WriteInt(http_status_code_
);
267 bool SerializedNavigationEntry::ReadFromPickle(PickleIterator
* iterator
) {
268 *this = SerializedNavigationEntry();
269 std::string virtual_url_spec
, page_state_data
;
270 int transition_type_int
= 0;
271 if (!iterator
->ReadInt(&index_
) ||
272 !iterator
->ReadString(&virtual_url_spec
) ||
273 !iterator
->ReadString16(&title_
) ||
274 !iterator
->ReadString(&page_state_data
) ||
275 !iterator
->ReadInt(&transition_type_int
))
277 virtual_url_
= GURL(virtual_url_spec
);
278 page_state_
= content::PageState::CreateFromEncodedData(page_state_data
);
279 transition_type_
= static_cast<content::PageTransition
>(transition_type_int
);
281 // type_mask did not always exist in the written stream. As such, we
282 // don't fail if it can't be read.
284 bool has_type_mask
= iterator
->ReadInt(&type_mask
);
287 has_post_data_
= type_mask
& HAS_POST_DATA
;
288 // the "referrer" property was added after type_mask to the written
289 // stream. As such, we don't fail if it can't be read.
290 std::string referrer_spec
;
291 if (!iterator
->ReadString(&referrer_spec
))
292 referrer_spec
= std::string();
293 // The "referrer policy" property was added even later, so we fall back to
294 // the default policy if the property is not present.
296 WebKit::WebReferrerPolicy policy
;
297 if (iterator
->ReadInt(&policy_int
))
298 policy
= static_cast<WebKit::WebReferrerPolicy
>(policy_int
);
300 policy
= WebKit::WebReferrerPolicyDefault
;
301 referrer_
= content::Referrer(GURL(referrer_spec
), policy
);
303 // If the original URL can't be found, leave it empty.
304 std::string original_request_url_spec
;
305 if (!iterator
->ReadString(&original_request_url_spec
))
306 original_request_url_spec
= std::string();
307 original_request_url_
= GURL(original_request_url_spec
);
309 // Default to not overriding the user agent if we don't have info.
310 if (!iterator
->ReadBool(&is_overriding_user_agent_
))
311 is_overriding_user_agent_
= false;
313 int64 timestamp_internal_value
= 0;
314 if (iterator
->ReadInt64(×tamp_internal_value
)) {
315 timestamp_
= base::Time::FromInternalValue(timestamp_internal_value
);
317 timestamp_
= base::Time();
320 // If the search terms field can't be found, leave it empty.
321 if (!iterator
->ReadString16(&search_terms_
))
322 search_terms_
.clear();
324 if (!iterator
->ReadInt(&http_status_code_
))
325 http_status_code_
= 0;
331 scoped_ptr
<NavigationEntry
> SerializedNavigationEntry::ToNavigationEntry(
333 content::BrowserContext
* browser_context
) const {
334 scoped_ptr
<NavigationEntry
> entry(
335 content::NavigationController::CreateNavigationEntry(
338 // Use a transition type of reload so that we don't incorrectly
339 // increase the typed count.
340 content::PAGE_TRANSITION_RELOAD
,
342 // The extra headers are not sync'ed across sessions.
346 entry
->SetTitle(title_
);
347 entry
->SetPageState(page_state_
);
348 entry
->SetPageID(page_id
);
349 entry
->SetHasPostData(has_post_data_
);
350 entry
->SetPostID(post_id_
);
351 entry
->SetOriginalRequestURL(original_request_url_
);
352 entry
->SetIsOverridingUserAgent(is_overriding_user_agent_
);
353 entry
->SetTimestamp(timestamp_
);
354 entry
->SetExtraData(kSearchTermsKey
, search_terms_
);
355 entry
->SetHttpStatusCode(http_status_code_
);
357 // These fields should have default values.
358 DCHECK_EQ(STATE_INVALID
, blocked_state_
);
359 DCHECK_EQ(0u, content_pack_categories_
.size());
364 // TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well?
365 // See http://crbug.com/67068.
366 sync_pb::TabNavigation
SerializedNavigationEntry::ToSyncData() const {
367 sync_pb::TabNavigation sync_data
;
368 sync_data
.set_virtual_url(virtual_url_
.spec());
369 // FIXME(zea): Support referrer policy?
370 sync_data
.set_referrer(referrer_
.url
.spec());
371 sync_data
.set_title(UTF16ToUTF8(title_
));
373 // Page transition core.
374 COMPILE_ASSERT(content::PAGE_TRANSITION_LAST_CORE
==
375 content::PAGE_TRANSITION_KEYWORD_GENERATED
,
376 PageTransitionCoreBounds
);
377 switch (PageTransitionStripQualifier(transition_type_
)) {
378 case content::PAGE_TRANSITION_LINK
:
379 sync_data
.set_page_transition(
380 sync_pb::SyncEnums_PageTransition_LINK
);
382 case content::PAGE_TRANSITION_TYPED
:
383 sync_data
.set_page_transition(
384 sync_pb::SyncEnums_PageTransition_TYPED
);
386 case content::PAGE_TRANSITION_AUTO_BOOKMARK
:
387 sync_data
.set_page_transition(
388 sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK
);
390 case content::PAGE_TRANSITION_AUTO_SUBFRAME
:
391 sync_data
.set_page_transition(
392 sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME
);
394 case content::PAGE_TRANSITION_MANUAL_SUBFRAME
:
395 sync_data
.set_page_transition(
396 sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME
);
398 case content::PAGE_TRANSITION_GENERATED
:
399 sync_data
.set_page_transition(
400 sync_pb::SyncEnums_PageTransition_GENERATED
);
402 case content::PAGE_TRANSITION_AUTO_TOPLEVEL
:
403 sync_data
.set_page_transition(
404 sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL
);
406 case content::PAGE_TRANSITION_FORM_SUBMIT
:
407 sync_data
.set_page_transition(
408 sync_pb::SyncEnums_PageTransition_FORM_SUBMIT
);
410 case content::PAGE_TRANSITION_RELOAD
:
411 sync_data
.set_page_transition(
412 sync_pb::SyncEnums_PageTransition_RELOAD
);
414 case content::PAGE_TRANSITION_KEYWORD
:
415 sync_data
.set_page_transition(
416 sync_pb::SyncEnums_PageTransition_KEYWORD
);
418 case content::PAGE_TRANSITION_KEYWORD_GENERATED
:
419 sync_data
.set_page_transition(
420 sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED
);
426 // Page transition qualifiers.
427 if (PageTransitionIsRedirect(transition_type_
)) {
428 if (transition_type_
& content::PAGE_TRANSITION_CLIENT_REDIRECT
) {
429 sync_data
.set_redirect_type(
430 sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT
);
431 } else if (transition_type_
& content::PAGE_TRANSITION_SERVER_REDIRECT
) {
432 sync_data
.set_redirect_type(
433 sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT
);
436 sync_data
.set_navigation_forward_back(
437 (transition_type_
& content::PAGE_TRANSITION_FORWARD_BACK
) != 0);
438 sync_data
.set_navigation_from_address_bar(
439 (transition_type_
& content::PAGE_TRANSITION_FROM_ADDRESS_BAR
) != 0);
440 sync_data
.set_navigation_home_page(
441 (transition_type_
& content::PAGE_TRANSITION_HOME_PAGE
) != 0);
442 sync_data
.set_navigation_chain_start(
443 (transition_type_
& content::PAGE_TRANSITION_CHAIN_START
) != 0);
444 sync_data
.set_navigation_chain_end(
445 (transition_type_
& content::PAGE_TRANSITION_CHAIN_END
) != 0);
447 sync_data
.set_unique_id(unique_id_
);
448 sync_data
.set_timestamp_msec(syncer::TimeToProtoTime(timestamp_
));
449 // The full-resolution timestamp works as a global ID.
450 sync_data
.set_global_id(timestamp_
.ToInternalValue());
452 sync_data
.set_search_terms(UTF16ToUTF8(search_terms_
));
454 sync_data
.set_http_status_code(http_status_code_
);
456 if (favicon_url_
.is_valid())
457 sync_data
.set_favicon_url(favicon_url_
.spec());
459 if (blocked_state_
!= STATE_INVALID
) {
460 sync_data
.set_blocked_state(
461 static_cast<sync_pb::TabNavigation_BlockedState
>(blocked_state_
));
464 for (std::set
<std::string
>::const_iterator it
=
465 content_pack_categories_
.begin();
466 it
!= content_pack_categories_
.end(); ++it
) {
467 sync_data
.add_content_pack_categories(*it
);
474 std::vector
<NavigationEntry
*> SerializedNavigationEntry::ToNavigationEntries(
475 const std::vector
<SerializedNavigationEntry
>& navigations
,
476 content::BrowserContext
* browser_context
) {
478 std::vector
<NavigationEntry
*> entries
;
479 for (std::vector
<SerializedNavigationEntry
>::const_iterator
480 it
= navigations
.begin(); it
!= navigations
.end(); ++it
) {
482 it
->ToNavigationEntry(page_id
, browser_context
).release());
488 } // namespace sessions