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