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       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                         blink::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 // base::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 base::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(base::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     blink::WebReferrerPolicy policy;
    297     if (iterator->ReadInt(&policy_int))
    298       policy = static_cast<blink::WebReferrerPolicy>(policy_int);
    299     else
    300       policy = blink::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
    489