Upstream version 5.34.104.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
61   return navigation;
62 }
63
64 SerializedNavigationEntry SerializedNavigationEntry::FromSyncData(
65     int index,
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());
77
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;
83         break;
84       case sync_pb::SyncEnums_PageTransition_TYPED:
85         transition = content::PAGE_TRANSITION_TYPED;
86         break;
87       case sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK:
88         transition = content::PAGE_TRANSITION_AUTO_BOOKMARK;
89         break;
90       case sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME:
91         transition = content::PAGE_TRANSITION_AUTO_SUBFRAME;
92         break;
93       case sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME:
94         transition = content::PAGE_TRANSITION_MANUAL_SUBFRAME;
95         break;
96       case sync_pb::SyncEnums_PageTransition_GENERATED:
97         transition = content::PAGE_TRANSITION_GENERATED;
98         break;
99       case sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL:
100         transition = content::PAGE_TRANSITION_AUTO_TOPLEVEL;
101         break;
102       case sync_pb::SyncEnums_PageTransition_FORM_SUBMIT:
103         transition = content::PAGE_TRANSITION_FORM_SUBMIT;
104         break;
105       case sync_pb::SyncEnums_PageTransition_RELOAD:
106         transition = content::PAGE_TRANSITION_RELOAD;
107         break;
108       case sync_pb::SyncEnums_PageTransition_KEYWORD:
109         transition = content::PAGE_TRANSITION_KEYWORD;
110         break;
111       case sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED:
112         transition =
113             content::PAGE_TRANSITION_KEYWORD_GENERATED;
114         break;
115       default:
116         transition = content::PAGE_TRANSITION_LINK;
117         break;
118     }
119   }
120
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;
125         break;
126       case sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT:
127         transition |= content::PAGE_TRANSITION_SERVER_REDIRECT;
128         break;
129     }
130   }
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;
141
142   navigation.transition_type_ =
143       static_cast<content::PageTransition>(transition);
144
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());
149
150   navigation.http_status_code_ = sync_data.http_status_code();
151
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());
155
156   navigation.Sanitize();
157
158   navigation.is_restored_ = true;
159
160   return navigation;
161 }
162
163 namespace {
164
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
168 // data written.
169 //
170 // TODO(akalin): Unify this with the same function in
171 // base_session_service.cc.
172 void WriteStringToPickle(Pickle* pickle,
173                          int* bytes_written,
174                          int max_bytes,
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);
180   } else {
181     pickle->WriteString(std::string());
182   }
183 }
184
185 // base::string16 version of WriteStringToPickle.
186 //
187 // TODO(akalin): Unify this, too.
188 void WriteString16ToPickle(Pickle* pickle,
189                            int* bytes_written,
190                            int max_bytes,
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);
196   } else {
197     pickle->WriteString16(base::string16());
198   }
199 }
200
201 // A mask used for arbitrary boolean values needed to represent a
202 // NavigationEntry. Currently only contains HAS_POST_DATA.
203 //
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.
207 enum TypeMask {
208   HAS_POST_DATA = 1
209 };
210
211 }  // namespace
212
213 // Pickle order:
214 //
215 // index_
216 // virtual_url_
217 // title_
218 // page_state_
219 // transition_type_
220 //
221 // Added on later:
222 //
223 // type_mask (has_post_data_)
224 // referrer_
225 // original_request_url_
226 // is_overriding_user_agent_
227 // timestamp_
228 // search_terms_
229 // http_status_code_
230
231 void SerializedNavigationEntry::WriteToPickle(int max_size,
232                                               Pickle* pickle) const {
233   pickle->WriteInt(index_);
234
235   int bytes_written = 0;
236
237   WriteStringToPickle(pickle, &bytes_written, max_size,
238                       virtual_url_.spec());
239
240   WriteString16ToPickle(pickle, &bytes_written, max_size, title_);
241
242   content::PageState page_state = page_state_;
243   if (has_post_data_)
244     page_state = page_state.RemovePasswordData();
245
246   WriteStringToPickle(pickle, &bytes_written, max_size,
247                       page_state.ToEncodedData());
248
249   pickle->WriteInt(transition_type_);
250
251   const int type_mask = has_post_data_ ? HAS_POST_DATA : 0;
252   pickle->WriteInt(type_mask);
253
254   WriteStringToPickle(
255       pickle, &bytes_written, max_size,
256       referrer_.url.is_valid() ? referrer_.url.spec() : std::string());
257
258   pickle->WriteInt(referrer_.policy);
259
260   // Save info required to override the user agent.
261   WriteStringToPickle(
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());
267
268   WriteString16ToPickle(pickle, &bytes_written, max_size, search_terms_);
269
270   pickle->WriteInt(http_status_code_);
271 }
272
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))
282     return false;
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);
286
287   // type_mask did not always exist in the written stream. As such, we
288   // don't fail if it can't be read.
289   int type_mask = 0;
290   bool has_type_mask = iterator->ReadInt(&type_mask);
291
292   if (has_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.
301     int policy_int;
302     blink::WebReferrerPolicy policy;
303     if (iterator->ReadInt(&policy_int))
304       policy = static_cast<blink::WebReferrerPolicy>(policy_int);
305     else
306       policy = blink::WebReferrerPolicyDefault;
307     referrer_ = content::Referrer(GURL(referrer_spec), policy);
308
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);
314
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;
318
319     int64 timestamp_internal_value = 0;
320     if (iterator->ReadInt64(&timestamp_internal_value)) {
321       timestamp_ = base::Time::FromInternalValue(timestamp_internal_value);
322     } else {
323       timestamp_ = base::Time();
324     }
325
326     // If the search terms field can't be found, leave it empty.
327     if (!iterator->ReadString16(&search_terms_))
328       search_terms_.clear();
329
330     if (!iterator->ReadInt(&http_status_code_))
331       http_status_code_ = 0;
332   }
333
334   Sanitize();
335
336   is_restored_ = true;
337
338   return true;
339 }
340
341 scoped_ptr<NavigationEntry> SerializedNavigationEntry::ToNavigationEntry(
342     int page_id,
343     content::BrowserContext* browser_context) const {
344   scoped_ptr<NavigationEntry> entry(
345       content::NavigationController::CreateNavigationEntry(
346           virtual_url_,
347           referrer_,
348           // Use a transition type of reload so that we don't incorrectly
349           // increase the typed count.
350           content::PAGE_TRANSITION_RELOAD,
351           false,
352           // The extra headers are not sync'ed across sessions.
353           std::string(),
354           browser_context));
355
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_);
366
367   // These fields should have default values.
368   DCHECK_EQ(STATE_INVALID, blocked_state_);
369   DCHECK_EQ(0u, content_pack_categories_.size());
370
371   return entry.Pass();
372 }
373
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_));
382
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);
391       break;
392     case content::PAGE_TRANSITION_TYPED:
393       sync_data.set_page_transition(
394           sync_pb::SyncEnums_PageTransition_TYPED);
395       break;
396     case content::PAGE_TRANSITION_AUTO_BOOKMARK:
397       sync_data.set_page_transition(
398           sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK);
399       break;
400     case content::PAGE_TRANSITION_AUTO_SUBFRAME:
401       sync_data.set_page_transition(
402         sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME);
403       break;
404     case content::PAGE_TRANSITION_MANUAL_SUBFRAME:
405       sync_data.set_page_transition(
406         sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME);
407       break;
408     case content::PAGE_TRANSITION_GENERATED:
409       sync_data.set_page_transition(
410         sync_pb::SyncEnums_PageTransition_GENERATED);
411       break;
412     case content::PAGE_TRANSITION_AUTO_TOPLEVEL:
413       sync_data.set_page_transition(
414         sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL);
415       break;
416     case content::PAGE_TRANSITION_FORM_SUBMIT:
417       sync_data.set_page_transition(
418         sync_pb::SyncEnums_PageTransition_FORM_SUBMIT);
419       break;
420     case content::PAGE_TRANSITION_RELOAD:
421       sync_data.set_page_transition(
422         sync_pb::SyncEnums_PageTransition_RELOAD);
423       break;
424     case content::PAGE_TRANSITION_KEYWORD:
425       sync_data.set_page_transition(
426         sync_pb::SyncEnums_PageTransition_KEYWORD);
427       break;
428     case content::PAGE_TRANSITION_KEYWORD_GENERATED:
429       sync_data.set_page_transition(
430         sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED);
431       break;
432     default:
433       NOTREACHED();
434   }
435
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);
444     }
445   }
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);
456
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());
461
462   sync_data.set_search_terms(base::UTF16ToUTF8(search_terms_));
463
464   sync_data.set_http_status_code(http_status_code_);
465
466   if (favicon_url_.is_valid())
467     sync_data.set_favicon_url(favicon_url_.spec());
468
469   if (blocked_state_ != STATE_INVALID) {
470     sync_data.set_blocked_state(
471         static_cast<sync_pb::TabNavigation_BlockedState>(blocked_state_));
472   }
473
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);
478   }
479
480   sync_data.set_is_restored(is_restored_);
481
482   return sync_data;
483 }
484
485 // static
486 std::vector<NavigationEntry*> SerializedNavigationEntry::ToNavigationEntries(
487     const std::vector<SerializedNavigationEntry>& navigations,
488     content::BrowserContext* browser_context) {
489   int page_id = 0;
490   std::vector<NavigationEntry*> entries;
491   for (std::vector<SerializedNavigationEntry>::const_iterator
492        it = navigations.begin(); it != navigations.end(); ++it) {
493     entries.push_back(
494         it->ToNavigationEntry(page_id, browser_context).release());
495     ++page_id;
496   }
497   return entries;
498 }
499
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_;
505
506   if (!referrer_.url.SchemeIsHTTPOrHTTPS())
507     referrer_ = content::Referrer();
508   switch (referrer_.policy) {
509     case blink::WebReferrerPolicyNever:
510       referrer_.url = GURL();
511       break;
512     case blink::WebReferrerPolicyAlways:
513       break;
514     case blink::WebReferrerPolicyOrigin:
515       referrer_.url = referrer_.url.GetWithEmptyPath();
516       break;
517     case blink::WebReferrerPolicyDefault:
518       // Fall through.
519     default:
520       referrer_.policy = blink::WebReferrerPolicyDefault;
521       if (referrer_.url.SchemeIsSecure() && !virtual_url_.SchemeIsSecure())
522         referrer_.url = GURL();
523   }
524
525   if (referrer_.url != old_referrer.url ||
526       referrer_.policy != old_referrer.policy) {
527     referrer_ = content::Referrer();
528     page_state_ = page_state_.RemoveReferrer();
529   }
530 }
531
532 }  // namespace sessions