Home | History | Annotate | Download | only in sessions
      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       blocked_state_(STATE_INVALID) {}
     30 
     31 SerializedNavigationEntry::~SerializedNavigationEntry() {}
     32 
     33 // static
     34 SerializedNavigationEntry SerializedNavigationEntry::FromNavigationEntry(
     35     int index,
     36     const NavigationEntry& entry) {
     37   SerializedNavigationEntry navigation;
     38   navigation.index_ = index;
     39   navigation.unique_id_ = entry.GetUniqueID();
     40   navigation.referrer_ = entry.GetReferrer();
     41   navigation.virtual_url_ = entry.GetVirtualURL();
     42   navigation.title_ = entry.GetTitle();
     43   navigation.page_state_ = entry.GetPageState();
     44   navigation.transition_type_ = entry.GetTransitionType();
     45   navigation.has_post_data_ = entry.GetHasPostData();
     46   navigation.post_id_ = entry.GetPostID();
     47   navigation.original_request_url_ = entry.GetOriginalRequestURL();
     48   navigation.is_overriding_user_agent_ = entry.GetIsOverridingUserAgent();
     49   navigation.timestamp_ = entry.GetTimestamp();
     50   // If you want to navigate a named frame in Chrome, you will first need to
     51   // add support for persisting it. It is currently only used for layout tests.
     52   CHECK(entry.GetFrameToNavigate().empty());
     53   entry.GetExtraData(kSearchTermsKey, &navigation.search_terms_);
     54   if (entry.GetFavicon().valid)
     55     navigation.favicon_url_ = entry.GetFavicon().url;
     56 
     57   return navigation;
     58 }
     59 
     60 SerializedNavigationEntry SerializedNavigationEntry::FromSyncData(
     61     int index,
     62     const sync_pb::TabNavigation& sync_data) {
     63   SerializedNavigationEntry navigation;
     64   navigation.index_ = index;
     65   navigation.unique_id_ = sync_data.unique_id();
     66   navigation.referrer_ =
     67       content::Referrer(GURL(sync_data.referrer()),
     68                         WebKit::WebReferrerPolicyDefault);
     69   navigation.virtual_url_ = GURL(sync_data.virtual_url());
     70   navigation.title_ = UTF8ToUTF16(sync_data.title());
     71   navigation.page_state_ =
     72       content::PageState::CreateFromEncodedData(sync_data.state());
     73 
     74   uint32 transition = 0;
     75   if (sync_data.has_page_transition()) {
     76     switch (sync_data.page_transition()) {
     77       case sync_pb::SyncEnums_PageTransition_LINK:
     78         transition = content::PAGE_TRANSITION_LINK;
     79         break;
     80       case sync_pb::SyncEnums_PageTransition_TYPED:
     81         transition = content::PAGE_TRANSITION_TYPED;
     82         break;
     83       case sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK:
     84         transition = content::PAGE_TRANSITION_AUTO_BOOKMARK;
     85         break;
     86       case sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME:
     87         transition = content::PAGE_TRANSITION_AUTO_SUBFRAME;
     88         break;
     89       case sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME:
     90         transition = content::PAGE_TRANSITION_MANUAL_SUBFRAME;
     91         break;
     92       case sync_pb::SyncEnums_PageTransition_GENERATED:
     93         transition = content::PAGE_TRANSITION_GENERATED;
     94         break;
     95       case sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL:
     96         transition = content::PAGE_TRANSITION_AUTO_TOPLEVEL;
     97         break;
     98       case sync_pb::SyncEnums_PageTransition_FORM_SUBMIT:
     99         transition = content::PAGE_TRANSITION_FORM_SUBMIT;
    100         break;
    101       case sync_pb::SyncEnums_PageTransition_RELOAD:
    102         transition = content::PAGE_TRANSITION_RELOAD;
    103         break;
    104       case sync_pb::SyncEnums_PageTransition_KEYWORD:
    105         transition = content::PAGE_TRANSITION_KEYWORD;
    106         break;
    107       case sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED:
    108         transition =
    109             content::PAGE_TRANSITION_KEYWORD_GENERATED;
    110         break;
    111       default:
    112         transition = content::PAGE_TRANSITION_LINK;
    113         break;
    114     }
    115   }
    116 
    117   if  (sync_data.has_redirect_type()) {
    118     switch (sync_data.redirect_type()) {
    119       case sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT:
    120         transition |= content::PAGE_TRANSITION_CLIENT_REDIRECT;
    121         break;
    122       case sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT:
    123         transition |= content::PAGE_TRANSITION_SERVER_REDIRECT;
    124         break;
    125     }
    126   }
    127   if (sync_data.navigation_forward_back())
    128       transition |= content::PAGE_TRANSITION_FORWARD_BACK;
    129   if (sync_data.navigation_from_address_bar())
    130       transition |= content::PAGE_TRANSITION_FROM_ADDRESS_BAR;
    131   if (sync_data.navigation_home_page())
    132       transition |= content::PAGE_TRANSITION_HOME_PAGE;
    133   if (sync_data.navigation_chain_start())
    134       transition |= content::PAGE_TRANSITION_CHAIN_START;
    135   if (sync_data.navigation_chain_end())
    136       transition |= content::PAGE_TRANSITION_CHAIN_END;
    137 
    138   navigation.transition_type_ =
    139       static_cast<content::PageTransition>(transition);
    140 
    141   navigation.timestamp_ = base::Time();
    142   navigation.search_terms_ = UTF8ToUTF16(sync_data.search_terms());
    143   if (sync_data.has_favicon_url())
    144     navigation.favicon_url_ = GURL(sync_data.favicon_url());
    145 
    146   // We shouldn't sync session data for managed users down at the moment.
    147   DCHECK(!sync_data.has_blocked_state());
    148   DCHECK_EQ(0, sync_data.content_pack_categories_size());
    149 
    150   return navigation;
    151 }
    152 
    153 namespace {
    154 
    155 // Helper used by SerializedNavigationEntry::WriteToPickle(). It writes |str| to
    156 // |pickle|, if and only if |str| fits within (|max_bytes| -
    157 // |*bytes_written|).  |bytes_written| is incremented to reflect the
    158 // data written.
    159 //
    160 // TODO(akalin): Unify this with the same function in
    161 // base_session_service.cc.
    162 void WriteStringToPickle(Pickle* pickle,
    163                          int* bytes_written,
    164                          int max_bytes,
    165                          const std::string& str) {
    166   int num_bytes = str.size() * sizeof(char);
    167   if (*bytes_written + num_bytes < max_bytes) {
    168     *bytes_written += num_bytes;
    169     pickle->WriteString(str);
    170   } else {
    171     pickle->WriteString(std::string());
    172   }
    173 }
    174 
    175 // string16 version of WriteStringToPickle.
    176 //
    177 // TODO(akalin): Unify this, too.
    178 void WriteString16ToPickle(Pickle* pickle,
    179                            int* bytes_written,
    180                            int max_bytes,
    181                            const string16& str) {
    182   int num_bytes = str.size() * sizeof(char16);
    183   if (*bytes_written + num_bytes < max_bytes) {
    184     *bytes_written += num_bytes;
    185     pickle->WriteString16(str);
    186   } else {
    187     pickle->WriteString16(string16());
    188   }
    189 }
    190 
    191 // A mask used for arbitrary boolean values needed to represent a
    192 // NavigationEntry. Currently only contains HAS_POST_DATA.
    193 //
    194 // NOTE(akalin): We may want to just serialize |has_post_data_|
    195 // directly.  Other bools (|is_overriding_user_agent_|) haven't been
    196 // added to this mask.
    197 enum TypeMask {
    198   HAS_POST_DATA = 1
    199 };
    200 
    201 }  // namespace
    202 
    203 // Pickle order:
    204 //
    205 // index_
    206 // virtual_url_
    207 // title_
    208 // page_state_
    209 // transition_type_
    210 //
    211 // Added on later:
    212 //
    213 // type_mask (has_post_data_)
    214 // referrer_
    215 // original_request_url_
    216 // is_overriding_user_agent_
    217 // timestamp_
    218 // search_terms_
    219 
    220 void SerializedNavigationEntry::WriteToPickle(int max_size,
    221                                               Pickle* pickle) const {
    222   pickle->WriteInt(index_);
    223 
    224   int bytes_written = 0;
    225 
    226   WriteStringToPickle(pickle, &bytes_written, max_size,
    227                       virtual_url_.spec());
    228 
    229   WriteString16ToPickle(pickle, &bytes_written, max_size, title_);
    230 
    231   content::PageState page_state = page_state_;
    232   if (has_post_data_)
    233     page_state = page_state.RemovePasswordData();
    234 
    235   WriteStringToPickle(pickle, &bytes_written, max_size,
    236                       page_state.ToEncodedData());
    237 
    238   pickle->WriteInt(transition_type_);
    239 
    240   const int type_mask = has_post_data_ ? HAS_POST_DATA : 0;
    241   pickle->WriteInt(type_mask);
    242 
    243   WriteStringToPickle(
    244       pickle, &bytes_written, max_size,
    245       referrer_.url.is_valid() ? referrer_.url.spec() : std::string());
    246 
    247   pickle->WriteInt(referrer_.policy);
    248 
    249   // Save info required to override the user agent.
    250   WriteStringToPickle(
    251       pickle, &bytes_written, max_size,
    252       original_request_url_.is_valid() ?
    253       original_request_url_.spec() : std::string());
    254   pickle->WriteBool(is_overriding_user_agent_);
    255   pickle->WriteInt64(timestamp_.ToInternalValue());
    256 
    257   WriteString16ToPickle(pickle, &bytes_written, max_size, search_terms_);
    258 }
    259 
    260 bool SerializedNavigationEntry::ReadFromPickle(PickleIterator* iterator) {
    261   *this = SerializedNavigationEntry();
    262   std::string virtual_url_spec, page_state_data;
    263   int transition_type_int = 0;
    264   if (!iterator->ReadInt(&index_) ||
    265       !iterator->ReadString(&virtual_url_spec) ||
    266       !iterator->ReadString16(&title_) ||
    267       !iterator->ReadString(&page_state_data) ||
    268       !iterator->ReadInt(&transition_type_int))
    269     return false;
    270   virtual_url_ = GURL(virtual_url_spec);
    271   page_state_ = content::PageState::CreateFromEncodedData(page_state_data);
    272   transition_type_ = static_cast<content::PageTransition>(transition_type_int);
    273 
    274   // type_mask did not always exist in the written stream. As such, we
    275   // don't fail if it can't be read.
    276   int type_mask = 0;
    277   bool has_type_mask = iterator->ReadInt(&type_mask);
    278 
    279   if (has_type_mask) {
    280     has_post_data_ = type_mask & HAS_POST_DATA;
    281     // the "referrer" property was added after type_mask to the written
    282     // stream. As such, we don't fail if it can't be read.
    283     std::string referrer_spec;
    284     if (!iterator->ReadString(&referrer_spec))
    285       referrer_spec = std::string();
    286     // The "referrer policy" property was added even later, so we fall back to
    287     // the default policy if the property is not present.
    288     int policy_int;
    289     WebKit::WebReferrerPolicy policy;
    290     if (iterator->ReadInt(&policy_int))
    291       policy = static_cast<WebKit::WebReferrerPolicy>(policy_int);
    292     else
    293       policy = WebKit::WebReferrerPolicyDefault;
    294     referrer_ = content::Referrer(GURL(referrer_spec), policy);
    295 
    296     // If the original URL can't be found, leave it empty.
    297     std::string original_request_url_spec;
    298     if (!iterator->ReadString(&original_request_url_spec))
    299       original_request_url_spec = std::string();
    300     original_request_url_ = GURL(original_request_url_spec);
    301 
    302     // Default to not overriding the user agent if we don't have info.
    303     if (!iterator->ReadBool(&is_overriding_user_agent_))
    304       is_overriding_user_agent_ = false;
    305 
    306     int64 timestamp_internal_value = 0;
    307     if (iterator->ReadInt64(&timestamp_internal_value)) {
    308       timestamp_ = base::Time::FromInternalValue(timestamp_internal_value);
    309     } else {
    310       timestamp_ = base::Time();
    311     }
    312 
    313     // If the search terms field can't be found, leave it empty.
    314     if (!iterator->ReadString16(&search_terms_))
    315       search_terms_.clear();
    316   }
    317 
    318   return true;
    319 }
    320 
    321 scoped_ptr<NavigationEntry> SerializedNavigationEntry::ToNavigationEntry(
    322     int page_id,
    323     content::BrowserContext* browser_context) const {
    324   scoped_ptr<NavigationEntry> entry(
    325       content::NavigationController::CreateNavigationEntry(
    326           virtual_url_,
    327           referrer_,
    328           // Use a transition type of reload so that we don't incorrectly
    329           // increase the typed count.
    330           content::PAGE_TRANSITION_RELOAD,
    331           false,
    332           // The extra headers are not sync'ed across sessions.
    333           std::string(),
    334           browser_context));
    335 
    336   entry->SetTitle(title_);
    337   entry->SetPageState(page_state_);
    338   entry->SetPageID(page_id);
    339   entry->SetHasPostData(has_post_data_);
    340   entry->SetPostID(post_id_);
    341   entry->SetOriginalRequestURL(original_request_url_);
    342   entry->SetIsOverridingUserAgent(is_overriding_user_agent_);
    343   entry->SetTimestamp(timestamp_);
    344   entry->SetExtraData(kSearchTermsKey, search_terms_);
    345 
    346   // These fields should have default values.
    347   DCHECK_EQ(STATE_INVALID, blocked_state_);
    348   DCHECK_EQ(0u, content_pack_categories_.size());
    349 
    350   return entry.Pass();
    351 }
    352 
    353 // TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well?
    354 // See http://crbug.com/67068.
    355 sync_pb::TabNavigation SerializedNavigationEntry::ToSyncData() const {
    356   sync_pb::TabNavigation sync_data;
    357   sync_data.set_virtual_url(virtual_url_.spec());
    358   // FIXME(zea): Support referrer policy?
    359   sync_data.set_referrer(referrer_.url.spec());
    360   sync_data.set_title(UTF16ToUTF8(title_));
    361 
    362   // Page transition core.
    363   COMPILE_ASSERT(content::PAGE_TRANSITION_LAST_CORE ==
    364                  content::PAGE_TRANSITION_KEYWORD_GENERATED,
    365                  PageTransitionCoreBounds);
    366   switch (PageTransitionStripQualifier(transition_type_)) {
    367     case content::PAGE_TRANSITION_LINK:
    368       sync_data.set_page_transition(
    369           sync_pb::SyncEnums_PageTransition_LINK);
    370       break;
    371     case content::PAGE_TRANSITION_TYPED:
    372       sync_data.set_page_transition(
    373           sync_pb::SyncEnums_PageTransition_TYPED);
    374       break;
    375     case content::PAGE_TRANSITION_AUTO_BOOKMARK:
    376       sync_data.set_page_transition(
    377           sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK);
    378       break;
    379     case content::PAGE_TRANSITION_AUTO_SUBFRAME:
    380       sync_data.set_page_transition(
    381         sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME);
    382       break;
    383     case content::PAGE_TRANSITION_MANUAL_SUBFRAME:
    384       sync_data.set_page_transition(
    385         sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME);
    386       break;
    387     case content::PAGE_TRANSITION_GENERATED:
    388       sync_data.set_page_transition(
    389         sync_pb::SyncEnums_PageTransition_GENERATED);
    390       break;
    391     case content::PAGE_TRANSITION_AUTO_TOPLEVEL:
    392       sync_data.set_page_transition(
    393         sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL);
    394       break;
    395     case content::PAGE_TRANSITION_FORM_SUBMIT:
    396       sync_data.set_page_transition(
    397         sync_pb::SyncEnums_PageTransition_FORM_SUBMIT);
    398       break;
    399     case content::PAGE_TRANSITION_RELOAD:
    400       sync_data.set_page_transition(
    401         sync_pb::SyncEnums_PageTransition_RELOAD);
    402       break;
    403     case content::PAGE_TRANSITION_KEYWORD:
    404       sync_data.set_page_transition(
    405         sync_pb::SyncEnums_PageTransition_KEYWORD);
    406       break;
    407     case content::PAGE_TRANSITION_KEYWORD_GENERATED:
    408       sync_data.set_page_transition(
    409         sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED);
    410       break;
    411     default:
    412       NOTREACHED();
    413   }
    414 
    415   // Page transition qualifiers.
    416   if (PageTransitionIsRedirect(transition_type_)) {
    417     if (transition_type_ & content::PAGE_TRANSITION_CLIENT_REDIRECT) {
    418       sync_data.set_redirect_type(
    419         sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT);
    420     } else if (transition_type_ & content::PAGE_TRANSITION_SERVER_REDIRECT) {
    421       sync_data.set_redirect_type(
    422         sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT);
    423     }
    424   }
    425   sync_data.set_navigation_forward_back(
    426       (transition_type_ & content::PAGE_TRANSITION_FORWARD_BACK) != 0);
    427   sync_data.set_navigation_from_address_bar(
    428       (transition_type_ & content::PAGE_TRANSITION_FROM_ADDRESS_BAR) != 0);
    429   sync_data.set_navigation_home_page(
    430       (transition_type_ & content::PAGE_TRANSITION_HOME_PAGE) != 0);
    431   sync_data.set_navigation_chain_start(
    432       (transition_type_ & content::PAGE_TRANSITION_CHAIN_START) != 0);
    433   sync_data.set_navigation_chain_end(
    434       (transition_type_ & content::PAGE_TRANSITION_CHAIN_END) != 0);
    435 
    436   sync_data.set_unique_id(unique_id_);
    437   sync_data.set_timestamp_msec(syncer::TimeToProtoTime(timestamp_));
    438   // The full-resolution timestamp works as a global ID.
    439   sync_data.set_global_id(timestamp_.ToInternalValue());
    440 
    441   sync_data.set_search_terms(UTF16ToUTF8(search_terms_));
    442 
    443   if (favicon_url_.is_valid())
    444     sync_data.set_favicon_url(favicon_url_.spec());
    445 
    446   if (blocked_state_ != STATE_INVALID) {
    447     sync_data.set_blocked_state(
    448         static_cast<sync_pb::TabNavigation_BlockedState>(blocked_state_));
    449   }
    450 
    451   for (std::set<std::string>::const_iterator it =
    452            content_pack_categories_.begin();
    453        it != content_pack_categories_.end(); ++it) {
    454     sync_data.add_content_pack_categories(*it);
    455   }
    456 
    457   return sync_data;
    458 }
    459 
    460 // static
    461 std::vector<NavigationEntry*> SerializedNavigationEntry::ToNavigationEntries(
    462     const std::vector<SerializedNavigationEntry>& navigations,
    463     content::BrowserContext* browser_context) {
    464   int page_id = 0;
    465   std::vector<NavigationEntry*> entries;
    466   for (std::vector<SerializedNavigationEntry>::const_iterator
    467        it = navigations.begin(); it != navigations.end(); ++it) {
    468     entries.push_back(
    469         it->ToNavigationEntry(page_id, browser_context).release());
    470     ++page_id;
    471   }
    472   return entries;
    473 }
    474 
    475 }  // namespace sessions
    476