From 85bda40d95c59676959ada7b742aadd3bdce1e7f Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Thu, 30 Jun 2022 07:31:18 +0000 Subject: [PATCH] Add CefPermissionHandler callback for media access (fixes issue #2582) --- BUILD.gn | 2 + cef_paths.gypi | 12 +- cef_paths2.gypi | 2 + include/capi/cef_client_capi.h | 10 +- include/capi/cef_media_access_handler_capi.h | 108 ++++ include/capi/cef_permission_handler_capi.h | 110 ++++ include/cef_api_hash.h | 8 +- include/cef_client.h | 9 + include/cef_permission_handler.h | 97 ++++ include/internal/cef_types.h | 32 +- .../browser/alloy/alloy_browser_host_impl.cc | 77 +-- libcef/browser/media_access_query.cc | 319 +++++++++++ libcef/browser/media_access_query.h | 33 ++ libcef_dll/cpptoc/client_cpptoc.cc | 20 +- .../cpptoc/media_access_callback_cpptoc.cc | 81 +++ .../cpptoc/media_access_callback_cpptoc.h | 38 ++ .../cpptoc/permission_handler_cpptoc.cc | 96 ++++ libcef_dll/cpptoc/permission_handler_cpptoc.h | 38 ++ libcef_dll/ctocpp/client_ctocpp.cc | 18 +- libcef_dll/ctocpp/client_ctocpp.h | 3 +- .../ctocpp/media_access_callback_ctocpp.cc | 71 +++ .../ctocpp/media_access_callback_ctocpp.h | 42 ++ .../ctocpp/permission_handler_ctocpp.cc | 89 +++ libcef_dll/ctocpp/permission_handler_ctocpp.h | 46 ++ libcef_dll/wrapper_types.h | 4 +- tests/cefclient/browser/client_handler.cc | 21 + tests/cefclient/browser/client_handler.h | 15 + tests/ceftests/client_app_delegates.cc | 5 + tests/ceftests/media_access_unittest.cc | 529 ++++++++++++++++++ 29 files changed, 1852 insertions(+), 83 deletions(-) create mode 100644 include/capi/cef_media_access_handler_capi.h create mode 100644 include/capi/cef_permission_handler_capi.h create mode 100644 include/cef_permission_handler.h create mode 100644 libcef/browser/media_access_query.cc create mode 100644 libcef/browser/media_access_query.h create mode 100644 libcef_dll/cpptoc/media_access_callback_cpptoc.cc create mode 100644 libcef_dll/cpptoc/media_access_callback_cpptoc.h create mode 100644 libcef_dll/cpptoc/permission_handler_cpptoc.cc create mode 100644 libcef_dll/cpptoc/permission_handler_cpptoc.h create mode 100644 libcef_dll/ctocpp/media_access_callback_ctocpp.cc create mode 100644 libcef_dll/ctocpp/media_access_callback_ctocpp.h create mode 100644 libcef_dll/ctocpp/permission_handler_ctocpp.cc create mode 100644 libcef_dll/ctocpp/permission_handler_ctocpp.h create mode 100644 tests/ceftests/media_access_unittest.cc diff --git a/BUILD.gn b/BUILD.gn index 883d6d798..59a9f7d5f 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -577,6 +577,8 @@ static_library("libcef_static") { "libcef/browser/javascript_dialog_manager.h", "libcef/browser/main_runner.cc", "libcef/browser/main_runner.h", + "libcef/browser/media_access_query.cc", + "libcef/browser/media_access_query.h", "libcef/browser/media_capture_devices_dispatcher.cc", "libcef/browser/media_capture_devices_dispatcher.h", "libcef/browser/media_router/media_route_impl.cc", diff --git a/cef_paths.gypi b/cef_paths.gypi index 7509e42f2..8aead0f27 100644 --- a/cef_paths.gypi +++ b/cef_paths.gypi @@ -8,7 +8,7 @@ # by hand. See the translator.README.txt file in the tools directory for # more information. # -# $hash=7ee0c6a789cc0574798332fe7c7cc10f862280af$ +# $hash=f8ae899cc69a48e9878fa4db2fcc3b7e54230062$ # { @@ -55,6 +55,7 @@ 'include/cef_origin_whitelist.h', 'include/cef_parser.h', 'include/cef_path_util.h', + 'include/cef_permission_handler.h', 'include/cef_print_handler.h', 'include/cef_print_settings.h', 'include/cef_process_message.h', @@ -154,6 +155,7 @@ 'include/capi/cef_origin_whitelist_capi.h', 'include/capi/cef_parser_capi.h', 'include/capi/cef_path_util_capi.h', + 'include/capi/cef_permission_handler_capi.h', 'include/capi/cef_print_handler_capi.h', 'include/capi/cef_print_settings_capi.h', 'include/capi/cef_process_message_capi.h', @@ -328,6 +330,8 @@ 'libcef_dll/cpptoc/list_value_cpptoc.h', 'libcef_dll/ctocpp/load_handler_ctocpp.cc', 'libcef_dll/ctocpp/load_handler_ctocpp.h', + 'libcef_dll/cpptoc/media_access_callback_cpptoc.cc', + 'libcef_dll/cpptoc/media_access_callback_cpptoc.h', 'libcef_dll/ctocpp/media_observer_ctocpp.cc', 'libcef_dll/ctocpp/media_observer_ctocpp.h', 'libcef_dll/cpptoc/media_route_cpptoc.cc', @@ -364,6 +368,8 @@ 'libcef_dll/ctocpp/views/panel_delegate_ctocpp.h', 'libcef_dll/ctocpp/pdf_print_callback_ctocpp.cc', 'libcef_dll/ctocpp/pdf_print_callback_ctocpp.h', + 'libcef_dll/ctocpp/permission_handler_ctocpp.cc', + 'libcef_dll/ctocpp/permission_handler_ctocpp.h', 'libcef_dll/cpptoc/post_data_cpptoc.cc', 'libcef_dll/cpptoc/post_data_cpptoc.h', 'libcef_dll/cpptoc/post_data_element_cpptoc.cc', @@ -636,6 +642,8 @@ 'libcef_dll/ctocpp/list_value_ctocpp.h', 'libcef_dll/cpptoc/load_handler_cpptoc.cc', 'libcef_dll/cpptoc/load_handler_cpptoc.h', + 'libcef_dll/ctocpp/media_access_callback_ctocpp.cc', + 'libcef_dll/ctocpp/media_access_callback_ctocpp.h', 'libcef_dll/cpptoc/media_observer_cpptoc.cc', 'libcef_dll/cpptoc/media_observer_cpptoc.h', 'libcef_dll/ctocpp/media_route_ctocpp.cc', @@ -672,6 +680,8 @@ 'libcef_dll/cpptoc/views/panel_delegate_cpptoc.h', 'libcef_dll/cpptoc/pdf_print_callback_cpptoc.cc', 'libcef_dll/cpptoc/pdf_print_callback_cpptoc.h', + 'libcef_dll/cpptoc/permission_handler_cpptoc.cc', + 'libcef_dll/cpptoc/permission_handler_cpptoc.h', 'libcef_dll/ctocpp/post_data_ctocpp.cc', 'libcef_dll/ctocpp/post_data_ctocpp.h', 'libcef_dll/ctocpp/post_data_element_ctocpp.cc', diff --git a/cef_paths2.gypi b/cef_paths2.gypi index e4bf1c457..8d8f8ae76 100644 --- a/cef_paths2.gypi +++ b/cef_paths2.gypi @@ -475,6 +475,7 @@ 'tests/ceftests/image_util.h', 'tests/ceftests/jsdialog_unittest.cc', 'tests/ceftests/life_span_unittest.cc', + 'tests/ceftests/media_access_unittest.cc', 'tests/ceftests/message_router_unittest.cc', 'tests/ceftests/navigation_unittest.cc', 'tests/ceftests/os_rendering_unittest.cc', @@ -562,6 +563,7 @@ 'tests/ceftests/cors_unittest.cc', 'tests/ceftests/dom_unittest.cc', 'tests/ceftests/frame_unittest.cc', + 'tests/ceftests/media_access_unittest.cc', 'tests/ceftests/message_router_unittest.cc', 'tests/ceftests/navigation_unittest.cc', 'tests/ceftests/pdf_viewer_unittest.cc', diff --git a/include/capi/cef_client_capi.h b/include/capi/cef_client_capi.h index 9f996da2c..ece0f24d6 100644 --- a/include/capi/cef_client_capi.h +++ b/include/capi/cef_client_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=7df3c13b75072c2ad5061cd3a344811222798903$ +// $hash=7e03d64dfcefc287c083e35e5ef9b3fa4f762b1b$ // #ifndef CEF_INCLUDE_CAPI_CEF_CLIENT_CAPI_H_ @@ -55,6 +55,7 @@ #include "include/capi/cef_keyboard_handler_capi.h" #include "include/capi/cef_life_span_handler_capi.h" #include "include/capi/cef_load_handler_capi.h" +#include "include/capi/cef_permission_handler_capi.h" #include "include/capi/cef_print_handler_capi.h" #include "include/capi/cef_process_message_capi.h" #include "include/capi/cef_render_handler_capi.h" @@ -139,6 +140,13 @@ typedef struct _cef_client_t { struct _cef_frame_handler_t*(CEF_CALLBACK* get_frame_handler)( struct _cef_client_t* self); + /// + // Return the handler for permission requests. If no handler is provided + // requests be denied by default. + /// + struct _cef_permission_handler_t*(CEF_CALLBACK* get_permission_handler)( + struct _cef_client_t* self); + /// // Return the handler for JavaScript dialogs. If no handler is provided the // default implementation will be used. diff --git a/include/capi/cef_media_access_handler_capi.h b/include/capi/cef_media_access_handler_capi.h new file mode 100644 index 000000000..3b8c9a821 --- /dev/null +++ b/include/capi/cef_media_access_handler_capi.h @@ -0,0 +1,108 @@ +// Copyright (c) 2022 Marshall A. Greenblatt. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the name Chromium Embedded +// Framework nor the names of its contributors may be used to endorse +// or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool and should not edited +// by hand. See the translator.README.txt file in the tools directory for +// more information. +// +// $hash=91101808168ec0faf1f39b1924579e31478a6616$ +// + +#ifndef CEF_INCLUDE_CAPI_CEF_MEDIA_ACCESS_HANDLER_CAPI_H_ +#define CEF_INCLUDE_CAPI_CEF_MEDIA_ACCESS_HANDLER_CAPI_H_ +#pragma once + +#include "include/capi/cef_base_capi.h" +#include "include/capi/cef_browser_capi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// +// Callback structure used for asynchronous continuation of media access +// permission requests. +/// +typedef struct _cef_media_access_callback_t { + /// + // Base structure. + /// + cef_base_ref_counted_t base; + + /// + // Call to allow or deny media access. If this callback was initiated in + // response to a getUserMedia (indicated by + // CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE and/or + // CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE being set) the + // |allowed_permissions| are required to match those given in + // |required_permissions| in the OnRequestMediaAccessPermission. + /// + void(CEF_CALLBACK* cont)(struct _cef_media_access_callback_t* self, + int allowed_permissions); + + /// + // Cancel the media access request. + /// + void(CEF_CALLBACK* cancel)(struct _cef_media_access_callback_t* self); +} cef_media_access_callback_t; + +/// +// Implement this structure to handle events related to media access permission +// requests. The functions of this structure will be called on the browser +// process UI thread. +/// +typedef struct _cef_media_access_handler_t { + /// + // Base structure. + /// + cef_base_ref_counted_t base; + + /// + // Called when a page requests permission to access media. |requesting_url| is + // the URL requesting permission. Return true (1) and call + // cef_media_access_callback_t::cont() either in this function or at a later + // time to continue or cancel the request. Return false (0) to cancel the + // request immediately. + /// + int(CEF_CALLBACK* on_request_media_access_permission)( + struct _cef_media_access_handler_t* self, + struct _cef_browser_t* browser, + struct _cef_frame_t* frame, + const cef_string_t* requesting_url, + int32_t requested_permissions, + struct _cef_media_access_callback_t* callback); +} cef_media_access_handler_t; + +#ifdef __cplusplus +} +#endif + +#endif // CEF_INCLUDE_CAPI_CEF_MEDIA_ACCESS_HANDLER_CAPI_H_ diff --git a/include/capi/cef_permission_handler_capi.h b/include/capi/cef_permission_handler_capi.h new file mode 100644 index 000000000..a443b3d0b --- /dev/null +++ b/include/capi/cef_permission_handler_capi.h @@ -0,0 +1,110 @@ +// Copyright (c) 2022 Marshall A. Greenblatt. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the name Chromium Embedded +// Framework nor the names of its contributors may be used to endorse +// or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool and should not edited +// by hand. See the translator.README.txt file in the tools directory for +// more information. +// +// $hash=e25fb66e356e1f01d67cb86433382b3318e9778d$ +// + +#ifndef CEF_INCLUDE_CAPI_CEF_PERMISSION_HANDLER_CAPI_H_ +#define CEF_INCLUDE_CAPI_CEF_PERMISSION_HANDLER_CAPI_H_ +#pragma once + +#include "include/capi/cef_base_capi.h" +#include "include/capi/cef_browser_capi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// +// Callback structure used for asynchronous continuation of media access +// permission requests. +/// +typedef struct _cef_media_access_callback_t { + /// + // Base structure. + /// + cef_base_ref_counted_t base; + + /// + // Call to allow or deny media access. If this callback was initiated in + // response to a getUserMedia (indicated by + // CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE and/or + // CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE being set) then + // |allowed_permissions| must match |required_permissions| passed to + // OnRequestMediaAccessPermission. + /// + void(CEF_CALLBACK* cont)(struct _cef_media_access_callback_t* self, + uint32 allowed_permissions); + + /// + // Cancel the media access request. + /// + void(CEF_CALLBACK* cancel)(struct _cef_media_access_callback_t* self); +} cef_media_access_callback_t; + +/// +// Implement this structure to handle events related to permission requests. The +// functions of this structure will be called on the browser process UI thread. +/// +typedef struct _cef_permission_handler_t { + /// + // Base structure. + /// + cef_base_ref_counted_t base; + + /// + // Called when a page requests permission to access media. |requesting_url| is + // the URL requesting permission. |requested_permissions| is a combination of + // values from cef_media_access_permission_types_t that represent the + // requested permissions. Return true (1) and call + // cef_media_access_callback_t::cont() either in this function or at a later + // time to continue or cancel the request. Return false (0) to cancel the + // request immediately. This function will not be called if the "--enable- + // media-stream" command-line switch is used to grant all permissions. + /// + int(CEF_CALLBACK* on_request_media_access_permission)( + struct _cef_permission_handler_t* self, + struct _cef_browser_t* browser, + struct _cef_frame_t* frame, + const cef_string_t* requesting_url, + uint32 requested_permissions, + struct _cef_media_access_callback_t* callback); +} cef_permission_handler_t; + +#ifdef __cplusplus +} +#endif + +#endif // CEF_INCLUDE_CAPI_CEF_PERMISSION_HANDLER_CAPI_H_ diff --git a/include/cef_api_hash.h b/include/cef_api_hash.h index 387eb3536..b9228316c 100644 --- a/include/cef_api_hash.h +++ b/include/cef_api_hash.h @@ -42,13 +42,13 @@ // way that may cause binary incompatibility with other builds. The universal // hash value will change if any platform is affected whereas the platform hash // values will change only if that particular platform is affected. -#define CEF_API_HASH_UNIVERSAL "eb7cc5b2fb5c87d0f580e8df1bb609729fc731d0" +#define CEF_API_HASH_UNIVERSAL "f0b6806a3e15f849013e58992ef11c99e5cfeb60" #if defined(OS_WIN) -#define CEF_API_HASH_PLATFORM "b61f8f06751ed7231b2036b71d714d074feadf5b" +#define CEF_API_HASH_PLATFORM "55ba6603a77fcbf93d58a44fbeae9ea52d000153" #elif defined(OS_MAC) -#define CEF_API_HASH_PLATFORM "d4ac1390d4c202e26633a0c95a560171ad05e4fe" +#define CEF_API_HASH_PLATFORM "4028739577ee54f7049f354acd724ecee071aa22" #elif defined(OS_LINUX) -#define CEF_API_HASH_PLATFORM "0d9fe8afb1b67a6098e08f7fe6d0ac7e70ce29e6" +#define CEF_API_HASH_PLATFORM "7464b32d2ed43a15d68a651199fda0c0fe040a8d" #endif #ifdef __cplusplus diff --git a/include/cef_client.h b/include/cef_client.h index 06266a577..fd3546627 100644 --- a/include/cef_client.h +++ b/include/cef_client.h @@ -53,6 +53,7 @@ #include "include/cef_keyboard_handler.h" #include "include/cef_life_span_handler.h" #include "include/cef_load_handler.h" +#include "include/cef_permission_handler.h" #include "include/cef_print_handler.h" #include "include/cef_process_message.h" #include "include/cef_render_handler.h" @@ -132,6 +133,14 @@ class CefClient : public virtual CefBaseRefCounted { /*--cef()--*/ virtual CefRefPtr GetFrameHandler() { return nullptr; } + /// + // Return the handler for permission requests. + /// + /*--cef()--*/ + virtual CefRefPtr GetPermissionHandler() { + return nullptr; + } + /// // Return the handler for JavaScript dialogs. If no handler is provided the // default implementation will be used. diff --git a/include/cef_permission_handler.h b/include/cef_permission_handler.h new file mode 100644 index 000000000..870e9d858 --- /dev/null +++ b/include/cef_permission_handler.h @@ -0,0 +1,97 @@ +// Copyright (c) 2022 Marshall A. Greenblatt. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the name Chromium Embedded +// Framework nor the names of its contributors may be used to endorse +// or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// --------------------------------------------------------------------------- +// +// The contents of this file must follow a specific format in order to +// support the CEF translator tool. See the translator.README.txt file in the +// tools directory for more information. +// + +#ifndef CEF_INCLUDE_CEF_PERMISSION_HANDLER_H_ +#define CEF_INCLUDE_CEF_PERMISSION_HANDLER_H_ +#pragma once + +#include "include/cef_base.h" +#include "include/cef_browser.h" + +/// +// Callback interface used for asynchronous continuation of media access +// permission requests. +/// +/*--cef(source=library)--*/ +class CefMediaAccessCallback : public virtual CefBaseRefCounted { + public: + /// + // Call to allow or deny media access. If this callback was initiated in + // response to a getUserMedia (indicated by + // CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE and/or + // CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE being set) then + // |allowed_permissions| must match |required_permissions| passed to + // OnRequestMediaAccessPermission. + /// + /*--cef(capi_name=cont)--*/ + virtual void Continue(uint32 allowed_permissions) = 0; + + /// + // Cancel the media access request. + /// + /*--cef()--*/ + virtual void Cancel() = 0; +}; + +/// +// Implement this interface to handle events related to permission requests. The +// methods of this class will be called on the browser process UI thread. +/// +/*--cef(source=client)--*/ +class CefPermissionHandler : public virtual CefBaseRefCounted { + public: + /// + // Called when a page requests permission to access media. |requesting_url| is + // the URL requesting permission. |requested_permissions| is a combination of + // values from cef_media_access_permission_types_t that represent the + // requested permissions. Return true and call + // CefMediaAccessCallback::Continue() either in this method or at a later time + // to continue or cancel the request. Return false to cancel the request + // immediately. This method will not be called if the "--enable-media-stream" + // command-line switch is used to grant all permissions. + /// + /*--cef()--*/ + virtual bool OnRequestMediaAccessPermission( + CefRefPtr browser, + CefRefPtr frame, + const CefString& requesting_url, + uint32 requested_permissions, + CefRefPtr callback) { + return false; + } +}; + +#endif // CEF_INCLUDE_CEF_PERMISSION_HANDLER_H_ diff --git a/include/internal/cef_types.h b/include/internal/cef_types.h index 98c19dfaf..5af1f0746 100644 --- a/include/internal/cef_types.h +++ b/include/internal/cef_types.h @@ -3262,7 +3262,7 @@ typedef struct _cef_touch_handle_state_t { // Combination of cef_touch_handle_state_flags_t values indicating what state // is set. /// - uint32_t flags; + uint32 flags; /// // Enabled state. Only set if |flags| contains CEF_THS_FLAG_ENABLED. @@ -3287,6 +3287,36 @@ typedef struct _cef_touch_handle_state_t { float alpha; } cef_touch_handle_state_t; +/// +// Media access permissions used by OnRequestMediaAccessPermission. +/// +typedef enum { + /// + // No permission. + /// + CEF_MEDIA_PERMISSION_NONE = 0, + + /// + // Device audio capture permission. + /// + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE = 1 << 0, + + /// + // Device video capture permission. + /// + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE = 1 << 1, + + /// + // Desktop audio capture permission. + /// + CEF_MEDIA_PERMISSION_DESKTOP_AUDIO_CAPTURE = 1 << 2, + + /// + // Desktop video capture permission. + /// + CEF_MEDIA_PERMISSION_DESKTOP_VIDEO_CAPTURE = 1 << 3, +} cef_media_access_permission_types_t; + #ifdef __cplusplus } #endif diff --git a/libcef/browser/alloy/alloy_browser_host_impl.cc b/libcef/browser/alloy/alloy_browser_host_impl.cc index 3173229ae..f6802b1de 100644 --- a/libcef/browser/alloy/alloy_browser_host_impl.cc +++ b/libcef/browser/alloy/alloy_browser_host_impl.cc @@ -17,7 +17,7 @@ #include "libcef/browser/browser_platform_delegate.h" #include "libcef/browser/context.h" #include "libcef/browser/devtools/devtools_manager.h" -#include "libcef/browser/media_capture_devices_dispatcher.h" +#include "libcef/browser/media_access_query.h" #include "libcef/browser/osr/osr_util.h" #include "libcef/browser/request_context_impl.h" #include "libcef/browser/thread_util.h" @@ -51,7 +51,6 @@ #include "extensions/common/constants.h" #include "extensions/common/extension.h" #include "net/base/net_errors.h" -#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h" #include "ui/events/base_event_utils.h" using content::KeyboardEventProcessingResult; @@ -1299,82 +1298,16 @@ void AlloyBrowserHostImpl::RequestMediaAccessPermission( content::WebContents* web_contents, const content::MediaStreamRequest& request, content::MediaResponseCallback callback) { - CEF_REQUIRE_UIT(); - - blink::MediaStreamDevices audio_devices; - blink::MediaStreamDevices video_devices; - - const base::CommandLine* command_line = - base::CommandLine::ForCurrentProcess(); - if (!command_line->HasSwitch(switches::kEnableMediaStream)) { - // Cancel the request. - std::move(callback).Run( - blink::mojom::StreamDevicesSet(), - blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED, - std::unique_ptr()); - return; - } - - // Based on chrome/browser/media/media_stream_devices_controller.cc - bool microphone_requested = - (request.audio_type == - blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE); - bool webcam_requested = (request.video_type == - blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE); - bool screen_requested = - (request.video_type == - blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE); - if (microphone_requested || webcam_requested || screen_requested) { - // Pick the desired device or fall back to the first available of the - // given type. - if (microphone_requested) { - CefMediaCaptureDevicesDispatcher::GetInstance()->GetRequestedDevice( - request.requested_audio_device_id, true, false, &audio_devices); - } - if (webcam_requested) { - CefMediaCaptureDevicesDispatcher::GetInstance()->GetRequestedDevice( - request.requested_video_device_id, false, true, &video_devices); - } - if (screen_requested) { - content::DesktopMediaID media_id; - if (request.requested_video_device_id.empty()) { - media_id = - content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN, - -1 /* webrtc::kFullDesktopScreenId */); - } else { - media_id = - content::DesktopMediaID::Parse(request.requested_video_device_id); - } - video_devices.push_back(blink::MediaStreamDevice( - blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE, - media_id.ToString(), "Screen")); - } - } - - blink::mojom::StreamDevicesSet stream_devices_set; - stream_devices_set.stream_devices.emplace_back( - blink::mojom::StreamDevices::New()); - blink::mojom::StreamDevices& devices = *stream_devices_set.stream_devices[0]; - - // At most one audio device and one video device can be used in a stream. - if (!audio_devices.empty()) - devices.audio_device = audio_devices.front(); - if (!video_devices.empty()) - devices.video_device = video_devices.front(); - - std::move(callback).Run(stream_devices_set, - blink::mojom::MediaStreamRequestResult::OK, - std::unique_ptr()); + media_access_query::RequestMediaAccessPermission(this, request, + std::move(callback)); } bool AlloyBrowserHostImpl::CheckMediaAccessPermission( content::RenderFrameHost* render_frame_host, const GURL& security_origin, blink::mojom::MediaStreamType type) { - // Check media access permission without prompting the user. - const base::CommandLine* command_line = - base::CommandLine::ForCurrentProcess(); - return command_line->HasSwitch(switches::kEnableMediaStream); + return media_access_query::CheckMediaAccessPermission(this, render_frame_host, + security_origin, type); } bool AlloyBrowserHostImpl::IsNeverComposited( diff --git a/libcef/browser/media_access_query.cc b/libcef/browser/media_access_query.cc new file mode 100644 index 000000000..73a0a83e9 --- /dev/null +++ b/libcef/browser/media_access_query.cc @@ -0,0 +1,319 @@ +// Copyright 2022 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 "libcef/browser/media_access_query.h" + +#include "include/cef_permission_handler.h" +#include "libcef/browser/browser_host_base.h" +#include "libcef/browser/media_capture_devices_dispatcher.h" +#include "libcef/common/cef_switches.h" + +#include "base/command_line.h" +#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h" + +namespace media_access_query { + +namespace { + +class CefMediaAccessQuery { + public: + using CallbackType = content::MediaResponseCallback; + + explicit CefMediaAccessQuery(const content::MediaStreamRequest& request, + CallbackType&& callback) + : request_(request), callback_(std::move(callback)) {} + + CefMediaAccessQuery(CefMediaAccessQuery&& query) + : request_(query.request_), callback_(std::move(query.callback_)) {} + CefMediaAccessQuery& operator=(CefMediaAccessQuery&& query) { + request_ = query.request_; + callback_ = std::move(query.callback_); + return *this; + } + + CefMediaAccessQuery(const CefMediaAccessQuery&) = delete; + CefMediaAccessQuery& operator=(const CefMediaAccessQuery&) = delete; + + bool is_null() const { return callback_.is_null(); } + + uint32_t requested_permissions() const { + int requested_permissions = CEF_MEDIA_PERMISSION_NONE; + if (device_audio_requested()) { + requested_permissions |= CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE; + } + if (device_video_requested()) { + requested_permissions |= CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE; + } + if (desktop_audio_requested()) { + requested_permissions |= CEF_MEDIA_PERMISSION_DESKTOP_AUDIO_CAPTURE; + } + if (desktop_video_requested()) { + requested_permissions |= CEF_MEDIA_PERMISSION_DESKTOP_VIDEO_CAPTURE; + } + return requested_permissions; + } + + void ExecuteCallback(uint32_t allowed_permissions) { + CEF_REQUIRE_UIT(); + + blink::mojom::MediaStreamRequestResult result; + blink::mojom::StreamDevicesSetPtr stream_devices_set; + + if (allowed_permissions == CEF_MEDIA_PERMISSION_NONE) { + result = blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED; + stream_devices_set = blink::mojom::StreamDevicesSet::New(); + } else { + bool error = false; + if (allowed_permissions == requested_permissions()) { + stream_devices_set = GetRequestedMediaDevices(); + } else { + stream_devices_set = GetAllowedMediaDevices(allowed_permissions, error); + } + result = error ? blink::mojom::MediaStreamRequestResult::INVALID_STATE + : blink::mojom::MediaStreamRequestResult::OK; + } + + std::move(callback_).Run(*stream_devices_set, result, + std::unique_ptr()); + } + + private: + bool device_audio_requested() const { + return request_.audio_type == + blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE; + } + + bool device_video_requested() const { + return request_.video_type == + blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE; + } + + bool desktop_audio_requested() const { + return (request_.audio_type == + blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE) || + (request_.audio_type == + blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE); + } + + bool desktop_video_requested() const { + return (request_.video_type == + blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE) || + (request_.video_type == + blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE); + } + + blink::mojom::StreamDevicesSetPtr GetRequestedMediaDevices() const { + CEF_REQUIRE_UIT(); + + blink::MediaStreamDevices audio_devices; + blink::MediaStreamDevices video_devices; + + if (device_audio_requested()) { + // Pick the desired device or fall back to the first available of the + // given type. + CefMediaCaptureDevicesDispatcher::GetInstance()->GetRequestedDevice( + request_.requested_audio_device_id, true, false, &audio_devices); + } + + if (device_video_requested()) { + // Pick the desired device or fall back to the first available of the + // given type. + CefMediaCaptureDevicesDispatcher::GetInstance()->GetRequestedDevice( + request_.requested_video_device_id, false, true, &video_devices); + } + + if (desktop_audio_requested()) { + audio_devices.push_back(blink::MediaStreamDevice( + request_.audio_type, "loopback", "System Audio")); + } + + if (desktop_video_requested()) { + content::DesktopMediaID media_id; + if (request_.requested_video_device_id.empty()) { + media_id = + content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN, + -1 /* webrtc::kFullDesktopScreenId */); + } else { + media_id = + content::DesktopMediaID::Parse(request_.requested_video_device_id); + } + video_devices.push_back(blink::MediaStreamDevice( + request_.video_type, media_id.ToString(), "Screen")); + } + + blink::mojom::StreamDevicesSetPtr stream_devices_set = + blink::mojom::StreamDevicesSet::New(); + stream_devices_set->stream_devices.emplace_back( + blink::mojom::StreamDevices::New()); + blink::mojom::StreamDevices& devices = + *stream_devices_set->stream_devices[0]; + + // At most one audio device and one video device can be used in a stream. + if (!audio_devices.empty()) + devices.audio_device = audio_devices.front(); + if (!video_devices.empty()) + devices.video_device = video_devices.front(); + + return stream_devices_set; + } + + blink::mojom::StreamDevicesSetPtr GetAllowedMediaDevices( + uint32_t allowed_permissions, + bool& error) { + error = false; + + const auto req_permissions = requested_permissions(); + + const bool device_audio_allowed = + allowed_permissions & CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE; + const bool device_video_allowed = + allowed_permissions & CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE; + const bool desktop_audio_allowed = + allowed_permissions & CEF_MEDIA_PERMISSION_DESKTOP_AUDIO_CAPTURE; + const bool desktop_video_allowed = + allowed_permissions & CEF_MEDIA_PERMISSION_DESKTOP_VIDEO_CAPTURE; + + blink::mojom::StreamDevicesSetPtr stream_devices_set; + + // getDisplayMedia must always request video + if (desktop_video_requested() && + (!desktop_video_allowed && desktop_audio_allowed)) { + LOG(WARNING) << "Response to getDisplayMedia is not allowed to only " + "return Audio"; + error = true; + } else if (!desktop_video_requested() && + req_permissions != allowed_permissions) { + LOG(WARNING) + << "Response to getUserMedia must match requested permissions (" + << req_permissions << " vs " << allowed_permissions << ")"; + error = true; + } + + if (error) { + stream_devices_set = blink::mojom::StreamDevicesSet::New(); + } else { + if (!device_audio_allowed && !desktop_audio_allowed) { + request_.audio_type = blink::mojom::MediaStreamType::NO_SERVICE; + } + if (!device_video_allowed && !desktop_video_allowed) { + request_.video_type = blink::mojom::MediaStreamType::NO_SERVICE; + } + stream_devices_set = GetRequestedMediaDevices(); + } + + return stream_devices_set; + } + + content::MediaStreamRequest request_; + CallbackType callback_; +}; + +class CefMediaAccessCallbackImpl : public CefMediaAccessCallback { + public: + using CallbackType = CefMediaAccessQuery; + + explicit CefMediaAccessCallbackImpl(CallbackType&& callback) + : callback_(std::move(callback)) {} + + CefMediaAccessCallbackImpl(const CefMediaAccessCallbackImpl&) = delete; + CefMediaAccessCallbackImpl& operator=(const CefMediaAccessCallbackImpl&) = + delete; + + ~CefMediaAccessCallbackImpl() override { + if (!callback_.is_null()) { + // The callback is still pending. Cancel it now. + if (CEF_CURRENTLY_ON_UIT()) { + RunNow(std::move(callback_), CEF_MEDIA_PERMISSION_NONE); + } else { + CEF_POST_TASK( + CEF_UIT, + base::BindOnce(&CefMediaAccessCallbackImpl::RunNow, + std::move(callback_), CEF_MEDIA_PERMISSION_NONE)); + } + } + } + + void Continue(uint32_t allowed_permissions) override { + if (CEF_CURRENTLY_ON_UIT()) { + if (!callback_.is_null()) { + RunNow(std::move(callback_), allowed_permissions); + } + } else { + CEF_POST_TASK(CEF_UIT, + base::BindOnce(&CefMediaAccessCallbackImpl::Continue, this, + allowed_permissions)); + } + } + + void Cancel() override { Continue(CEF_MEDIA_PERMISSION_NONE); } + + [[nodiscard]] CallbackType Disconnect() { return std::move(callback_); } + + private: + static void RunNow(CallbackType callback, uint32_t allowed_permissions) { + callback.ExecuteCallback(allowed_permissions); + } + + CallbackType callback_; + + IMPLEMENT_REFCOUNTING(CefMediaAccessCallbackImpl); +}; + +bool CheckCommandLinePermission() { + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); + return command_line->HasSwitch(switches::kEnableMediaStream); +} + +} // namespace + +bool CheckMediaAccessPermission(CefBrowserHostBase* browser, + content::RenderFrameHost* render_frame_host, + const GURL& security_origin, + blink::mojom::MediaStreamType type) { + // Always allowed here. RequestMediaAccessPermission will be called. + return true; +} + +void RequestMediaAccessPermission(CefBrowserHostBase* browser, + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback) { + CEF_REQUIRE_UIT(); + + CefMediaAccessQuery query(request, std::move(callback)); + + if (CheckCommandLinePermission()) { + // Allow all requested permissions. + query.ExecuteCallback(query.requested_permissions()); + return; + } + + bool proceed = false; + + if (auto client = browser->GetClient()) { + if (auto handler = client->GetPermissionHandler()) { + const auto requested_permissions = query.requested_permissions(); + CefRefPtr callbackImpl( + new CefMediaAccessCallbackImpl(std::move(query))); + + auto frame = + browser->GetFrameForGlobalId(content::GlobalRenderFrameHostId( + request.render_process_id, request.render_frame_id)); + if (!frame) + frame = browser->GetMainFrame(); + proceed = handler->OnRequestMediaAccessPermission( + browser, frame, request.security_origin.spec(), requested_permissions, + callbackImpl.get()); + if (!proceed) + query = callbackImpl->Disconnect(); + } + } + + if (!proceed && !query.is_null()) { + // Disallow access by default. + query.ExecuteCallback(CEF_MEDIA_PERMISSION_NONE); + } +} + +} // namespace media_access_query diff --git a/libcef/browser/media_access_query.h b/libcef/browser/media_access_query.h new file mode 100644 index 000000000..70283b438 --- /dev/null +++ b/libcef/browser/media_access_query.h @@ -0,0 +1,33 @@ +// Copyright 2022 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. + +#ifndef CEF_LIBCEF_BROWSER_MEDIA_ACCESS_QUERY_H_ +#define CEF_LIBCEF_BROWSER_MEDIA_ACCESS_QUERY_H_ +#pragma once + +#include "content/public/browser/media_stream_request.h" + +namespace content { +class RenderFrameHost; +} + +class CefBrowserHostBase; +class GURL; + +namespace media_access_query { + +// Called from WebContentsDelegate::CheckMediaAccessPermission. +bool CheckMediaAccessPermission(CefBrowserHostBase* browser, + content::RenderFrameHost* render_frame_host, + const GURL& security_origin, + blink::mojom::MediaStreamType type); + +// Called from WebContentsDelegate::RequestMediaAccessPermission. +void RequestMediaAccessPermission(CefBrowserHostBase* browser, + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback); + +} // namespace media_access_query + +#endif // CEF_LIBCEF_BROWSER_MEDIA_ACCESS_QUERY_H_ diff --git a/libcef_dll/cpptoc/client_cpptoc.cc b/libcef_dll/cpptoc/client_cpptoc.cc index 919370adb..f1b5694f8 100644 --- a/libcef_dll/cpptoc/client_cpptoc.cc +++ b/libcef_dll/cpptoc/client_cpptoc.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=b299c22d4f97022efefefeb17a7848b0b44d4af3$ +// $hash=6af3f901e4429197d0e47b15a68e980ea574b256$ // #include "libcef_dll/cpptoc/client_cpptoc.h" @@ -27,6 +27,7 @@ #include "libcef_dll/cpptoc/keyboard_handler_cpptoc.h" #include "libcef_dll/cpptoc/life_span_handler_cpptoc.h" #include "libcef_dll/cpptoc/load_handler_cpptoc.h" +#include "libcef_dll/cpptoc/permission_handler_cpptoc.h" #include "libcef_dll/cpptoc/print_handler_cpptoc.h" #include "libcef_dll/cpptoc/render_handler_cpptoc.h" #include "libcef_dll/cpptoc/request_handler_cpptoc.h" @@ -198,6 +199,22 @@ client_get_frame_handler(struct _cef_client_t* self) { return CefFrameHandlerCppToC::Wrap(_retval); } +struct _cef_permission_handler_t* CEF_CALLBACK +client_get_permission_handler(struct _cef_client_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return NULL; + + // Execute + CefRefPtr _retval = + CefClientCppToC::Get(self)->GetPermissionHandler(); + + // Return type: refptr_same + return CefPermissionHandlerCppToC::Wrap(_retval); +} + struct _cef_jsdialog_handler_t* CEF_CALLBACK client_get_jsdialog_handler(struct _cef_client_t* self) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -358,6 +375,7 @@ CefClientCppToC::CefClientCppToC() { GetStruct()->get_find_handler = client_get_find_handler; GetStruct()->get_focus_handler = client_get_focus_handler; GetStruct()->get_frame_handler = client_get_frame_handler; + GetStruct()->get_permission_handler = client_get_permission_handler; GetStruct()->get_jsdialog_handler = client_get_jsdialog_handler; GetStruct()->get_keyboard_handler = client_get_keyboard_handler; GetStruct()->get_life_span_handler = client_get_life_span_handler; diff --git a/libcef_dll/cpptoc/media_access_callback_cpptoc.cc b/libcef_dll/cpptoc/media_access_callback_cpptoc.cc new file mode 100644 index 000000000..a80be51db --- /dev/null +++ b/libcef_dll/cpptoc/media_access_callback_cpptoc.cc @@ -0,0 +1,81 @@ +// Copyright (c) 2022 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=dd3f62153541c74c294da2a33265a96491d17f2b$ +// + +#include "libcef_dll/cpptoc/media_access_callback_cpptoc.h" +#include "libcef_dll/shutdown_checker.h" + +namespace { + +// MEMBER FUNCTIONS - Body may be edited by hand. + +void CEF_CALLBACK +media_access_callback_cont(struct _cef_media_access_callback_t* self, + uint32 allowed_permissions) { + shutdown_checker::AssertNotShutdown(); + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + + // Execute + CefMediaAccessCallbackCppToC::Get(self)->Continue(allowed_permissions); +} + +void CEF_CALLBACK +media_access_callback_cancel(struct _cef_media_access_callback_t* self) { + shutdown_checker::AssertNotShutdown(); + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + + // Execute + CefMediaAccessCallbackCppToC::Get(self)->Cancel(); +} + +} // namespace + +// CONSTRUCTOR - Do not edit by hand. + +CefMediaAccessCallbackCppToC::CefMediaAccessCallbackCppToC() { + GetStruct()->cont = media_access_callback_cont; + GetStruct()->cancel = media_access_callback_cancel; +} + +// DESTRUCTOR - Do not edit by hand. + +CefMediaAccessCallbackCppToC::~CefMediaAccessCallbackCppToC() { + shutdown_checker::AssertNotShutdown(); +} + +template <> +CefRefPtr CefCppToCRefCounted< + CefMediaAccessCallbackCppToC, + CefMediaAccessCallback, + cef_media_access_callback_t>::UnwrapDerived(CefWrapperType type, + cef_media_access_callback_t* + s) { + NOTREACHED() << "Unexpected class type: " << type; + return nullptr; +} + +template <> +CefWrapperType CefCppToCRefCounted::kWrapperType = + WT_MEDIA_ACCESS_CALLBACK; diff --git a/libcef_dll/cpptoc/media_access_callback_cpptoc.h b/libcef_dll/cpptoc/media_access_callback_cpptoc.h new file mode 100644 index 000000000..1332c51d6 --- /dev/null +++ b/libcef_dll/cpptoc/media_access_callback_cpptoc.h @@ -0,0 +1,38 @@ +// Copyright (c) 2022 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=0adf2c32c67b7480fa4c544c5c570674d6917f7f$ +// + +#ifndef CEF_LIBCEF_DLL_CPPTOC_MEDIA_ACCESS_CALLBACK_CPPTOC_H_ +#define CEF_LIBCEF_DLL_CPPTOC_MEDIA_ACCESS_CALLBACK_CPPTOC_H_ +#pragma once + +#if !defined(BUILDING_CEF_SHARED) +#error This file can be included DLL-side only +#endif + +#include "include/capi/cef_permission_handler_capi.h" +#include "include/cef_permission_handler.h" +#include "libcef_dll/cpptoc/cpptoc_ref_counted.h" + +// Wrap a C++ class with a C structure. +// This class may be instantiated and accessed DLL-side only. +class CefMediaAccessCallbackCppToC + : public CefCppToCRefCounted { + public: + CefMediaAccessCallbackCppToC(); + virtual ~CefMediaAccessCallbackCppToC(); +}; + +#endif // CEF_LIBCEF_DLL_CPPTOC_MEDIA_ACCESS_CALLBACK_CPPTOC_H_ diff --git a/libcef_dll/cpptoc/permission_handler_cpptoc.cc b/libcef_dll/cpptoc/permission_handler_cpptoc.cc new file mode 100644 index 000000000..3f8dd5ae4 --- /dev/null +++ b/libcef_dll/cpptoc/permission_handler_cpptoc.cc @@ -0,0 +1,96 @@ +// Copyright (c) 2022 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=9c8d3c2295998f7a976967aa7f4b6a6da7c5c53d$ +// + +#include "libcef_dll/cpptoc/permission_handler_cpptoc.h" +#include "libcef_dll/ctocpp/browser_ctocpp.h" +#include "libcef_dll/ctocpp/frame_ctocpp.h" +#include "libcef_dll/ctocpp/media_access_callback_ctocpp.h" +#include "libcef_dll/shutdown_checker.h" + +namespace { + +// MEMBER FUNCTIONS - Body may be edited by hand. + +int CEF_CALLBACK permission_handler_on_request_media_access_permission( + struct _cef_permission_handler_t* self, + cef_browser_t* browser, + cef_frame_t* frame, + const cef_string_t* requesting_url, + uint32 requested_permissions, + cef_media_access_callback_t* callback) { + shutdown_checker::AssertNotShutdown(); + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + // Verify param: browser; type: refptr_diff + DCHECK(browser); + if (!browser) + return 0; + // Verify param: frame; type: refptr_diff + DCHECK(frame); + if (!frame) + return 0; + // Verify param: requesting_url; type: string_byref_const + DCHECK(requesting_url); + if (!requesting_url) + return 0; + // Verify param: callback; type: refptr_diff + DCHECK(callback); + if (!callback) + return 0; + + // Execute + bool _retval = + CefPermissionHandlerCppToC::Get(self)->OnRequestMediaAccessPermission( + CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame), + CefString(requesting_url), requested_permissions, + CefMediaAccessCallbackCToCpp::Wrap(callback)); + + // Return type: bool + return _retval; +} + +} // namespace + +// CONSTRUCTOR - Do not edit by hand. + +CefPermissionHandlerCppToC::CefPermissionHandlerCppToC() { + GetStruct()->on_request_media_access_permission = + permission_handler_on_request_media_access_permission; +} + +// DESTRUCTOR - Do not edit by hand. + +CefPermissionHandlerCppToC::~CefPermissionHandlerCppToC() { + shutdown_checker::AssertNotShutdown(); +} + +template <> +CefRefPtr CefCppToCRefCounted< + CefPermissionHandlerCppToC, + CefPermissionHandler, + cef_permission_handler_t>::UnwrapDerived(CefWrapperType type, + cef_permission_handler_t* s) { + NOTREACHED() << "Unexpected class type: " << type; + return nullptr; +} + +template <> +CefWrapperType CefCppToCRefCounted::kWrapperType = + WT_PERMISSION_HANDLER; diff --git a/libcef_dll/cpptoc/permission_handler_cpptoc.h b/libcef_dll/cpptoc/permission_handler_cpptoc.h new file mode 100644 index 000000000..499153f8d --- /dev/null +++ b/libcef_dll/cpptoc/permission_handler_cpptoc.h @@ -0,0 +1,38 @@ +// Copyright (c) 2022 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=0f90cfade714209df150953eabed70bc7b264122$ +// + +#ifndef CEF_LIBCEF_DLL_CPPTOC_PERMISSION_HANDLER_CPPTOC_H_ +#define CEF_LIBCEF_DLL_CPPTOC_PERMISSION_HANDLER_CPPTOC_H_ +#pragma once + +#if !defined(WRAPPING_CEF_SHARED) +#error This file can be included wrapper-side only +#endif + +#include "include/capi/cef_permission_handler_capi.h" +#include "include/cef_permission_handler.h" +#include "libcef_dll/cpptoc/cpptoc_ref_counted.h" + +// Wrap a C++ class with a C structure. +// This class may be instantiated and accessed wrapper-side only. +class CefPermissionHandlerCppToC + : public CefCppToCRefCounted { + public: + CefPermissionHandlerCppToC(); + virtual ~CefPermissionHandlerCppToC(); +}; + +#endif // CEF_LIBCEF_DLL_CPPTOC_PERMISSION_HANDLER_CPPTOC_H_ diff --git a/libcef_dll/ctocpp/client_ctocpp.cc b/libcef_dll/ctocpp/client_ctocpp.cc index 84e7d9f62..4ef190763 100644 --- a/libcef_dll/ctocpp/client_ctocpp.cc +++ b/libcef_dll/ctocpp/client_ctocpp.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=8e158f3ea54211b95f0b716b1eeec2ff475f1245$ +// $hash=71c2e9dd248276daa80be557773fe86bdc2bcb99$ // #include "libcef_dll/ctocpp/client_ctocpp.h" @@ -30,6 +30,7 @@ #include "libcef_dll/ctocpp/keyboard_handler_ctocpp.h" #include "libcef_dll/ctocpp/life_span_handler_ctocpp.h" #include "libcef_dll/ctocpp/load_handler_ctocpp.h" +#include "libcef_dll/ctocpp/permission_handler_ctocpp.h" #include "libcef_dll/ctocpp/print_handler_ctocpp.h" #include "libcef_dll/ctocpp/render_handler_ctocpp.h" #include "libcef_dll/ctocpp/request_handler_ctocpp.h" @@ -187,6 +188,21 @@ CefRefPtr CefClientCToCpp::GetFrameHandler() { return CefFrameHandlerCToCpp::Wrap(_retval); } +NO_SANITIZE("cfi-icall") +CefRefPtr CefClientCToCpp::GetPermissionHandler() { + cef_client_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, get_permission_handler)) + return nullptr; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + cef_permission_handler_t* _retval = _struct->get_permission_handler(_struct); + + // Return type: refptr_same + return CefPermissionHandlerCToCpp::Wrap(_retval); +} + NO_SANITIZE("cfi-icall") CefRefPtr CefClientCToCpp::GetJSDialogHandler() { cef_client_t* _struct = GetStruct(); diff --git a/libcef_dll/ctocpp/client_ctocpp.h b/libcef_dll/ctocpp/client_ctocpp.h index 8513fc32f..e16ee083d 100644 --- a/libcef_dll/ctocpp/client_ctocpp.h +++ b/libcef_dll/ctocpp/client_ctocpp.h @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=69f82ee575779deaf1d3581dbedcf8aa75f35710$ +// $hash=821429d4e38b03407a1e6cf73a67e479810f0c21$ // #ifndef CEF_LIBCEF_DLL_CTOCPP_CLIENT_CTOCPP_H_ @@ -43,6 +43,7 @@ class CefClientCToCpp CefRefPtr GetFindHandler() override; CefRefPtr GetFocusHandler() override; CefRefPtr GetFrameHandler() override; + CefRefPtr GetPermissionHandler() override; CefRefPtr GetJSDialogHandler() override; CefRefPtr GetKeyboardHandler() override; CefRefPtr GetLifeSpanHandler() override; diff --git a/libcef_dll/ctocpp/media_access_callback_ctocpp.cc b/libcef_dll/ctocpp/media_access_callback_ctocpp.cc new file mode 100644 index 000000000..532d22e91 --- /dev/null +++ b/libcef_dll/ctocpp/media_access_callback_ctocpp.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2022 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=bfd836add2381882b8c0408181621ebf24cddce1$ +// + +#include "libcef_dll/ctocpp/media_access_callback_ctocpp.h" +#include "libcef_dll/shutdown_checker.h" + +// VIRTUAL METHODS - Body may be edited by hand. + +NO_SANITIZE("cfi-icall") +void CefMediaAccessCallbackCToCpp::Continue(uint32 allowed_permissions) { + shutdown_checker::AssertNotShutdown(); + + cef_media_access_callback_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, cont)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + _struct->cont(_struct, allowed_permissions); +} + +NO_SANITIZE("cfi-icall") void CefMediaAccessCallbackCToCpp::Cancel() { + shutdown_checker::AssertNotShutdown(); + + cef_media_access_callback_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, cancel)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + _struct->cancel(_struct); +} + +// CONSTRUCTOR - Do not edit by hand. + +CefMediaAccessCallbackCToCpp::CefMediaAccessCallbackCToCpp() {} + +// DESTRUCTOR - Do not edit by hand. + +CefMediaAccessCallbackCToCpp::~CefMediaAccessCallbackCToCpp() { + shutdown_checker::AssertNotShutdown(); +} + +template <> +cef_media_access_callback_t* CefCToCppRefCounted< + CefMediaAccessCallbackCToCpp, + CefMediaAccessCallback, + cef_media_access_callback_t>::UnwrapDerived(CefWrapperType type, + CefMediaAccessCallback* c) { + NOTREACHED() << "Unexpected class type: " << type; + return nullptr; +} + +template <> +CefWrapperType CefCToCppRefCounted::kWrapperType = + WT_MEDIA_ACCESS_CALLBACK; diff --git a/libcef_dll/ctocpp/media_access_callback_ctocpp.h b/libcef_dll/ctocpp/media_access_callback_ctocpp.h new file mode 100644 index 000000000..ed2960770 --- /dev/null +++ b/libcef_dll/ctocpp/media_access_callback_ctocpp.h @@ -0,0 +1,42 @@ +// Copyright (c) 2022 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=1b4c8da03ff2853b057860369bdb5dd1b48cf046$ +// + +#ifndef CEF_LIBCEF_DLL_CTOCPP_MEDIA_ACCESS_CALLBACK_CTOCPP_H_ +#define CEF_LIBCEF_DLL_CTOCPP_MEDIA_ACCESS_CALLBACK_CTOCPP_H_ +#pragma once + +#if !defined(WRAPPING_CEF_SHARED) +#error This file can be included wrapper-side only +#endif + +#include "include/capi/cef_permission_handler_capi.h" +#include "include/cef_permission_handler.h" +#include "libcef_dll/ctocpp/ctocpp_ref_counted.h" + +// Wrap a C structure with a C++ class. +// This class may be instantiated and accessed wrapper-side only. +class CefMediaAccessCallbackCToCpp + : public CefCToCppRefCounted { + public: + CefMediaAccessCallbackCToCpp(); + virtual ~CefMediaAccessCallbackCToCpp(); + + // CefMediaAccessCallback methods. + void Continue(uint32 allowed_permissions) override; + void Cancel() override; +}; + +#endif // CEF_LIBCEF_DLL_CTOCPP_MEDIA_ACCESS_CALLBACK_CTOCPP_H_ diff --git a/libcef_dll/ctocpp/permission_handler_ctocpp.cc b/libcef_dll/ctocpp/permission_handler_ctocpp.cc new file mode 100644 index 000000000..e1ca9c19b --- /dev/null +++ b/libcef_dll/ctocpp/permission_handler_ctocpp.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2022 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=f9073ac6e5a3634a3bf3c919ac1b0a9c607b4398$ +// + +#include "libcef_dll/ctocpp/permission_handler_ctocpp.h" +#include "libcef_dll/cpptoc/browser_cpptoc.h" +#include "libcef_dll/cpptoc/frame_cpptoc.h" +#include "libcef_dll/cpptoc/media_access_callback_cpptoc.h" +#include "libcef_dll/shutdown_checker.h" + +// VIRTUAL METHODS - Body may be edited by hand. + +NO_SANITIZE("cfi-icall") +bool CefPermissionHandlerCToCpp::OnRequestMediaAccessPermission( + CefRefPtr browser, + CefRefPtr frame, + const CefString& requesting_url, + uint32 requested_permissions, + CefRefPtr callback) { + shutdown_checker::AssertNotShutdown(); + + cef_permission_handler_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_request_media_access_permission)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: browser; type: refptr_diff + DCHECK(browser.get()); + if (!browser.get()) + return false; + // Verify param: frame; type: refptr_diff + DCHECK(frame.get()); + if (!frame.get()) + return false; + // Verify param: requesting_url; type: string_byref_const + DCHECK(!requesting_url.empty()); + if (requesting_url.empty()) + return false; + // Verify param: callback; type: refptr_diff + DCHECK(callback.get()); + if (!callback.get()) + return false; + + // Execute + int _retval = _struct->on_request_media_access_permission( + _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame), + requesting_url.GetStruct(), requested_permissions, + CefMediaAccessCallbackCppToC::Wrap(callback)); + + // Return type: bool + return _retval ? true : false; +} + +// CONSTRUCTOR - Do not edit by hand. + +CefPermissionHandlerCToCpp::CefPermissionHandlerCToCpp() {} + +// DESTRUCTOR - Do not edit by hand. + +CefPermissionHandlerCToCpp::~CefPermissionHandlerCToCpp() { + shutdown_checker::AssertNotShutdown(); +} + +template <> +cef_permission_handler_t* CefCToCppRefCounted< + CefPermissionHandlerCToCpp, + CefPermissionHandler, + cef_permission_handler_t>::UnwrapDerived(CefWrapperType type, + CefPermissionHandler* c) { + NOTREACHED() << "Unexpected class type: " << type; + return nullptr; +} + +template <> +CefWrapperType CefCToCppRefCounted::kWrapperType = + WT_PERMISSION_HANDLER; diff --git a/libcef_dll/ctocpp/permission_handler_ctocpp.h b/libcef_dll/ctocpp/permission_handler_ctocpp.h new file mode 100644 index 000000000..9daff0564 --- /dev/null +++ b/libcef_dll/ctocpp/permission_handler_ctocpp.h @@ -0,0 +1,46 @@ +// Copyright (c) 2022 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=1c8392555b99119b866989c5461ad48a0ac662b5$ +// + +#ifndef CEF_LIBCEF_DLL_CTOCPP_PERMISSION_HANDLER_CTOCPP_H_ +#define CEF_LIBCEF_DLL_CTOCPP_PERMISSION_HANDLER_CTOCPP_H_ +#pragma once + +#if !defined(BUILDING_CEF_SHARED) +#error This file can be included DLL-side only +#endif + +#include "include/capi/cef_permission_handler_capi.h" +#include "include/cef_permission_handler.h" +#include "libcef_dll/ctocpp/ctocpp_ref_counted.h" + +// Wrap a C structure with a C++ class. +// This class may be instantiated and accessed DLL-side only. +class CefPermissionHandlerCToCpp + : public CefCToCppRefCounted { + public: + CefPermissionHandlerCToCpp(); + virtual ~CefPermissionHandlerCToCpp(); + + // CefPermissionHandler methods. + bool OnRequestMediaAccessPermission( + CefRefPtr browser, + CefRefPtr frame, + const CefString& requesting_url, + uint32 requested_permissions, + CefRefPtr callback) override; +}; + +#endif // CEF_LIBCEF_DLL_CTOCPP_PERMISSION_HANDLER_CTOCPP_H_ diff --git a/libcef_dll/wrapper_types.h b/libcef_dll/wrapper_types.h index 08751482e..2c3da0203 100644 --- a/libcef_dll/wrapper_types.h +++ b/libcef_dll/wrapper_types.h @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=98d509772d772e85d47d53e3df21f7317799c19e$ +// $hash=54ed016f3b2bbffebc96ac9cadd3c3986d240342$ // #ifndef CEF_LIBCEF_DLL_WRAPPER_TYPES_H_ @@ -77,6 +77,7 @@ enum CefWrapperType { WT_LIFE_SPAN_HANDLER, WT_LIST_VALUE, WT_LOAD_HANDLER, + WT_MEDIA_ACCESS_CALLBACK, WT_MEDIA_OBSERVER, WT_MEDIA_ROUTE, WT_MEDIA_ROUTE_CREATE_CALLBACK, @@ -95,6 +96,7 @@ enum CefWrapperType { WT_PANEL, WT_PANEL_DELEGATE, WT_PDF_PRINT_CALLBACK, + WT_PERMISSION_HANDLER, WT_POST_DATA, WT_POST_DATA_ELEMENT, WT_PRINT_DIALOG_CALLBACK, diff --git a/tests/cefclient/browser/client_handler.cc b/tests/cefclient/browser/client_handler.cc index a6d89bd3f..b4598ea28 100644 --- a/tests/cefclient/browser/client_handler.cc +++ b/tests/cefclient/browser/client_handler.cc @@ -43,6 +43,7 @@ enum client_menu_ids { CLIENT_ID_INSPECT_ELEMENT, CLIENT_ID_SHOW_SSL_INFO, CLIENT_ID_CURSOR_CHANGE_DISABLED, + CLIENT_ID_MEDIA_HANDLING_DISABLED, CLIENT_ID_OFFLINE, CLIENT_ID_TESTMENU_SUBMENU, CLIENT_ID_TESTMENU_CHECKITEM, @@ -407,6 +408,12 @@ void ClientHandler::OnBeforeContextMenu(CefRefPtr browser, "Cursor change disabled"); if (mouse_cursor_change_disabled_) model->SetChecked(CLIENT_ID_CURSOR_CHANGE_DISABLED, true); + + model->AddSeparator(); + model->AddCheckItem(CLIENT_ID_MEDIA_HANDLING_DISABLED, + "Media handling disabled"); + if (media_handling_disabled_) + model->SetChecked(CLIENT_ID_MEDIA_HANDLING_DISABLED, true); } model->AddSeparator(); @@ -445,6 +452,9 @@ bool ClientHandler::OnContextMenuCommand(CefRefPtr browser, case CLIENT_ID_CURSOR_CHANGE_DISABLED: mouse_cursor_change_disabled_ = !mouse_cursor_change_disabled_; return true; + case CLIENT_ID_MEDIA_HANDLING_DISABLED: + media_handling_disabled_ = !media_handling_disabled_; + return true; case CLIENT_ID_OFFLINE: offline_ = !offline_; SetOfflineState(browser, offline_); @@ -777,6 +787,17 @@ void ClientHandler::OnLoadError(CefRefPtr browser, LoadErrorPage(frame, failedUrl, errorCode, errorText); } +bool ClientHandler::OnRequestMediaAccessPermission( + CefRefPtr browser, + CefRefPtr frame, + const CefString& requesting_url, + uint32 requested_permissions, + CefRefPtr callback) { + callback->Continue(media_handling_disabled_ ? CEF_MEDIA_PERMISSION_NONE + : requested_permissions); + return true; +} + bool ClientHandler::OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame, CefRefPtr request, diff --git a/tests/cefclient/browser/client_handler.h b/tests/cefclient/browser/client_handler.h index f422b1314..9e5d73c01 100644 --- a/tests/cefclient/browser/client_handler.h +++ b/tests/cefclient/browser/client_handler.h @@ -37,6 +37,7 @@ class ClientHandler : public CefClient, public CefKeyboardHandler, public CefLifeSpanHandler, public CefLoadHandler, + public CefPermissionHandler, public CefRequestHandler, public CefResourceRequestHandler { public: @@ -114,6 +115,9 @@ class ClientHandler : public CefClient, CefRefPtr GetLifeSpanHandler() override { return this; } CefRefPtr GetLoadHandler() override { return this; } CefRefPtr GetRequestHandler() override { return this; } + CefRefPtr GetPermissionHandler() override { + return this; + } bool OnProcessMessageReceived(CefRefPtr browser, CefRefPtr frame, CefProcessId source_process, @@ -229,6 +233,14 @@ class ClientHandler : public CefClient, const CefString& errorText, const CefString& failedUrl) override; + // CefPermissionHandler methods + bool OnRequestMediaAccessPermission( + CefRefPtr browser, + CefRefPtr frame, + const CefString& requesting_url, + uint32 requested_permissions, + CefRefPtr callback) override; + // CefRequestHandler methods bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame, @@ -382,6 +394,9 @@ class ClientHandler : public CefClient, // True if mouse cursor change is disabled. bool mouse_cursor_change_disabled_; + // True if media handling is disabled. + bool media_handling_disabled_ = true; + // True if the browser is currently offline. bool offline_; diff --git a/tests/ceftests/client_app_delegates.cc b/tests/ceftests/client_app_delegates.cc index b99f24153..0f3971460 100644 --- a/tests/ceftests/client_app_delegates.cc +++ b/tests/ceftests/client_app_delegates.cc @@ -22,6 +22,11 @@ void CreateBrowserDelegates(ClientAppBrowser::DelegateSet& delegates) { delegates); CreatePreferenceBrowserTests(delegates); + // Bring in the media access tests. + extern void CreateMediaAccessBrowserTests(ClientAppBrowser::DelegateSet & + delegates); + CreateMediaAccessBrowserTests(delegates); + // Bring in URLRequest tests. extern void CreateURLRequestBrowserTests(ClientAppBrowser::DelegateSet & delegates); diff --git a/tests/ceftests/media_access_unittest.cc b/tests/ceftests/media_access_unittest.cc new file mode 100644 index 000000000..c2079be2a --- /dev/null +++ b/tests/ceftests/media_access_unittest.cc @@ -0,0 +1,529 @@ +// Copyright 2022 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 +#include + +#include "include/base/cef_bind.h" +#include "include/cef_parser.h" +#include "include/cef_permission_handler.h" +#include "include/cef_request_context_handler.h" +#include "include/wrapper/cef_closure_task.h" +#include "include/wrapper/cef_stream_resource_handler.h" +#include "tests/ceftests/test_handler.h" +#include "tests/ceftests/test_suite.h" +#include "tests/gtest/include/gtest/gtest.h" +#include "tests/shared/browser/client_app_browser.h" + +namespace { + +// Media access requires HTTPS. +const char kMediaUrl[] = "https://media-access-test/media.html"; + +// Browser-side app delegate. +class MediaAccessBrowserTest : public client::ClientAppBrowser::Delegate, + public CefPermissionHandler { + public: + MediaAccessBrowserTest() {} + + void OnBeforeCommandLineProcessing( + CefRefPtr app, + CefRefPtr command_line) override { + // We might run tests on systems that don't have media device, + // so just use fake devices. + command_line->AppendSwitch("use-fake-device-for-media-stream"); + } + + private: + IMPLEMENT_REFCOUNTING(MediaAccessBrowserTest); +}; + +class TestSetup { + public: + TestSetup() {} + + bool deny_implicitly = false; + bool continue_async = false; + + TrackCallback got_success; + TrackCallback got_audio; + TrackCallback got_video; +}; + +class MediaAccessTestHandler : public TestHandler, public CefPermissionHandler { + public: + MediaAccessTestHandler(TestSetup* tr, uint32 request, uint32 response) + : test_setup_(tr), request_(request), response_(response) {} + + cef_return_value_t OnBeforeResourceLoad( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request, + CefRefPtr callback) override { + std::string newUrl = request->GetURL(); + if (newUrl.find("tests/exit") != std::string::npos) { + CefURLParts url_parts; + CefParseURL(newUrl, url_parts); + if (newUrl.find("SUCCESS") != std::string::npos) { + test_setup_->got_success.yes(); + std::string data_string = newUrl.substr(newUrl.find("&data=") + + std::string("&data=").length()); + std::string data_string_decoded = CefURIDecode( + data_string, false, + static_cast( + UU_SPACES | UU_URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS)); + auto obj = CefParseJSON(data_string_decoded, + JSON_PARSER_ALLOW_TRAILING_COMMAS); + CefRefPtr data = obj->GetDictionary(); + const auto got_video = data->GetBool("got_video_track"); + const auto got_audio = data->GetBool("got_audio_track"); + if (got_video) { + test_setup_->got_video.yes(); + } + if (got_audio) { + test_setup_->got_audio.yes(); + } + } + DestroyTest(); + return RV_CANCEL; + } + + return RV_CONTINUE; + } + + void RunTest() override { + std::string page = + "" + "" + "MEDIA ACCESS TEST"; + + // Create the request context that will use an in-memory cache. + CefRequestContextSettings settings; + CefRefPtr request_context = + CefRequestContext::CreateContext(settings, nullptr); + + AddResource(kMediaUrl, page, "text/html"); + + // Create the browser. + CreateBrowser(kMediaUrl, request_context); + + // Time out the test after a reasonable period of time. + SetTestTimeout(); + } + + CefRefPtr GetPermissionHandler() override { + return this; + } + + void CompleteTest() { + if (!CefCurrentlyOn(TID_UI)) { + CefPostTask(TID_UI, + base::BindOnce(&MediaAccessTestHandler::CompleteTest, this)); + return; + } + + DestroyTest(); + } + + bool OnRequestMediaAccessPermission( + CefRefPtr browser, + CefRefPtr frame, + const CefString& requesting_url, + uint32 requested_permissions, + CefRefPtr callback) override { + EXPECT_UI_THREAD(); + EXPECT_TRUE(frame->IsMain()); + + if (test_setup_->deny_implicitly) { + return false; + } + + EXPECT_EQ(requested_permissions, request_); + + if (test_setup_->continue_async) { + CefPostTask(TID_UI, base::BindOnce(&CefMediaAccessCallback::Continue, + callback, response_)); + } else { + callback->Continue(response_); + } + return true; + } + + protected: + TestSetup* const test_setup_; + const uint32 request_; + const uint32 response_; + + IMPLEMENT_REFCOUNTING(MediaAccessTestHandler); +}; +} // namespace + +// Capture device tests +TEST(MediaAccessTest, DeviceFailureWhenReturningFalse) { + TestSetup test_setup; + test_setup.deny_implicitly = true; + + CefRefPtr handler = + new MediaAccessTestHandler(&test_setup, + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE | + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_NONE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DeviceFailureWhenReturningNoPermission) { + TestSetup test_setup; + + CefRefPtr handler = + new MediaAccessTestHandler(&test_setup, + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE | + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_NONE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DeviceFailureWhenReturningNoPermissionAsync) { + TestSetup test_setup; + test_setup.continue_async = true; + + CefRefPtr handler = + new MediaAccessTestHandler(&test_setup, + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE | + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_NONE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DeviceFailureWhenRequestingAudioButReturningVideo) { + TestSetup test_setup; + + CefRefPtr handler = new MediaAccessTestHandler( + &test_setup, CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE, + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DeviceFailureWhenRequestingVideoButReturningAudio) { + TestSetup test_setup; + + CefRefPtr handler = new MediaAccessTestHandler( + &test_setup, CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DevicePartialFailureReturningVideo) { + TestSetup test_setup; + + CefRefPtr handler = + new MediaAccessTestHandler(&test_setup, + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE | + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DevicePartialFailureReturningAudio) { + TestSetup test_setup; + + CefRefPtr handler = + new MediaAccessTestHandler(&test_setup, + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE | + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DeviceFailureWhenReturningScreenCapture1) { + TestSetup test_setup; + + CefRefPtr handler = + new MediaAccessTestHandler(&test_setup, + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE | + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_DESKTOP_AUDIO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DeviceFailureWhenReturningScreenCapture2) { + TestSetup test_setup; + + CefRefPtr handler = + new MediaAccessTestHandler(&test_setup, + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE | + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_DESKTOP_VIDEO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DeviceFailureWhenReturningScreenCapture3) { + TestSetup test_setup; + + CefRefPtr handler = new MediaAccessTestHandler( + &test_setup, CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE, + CEF_MEDIA_PERMISSION_DESKTOP_VIDEO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DeviceFailureWhenReturningScreenCapture4) { + TestSetup test_setup; + + CefRefPtr handler = new MediaAccessTestHandler( + &test_setup, CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE, + CEF_MEDIA_PERMISSION_DESKTOP_AUDIO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DeviceFailureWhenReturningScreenCapture5) { + TestSetup test_setup; + + CefRefPtr handler = new MediaAccessTestHandler( + &test_setup, CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_DESKTOP_VIDEO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DeviceFailureWhenReturningScreenCapture6) { + TestSetup test_setup; + + CefRefPtr handler = new MediaAccessTestHandler( + &test_setup, CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_DESKTOP_AUDIO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DeviceSuccessAudioOnly) { + TestSetup test_setup; + + CefRefPtr handler = new MediaAccessTestHandler( + &test_setup, CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE, + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(test_setup.got_success); + EXPECT_TRUE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DeviceSuccessVideoOnly) { + TestSetup test_setup; + + CefRefPtr handler = new MediaAccessTestHandler( + &test_setup, CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_TRUE(test_setup.got_video); +} + +TEST(MediaAccessTest, DeviceSuccessAudioVideo) { + TestSetup test_setup; + + CefRefPtr handler = + new MediaAccessTestHandler(&test_setup, + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE | + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE, + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE | + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(test_setup.got_success); + EXPECT_TRUE(test_setup.got_audio); + EXPECT_TRUE(test_setup.got_video); +} + +TEST(MediaAccessTest, DeviceSuccessAudioVideoAsync) { + TestSetup test_setup; + test_setup.continue_async = true; + + CefRefPtr handler = + new MediaAccessTestHandler(&test_setup, + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE | + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE, + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE | + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(test_setup.got_success); + EXPECT_TRUE(test_setup.got_audio); + EXPECT_TRUE(test_setup.got_video); +} + +// Screen capture tests +TEST(MediaAccessTest, DesktopFailureWhenReturningNoPermission) { + TestSetup test_setup; + + CefRefPtr handler = + new MediaAccessTestHandler(&test_setup, + CEF_MEDIA_PERMISSION_DESKTOP_AUDIO_CAPTURE | + CEF_MEDIA_PERMISSION_DESKTOP_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_NONE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DesktopFailureWhenRequestingVideoButReturningAudio) { + TestSetup test_setup; + + CefRefPtr handler = new MediaAccessTestHandler( + &test_setup, CEF_MEDIA_PERMISSION_DESKTOP_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_DESKTOP_AUDIO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +TEST(MediaAccessTest, DesktopPartialSuccessReturningVideo) { + TestSetup test_setup; + + CefRefPtr handler = + new MediaAccessTestHandler(&test_setup, + CEF_MEDIA_PERMISSION_DESKTOP_AUDIO_CAPTURE | + CEF_MEDIA_PERMISSION_DESKTOP_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_DESKTOP_VIDEO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_TRUE(test_setup.got_video); +} + +TEST(MediaAccessTest, DesktopPartialFailureReturningAudio) { + TestSetup test_setup; + CefRefPtr handler = + new MediaAccessTestHandler(&test_setup, + CEF_MEDIA_PERMISSION_DESKTOP_AUDIO_CAPTURE | + CEF_MEDIA_PERMISSION_DESKTOP_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_DESKTOP_AUDIO_CAPTURE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_FALSE(test_setup.got_success); + EXPECT_FALSE(test_setup.got_audio); + EXPECT_FALSE(test_setup.got_video); +} + +// Entry point for creating media access browser test objects. +// Called from client_app_delegates.cc. +void CreateMediaAccessBrowserTests( + client::ClientAppBrowser::DelegateSet& delegates) { + delegates.insert(new MediaAccessBrowserTest); +}