OLD | NEW |
| (Empty) |
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/pickle.h" | |
10 #include "base/time/time.h" | |
11 #include "content/public/browser/child_process_security_policy.h" | |
12 #include "content/public/browser/navigation_controller.h" | |
13 #include "content/public/browser/navigation_entry.h" | |
14 #include "content/public/browser/render_process_host.h" | |
15 #include "content/public/browser/restore_type.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 const uint32_t AW_STATE_VERSION = internal::AW_STATE_VERSION_DATA_URL; | |
36 | |
37 } // namespace | |
38 | |
39 bool WriteToPickle(const content::WebContents& web_contents, | |
40 base::Pickle* pickle) { | |
41 DCHECK(pickle); | |
42 | |
43 if (!internal::WriteHeaderToPickle(pickle)) | |
44 return false; | |
45 | |
46 const content::NavigationController& controller = | |
47 web_contents.GetController(); | |
48 const int entry_count = controller.GetEntryCount(); | |
49 const int selected_entry = controller.GetCurrentEntryIndex(); | |
50 DCHECK_GE(entry_count, 0); | |
51 DCHECK_GE(selected_entry, -1); // -1 is valid | |
52 DCHECK_LT(selected_entry, entry_count); | |
53 | |
54 if (!pickle->WriteInt(entry_count)) | |
55 return false; | |
56 | |
57 if (!pickle->WriteInt(selected_entry)) | |
58 return false; | |
59 | |
60 for (int i = 0; i < entry_count; ++i) { | |
61 if (!internal::WriteNavigationEntryToPickle(*controller.GetEntryAtIndex(i), | |
62 pickle)) | |
63 return false; | |
64 } | |
65 | |
66 // Please update AW_STATE_VERSION and IsSupportedVersion() if serialization | |
67 // format is changed. | |
68 // Make sure the serialization format is updated in a backwards compatible | |
69 // way. | |
70 | |
71 return true; | |
72 } | |
73 | |
74 bool RestoreFromPickle(base::PickleIterator* iterator, | |
75 content::WebContents* web_contents) { | |
76 DCHECK(iterator); | |
77 DCHECK(web_contents); | |
78 | |
79 uint32_t state_version = internal::RestoreHeaderFromPickle(iterator); | |
80 if (!state_version) | |
81 return false; | |
82 | |
83 int entry_count = -1; | |
84 int selected_entry = -2; // -1 is a valid value | |
85 | |
86 if (!iterator->ReadInt(&entry_count)) | |
87 return false; | |
88 | |
89 if (!iterator->ReadInt(&selected_entry)) | |
90 return false; | |
91 | |
92 if (entry_count < 0) | |
93 return false; | |
94 if (selected_entry < -1) | |
95 return false; | |
96 if (selected_entry >= entry_count) | |
97 return false; | |
98 | |
99 std::vector<std::unique_ptr<content::NavigationEntry>> entries; | |
100 entries.reserve(entry_count); | |
101 for (int i = 0; i < entry_count; ++i) { | |
102 entries.push_back(content::NavigationEntry::Create()); | |
103 if (!internal::RestoreNavigationEntryFromPickle(state_version, iterator, | |
104 entries[i].get())) | |
105 return false; | |
106 } | |
107 | |
108 // |web_contents| takes ownership of these entries after this call. | |
109 content::NavigationController& controller = web_contents->GetController(); | |
110 controller.Restore(selected_entry, | |
111 content::RestoreType::LAST_SESSION_EXITED_CLEANLY, | |
112 &entries); | |
113 DCHECK_EQ(0u, entries.size()); | |
114 | |
115 if (controller.GetLastCommittedEntry()) { | |
116 // Set up the file access rights for the selected navigation entry. | |
117 // TODO(joth): This is duplicated from chrome/.../session_restore.cc and | |
118 // should be shared e.g. in NavigationController. http://crbug.com/68222 | |
119 const int id = web_contents->GetRenderProcessHost()->GetID(); | |
120 const content::PageState& page_state = | |
121 controller.GetLastCommittedEntry()->GetPageState(); | |
122 const std::vector<base::FilePath>& file_paths = | |
123 page_state.GetReferencedFiles(); | |
124 for (std::vector<base::FilePath>::const_iterator file = file_paths.begin(); | |
125 file != file_paths.end(); ++file) { | |
126 content::ChildProcessSecurityPolicy::GetInstance()->GrantReadFile(id, | |
127 *file); | |
128 } | |
129 } | |
130 | |
131 controller.LoadIfNecessary(); | |
132 | |
133 return true; | |
134 } | |
135 | |
136 namespace internal { | |
137 | |
138 bool WriteHeaderToPickle(base::Pickle* pickle) { | |
139 return WriteHeaderToPickle(AW_STATE_VERSION, pickle); | |
140 } | |
141 | |
142 bool WriteHeaderToPickle(uint32_t state_version, base::Pickle* pickle) { | |
143 return pickle->WriteUInt32(state_version); | |
144 } | |
145 | |
146 uint32_t RestoreHeaderFromPickle(base::PickleIterator* iterator) { | |
147 uint32_t state_version = -1; | |
148 if (!iterator->ReadUInt32(&state_version)) | |
149 return 0; | |
150 | |
151 if (IsSupportedVersion(state_version)) { | |
152 return state_version; | |
153 } | |
154 | |
155 return 0; | |
156 } | |
157 | |
158 bool IsSupportedVersion(uint32_t state_version) { | |
159 return state_version == internal::AW_STATE_VERSION_INITIAL || | |
160 state_version == internal::AW_STATE_VERSION_DATA_URL; | |
161 } | |
162 | |
163 bool WriteNavigationEntryToPickle(const content::NavigationEntry& entry, | |
164 base::Pickle* pickle) { | |
165 return WriteNavigationEntryToPickle(AW_STATE_VERSION, entry, pickle); | |
166 } | |
167 | |
168 bool WriteNavigationEntryToPickle(uint32_t state_version, | |
169 const content::NavigationEntry& entry, | |
170 base::Pickle* pickle) { | |
171 DCHECK(IsSupportedVersion(state_version)); | |
172 if (!pickle->WriteString(entry.GetURL().spec())) | |
173 return false; | |
174 | |
175 if (!pickle->WriteString(entry.GetVirtualURL().spec())) | |
176 return false; | |
177 | |
178 const content::Referrer& referrer = entry.GetReferrer(); | |
179 if (!pickle->WriteString(referrer.url.spec())) | |
180 return false; | |
181 if (!pickle->WriteInt(static_cast<int>(referrer.policy))) | |
182 return false; | |
183 | |
184 if (!pickle->WriteString16(entry.GetTitle())) | |
185 return false; | |
186 | |
187 if (!pickle->WriteString(entry.GetPageState().ToEncodedData())) | |
188 return false; | |
189 | |
190 if (!pickle->WriteBool(static_cast<int>(entry.GetHasPostData()))) | |
191 return false; | |
192 | |
193 if (!pickle->WriteString(entry.GetOriginalRequestURL().spec())) | |
194 return false; | |
195 | |
196 if (!pickle->WriteString(entry.GetBaseURLForDataURL().spec())) | |
197 return false; | |
198 | |
199 if (state_version >= internal::AW_STATE_VERSION_DATA_URL) { | |
200 const char* data = nullptr; | |
201 size_t size = 0; | |
202 scoped_refptr<const base::RefCountedString> s = entry.GetDataURLAsString(); | |
203 if (s) { | |
204 data = s->front_as<char>(); | |
205 size = s->size(); | |
206 } | |
207 // Even when |entry.GetDataForDataURL()| is null we still need to write a | |
208 // zero-length entry to ensure the fields all line up when read back in. | |
209 if (!pickle->WriteData(data, size)) | |
210 return false; | |
211 } | |
212 | |
213 if (!pickle->WriteBool(static_cast<int>(entry.GetIsOverridingUserAgent()))) | |
214 return false; | |
215 | |
216 if (!pickle->WriteInt64(entry.GetTimestamp().ToInternalValue())) | |
217 return false; | |
218 | |
219 if (!pickle->WriteInt(entry.GetHttpStatusCode())) | |
220 return false; | |
221 | |
222 // Please update AW_STATE_VERSION and IsSupportedVersion() if serialization | |
223 // format is changed. | |
224 // Make sure the serialization format is updated in a backwards compatible | |
225 // way. | |
226 | |
227 return true; | |
228 } | |
229 | |
230 bool RestoreNavigationEntryFromPickle(base::PickleIterator* iterator, | |
231 content::NavigationEntry* entry) { | |
232 return RestoreNavigationEntryFromPickle(AW_STATE_VERSION, iterator, entry); | |
233 } | |
234 | |
235 bool RestoreNavigationEntryFromPickle(uint32_t state_version, | |
236 base::PickleIterator* iterator, | |
237 content::NavigationEntry* entry) { | |
238 DCHECK(IsSupportedVersion(state_version)); | |
239 { | |
240 string url; | |
241 if (!iterator->ReadString(&url)) | |
242 return false; | |
243 entry->SetURL(GURL(url)); | |
244 } | |
245 | |
246 { | |
247 string virtual_url; | |
248 if (!iterator->ReadString(&virtual_url)) | |
249 return false; | |
250 entry->SetVirtualURL(GURL(virtual_url)); | |
251 } | |
252 | |
253 { | |
254 content::Referrer referrer; | |
255 string referrer_url; | |
256 int policy; | |
257 | |
258 if (!iterator->ReadString(&referrer_url)) | |
259 return false; | |
260 if (!iterator->ReadInt(&policy)) | |
261 return false; | |
262 | |
263 referrer.url = GURL(referrer_url); | |
264 referrer.policy = static_cast<blink::WebReferrerPolicy>(policy); | |
265 entry->SetReferrer(referrer); | |
266 } | |
267 | |
268 { | |
269 base::string16 title; | |
270 if (!iterator->ReadString16(&title)) | |
271 return false; | |
272 entry->SetTitle(title); | |
273 } | |
274 | |
275 { | |
276 string content_state; | |
277 if (!iterator->ReadString(&content_state)) | |
278 return false; | |
279 entry->SetPageState( | |
280 content::PageState::CreateFromEncodedData(content_state)); | |
281 } | |
282 | |
283 { | |
284 bool has_post_data; | |
285 if (!iterator->ReadBool(&has_post_data)) | |
286 return false; | |
287 entry->SetHasPostData(has_post_data); | |
288 } | |
289 | |
290 { | |
291 string original_request_url; | |
292 if (!iterator->ReadString(&original_request_url)) | |
293 return false; | |
294 entry->SetOriginalRequestURL(GURL(original_request_url)); | |
295 } | |
296 | |
297 { | |
298 string base_url_for_data_url; | |
299 if (!iterator->ReadString(&base_url_for_data_url)) | |
300 return false; | |
301 entry->SetBaseURLForDataURL(GURL(base_url_for_data_url)); | |
302 } | |
303 | |
304 if (state_version >= internal::AW_STATE_VERSION_DATA_URL) { | |
305 const char* data; | |
306 int size; | |
307 if (!iterator->ReadData(&data, &size)) | |
308 return false; | |
309 if (size > 0) { | |
310 scoped_refptr<base::RefCountedString> ref = new base::RefCountedString(); | |
311 ref->data().assign(data, size); | |
312 entry->SetDataURLAsString(ref); | |
313 } | |
314 } | |
315 | |
316 { | |
317 bool is_overriding_user_agent; | |
318 if (!iterator->ReadBool(&is_overriding_user_agent)) | |
319 return false; | |
320 entry->SetIsOverridingUserAgent(is_overriding_user_agent); | |
321 } | |
322 | |
323 { | |
324 int64_t timestamp; | |
325 if (!iterator->ReadInt64(×tamp)) | |
326 return false; | |
327 entry->SetTimestamp(base::Time::FromInternalValue(timestamp)); | |
328 } | |
329 | |
330 { | |
331 int http_status_code; | |
332 if (!iterator->ReadInt(&http_status_code)) | |
333 return false; | |
334 entry->SetHttpStatusCode(http_status_code); | |
335 } | |
336 | |
337 return true; | |
338 } | |
339 | |
340 } // namespace internal | |
341 | |
342 } // namespace android_webview | |
OLD | NEW |