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(×tamp)) 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