- add sources.
[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_ =
69       content::Referrer(GURL(sync_data.referrer()),
70                         WebKit::WebReferrerPolicyDefault);
71   navigation.virtual_url_ = GURL(sync_data.virtual_url());
72   navigation.title_ = 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_ = 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   return navigation;
155 }
156
157 namespace {
158
159 // Helper used by SerializedNavigationEntry::WriteToPickle(). It writes |str| to
160 // |pickle|, if and only if |str| fits within (|max_bytes| -
161 // |*bytes_written|).  |bytes_written| is incremented to reflect the
162 // data written.
163 //
164 // TODO(akalin): Unify this with the same function in
165 // base_session_service.cc.
166 void WriteStringToPickle(Pickle* pickle,
167                          int* bytes_written,
168                          int max_bytes,
169                          const std::string& str) {
170   int num_bytes = str.size() * sizeof(char);
171   if (*bytes_written + num_bytes < max_bytes) {
172     *bytes_written += num_bytes;
173     pickle->WriteString(str);
174   } else {
175     pickle->WriteString(std::string());
176   }
177 }
178
179 // string16 version of WriteStringToPickle.
180 //
181 // TODO(akalin): Unify this, too.
182 void WriteString16ToPickle(Pickle* pickle,
183                            int* bytes_written,
184                            int max_bytes,
185                            const string16& str) {
186   int num_bytes = str.size() * sizeof(char16);
187   if (*bytes_written + num_bytes < max_bytes) {
188     *bytes_written += num_bytes;
189     pickle->WriteString16(str);
190   } else {
191     pickle->WriteString16(string16());
192   }
193 }
194
195 // A mask used for arbitrary boolean values needed to represent a
196 // NavigationEntry. Currently only contains HAS_POST_DATA.
197 //
198 // NOTE(akalin): We may want to just serialize |has_post_data_|
199 // directly.  Other bools (|is_overriding_user_agent_|) haven't been
200 // added to this mask.
201 enum TypeMask {
202   HAS_POST_DATA = 1
203 };
204
205 }  // namespace
206
207 // Pickle order:
208 //
209 // index_
210 // virtual_url_
211 // title_
212 // page_state_
213 // transition_type_
214 //
215 // Added on later:
216 //
217 // type_mask (has_post_data_)
218 // referrer_
219 // original_request_url_
220 // is_overriding_user_agent_
221 // timestamp_
222 // search_terms_
223 // http_status_code_
224
225 void SerializedNavigationEntry::WriteToPickle(int max_size,
226                                               Pickle* pickle) const {
227   pickle->WriteInt(index_);
228
229   int bytes_written = 0;
230
231   WriteStringToPickle(pickle, &bytes_written, max_size,
232                       virtual_url_.spec());
233
234   WriteString16ToPickle(pickle, &bytes_written, max_size, title_);
235
236   content::PageState page_state = page_state_;
237   if (has_post_data_)
238     page_state = page_state.RemovePasswordData();
239
240   WriteStringToPickle(pickle, &bytes_written, max_size,
241                       page_state.ToEncodedData());
242
243   pickle->WriteInt(transition_type_);
244
245   const int type_mask = has_post_data_ ? HAS_POST_DATA : 0;
246   pickle->WriteInt(type_mask);
247
248   WriteStringToPickle(
249       pickle, &bytes_written, max_size,
250       referrer_.url.is_valid() ? referrer_.url.spec() : std::string());
251
252   pickle->WriteInt(referrer_.policy);
253
254   // Save info required to override the user agent.
255   WriteStringToPickle(
256       pickle, &bytes_written, max_size,
257       original_request_url_.is_valid() ?
258       original_request_url_.spec() : std::string());
259   pickle->WriteBool(is_overriding_user_agent_);
260   pickle->WriteInt64(timestamp_.ToInternalValue());
261
262   WriteString16ToPickle(pickle, &bytes_written, max_size, search_terms_);
263
264   pickle->WriteInt(http_status_code_);
265 }
266
267 bool SerializedNavigationEntry::ReadFromPickle(PickleIterator* iterator) {
268   *this = SerializedNavigationEntry();
269   std::string virtual_url_spec, page_state_data;
270   int transition_type_int = 0;
271   if (!iterator->ReadInt(&index_) ||
272       !iterator->ReadString(&virtual_url_spec) ||
273       !iterator->ReadString16(&title_) ||
274       !iterator->ReadString(&page_state_data) ||
275       !iterator->ReadInt(&transition_type_int))
276     return false;
277   virtual_url_ = GURL(virtual_url_spec);
278   page_state_ = content::PageState::CreateFromEncodedData(page_state_data);
279   transition_type_ = static_cast<content::PageTransition>(transition_type_int);
280
281   // type_mask did not always exist in the written stream. As such, we
282   // don't fail if it can't be read.
283   int type_mask = 0;
284   bool has_type_mask = iterator->ReadInt(&type_mask);
285
286   if (has_type_mask) {
287     has_post_data_ = type_mask & HAS_POST_DATA;
288     // the "referrer" property was added after type_mask to the written
289     // stream. As such, we don't fail if it can't be read.
290     std::string referrer_spec;
291     if (!iterator->ReadString(&referrer_spec))
292       referrer_spec = std::string();
293     // The "referrer policy" property was added even later, so we fall back to
294     // the default policy if the property is not present.
295     int policy_int;
296     WebKit::WebReferrerPolicy policy;
297     if (iterator->ReadInt(&policy_int))
298       policy = static_cast<WebKit::WebReferrerPolicy>(policy_int);
299     else
300       policy = WebKit::WebReferrerPolicyDefault;
301     referrer_ = content::Referrer(GURL(referrer_spec), policy);
302
303     // If the original URL can't be found, leave it empty.
304     std::string original_request_url_spec;
305     if (!iterator->ReadString(&original_request_url_spec))
306       original_request_url_spec = std::string();
307     original_request_url_ = GURL(original_request_url_spec);
308
309     // Default to not overriding the user agent if we don't have info.
310     if (!iterator->ReadBool(&is_overriding_user_agent_))
311       is_overriding_user_agent_ = false;
312
313     int64 timestamp_internal_value = 0;
314     if (iterator->ReadInt64(&timestamp_internal_value)) {
315       timestamp_ = base::Time::FromInternalValue(timestamp_internal_value);
316     } else {
317       timestamp_ = base::Time();
318     }
319
320     // If the search terms field can't be found, leave it empty.
321     if (!iterator->ReadString16(&search_terms_))
322       search_terms_.clear();
323
324     if (!iterator->ReadInt(&http_status_code_))
325       http_status_code_ = 0;
326   }
327
328   return true;
329 }
330
331 scoped_ptr<NavigationEntry> SerializedNavigationEntry::ToNavigationEntry(
332     int page_id,
333     content::BrowserContext* browser_context) const {
334   scoped_ptr<NavigationEntry> entry(
335       content::NavigationController::CreateNavigationEntry(
336           virtual_url_,
337           referrer_,
338           // Use a transition type of reload so that we don't incorrectly
339           // increase the typed count.
340           content::PAGE_TRANSITION_RELOAD,
341           false,
342           // The extra headers are not sync'ed across sessions.
343           std::string(),
344           browser_context));
345
346   entry->SetTitle(title_);
347   entry->SetPageState(page_state_);
348   entry->SetPageID(page_id);
349   entry->SetHasPostData(has_post_data_);
350   entry->SetPostID(post_id_);
351   entry->SetOriginalRequestURL(original_request_url_);
352   entry->SetIsOverridingUserAgent(is_overriding_user_agent_);
353   entry->SetTimestamp(timestamp_);
354   entry->SetExtraData(kSearchTermsKey, search_terms_);
355   entry->SetHttpStatusCode(http_status_code_);
356
357   // These fields should have default values.
358   DCHECK_EQ(STATE_INVALID, blocked_state_);
359   DCHECK_EQ(0u, content_pack_categories_.size());
360
361   return entry.Pass();
362 }
363
364 // TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well?
365 // See http://crbug.com/67068.
366 sync_pb::TabNavigation SerializedNavigationEntry::ToSyncData() const {
367   sync_pb::TabNavigation sync_data;
368   sync_data.set_virtual_url(virtual_url_.spec());
369   // FIXME(zea): Support referrer policy?
370   sync_data.set_referrer(referrer_.url.spec());
371   sync_data.set_title(UTF16ToUTF8(title_));
372
373   // Page transition core.
374   COMPILE_ASSERT(content::PAGE_TRANSITION_LAST_CORE ==
375                  content::PAGE_TRANSITION_KEYWORD_GENERATED,
376                  PageTransitionCoreBounds);
377   switch (PageTransitionStripQualifier(transition_type_)) {
378     case content::PAGE_TRANSITION_LINK:
379       sync_data.set_page_transition(
380           sync_pb::SyncEnums_PageTransition_LINK);
381       break;
382     case content::PAGE_TRANSITION_TYPED:
383       sync_data.set_page_transition(
384           sync_pb::SyncEnums_PageTransition_TYPED);
385       break;
386     case content::PAGE_TRANSITION_AUTO_BOOKMARK:
387       sync_data.set_page_transition(
388           sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK);
389       break;
390     case content::PAGE_TRANSITION_AUTO_SUBFRAME:
391       sync_data.set_page_transition(
392         sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME);
393       break;
394     case content::PAGE_TRANSITION_MANUAL_SUBFRAME:
395       sync_data.set_page_transition(
396         sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME);
397       break;
398     case content::PAGE_TRANSITION_GENERATED:
399       sync_data.set_page_transition(
400         sync_pb::SyncEnums_PageTransition_GENERATED);
401       break;
402     case content::PAGE_TRANSITION_AUTO_TOPLEVEL:
403       sync_data.set_page_transition(
404         sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL);
405       break;
406     case content::PAGE_TRANSITION_FORM_SUBMIT:
407       sync_data.set_page_transition(
408         sync_pb::SyncEnums_PageTransition_FORM_SUBMIT);
409       break;
410     case content::PAGE_TRANSITION_RELOAD:
411       sync_data.set_page_transition(
412         sync_pb::SyncEnums_PageTransition_RELOAD);
413       break;
414     case content::PAGE_TRANSITION_KEYWORD:
415       sync_data.set_page_transition(
416         sync_pb::SyncEnums_PageTransition_KEYWORD);
417       break;
418     case content::PAGE_TRANSITION_KEYWORD_GENERATED:
419       sync_data.set_page_transition(
420         sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED);
421       break;
422     default:
423       NOTREACHED();
424   }
425
426   // Page transition qualifiers.
427   if (PageTransitionIsRedirect(transition_type_)) {
428     if (transition_type_ & content::PAGE_TRANSITION_CLIENT_REDIRECT) {
429       sync_data.set_redirect_type(
430         sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT);
431     } else if (transition_type_ & content::PAGE_TRANSITION_SERVER_REDIRECT) {
432       sync_data.set_redirect_type(
433         sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT);
434     }
435   }
436   sync_data.set_navigation_forward_back(
437       (transition_type_ & content::PAGE_TRANSITION_FORWARD_BACK) != 0);
438   sync_data.set_navigation_from_address_bar(
439       (transition_type_ & content::PAGE_TRANSITION_FROM_ADDRESS_BAR) != 0);
440   sync_data.set_navigation_home_page(
441       (transition_type_ & content::PAGE_TRANSITION_HOME_PAGE) != 0);
442   sync_data.set_navigation_chain_start(
443       (transition_type_ & content::PAGE_TRANSITION_CHAIN_START) != 0);
444   sync_data.set_navigation_chain_end(
445       (transition_type_ & content::PAGE_TRANSITION_CHAIN_END) != 0);
446
447   sync_data.set_unique_id(unique_id_);
448   sync_data.set_timestamp_msec(syncer::TimeToProtoTime(timestamp_));
449   // The full-resolution timestamp works as a global ID.
450   sync_data.set_global_id(timestamp_.ToInternalValue());
451
452   sync_data.set_search_terms(UTF16ToUTF8(search_terms_));
453
454   sync_data.set_http_status_code(http_status_code_);
455
456   if (favicon_url_.is_valid())
457     sync_data.set_favicon_url(favicon_url_.spec());
458
459   if (blocked_state_ != STATE_INVALID) {
460     sync_data.set_blocked_state(
461         static_cast<sync_pb::TabNavigation_BlockedState>(blocked_state_));
462   }
463
464   for (std::set<std::string>::const_iterator it =
465            content_pack_categories_.begin();
466        it != content_pack_categories_.end(); ++it) {
467     sync_data.add_content_pack_categories(*it);
468   }
469
470   return sync_data;
471 }
472
473 // static
474 std::vector<NavigationEntry*> SerializedNavigationEntry::ToNavigationEntries(
475     const std::vector<SerializedNavigationEntry>& navigations,
476     content::BrowserContext* browser_context) {
477   int page_id = 0;
478   std::vector<NavigationEntry*> entries;
479   for (std::vector<SerializedNavigationEntry>::const_iterator
480        it = navigations.begin(); it != navigations.end(); ++it) {
481     entries.push_back(
482         it->ToNavigationEntry(page_id, browser_context).release());
483     ++page_id;
484   }
485   return entries;
486 }
487
488 }  // namespace sessions