state_serializer.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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. #include "android_webview/browser/state_serializer.h"
  5. #include <memory>
  6. #include <string>
  7. #include <vector>
  8. #include "base/pickle.h"
  9. #include "base/time/time.h"
  10. #include "content/public/browser/navigation_controller.h"
  11. #include "content/public/browser/navigation_entry.h"
  12. #include "content/public/browser/navigation_entry_restore_context.h"
  13. #include "content/public/browser/render_process_host.h"
  14. #include "content/public/browser/restore_type.h"
  15. #include "content/public/browser/web_contents.h"
  16. #include "content/public/common/referrer.h"
  17. #include "third_party/blink/public/common/features.h"
  18. #include "third_party/blink/public/common/page_state/page_state.h"
  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. using std::string;
  29. namespace android_webview {
  30. namespace {
  31. const uint32_t AW_STATE_VERSION = internal::AW_STATE_VERSION_DATA_URL;
  32. } // namespace
  33. void WriteToPickle(content::WebContents& web_contents, base::Pickle* pickle) {
  34. DCHECK(pickle);
  35. internal::WriteHeaderToPickle(pickle);
  36. content::NavigationController& controller = web_contents.GetController();
  37. const int entry_count = controller.GetEntryCount();
  38. const int selected_entry = controller.GetLastCommittedEntryIndex();
  39. if (blink::features::IsInitialNavigationEntryEnabled()) {
  40. // When InitialNavigationEntry is enabled, a NavigationEntry will always
  41. // exist, so there will always be at least 1 entry.
  42. DCHECK_GE(entry_count, 1);
  43. DCHECK_GE(selected_entry, 0);
  44. } else {
  45. // When InitialNavigationEntry is disabled, there might be 0 entries.
  46. DCHECK_GE(entry_count, 0);
  47. DCHECK_GE(selected_entry, -1); // -1 is valid
  48. }
  49. DCHECK_LT(selected_entry, entry_count);
  50. pickle->WriteInt(entry_count);
  51. pickle->WriteInt(selected_entry);
  52. for (int i = 0; i < entry_count; ++i) {
  53. internal::WriteNavigationEntryToPickle(*controller.GetEntryAtIndex(i),
  54. pickle);
  55. }
  56. // Please update AW_STATE_VERSION and IsSupportedVersion() if serialization
  57. // format is changed.
  58. // Make sure the serialization format is updated in a backwards compatible
  59. // way.
  60. }
  61. bool RestoreFromPickle(base::PickleIterator* iterator,
  62. content::WebContents* web_contents) {
  63. DCHECK(iterator);
  64. DCHECK(web_contents);
  65. uint32_t state_version = internal::RestoreHeaderFromPickle(iterator);
  66. if (!state_version)
  67. return false;
  68. int entry_count = -1;
  69. int selected_entry = -2; // -1 is a valid value
  70. if (!iterator->ReadInt(&entry_count))
  71. return false;
  72. if (!iterator->ReadInt(&selected_entry))
  73. return false;
  74. if (entry_count < 0)
  75. return false;
  76. if (selected_entry < -1)
  77. return false;
  78. if (selected_entry >= entry_count)
  79. return false;
  80. std::unique_ptr<content::NavigationEntryRestoreContext> context =
  81. content::NavigationEntryRestoreContext::Create();
  82. std::vector<std::unique_ptr<content::NavigationEntry>> entries;
  83. entries.reserve(entry_count);
  84. for (int i = 0; i < entry_count; ++i) {
  85. entries.push_back(content::NavigationEntry::Create());
  86. if (!internal::RestoreNavigationEntryFromPickle(
  87. state_version, iterator, entries[i].get(), context.get()))
  88. return false;
  89. }
  90. // |web_contents| takes ownership of these entries after this call.
  91. content::NavigationController& controller = web_contents->GetController();
  92. controller.Restore(selected_entry, content::RestoreType::kRestored, &entries);
  93. DCHECK_EQ(0u, entries.size());
  94. controller.LoadIfNecessary();
  95. return true;
  96. }
  97. namespace internal {
  98. void WriteHeaderToPickle(base::Pickle* pickle) {
  99. WriteHeaderToPickle(AW_STATE_VERSION, pickle);
  100. }
  101. void WriteHeaderToPickle(uint32_t state_version, base::Pickle* pickle) {
  102. pickle->WriteUInt32(state_version);
  103. }
  104. uint32_t RestoreHeaderFromPickle(base::PickleIterator* iterator) {
  105. uint32_t state_version = -1;
  106. if (!iterator->ReadUInt32(&state_version))
  107. return 0;
  108. if (IsSupportedVersion(state_version)) {
  109. return state_version;
  110. }
  111. return 0;
  112. }
  113. bool IsSupportedVersion(uint32_t state_version) {
  114. return state_version == internal::AW_STATE_VERSION_INITIAL ||
  115. state_version == internal::AW_STATE_VERSION_DATA_URL;
  116. }
  117. void WriteNavigationEntryToPickle(content::NavigationEntry& entry,
  118. base::Pickle* pickle) {
  119. WriteNavigationEntryToPickle(AW_STATE_VERSION, entry, pickle);
  120. }
  121. void WriteNavigationEntryToPickle(uint32_t state_version,
  122. content::NavigationEntry& entry,
  123. base::Pickle* pickle) {
  124. DCHECK(IsSupportedVersion(state_version));
  125. pickle->WriteString(entry.GetURL().spec());
  126. pickle->WriteString(entry.GetVirtualURL().spec());
  127. const content::Referrer& referrer = entry.GetReferrer();
  128. pickle->WriteString(referrer.url.spec());
  129. pickle->WriteInt(static_cast<int>(referrer.policy));
  130. pickle->WriteString16(entry.GetTitle());
  131. pickle->WriteString(entry.GetPageState().ToEncodedData());
  132. pickle->WriteBool(static_cast<int>(entry.GetHasPostData()));
  133. pickle->WriteString(entry.GetOriginalRequestURL().spec());
  134. pickle->WriteString(entry.GetBaseURLForDataURL().spec());
  135. if (state_version >= internal::AW_STATE_VERSION_DATA_URL) {
  136. const char* data = nullptr;
  137. size_t size = 0;
  138. const scoped_refptr<const base::RefCountedString>& s =
  139. entry.GetDataURLAsString();
  140. if (s) {
  141. data = s->front_as<char>();
  142. size = s->size();
  143. }
  144. // Even when |entry.GetDataForDataURL()| is null we still need to write a
  145. // zero-length entry to ensure the fields all line up when read back in.
  146. pickle->WriteData(data, size);
  147. }
  148. pickle->WriteBool(static_cast<int>(entry.GetIsOverridingUserAgent()));
  149. pickle->WriteInt64(entry.GetTimestamp().ToInternalValue());
  150. pickle->WriteInt(entry.GetHttpStatusCode());
  151. // Please update AW_STATE_VERSION and IsSupportedVersion() if serialization
  152. // format is changed.
  153. // Make sure the serialization format is updated in a backwards compatible
  154. // way.
  155. }
  156. bool RestoreNavigationEntryFromPickle(
  157. base::PickleIterator* iterator,
  158. content::NavigationEntry* entry,
  159. content::NavigationEntryRestoreContext* context) {
  160. return RestoreNavigationEntryFromPickle(AW_STATE_VERSION, iterator, entry,
  161. context);
  162. }
  163. bool RestoreNavigationEntryFromPickle(
  164. uint32_t state_version,
  165. base::PickleIterator* iterator,
  166. content::NavigationEntry* entry,
  167. content::NavigationEntryRestoreContext* context) {
  168. DCHECK(IsSupportedVersion(state_version));
  169. DCHECK(iterator);
  170. DCHECK(entry);
  171. DCHECK(context);
  172. GURL deserialized_url;
  173. {
  174. string url;
  175. if (!iterator->ReadString(&url))
  176. return false;
  177. deserialized_url = GURL(url);
  178. // Note: The url will be cloberred by the SetPageState call below (see how
  179. // RecursivelyGenerateFrameEntries uses PageState data to create
  180. // FrameNavigationEntries).
  181. //
  182. // Nevertheless, we call SetURL here to temporarily set the URL, because it
  183. // modifies the state that might be depended on in some calls below (e.g.
  184. // the SetVirtualURL call).
  185. entry->SetURL(deserialized_url);
  186. }
  187. {
  188. string virtual_url;
  189. if (!iterator->ReadString(&virtual_url))
  190. return false;
  191. entry->SetVirtualURL(GURL(virtual_url));
  192. }
  193. content::Referrer deserialized_referrer;
  194. {
  195. // Note: The referrer will be cloberred by the SetPageState call below (see
  196. // how RecursivelyGenerateFrameEntries uses PageState data to create
  197. // FrameNavigationEntries).
  198. string referrer_url;
  199. int policy;
  200. if (!iterator->ReadString(&referrer_url))
  201. return false;
  202. if (!iterator->ReadInt(&policy))
  203. return false;
  204. deserialized_referrer.url = GURL(referrer_url);
  205. deserialized_referrer.policy = content::Referrer::ConvertToPolicy(policy);
  206. }
  207. {
  208. std::u16string title;
  209. if (!iterator->ReadString16(&title))
  210. return false;
  211. entry->SetTitle(title);
  212. }
  213. {
  214. string content_state;
  215. if (!iterator->ReadString(&content_state))
  216. return false;
  217. // In legacy output of WebViewProvider.saveState, the |content_state|
  218. // might be empty - we need to gracefully handle such data when
  219. // it is deserialized via WebViewProvider.restoreState.
  220. if (content_state.empty()) {
  221. // Ensure that the deserialized/restored content::NavigationEntry (and
  222. // the content::FrameNavigationEntry underneath) has a valid PageState.
  223. entry->SetPageState(blink::PageState::CreateFromURL(deserialized_url),
  224. context);
  225. // The |deserialized_referrer| might be inconsistent with the referrer
  226. // embedded inside the PageState set above. Nevertheless, to minimize
  227. // changes to behavior of old session restore entries, we restore the
  228. // deserialized referrer here.
  229. //
  230. // TODO(lukasza): Consider including the |deserialized_referrer| in the
  231. // PageState set above + drop the SetReferrer call below. This will
  232. // slightly change the legacy behavior, but will make PageState and
  233. // Referrer consistent.
  234. entry->SetReferrer(deserialized_referrer);
  235. } else {
  236. // Note that PageState covers and will clobber some of the values covered
  237. // by data within |iterator| (e.g. URL and referrer).
  238. entry->SetPageState(
  239. blink::PageState::CreateFromEncodedData(content_state), context);
  240. // |deserialized_url| and |deserialized_referrer| are redundant wrt
  241. // PageState, but they should be consistent / in-sync.
  242. DCHECK_EQ(deserialized_url, entry->GetURL());
  243. DCHECK_EQ(deserialized_referrer.url, entry->GetReferrer().url);
  244. DCHECK_EQ(deserialized_referrer.policy, entry->GetReferrer().policy);
  245. }
  246. }
  247. {
  248. bool has_post_data;
  249. if (!iterator->ReadBool(&has_post_data))
  250. return false;
  251. entry->SetHasPostData(has_post_data);
  252. }
  253. {
  254. string original_request_url;
  255. if (!iterator->ReadString(&original_request_url))
  256. return false;
  257. entry->SetOriginalRequestURL(GURL(original_request_url));
  258. }
  259. {
  260. string base_url_for_data_url;
  261. if (!iterator->ReadString(&base_url_for_data_url))
  262. return false;
  263. entry->SetBaseURLForDataURL(GURL(base_url_for_data_url));
  264. }
  265. if (state_version >= internal::AW_STATE_VERSION_DATA_URL) {
  266. const char* data;
  267. size_t size;
  268. if (!iterator->ReadData(&data, &size))
  269. return false;
  270. if (size > 0) {
  271. scoped_refptr<base::RefCountedString> ref = new base::RefCountedString();
  272. ref->data().assign(data, size);
  273. entry->SetDataURLAsString(ref);
  274. }
  275. }
  276. {
  277. bool is_overriding_user_agent;
  278. if (!iterator->ReadBool(&is_overriding_user_agent))
  279. return false;
  280. entry->SetIsOverridingUserAgent(is_overriding_user_agent);
  281. }
  282. {
  283. int64_t timestamp;
  284. if (!iterator->ReadInt64(&timestamp))
  285. return false;
  286. entry->SetTimestamp(base::Time::FromInternalValue(timestamp));
  287. }
  288. {
  289. int http_status_code;
  290. if (!iterator->ReadInt(&http_status_code))
  291. return false;
  292. entry->SetHttpStatusCode(http_status_code);
  293. }
  294. return true;
  295. }
  296. } // namespace internal
  297. } // namespace android_webview