diff options
author | sgurun@chromium.org <sgurun@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-25 04:46:38 +0000 |
---|---|---|
committer | sgurun@chromium.org <sgurun@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-25 04:46:38 +0000 |
commit | c1a734423680e44bdfb85cf92cea5405708c9085 (patch) | |
tree | fa9894a9b038d12a9c0414b502aa14de71e29d25 | |
parent | a3f0f9e11c1047d1a7f434943ff9c799ce77e4d7 (diff) | |
download | chromium_src-c1a734423680e44bdfb85cf92cea5405708c9085.zip chromium_src-c1a734423680e44bdfb85cf92cea5405708c9085.tar.gz chromium_src-c1a734423680e44bdfb85cf92cea5405708c9085.tar.bz2 |
Handle resubmission of HTTP Posts.
Implement functionality to handle resubmission of forms in Webview.
BUG=None
Review URL: https://chromiumcodereview.appspot.com/11187032
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@164014 0039d316-1c4b-4281-b951-d872f2087c98
12 files changed, 257 insertions, 1 deletions
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java index 9cd3ba6..8951329 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java @@ -6,6 +6,9 @@ package org.chromium.android_webview; import android.graphics.Rect; import android.graphics.RectF; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.util.Log; import android.view.KeyEvent; import android.webkit.ConsoleMessage; @@ -38,6 +41,29 @@ public abstract class AwContentsClient extends ContentViewClient { //-------------------------------------------------------------------------------------------- class WebContentsDelegateAdapter extends AwWebContentsDelegate { + + // The message ids. + public final static int CONTINUE_PENDING_RELOAD = 1; + public final static int CANCEL_PENDING_RELOAD = 2; + + // Handler associated with this adapter. + // TODO(sgurun) Remember the URL to cancel the resend behavior + // if it is different than the most recent NavigationController entry. + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case CONTINUE_PENDING_RELOAD: + ((ContentViewCore) msg.obj).continuePendingReload(); + break; + case CANCEL_PENDING_RELOAD: + ((ContentViewCore) msg.obj).cancelPendingReload(); + break; + } + } + }; + @Override public void onLoadProgressChanged(int progress) { AwContentsClient.this.onProgressChanged(progress); @@ -100,6 +126,13 @@ public abstract class AwContentsClient extends ContentViewClient { public void onUrlStarredChanged(boolean starred) { // TODO: implement } + + @Override + public void showRepostFormWarningDialog(ContentViewCore contentViewCore) { + Message dontResend = mHandler.obtainMessage(CANCEL_PENDING_RELOAD, contentViewCore); + Message resend = mHandler.obtainMessage(CONTINUE_PENDING_RELOAD, contentViewCore); + AwContentsClient.this.onFormResubmission(dontResend, resend); + } } class AwWebContentsObserver extends WebContentsObserverAndroid { @@ -167,6 +200,8 @@ public abstract class AwContentsClient extends ContentViewClient { public abstract void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm); + public abstract void onFormResubmission(Message dontResend, Message resend); + protected abstract void handleJsAlert(String url, String message, JsResultReceiver receiver); protected abstract void handleJsBeforeUnload(String url, String message, diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidWebViewTestBase.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidWebViewTestBase.java index ad15bf1..c516b96 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidWebViewTestBase.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidWebViewTestBase.java @@ -125,6 +125,37 @@ public class AndroidWebViewTestBase } /** + * Posts url on the UI thread and blocks until onPageFinished is called. + */ + protected void postUrlSync(final AwContents awContents, + CallbackHelper onPageFinishedHelper, final String url, + byte[] postData) throws Throwable { + int currentCallCount = onPageFinishedHelper.getCallCount(); + postUrlAsync(awContents, url, postData); + onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_SECONDS, + TimeUnit.SECONDS); + } + + /** + * Loads url on the UI thread but does not block. + */ + protected void postUrlAsync(final AwContents awContents, + final String url, byte[] postData) throws Throwable { + class PostUrl implements Runnable { + byte[] mPostData; + public PostUrl(byte[] postData) { + mPostData = postData; + } + @Override + public void run() { + awContents.loadUrl(LoadUrlParams.createLoadHttpPostParams(url, + mPostData)); + } + } + runTestOnUiThread(new PostUrl(postData)); + } + + /** * Loads data on the UI thread and blocks until onPageFinished is called. */ protected void loadDataSync(final AwContents awContents, diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java new file mode 100644 index 0000000..2e13880 --- /dev/null +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java @@ -0,0 +1,127 @@ +// 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. + +package org.chromium.android_webview.test; + +import android.content.Context; +import android.os.Message; +import android.test.suitebuilder.annotation.SmallTest; + +import org.apache.http.util.EncodingUtils; +import org.chromium.android_webview.AwContents; +import org.chromium.android_webview.test.util.TestWebServer; +import org.chromium.base.test.util.Feature; +import org.chromium.content.browser.ContentViewCore; +import org.chromium.content.browser.test.util.TestCallbackHelperContainer; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Tests if resubmission of post data is handled properly. + */ +public class AwContentsClientOnFormResubmissionTest extends AndroidWebViewTestBase { + + private static class TestAwContentsClient + extends org.chromium.android_webview.test.TestAwContentsClient { + + // Number of times onFormResubmit is called. + private int mResubmissions = 0; + // Whether to resubmit Post data on reload. + private boolean mResubmit = false; + + public int getResubmissions() { + return mResubmissions; + } + public void setResubmit(boolean resubmit) { + mResubmit = resubmit; + } + @Override + public void onFormResubmission(Message dontResend, Message resend) { + mResubmissions++; + if (mResubmit) { + resend.sendToTarget(); + } else { + dontResend.sendToTarget(); + } + } + } + + // Server responses for load and reload of posts. + private final String LOAD_RESPONSE = + "<html><head><title>Load</title></head><body>HELLO</body></html>"; + private final String RELOAD_RESPONSE = + "<html><head><title>Reload</title></head><body>HELLO</body></html>"; + + // Server timeout in seconds. Used to detect dontResend case. + private final int TIMEOUT = 3; + + // The web server. + private TestWebServer mServer; + // The mock client. + private TestAwContentsClient mContentsClient; + private AwContents mAwContents; + + @Override + public void setUp() throws Exception { + super.setUp(); + mServer = new TestWebServer(false); + mContentsClient = new TestAwContentsClient(); + final AwTestContainerView testContainerView = + createAwTestContainerViewOnMainSync(mContentsClient); + mAwContents = testContainerView.getAwContents(); + } + + @Override + public void tearDown() throws Exception { + mServer.shutdown(); + super.tearDown(); + } + + @SmallTest + @Feature({"Android-WebView", "Navigation"}) + public void testResend() throws Throwable { + mContentsClient.setResubmit(true); + doReload(); + assertEquals(1, mContentsClient.getResubmissions()); + assertEquals("Reload", getTitleOnUiThread(mAwContents)); + } + + @SmallTest + @Feature({"Android-WebView", "Navigation"}) + public void testDontResend() throws Throwable { + mContentsClient.setResubmit(false); + doReload(); + assertEquals(1, mContentsClient.getResubmissions()); + assertEquals("Load", getTitleOnUiThread(mAwContents)); + } + + protected void doReload() throws Throwable { + String url = mServer.setResponse("/form", LOAD_RESPONSE, null); + String postData = "content=blabla"; + byte[] data = EncodingUtils.getBytes(postData, "BASE64"); + postUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url, data); + assertEquals(0, mContentsClient.getResubmissions()); + assertEquals("Load", getTitleOnUiThread(mAwContents)); + // Verify reload works as expected. + mServer.setResponse("/form", RELOAD_RESPONSE, null); + TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper = + mContentsClient.getOnPageFinishedHelper(); + int callCount = onPageFinishedHelper.getCallCount(); + // Run reload on UI thread. + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + mAwContents.getContentViewCore().reload(); + } + }); + try { + // Wait for page finished callback, or a timeout. A timeout is necessary + // to detect a dontResend response. + onPageFinishedHelper.waitForCallback(callCount, 1, TIMEOUT, TimeUnit.SECONDS); + } catch (TimeoutException e) { + } + } +} diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/NullContentsClient.java b/android_webview/javatests/src/org/chromium/android_webview/test/NullContentsClient.java index 2760c2d..594b322 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/NullContentsClient.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/NullContentsClient.java @@ -5,6 +5,7 @@ package org.chromium.android_webview.test; import android.content.Context; +import android.os.Message; import android.view.KeyEvent; import android.webkit.ConsoleMessage; @@ -84,4 +85,9 @@ class NullContentsClient extends AwContentsClient { @Override public void onReceivedError(int errorCode, String description, String failingUrl) { } + + @Override + public void onFormResubmission(Message dontResend, Message resend) { + dontResend.sendToTarget(); + } } diff --git a/chrome/browser/component/web_contents_delegate_android/DEPS b/chrome/browser/component/web_contents_delegate_android/DEPS index beaf5c3..2e11b18 100644 --- a/chrome/browser/component/web_contents_delegate_android/DEPS +++ b/chrome/browser/component/web_contents_delegate_android/DEPS @@ -1,3 +1,4 @@ include_rules = [ "+chrome/browser/component/web_contents_delegate_android", + "+content/public/android/java", ] diff --git a/chrome/browser/component/web_contents_delegate_android/java/src/org/chromium/chrome/browser/component/web_contents_delegate_android/WebContentsDelegateAndroid.java b/chrome/browser/component/web_contents_delegate_android/java/src/org/chromium/chrome/browser/component/web_contents_delegate_android/WebContentsDelegateAndroid.java index cee3cdf..be6934c 100644 --- a/chrome/browser/component/web_contents_delegate_android/java/src/org/chromium/chrome/browser/component/web_contents_delegate_android/WebContentsDelegateAndroid.java +++ b/chrome/browser/component/web_contents_delegate_android/java/src/org/chromium/chrome/browser/component/web_contents_delegate_android/WebContentsDelegateAndroid.java @@ -9,6 +9,7 @@ import android.view.KeyEvent; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; +import org.chromium.content.browser.ContentViewCore; /** * Java peer of the native class of the same name. @@ -106,4 +107,12 @@ public class WebContentsDelegateAndroid { String sourceId) { return false; } + + /** + * Report a form resubmission. The overwriter of this function should eventually call + * either of ContentViewCore.ContinuePendingReload or ContentViewCore.CancelPendingReload. + */ + @CalledByNative + public void showRepostFormWarningDialog(ContentViewCore contentViewCore) { + } } diff --git a/chrome/browser/component/web_contents_delegate_android/web_contents_delegate_android.cc b/chrome/browser/component/web_contents_delegate_android/web_contents_delegate_android.cc index 427f203..af997c4 100644 --- a/chrome/browser/component/web_contents_delegate_android/web_contents_delegate_android.cc +++ b/chrome/browser/component/web_contents_delegate_android/web_contents_delegate_android.cc @@ -8,6 +8,7 @@ #include "base/android/jni_android.h" #include "base/android/jni_string.h" +#include "content/public/browser/android/content_view_core.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/invalidate_type.h" #include "content/public/browser/page_navigator.h" @@ -265,6 +266,20 @@ bool WebContentsDelegateAndroid::TakeFocus(WebContents* source, bool reverse) { env, obj.obj(), reverse); } +void WebContentsDelegateAndroid::ShowRepostFormWarningDialog( + WebContents* source) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env); + if (obj.is_null()) + return; + ScopedJavaLocalRef<jobject> content_view_core = + content::ContentViewCore::FromWebContents(source)->GetJavaObject(); + if (content_view_core.is_null()) + return; + Java_WebContentsDelegateAndroid_showRepostFormWarningDialog(env, obj.obj(), + content_view_core.obj()); +} + // ---------------------------------------------------------------------------- // Native JNI methods // ---------------------------------------------------------------------------- diff --git a/chrome/browser/component/web_contents_delegate_android/web_contents_delegate_android.gypi b/chrome/browser/component/web_contents_delegate_android/web_contents_delegate_android.gypi index 922e802..23edd76 100644 --- a/chrome/browser/component/web_contents_delegate_android/web_contents_delegate_android.gypi +++ b/chrome/browser/component/web_contents_delegate_android/web_contents_delegate_android.gypi @@ -37,6 +37,7 @@ 'type': 'none', 'dependencies': [ '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/content/content.gyp:content_java', ], 'variables': { 'package_name': 'web_contents_delegate_android', diff --git a/chrome/browser/component/web_contents_delegate_android/web_contents_delegate_android.h b/chrome/browser/component/web_contents_delegate_android/web_contents_delegate_android.h index 069aef7..52e0e25 100644 --- a/chrome/browser/component/web_contents_delegate_android/web_contents_delegate_android.h +++ b/chrome/browser/component/web_contents_delegate_android/web_contents_delegate_android.h @@ -90,6 +90,9 @@ class WebContentsDelegateAndroid : public content::WebContentsDelegate { const content::NativeWebKeyboardEvent& event) OVERRIDE; virtual bool TakeFocus(content::WebContents* source, bool reverse) OVERRIDE; + virtual void ShowRepostFormWarningDialog( + content::WebContents* source) OVERRIDE; + protected: base::android::ScopedJavaLocalRef<jobject> GetJavaDelegate(JNIEnv* env) const; diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc index e86d70d..9a88630 100644 --- a/content/browser/android/content_view_core_impl.cc +++ b/content/browser/android/content_view_core_impl.cc @@ -870,10 +870,18 @@ void ContentViewCoreImpl::StopLoading(JNIEnv* env, jobject obj) { void ContentViewCoreImpl::Reload(JNIEnv* env, jobject obj) { // Set check_for_repost parameter to false as we have no repost confirmation // dialog ("confirm form resubmission" screen will still appear, however). - web_contents_->GetController().Reload(false); + web_contents_->GetController().Reload(true); tab_crashed_ = false; } +void ContentViewCoreImpl::CancelPendingReload(JNIEnv* env, jobject obj) { + web_contents_->GetController().CancelPendingReload(); +} + +void ContentViewCoreImpl::ContinuePendingReload(JNIEnv* env, jobject obj) { + web_contents_->GetController().ContinuePendingReload(); +} + void ContentViewCoreImpl::ClearHistory(JNIEnv* env, jobject obj) { web_contents_->GetController().PruneAllButActive(); } diff --git a/content/browser/android/content_view_core_impl.h b/content/browser/android/content_view_core_impl.h index e2066a8..d5890f2 100644 --- a/content/browser/android/content_view_core_impl.h +++ b/content/browser/android/content_view_core_impl.h @@ -147,6 +147,8 @@ class ContentViewCoreImpl : public ContentViewCore, void GoToOffset(JNIEnv* env, jobject obj, jint offset); void StopLoading(JNIEnv* env, jobject obj); void Reload(JNIEnv* env, jobject obj); + void CancelPendingReload(JNIEnv* env, jobject obj); + void ContinuePendingReload(JNIEnv* env, jobject obj); jboolean NeedsReload(JNIEnv* env, jobject obj); void ClearHistory(JNIEnv* env, jobject obj); jint EvaluateJavaScript(JNIEnv* env, jobject obj, jstring script); diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java index 1aadecd..a2c03b3 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java +++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java @@ -846,6 +846,20 @@ public class ContentViewCore implements MotionEventDelegate { } /** + * Cancel the pending reload. + */ + public void cancelPendingReload() { + if (mNativeContentViewCore != 0) nativeCancelPendingReload(mNativeContentViewCore); + } + + /** + * Continue the pending reload. + */ + public void continuePendingReload() { + if (mNativeContentViewCore != 0) nativeContinuePendingReload(mNativeContentViewCore); + } + + /** * Clears the ContentViewCore's page history in both the backwards and * forwards directions. */ @@ -2241,6 +2255,10 @@ public class ContentViewCore implements MotionEventDelegate { private native void nativeReload(int nativeContentViewCoreImpl); + private native void nativeCancelPendingReload(int nativeContentViewCoreImpl); + + private native void nativeContinuePendingReload(int nativeContentViewCoreImpl); + private native void nativeSelectPopupMenuItems(int nativeContentViewCoreImpl, int[] indices); private native void nativeScrollFocusedEditableNodeIntoView(int nativeContentViewCoreImpl); |