Commit 0dc3d913 authored by Yaron Friedman's avatar Yaron Friedman Committed by Commit Bot

Reduce unnecessary OOM from screen captures.

WebContents.getContentBitmapAsync was unnecessarily passing the bitmap
to java before compressing it and saving to disk.

Instead, changes the API so that the caller provides a directory in which to
grab screenshots, and then WebContents seemlessly creates a new temp
file, compresses to jpeg and saves to that directory, requiring 0 java heap
allocation. As part of this async screen capture, also get a public Uri
for exporting via ContentProvider because all clients do this anyway.
BUG=651348

Change-Id: I6be01348f225adfa370c6a5e08ebd57f15e8fe93
Reviewed-on: https://chromium-review.googlesource.com/935670Reviewed-by: default avatarBernhard Bauer <bauerb@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Commit-Queue: Yaron Friedman <yfriedman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#550240}
parent 18f96488
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "base/android/callback_android.h" #include "base/android/callback_android.h"
#include "base/android/jni_array.h" #include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h" #include "base/android/scoped_java_ref.h"
#include "jni/Callback_jni.h" #include "jni/Callback_jni.h"
...@@ -13,25 +14,29 @@ namespace android { ...@@ -13,25 +14,29 @@ namespace android {
void RunCallbackAndroid(const JavaRef<jobject>& callback, void RunCallbackAndroid(const JavaRef<jobject>& callback,
const JavaRef<jobject>& arg) { const JavaRef<jobject>& arg) {
Java_Helper_onObjectResultFromNative(base::android::AttachCurrentThread(), Java_Helper_onObjectResultFromNative(AttachCurrentThread(), callback, arg);
callback, arg);
} }
void RunCallbackAndroid(const JavaRef<jobject>& callback, bool arg) { void RunCallbackAndroid(const JavaRef<jobject>& callback, bool arg) {
Java_Helper_onBooleanResultFromNative(base::android::AttachCurrentThread(), Java_Helper_onBooleanResultFromNative(AttachCurrentThread(), callback,
callback, static_cast<jboolean>(arg)); static_cast<jboolean>(arg));
} }
void RunCallbackAndroid(const JavaRef<jobject>& callback, int arg) { void RunCallbackAndroid(const JavaRef<jobject>& callback, int arg) {
Java_Helper_onIntResultFromNative(base::android::AttachCurrentThread(), Java_Helper_onIntResultFromNative(AttachCurrentThread(), callback, arg);
callback, arg); }
void RunStringCallbackAndroid(const JavaRef<jobject>& callback,
const std::string& arg) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> java_string = ConvertUTF8ToJavaString(env, arg);
Java_Helper_onObjectResultFromNative(env, callback, java_string);
} }
void RunCallbackAndroid(const JavaRef<jobject>& callback, void RunCallbackAndroid(const JavaRef<jobject>& callback,
const std::vector<uint8_t>& arg) { const std::vector<uint8_t>& arg) {
JNIEnv* env = base::android::AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
base::android::ScopedJavaLocalRef<jbyteArray> j_bytes = ScopedJavaLocalRef<jbyteArray> j_bytes = ToJavaByteArray(env, arg);
base::android::ToJavaByteArray(env, arg);
Java_Helper_onObjectResultFromNative(env, callback, j_bytes); Java_Helper_onObjectResultFromNative(env, callback, j_bytes);
} }
......
...@@ -24,6 +24,9 @@ void BASE_EXPORT RunCallbackAndroid(const JavaRef<jobject>& callback, ...@@ -24,6 +24,9 @@ void BASE_EXPORT RunCallbackAndroid(const JavaRef<jobject>& callback,
void BASE_EXPORT RunCallbackAndroid(const JavaRef<jobject>& callback, int arg); void BASE_EXPORT RunCallbackAndroid(const JavaRef<jobject>& callback, int arg);
void BASE_EXPORT RunStringCallbackAndroid(const JavaRef<jobject>& callback,
const std::string& arg);
void BASE_EXPORT RunCallbackAndroid(const JavaRef<jobject>& callback, void BASE_EXPORT RunCallbackAndroid(const JavaRef<jobject>& callback,
const std::vector<uint8_t>& arg); const std::vector<uint8_t>& arg);
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
package org.chromium.chrome.browser.customtabs; package org.chromium.chrome.browser.customtabs;
import android.app.Application; import android.app.Application;
import android.graphics.Bitmap;
import android.graphics.Rect; import android.graphics.Rect;
import android.net.Uri;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.customtabs.CustomTabsSessionToken; import android.support.customtabs.CustomTabsSessionToken;
import android.text.TextUtils; import android.text.TextUtils;
...@@ -14,6 +14,7 @@ import android.text.TextUtils; ...@@ -14,6 +14,7 @@ import android.text.TextUtils;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.prerender.ExternalPrerenderHandler; import org.chromium.chrome.browser.prerender.ExternalPrerenderHandler;
import org.chromium.chrome.browser.share.ShareHelper;
import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabObserver; import org.chromium.chrome.browser.tab.TabObserver;
...@@ -103,7 +104,7 @@ class CustomTabObserver extends EmptyTabObserver { ...@@ -103,7 +104,7 @@ class CustomTabObserver extends EmptyTabObserver {
} else if (mCurrentState == STATE_WAITING_LOAD_FINISH) { } else if (mCurrentState == STATE_WAITING_LOAD_FINISH) {
if (mCustomTabsConnection != null) { if (mCustomTabsConnection != null) {
mCustomTabsConnection.sendNavigationInfo( mCustomTabsConnection.sendNavigationInfo(
mSession, tab.getUrl(), tab.getTitle(), null); mSession, tab.getUrl(), tab.getTitle(), (Uri) null);
} }
mPageLoadStartedTimestamp = SystemClock.elapsedRealtime(); mPageLoadStartedTimestamp = SystemClock.elapsedRealtime();
} }
...@@ -198,11 +199,11 @@ class CustomTabObserver extends EmptyTabObserver { ...@@ -198,11 +199,11 @@ class CustomTabObserver extends EmptyTabObserver {
if (!mCustomTabsConnection.shouldSendNavigationInfoForSession(mSession)) return; if (!mCustomTabsConnection.shouldSendNavigationInfoForSession(mSession)) return;
if (tab.getWebContents() == null) return; if (tab.getWebContents() == null) return;
tab.getWebContents().getContentBitmapAsync( ShareHelper.captureScreenshotForContents(tab.getWebContents(), mContentBitmapWidth,
mContentBitmapWidth, mContentBitmapHeight, (Bitmap bitmap) -> { mContentBitmapHeight, (Uri snapshotPath) -> {
if (TextUtils.isEmpty(tab.getTitle()) && bitmap == null) return; if (TextUtils.isEmpty(tab.getTitle()) && snapshotPath == null) return;
mCustomTabsConnection.sendNavigationInfo( mCustomTabsConnection.sendNavigationInfo(
mSession, tab.getUrl(), tab.getTitle(), bitmap); mSession, tab.getUrl(), tab.getTitle(), snapshotPath);
}); });
} }
} }
...@@ -1012,10 +1012,15 @@ public class CustomTabsConnection { ...@@ -1012,10 +1012,15 @@ public class CustomTabsConnection {
* @param session The session to use for getting client callback. * @param session The session to use for getting client callback.
* @param url The current url for the tab. * @param url The current url for the tab.
* @param title The current title for the tab. * @param title The current title for the tab.
* @param screenshot A screenshot of the tab contents. * @param snapshotPath Uri location for screenshot of the tab contents which is publicly
* available for sharing.
*/ */
public void sendNavigationInfo( public void sendNavigationInfo(
CustomTabsSessionToken session, String url, String title, Bitmap screenshot) { } CustomTabsSessionToken session, String url, String title, Uri snapshotPath) {}
// TODO(yfriedman): Remove when internal code is deleted.
public void sendNavigationInfo(
CustomTabsSessionToken session, String url, String title, Bitmap snapshotPath) {}
/** /**
* Called when the bottom bar for the custom tab has been hidden or shown completely by user * Called when the bottom bar for the custom tab has been hidden or shown completely by user
......
...@@ -45,6 +45,7 @@ import org.chromium.base.StreamUtil; ...@@ -45,6 +45,7 @@ import org.chromium.base.StreamUtil;
import org.chromium.base.VisibleForTesting; import org.chromium.base.VisibleForTesting;
import org.chromium.base.metrics.CachedMetrics; import org.chromium.base.metrics.CachedMetrics;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.UiUtils; import org.chromium.ui.UiUtils;
import java.io.File; import java.io.File;
...@@ -366,54 +367,60 @@ public class ShareHelper { ...@@ -366,54 +367,60 @@ public class ShareHelper {
}.execute(); }.execute();
} }
/** private static class ExternallyVisibleUriCallback implements Callback<String> {
* Persists the screenshot file and notifies the file provider that the file is ready to be private Callback<Uri> mComposedCallback;
* accessed by the client. ExternallyVisibleUriCallback(Callback<Uri> cb) {
* mComposedCallback = cb;
* The bitmap is compressed to JPEG before being written to the file.
*
* @param screenshot The screenshot bitmap to be written to file.
* @param callback The callback that will be called once the bitmap is saved.
*/
public static void saveScreenshotToDisk(final Bitmap screenshot, final Context context,
final Callback<Uri> callback) {
if (screenshot == null) {
callback.onResult(null);
return;
} }
new AsyncTask<Void, Void, Uri>() { @Override
@Override public void onResult(final String path) {
protected Uri doInBackground(Void... params) { if (TextUtils.isEmpty(path)) {
FileOutputStream fOut = null; mComposedCallback.onResult(null);
try { return;
File path = new File(UiUtils.getDirectoryForImageCapture(context) + "/" }
+ SHARE_IMAGES_DIRECTORY_NAME);
if (path.exists() || path.mkdir()) { new AsyncTask<Void, Void, Uri>() {
String fileName = String.valueOf(System.currentTimeMillis()); @Override
File saveFile = File.createTempFile(fileName, JPEG_EXTENSION, path); protected Uri doInBackground(Void... v) {
fOut = new FileOutputStream(saveFile); return ApiCompatibilityUtils.getUriForImageCaptureFile(new File(path));
screenshot.compress(Bitmap.CompressFormat.JPEG, 85, fOut);
return ApiCompatibilityUtils.getUriForImageCaptureFile(saveFile);
}
} catch (IOException ie) {
Log.w(TAG, "Ignoring IOException when saving screenshot.", ie);
} finally {
StreamUtil.closeQuietly(fOut);
} }
return null; @Override
protected void onPostExecute(Uri uri) {
mComposedCallback.onResult(uri);
}
} }
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
@Override // TODO(yfriedman): Remove after internal tree is updated.
protected void onPostExecute(Uri fileUri) { public static void saveScreenshotToDisk(
fileUri = ApplicationStatus.getStateForApplication() Bitmap screenshot, Context context, Callback<Uri> callback) {}
!= ApplicationState.HAS_DESTROYED_ACTIVITIES
? fileUri /**
: null; * Captures a screenshot for the provided web contents, persists it and notifies the file
callback.onResult(fileUri); * provider that the file is ready to be accessed by the client.
} *
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); * The screenshot is compressed to JPEG before being written to the file.
*
* @param contents The WebContents instance for which to capture a screenshot.
* @param width The desired width of the resulting screenshot, or 0 for "auto."
* @param height The desired height of the resulting screenshot, or 0 for "auto."
* @param callback The callback that will be called once the screenshot is saved.
*/
public static void captureScreenshotForContents(
WebContents contents, int width, int height, Callback<Uri> callback) {
try {
String path = UiUtils.getDirectoryForImageCapture(ContextUtils.getApplicationContext())
+ File.separator + SHARE_IMAGES_DIRECTORY_NAME;
contents.getContentBitmapAsync(
width, height, path, new ExternallyVisibleUriCallback(callback));
} catch (IOException e) {
Log.e(TAG, "Error getting content bitmap: ", e);
callback.onResult(null);
}
} }
/** /**
......
...@@ -18,7 +18,6 @@ import org.chromium.chrome.browser.printing.PrintShareActivity; ...@@ -18,7 +18,6 @@ import org.chromium.chrome.browser.printing.PrintShareActivity;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.util.ChromeFileProvider; import org.chromium.chrome.browser.util.ChromeFileProvider;
import org.chromium.components.ui_metrics.CanonicalURLResult; import org.chromium.components.ui_metrics.CanonicalURLResult;
import org.chromium.content_public.browser.ContentBitmapCallback;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.net.GURLUtils; import org.chromium.net.GURLUtils;
...@@ -196,15 +195,14 @@ public class ShareMenuActionHandler { ...@@ -196,15 +195,14 @@ public class ShareMenuActionHandler {
if (blockingUri == null) return; if (blockingUri == null) return;
// Start screenshot capture and notify the provider when it is ready. // Start screenshot capture and notify the provider when it is ready.
ContentBitmapCallback callback = Callback<Uri> callback = (saveFile) -> {
(bitmap) -> ShareHelper.saveScreenshotToDisk(bitmap, mainActivity, result -> {
// Unblock the file once it is saved to disk. // Unblock the file once it is saved to disk.
ChromeFileProvider.notifyFileReady(blockingUri, result); ChromeFileProvider.notifyFileReady(blockingUri, saveFile);
}); };
if (sScreenshotCaptureSkippedForTesting) { if (sScreenshotCaptureSkippedForTesting) {
callback.onFinishGetBitmap(null); callback.onResult(null);
} else { } else {
webContents.getContentBitmapAsync(0, 0, callback); ShareHelper.captureScreenshotForContents(webContents, 0, 0, callback);
} }
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/android/callback_android.h"
#include "base/android/jni_android.h" #include "base/android/jni_android.h"
#include "base/android/jni_array.h" #include "base/android/jni_array.h"
#include "base/android/jni_string.h" #include "base/android/jni_string.h"
...@@ -16,6 +17,7 @@ ...@@ -16,6 +17,7 @@
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/task_scheduler/post_task.h"
#include "content/browser/accessibility/browser_accessibility_android.h" #include "content/browser/accessibility/browser_accessibility_android.h"
#include "content/browser/accessibility/browser_accessibility_manager_android.h" #include "content/browser/accessibility/browser_accessibility_manager_android.h"
#include "content/browser/android/content_view_core.h" #include "content/browser/android/content_view_core.h"
...@@ -44,6 +46,7 @@ ...@@ -44,6 +46,7 @@
#include "ui/android/overscroll_refresh_handler.h" #include "ui/android/overscroll_refresh_handler.h"
#include "ui/android/window_android.h" #include "ui/android/window_android.h"
#include "ui/gfx/android/java_bitmap.h" #include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect.h"
...@@ -131,6 +134,34 @@ void AXTreeSnapshotCallback(const ScopedJavaGlobalRef<jobject>& callback, ...@@ -131,6 +134,34 @@ void AXTreeSnapshotCallback(const ScopedJavaGlobalRef<jobject>& callback,
Java_WebContentsImpl_onAccessibilitySnapshot(env, j_root, callback); Java_WebContentsImpl_onAccessibilitySnapshot(env, j_root, callback);
} }
std::string CompressAndSaveBitmap(const std::string& dir,
const SkBitmap& bitmap) {
base::AssertBlockingAllowed();
std::vector<unsigned char> data;
if (!gfx::JPEGCodec::Encode(bitmap, 85, &data))
return std::string();
base::FilePath screenshot_dir(dir);
if (!base::DirectoryExists(screenshot_dir)) {
base::CreateDirectory(screenshot_dir);
}
base::FilePath screenshot_path;
base::ScopedFILE out_file(
base::CreateAndOpenTemporaryFileInDir(screenshot_dir, &screenshot_path));
unsigned int bytes_written =
fwrite(reinterpret_cast<const char*>(data.data()), 1, data.size(),
out_file.get());
// If there were errors, don't leave a partial file around.
if (bytes_written != data.size()) {
base::DeleteFile(screenshot_path, false);
return std::string();
}
return screenshot_path.value();
}
} // namespace } // namespace
// static // static
...@@ -589,12 +620,14 @@ void WebContentsAndroid::GetContentBitmap( ...@@ -589,12 +620,14 @@ void WebContentsAndroid::GetContentBitmap(
const JavaParamRef<jobject>& obj, const JavaParamRef<jobject>& obj,
jint width, jint width,
jint height, jint height,
const JavaParamRef<jstring>& jpath,
const JavaParamRef<jobject>& jcallback) { const JavaParamRef<jobject>& jcallback) {
RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid();
base::OnceCallback<void(const SkBitmap&)> result_callback = base::BindOnce( base::OnceCallback<void(const SkBitmap&)> result_callback = base::BindOnce(
&WebContentsAndroid::OnFinishGetContentBitmap, weak_factory_.GetWeakPtr(), &WebContentsAndroid::OnFinishGetContentBitmap, weak_factory_.GetWeakPtr(),
ScopedJavaGlobalRef<jobject>(env, obj), ScopedJavaGlobalRef<jobject>(env, obj),
ScopedJavaGlobalRef<jobject>(env, jcallback)); ScopedJavaGlobalRef<jobject>(env, jcallback),
ConvertJavaStringToUTF8(env, jpath));
RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid();
if (!view) { if (!view) {
std::move(result_callback).Run(SkBitmap()); std::move(result_callback).Run(SkBitmap());
return; return;
...@@ -704,13 +737,22 @@ ScopedJavaLocalRef<jobject> WebContentsAndroid::GetOrCreateEventForwarder( ...@@ -704,13 +737,22 @@ ScopedJavaLocalRef<jobject> WebContentsAndroid::GetOrCreateEventForwarder(
void WebContentsAndroid::OnFinishGetContentBitmap( void WebContentsAndroid::OnFinishGetContentBitmap(
const JavaRef<jobject>& obj, const JavaRef<jobject>& obj,
const JavaRef<jobject>& callback, const JavaRef<jobject>& callback,
const std::string& path,
const SkBitmap& bitmap) { const SkBitmap& bitmap) {
JNIEnv* env = base::android::AttachCurrentThread(); JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jobject> java_bitmap; ScopedJavaLocalRef<jobject> java_bitmap;
if (!bitmap.drawsNothing()) if (!bitmap.drawsNothing()) {
java_bitmap = gfx::ConvertToJavaBitmap(&bitmap); auto task_runner = base::CreateSequencedTaskRunnerWithTraits(
Java_WebContentsImpl_onGetContentBitmapFinished(env, obj, callback, {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
java_bitmap); base::PostTaskAndReplyWithResult(
task_runner.get(), FROM_HERE,
base::BindOnce(&CompressAndSaveBitmap, path, bitmap),
base::BindOnce(&base::android::RunStringCallbackAndroid,
ScopedJavaGlobalRef<jobject>(env, callback.obj())));
return;
}
// If readback failed, call empty callback
base::android::RunStringCallbackAndroid(callback, std::string());
} }
void WebContentsAndroid::OnFinishDownloadImage( void WebContentsAndroid::OnFinishDownloadImage(
......
...@@ -178,6 +178,7 @@ class CONTENT_EXPORT WebContentsAndroid ...@@ -178,6 +178,7 @@ class CONTENT_EXPORT WebContentsAndroid
const base::android::JavaParamRef<jobject>& obj, const base::android::JavaParamRef<jobject>& obj,
jint width, jint width,
jint height, jint height,
const base::android::JavaParamRef<jstring>& jpath,
const base::android::JavaParamRef<jobject>& jcallback); const base::android::JavaParamRef<jobject>& jcallback);
void ReloadLoFiImages(JNIEnv* env, void ReloadLoFiImages(JNIEnv* env,
...@@ -206,6 +207,7 @@ class CONTENT_EXPORT WebContentsAndroid ...@@ -206,6 +207,7 @@ class CONTENT_EXPORT WebContentsAndroid
bool IsPictureInPictureAllowedForFullscreenVideo( bool IsPictureInPictureAllowedForFullscreenVideo(
JNIEnv* env, JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj); const base::android::JavaParamRef<jobject>& obj);
base::android::ScopedJavaLocalRef<jobject> GetFullscreenVideoSize( base::android::ScopedJavaLocalRef<jobject> GetFullscreenVideoSize(
JNIEnv* env, JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj); const base::android::JavaParamRef<jobject>& obj);
...@@ -228,6 +230,7 @@ class CONTENT_EXPORT WebContentsAndroid ...@@ -228,6 +230,7 @@ class CONTENT_EXPORT WebContentsAndroid
void OnFinishGetContentBitmap(const base::android::JavaRef<jobject>& obj, void OnFinishGetContentBitmap(const base::android::JavaRef<jobject>& obj,
const base::android::JavaRef<jobject>& callback, const base::android::JavaRef<jobject>& callback,
const std::string& path,
const SkBitmap& bitmap); const SkBitmap& bitmap);
void OnFinishDownloadImage(const base::android::JavaRef<jobject>& obj, void OnFinishDownloadImage(const base::android::JavaRef<jobject>& obj,
......
...@@ -232,7 +232,6 @@ android_library("content_java") { ...@@ -232,7 +232,6 @@ android_library("content_java") {
"java/src/org/chromium/content_public/browser/ActionModeCallbackHelper.java", "java/src/org/chromium/content_public/browser/ActionModeCallbackHelper.java",
"java/src/org/chromium/content_public/browser/ChildProcessUtils.java", "java/src/org/chromium/content_public/browser/ChildProcessUtils.java",
"java/src/org/chromium/content_public/browser/InputMethodManagerWrapper.java", "java/src/org/chromium/content_public/browser/InputMethodManagerWrapper.java",
"java/src/org/chromium/content_public/browser/ContentBitmapCallback.java",
"java/src/org/chromium/content_public/browser/ContentViewCore.java", "java/src/org/chromium/content_public/browser/ContentViewCore.java",
"java/src/org/chromium/content_public/browser/GestureListenerManager.java", "java/src/org/chromium/content_public/browser/GestureListenerManager.java",
"java/src/org/chromium/content_public/browser/GestureStateListener.java", "java/src/org/chromium/content_public/browser/GestureStateListener.java",
......
...@@ -15,6 +15,7 @@ import android.os.ParcelUuid; ...@@ -15,6 +15,7 @@ import android.os.ParcelUuid;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.chromium.base.Callback;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.base.ThreadUtils; import org.chromium.base.ThreadUtils;
import org.chromium.base.VisibleForTesting; import org.chromium.base.VisibleForTesting;
...@@ -30,7 +31,6 @@ import org.chromium.content.browser.selection.SelectionPopupControllerImpl; ...@@ -30,7 +31,6 @@ import org.chromium.content.browser.selection.SelectionPopupControllerImpl;
import org.chromium.content_public.browser.AccessibilitySnapshotCallback; import org.chromium.content_public.browser.AccessibilitySnapshotCallback;
import org.chromium.content_public.browser.AccessibilitySnapshotNode; import org.chromium.content_public.browser.AccessibilitySnapshotNode;
import org.chromium.content_public.browser.ChildProcessImportance; import org.chromium.content_public.browser.ChildProcessImportance;
import org.chromium.content_public.browser.ContentBitmapCallback;
import org.chromium.content_public.browser.ImageDownloadCallback; import org.chromium.content_public.browser.ImageDownloadCallback;
import org.chromium.content_public.browser.JavaScriptCallback; import org.chromium.content_public.browser.JavaScriptCallback;
import org.chromium.content_public.browser.MessagePort; import org.chromium.content_public.browser.MessagePort;
...@@ -622,13 +622,9 @@ public class WebContentsImpl implements WebContents, RenderFrameHostDelegate { ...@@ -622,13 +622,9 @@ public class WebContentsImpl implements WebContents, RenderFrameHostDelegate {
} }
@Override @Override
public void getContentBitmapAsync(int width, int height, ContentBitmapCallback callback) { public void getContentBitmapAsync(
nativeGetContentBitmap(mNativeWebContentsAndroid, width, height, callback); int width, int height, String path, Callback<String> callback) {
} nativeGetContentBitmap(mNativeWebContentsAndroid, width, height, path, callback);
@CalledByNative
private void onGetContentBitmapFinished(ContentBitmapCallback callback, Bitmap bitmap) {
callback.onFinishGetBitmap(bitmap);
} }
@Override @Override
...@@ -827,8 +823,8 @@ public class WebContentsImpl implements WebContents, RenderFrameHostDelegate { ...@@ -827,8 +823,8 @@ public class WebContentsImpl implements WebContents, RenderFrameHostDelegate {
long nativeWebContentsAndroid, AccessibilitySnapshotCallback callback); long nativeWebContentsAndroid, AccessibilitySnapshotCallback callback);
private native void nativeSetOverscrollRefreshHandler( private native void nativeSetOverscrollRefreshHandler(
long nativeWebContentsAndroid, OverscrollRefreshHandler nativeOverscrollRefreshHandler); long nativeWebContentsAndroid, OverscrollRefreshHandler nativeOverscrollRefreshHandler);
private native void nativeGetContentBitmap( private native void nativeGetContentBitmap(long nativeWebContentsAndroid, int width, int height,
long nativeWebContentsAndroid, int width, int height, ContentBitmapCallback callback); String path, Callback<String> callback);
private native void nativeReloadLoFiImages(long nativeWebContentsAndroid); private native void nativeReloadLoFiImages(long nativeWebContentsAndroid);
private native int nativeDownloadImage(long nativeWebContentsAndroid, private native int nativeDownloadImage(long nativeWebContentsAndroid,
String url, boolean isFavicon, int maxBitmapSize, String url, boolean isFavicon, int maxBitmapSize,
......
// Copyright 2015 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.content_public.browser;
import android.graphics.Bitmap;
/**
* An interface used to get notified of the completion of content bitmap acquisition.
*/
public interface ContentBitmapCallback {
/**
* Called when bitmap version of the content is acquired.
*
* @param bitmap content snapshot in the format of {@link Bitmap}, or null
* if the operation failed.
*/
void onFinishGetBitmap(Bitmap bitmap);
}
...@@ -9,6 +9,7 @@ import android.os.Handler; ...@@ -9,6 +9,7 @@ import android.os.Handler;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.chromium.base.Callback;
import org.chromium.base.VisibleForTesting; import org.chromium.base.VisibleForTesting;
import org.chromium.ui.OverscrollRefreshHandler; import org.chromium.ui.OverscrollRefreshHandler;
import org.chromium.ui.base.EventForwarder; import org.chromium.ui.base.EventForwarder;
...@@ -393,10 +394,11 @@ public interface WebContents extends Parcelable { ...@@ -393,10 +394,11 @@ public interface WebContents extends Parcelable {
* *
* @param width The width of the resulting bitmap, or 0 for "auto." * @param width The width of the resulting bitmap, or 0 for "auto."
* @param height The height of the resulting bitmap, or 0 for "auto." * @param height The height of the resulting bitmap, or 0 for "auto."
* @param path The folder in which to store the screenshot.
* @param callback May be called synchronously, or at a later point, to deliver the bitmap * @param callback May be called synchronously, or at a later point, to deliver the bitmap
* result (or a failure code). * result (or a failure code).
*/ */
void getContentBitmapAsync(int width, int height, ContentBitmapCallback callback); void getContentBitmapAsync(int width, int height, String path, Callback<String> callback);
/** /**
* Reloads all the Lo-Fi images in this WebContents. * Reloads all the Lo-Fi images in this WebContents.
......
...@@ -9,8 +9,8 @@ import android.graphics.Rect; ...@@ -9,8 +9,8 @@ import android.graphics.Rect;
import android.os.Handler; import android.os.Handler;
import android.os.Parcel; import android.os.Parcel;
import org.chromium.base.Callback;
import org.chromium.content_public.browser.AccessibilitySnapshotCallback; import org.chromium.content_public.browser.AccessibilitySnapshotCallback;
import org.chromium.content_public.browser.ContentBitmapCallback;
import org.chromium.content_public.browser.ImageDownloadCallback; import org.chromium.content_public.browser.ImageDownloadCallback;
import org.chromium.content_public.browser.JavaScriptCallback; import org.chromium.content_public.browser.JavaScriptCallback;
import org.chromium.content_public.browser.MessagePort; import org.chromium.content_public.browser.MessagePort;
...@@ -213,7 +213,8 @@ public class MockWebContents implements WebContents { ...@@ -213,7 +213,8 @@ public class MockWebContents implements WebContents {
public void setOverscrollRefreshHandler(OverscrollRefreshHandler handler) {} public void setOverscrollRefreshHandler(OverscrollRefreshHandler handler) {}
@Override @Override
public void getContentBitmapAsync(int width, int height, ContentBitmapCallback callback) {} public void getContentBitmapAsync(
int width, int height, String path, Callback<String> callback) {}
@Override @Override
public void reloadLoFiImages() {} public void reloadLoFiImages() {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment