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