Upstream version 9.38.198.0
[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       is_restored_(false),
31       blocked_state_(STATE_INVALID) {}
32
33 SerializedNavigationEntry::~SerializedNavigationEntry() {}
34
35 // static
36 SerializedNavigationEntry SerializedNavigationEntry::FromNavigationEntry(
37     int index,
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();
61
62   return navigation;
63 }
64
65 SerializedNavigationEntry SerializedNavigationEntry::FromSyncData(
66     int index,
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());
78
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;
84         break;
85       case sync_pb::SyncEnums_PageTransition_TYPED:
86         transition = content::PAGE_TRANSITION_TYPED;
87         break;
88       case sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK:
89         transition = content::PAGE_TRANSITION_AUTO_BOOKMARK;
90         break;
91       case sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME:
92         transition = content::PAGE_TRANSITION_AUTO_SUBFRAME;
93         break;
94       case sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME:
95         transition = content::PAGE_TRANSITION_MANUAL_SUBFRAME;
96         break;
97       case sync_pb::SyncEnums_PageTransition_GENERATED:
98         transition = content::PAGE_TRANSITION_GENERATED;
99         break;
100       case sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL:
101         transition = content::PAGE_TRANSITION_AUTO_TOPLEVEL;
102         break;
103       case sync_pb::SyncEnums_PageTransition_FORM_SUBMIT:
104         transition = content::PAGE_TRANSITION_FORM_SUBMIT;
105         break;
106       case sync_pb::SyncEnums_PageTransition_RELOAD:
107         transition = content::PAGE_TRANSITION_RELOAD;
108         break;
109       case sync_pb::SyncEnums_PageTransition_KEYWORD:
110         transition = content::PAGE_TRANSITION_KEYWORD;
111         break;
112       case sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED:
113         transition =
114             content::PAGE_TRANSITION_KEYWORD_GENERATED;
115         break;
116       default:
117         transition = content::PAGE_TRANSITION_LINK;
118         break;
119     }
120   }
121
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;
126         break;
127       case sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT:
128         transition |= content::PAGE_TRANSITION_SERVER_REDIRECT;
129         break;
130     }
131   }
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;
142
143   navigation.transition_type_ =
144       static_cast<content::PageTransition>(transition);
145
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());
150
151   navigation.http_status_code_ = sync_data.http_status_code();
152
153   navigation.Sanitize();
154
155   navigation.is_restored_ = true;
156
157   return navigation;
158 }
159
160 namespace {
161
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
165 // data written.
166 //
167 // TODO(akalin): Unify this with the same function in
168 // base_session_service.cc.
169 void WriteStringToPickle(Pickle* pickle,
170                          int* bytes_written,
171                          int max_bytes,
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);
177   } else {
178     pickle->WriteString(std::string());
179   }
180 }
181
182 // base::string16 version of WriteStringToPickle.
183 //
184 // TODO(akalin): Unify this, too.
185 void WriteString16ToPickle(Pickle* pickle,
186                            int* bytes_written,
187                            int max_bytes,
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);
193   } else {
194     pickle->WriteString16(base::string16());
195   }
196 }
197
198 // A mask used for arbitrary boolean values needed to represent a
199 // NavigationEntry. Currently only contains HAS_POST_DATA.
200 //
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.
204 enum TypeMask {
205   HAS_POST_DATA = 1
206 };
207
208 }  // namespace
209
210 // Pickle order:
211 //
212 // index_
213 // virtual_url_
214 // title_
215 // page_state_
216 // transition_type_
217 //
218 // Added on later:
219 //
220 // type_mask (has_post_data_)
221 // referrer_
222 // original_request_url_
223 // is_overriding_user_agent_
224 // timestamp_
225 // search_terms_
226 // http_status_code_
227
228 void SerializedNavigationEntry::WriteToPickle(int max_size,
229                                               Pickle* pickle) const {
230   pickle->WriteInt(index_);
231
232   int bytes_written = 0;
233
234   WriteStringToPickle(pickle, &bytes_written, max_size,
235                       virtual_url_.spec());
236
237   WriteString16ToPickle(pickle, &bytes_written, max_size, title_);
238
239   content::PageState page_state = page_state_;
240   if (has_post_data_)
241     page_state = page_state.RemovePasswordData();
242
243   WriteStringToPickle(pickle, &bytes_written, max_size,
244                       page_state.ToEncodedData());
245
246   pickle->WriteInt(transition_type_);
247
248   const int type_mask = has_post_data_ ? HAS_POST_DATA : 0;
249   pickle->WriteInt(type_mask);
250
251   WriteStringToPickle(
252       pickle, &bytes_written, max_size,
253       referrer_.url.is_valid() ? referrer_.url.spec() : std::string());
254
255   pickle->WriteInt(referrer_.policy);
256
257   // Save info required to override the user agent.
258   WriteStringToPickle(
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());
264
265   WriteString16ToPickle(pickle, &bytes_written, max_size, search_terms_);
266
267   pickle->WriteInt(http_status_code_);
268 }
269
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))
279     return false;
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);
283
284   // type_mask did not always exist in the written stream. As such, we
285   // don't fail if it can't be read.
286   int type_mask = 0;
287   bool has_type_mask = iterator->ReadInt(&type_mask);
288
289   if (has_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.
298     int policy_int;
299     blink::WebReferrerPolicy policy;
300     if (iterator->ReadInt(&policy_int))
301       policy = static_cast<blink::WebReferrerPolicy>(policy_int);
302     else
303       policy = blink::WebReferrerPolicyDefault;
304     referrer_ = content::Referrer(GURL(referrer_spec), policy);
305
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);
311
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;
315
316     int64 timestamp_internal_value = 0;
317     if (iterator->ReadInt64(&timestamp_internal_value)) {
318       timestamp_ = base::Time::FromInternalValue(timestamp_internal_value);
319     } else {
320       timestamp_ = base::Time();
321     }
322
323     // If the search terms field can't be found, leave it empty.
324     if (!iterator->ReadString16(&search_terms_))
325       search_terms_.clear();
326
327     if (!iterator->ReadInt(&http_status_code_))
328       http_status_code_ = 0;
329   }
330
331   Sanitize();
332
333   is_restored_ = true;
334
335   return true;
336 }
337
338 scoped_ptr<NavigationEntry> SerializedNavigationEntry::ToNavigationEntry(
339     int page_id,
340     content::BrowserContext* browser_context) const {
341   scoped_ptr<NavigationEntry> entry(
342       content::NavigationController::CreateNavigationEntry(
343           virtual_url_,
344           referrer_,
345           // Use a transition type of reload so that we don't incorrectly
346           // increase the typed count.
347           content::PAGE_TRANSITION_RELOAD,
348           false,
349           // The extra headers are not sync'ed across sessions.
350           std::string(),
351           browser_context));
352
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_);
364
365   // These fields should have default values.
366   DCHECK_EQ(STATE_INVALID, blocked_state_);
367   DCHECK_EQ(0u, content_pack_categories_.size());
368
369   return entry.Pass();
370 }
371
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_));
380
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);
389       break;
390     case content::PAGE_TRANSITION_TYPED:
391       sync_data.set_page_transition(
392           sync_pb::SyncEnums_PageTransition_TYPED);
393       break;
394     case content::PAGE_TRANSITION_AUTO_BOOKMARK:
395       sync_data.set_page_transition(
396           sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK);
397       break;
398     case content::PAGE_TRANSITION_AUTO_SUBFRAME:
399       sync_data.set_page_transition(
400         sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME);
401       break;
402     case content::PAGE_TRANSITION_MANUAL_SUBFRAME:
403       sync_data.set_page_transition(
404         sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME);
405       break;
406     case content::PAGE_TRANSITION_GENERATED:
407       sync_data.set_page_transition(
408         sync_pb::SyncEnums_PageTransition_GENERATED);
409       break;
410     case content::PAGE_TRANSITION_AUTO_TOPLEVEL:
411       sync_data.set_page_transition(
412         sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL);
413       break;
414     case content::PAGE_TRANSITION_FORM_SUBMIT:
415       sync_data.set_page_transition(
416         sync_pb::SyncEnums_PageTransition_FORM_SUBMIT);
417       break;
418     case content::PAGE_TRANSITION_RELOAD:
419       sync_data.set_page_transition(
420         sync_pb::SyncEnums_PageTransition_RELOAD);
421       break;
422     case content::PAGE_TRANSITION_KEYWORD:
423       sync_data.set_page_transition(
424         sync_pb::SyncEnums_PageTransition_KEYWORD);
425       break;
426     case content::PAGE_TRANSITION_KEYWORD_GENERATED:
427       sync_data.set_page_transition(
428         sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED);
429       break;
430     default:
431       NOTREACHED();
432   }
433
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);
442     }
443   }
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);
454
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());
459
460   sync_data.set_search_terms(base::UTF16ToUTF8(search_terms_));
461
462   sync_data.set_http_status_code(http_status_code_);
463
464   if (favicon_url_.is_valid())
465     sync_data.set_favicon_url(favicon_url_.spec());
466
467   if (blocked_state_ != STATE_INVALID) {
468     sync_data.set_blocked_state(
469         static_cast<sync_pb::TabNavigation_BlockedState>(blocked_state_));
470   }
471
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);
476   }
477
478   // Copy all redirect chain entries except the last URL (which should match
479   // the virtual_url).
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());
486     }
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());
491     }
492   }
493
494   sync_data.set_is_restored(is_restored_);
495
496   return sync_data;
497 }
498
499 // static
500 std::vector<NavigationEntry*> SerializedNavigationEntry::ToNavigationEntries(
501     const std::vector<SerializedNavigationEntry>& navigations,
502     content::BrowserContext* browser_context) {
503   int page_id = 0;
504   std::vector<NavigationEntry*> entries;
505   for (std::vector<SerializedNavigationEntry>::const_iterator
506        it = navigations.begin(); it != navigations.end(); ++it) {
507     entries.push_back(
508         it->ToNavigationEntry(page_id, browser_context).release());
509     ++page_id;
510   }
511   return entries;
512 }
513
514 void SerializedNavigationEntry::Sanitize() {
515   content::Referrer new_referrer =
516       content::Referrer::SanitizeForRequest(virtual_url_, referrer_);
517
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();
524   }
525 }
526
527 }  // namespace sessions