44c5a0798990c16012ca6b25982bbfbdc422d082
[platform/framework/web/crosswalk.git] / src / components / sessions / serialized_navigation_entry.cc
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.
4
5 #include "components/sessions/serialized_navigation_entry.h"
6
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"
15
16 using content::NavigationEntry;
17
18 namespace sessions {
19
20 const char kSearchTermsKey[] = "search_terms";
21
22 SerializedNavigationEntry::SerializedNavigationEntry()
23     : index_(-1),
24       unique_id_(0),
25       transition_type_(content::PAGE_TRANSITION_TYPED),
26       has_post_data_(false),
27       post_id_(-1),
28       is_overriding_user_agent_(false),
29       http_status_code_(0),
30       blocked_state_(STATE_INVALID) {}
31
32 SerializedNavigationEntry::~SerializedNavigationEntry() {}
33
34 // static
35 SerializedNavigationEntry SerializedNavigationEntry::FromNavigationEntry(
36     int index,
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();
58
59   return navigation;
60 }
61
62 SerializedNavigationEntry SerializedNavigationEntry::FromSyncData(
63     int index,
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_ = content::Referrer(
69       GURL(sync_data.referrer()),
70       static_cast<blink::WebReferrerPolicy>(sync_data.referrer_policy()));
71   navigation.virtual_url_ = GURL(sync_data.virtual_url());
72   navigation.title_ = base::UTF8ToUTF16(sync_data.title());
73   navigation.page_state_ =
74       content::PageState::CreateFromEncodedData(sync_data.state());
75
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;
81         break;
82       case sync_pb::SyncEnums_PageTransition_TYPED:
83         transition = content::PAGE_TRANSITION_TYPED;
84         break;
85       case sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK:
86         transition = content::PAGE_TRANSITION_AUTO_BOOKMARK;
87         break;
88       case sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME:
89         transition = content::PAGE_TRANSITION_AUTO_SUBFRAME;
90         break;
91       case sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME:
92         transition = content::PAGE_TRANSITION_MANUAL_SUBFRAME;
93         break;
94       case sync_pb::SyncEnums_PageTransition_GENERATED:
95         transition = content::PAGE_TRANSITION_GENERATED;
96         break;
97       case sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL:
98         transition = content::PAGE_TRANSITION_AUTO_TOPLEVEL;
99         break;
100       case sync_pb::SyncEnums_PageTransition_FORM_SUBMIT:
101         transition = content::PAGE_TRANSITION_FORM_SUBMIT;
102         break;
103       case sync_pb::SyncEnums_PageTransition_RELOAD:
104         transition = content::PAGE_TRANSITION_RELOAD;
105         break;
106       case sync_pb::SyncEnums_PageTransition_KEYWORD:
107         transition = content::PAGE_TRANSITION_KEYWORD;
108         break;
109       case sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED:
110         transition =
111             content::PAGE_TRANSITION_KEYWORD_GENERATED;
112         break;
113       default:
114         transition = content::PAGE_TRANSITION_LINK;
115         break;
116     }
117   }
118
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;
123         break;
124       case sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT:
125         transition |= content::PAGE_TRANSITION_SERVER_REDIRECT;
126         break;
127     }
128   }
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;
139
140   navigation.transition_type_ =
141       static_cast<content::PageTransition>(transition);
142
143   navigation.timestamp_ = base::Time();
144   navigation.search_terms_ = base::UTF8ToUTF16(sync_data.search_terms());
145   if (sync_data.has_favicon_url())
146     navigation.favicon_url_ = GURL(sync_data.favicon_url());
147
148   navigation.http_status_code_ = sync_data.http_status_code();
149
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());
153
154   navigation.Sanitize();
155
156   return navigation;
157 }
158
159 namespace {
160
161 // Helper used by SerializedNavigationEntry::WriteToPickle(). It writes |str| to
162 // |pickle|, if and only if |str| fits within (|max_bytes| -
163 // |*bytes_written|).  |bytes_written| is incremented to reflect the
164 // data written.
165 //
166 // TODO(akalin): Unify this with the same function in
167 // base_session_service.cc.
168 void WriteStringToPickle(Pickle* pickle,
169                          int* bytes_written,
170                          int max_bytes,
171                          const std::string& str) {
172   int num_bytes = str.size() * sizeof(char);
173   if (*bytes_written + num_bytes < max_bytes) {
174     *bytes_written += num_bytes;
175     pickle->WriteString(str);
176   } else {
177     pickle->WriteString(std::string());
178   }
179 }
180
181 // base::string16 version of WriteStringToPickle.
182 //
183 // TODO(akalin): Unify this, too.
184 void WriteString16ToPickle(Pickle* pickle,
185                            int* bytes_written,
186                            int max_bytes,
187                            const base::string16& str) {
188   int num_bytes = str.size() * sizeof(base::char16);
189   if (*bytes_written + num_bytes < max_bytes) {
190     *bytes_written += num_bytes;
191     pickle->WriteString16(str);
192   } else {
193     pickle->WriteString16(base::string16());
194   }
195 }
196
197 // A mask used for arbitrary boolean values needed to represent a
198 // NavigationEntry. Currently only contains HAS_POST_DATA.
199 //
200 // NOTE(akalin): We may want to just serialize |has_post_data_|
201 // directly.  Other bools (|is_overriding_user_agent_|) haven't been
202 // added to this mask.
203 enum TypeMask {
204   HAS_POST_DATA = 1
205 };
206
207 }  // namespace
208
209 // Pickle order:
210 //
211 // index_
212 // virtual_url_
213 // title_
214 // page_state_
215 // transition_type_
216 //
217 // Added on later:
218 //
219 // type_mask (has_post_data_)
220 // referrer_
221 // original_request_url_
222 // is_overriding_user_agent_
223 // timestamp_
224 // search_terms_
225 // http_status_code_
226
227 void SerializedNavigationEntry::WriteToPickle(int max_size,
228                                               Pickle* pickle) const {
229   pickle->WriteInt(index_);
230
231   int bytes_written = 0;
232
233   WriteStringToPickle(pickle, &bytes_written, max_size,
234                       virtual_url_.spec());
235
236   WriteString16ToPickle(pickle, &bytes_written, max_size, title_);
237
238   content::PageState page_state = page_state_;
239   if (has_post_data_)
240     page_state = page_state.RemovePasswordData();
241
242   WriteStringToPickle(pickle, &bytes_written, max_size,
243                       page_state.ToEncodedData());
244
245   pickle->WriteInt(transition_type_);
246
247   const int type_mask = has_post_data_ ? HAS_POST_DATA : 0;
248   pickle->WriteInt(type_mask);
249
250   WriteStringToPickle(
251       pickle, &bytes_written, max_size,
252       referrer_.url.is_valid() ? referrer_.url.spec() : std::string());
253
254   pickle->WriteInt(referrer_.policy);
255
256   // Save info required to override the user agent.
257   WriteStringToPickle(
258       pickle, &bytes_written, max_size,
259       original_request_url_.is_valid() ?
260       original_request_url_.spec() : std::string());
261   pickle->WriteBool(is_overriding_user_agent_);
262   pickle->WriteInt64(timestamp_.ToInternalValue());
263
264   WriteString16ToPickle(pickle, &bytes_written, max_size, search_terms_);
265
266   pickle->WriteInt(http_status_code_);
267 }
268
269 bool SerializedNavigationEntry::ReadFromPickle(PickleIterator* iterator) {
270   *this = SerializedNavigationEntry();
271   std::string virtual_url_spec, page_state_data;
272   int transition_type_int = 0;
273   if (!iterator->ReadInt(&index_) ||
274       !iterator->ReadString(&virtual_url_spec) ||
275       !iterator->ReadString16(&title_) ||
276       !iterator->ReadString(&page_state_data) ||
277       !iterator->ReadInt(&transition_type_int))
278     return false;
279   virtual_url_ = GURL(virtual_url_spec);
280   page_state_ = content::PageState::CreateFromEncodedData(page_state_data);
281   transition_type_ = static_cast<content::PageTransition>(transition_type_int);
282
283   // type_mask did not always exist in the written stream. As such, we
284   // don't fail if it can't be read.
285   int type_mask = 0;
286   bool has_type_mask = iterator->ReadInt(&type_mask);
287
288   if (has_type_mask) {
289     has_post_data_ = type_mask & HAS_POST_DATA;
290     // the "referrer" property was added after type_mask to the written
291     // stream. As such, we don't fail if it can't be read.
292     std::string referrer_spec;
293     if (!iterator->ReadString(&referrer_spec))
294       referrer_spec = std::string();
295     // The "referrer policy" property was added even later, so we fall back to
296     // the default policy if the property is not present.
297     int policy_int;
298     blink::WebReferrerPolicy policy;
299     if (iterator->ReadInt(&policy_int))
300       policy = static_cast<blink::WebReferrerPolicy>(policy_int);
301     else
302       policy = blink::WebReferrerPolicyDefault;
303     referrer_ = content::Referrer(GURL(referrer_spec), policy);
304
305     // If the original URL can't be found, leave it empty.
306     std::string original_request_url_spec;
307     if (!iterator->ReadString(&original_request_url_spec))
308       original_request_url_spec = std::string();
309     original_request_url_ = GURL(original_request_url_spec);
310
311     // Default to not overriding the user agent if we don't have info.
312     if (!iterator->ReadBool(&is_overriding_user_agent_))
313       is_overriding_user_agent_ = false;
314
315     int64 timestamp_internal_value = 0;
316     if (iterator->ReadInt64(&timestamp_internal_value)) {
317       timestamp_ = base::Time::FromInternalValue(timestamp_internal_value);
318     } else {
319       timestamp_ = base::Time();
320     }
321
322     // If the search terms field can't be found, leave it empty.
323     if (!iterator->ReadString16(&search_terms_))
324       search_terms_.clear();
325
326     if (!iterator->ReadInt(&http_status_code_))
327       http_status_code_ = 0;
328   }
329
330   Sanitize();
331
332   return true;
333 }
334
335 scoped_ptr<NavigationEntry> SerializedNavigationEntry::ToNavigationEntry(
336     int page_id,
337     content::BrowserContext* browser_context) const {
338   scoped_ptr<NavigationEntry> entry(
339       content::NavigationController::CreateNavigationEntry(
340           virtual_url_,
341           referrer_,
342           // Use a transition type of reload so that we don't incorrectly
343           // increase the typed count.
344           content::PAGE_TRANSITION_RELOAD,
345           false,
346           // The extra headers are not sync'ed across sessions.
347           std::string(),
348           browser_context));
349
350   entry->SetTitle(title_);
351   entry->SetPageState(page_state_);
352   entry->SetPageID(page_id);
353   entry->SetHasPostData(has_post_data_);
354   entry->SetPostID(post_id_);
355   entry->SetOriginalRequestURL(original_request_url_);
356   entry->SetIsOverridingUserAgent(is_overriding_user_agent_);
357   entry->SetTimestamp(timestamp_);
358   entry->SetExtraData(kSearchTermsKey, search_terms_);
359   entry->SetHttpStatusCode(http_status_code_);
360
361   // These fields should have default values.
362   DCHECK_EQ(STATE_INVALID, blocked_state_);
363   DCHECK_EQ(0u, content_pack_categories_.size());
364
365   return entry.Pass();
366 }
367
368 // TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well?
369 // See http://crbug.com/67068.
370 sync_pb::TabNavigation SerializedNavigationEntry::ToSyncData() const {
371   sync_pb::TabNavigation sync_data;
372   sync_data.set_virtual_url(virtual_url_.spec());
373   sync_data.set_referrer(referrer_.url.spec());
374   sync_data.set_referrer_policy(referrer_.policy);
375   sync_data.set_title(base::UTF16ToUTF8(title_));
376
377   // Page transition core.
378   COMPILE_ASSERT(content::PAGE_TRANSITION_LAST_CORE ==
379                  content::PAGE_TRANSITION_KEYWORD_GENERATED,
380                  PageTransitionCoreBounds);
381   switch (PageTransitionStripQualifier(transition_type_)) {
382     case content::PAGE_TRANSITION_LINK:
383       sync_data.set_page_transition(
384           sync_pb::SyncEnums_PageTransition_LINK);
385       break;
386     case content::PAGE_TRANSITION_TYPED:
387       sync_data.set_page_transition(
388           sync_pb::SyncEnums_PageTransition_TYPED);
389       break;
390     case content::PAGE_TRANSITION_AUTO_BOOKMARK:
391       sync_data.set_page_transition(
392           sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK);
393       break;
394     case content::PAGE_TRANSITION_AUTO_SUBFRAME:
395       sync_data.set_page_transition(
396         sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME);
397       break;
398     case content::PAGE_TRANSITION_MANUAL_SUBFRAME:
399       sync_data.set_page_transition(
400         sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME);
401       break;
402     case content::PAGE_TRANSITION_GENERATED:
403       sync_data.set_page_transition(
404         sync_pb::SyncEnums_PageTransition_GENERATED);
405       break;
406     case content::PAGE_TRANSITION_AUTO_TOPLEVEL:
407       sync_data.set_page_transition(
408         sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL);
409       break;
410     case content::PAGE_TRANSITION_FORM_SUBMIT:
411       sync_data.set_page_transition(
412         sync_pb::SyncEnums_PageTransition_FORM_SUBMIT);
413       break;
414     case content::PAGE_TRANSITION_RELOAD:
415       sync_data.set_page_transition(
416         sync_pb::SyncEnums_PageTransition_RELOAD);
417       break;
418     case content::PAGE_TRANSITION_KEYWORD:
419       sync_data.set_page_transition(
420         sync_pb::SyncEnums_PageTransition_KEYWORD);
421       break;
422     case content::PAGE_TRANSITION_KEYWORD_GENERATED:
423       sync_data.set_page_transition(
424         sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED);
425       break;
426     default:
427       NOTREACHED();
428   }
429
430   // Page transition qualifiers.
431   if (PageTransitionIsRedirect(transition_type_)) {
432     if (transition_type_ & content::PAGE_TRANSITION_CLIENT_REDIRECT) {
433       sync_data.set_redirect_type(
434         sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT);
435     } else if (transition_type_ & content::PAGE_TRANSITION_SERVER_REDIRECT) {
436       sync_data.set_redirect_type(
437         sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT);
438     }
439   }
440   sync_data.set_navigation_forward_back(
441       (transition_type_ & content::PAGE_TRANSITION_FORWARD_BACK) != 0);
442   sync_data.set_navigation_from_address_bar(
443       (transition_type_ & content::PAGE_TRANSITION_FROM_ADDRESS_BAR) != 0);
444   sync_data.set_navigation_home_page(
445       (transition_type_ & content::PAGE_TRANSITION_HOME_PAGE) != 0);
446   sync_data.set_navigation_chain_start(
447       (transition_type_ & content::PAGE_TRANSITION_CHAIN_START) != 0);
448   sync_data.set_navigation_chain_end(
449       (transition_type_ & content::PAGE_TRANSITION_CHAIN_END) != 0);
450
451   sync_data.set_unique_id(unique_id_);
452   sync_data.set_timestamp_msec(syncer::TimeToProtoTime(timestamp_));
453   // The full-resolution timestamp works as a global ID.
454   sync_data.set_global_id(timestamp_.ToInternalValue());
455
456   sync_data.set_search_terms(base::UTF16ToUTF8(search_terms_));
457
458   sync_data.set_http_status_code(http_status_code_);
459
460   if (favicon_url_.is_valid())
461     sync_data.set_favicon_url(favicon_url_.spec());
462
463   if (blocked_state_ != STATE_INVALID) {
464     sync_data.set_blocked_state(
465         static_cast<sync_pb::TabNavigation_BlockedState>(blocked_state_));
466   }
467
468   for (std::set<std::string>::const_iterator it =
469            content_pack_categories_.begin();
470        it != content_pack_categories_.end(); ++it) {
471     sync_data.add_content_pack_categories(*it);
472   }
473
474   return sync_data;
475 }
476
477 // static
478 std::vector<NavigationEntry*> SerializedNavigationEntry::ToNavigationEntries(
479     const std::vector<SerializedNavigationEntry>& navigations,
480     content::BrowserContext* browser_context) {
481   int page_id = 0;
482   std::vector<NavigationEntry*> entries;
483   for (std::vector<SerializedNavigationEntry>::const_iterator
484        it = navigations.begin(); it != navigations.end(); ++it) {
485     entries.push_back(
486         it->ToNavigationEntry(page_id, browser_context).release());
487     ++page_id;
488   }
489   return entries;
490 }
491
492 void SerializedNavigationEntry::Sanitize() {
493   // Store original referrer so we can later see whether it was actually
494   // changed during sanitization, and we need to strip the referrer from the
495   // page state as well.
496   content::Referrer old_referrer = referrer_;
497
498   if (!referrer_.url.SchemeIsHTTPOrHTTPS())
499     referrer_ = content::Referrer();
500   switch (referrer_.policy) {
501     case blink::WebReferrerPolicyNever:
502       referrer_.url = GURL();
503       break;
504     case blink::WebReferrerPolicyAlways:
505       break;
506     case blink::WebReferrerPolicyOrigin:
507       referrer_.url = referrer_.url.GetWithEmptyPath();
508       break;
509     case blink::WebReferrerPolicyDefault:
510       // Fall through.
511     default:
512       referrer_.policy = blink::WebReferrerPolicyDefault;
513       if (referrer_.url.SchemeIsSecure() && !virtual_url_.SchemeIsSecure())
514         referrer_.url = GURL();
515   }
516
517   if (referrer_.url != old_referrer.url ||
518       referrer_.policy != old_referrer.policy) {
519     referrer_ = content::Referrer();
520     page_state_ = page_state_.RemoveReferrer();
521   }
522 }
523
524 }  // namespace sessions