summaryrefslogtreecommitdiffstats
path: root/android_webview
diff options
context:
space:
mode:
authortedchoc@chromium.org <tedchoc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-19 23:54:22 +0000
committertedchoc@chromium.org <tedchoc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-19 23:54:22 +0000
commit1a915766adf9c390e7d02be5257aee35c2082f01 (patch)
tree514373e8b989f49974a6c4a5087279d35d9ea197 /android_webview
parent3ad1722d24ea0fb3e81e159e0ed0952b098cd003 (diff)
downloadchromium_src-1a915766adf9c390e7d02be5257aee35c2082f01.zip
chromium_src-1a915766adf9c390e7d02be5257aee35c2082f01.tar.gz
chromium_src-1a915766adf9c390e7d02be5257aee35c2082f01.tar.bz2
Add WebView.saveWebArchive support to android_webview.
BUG=146004 Review URL: https://chromiumcodereview.appspot.com/10946013 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@157658 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'android_webview')
-rw-r--r--android_webview/java/src/org/chromium/android_webview/AwContents.java108
-rw-r--r--android_webview/javatests/src/org/chromium/android_webview/test/ArchiveTest.java138
-rw-r--r--android_webview/native/aw_contents.cc27
-rw-r--r--android_webview/native/aw_contents.h1
4 files changed, 269 insertions, 5 deletions
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 0481380..ba8b4f1 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -4,15 +4,25 @@
package org.chromium.android_webview;
-import android.view.ViewGroup;
+import android.os.AsyncTask;
import android.os.Message;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.ViewGroup;
+import android.webkit.ValueCallback;
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
+import org.chromium.base.ThreadUtils;
import org.chromium.content.browser.ContentViewCore;
+import org.chromium.content.browser.NavigationHistory;
import org.chromium.content.common.CleanupReference;
import org.chromium.ui.gfx.NativeWindow;
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+
/**
* Exposes the native AwContents class, and together these classes wrap the ContentViewCore
* and Browser components that are required to implement Android WebView API. This is the
@@ -23,6 +33,9 @@ import org.chromium.ui.gfx.NativeWindow;
*/
@JNINamespace("android_webview")
public class AwContents {
+ private static final String TAG = AwContents.class.getSimpleName();
+
+ private static final String WEB_ARCHIVE_EXTENSION = ".mht";
private int mNativeAwContents;
private ContentViewCore mContentViewCore;
@@ -102,7 +115,37 @@ public class AwContents {
//--------------------------------------------------------------------------------------------
public void documentHasImages(Message message) {
- nativeDocumentHasImages(mNativeAwContents, message);
+ nativeDocumentHasImages(mNativeAwContents, message);
+ }
+
+ public void saveWebArchive(
+ final String basename, boolean autoname, final ValueCallback<String> callback) {
+ if (!autoname) {
+ saveWebArchiveInternal(basename, callback);
+ return;
+ }
+ // If auto-generating the file name, handle the name generation on a background thread
+ // as it will require I/O access for checking whether previous files existed.
+ new AsyncTask<Void, Void, String>() {
+ @Override
+ protected String doInBackground(Void... params) {
+ return generateArchiveAutoNamePath(getOriginalUrl(), basename);
+ }
+
+ @Override
+ protected void onPostExecute(String result) {
+ saveWebArchiveInternal(result, callback);
+ }
+ }.execute();
+ }
+
+ public String getOriginalUrl() {
+ NavigationHistory history = mContentViewCore.getNavigationHistory();
+ int currentIndex = history.getCurrentEntryIndex();
+ if (currentIndex >= 0 && currentIndex < history.getEntryCount()) {
+ return history.getEntryAtIndex(currentIndex).getOriginalUrl();
+ }
+ return null;
}
//--------------------------------------------------------------------------------------------
@@ -115,11 +158,70 @@ public class AwContents {
message.sendToTarget();
}
+ /** Callback for generateMHTML. */
+ @CalledByNative
+ private static void generateMHTMLCallback(
+ String path, long size, ValueCallback<String> callback) {
+ if (callback == null) return;
+ callback.onReceiveValue(size < 0 ? null : path);
+ }
+
@CalledByNative
private void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm) {
mContentsClient.onReceivedHttpAuthRequest(handler, host, realm);
}
+ // -------------------------------------------------------------------------------------------
+ // Helper methods
+ // -------------------------------------------------------------------------------------------
+
+ private void saveWebArchiveInternal(String path, final ValueCallback<String> callback) {
+ if (path == null) {
+ ThreadUtils.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ callback.onReceiveValue(null);
+ }
+ });
+ } else {
+ nativeGenerateMHTML(mNativeAwContents, path, callback);
+ }
+ }
+
+ /**
+ * Try to generate a pathname for saving an MHTML archive. This roughly follows WebView's
+ * autoname logic.
+ */
+ private static String generateArchiveAutoNamePath(String originalUrl, String baseName) {
+ String name = null;
+ if (originalUrl != null && !originalUrl.isEmpty()) {
+ try {
+ String path = new URL(originalUrl).getPath();
+ int lastSlash = path.lastIndexOf('/');
+ if (lastSlash > 0) {
+ name = path.substring(lastSlash + 1);
+ } else {
+ name = path;
+ }
+ } catch (MalformedURLException e) {
+ // If it fails parsing the URL, we'll just rely on the default name below.
+ }
+ }
+
+ if (TextUtils.isEmpty(name)) name = "index";
+
+ String testName = baseName + name + WEB_ARCHIVE_EXTENSION;
+ if (!new File(testName).exists()) return testName;
+
+ for (int i = 1; i < 100; i++) {
+ testName = baseName + name + "-" + i + WEB_ARCHIVE_EXTENSION;
+ if (!new File(testName).exists()) return testName;
+ }
+
+ Log.e(TAG, "Unable to auto generate archive name for path: " + baseName);
+ return null;
+ }
+
//--------------------------------------------------------------------------------------------
// Native methods
//--------------------------------------------------------------------------------------------
@@ -131,6 +233,8 @@ public class AwContents {
private native int nativeGetWebContents(int nativeAwContents);
private native void nativeDocumentHasImages(int nativeAwContents, Message message);
+ private native void nativeGenerateMHTML(
+ int nativeAwContents, String path, ValueCallback<String> callback);
private native void nativeSetIoThreadClient(int nativeAwContents,
AwContentsIoThreadClient ioThreadClient);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ArchiveTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ArchiveTest.java
new file mode 100644
index 0000000..b487d7c
--- /dev/null
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ArchiveTest.java
@@ -0,0 +1,138 @@
+// 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.test.suitebuilder.annotation.SmallTest;
+import android.webkit.ValueCallback;
+
+import org.chromium.android_webview.AwContents;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.Feature;
+
+import java.io.File;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class ArchiveTest extends AndroidWebViewTestBase {
+
+ private static final long TEST_TIMEOUT = 20000L;
+
+ private static final String TEST_PAGE =
+ "data:text/html;utf-8,<html><head></head><body>test</body></html>";
+
+ private TestAwContentsClient mContentsClient = new TestAwContentsClient();
+ private AwTestContainerView mTestContainerView;
+
+ @Override
+ protected void setUp() throws Exception {
+ mTestContainerView = createAwTestContainerViewOnMainSync(mContentsClient);
+ }
+
+ private void doArchiveTest(final AwContents contents, final String path,
+ final boolean autoName, String expectedPath) throws InterruptedException {
+ if (expectedPath != null) {
+ File file = new File(expectedPath);
+ file.delete();
+ }
+
+ // Set up a handler to handle the completion callback
+ final Semaphore s = new Semaphore(0);
+ final AtomicReference<String> msgPath = new AtomicReference<String>();
+ final ValueCallback<String> callback = new ValueCallback<String>() {
+ @Override
+ public void onReceiveValue(String path) {
+ msgPath.set(path);
+ s.release();
+ }
+ };
+
+ // Generate MHTML and wait for completion
+ ThreadUtils.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ contents.saveWebArchive(path, autoName, callback);
+ }
+ });
+ assertTrue(s.tryAcquire(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+
+ assertEquals(expectedPath, msgPath.get());
+ if (expectedPath != null) {
+ File file = new File(expectedPath);
+ assertTrue(file.exists());
+ assertTrue(file.length() > 0);
+ } else {
+ // A path was provided, but the expected path was null. This means the save should have
+ // failed, and so there shouldn't be a file path path.
+ if (path != null) {
+ assertFalse(new File(path).exists());
+ }
+ }
+ }
+
+ @SmallTest
+ @Feature({"Android-WebView"})
+ public void testExplicitGoodPath() throws Throwable {
+ final String path = new File(getActivity().getFilesDir(), "test.mht").getAbsolutePath();
+ File file = new File(path);
+ file.delete();
+ assertFalse(file.exists());
+
+ loadUrlSync(mTestContainerView.getContentViewCore(),
+ mContentsClient.getOnPageFinishedHelper(), TEST_PAGE);
+
+ doArchiveTest(mTestContainerView.getAwContents(), path, false, path);
+ }
+
+ @SmallTest
+ @Feature({"Android-WebView"})
+ public void testAutoGoodPath() throws Throwable {
+ final String path = getActivity().getFilesDir().getAbsolutePath() + "/";
+
+ loadUrlSync(mTestContainerView.getContentViewCore(),
+ mContentsClient.getOnPageFinishedHelper(), TEST_PAGE);
+
+ // Create the first archive
+ {
+ String expectedPath = path + "index.mht";
+ doArchiveTest(mTestContainerView.getAwContents(), path, true, expectedPath);
+ }
+
+ // Create a second archive, making sure that the second archive's name is auto incremented.
+ {
+ String expectedPath = path + "index-1.mht";
+ doArchiveTest(mTestContainerView.getAwContents(), path, true, expectedPath);
+ }
+ }
+
+ @SmallTest
+ @Feature({"Android-WebView"})
+ public void testExplicitBadPath() throws Throwable {
+ final String path = new File("/foo/bar/baz.mht").getAbsolutePath();
+ File file = new File(path);
+ file.delete();
+ assertFalse(file.exists());
+
+ loadUrlSync(mTestContainerView.getContentViewCore(),
+ mContentsClient.getOnPageFinishedHelper(), TEST_PAGE);
+
+ doArchiveTest(mTestContainerView.getAwContents(), path, false, null);
+ }
+
+ @SmallTest
+ @Feature({"Android-WebView"})
+ public void testAutoBadPath() throws Throwable {
+ final String path = new File("/foo/bar/").getAbsolutePath();
+ File file = new File(path);
+ file.delete();
+ assertFalse(file.exists());
+
+ loadUrlSync(mTestContainerView.getContentViewCore(),
+ mContentsClient.getOnPageFinishedHelper(), TEST_PAGE);
+
+ doArchiveTest(mTestContainerView.getAwContents(), path, true, null);
+ }
+
+}
diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc
index be5193c..4b21b58 100644
--- a/android_webview/native/aw_contents.cc
+++ b/android_webview/native/aw_contents.cc
@@ -102,10 +102,31 @@ void DocumentHasImagesCallback(ScopedJavaGlobalRef<jobject>* message,
} // namespace
void AwContents::DocumentHasImages(JNIEnv* env, jobject obj, jobject message) {
+ ScopedJavaGlobalRef<jobject>* j_message = new ScopedJavaGlobalRef<jobject>();
+ j_message->Reset(env, message);
render_view_host_ext_->DocumentHasImages(
- base::Bind(&DocumentHasImagesCallback,
- base::Owned(new ScopedJavaGlobalRef<jobject>(
- ScopedJavaLocalRef<jobject>(env, message)))));
+ base::Bind(&DocumentHasImagesCallback, base::Owned(j_message)));
+}
+
+namespace {
+void GenerateMHTMLCallback(ScopedJavaGlobalRef<jobject>* callback,
+ const FilePath& path, int64 size) {
+ JNIEnv* env = AttachCurrentThread();
+ // Android files are UTF8, so the path conversion below is safe.
+ Java_AwContents_generateMHTMLCallback(
+ env,
+ base::android::ConvertUTF8ToJavaString(env, path.AsUTF8Unsafe()).obj(),
+ size, callback->obj());
+}
+} // namespace
+
+void AwContents::GenerateMHTML(JNIEnv* env, jobject obj,
+ jstring jpath, jobject callback) {
+ ScopedJavaGlobalRef<jobject>* j_callback = new ScopedJavaGlobalRef<jobject>();
+ j_callback->Reset(env, callback);
+ contents_container_->GetWebContents()->GenerateMHTML(
+ FilePath(base::android::ConvertJavaStringToUTF8(env, jpath)),
+ base::Bind(&GenerateMHTMLCallback, base::Owned(j_callback)));
}
void AwContents::onReceivedHttpAuthRequest(
diff --git a/android_webview/native/aw_contents.h b/android_webview/native/aw_contents.h
index c756e25..e441bd1 100644
--- a/android_webview/native/aw_contents.h
+++ b/android_webview/native/aw_contents.h
@@ -49,6 +49,7 @@ class AwContents {
jint GetWebContents(JNIEnv* env, jobject obj);
void Destroy(JNIEnv* env, jobject obj);
void DocumentHasImages(JNIEnv* env, jobject obj, jobject message);
+ void GenerateMHTML(JNIEnv* env, jobject obj, jstring jpath, jobject callback);
void SetIoThreadClient(JNIEnv* env, jobject obj, jobject client);
private: