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 "components/sessions/core/serialized_navigation_driver.h"
10 #include "sync/protocol/session_specifics.pb.h"
11 #include "sync/util/time.h"
15 const char kSearchTermsKey
[] = "search_terms";
17 SerializedNavigationEntry::SerializedNavigationEntry()
20 transition_type_(ui::PAGE_TRANSITION_TYPED
),
21 has_post_data_(false),
23 is_overriding_user_agent_(false),
26 blocked_state_(STATE_INVALID
) {
28 SerializedNavigationDriver::Get()->GetDefaultReferrerPolicy();
31 SerializedNavigationEntry::~SerializedNavigationEntry() {}
33 SerializedNavigationEntry
SerializedNavigationEntry::FromSyncData(
35 const sync_pb::TabNavigation
& sync_data
) {
36 SerializedNavigationEntry navigation
;
37 navigation
.index_
= index
;
38 navigation
.unique_id_
= sync_data
.unique_id();
39 navigation
.referrer_url_
= GURL(sync_data
.referrer());
40 navigation
.referrer_policy_
= sync_data
.referrer_policy();
41 navigation
.virtual_url_
= GURL(sync_data
.virtual_url());
42 navigation
.title_
= base::UTF8ToUTF16(sync_data
.title());
43 navigation
.encoded_page_state_
= sync_data
.state();
45 uint32 transition
= 0;
46 if (sync_data
.has_page_transition()) {
47 switch (sync_data
.page_transition()) {
48 case sync_pb::SyncEnums_PageTransition_LINK
:
49 transition
= ui::PAGE_TRANSITION_LINK
;
51 case sync_pb::SyncEnums_PageTransition_TYPED
:
52 transition
= ui::PAGE_TRANSITION_TYPED
;
54 case sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK
:
55 transition
= ui::PAGE_TRANSITION_AUTO_BOOKMARK
;
57 case sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME
:
58 transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
60 case sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME
:
61 transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
63 case sync_pb::SyncEnums_PageTransition_GENERATED
:
64 transition
= ui::PAGE_TRANSITION_GENERATED
;
66 case sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL
:
67 transition
= ui::PAGE_TRANSITION_AUTO_TOPLEVEL
;
69 case sync_pb::SyncEnums_PageTransition_FORM_SUBMIT
:
70 transition
= ui::PAGE_TRANSITION_FORM_SUBMIT
;
72 case sync_pb::SyncEnums_PageTransition_RELOAD
:
73 transition
= ui::PAGE_TRANSITION_RELOAD
;
75 case sync_pb::SyncEnums_PageTransition_KEYWORD
:
76 transition
= ui::PAGE_TRANSITION_KEYWORD
;
78 case sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED
:
79 transition
= ui::PAGE_TRANSITION_KEYWORD_GENERATED
;
82 transition
= ui::PAGE_TRANSITION_LINK
;
87 if (sync_data
.has_redirect_type()) {
88 switch (sync_data
.redirect_type()) {
89 case sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT
:
90 transition
|= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
92 case sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT
:
93 transition
|= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
97 if (sync_data
.navigation_forward_back())
98 transition
|= ui::PAGE_TRANSITION_FORWARD_BACK
;
99 if (sync_data
.navigation_from_address_bar())
100 transition
|= ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
;
101 if (sync_data
.navigation_home_page())
102 transition
|= ui::PAGE_TRANSITION_HOME_PAGE
;
103 if (sync_data
.navigation_chain_start())
104 transition
|= ui::PAGE_TRANSITION_CHAIN_START
;
105 if (sync_data
.navigation_chain_end())
106 transition
|= ui::PAGE_TRANSITION_CHAIN_END
;
108 navigation
.transition_type_
= static_cast<ui::PageTransition
>(transition
);
110 navigation
.timestamp_
= base::Time();
111 navigation
.search_terms_
= base::UTF8ToUTF16(sync_data
.search_terms());
112 if (sync_data
.has_favicon_url())
113 navigation
.favicon_url_
= GURL(sync_data
.favicon_url());
115 navigation
.http_status_code_
= sync_data
.http_status_code();
117 SerializedNavigationDriver::Get()->Sanitize(&navigation
);
119 navigation
.is_restored_
= true;
126 // Helper used by SerializedNavigationEntry::WriteToPickle(). It writes |str| to
127 // |pickle|, if and only if |str| fits within (|max_bytes| -
128 // |*bytes_written|). |bytes_written| is incremented to reflect the
131 // TODO(akalin): Unify this with the same function in
132 // base_session_service.cc.
133 void WriteStringToPickle(Pickle
* pickle
,
136 const std::string
& str
) {
137 int num_bytes
= str
.size() * sizeof(char);
138 if (*bytes_written
+ num_bytes
< max_bytes
) {
139 *bytes_written
+= num_bytes
;
140 pickle
->WriteString(str
);
142 pickle
->WriteString(std::string());
146 // base::string16 version of WriteStringToPickle.
148 // TODO(akalin): Unify this, too.
149 void WriteString16ToPickle(Pickle
* pickle
,
152 const base::string16
& str
) {
153 int num_bytes
= str
.size() * sizeof(base::char16
);
154 if (*bytes_written
+ num_bytes
< max_bytes
) {
155 *bytes_written
+= num_bytes
;
156 pickle
->WriteString16(str
);
158 pickle
->WriteString16(base::string16());
162 // A mask used for arbitrary boolean values needed to represent a
163 // NavigationEntry. Currently only contains HAS_POST_DATA.
165 // NOTE(akalin): We may want to just serialize |has_post_data_|
166 // directly. Other bools (|is_overriding_user_agent_|) haven't been
167 // added to this mask.
179 // encoded_page_state_
184 // type_mask (has_post_data_)
187 // original_request_url_
188 // is_overriding_user_agent_
193 void SerializedNavigationEntry::WriteToPickle(int max_size
,
194 Pickle
* pickle
) const {
195 pickle
->WriteInt(index_
);
197 int bytes_written
= 0;
199 WriteStringToPickle(pickle
, &bytes_written
, max_size
,
200 virtual_url_
.spec());
202 WriteString16ToPickle(pickle
, &bytes_written
, max_size
, title_
);
204 const std::string encoded_page_state
=
205 SerializedNavigationDriver::Get()->GetSanitizedPageStateForPickle(this);
206 WriteStringToPickle(pickle
, &bytes_written
, max_size
, encoded_page_state
);
208 pickle
->WriteInt(transition_type_
);
210 const int type_mask
= has_post_data_
? HAS_POST_DATA
: 0;
211 pickle
->WriteInt(type_mask
);
214 pickle
, &bytes_written
, max_size
,
215 referrer_url_
.is_valid() ? referrer_url_
.spec() : std::string());
217 pickle
->WriteInt(referrer_policy_
);
219 // Save info required to override the user agent.
221 pickle
, &bytes_written
, max_size
,
222 original_request_url_
.is_valid() ?
223 original_request_url_
.spec() : std::string());
224 pickle
->WriteBool(is_overriding_user_agent_
);
225 pickle
->WriteInt64(timestamp_
.ToInternalValue());
227 WriteString16ToPickle(pickle
, &bytes_written
, max_size
, search_terms_
);
229 pickle
->WriteInt(http_status_code_
);
232 bool SerializedNavigationEntry::ReadFromPickle(PickleIterator
* iterator
) {
233 *this = SerializedNavigationEntry();
234 std::string virtual_url_spec
;
235 int transition_type_int
= 0;
236 if (!iterator
->ReadInt(&index_
) ||
237 !iterator
->ReadString(&virtual_url_spec
) ||
238 !iterator
->ReadString16(&title_
) ||
239 !iterator
->ReadString(&encoded_page_state_
) ||
240 !iterator
->ReadInt(&transition_type_int
))
242 virtual_url_
= GURL(virtual_url_spec
);
243 transition_type_
= ui::PageTransitionFromInt(transition_type_int
);
245 // type_mask did not always exist in the written stream. As such, we
246 // don't fail if it can't be read.
248 bool has_type_mask
= iterator
->ReadInt(&type_mask
);
251 has_post_data_
= type_mask
& HAS_POST_DATA
;
252 // the "referrer" property was added after type_mask to the written
253 // stream. As such, we don't fail if it can't be read.
254 std::string referrer_spec
;
255 if (!iterator
->ReadString(&referrer_spec
))
256 referrer_spec
= std::string();
257 referrer_url_
= GURL(referrer_spec
);
259 // The "referrer policy" property was added even later, so we fall back to
260 // the default policy if the property is not present.
261 if (!iterator
->ReadInt(&referrer_policy_
))
263 SerializedNavigationDriver::Get()->GetDefaultReferrerPolicy();
265 // If the original URL can't be found, leave it empty.
266 std::string original_request_url_spec
;
267 if (!iterator
->ReadString(&original_request_url_spec
))
268 original_request_url_spec
= std::string();
269 original_request_url_
= GURL(original_request_url_spec
);
271 // Default to not overriding the user agent if we don't have info.
272 if (!iterator
->ReadBool(&is_overriding_user_agent_
))
273 is_overriding_user_agent_
= false;
275 int64 timestamp_internal_value
= 0;
276 if (iterator
->ReadInt64(×tamp_internal_value
)) {
277 timestamp_
= base::Time::FromInternalValue(timestamp_internal_value
);
279 timestamp_
= base::Time();
282 // If the search terms field can't be found, leave it empty.
283 if (!iterator
->ReadString16(&search_terms_
))
284 search_terms_
.clear();
286 if (!iterator
->ReadInt(&http_status_code_
))
287 http_status_code_
= 0;
290 SerializedNavigationDriver::Get()->Sanitize(this);
297 // TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well?
298 // See http://crbug.com/67068.
299 sync_pb::TabNavigation
SerializedNavigationEntry::ToSyncData() const {
300 sync_pb::TabNavigation sync_data
;
301 sync_data
.set_virtual_url(virtual_url_
.spec());
302 sync_data
.set_referrer(referrer_url_
.spec());
303 sync_data
.set_referrer_policy(referrer_policy_
);
304 sync_data
.set_title(base::UTF16ToUTF8(title_
));
306 // Page transition core.
307 COMPILE_ASSERT(ui::PAGE_TRANSITION_LAST_CORE
==
308 ui::PAGE_TRANSITION_KEYWORD_GENERATED
,
309 PageTransitionCoreBounds
);
310 switch (ui::PageTransitionStripQualifier(transition_type_
)) {
311 case ui::PAGE_TRANSITION_LINK
:
312 sync_data
.set_page_transition(
313 sync_pb::SyncEnums_PageTransition_LINK
);
315 case ui::PAGE_TRANSITION_TYPED
:
316 sync_data
.set_page_transition(
317 sync_pb::SyncEnums_PageTransition_TYPED
);
319 case ui::PAGE_TRANSITION_AUTO_BOOKMARK
:
320 sync_data
.set_page_transition(
321 sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK
);
323 case ui::PAGE_TRANSITION_AUTO_SUBFRAME
:
324 sync_data
.set_page_transition(
325 sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME
);
327 case ui::PAGE_TRANSITION_MANUAL_SUBFRAME
:
328 sync_data
.set_page_transition(
329 sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME
);
331 case ui::PAGE_TRANSITION_GENERATED
:
332 sync_data
.set_page_transition(
333 sync_pb::SyncEnums_PageTransition_GENERATED
);
335 case ui::PAGE_TRANSITION_AUTO_TOPLEVEL
:
336 sync_data
.set_page_transition(
337 sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL
);
339 case ui::PAGE_TRANSITION_FORM_SUBMIT
:
340 sync_data
.set_page_transition(
341 sync_pb::SyncEnums_PageTransition_FORM_SUBMIT
);
343 case ui::PAGE_TRANSITION_RELOAD
:
344 sync_data
.set_page_transition(
345 sync_pb::SyncEnums_PageTransition_RELOAD
);
347 case ui::PAGE_TRANSITION_KEYWORD
:
348 sync_data
.set_page_transition(
349 sync_pb::SyncEnums_PageTransition_KEYWORD
);
351 case ui::PAGE_TRANSITION_KEYWORD_GENERATED
:
352 sync_data
.set_page_transition(
353 sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED
);
359 // Page transition qualifiers.
360 if (ui::PageTransitionIsRedirect(transition_type_
)) {
361 if (transition_type_
& ui::PAGE_TRANSITION_CLIENT_REDIRECT
) {
362 sync_data
.set_redirect_type(
363 sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT
);
364 } else if (transition_type_
& ui::PAGE_TRANSITION_SERVER_REDIRECT
) {
365 sync_data
.set_redirect_type(
366 sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT
);
369 sync_data
.set_navigation_forward_back(
370 (transition_type_
& ui::PAGE_TRANSITION_FORWARD_BACK
) != 0);
371 sync_data
.set_navigation_from_address_bar(
372 (transition_type_
& ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
) != 0);
373 sync_data
.set_navigation_home_page(
374 (transition_type_
& ui::PAGE_TRANSITION_HOME_PAGE
) != 0);
375 sync_data
.set_navigation_chain_start(
376 (transition_type_
& ui::PAGE_TRANSITION_CHAIN_START
) != 0);
377 sync_data
.set_navigation_chain_end(
378 (transition_type_
& ui::PAGE_TRANSITION_CHAIN_END
) != 0);
380 sync_data
.set_unique_id(unique_id_
);
381 sync_data
.set_timestamp_msec(syncer::TimeToProtoTime(timestamp_
));
382 // The full-resolution timestamp works as a global ID.
383 sync_data
.set_global_id(timestamp_
.ToInternalValue());
385 sync_data
.set_search_terms(base::UTF16ToUTF8(search_terms_
));
387 sync_data
.set_http_status_code(http_status_code_
);
389 if (favicon_url_
.is_valid())
390 sync_data
.set_favicon_url(favicon_url_
.spec());
392 if (blocked_state_
!= STATE_INVALID
) {
393 sync_data
.set_blocked_state(
394 static_cast<sync_pb::TabNavigation_BlockedState
>(blocked_state_
));
397 for (std::set
<std::string
>::const_iterator it
=
398 content_pack_categories_
.begin();
399 it
!= content_pack_categories_
.end(); ++it
) {
400 sync_data
.add_content_pack_categories(*it
);
403 // Copy all redirect chain entries except the last URL (which should match
405 if (redirect_chain_
.size() > 1) { // Single entry chains have no redirection.
406 size_t last_entry
= redirect_chain_
.size() - 1;
407 for (size_t i
= 0; i
< last_entry
; i
++) {
408 sync_pb::NavigationRedirect
* navigation_redirect
=
409 sync_data
.add_navigation_redirect();
410 navigation_redirect
->set_url(redirect_chain_
[i
].spec());
412 // If the last URL didn't match the virtual_url, record it separately.
413 if (sync_data
.virtual_url() != redirect_chain_
[last_entry
].spec()) {
414 sync_data
.set_last_navigation_redirect_url(
415 redirect_chain_
[last_entry
].spec());
419 sync_data
.set_is_restored(is_restored_
);
424 } // namespace sessions