diff options
author | boliu@chromium.org <boliu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-21 12:34:09 +0000 |
---|---|---|
committer | boliu@chromium.org <boliu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-21 12:34:09 +0000 |
commit | 1f0d041d875f287e1da906c39db249f713433ef2 (patch) | |
tree | 3c193ea4634a524e511967722d9f582571451d3a /android_webview | |
parent | f45a5f17a7cd4b7819f59fd0048cf6f6f37113b0 (diff) | |
download | chromium_src-1f0d041d875f287e1da906c39db249f713433ef2.zip chromium_src-1f0d041d875f287e1da906c39db249f713433ef2.tar.gz chromium_src-1f0d041d875f287e1da906c39db249f713433ef2.tar.bz2 |
Android WebView save/restoreState and backForardList Part 1
Design for save/restoreState is do all serialization in native and pass
an opaque byte array to java to put into the bundle.
This patch contains the native code to (de)serialize a WebContents with
some unit test coverage.
Reasons for not re-using TabNavigation under chrome/:
* Android WebView has different requirements for fields to store since
we are the only ones using values like base_url_for_data_url.
* TabNavigation does unnecessary copying of data, which in Android
WebView case, is undesired since save/restore is called in Android
very frequently.
* TabNavigation is tightly integrated with the rest of chrome session
restore and sync code, and has other purpose in addition to serializing
NavigationEntry.
Optimization-wise, there will be an unnecessary copy when converting
native to java byte array. Only way to avoid it is to know the
allocation size before pickling starts. Maybe consider pre-computing the
size (brittle), or use chrome's design of capping the size of the data
and throwing away old entries and "less important" information. Only
consider this if speed becomes a problem.
BUG=
Android WebView only change. Ran through trybots.
NOTRY=true
Review URL: https://chromiumcodereview.appspot.com/11420056
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@169024 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'android_webview')
-rw-r--r-- | android_webview/android_webview_tests.gypi | 1 | ||||
-rw-r--r-- | android_webview/native/state_serializer.cc | 245 | ||||
-rw-r--r-- | android_webview/native/state_serializer.h | 50 | ||||
-rw-r--r-- | android_webview/native/state_serializer_unittests.cc | 86 | ||||
-rw-r--r-- | android_webview/native/webview_native.gyp | 4 |
5 files changed, 385 insertions, 1 deletions
diff --git a/android_webview/android_webview_tests.gypi b/android_webview/android_webview_tests.gypi index 20585b5..fb9317f 100644 --- a/android_webview/android_webview_tests.gypi +++ b/android_webview/android_webview_tests.gypi @@ -45,6 +45,7 @@ 'sources': [ 'lib/main/webview_tests.cc', 'native/android_stream_reader_url_request_job_unittests.cc', + 'native/state_serializer_unittests.cc', ], }, { diff --git a/android_webview/native/state_serializer.cc b/android_webview/native/state_serializer.cc new file mode 100644 index 0000000..a777cd8e9 --- /dev/null +++ b/android_webview/native/state_serializer.cc @@ -0,0 +1,245 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "android_webview/native/state_serializer.h" + +#include <string> + +#include "base/memory/scoped_vector.h" +#include "base/pickle.h" +#include "base/time.h" +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/web_contents.h" + +// Reasons for not re-using TabNavigation under chrome/ as of 20121116: +// * Android WebView has different requirements for fields to store since +// we are the only ones using values like BaseURLForDataURL. +// * TabNavigation does unnecessary copying of data, which in Android +// WebView case, is undesired since save/restore is called in Android +// very frequently. +// * TabNavigation is tightly integrated with the rest of chrome session +// restore and sync code, and has other purpose in addition to serializing +// NavigationEntry. + +using std::string; + +namespace android_webview { + +namespace { + +// Sanity check value that we are restoring from a valid pickle. +// This can potentially used as an actual serialization version number in the +// future if we ever decide to support restoring from older versions. +const uint32 AW_STATE_VERSION = 20121116; + +} // namespace + +bool WriteToPickle(const content::WebContents& web_contents, + Pickle* pickle) { + DCHECK(pickle); + + if (!internal::WriteHeaderToPickle(pickle)) + return false; + + const content::NavigationController& controller = + web_contents.GetController(); + const int entry_count = controller.GetEntryCount(); + const int selected_entry = controller.GetCurrentEntryIndex(); + DCHECK(entry_count >= 0); + DCHECK(selected_entry >= -1); // -1 is valid + DCHECK(selected_entry < entry_count); + + if (!pickle->WriteInt(entry_count)) + return false; + + if (!pickle->WriteInt(selected_entry)) + return false; + + for (int i = 0; i < entry_count; ++i) { + if (!internal::WriteNavigationEntryToPickle(*controller.GetEntryAtIndex(i), + pickle)) + return false; + } + + // Please update AW_STATE_VERSION if serialization format is changed. + + return true; +} + +bool RestoreFromPickle(PickleIterator* iterator, + content::WebContents* web_contents) { + DCHECK(iterator); + DCHECK(web_contents); + + if (!internal::RestoreHeaderFromPickle(iterator)) + return false; + + int entry_count = -1; + int selected_entry = -2; // -1 is a valid value + + if (!iterator->ReadInt(&entry_count)) + return false; + + if (!iterator->ReadInt(&selected_entry)) + return false; + + if (entry_count < 0) + return false; + if (selected_entry < -1) + return false; + if (selected_entry >= entry_count) + return false; + + ScopedVector<content::NavigationEntry> restored_entries; + for (int i = 0; i < entry_count; ++i) { + restored_entries.push_back(content::NavigationEntry::Create()); + if (!internal::RestoreNavigationEntryFromPickle(iterator, + restored_entries[i])) + return false; + } + + // |web_contents| takes ownership of these entries after this call. + web_contents->GetController().Restore( + selected_entry, + content::NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY, + &restored_entries.get()); + DCHECK_EQ(0u, restored_entries.size()); + + return true; +} + +namespace internal { + +bool WriteHeaderToPickle(Pickle* pickle) { + return pickle->WriteUInt32(AW_STATE_VERSION); +} + +bool RestoreHeaderFromPickle(PickleIterator* iterator) { + uint32 state_version = -1; + if (!iterator->ReadUInt32(&state_version)) + return false; + + if (AW_STATE_VERSION != state_version) + return false; + + return true; +} + +bool WriteNavigationEntryToPickle(const content::NavigationEntry& entry, + Pickle* pickle) { + if (!pickle->WriteString(entry.GetURL().spec())) + return false; + + if (!pickle->WriteString(entry.GetVirtualURL().spec())) + return false; + + const content::Referrer& referrer = entry.GetReferrer(); + if (!pickle->WriteString(referrer.url.spec())) + return false; + if (!pickle->WriteInt(static_cast<int>(referrer.policy))) + return false; + + if (!pickle->WriteString16(entry.GetTitle())) + return false; + + if (!pickle->WriteString(entry.GetContentState())) + return false; + + if (!pickle->WriteBool(static_cast<int>(entry.GetHasPostData()))) + return false; + + if (!pickle->WriteString(entry.GetOriginalRequestURL().spec())) + return false; + + if (!pickle->WriteBool(static_cast<int>(entry.GetIsOverridingUserAgent()))) + return false; + + if (!pickle->WriteInt64(entry.GetTimestamp().ToInternalValue())) + return false; + + // Please update AW_STATE_VERSION if serialization format is changed. + + return true; +} + +bool RestoreNavigationEntryFromPickle(PickleIterator* iterator, + content::NavigationEntry* entry) { + { + string url; + if (!iterator->ReadString(&url)) + return false; + entry->SetURL(GURL(url)); + } + + { + string virtual_url; + if (!iterator->ReadString(&virtual_url)) + return false; + entry->SetVirtualURL(GURL(virtual_url)); + } + + { + content::Referrer referrer; + string referrer_url; + int policy; + + if (!iterator->ReadString(&referrer_url)) + return false; + if (!iterator->ReadInt(&policy)) + return false; + + referrer.url = GURL(referrer_url); + referrer.policy = static_cast<WebKit::WebReferrerPolicy>(policy); + entry->SetReferrer(referrer); + } + + { + string16 title; + if (!iterator->ReadString16(&title)) + return false; + entry->SetTitle(title); + } + + { + string content_state; + if (!iterator->ReadString(&content_state)) + return false; + entry->SetContentState(content_state); + } + + { + bool has_post_data; + if (!iterator->ReadBool(&has_post_data)) + return false; + entry->SetHasPostData(has_post_data); + } + + { + string original_request_url; + if (!iterator->ReadString(&original_request_url)) + return false; + entry->SetOriginalRequestURL(GURL(original_request_url)); + } + + { + bool is_overriding_user_agent; + if (!iterator->ReadBool(&is_overriding_user_agent)) + return false; + entry->SetIsOverridingUserAgent(is_overriding_user_agent); + } + + { + int64 timestamp; + if (!iterator->ReadInt64(×tamp)) + return false; + entry->SetTimestamp(base::Time::FromInternalValue(timestamp)); + } + + return true; +} + +} // namespace internal + +} // namespace android_webview diff --git a/android_webview/native/state_serializer.h b/android_webview/native/state_serializer.h new file mode 100644 index 0000000..3dc6181 --- /dev/null +++ b/android_webview/native/state_serializer.h @@ -0,0 +1,50 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +#ifndef ANDROID_WEBVIEW_NATIVE_STATE_SERIALIZER_H_ +#define ANDROID_WEBVIEW_NATIVE_STATE_SERIALIZER_H_ + +#include "base/compiler_specific.h" + +class Pickle; +class PickleIterator; + +namespace content { + +class NavigationEntry; +class WebContents; + +} // namespace content + +namespace android_webview { + +// Write and restore a WebContents to and from a pickle. Return true on +// success. + +// Note that |pickle| may be changed even if function returns false. +bool WriteToPickle(const content::WebContents& web_contents, + Pickle* pickle) WARN_UNUSED_RESULT; + +// |web_contents| will not be modified if function returns false. +bool RestoreFromPickle(PickleIterator* iterator, + content::WebContents* web_contents) WARN_UNUSED_RESULT; + + +namespace internal { +// Functions below are individual helper functiosn called by functions above. +// They are broken up for unit testing, and should not be called out side of +// tests. +bool WriteHeaderToPickle(Pickle* pickle) WARN_UNUSED_RESULT; +bool RestoreHeaderFromPickle(PickleIterator* iterator) WARN_UNUSED_RESULT; +bool WriteNavigationEntryToPickle(const content::NavigationEntry& entry, + Pickle* pickle) WARN_UNUSED_RESULT; +bool RestoreNavigationEntryFromPickle( + PickleIterator* iterator, + content::NavigationEntry* entry) WARN_UNUSED_RESULT; + +} // namespace interanl + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_NATIVE_STATE_SERIALIZER_H_ diff --git a/android_webview/native/state_serializer_unittests.cc b/android_webview/native/state_serializer_unittests.cc new file mode 100644 index 0000000..44349fe --- /dev/null +++ b/android_webview/native/state_serializer_unittests.cc @@ -0,0 +1,86 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "android_webview/native/state_serializer.h" + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/pickle.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "content/public/browser/content_browser_client.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/common/content_client.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; + +namespace android_webview { + +TEST(AndroidWebViewStateSerializerTest, TestHeaderSerialization) { + Pickle pickle; + bool result = internal::WriteHeaderToPickle(&pickle); + EXPECT_TRUE(result); + + PickleIterator iterator(pickle); + result = internal::RestoreHeaderFromPickle(&iterator); + EXPECT_TRUE(result); +} + +TEST(AndroidWebViewStateSerializerTest, TestNavigationEntrySerialization) { + // This is required for NavigationEntry::Create. + content::ContentClient content_client; + content::ContentBrowserClient browser_client; + content_client.set_browser_for_testing(&browser_client); + content::SetContentClient(&content_client); + + scoped_ptr<content::NavigationEntry> entry( + content::NavigationEntry::Create()); + + const GURL url("http://url"); + const GURL virtual_url("http://virtual_url"); + content::Referrer referrer; + referrer.url = GURL("http://referrer_url"); + referrer.policy = WebKit::WebReferrerPolicyOrigin; + const string16 title(UTF8ToUTF16("title")); + const string content_state("completely bogus state"); + const bool has_post_data = true; + const GURL original_request_url("http://original_request_url"); + const bool is_overriding_user_agent = true; + const base::Time timestamp = base::Time::FromInternalValue(12345); + + entry->SetURL(url); + entry->SetVirtualURL(virtual_url); + entry->SetReferrer(referrer); + entry->SetTitle(title); + entry->SetContentState(content_state); + entry->SetHasPostData(has_post_data); + entry->SetOriginalRequestURL(original_request_url); + entry->SetIsOverridingUserAgent(is_overriding_user_agent); + entry->SetTimestamp(timestamp); + + Pickle pickle; + bool result = internal::WriteNavigationEntryToPickle(*entry, &pickle); + EXPECT_TRUE(result); + + scoped_ptr<content::NavigationEntry> copy(content::NavigationEntry::Create()); + PickleIterator iterator(pickle); + result = internal::RestoreNavigationEntryFromPickle(&iterator, copy.get()); + EXPECT_TRUE(result); + + EXPECT_EQ(url, copy->GetURL()); + EXPECT_EQ(virtual_url, copy->GetVirtualURL()); + EXPECT_EQ(referrer.url, copy->GetReferrer().url); + EXPECT_EQ(referrer.policy, copy->GetReferrer().policy); + EXPECT_EQ(title, copy->GetTitle()); + EXPECT_EQ(content_state, copy->GetContentState()); + EXPECT_EQ(has_post_data, copy->GetHasPostData()); + EXPECT_EQ(original_request_url, copy->GetOriginalRequestURL()); + EXPECT_EQ(is_overriding_user_agent, copy->GetIsOverridingUserAgent()); + EXPECT_EQ(timestamp, copy->GetTimestamp()); +} + +} // namespace android_webview diff --git a/android_webview/native/webview_native.gyp b/android_webview/native/webview_native.gyp index 3bdc34b..fb3b09a 100644 --- a/android_webview/native/webview_native.gyp +++ b/android_webview/native/webview_native.gyp @@ -36,8 +36,8 @@ 'aw_http_auth_handler.h', 'aw_javascript_dialog_creator.cc', 'aw_javascript_dialog_creator.h', - 'aw_resource.h', 'aw_resource.cc', + 'aw_resource.h', 'aw_web_contents_delegate.cc', 'aw_web_contents_delegate.h', 'cookie_manager.cc', @@ -47,6 +47,8 @@ 'js_result_handler.cc', 'js_result_handler.h', 'net_init_native_callback.cc', + 'state_serializer.cc', + 'state_serializer.h', ], }, { |