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(); } } }