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
29using std::string;
30
31namespace android_webview {
32
33namespace {
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.
38const uint32 AW_STATE_VERSION = 20130814;
39
40}  // namespace
41
42bool 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_GE(entry_count, 0);
54  DCHECK_GE(selected_entry, -1);  // -1 is valid
55  DCHECK_LT(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
74bool 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
137namespace internal {
138
139bool WriteHeaderToPickle(Pickle* pickle) {
140  return pickle->WriteUInt32(AW_STATE_VERSION);
141}
142
143bool 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
154bool 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
197bool 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