native/render_handler.cpp (470 lines of code) (raw):
// Copyright (c) 2014 The Chromium Embedded Framework 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 "render_handler.h"
#include "client_handler.h"
#include "jni_util.h"
namespace {
// Create a new java.awt.Rectangle.
jobject NewJNIRect(JNIEnv* env, const CefRect& rect) {
ScopedJNIClass cls(env, "java/awt/Rectangle");
if (!cls)
return nullptr;
ScopedJNIObjectLocal obj(env, NewJNIObject(env, cls));
if (!obj)
return nullptr;
if (SetJNIFieldInt(env, cls, obj, "x", rect.x) &&
SetJNIFieldInt(env, cls, obj, "y", rect.y) &&
SetJNIFieldInt(env, cls, obj, "width", rect.width) &&
SetJNIFieldInt(env, cls, obj, "height", rect.height)) {
return obj.Release();
}
return nullptr;
}
jobject NewJNIDimension(JNIEnv* env, int width, int height) {
ScopedJNIClass cls(env, "java/awt/Dimension");
if (!cls)
return nullptr;
ScopedJNIObjectLocal obj(env, NewJNIObject(env, cls));
if (!obj)
return nullptr;
if (SetJNIFieldInt(env, cls, obj, "width", width) &&
SetJNIFieldInt(env, cls, obj, "height", height)) {
return obj.Release();
}
return nullptr;
}
jobject convertColorType(JNIEnv *env, cef_color_type_t colorType) {
const char *enumConstName = nullptr;
switch (colorType) {
case CEF_COLOR_TYPE_RGBA_8888:
enumConstName = "CEF_COLOR_TYPE_RGBA_8888";
break;
case CEF_COLOR_TYPE_BGRA_8888:
enumConstName = "CEF_COLOR_TYPE_BGRA_8888";
break;
default:
LOG(ERROR) << "Unknown color type " << colorType;
return nullptr;
}
ScopedJNIClass enumCls(env, "org/cef/handler/CefAcceleratedPaintInfo$ColorType");
if (!enumCls) {
LOG(ERROR) << "Failed to find org.cef.handler.CefAcceleratedPaintInfo$ColorType";
return nullptr;
}
jfieldID enumFieldId = env->GetStaticFieldID(enumCls, enumConstName,
"Lorg/cef/handler/CefAcceleratedPaintInfo$ColorType;");
if (!enumFieldId) {
env->ExceptionClear();
LOG(ERROR) << "Failed to find org.cef.handler.CefAcceleratedPaintInfo$ColorType." << enumConstName;
return nullptr;
}
jobject enumVal = env->GetStaticObjectField(enumCls, enumFieldId);
if (!enumVal) {
env->ExceptionClear();
LOG(ERROR) << "Failed to get org.cef.handler.CefAcceleratedPaintInfo$ColorType." << enumConstName;
return nullptr;
}
return enumVal;
}
jobject convertAcceleratedPaintInfo(JNIEnv *env, const CefAcceleratedPaintInfo &info) {
ScopedJNIClass infoCls(env, "org/cef/handler/CefAcceleratedPaintInfo");
if (!infoCls) {
LOG(ERROR) << "Failed to find org.cef.handler.CefAcceleratedPaintInfo";
return nullptr;
}
ScopedJNIObjectLocal jInfo(env, NewJNIObject(env, infoCls));
if (!jInfo) {
LOG(ERROR) << "Failed to create org.cef.handler.CefAcceleratedPaintInfo";
return nullptr;
}
ScopedJNIObjectLocal colorType(env, convertColorType(env, info.format));
if (!colorType) {
return nullptr;
}
jfieldID formatFid = env->GetFieldID(infoCls, "format",
"Lorg/cef/handler/CefAcceleratedPaintInfo$ColorType;");
if (!formatFid) {
env->ExceptionDescribe();
env->ExceptionClear();
LOG(ERROR) << "Failed to get field ID for CefAcceleratedPaintInfo.format";
return nullptr;
}
env->SetObjectField(jInfo, formatFid, colorType.get());
jfieldID handleFid = env->GetFieldID(infoCls, "handle", "J");
if (!handleFid) {
env->ExceptionDescribe();
env->ExceptionClear();
LOG(ERROR) << "Failed to get field ID for CefAcceleratedPaintInfo.handle";
return nullptr;
}
#ifdef OS_MAC
env->SetLongField(jInfo, handleFid, static_cast<jlong>(reinterpret_cast<intptr_t>(info.shared_texture_io_surface)));
#elif OS_WIN
env->SetLongField(jInfo, handleFid, static_cast<jlong>(reinterpret_cast<intptr_t>(info.shared_texture_handle)));
#else
// TODO: implement for Linux (support cef_accelerated_paint_native_pixmap_plane_t planes[kAcceleratedPaintMaxPlanes])
LOG(ERROR) << "Unimplemented CefAcceleratedPaint for Linux";
#endif
ScopedJNIObjectLocal jCodedSize(env, NewJNIDimension(env,
info.extra.coded_size.width,
info.extra.coded_size.height));
if (!jCodedSize) {
LOG(ERROR) << "Failed to create Dimension for CefAcceleratedPaintInfo.codedSize";
return nullptr;
}
jfieldID codedSizeFid = env->GetFieldID(infoCls, "codedSize", "Ljava/awt/Dimension;");
if (!codedSizeFid) {
env->ExceptionDescribe();
env->ExceptionClear();
LOG(ERROR) << "Failed to get field ID for CefAcceleratedPaintInfo.codedSize";
return nullptr;
}
env->SetObjectField(jInfo, codedSizeFid, jCodedSize.get());
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->ExceptionClear();
LOG(ERROR) << "Failed to set field for CefAcceleratedPaintInfo.codedSize";
return nullptr;
}
return jInfo.Release();
}
jobject NewJNIScreenInfo(JNIEnv* env, CefScreenInfo& screenInfo) {
ScopedJNIClass cls(env, "org/cef/handler/CefScreenInfo");
if (!cls) {
return nullptr;
}
ScopedJNIObjectLocal obj(env, NewJNIObject(env, cls));
if (!obj) {
return nullptr;
}
if (SetJNIFieldDouble(env, cls, obj, "device_scale_factor",
(double)screenInfo.device_scale_factor) &&
SetJNIFieldInt(env, cls, obj, "depth", screenInfo.depth) &&
SetJNIFieldInt(env, cls, obj, "depth_per_component",
screenInfo.depth_per_component) &&
SetJNIFieldBoolean(env, cls, obj, "is_monochrome",
screenInfo.is_monochrome) &&
SetJNIFieldInt(env, cls, obj, "x", screenInfo.rect.x) &&
SetJNIFieldInt(env, cls, obj, "y", screenInfo.rect.y) &&
SetJNIFieldInt(env, cls, obj, "width", screenInfo.rect.width) &&
SetJNIFieldInt(env, cls, obj, "height", screenInfo.rect.height) &&
SetJNIFieldInt(env, cls, obj, "available_x",
screenInfo.available_rect.x) &&
SetJNIFieldInt(env, cls, obj, "available_y",
screenInfo.available_rect.y) &&
SetJNIFieldInt(env, cls, obj, "available_width",
screenInfo.available_rect.width) &&
SetJNIFieldInt(env, cls, obj, "available_height",
screenInfo.available_rect.height)) {
return obj.Release();
}
return nullptr;
}
bool GetJNIScreenInfo(JNIEnv* env, jobject jScreenInfo, CefScreenInfo& dest) {
ScopedJNIClass cls(env, "org/cef/handler/CefScreenInfo");
if (!cls) {
return false;
}
ScopedJNIObjectLocal obj(env, jScreenInfo);
if (!obj) {
return false;
}
double tmp;
if (!GetJNIFieldDouble(env, cls, obj, "device_scale_factor", &tmp)) {
return false;
}
dest.device_scale_factor = (float)tmp;
if (GetJNIFieldInt(env, cls, obj, "depth", &(dest.depth)) &&
GetJNIFieldInt(env, cls, obj, "depth_per_component",
&(dest.depth_per_component)) &&
GetJNIFieldBoolean(env, cls, obj, "is_monochrome",
&(dest.is_monochrome)) &&
GetJNIFieldInt(env, cls, obj, "x", &(dest.rect.x)) &&
GetJNIFieldInt(env, cls, obj, "y", &(dest.rect.y)) &&
GetJNIFieldInt(env, cls, obj, "width", &(dest.rect.width)) &&
GetJNIFieldInt(env, cls, obj, "height", &(dest.rect.height)) &&
GetJNIFieldInt(env, cls, obj, "available_x", &(dest.available_rect.x)) &&
GetJNIFieldInt(env, cls, obj, "available_y", &(dest.available_rect.y)) &&
GetJNIFieldInt(env, cls, obj, "available_width",
&(dest.available_rect.width)) &&
GetJNIFieldInt(env, cls, obj, "available_height",
&(dest.available_rect.height))
) {
return true;
} else {
return false;
}
}
// create a new array of java.awt.Rectangle.
jobjectArray NewJNIRectArray(JNIEnv* env, const std::vector<CefRect>& vals) {
if (vals.empty())
return nullptr;
ScopedJNIClass cls(env, "java/awt/Rectangle");
if (!cls)
return nullptr;
const jsize size = static_cast<jsize>(vals.size());
jobjectArray arr = env->NewObjectArray(size, cls, nullptr);
for (jsize i = 0; i < size; i++) {
ScopedJNIObjectLocal rect_obj(env, NewJNIRect(env, vals[i]));
env->SetObjectArrayElement(arr, i, rect_obj);
}
return arr;
}
// Create a new java.awt.Point.
jobject NewJNIPoint(JNIEnv* env, int x, int y) {
ScopedJNIClass cls(env, "java/awt/Point");
if (!cls)
return nullptr;
ScopedJNIObjectLocal obj(env, NewJNIObject(env, cls));
if (!obj)
return nullptr;
if (SetJNIFieldInt(env, cls, obj, "x", x) &&
SetJNIFieldInt(env, cls, obj, "y", y)) {
return obj.Release();
}
return nullptr;
}
jobject NewJNIRange(JNIEnv* env, int from, int to) {
ScopedJNIClass cls(env, "org/cef/misc/CefRange");
if (!cls) {
LOG(ERROR) << "Failed to find org.cef.misc.Range";
return nullptr;
}
return NewJNIObject(env, "org/cef/misc/CefRange", "(II)V", from, to);
}
} // namespace
RenderHandler::RenderHandler(JNIEnv* env, jobject handler)
: handle_(env, handler) {}
bool RenderHandler::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
CefRect& rect) {
ScopedJNIEnv env;
if (!env)
return false;
ScopedJNIBrowser jbrowser(env, browser);
bool result = GetViewRect(jbrowser, rect);
return result;
}
void RenderHandler::GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) {
ScopedJNIEnv env;
if (!env)
return;
ScopedJNIBrowser jbrowser(env, browser);
if (!GetViewRect(jbrowser, rect)) {
rect = CefRect(0, 0, 1, 1);
}
}
///
// Called to allow the client to fill in the CefScreenInfo object with
// appropriate values. Return true if the |screen_info| structure has been
// modified.
//
// If the screen info rectangle is left empty the rectangle from GetViewRect
// will be used. If the rectangle is still empty or invalid popups may not be
// drawn correctly.
///
/*--cef()--*/
bool RenderHandler::GetScreenInfo(CefRefPtr<CefBrowser> browser,
CefScreenInfo& screen_info) {
ScopedJNIEnv env;
if (!env) {
return false;
}
ScopedJNIObjectLocal jScreenInfo(env, NewJNIScreenInfo(env, screen_info));
if (!jScreenInfo) {
return false;
}
ScopedJNIBrowser jbrowser(env, browser);
jboolean jresult = 0;
JNI_CALL_BOOLEAN_METHOD(
jresult, env, handle_, "getScreenInfo",
"(Lorg/cef/browser/CefBrowser;Lorg/cef/handler/CefScreenInfo;)Z",
jbrowser.get(), jScreenInfo.get());
if (jresult) {
if (GetJNIScreenInfo(env, jScreenInfo.get(), screen_info)) {
return true;
}
}
return false;
}
bool RenderHandler::GetScreenPoint(CefRefPtr<CefBrowser> browser,
int viewX,
int viewY,
int& screenX,
int& screenY) {
ScopedJNIEnv env;
if (!env)
return false;
ScopedJNIBrowser jbrowser(env, browser);
return GetScreenPoint(jbrowser, viewX, viewY, screenX, screenY);
}
void RenderHandler::OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) {
ScopedJNIEnv env;
if (!env)
return;
ScopedJNIBrowser jbrowser(env, browser);
JNI_CALL_VOID_METHOD(env, handle_, "onPopupShow",
"(Lorg/cef/browser/CefBrowser;Z)V", jbrowser.get(),
(jboolean)show);
}
void RenderHandler::OnPopupSize(CefRefPtr<CefBrowser> browser,
const CefRect& rect) {
ScopedJNIEnv env;
if (!env)
return;
ScopedJNIObjectLocal jrect(env, NewJNIRect(env, rect));
if (!jrect)
return;
ScopedJNIBrowser jbrowser(env, browser);
JNI_CALL_VOID_METHOD(env, handle_, "onPopupSize",
"(Lorg/cef/browser/CefBrowser;Ljava/awt/Rectangle;)V",
jbrowser.get(), jrect.get());
}
void RenderHandler::OnPaint(CefRefPtr<CefBrowser> browser,
PaintElementType type,
const RectList& dirtyRects,
const void* buffer,
int width,
int height) {
ScopedJNIEnv env;
if (!env)
return;
ScopedJNIBrowser jbrowser(env, browser);
jboolean jtype = type == PET_VIEW ? JNI_FALSE : JNI_TRUE;
ScopedJNIObjectLocal jrectArray(env, NewJNIRectArray(env, dirtyRects));
ScopedJNIObjectLocal jdirectBuffer(
env,
env->NewDirectByteBuffer(const_cast<void*>(buffer), width * height * 4));
JNI_CALL_VOID_METHOD(env, handle_, "onPaint",
"(Lorg/cef/browser/CefBrowser;Z[Ljava/awt/"
"Rectangle;Ljava/nio/ByteBuffer;II)V",
jbrowser.get(), jtype, jrectArray.get(),
jdirectBuffer.get(), width, height);
}
void RenderHandler::OnAcceleratedPaint(
CefRefPtr<CefBrowser> browser,
CefRenderHandler::PaintElementType type,
const CefRenderHandler::RectList& dirtyRects,
const CefAcceleratedPaintInfo& info) {
/* This check could be useful to get red of image jumping/flickering on resize
* but it's better to do it on the Java side.
if (info.extra.coded_size != info.extra.source_size) {
return;
}
*/
ScopedJNIEnv env;
if (!env) {
return;
}
ScopedJNIBrowser jbrowser(env, browser);
jboolean jtype = type == PET_VIEW ? JNI_FALSE : JNI_TRUE;
ScopedJNIObjectLocal jrectArray(env, NewJNIRectArray(env, dirtyRects));
ScopedJNIObjectLocal jInfo(env, convertAcceleratedPaintInfo(env, info));
JNI_CALL_VOID_METHOD(env, handle_, "onAcceleratedPaint",
"(Lorg/cef/browser/CefBrowser;Z[Ljava/awt/"
"Rectangle;Lorg/cef/handler/CefAcceleratedPaintInfo;)V",
jbrowser.get(), jtype, jrectArray.get(), jInfo.get());
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->ExceptionClear();
LOG(ERROR) << "Failed to call OnAcceleratedPaint";
return;
}
}
bool RenderHandler::StartDragging(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDragData> drag_data,
DragOperationsMask allowed_ops,
int x,
int y) {
ScopedJNIEnv env;
if (!env)
return false;
ScopedJNIBrowser jbrowser(env, browser);
ScopedJNIDragData jdragdata(env, drag_data);
jdragdata.SetTemporary();
jboolean jresult = JNI_FALSE;
JNI_CALL_METHOD(
env, handle_, "startDragging",
"(Lorg/cef/browser/CefBrowser;Lorg/cef/callback/CefDragData;III)Z",
Boolean, jresult, jbrowser.get(), jdragdata.get(), (jint)allowed_ops,
(jint)x, (jint)y);
return (jresult != JNI_FALSE);
}
void RenderHandler::UpdateDragCursor(CefRefPtr<CefBrowser> browser,
DragOperation operation) {
ScopedJNIEnv env;
if (!env)
return;
ScopedJNIBrowser jbrowser(env, browser);
JNI_CALL_VOID_METHOD(env, handle_, "updateDragCursor",
"(Lorg/cef/browser/CefBrowser;I)V", jbrowser.get(),
(jint)operation);
}
bool RenderHandler::GetViewRect(jobject browser, CefRect& rect) {
ScopedJNIEnv env;
if (!env)
return false;
ScopedJNIObjectResult jreturn(env);
JNI_CALL_METHOD(env, handle_, "getViewRect",
"(Lorg/cef/browser/CefBrowser;)Ljava/awt/Rectangle;", Object,
jreturn, browser);
if (jreturn) {
rect = GetJNIRect(env, jreturn);
return true;
}
return false;
}
bool RenderHandler::GetScreenPoint(jobject browser,
int viewX,
int viewY,
int& screenX,
int& screenY) {
ScopedJNIEnv env;
if (!env)
return false;
ScopedJNIObjectLocal jpoint(env, NewJNIPoint(env, viewX, viewY));
if (!jpoint)
return false;
ScopedJNIObjectResult jreturn(env);
JNI_CALL_METHOD(
env, handle_, "getScreenPoint",
"(Lorg/cef/browser/CefBrowser;Ljava/awt/Point;)Ljava/awt/Point;", Object,
jreturn, browser, jpoint.get());
if (jreturn) {
GetJNIPoint(env, jreturn, &screenX, &screenY);
return true;
}
return false;
}
void RenderHandler::OnImeCompositionRangeChanged(
CefRefPtr<CefBrowser> browser,
const CefRange& selected_range,
const CefRenderHandler::RectList& character_bounds) {
ScopedJNIEnv env;
if (!env) {
return;
}
ScopedJNIBrowser jBrowser(env, browser);
ScopedJNIObjectLocal jSelectionRange(
env, NewJNIRange(env, selected_range.from, selected_range.to));
ScopedJNIObjectLocal jCharBounds(env, NewJNIRectArray(env, character_bounds));
if (env && handle_) {
ScopedJNIClass _cls(env, env->GetObjectClass(handle_));
jmethodID _methodId =
env->GetMethodID(_cls, "OnImeCompositionRangeChanged",
"(Lorg/cef/browser/CefBrowser;Lorg/cef/misc/"
"CefRange;[Ljava/awt/Rectangle;)V");
if (_methodId != nullptr) {
env->CallVoidMethod(handle_, _methodId, jBrowser.get(),
jSelectionRange.get(), jCharBounds.get());
}
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
}
}
void RenderHandler::OnTextSelectionChanged(CefRefPtr<CefBrowser> browser,
const CefString& selected_text,
const CefRange& selected_range) {
ScopedJNIEnv env;
if (!env) {
return;
}
ScopedJNIBrowser jBrowser(env, browser);
ScopedJNIObjectLocal jSelectedText(env, NewJNIString(env, selected_text));
ScopedJNIObjectLocal jSelectionRange(
env, NewJNIRange(env, selected_range.from, selected_range.to));
if (env && handle_) {
ScopedJNIClass _cls(env, env->GetObjectClass(handle_));
jmethodID _methodId =
env->GetMethodID(_cls, "OnTextSelectionChanged",
"(Lorg/cef/browser/CefBrowser;Ljava/lang/String;Lorg/"
"cef/misc/CefRange;)V");
if (_methodId != nullptr) {
env->CallVoidMethod(handle_, _methodId, jBrowser.get(),
jSelectedText.get(), jSelectionRange.get());
}
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
}
}