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();
64 SerializedNavigationEntry SerializedNavigationEntry::FromSyncData(
66 const sync_pb::TabNavigation& sync_data) {
67 SerializedNavigationEntry navigation;
68 navigation.index_ = index;
69 navigation.unique_id_ = sync_data.unique_id();
70 navigation.referrer_ = content::Referrer(
71 GURL(sync_data.referrer()),
72 static_cast<blink::WebReferrerPolicy>(sync_data.referrer_policy()));
73 navigation.virtual_url_ = GURL(sync_data.virtual_url());
74 navigation.title_ = base::UTF8ToUTF16(sync_data.title());
75 navigation.page_state_ =
76 content::PageState::CreateFromEncodedData(sync_data.state());
78 uint32 transition = 0;
79 if (sync_data.has_page_transition()) {
80 switch (sync_data.page_transition()) {
81 case sync_pb::SyncEnums_PageTransition_LINK:
82 transition = content::PAGE_TRANSITION_LINK;
84 case sync_pb::SyncEnums_PageTransition_TYPED:
85 transition = content::PAGE_TRANSITION_TYPED;
87 case sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK:
88 transition = content::PAGE_TRANSITION_AUTO_BOOKMARK;
90 case sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME:
91 transition = content::PAGE_TRANSITION_AUTO_SUBFRAME;
93 case sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME:
94 transition = content::PAGE_TRANSITION_MANUAL_SUBFRAME;
96 case sync_pb::SyncEnums_PageTransition_GENERATED:
97 transition = content::PAGE_TRANSITION_GENERATED;
99 case sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL:
100 transition = content::PAGE_TRANSITION_AUTO_TOPLEVEL;
102 case sync_pb::SyncEnums_PageTransition_FORM_SUBMIT:
103 transition = content::PAGE_TRANSITION_FORM_SUBMIT;
105 case sync_pb::SyncEnums_PageTransition_RELOAD:
106 transition = content::PAGE_TRANSITION_RELOAD;
108 case sync_pb::SyncEnums_PageTransition_KEYWORD:
109 transition = content::PAGE_TRANSITION_KEYWORD;
111 case sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED:
113 content::PAGE_TRANSITION_KEYWORD_GENERATED;
116 transition = content::PAGE_TRANSITION_LINK;
121 if (sync_data.has_redirect_type()) {
122 switch (sync_data.redirect_type()) {
123 case sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT:
124 transition |= content::PAGE_TRANSITION_CLIENT_REDIRECT;
126 case sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT:
127 transition |= content::PAGE_TRANSITION_SERVER_REDIRECT;
131 if (sync_data.navigation_forward_back())
132 transition |= content::PAGE_TRANSITION_FORWARD_BACK;
133 if (sync_data.navigation_from_address_bar())
134 transition |= content::PAGE_TRANSITION_FROM_ADDRESS_BAR;
135 if (sync_data.navigation_home_page())
136 transition |= content::PAGE_TRANSITION_HOME_PAGE;
137 if (sync_data.navigation_chain_start())
138 transition |= content::PAGE_TRANSITION_CHAIN_START;
139 if (sync_data.navigation_chain_end())
140 transition |= content::PAGE_TRANSITION_CHAIN_END;
142 navigation.transition_type_ =
143 static_cast<content::PageTransition>(transition);
145 navigation.timestamp_ = base::Time();
146 navigation.search_terms_ = base::UTF8ToUTF16(sync_data.search_terms());
147 if (sync_data.has_favicon_url())
148 navigation.favicon_url_ = GURL(sync_data.favicon_url());
150 navigation.http_status_code_ = sync_data.http_status_code();
152 // We shouldn't sync session data for managed users down at the moment.
153 DCHECK(!sync_data.has_blocked_state());
154 DCHECK_EQ(0, sync_data.content_pack_categories_size());
156 navigation.Sanitize();
158 navigation.is_restored_ = true;
165 // Helper used by SerializedNavigationEntry::WriteToPickle(). It writes |str| to
166 // |pickle|, if and only if |str| fits within (|max_bytes| -
167 // |*bytes_written|). |bytes_written| is incremented to reflect the
170 // TODO(akalin): Unify this with the same function in
171 // base_session_service.cc.
172 void WriteStringToPickle(Pickle* pickle,
175 const std::string& str) {
176 int num_bytes = str.size() * sizeof(char);
177 if (*bytes_written + num_bytes < max_bytes) {
178 *bytes_written += num_bytes;
179 pickle->WriteString(str);
181 pickle->WriteString(std::string());
185 // base::string16 version of WriteStringToPickle.
187 // TODO(akalin): Unify this, too.
188 void WriteString16ToPickle(Pickle* pickle,
191 const base::string16& str) {
192 int num_bytes = str.size() * sizeof(base::char16);
193 if (*bytes_written + num_bytes < max_bytes) {
194 *bytes_written += num_bytes;
195 pickle->WriteString16(str);
197 pickle->WriteString16(base::string16());
201 // A mask used for arbitrary boolean values needed to represent a
202 // NavigationEntry. Currently only contains HAS_POST_DATA.
204 // NOTE(akalin): We may want to just serialize |has_post_data_|
205 // directly. Other bools (|is_overriding_user_agent_|) haven't been
206 // added to this mask.
223 // type_mask (has_post_data_)
225 // original_request_url_
226 // is_overriding_user_agent_
231 void SerializedNavigationEntry::WriteToPickle(int max_size,
232 Pickle* pickle) const {
233 pickle->WriteInt(index_);
235 int bytes_written = 0;
237 WriteStringToPickle(pickle, &bytes_written, max_size,
238 virtual_url_.spec());
240 WriteString16ToPickle(pickle, &bytes_written, max_size, title_);
242 content::PageState page_state = page_state_;
244 page_state = page_state.RemovePasswordData();
246 WriteStringToPickle(pickle, &bytes_written, max_size,
247 page_state.ToEncodedData());
249 pickle->WriteInt(transition_type_);
251 const int type_mask = has_post_data_ ? HAS_POST_DATA : 0;
252 pickle->WriteInt(type_mask);
255 pickle, &bytes_written, max_size,
256 referrer_.url.is_valid() ? referrer_.url.spec() : std::string());
258 pickle->WriteInt(referrer_.policy);
260 // Save info required to override the user agent.
262 pickle, &bytes_written, max_size,
263 original_request_url_.is_valid() ?
264 original_request_url_.spec() : std::string());
265 pickle->WriteBool(is_overriding_user_agent_);
266 pickle->WriteInt64(timestamp_.ToInternalValue());
268 WriteString16ToPickle(pickle, &bytes_written, max_size, search_terms_);
270 pickle->WriteInt(http_status_code_);
273 bool SerializedNavigationEntry::ReadFromPickle(PickleIterator* iterator) {
274 *this = SerializedNavigationEntry();
275 std::string virtual_url_spec, page_state_data;
276 int transition_type_int = 0;
277 if (!iterator->ReadInt(&index_) ||
278 !iterator->ReadString(&virtual_url_spec) ||
279 !iterator->ReadString16(&title_) ||
280 !iterator->ReadString(&page_state_data) ||
281 !iterator->ReadInt(&transition_type_int))
283 virtual_url_ = GURL(virtual_url_spec);
284 page_state_ = content::PageState::CreateFromEncodedData(page_state_data);
285 transition_type_ = static_cast<content::PageTransition>(transition_type_int);
287 // type_mask did not always exist in the written stream. As such, we
288 // don't fail if it can't be read.
290 bool has_type_mask = iterator->ReadInt(&type_mask);
293 has_post_data_ = type_mask & HAS_POST_DATA;
294 // the "referrer" property was added after type_mask to the written
295 // stream. As such, we don't fail if it can't be read.
296 std::string referrer_spec;
297 if (!iterator->ReadString(&referrer_spec))
298 referrer_spec = std::string();
299 // The "referrer policy" property was added even later, so we fall back to
300 // the default policy if the property is not present.
302 blink::WebReferrerPolicy policy;
303 if (iterator->ReadInt(&policy_int))
304 policy = static_cast<blink::WebReferrerPolicy>(policy_int);
306 policy = blink::WebReferrerPolicyDefault;
307 referrer_ = content::Referrer(GURL(referrer_spec), policy);
309 // If the original URL can't be found, leave it empty.
310 std::string original_request_url_spec;
311 if (!iterator->ReadString(&original_request_url_spec))
312 original_request_url_spec = std::string();
313 original_request_url_ = GURL(original_request_url_spec);
315 // Default to not overriding the user agent if we don't have info.
316 if (!iterator->ReadBool(&is_overriding_user_agent_))
317 is_overriding_user_agent_ = false;
319 int64 timestamp_internal_value = 0;
320 if (iterator->ReadInt64(×tamp_internal_value)) {
321 timestamp_ = base::Time::FromInternalValue(timestamp_internal_value);
323 timestamp_ = base::Time();
326 // If the search terms field can't be found, leave it empty.
327 if (!iterator->ReadString16(&search_terms_))
328 search_terms_.clear();
330 if (!iterator->ReadInt(&http_status_code_))
331 http_status_code_ = 0;
341 scoped_ptr<NavigationEntry> SerializedNavigationEntry::ToNavigationEntry(
343 content::BrowserContext* browser_context) const {
344 scoped_ptr<NavigationEntry> entry(
345 content::NavigationController::CreateNavigationEntry(
348 // Use a transition type of reload so that we don't incorrectly
349 // increase the typed count.
350 content::PAGE_TRANSITION_RELOAD,
352 // The extra headers are not sync'ed across sessions.
356 entry->SetTitle(title_);
357 entry->SetPageState(page_state_);
358 entry->SetPageID(page_id);
359 entry->SetHasPostData(has_post_data_);
360 entry->SetPostID(post_id_);
361 entry->SetOriginalRequestURL(original_request_url_);
362 entry->SetIsOverridingUserAgent(is_overriding_user_agent_);
363 entry->SetTimestamp(timestamp_);
364 entry->SetExtraData(kSearchTermsKey, search_terms_);
365 entry->SetHttpStatusCode(http_status_code_);
367 // These fields should have default values.
368 DCHECK_EQ(STATE_INVALID, blocked_state_);
369 DCHECK_EQ(0u, content_pack_categories_.size());
374 // TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well?
375 // See http://crbug.com/67068.
376 sync_pb::TabNavigation SerializedNavigationEntry::ToSyncData() const {
377 sync_pb::TabNavigation sync_data;
378 sync_data.set_virtual_url(virtual_url_.spec());
379 sync_data.set_referrer(referrer_.url.spec());
380 sync_data.set_referrer_policy(referrer_.policy);
381 sync_data.set_title(base::UTF16ToUTF8(title_));
383 // Page transition core.
384 COMPILE_ASSERT(content::PAGE_TRANSITION_LAST_CORE ==
385 content::PAGE_TRANSITION_KEYWORD_GENERATED,
386 PageTransitionCoreBounds);
387 switch (PageTransitionStripQualifier(transition_type_)) {
388 case content::PAGE_TRANSITION_LINK:
389 sync_data.set_page_transition(
390 sync_pb::SyncEnums_PageTransition_LINK);
392 case content::PAGE_TRANSITION_TYPED:
393 sync_data.set_page_transition(
394 sync_pb::SyncEnums_PageTransition_TYPED);
396 case content::PAGE_TRANSITION_AUTO_BOOKMARK:
397 sync_data.set_page_transition(
398 sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK);
400 case content::PAGE_TRANSITION_AUTO_SUBFRAME:
401 sync_data.set_page_transition(
402 sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME);
404 case content::PAGE_TRANSITION_MANUAL_SUBFRAME:
405 sync_data.set_page_transition(
406 sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME);
408 case content::PAGE_TRANSITION_GENERATED:
409 sync_data.set_page_transition(
410 sync_pb::SyncEnums_PageTransition_GENERATED);
412 case content::PAGE_TRANSITION_AUTO_TOPLEVEL:
413 sync_data.set_page_transition(
414 sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL);
416 case content::PAGE_TRANSITION_FORM_SUBMIT:
417 sync_data.set_page_transition(
418 sync_pb::SyncEnums_PageTransition_FORM_SUBMIT);
420 case content::PAGE_TRANSITION_RELOAD:
421 sync_data.set_page_transition(
422 sync_pb::SyncEnums_PageTransition_RELOAD);
424 case content::PAGE_TRANSITION_KEYWORD:
425 sync_data.set_page_transition(
426 sync_pb::SyncEnums_PageTransition_KEYWORD);
428 case content::PAGE_TRANSITION_KEYWORD_GENERATED:
429 sync_data.set_page_transition(
430 sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED);
436 // Page transition qualifiers.
437 if (PageTransitionIsRedirect(transition_type_)) {
438 if (transition_type_ & content::PAGE_TRANSITION_CLIENT_REDIRECT) {
439 sync_data.set_redirect_type(
440 sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT);
441 } else if (transition_type_ & content::PAGE_TRANSITION_SERVER_REDIRECT) {
442 sync_data.set_redirect_type(
443 sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT);
446 sync_data.set_navigation_forward_back(
447 (transition_type_ & content::PAGE_TRANSITION_FORWARD_BACK) != 0);
448 sync_data.set_navigation_from_address_bar(
449 (transition_type_ & content::PAGE_TRANSITION_FROM_ADDRESS_BAR) != 0);
450 sync_data.set_navigation_home_page(
451 (transition_type_ & content::PAGE_TRANSITION_HOME_PAGE) != 0);
452 sync_data.set_navigation_chain_start(
453 (transition_type_ & content::PAGE_TRANSITION_CHAIN_START) != 0);
454 sync_data.set_navigation_chain_end(
455 (transition_type_ & content::PAGE_TRANSITION_CHAIN_END) != 0);
457 sync_data.set_unique_id(unique_id_);
458 sync_data.set_timestamp_msec(syncer::TimeToProtoTime(timestamp_));
459 // The full-resolution timestamp works as a global ID.
460 sync_data.set_global_id(timestamp_.ToInternalValue());
462 sync_data.set_search_terms(base::UTF16ToUTF8(search_terms_));
464 sync_data.set_http_status_code(http_status_code_);
466 if (favicon_url_.is_valid())
467 sync_data.set_favicon_url(favicon_url_.spec());
469 if (blocked_state_ != STATE_INVALID) {
470 sync_data.set_blocked_state(
471 static_cast<sync_pb::TabNavigation_BlockedState>(blocked_state_));
474 for (std::set<std::string>::const_iterator it =
475 content_pack_categories_.begin();
476 it != content_pack_categories_.end(); ++it) {
477 sync_data.add_content_pack_categories(*it);
480 sync_data.set_is_restored(is_restored_);
486 std::vector<NavigationEntry*> SerializedNavigationEntry::ToNavigationEntries(
487 const std::vector<SerializedNavigationEntry>& navigations,
488 content::BrowserContext* browser_context) {
490 std::vector<NavigationEntry*> entries;
491 for (std::vector<SerializedNavigationEntry>::const_iterator
492 it = navigations.begin(); it != navigations.end(); ++it) {
494 it->ToNavigationEntry(page_id, browser_context).release());
500 void SerializedNavigationEntry::Sanitize() {
501 // Store original referrer so we can later see whether it was actually
502 // changed during sanitization, and we need to strip the referrer from the
503 // page state as well.
504 content::Referrer old_referrer = referrer_;
506 if (!referrer_.url.SchemeIsHTTPOrHTTPS())
507 referrer_ = content::Referrer();
508 switch (referrer_.policy) {
509 case blink::WebReferrerPolicyNever:
510 referrer_.url = GURL();
512 case blink::WebReferrerPolicyAlways:
514 case blink::WebReferrerPolicyOrigin:
515 referrer_.url = referrer_.url.GetWithEmptyPath();
517 case blink::WebReferrerPolicyDefault:
520 referrer_.policy = blink::WebReferrerPolicyDefault;
521 if (referrer_.url.SchemeIsSecure() && !virtual_url_.SchemeIsSecure())
522 referrer_.url = GURL();
525 if (referrer_.url != old_referrer.url ||
526 referrer_.policy != old_referrer.policy) {
527 referrer_ = content::Referrer();
528 page_state_ = page_state_.RemoveReferrer();
532 } // namespace sessions