Home | History | Annotate | Download | only in native
      1 // Copyright (c) 2012 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 "android_webview/native/state_serializer.h"
      6 
      7 #include <string>
      8 
      9 #include "base/memory/scoped_vector.h"
     10 #include "base/pickle.h"
     11 #include "base/time/time.h"
     12 #include "content/public/browser/child_process_security_policy.h"
     13 #include "content/public/browser/navigation_controller.h"
     14 #include "content/public/browser/navigation_entry.h"
     15 #include "content/public/browser/render_process_host.h"
     16 #include "content/public/browser/web_contents.h"
     17 #include "content/public/common/page_state.h"
     18 
     19 // Reasons for not re-using TabNavigation under chrome/ as of 20121116:
     20 // * Android WebView has different requirements for fields to store since
     21 //   we are the only ones using values like BaseURLForDataURL.
     22 // * TabNavigation does unnecessary copying of data, which in Android
     23 //   WebView case, is undesired since save/restore is called in Android
     24 //   very frequently.
     25 // * TabNavigation is tightly integrated with the rest of chrome session
     26 //   restore and sync code, and has other purpose in addition to serializing
     27 //   NavigationEntry.
     28 
     29 using std::string;
     30 
     31 namespace android_webview {
     32 
     33 namespace {
     34 
     35 // Sanity check value that we are restoring from a valid pickle.
     36 // This can potentially used as an actual serialization version number in the
     37 // future if we ever decide to support restoring from older versions.
     38 const uint32 AW_STATE_VERSION = 20130814;
     39 
     40 }  // namespace
     41 
     42 bool WriteToPickle(const content::WebContents& web_contents,
     43                    Pickle* pickle) {
     44   DCHECK(pickle);
     45 
     46   if (!internal::WriteHeaderToPickle(pickle))
     47     return false;
     48 
     49   const content::NavigationController& controller =
     50       web_contents.GetController();
     51   const int entry_count = controller.GetEntryCount();
     52   const int selected_entry = controller.GetCurrentEntryIndex();
     53   DCHECK(entry_count >= 0);
     54   DCHECK(selected_entry >= -1);  // -1 is valid
     55   DCHECK(selected_entry < entry_count);
     56 
     57   if (!pickle->WriteInt(entry_count))
     58     return false;
     59 
     60   if (!pickle->WriteInt(selected_entry))
     61     return false;
     62 
     63   for (int i = 0; i < entry_count; ++i) {
     64     if (!internal::WriteNavigationEntryToPickle(*controller.GetEntryAtIndex(i),
     65                                                 pickle))
     66       return false;
     67   }
     68 
     69   // Please update AW_STATE_VERSION if serialization format is changed.
     70 
     71   return true;
     72 }
     73 
     74 bool RestoreFromPickle(PickleIterator* iterator,
     75                        content::WebContents* web_contents) {
     76   DCHECK(iterator);
     77   DCHECK(web_contents);
     78 
     79   if (!internal::RestoreHeaderFromPickle(iterator))
     80     return false;
     81 
     82   int entry_count = -1;
     83   int selected_entry = -2;  // -1 is a valid value
     84 
     85   if (!iterator->ReadInt(&entry_count))
     86     return false;
     87 
     88   if (!iterator->ReadInt(&selected_entry))
     89     return false;
     90 
     91   if (entry_count < 0)
     92     return false;
     93   if (selected_entry < -1)
     94     return false;
     95   if (selected_entry >= entry_count)
     96     return false;
     97 
     98   ScopedVector<content::NavigationEntry> restored_entries;
     99   for (int i = 0; i < entry_count; ++i) {
    100     restored_entries.push_back(content::NavigationEntry::Create());
    101     if (!internal::RestoreNavigationEntryFromPickle(iterator,
    102                                                     restored_entries[i]))
    103       return false;
    104 
    105     restored_entries[i]->SetPageID(i);
    106   }
    107 
    108   // |web_contents| takes ownership of these entries after this call.
    109   content::NavigationController& controller = web_contents->GetController();
    110   controller.Restore(
    111       selected_entry,
    112       content::NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
    113       &restored_entries.get());
    114   DCHECK_EQ(0u, restored_entries.size());
    115 
    116   if (controller.GetActiveEntry()) {
    117     // Set up the file access rights for the selected navigation entry.
    118     // TODO(joth): This is duplicated from chrome/.../session_restore.cc and
    119     // should be shared e.g. in  NavigationController. http://crbug.com/68222
    120     const int id = web_contents->GetRenderProcessHost()->GetID();
    121     const content::PageState& page_state =
    122         controller.GetActiveEntry()->GetPageState();
    123     const std::vector<base::FilePath>& file_paths =
    124         page_state.GetReferencedFiles();
    125     for (std::vector<base::FilePath>::const_iterator file = file_paths.begin();
    126          file != file_paths.end(); ++file) {
    127       content::ChildProcessSecurityPolicy::GetInstance()->GrantReadFile(id,
    128                                                                         *file);
    129     }
    130   }
    131 
    132   controller.LoadIfNecessary();
    133 
    134   return true;
    135 }
    136 
    137 namespace internal {
    138 
    139 bool WriteHeaderToPickle(Pickle* pickle) {
    140   return pickle->WriteUInt32(AW_STATE_VERSION);
    141 }
    142 
    143 bool RestoreHeaderFromPickle(PickleIterator* iterator) {
    144   uint32 state_version = -1;
    145   if (!iterator->ReadUInt32(&state_version))
    146     return false;
    147 
    148   if (AW_STATE_VERSION != state_version)
    149     return false;
    150 
    151   return true;
    152 }
    153 
    154 bool WriteNavigationEntryToPickle(const content::NavigationEntry& entry,
    155                                   Pickle* pickle) {
    156   if (!pickle->WriteString(entry.GetURL().spec()))
    157     return false;
    158 
    159   if (!pickle->WriteString(entry.GetVirtualURL().spec()))
    160     return false;
    161 
    162   const content::Referrer& referrer = entry.GetReferrer();
    163   if (!pickle->WriteString(referrer.url.spec()))
    164     return false;
    165   if (!pickle->WriteInt(static_cast<int>(referrer.policy)))
    166     return false;
    167 
    168   if (!pickle->WriteString16(entry.GetTitle()))
    169     return false;
    170 
    171   if (!pickle->WriteString(entry.GetPageState().ToEncodedData()))
    172     return false;
    173 
    174   if (!pickle->WriteBool(static_cast<int>(entry.GetHasPostData())))
    175     return false;
    176 
    177   if (!pickle->WriteString(entry.GetOriginalRequestURL().spec()))
    178     return false;
    179 
    180   if (!pickle->WriteString(entry.GetBaseURLForDataURL().spec()))
    181     return false;
    182 
    183   if (!pickle->WriteBool(static_cast<int>(entry.GetIsOverridingUserAgent())))
    184     return false;
    185 
    186   if (!pickle->WriteInt64(entry.GetTimestamp().ToInternalValue()))
    187     return false;
    188 
    189   if (!pickle->WriteInt(entry.GetHttpStatusCode()))
    190     return false;
    191 
    192   // Please update AW_STATE_VERSION if serialization format is changed.
    193 
    194   return true;
    195 }
    196 
    197 bool RestoreNavigationEntryFromPickle(PickleIterator* iterator,
    198                                       content::NavigationEntry* entry) {
    199   {
    200     string url;
    201     if (!iterator->ReadString(&url))
    202       return false;
    203     entry->SetURL(GURL(url));
    204   }
    205 
    206   {
    207     string virtual_url;
    208     if (!iterator->ReadString(&virtual_url))
    209       return false;
    210     entry->SetVirtualURL(GURL(virtual_url));
    211   }
    212 
    213   {
    214     content::Referrer referrer;
    215     string referrer_url;
    216     int policy;
    217 
    218     if (!iterator->ReadString(&referrer_url))
    219       return false;
    220     if (!iterator->ReadInt(&policy))
    221       return false;
    222 
    223     referrer.url = GURL(referrer_url);
    224     referrer.policy = static_cast<blink::WebReferrerPolicy>(policy);
    225     entry->SetReferrer(referrer);
    226   }
    227 
    228   {
    229     base::string16 title;
    230     if (!iterator->ReadString16(&title))
    231       return false;
    232     entry->SetTitle(title);
    233   }
    234 
    235   {
    236     string content_state;
    237     if (!iterator->ReadString(&content_state))
    238       return false;
    239     entry->SetPageState(
    240         content::PageState::CreateFromEncodedData(content_state));
    241   }
    242 
    243   {
    244     bool has_post_data;
    245     if (!iterator->ReadBool(&has_post_data))
    246       return false;
    247     entry->SetHasPostData(has_post_data);
    248   }
    249 
    250   {
    251     string original_request_url;
    252     if (!iterator->ReadString(&original_request_url))
    253       return false;
    254     entry->SetOriginalRequestURL(GURL(original_request_url));
    255   }
    256 
    257   {
    258     string base_url_for_data_url;
    259     if (!iterator->ReadString(&base_url_for_data_url))
    260       return false;
    261     entry->SetBaseURLForDataURL(GURL(base_url_for_data_url));
    262   }
    263 
    264   {
    265     bool is_overriding_user_agent;
    266     if (!iterator->ReadBool(&is_overriding_user_agent))
    267       return false;
    268     entry->SetIsOverridingUserAgent(is_overriding_user_agent);
    269   }
    270 
    271   {
    272     int64 timestamp;
    273     if (!iterator->ReadInt64(&timestamp))
    274       return false;
    275     entry->SetTimestamp(base::Time::FromInternalValue(timestamp));
    276   }
    277 
    278   {
    279     int http_status_code;
    280     if (!iterator->ReadInt(&http_status_code))
    281       return false;
    282     entry->SetHttpStatusCode(http_status_code);
    283   }
    284 
    285   return true;
    286 }
    287 
    288 }  // namespace internal
    289 
    290 }  // namespace android_webview
    291