diff --git a/java/org/cef/CefClient.java b/java/org/cef/CefClient.java index 687be93d..e4914633 100644 --- a/java/org/cef/CefClient.java +++ b/java/org/cef/CefClient.java @@ -5,36 +5,8 @@ package org.cef; import org.cef.browser.*; -import org.cef.callback.CefAuthCallback; -import org.cef.callback.CefBeforeDownloadCallback; -import org.cef.callback.CefCallback; -import org.cef.callback.CefContextMenuParams; -import org.cef.callback.CefDownloadItem; -import org.cef.callback.CefDownloadItemCallback; -import org.cef.callback.CefDragData; -import org.cef.callback.CefFileDialogCallback; -import org.cef.callback.CefJSDialogCallback; -import org.cef.callback.CefMenuModel; -import org.cef.callback.CefPrintDialogCallback; -import org.cef.callback.CefPrintJobCallback; -import org.cef.handler.CefClientHandler; -import org.cef.handler.CefContextMenuHandler; -import org.cef.handler.CefDialogHandler; -import org.cef.handler.CefDisplayHandler; -import org.cef.handler.CefDownloadHandler; -import org.cef.handler.CefDragHandler; -import org.cef.handler.CefFocusHandler; -import org.cef.handler.CefJSDialogHandler; -import org.cef.handler.CefKeyboardHandler; -import org.cef.handler.CefLifeSpanHandler; -import org.cef.handler.CefLoadHandler; -import org.cef.handler.CefPrintHandler; -import org.cef.handler.CefRenderHandler; -import org.cef.handler.CefRequestHandler; -import org.cef.handler.CefResourceHandler; -import org.cef.handler.CefResourceRequestHandler; -import org.cef.handler.CefScreenInfo; -import org.cef.handler.CefWindowHandler; +import org.cef.callback.*; +import org.cef.handler.*; import org.cef.misc.BoolRef; import org.cef.misc.CefPrintSettings; import org.cef.misc.StringRef; @@ -54,6 +26,7 @@ import java.beans.PropertyChangeListener; import java.nio.ByteBuffer; import java.util.Collection; +import java.util.EnumSet; import java.util.HashMap; import java.util.Vector; import java.util.function.Consumer; @@ -67,7 +40,7 @@ public class CefClient extends CefClientHandler implements CefContextMenuHandler, CefDialogHandler, CefDisplayHandler, CefDownloadHandler, CefDragHandler, CefFocusHandler, CefJSDialogHandler, CefKeyboardHandler, CefLifeSpanHandler, CefLoadHandler, CefPrintHandler, CefRenderHandler, - CefRequestHandler, CefWindowHandler { + CefRequestHandler, CefWindowHandler, CefPermissionHandler { private HashMap browser_ = new HashMap(); private CefContextMenuHandler contextMenuHandler_ = null; private CefDialogHandler dialogHandler_ = null; @@ -81,6 +54,7 @@ public class CefClient extends CefClientHandler private CefLoadHandler loadHandler_ = null; private CefPrintHandler printHandler_ = null; private CefRequestHandler requestHandler_ = null; + private CefPermissionHandler permissionHandler_ = null; private boolean isDisposed_ = false; private volatile CefBrowser focusedBrowser_ = null; private final PropertyChangeListener propertyChangeListener = new PropertyChangeListener() { @@ -794,6 +768,55 @@ public void updateDragCursor(CefBrowser browser, int operation) { if (realHandler != null) realHandler.updateDragCursor(browser, operation); } + // CefPermissionHandler + + @Override + protected CefPermissionHandler getPermissionHandler() { + return this; + } + + public CefClient addPermissionHandler(CefPermissionHandler handler) { + if (permissionHandler_ == null) permissionHandler_ = handler; + return this; + } + + public void removePermissionHandler() { + permissionHandler_ = null; + } + + @Override + public boolean onRequestMediaAccessPermission( + CefBrowser browser, + CefFrame frame, + String requestingOrigin, + int requestedPermissions, + CefMediaAccessCallback callback) { + if (permissionHandler_ != null && browser != null) { + return permissionHandler_.onRequestMediaAccessPermission(rowser, frame, requestingOrigin, requestedPermissions, callback); + } + return false; + } + + @Override + public boolean onShowPermissionPrompt( + CefBrowser browser, + long promptId, + String requestingOrigin, + EnumSet requestedPermissions, + CefPermissionPromptCallback callback) { + if (permissionHandler_ != null && browser != null) { + return permissionHandler_.onShowPermissionPrompt(browser, promptId, requestingOrigin, requestedPermissions, callback); + } + return false; + } + + @Override + public void onDismissPermissionPrompt(CefBrowser browser, long promptId, CefPermissionRequestResult result) { + if (permissionHandler_ != null && browser != null) { + permissionHandler_.onDismissPermissionPrompt(browser, promptId, result); + } + } + // CefRequestHandler public CefClient addRequestHandler(CefRequestHandler handler) { diff --git a/java/org/cef/callback/CefMediaAccessCallback.java b/java/org/cef/callback/CefMediaAccessCallback.java new file mode 100644 index 00000000..678b5dfa --- /dev/null +++ b/java/org/cef/callback/CefMediaAccessCallback.java @@ -0,0 +1,6 @@ +package org.cef.callback; + +public interface CefMediaAccessCallback { + void Continue(int allowedPermissions); + void Cancel(); +} diff --git a/java/org/cef/callback/CefMediaAccessCallback_N.java b/java/org/cef/callback/CefMediaAccessCallback_N.java new file mode 100644 index 00000000..03eeca4c --- /dev/null +++ b/java/org/cef/callback/CefMediaAccessCallback_N.java @@ -0,0 +1,32 @@ +package org.cef.callback; + +class CefMediaAccessCallback_N extends CefNativeAdapter implements CefMediaAccessCallback { + CefMediaAccessCallback_N() {} + + @Override + protected void finalize() throws Throwable { + Cancel(); + super.finalize(); + } + + @Override + public void Continue(int allowedPermissions) { + try { + N_Continue(getNativeRef(null), allowedPermissions); + } catch (UnsatisfiedLinkError ule) { + ule.printStackTrace(); + } + } + + @Override + public void Cancel() { + try { + N_Cancel(getNativeRef(null)); + } catch (UnsatisfiedLinkError ule) { + ule.printStackTrace(); + } + } + + private final native void N_Continue(long self, int allowedPermissions); + private final native void N_Cancel(long self); +} diff --git a/java/org/cef/callback/CefPermissionPromptCallback.java b/java/org/cef/callback/CefPermissionPromptCallback.java new file mode 100644 index 00000000..ed828a04 --- /dev/null +++ b/java/org/cef/callback/CefPermissionPromptCallback.java @@ -0,0 +1,7 @@ +package org.cef.callback; + +import org.cef.handler.CefPermissionRequestResult; + +public interface CefPermissionPromptCallback { + void Continue(CefPermissionRequestResult result); +} diff --git a/java/org/cef/callback/CefPermissionPromptCallback_N.java b/java/org/cef/callback/CefPermissionPromptCallback_N.java new file mode 100644 index 00000000..2af9ddd9 --- /dev/null +++ b/java/org/cef/callback/CefPermissionPromptCallback_N.java @@ -0,0 +1,24 @@ +package org.cef.callback; + +import org.cef.handler.CefPermissionRequestResult; + +class CefPermissionPromptCallback_N extends CefNativeAdapter implements CefPermissionPromptCallback { + CefPermissionPromptCallback_N() {} + + @Override + protected void finalize() throws Throwable { + Continue(CefPermissionRequestResult.DISMISS); + super.finalize(); + } + + @Override + public void Continue(CefPermissionRequestResult result) { + try { + N_Continue(getNativeRef(null), result.getNativeValue()); + } catch (UnsatisfiedLinkError ule) { + ule.printStackTrace(); + } + } + + private final native void N_Continue(long self, int result); +} diff --git a/java/org/cef/handler/CefClientHandler.java b/java/org/cef/handler/CefClientHandler.java index 6a263a8e..5621118d 100644 --- a/java/org/cef/handler/CefClientHandler.java +++ b/java/org/cef/handler/CefClientHandler.java @@ -159,6 +159,13 @@ protected void dispose() { */ abstract protected CefRequestHandler getRequestHandler(); + /** + * Return the handler for permission requests. + * This method is a callback method and is called by + * the native code. + */ + abstract protected CefPermissionHandler getPermissionHandler(); + /** * Return the handler for windowed rendering events. * This method is a callback method and is called by diff --git a/java/org/cef/handler/CefPermissionHandler.java b/java/org/cef/handler/CefPermissionHandler.java new file mode 100644 index 00000000..6da2d882 --- /dev/null +++ b/java/org/cef/handler/CefPermissionHandler.java @@ -0,0 +1,28 @@ +package org.cef.handler; + +import java.util.EnumSet; +import org.cef.browser.CefBrowser; +import org.cef.browser.CefFrame; +import org.cef.callback.CefMediaAccessCallback; +import org.cef.callback.CefPermissionPromptCallback; + +public interface CefPermissionHandler { + boolean onRequestMediaAccessPermission( + CefBrowser browser, + CefFrame frame, + String requestingOrigin, + int requestedPermissions, + CefMediaAccessCallback callback); + + boolean onShowPermissionPrompt( + CefBrowser browser, + long promptId, + String requestingOrigin, + EnumSet requestedPermissions, + CefPermissionPromptCallback callback); + + void onDismissPermissionPrompt( + CefBrowser browser, + long promptId, + CefPermissionRequestResult result); +} diff --git a/java/org/cef/handler/CefPermissionHandlerAdapter.java b/java/org/cef/handler/CefPermissionHandlerAdapter.java new file mode 100644 index 00000000..7ef6cc91 --- /dev/null +++ b/java/org/cef/handler/CefPermissionHandlerAdapter.java @@ -0,0 +1,107 @@ +package org.cef.handler; + +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.Map; +import org.cef.browser.CefBrowser; +import org.cef.browser.CefFrame; +import org.cef.callback.CefMediaAccessCallback; +import org.cef.callback.CefPermissionPromptCallback; + +public abstract class CefPermissionHandlerAdapter implements CefPermissionHandler { + private final Map promptPolicy = new EnumMap<>(CefPermissionRequestType.class); + + protected CefPermissionHandlerAdapter() { + for (CefPermissionRequestType type : CefPermissionRequestType.values()) { + promptPolicy.put(type, Boolean.FALSE); + } + promptPolicy.put(CefPermissionRequestType.NONE, Boolean.FALSE); + } + + public CefPermissionHandlerAdapter setPermission(CefPermissionRequestType type, boolean allow) { + promptPolicy.put(type, allow); + return this; + } + + public boolean isAllowed(CefPermissionRequestType type) { + return Boolean.TRUE.equals(promptPolicy.get(type)); + } + + protected CefPermissionRequestResult evaluatePrompt( + CefBrowser browser, + long promptId, + String requestingOrigin, + EnumSet requestedPermissions) { + + for (CefPermissionRequestType type : requestedPermissions) { + switch (type) { + case AR_SESSION: + case CAMERA_PAN_TILT_ZOOM: + case CAMERA_STREAM: + case CAPTURED_SURFACE_CONTROL: + case CLIPBOARD: + case TOP_LEVEL_STORAGE_ACCESS: + case DISK_QUOTA: + case LOCAL_FONTS: + case GEOLOCATION: + case HAND_TRACKING: + case IDENTITY_PROVIDER: + case IDLE_DETECTION: + case MIC_STREAM: + case MIDI_SYSEX: + case MULTIPLE_DOWNLOADS: + case NOTIFICATIONS: + case KEYBOARD_LOCK: + case POINTER_LOCK: + case PROTECTED_MEDIA_IDENTIFIER: + case REGISTER_PROTOCOL_HANDLER: + case STORAGE_ACCESS: + case VR_SESSION: + case WEB_APP_INSTALLATION: + case WINDOW_MANAGEMENT: + case FILE_SYSTEM_ACCESS: + case LOCAL_NETWORK_ACCESS: + case LOCAL_NETWORK: + case LOOPBACK_NETWORK: + if (!isAllowed(type)) { + return CefPermissionRequestResult.DENY; + } + break; + case NONE: + default: + return CefPermissionRequestResult.IGNORE; + } + } + + return CefPermissionRequestResult.ACCEPT; + } + + @Override + public boolean onShowPermissionPrompt( + CefBrowser browser, + long promptId, + String requestingOrigin, + EnumSet requestedPermissions, + CefPermissionPromptCallback callback) { + callback.Continue(evaluatePrompt(browser, promptId, requestingOrigin, requestedPermissions)); + return true; + } + + @Override + public boolean onRequestMediaAccessPermission( + CefBrowser browser, + CefFrame frame, + String requestingOrigin, + int requestedPermissions, + CefMediaAccessCallback callback) { + callback.Continue(requestedPermissions); + return true; + } + + @Override + public void onDismissPermissionPrompt( + CefBrowser browser, + long promptId, + CefPermissionRequestResult result) { + } +} diff --git a/java/org/cef/handler/CefPermissionRequestResult.java b/java/org/cef/handler/CefPermissionRequestResult.java new file mode 100644 index 00000000..de5a1f75 --- /dev/null +++ b/java/org/cef/handler/CefPermissionRequestResult.java @@ -0,0 +1,18 @@ +package org.cef.handler; + +public enum CefPermissionRequestResult { + ACCEPT(0), + DENY(1), + DISMISS(2), + IGNORE(3); + + private final int nativeValue; + + CefPermissionRequestResult(int nativeValue) { + this.nativeValue = nativeValue; + } + + public int getNativeValue() { + return nativeValue; + } +} diff --git a/java/org/cef/handler/CefPermissionRequestType.java b/java/org/cef/handler/CefPermissionRequestType.java new file mode 100644 index 00000000..ff83e721 --- /dev/null +++ b/java/org/cef/handler/CefPermissionRequestType.java @@ -0,0 +1,66 @@ +package org.cef.handler; + +import java.util.EnumSet; + +public enum CefPermissionRequestType { + NONE(0), + AR_SESSION(1 << 0), + CAMERA_PAN_TILT_ZOOM(1 << 1), + CAMERA_STREAM(1 << 2), + CAPTURED_SURFACE_CONTROL(1 << 3), + CLIPBOARD(1 << 4), + TOP_LEVEL_STORAGE_ACCESS(1 << 5), + DISK_QUOTA(1 << 6), + LOCAL_FONTS(1 << 7), + GEOLOCATION(1 << 8), + HAND_TRACKING(1 << 9), + IDENTITY_PROVIDER(1 << 10), + IDLE_DETECTION(1 << 11), + MIC_STREAM(1 << 12), + MIDI_SYSEX(1 << 13), + MULTIPLE_DOWNLOADS(1 << 14), + NOTIFICATIONS(1 << 15), + KEYBOARD_LOCK(1 << 16), + POINTER_LOCK(1 << 17), + PROTECTED_MEDIA_IDENTIFIER(1 << 18), + REGISTER_PROTOCOL_HANDLER(1 << 19), + STORAGE_ACCESS(1 << 20), + VR_SESSION(1 << 21), + WEB_APP_INSTALLATION(1 << 22), + WINDOW_MANAGEMENT(1 << 23), + FILE_SYSTEM_ACCESS(1 << 24), + LOCAL_NETWORK_ACCESS(1 << 25), + LOCAL_NETWORK(1 << 26), + LOOPBACK_NETWORK(1 << 27); + + private final int mask; + + CefPermissionRequestType(int mask) { + this.mask = mask; + } + + public int getMask() { + return mask; + } + + public static EnumSet fromMask(int mask) { + EnumSet result = EnumSet.noneOf(CefPermissionRequestType.class); + for (CefPermissionRequestType type : values()) { + if (type != NONE && (mask & type.mask) != 0) { + result.add(type); + } + } + if (result.isEmpty()) { + result.add(NONE); + } + return result; + } + + public static int toMask(EnumSet types) { + int result = 0; + for (CefPermissionRequestType type : types) { + result |= type.mask; + } + return result; + } +} diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index cdcd1ef4..f586c9ec 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -38,10 +38,12 @@ set(JCEF_SRCS CefFrame_N.h CefJSDialogCallback_N.h CefJSDialogCallback_N.cpp + CefMediaAccessCallback_N.cpp CefMenuModel_N.cpp CefMenuModel_N.h CefMessageRouter_N.cpp CefMessageRouter_N.h + CefPermissionPromptCallback_N.cpp CefPostData_N.cpp CefPostData_N.h CefPostDataElement_N.cpp @@ -116,10 +118,16 @@ set(JCEF_SRCS life_span_handler.h load_handler.cpp load_handler.h + media_access_callback.cpp + media_access_callback.h message_router_handler.cpp message_router_handler.h pdf_print_callback.cpp pdf_print_callback.h + permission_handler.cpp + permission_handler.h + permission_prompt_callback.cpp + permission_prompt_callback.h print_handler.cpp print_handler.h render_handler.cpp diff --git a/native/CefMediaAccessCallback_N.cpp b/native/CefMediaAccessCallback_N.cpp new file mode 100644 index 00000000..a9131e39 --- /dev/null +++ b/native/CefMediaAccessCallback_N.cpp @@ -0,0 +1,36 @@ +#include "CefMediaAccessCallback_N.h" + +#include "include/cef_permission_handler.h" + +#include "jni_scoped_helpers.h" +#include "jni_util.h" + +namespace { + CefRefPtr GetSelf(jlong self) { + return reinterpret_cast(self); + } + + void ClearSelf(JNIEnv* env, jobject obj) { + SetCefForJNIObject(env, obj, nullptr, "CefMediaAccessCallback"); + } +} + +JNIEXPORT void JNICALL +Java_org_cef_callback_CefMediaAccessCallback_1N_N_1Continue(JNIEnv* env, jobject obj, jlong self, jint allowed_permissions) { + CefRefPtr callback = GetSelf(self); + if (!callback) + return; + + callback->Continue(static_cast(allowed_permissions)); + ClearSelf(env, obj); +} + +JNIEXPORT void JNICALL +Java_org_cef_callback_CefMediaAccessCallback_1N_N_1Cancel(JNIEnv* env, jobject obj, jlong self) { + CefRefPtr callback = GetSelf(self); + if (!callback) + return; + + callback->Cancel(); + ClearSelf(env, obj); +} diff --git a/native/CefMediaAccessCallback_N.h b/native/CefMediaAccessCallback_N.h new file mode 100644 index 00000000..203367a9 --- /dev/null +++ b/native/CefMediaAccessCallback_N.h @@ -0,0 +1,21 @@ +#ifndef JCEF_NATIVE_CEF_MEDIA_ACCESS_CALLBACK_N_H_ +#define JCEF_NATIVE_CEF_MEDIA_ACCESS_CALLBACK_N_H_ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +JNIEXPORT void JNICALL +Java_org_cef_callback_CefMediaAccessCallback_1N_N_1Continue(JNIEnv*, jobject, jlong, jint); + +JNIEXPORT void JNICALL +Java_org_cef_callback_CefMediaAccessCallback_1N_N_1Cancel(JNIEnv*, jobject, jlong); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/native/CefPermissionPromptCallback_N.cpp b/native/CefPermissionPromptCallback_N.cpp new file mode 100644 index 00000000..b1be869e --- /dev/null +++ b/native/CefPermissionPromptCallback_N.cpp @@ -0,0 +1,26 @@ +#include "CefPermissionPromptCallback_N.h" + +#include "include/cef_permission_handler.h" + +#include "jni_scoped_helpers.h" +#include "jni_util.h" + +namespace { + CefRefPtr GetSelf(jlong self) { + return reinterpret_cast(self); + } + + void ClearSelf(JNIEnv* env, jobject obj) { + SetCefForJNIObject(env, obj, nullptr, "CefPermissionPromptCallback"); + } +} + +JNIEXPORT void JNICALL +Java_org_cef_callback_CefPermissionPromptCallback_1N_N_1Continue(JNIEnv* env, jobject obj, jlong self, jint result) { + CefRefPtr callback = GetSelf(self); + if (!callback) + return; + + callback->Continue(static_cast(result)); + ClearSelf(env, obj); +} diff --git a/native/CefPermissionPromptCallback_N.h b/native/CefPermissionPromptCallback_N.h new file mode 100644 index 00000000..e5034951 --- /dev/null +++ b/native/CefPermissionPromptCallback_N.h @@ -0,0 +1,16 @@ +#ifndef JCEF_NATIVE_CEF_PERMISSION_PROMPT_CALLBACK_N_H_ +#define JCEF_NATIVE_CEF_PERMISSION_PROMPT_CALLBACK_N_H_ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif +JNIEXPORT void JNICALL +Java_org_cef_callback_CefPermissionPromptCallback_1N_N_1Continue(JNIEnv*, jobject, jlong, jint); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/native/client_handler.cpp b/native/client_handler.cpp index 9b5567fc..3acadca5 100644 --- a/native/client_handler.cpp +++ b/native/client_handler.cpp @@ -145,6 +145,10 @@ CefRefPtr ClientHandler::GetRequestHandler() { return GetHandler("RequestHandler"); } +CefRefPtr ClientHandler::GetPermissionHandler() { + return GetHandler("PermissionHandler"); +} + bool ClientHandler::OnProcessMessageReceived( CefRefPtr browser, CefRefPtr frame, diff --git a/native/client_handler.h b/native/client_handler.h index db285828..2b9f4c7b 100644 --- a/native/client_handler.h +++ b/native/client_handler.h @@ -17,6 +17,7 @@ #include "jni_scoped_helpers.h" #include "message_router_handler.h" #include "window_handler.h" +#include "permission_handler.h" // ClientHandler implementation. class ClientHandler : public CefClient { @@ -37,6 +38,7 @@ class ClientHandler : public CefClient { CefRefPtr GetPrintHandler() override; CefRefPtr GetRenderHandler() override; CefRefPtr GetRequestHandler() override; + CefRefPtr GetPermissionHandler() override; bool OnProcessMessageReceived(CefRefPtr browser, CefRefPtr frame, diff --git a/native/jni_util.cpp b/native/jni_util.cpp index 02e24a51..e6419fc0 100644 --- a/native/jni_util.cpp +++ b/native/jni_util.cpp @@ -1024,6 +1024,105 @@ jobject NewJNIObjectFromCefValue(JNIEnv* env, const CefRefPtr value) { } } +jobject NewJNIPermissionRequestResult(JNIEnv* env, cef_permission_request_result_t result) { + const char* name = "IGNORE"; + switch (result) { + case CEF_PERMISSION_RESULT_ACCEPT: + name = "ACCEPT"; + break; + case CEF_PERMISSION_RESULT_DENY: + name = "DENY"; + break; + case CEF_PERMISSION_RESULT_DISMISS: + name = "DISMISS"; + break; + case CEF_PERMISSION_RESULT_IGNORE: + default: + name = "IGNORE"; + break; + } + + jclass cls = env->FindClass("org/cef/handler/CefPermissionRequestResult"); + jmethodID valueOf = env->GetStaticMethodID(cls, "valueOf", "(Ljava/lang/String;)Lorg/cef/handler/CefPermissionRequestResult;"); + jstring jname = env->NewStringUTF(name); + jobject resultObj = env->CallStaticObjectMethod(cls, valueOf, jname); + env->DeleteLocalRef(jname); + env->DeleteLocalRef(cls); + return resultObj; +} + +jobject NewJNIPermissionRequestTypeEnumSet(JNIEnv* env, uint32_t mask) { + jclass enumCls = env->FindClass("org/cef/handler/CefPermissionRequestType"); + jclass enumSetCls = env->FindClass("java/util/EnumSet"); + + jmethodID noneOf = env->GetStaticMethodID(enumSetCls, "noneOf", "(Ljava/lang/Class;)Ljava/util/EnumSet;"); + jobject enumSet = env->CallStaticObjectMethod(enumSetCls, noneOf, enumCls); + + jmethodID add = env->GetMethodID(enumSetCls, "add", "(Ljava/lang/Object;)Z"); + jmethodID valueOf = env->GetStaticMethodID(enumCls, "valueOf", "(Ljava/lang/String;)Lorg/cef/handler/CefPermissionRequestType;"); + + struct Entry { + uint32_t mask; + const char* name; + }; + + Entry entries[] = { + {CEF_PERMISSION_TYPE_NONE, "NONE"}, + {CEF_PERMISSION_TYPE_AR_SESSION, "AR_SESSION"}, + {CEF_PERMISSION_TYPE_CAMERA_PAN_TILT_ZOOM, "CAMERA_PAN_TILT_ZOOM"}, + {CEF_PERMISSION_TYPE_CAMERA_STREAM, "CAMERA_STREAM"}, + {CEF_PERMISSION_TYPE_CAPTURED_SURFACE_CONTROL, "CAPTURED_SURFACE_CONTROL"}, + {CEF_PERMISSION_TYPE_CLIPBOARD, "CLIPBOARD"}, + {CEF_PERMISSION_TYPE_TOP_LEVEL_STORAGE_ACCESS, "TOP_LEVEL_STORAGE_ACCESS"}, + {CEF_PERMISSION_TYPE_DISK_QUOTA, "DISK_QUOTA"}, + {CEF_PERMISSION_TYPE_LOCAL_FONTS, "LOCAL_FONTS"}, + {CEF_PERMISSION_TYPE_GEOLOCATION, "GEOLOCATION"}, + {CEF_PERMISSION_TYPE_HAND_TRACKING, "HAND_TRACKING"}, + {CEF_PERMISSION_TYPE_IDENTITY_PROVIDER, "IDENTITY_PROVIDER"}, + {CEF_PERMISSION_TYPE_IDLE_DETECTION, "IDLE_DETECTION"}, + {CEF_PERMISSION_TYPE_MIC_STREAM, "MIC_STREAM"}, + {CEF_PERMISSION_TYPE_MIDI_SYSEX, "MIDI_SYSEX"}, + {CEF_PERMISSION_TYPE_MULTIPLE_DOWNLOADS, "MULTIPLE_DOWNLOADS"}, + {CEF_PERMISSION_TYPE_NOTIFICATIONS, "NOTIFICATIONS"}, + {CEF_PERMISSION_TYPE_KEYBOARD_LOCK, "KEYBOARD_LOCK"}, + {CEF_PERMISSION_TYPE_POINTER_LOCK, "POINTER_LOCK"}, + {CEF_PERMISSION_TYPE_PROTECTED_MEDIA_IDENTIFIER, "PROTECTED_MEDIA_IDENTIFIER"}, + {CEF_PERMISSION_TYPE_REGISTER_PROTOCOL_HANDLER, "REGISTER_PROTOCOL_HANDLER"}, + {CEF_PERMISSION_TYPE_STORAGE_ACCESS, "STORAGE_ACCESS"}, + {CEF_PERMISSION_TYPE_VR_SESSION, "VR_SESSION"}, + {CEF_PERMISSION_TYPE_WEB_APP_INSTALLATION, "WEB_APP_INSTALLATION"}, + {CEF_PERMISSION_TYPE_WINDOW_MANAGEMENT, "WINDOW_MANAGEMENT"}, + {CEF_PERMISSION_TYPE_FILE_SYSTEM_ACCESS, "FILE_SYSTEM_ACCESS"}, + {CEF_PERMISSION_TYPE_LOCAL_NETWORK_ACCESS, "LOCAL_NETWORK_ACCESS"}, + {CEF_PERMISSION_TYPE_LOCAL_NETWORK, "LOCAL_NETWORK"}, + {CEF_PERMISSION_TYPE_LOOPBACK_NETWORK, "LOOPBACK_NETWORK"} + }; + + bool any = false; + for (const auto& entry : entries) { + if ((mask & entry.mask) != 0) { + jstring jname = env->NewStringUTF(entry.name); + jobject enumVal = env->CallStaticObjectMethod(enumCls, valueOf, jname); + env->CallBooleanMethod(enumSet, add, enumVal); + env->DeleteLocalRef(enumVal); + env->DeleteLocalRef(jname); + any = true; + } + } + + if (!any) { + jstring jname = env->NewStringUTF("NONE"); + jobject enumVal = env->CallStaticObjectMethod(enumCls, valueOf, jname); + env->CallBooleanMethod(enumSet, add, enumVal); + env->DeleteLocalRef(enumVal); + env->DeleteLocalRef(jname); + } + + env->DeleteLocalRef(enumCls); + env->DeleteLocalRef(enumSetCls); + return enumSet; +} + cef_errorcode_t GetJNIErrorCode(JNIEnv* env, jobject jerrorCode) { cef_errorcode_t errorCode = ERR_NONE; diff --git a/native/jni_util.h b/native/jni_util.h index 6028427c..61abfa35 100644 --- a/native/jni_util.h +++ b/native/jni_util.h @@ -117,6 +117,9 @@ jobject NewJNIArrayList(JNIEnv* env); jobject NewJNIObjectFromCefValue(JNIEnv* env, const CefRefPtr value); +jobject NewJNIPermissionRequestResult(JNIEnv* env, cef_permission_request_result_t result); +jobject NewJNIPermissionRequestTypeEnumSet(JNIEnv* env, uint32_t mask); + jboolean GetJNIBoolean(JNIEnv* env, jobject jbool); jint GetJNIInteger(JNIEnv* env, jobject jint); jdouble GetJNIDouble(JNIEnv* env, jobject jdouble); diff --git a/native/media_access_callback.cpp b/native/media_access_callback.cpp new file mode 100644 index 00000000..edc8013e --- /dev/null +++ b/native/media_access_callback.cpp @@ -0,0 +1,29 @@ +#include "media_access_callback.h" + +#include "jni_scoped_helpers.h" +#include "jni_util.h" + +MediaAccessCallback::MediaAccessCallback(CefRefPtr callback) : callback_(callback) {} + +void MediaAccessCallback::Continue(uint32_t allowed_permissions) { + if (callback_) + callback_->Continue(allowed_permissions); +} + +void MediaAccessCallback::Cancel() { + if (callback_) + callback_->Cancel(); +} + +jobject NewJNIMediaAccessCallback(JNIEnv* env, CefRefPtr callback) { + if (!callback) + return nullptr; + + jobject jcallback = NewJNIObject(env, "org/cef/callback/CefMediaAccessCallback_N"); + if (!jcallback) + return nullptr; + + SetCefForJNIObject(env, jcallback, callback.get(), "CefMediaAccessCallback"); + + return jcallback; +} diff --git a/native/media_access_callback.h b/native/media_access_callback.h new file mode 100644 index 00000000..d23e7d51 --- /dev/null +++ b/native/media_access_callback.h @@ -0,0 +1,23 @@ +#ifndef JCEF_NATIVE_MEDIA_ACCESS_CALLBACK_H_ +#define JCEF_NATIVE_MEDIA_ACCESS_CALLBACK_H_ +#pragma once + +#include +#include "include/cef_permission_handler.h" +#include "include/cef_base.h" + +class MediaAccessCallback : public CefBaseRefCounted { + public: + explicit MediaAccessCallback(CefRefPtr callback); + + void Continue(uint32_t allowed_permissions); + void Cancel(); + + private: + CefRefPtr callback_; + IMPLEMENT_REFCOUNTING(MediaAccessCallback); +}; + +jobject NewJNIMediaAccessCallback(JNIEnv* env, CefRefPtr callback); + +#endif diff --git a/native/permission_handler.cpp b/native/permission_handler.cpp new file mode 100644 index 00000000..cc1b522e --- /dev/null +++ b/native/permission_handler.cpp @@ -0,0 +1,99 @@ +#include "permission_handler.h" + +#include "jni_util.h" +#include "media_access_callback.h" +#include "permission_prompt_callback.h" + +PermissionHandler::PermissionHandler(JNIEnv* env, jobject handler) : handle_(env, handler) {} + +bool PermissionHandler::OnRequestMediaAccessPermission( + CefRefPtr browser, + CefRefPtr frame, + const CefString& requesting_origin, + uint32_t requested_permissions, + CefRefPtr callback) { + ScopedJNIEnv env; + if (!env) + return false; + + ScopedJNIBrowser jbrowser(env, browser); + ScopedJNIFrame jframe(env, frame); + jframe.SetTemporary(); + ScopedJNIString jorigin(env, requesting_origin); + + jobject jcallback = NewJNIMediaAccessCallback(env, callback); + + jboolean jresult = JNI_FALSE; + JNI_CALL_METHOD(env, handle_, "onRequestMediaAccessPermission", + "(Lorg/cef/browser/CefBrowser;" + "Lorg/cef/browser/CefFrame;" + "Ljava/lang/String;" + "ILorg/cef/callback/CefMediaAccessCallback;)Z", + Boolean, jresult, + jbrowser.get(), + jframe.get(), + jorigin.get(), + static_cast(requested_permissions), + jcallback); + + if (jcallback) + env->DeleteLocalRef(jcallback); + + return (jresult != JNI_FALSE); +} + +bool PermissionHandler::OnShowPermissionPrompt( + CefRefPtr browser, + uint64_t prompt_id, + const CefString& requesting_origin, + uint32_t requested_permissions, + CefRefPtr callback) { + ScopedJNIEnv env; + if (!env) + return false; + + ScopedJNIBrowser jbrowser(env, browser); + ScopedJNIString jorigin(env, requesting_origin); + + jobject jpermissionSet = NewJNIPermissionRequestTypeEnumSet(env, requested_permissions); + jobject jcallback = NewJNIPermissionPromptCallback(env, callback); + + jboolean jresult = JNI_FALSE; + JNI_CALL_METHOD(env, handle_, "onShowPermissionPrompt", + "(Lorg/cef/browser/CefBrowser;" + "JLjava/lang/String;" + "Ljava/util/EnumSet;" + "Lorg/cef/callback/CefPermissionPromptCallback;)Z", + Boolean, jresult, + jbrowser.get(), + static_cast(prompt_id), + jorigin.get(), + jpermissionSet, + jcallback); + + if (jpermissionSet) + env->DeleteLocalRef(jpermissionSet); + if (jcallback) + env->DeleteLocalRef(jcallback); + + return (jresult != JNI_FALSE); +} + +void PermissionHandler::OnDismissPermissionPrompt( + CefRefPtr browser, + uint64_t prompt_id, + cef_permission_request_result_t result) { + ScopedJNIEnv env; + if (!env) + return; + + ScopedJNIBrowser jbrowser(env, browser); + ScopedJNIObjectLocal jresult(env, NewJNIPermissionRequestResult(env, result)); + + JNI_CALL_VOID_METHOD(env, handle_, "onDismissPermissionPrompt", + "(Lorg/cef/browser/CefBrowser;J" + "Lorg/cef/handler/CefPermissionRequestResult;)V", + jbrowser.get(), + static_cast(prompt_id), + jresult.get()); +} diff --git a/native/permission_handler.h b/native/permission_handler.h new file mode 100644 index 00000000..189cdbe3 --- /dev/null +++ b/native/permission_handler.h @@ -0,0 +1,37 @@ +#ifndef JCEF_NATIVE_PERMISSION_HANDLER_H_ +#define JCEF_NATIVE_PERMISSION_HANDLER_H_ +#pragma once + +#include +#include "include/cef_permission_handler.h" +#include "jni_scoped_helpers.h" + +class PermissionHandler : public CefPermissionHandler { + public: + PermissionHandler(JNIEnv* env, jobject handler); + + bool OnRequestMediaAccessPermission( + CefRefPtr browser, + CefRefPtr frame, + const CefString& requesting_origin, + uint32_t requested_permissions, + CefRefPtr callback) override; + + bool OnShowPermissionPrompt( + CefRefPtr browser, + uint64_t prompt_id, + const CefString& requesting_origin, + uint32_t requested_permissions, + CefRefPtr callback) override; + + void OnDismissPermissionPrompt( + CefRefPtr browser, + uint64_t prompt_id, + cef_permission_request_result_t result) override; + + protected: + ScopedJNIObjectGlobal handle_; + IMPLEMENT_REFCOUNTING(PermissionHandler); +}; + +#endif diff --git a/native/permission_prompt_callback.cpp b/native/permission_prompt_callback.cpp new file mode 100644 index 00000000..07d40c9a --- /dev/null +++ b/native/permission_prompt_callback.cpp @@ -0,0 +1,24 @@ +#include "permission_prompt_callback.h" + +#include "jni_scoped_helpers.h" +#include "jni_util.h" + +PermissionPromptCallback::PermissionPromptCallback(CefRefPtr callback) : callback_(callback) {} + +void PermissionPromptCallback::Continue(cef_permission_request_result_t result) { + if (callback_) + callback_->Continue(result); +} + +jobject NewJNIPermissionPromptCallback(JNIEnv* env, CefRefPtr callback) { + if (!callback) + return nullptr; + + jobject jcallback = NewJNIObject(env, "org/cef/callback/CefPermissionPromptCallback_N"); + if (!jcallback) + return nullptr; + + SetCefForJNIObject(env, jcallback, callback.get(), "CefPermissionPromptCallback"); + + return jcallback; +} diff --git a/native/permission_prompt_callback.h b/native/permission_prompt_callback.h new file mode 100644 index 00000000..506c8ff8 --- /dev/null +++ b/native/permission_prompt_callback.h @@ -0,0 +1,22 @@ +#ifndef JCEF_NATIVE_PERMISSION_PROMPT_CALLBACK_H_ +#define JCEF_NATIVE_PERMISSION_PROMPT_CALLBACK_H_ +#pragma once + +#include +#include "include/cef_permission_handler.h" +#include "include/cef_base.h" + +class PermissionPromptCallback : public CefBaseRefCounted { + public: + explicit PermissionPromptCallback(CefRefPtr callback); + + void Continue(cef_permission_request_result_t result); + + private: + CefRefPtr callback_; + IMPLEMENT_REFCOUNTING(PermissionPromptCallback); +}; + +jobject NewJNIPermissionPromptCallback(JNIEnv* env, CefRefPtr callback); + +#endif