Add a shared memory variant of CefProcessMessage (fixes issue #3126)

CefSharedProcessMessageBuilder supports creation of a CefProcessMessage
backed by a CefSharedMemoryRegion.

Performance tests comparing the existing ArgumentList approach and the new
SharedMemoryRegion approach have been added to cefclient at
http://tests/ipc_performance.

CefMessageRouter has been updated to use SharedMemoryRegion as transport
for larger message payloads. The threshold is configurable via
|CefMessageRouterConfig.message_size_threshold|.

To test:
run `ceftests --gtest_filter=SendSharedProcessMessageTest.*:SharedProcessMessageTest.*:MessageRouterTest.Threshold*`
This commit is contained in:
Nik Pavlov 2022-07-04 09:49:15 +00:00 committed by Marshall Greenblatt
parent a931d49f3e
commit 81e892d19e
55 changed files with 4025 additions and 1132 deletions

View File

@ -814,6 +814,8 @@ static_library("libcef_static") {
"libcef/common/parser_impl.cc",
"libcef/common/process_message_impl.cc",
"libcef/common/process_message_impl.h",
"libcef/common/process_message_smr_impl.cc",
"libcef/common/process_message_smr_impl.h",
"libcef/common/request_impl.cc",
"libcef/common/request_impl.h",
"libcef/common/resource_bundle_delegate.cc",

View File

@ -8,7 +8,7 @@
# by hand. See the translator.README.txt file in the tools directory for
# more information.
#
# $hash=f8ae899cc69a48e9878fa4db2fcc3b7e54230062$
# $hash=f374acb217db4183917195716d5522a9eb897cdf$
#
{
@ -75,6 +75,8 @@
'include/cef_response_filter.h',
'include/cef_scheme.h',
'include/cef_server.h',
'include/cef_shared_memory_region.h',
'include/cef_shared_process_message_builder.h',
'include/cef_ssl_info.h',
'include/cef_ssl_status.h',
'include/cef_stream.h',
@ -175,6 +177,8 @@
'include/capi/cef_response_filter_capi.h',
'include/capi/cef_scheme_capi.h',
'include/capi/cef_server_capi.h',
'include/capi/cef_shared_memory_region_capi.h',
'include/capi/cef_shared_process_message_builder_capi.h',
'include/capi/cef_ssl_info_capi.h',
'include/capi/cef_ssl_status_capi.h',
'include/capi/cef_stream_capi.h',
@ -442,6 +446,10 @@
'libcef_dll/ctocpp/server_handler_ctocpp.h',
'libcef_dll/ctocpp/set_cookie_callback_ctocpp.cc',
'libcef_dll/ctocpp/set_cookie_callback_ctocpp.h',
'libcef_dll/cpptoc/shared_memory_region_cpptoc.cc',
'libcef_dll/cpptoc/shared_memory_region_cpptoc.h',
'libcef_dll/cpptoc/shared_process_message_builder_cpptoc.cc',
'libcef_dll/cpptoc/shared_process_message_builder_cpptoc.h',
'libcef_dll/cpptoc/stream_reader_cpptoc.cc',
'libcef_dll/cpptoc/stream_reader_cpptoc.h',
'libcef_dll/cpptoc/stream_writer_cpptoc.cc',
@ -754,6 +762,10 @@
'libcef_dll/cpptoc/server_handler_cpptoc.h',
'libcef_dll/cpptoc/set_cookie_callback_cpptoc.cc',
'libcef_dll/cpptoc/set_cookie_callback_cpptoc.h',
'libcef_dll/ctocpp/shared_memory_region_ctocpp.cc',
'libcef_dll/ctocpp/shared_memory_region_ctocpp.h',
'libcef_dll/ctocpp/shared_process_message_builder_ctocpp.cc',
'libcef_dll/ctocpp/shared_process_message_builder_ctocpp.h',
'libcef_dll/ctocpp/stream_reader_ctocpp.cc',
'libcef_dll/ctocpp/stream_reader_ctocpp.h',
'libcef_dll/ctocpp/stream_writer_ctocpp.cc',

View File

@ -177,6 +177,8 @@
'tests/shared/browser/resource_util.h',
],
'shared_sources_common': [
'tests/shared/common/binary_value_utils.cc',
'tests/shared/common/binary_value_utils.h',
'tests/shared/common/client_app.cc',
'tests/shared/common/client_app.h',
'tests/shared/common/client_app_other.cc',
@ -289,6 +291,8 @@
'tests/cefclient/renderer/client_app_delegates_renderer.cc',
'tests/cefclient/renderer/client_renderer.cc',
'tests/cefclient/renderer/client_renderer.h',
'tests/cefclient/renderer/ipc_performance_test.cc',
'tests/cefclient/renderer/ipc_performance_test.h',
'tests/cefclient/renderer/performance_test.cc',
'tests/cefclient/renderer/performance_test.h',
'tests/cefclient/renderer/performance_test_setup.h',
@ -298,6 +302,7 @@
'tests/cefclient/resources/binding.html',
'tests/cefclient/resources/dialogs.html',
'tests/cefclient/resources/draggable.html',
'tests/cefclient/resources/ipc_performance.html',
'tests/cefclient/resources/localstorage.html',
'tests/cefclient/resources/logo.png',
'tests/cefclient/resources/media_router.html',
@ -476,7 +481,12 @@
'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/message_router_harness_unittest.cc',
'tests/ceftests/message_router_multi_query_unittest.cc',
'tests/ceftests/message_router_single_query_unittest.cc',
'tests/ceftests/message_router_threshold_unittest.cc',
'tests/ceftests/message_router_unittest_utils.cc',
'tests/ceftests/message_router_unittest_utils.h',
'tests/ceftests/navigation_unittest.cc',
'tests/ceftests/os_rendering_unittest.cc',
'tests/ceftests/osr_accessibility_unittest.cc',
@ -499,6 +509,8 @@
'tests/ceftests/scheme_handler_unittest.cc',
'tests/ceftests/scoped_temp_dir_unittest.cc',
'tests/ceftests/server_unittest.cc',
'tests/ceftests/send_shared_process_message_unittest.cc',
"tests/ceftests/shared_process_message_unittest.cc",
'tests/ceftests/stream_unittest.cc',
'tests/ceftests/stream_resource_handler_unittest.cc',
'tests/ceftests/string_unittest.cc',
@ -564,7 +576,12 @@
'tests/ceftests/dom_unittest.cc',
'tests/ceftests/frame_unittest.cc',
'tests/ceftests/media_access_unittest.cc',
'tests/ceftests/message_router_unittest.cc',
'tests/ceftests/message_router_harness_unittest.cc',
'tests/ceftests/message_router_multi_query_unittest.cc',
'tests/ceftests/message_router_single_query_unittest.cc',
'tests/ceftests/message_router_threshold_unittest.cc',
'tests/ceftests/message_router_unittest_utils.cc',
'tests/ceftests/message_router_unittest_utils.h',
'tests/ceftests/navigation_unittest.cc',
'tests/ceftests/pdf_viewer_unittest.cc',
'tests/ceftests/preference_unittest.cc',
@ -576,6 +593,8 @@
'tests/ceftests/routing_test_handler.cc',
'tests/ceftests/routing_test_handler.h',
'tests/ceftests/scheme_handler_unittest.cc',
'tests/ceftests/send_shared_process_message_unittest.cc',
"tests/ceftests/shared_process_message_unittest.cc",
'tests/ceftests/urlrequest_unittest.cc',
'tests/ceftests/test_handler.cc',
'tests/ceftests/test_handler.h',

View File

@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for
// more information.
//
// $hash=2549ea10cd3a41bc04ab81bad24eb12787de68b9$
// $hash=026a7f827962222a1df8b62a8e7bdfbf4dce27e0$
//
#ifndef CEF_INCLUDE_CAPI_CEF_PROCESS_MESSAGE_CAPI_H_
@ -41,6 +41,7 @@
#pragma once
#include "include/capi/cef_base_capi.h"
#include "include/capi/cef_shared_memory_region_capi.h"
#include "include/capi/cef_values_capi.h"
#ifdef __cplusplus
@ -69,7 +70,8 @@ typedef struct _cef_process_message_t {
int(CEF_CALLBACK* is_read_only)(struct _cef_process_message_t* self);
///
// Returns a writable copy of this object.
// Returns a writable copy of this object. Returns nullptr when message
// contains a shared memory region.
///
struct _cef_process_message_t*(CEF_CALLBACK* copy)(
struct _cef_process_message_t* self);
@ -82,10 +84,18 @@ typedef struct _cef_process_message_t {
struct _cef_process_message_t* self);
///
// Returns the list of arguments.
// Returns the list of arguments. Returns nullptr when message contains a
// shared memory region.
///
struct _cef_list_value_t*(CEF_CALLBACK* get_argument_list)(
struct _cef_process_message_t* self);
///
// Returns the shared memory region. Returns nullptr when message contains an
// argument list.
///
struct _cef_shared_memory_region_t*(CEF_CALLBACK* get_shared_memory_region)(
struct _cef_process_message_t* self);
} cef_process_message_t;
///

View File

@ -0,0 +1,79 @@
// 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=212f13ac6baeeefb86f1648d1e18ccba95fd5f79$
//
#ifndef CEF_INCLUDE_CAPI_CEF_SHARED_MEMORY_REGION_CAPI_H_
#define CEF_INCLUDE_CAPI_CEF_SHARED_MEMORY_REGION_CAPI_H_
#pragma once
#include "include/capi/cef_base_capi.h"
#ifdef __cplusplus
extern "C" {
#endif
///
// Structure that wraps platform-dependent share memory region mapping.
///
typedef struct _cef_shared_memory_region_t {
///
// Base structure.
///
cef_base_ref_counted_t base;
///
// Returns true (1) if the mapping is valid.
///
int(CEF_CALLBACK* is_valid)(struct _cef_shared_memory_region_t* self);
///
// Returns the size of the mapping in bytes. Returns 0 for invalid instances.
///
size_t(CEF_CALLBACK* size)(struct _cef_shared_memory_region_t* self);
///
// Returns the pointer to the memory. Returns nullptr for invalid instances.
// The returned pointer is only valid for the life span of this object.
///
const void*(CEF_CALLBACK* memory)(struct _cef_shared_memory_region_t* self);
} cef_shared_memory_region_t;
#ifdef __cplusplus
}
#endif
#endif // CEF_INCLUDE_CAPI_CEF_SHARED_MEMORY_REGION_CAPI_H_

View File

@ -0,0 +1,101 @@
// 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=0ed88d26dab045ebef0f4f4ae209e7f11206a242$
//
#ifndef CEF_INCLUDE_CAPI_CEF_SHARED_PROCESS_MESSAGE_BUILDER_CAPI_H_
#define CEF_INCLUDE_CAPI_CEF_SHARED_PROCESS_MESSAGE_BUILDER_CAPI_H_
#pragma once
#include "include/capi/cef_process_message_capi.h"
#ifdef __cplusplus
extern "C" {
#endif
///
// Structure that builds a cef_process_message_t containing a shared memory
// region. This structure is not thread-safe but may be used exclusively on a
// different thread from the one which constructed it.
///
typedef struct _cef_shared_process_message_builder_t {
///
// Base structure.
///
cef_base_ref_counted_t base;
///
// Returns true (1) if the builder is valid.
///
int(CEF_CALLBACK* is_valid)(
struct _cef_shared_process_message_builder_t* self);
///
// Returns the size of the shared memory region in bytes. Returns 0 for
// invalid instances.
///
size_t(CEF_CALLBACK* size)(
struct _cef_shared_process_message_builder_t* self);
///
// Returns the pointer to the writable memory. Returns nullptr for invalid
// instances. The returned pointer is only valid for the life span of this
// object.
///
void*(CEF_CALLBACK* memory)(
struct _cef_shared_process_message_builder_t* self);
///
// Creates a new cef_process_message_t from the data provided to the builder.
// Returns nullptr for invalid instances. Invalidates the builder instance.
///
struct _cef_process_message_t*(CEF_CALLBACK* build)(
struct _cef_shared_process_message_builder_t* self);
} cef_shared_process_message_builder_t;
///
// Creates a new cef_shared_process_message_builder_t with the specified |name|
// and shared memory region of specified |byte_size|.
///
CEF_EXPORT cef_shared_process_message_builder_t*
cef_shared_process_message_builder_create(const cef_string_t* name,
size_t byte_size);
#ifdef __cplusplus
}
#endif
#endif // CEF_INCLUDE_CAPI_CEF_SHARED_PROCESS_MESSAGE_BUILDER_CAPI_H_

View File

@ -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 "794a4cf2ad83db17558bd2ca2d721487875a37e8"
#define CEF_API_HASH_UNIVERSAL "d3fdeba02acc73ac571a1be658789f2ff770f09c"
#if defined(OS_WIN)
#define CEF_API_HASH_PLATFORM "aa627f71c1cbdf13beeb3fe740337f4cc1cb5dc5"
#define CEF_API_HASH_PLATFORM "94db0746536862260c9b47d54128a744dbb29fcf"
#elif defined(OS_MAC)
#define CEF_API_HASH_PLATFORM "6bda80f2ee107a22780193a7af9101eb5a4db8a0"
#define CEF_API_HASH_PLATFORM "1c8d61e3bee1c974a2f71688bbdcfc0f6f01d457"
#elif defined(OS_LINUX)
#define CEF_API_HASH_PLATFORM "c83b942ce72835d0ea01bfc4cab3928b92abdb85"
#define CEF_API_HASH_PLATFORM "6d9e52b9e54ded43a7e0dfcf80b6f40ec75f3215"
#endif
#ifdef __cplusplus

View File

@ -39,6 +39,7 @@
#pragma once
#include "include/cef_base.h"
#include "include/cef_shared_memory_region.h"
#include "include/cef_values.h"
typedef cef_process_id_t CefProcessId;
@ -71,6 +72,7 @@ class CefProcessMessage : public virtual CefBaseRefCounted {
///
// Returns a writable copy of this object.
// Returns nullptr when message contains a shared memory region.
///
/*--cef()--*/
virtual CefRefPtr<CefProcessMessage> Copy() = 0;
@ -83,9 +85,17 @@ class CefProcessMessage : public virtual CefBaseRefCounted {
///
// Returns the list of arguments.
// Returns nullptr when message contains a shared memory region.
///
/*--cef()--*/
virtual CefRefPtr<CefListValue> GetArgumentList() = 0;
///
// Returns the shared memory region.
// Returns nullptr when message contains an argument list.
///
/*--cef()--*/
virtual CefRefPtr<CefSharedMemoryRegion> GetSharedMemoryRegion() = 0;
};
#endif // CEF_INCLUDE_CEF_MESSAGE_H_

View File

@ -0,0 +1,69 @@
// 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_SHARED_MEMORY_REGION_H_
#define CEF_INCLUDE_CEF_SHARED_MEMORY_REGION_H_
#pragma once
#include "include/cef_base.h"
///
// Class that wraps platform-dependent share memory region mapping.
///
/*--cef(source=library)--*/
class CefSharedMemoryRegion : public virtual CefBaseRefCounted {
public:
///
// Returns true if the mapping is valid.
///
/*--cef()--*/
virtual bool IsValid() = 0;
///
// Returns the size of the mapping in bytes. Returns 0 for invalid instances.
///
/*--cef()--*/
virtual size_t Size() = 0;
///
// Returns the pointer to the memory. Returns nullptr for invalid instances.
// The returned pointer is only valid for the life span of this object.
///
/*--cef()--*/
virtual const void* Memory() = 0;
};
#endif // CEF_INCLUDE_CEF_SHARED_MEMORY_REGION_H_

View File

@ -0,0 +1,87 @@
// 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_SHARED_PROCESS_MESSAGE_BUILDER_H_
#define CEF_INCLUDE_CEF_SHARED_PROCESS_MESSAGE_BUILDER_H_
#pragma once
#include "include/cef_process_message.h"
///
// Class that builds a CefProcessMessage containing a shared memory region.
// This class is not thread-safe but may be used exclusively on a different
// thread from the one which constructed it.
///
/*--cef(source=library)--*/
class CefSharedProcessMessageBuilder : public virtual CefBaseRefCounted {
public:
///
// Creates a new CefSharedProcessMessageBuilder with the specified |name| and
// shared memory region of specified |byte_size|.
///
/*--cef()--*/
static CefRefPtr<CefSharedProcessMessageBuilder> Create(const CefString& name,
size_t byte_size);
///
// Returns true if the builder is valid.
///
/*--cef()--*/
virtual bool IsValid() = 0;
///
// Returns the size of the shared memory region in bytes. Returns 0 for
// invalid instances.
///
/*--cef()--*/
virtual size_t Size() = 0;
///
// Returns the pointer to the writable memory. Returns nullptr for invalid
// instances. The returned pointer is only valid for the life span of this
// object.
///
/*--cef()--*/
virtual void* Memory() = 0;
///
// Creates a new CefProcessMessage from the data provided to the builder.
// Returns nullptr for invalid instances. Invalidates the builder instance.
///
/*--cef()--*/
virtual CefRefPtr<CefProcessMessage> Build() = 0;
};
#endif // CEF_INCLUDE_CEF_SHARED_PROCESS_MESSAGE_BUILDER_H_

View File

@ -207,6 +207,10 @@ struct CefMessageRouterConfig {
// Name of the JavaScript function that will be added to the 'window' object
// for canceling a pending query. The default value is "cefQueryCancel".
CefString js_cancel_function;
// Messages of size (in bytes) larger than this threshold will be sent via
// shared memory region.
size_t message_size_threshold;
};
///

View File

@ -42,6 +42,16 @@ void CefBrowserFrame::SendMessage(const std::string& name,
}
}
void CefBrowserFrame::SendSharedMemoryRegion(
const std::string& name,
base::ReadOnlySharedMemoryRegion region) {
// Always send to the newly created RFH, which may be speculative when
// navigating cross-origin.
if (auto host = GetFrameHost(/*prefer_speculative=*/true)) {
host->SendSharedMemoryRegion(name, std::move(region));
}
}
void CefBrowserFrame::FrameAttached(
mojo::PendingRemote<cef::mojom::RenderFrame> render_frame,
bool reattached) {

View File

@ -36,6 +36,8 @@ class CefBrowserFrame
private:
// cef::mojom::BrowserFrame methods:
void SendMessage(const std::string& name, base::Value arguments) override;
void SendSharedMemoryRegion(const std::string& name,
base::ReadOnlySharedMemoryRegion region) override;
void FrameAttached(mojo::PendingRemote<cef::mojom::RenderFrame> render_frame,
bool reattached) override;
void DidFinishFrameLoad(const GURL& validated_url,

View File

@ -13,6 +13,7 @@
#include "libcef/common/frame_util.h"
#include "libcef/common/net/url_util.h"
#include "libcef/common/process_message_impl.h"
#include "libcef/common/process_message_smr_impl.h"
#include "libcef/common/request_impl.h"
#include "libcef/common/string_util.h"
#include "libcef/common/task_runner_impl.h"
@ -238,18 +239,31 @@ void CefFrameHostImpl::SendProcessMessage(
if (!message || !message->IsValid())
return;
// Invalidate the message object immediately by taking the argument list.
auto argument_list =
static_cast<CefProcessMessageImpl*>(message.get())->TakeArgumentList();
if (message->GetArgumentList() != nullptr) {
// Invalidate the message object immediately by taking the argument list.
auto argument_list =
static_cast<CefProcessMessageImpl*>(message.get())->TakeArgumentList();
SendToRenderFrame(__FUNCTION__,
base::BindOnce(
[](const CefString& name, base::ListValue argument_list,
const RenderFrameType& render_frame) {
render_frame->SendMessage(name,
std::move(argument_list));
},
message->GetName(), std::move(argument_list)));
SendToRenderFrame(
__FUNCTION__,
base::BindOnce(
[](const CefString& name, base::ListValue argument_list,
const RenderFrameType& render_frame) {
render_frame->SendMessage(name, std::move(argument_list));
},
message->GetName(), std::move(argument_list)));
} else {
auto region =
static_cast<CefProcessMessageSMRImpl*>(message.get())->TakeRegion();
SendToRenderFrame(
__FUNCTION__,
base::BindOnce(
[](const CefString& name, base::ReadOnlySharedMemoryRegion region,
const RenderFrameType& render_frame) {
render_frame->SendSharedMemoryRegion(name, std::move(region));
},
message->GetName(), std::move(region)));
}
}
void CefFrameHostImpl::SetFocused(bool focused) {
@ -564,6 +578,19 @@ void CefFrameHostImpl::SendMessage(const std::string& name,
}
}
void CefFrameHostImpl::SendSharedMemoryRegion(
const std::string& name,
base::ReadOnlySharedMemoryRegion region) {
if (auto browser = GetBrowserHostBase()) {
if (auto client = browser->GetClient()) {
CefRefPtr<CefProcessMessage> message(
new CefProcessMessageSMRImpl(name, std::move(region)));
browser->GetClient()->OnProcessMessageReceived(browser.get(), this,
PID_RENDERER, message);
}
}
}
void CefFrameHostImpl::FrameAttached(
mojo::PendingRemote<cef::mojom::RenderFrame> render_frame_remote,
bool reattached) {

View File

@ -126,6 +126,8 @@ class CefFrameHostImpl : public CefFrame, public cef::mojom::BrowserFrame {
// cef::mojom::BrowserFrame methods forwarded from CefBrowserFrame.
void SendMessage(const std::string& name, base::Value arguments) override;
void SendSharedMemoryRegion(const std::string& name,
base::ReadOnlySharedMemoryRegion region) override;
void FrameAttached(mojo::PendingRemote<cef::mojom::RenderFrame> render_frame,
bool reattached) override;
void DidFinishFrameLoad(const GURL& validated_url,

View File

@ -55,6 +55,9 @@ interface RenderFrame {
// Send a message to the render process.
SendMessage(string name, mojo_base.mojom.Value arguments);
// Send a shared memory region to the render process.
SendSharedMemoryRegion(string name, mojo_base.mojom.ReadOnlySharedMemoryRegion region);
// Send a command.
SendCommand(string command);
@ -80,6 +83,9 @@ interface BrowserFrame {
// Send a message to the browser process.
SendMessage(string name, mojo_base.mojom.Value arguments);
// Send a shared memory region to the browser process.
SendSharedMemoryRegion(string name, mojo_base.mojom.ReadOnlySharedMemoryRegion region);
// The render frame is ready to begin handling actions.
FrameAttached(pending_remote<RenderFrame> render_frame,
bool reattached);

View File

@ -42,6 +42,9 @@ class CefProcessMessageImpl : public CefProcessMessage {
CefRefPtr<CefProcessMessage> Copy() override;
CefString GetName() override;
CefRefPtr<CefListValue> GetArgumentList() override;
CefRefPtr<CefSharedMemoryRegion> GetSharedMemoryRegion() override {
return nullptr;
}
private:
const CefString name_;

View File

@ -0,0 +1,90 @@
// 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.
#include "libcef/common/process_message_smr_impl.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/shared_memory_mapping.h"
namespace {
class CefSharedMemoryRegionImpl final : public CefSharedMemoryRegion {
public:
CefSharedMemoryRegionImpl(base::ReadOnlySharedMemoryMapping&& mapping)
: mapping_(std::move(mapping)) {}
CefSharedMemoryRegionImpl(const CefSharedMemoryRegionImpl&) = delete;
CefSharedMemoryRegionImpl& operator=(const CefSharedMemoryRegionImpl&) =
delete;
// CefSharedMemoryRegion methods
bool IsValid() override { return mapping_.IsValid(); }
size_t Size() override { return IsValid() ? mapping_.size() : 0; }
const void* Memory() override { return mapping_.memory(); }
private:
base::ReadOnlySharedMemoryMapping mapping_;
IMPLEMENT_REFCOUNTING(CefSharedMemoryRegionImpl);
};
} // namespace
CefProcessMessageSMRImpl::CefProcessMessageSMRImpl(
const CefString& name,
base::ReadOnlySharedMemoryRegion&& region)
: name_(name), region_(std::move(region)) {
DCHECK(!name_.empty());
DCHECK(region_.IsValid());
}
CefProcessMessageSMRImpl::~CefProcessMessageSMRImpl() = default;
bool CefProcessMessageSMRImpl::IsValid() {
return region_.IsValid();
}
CefString CefProcessMessageSMRImpl::GetName() {
return name_;
}
CefRefPtr<CefSharedMemoryRegion>
CefProcessMessageSMRImpl::GetSharedMemoryRegion() {
return new CefSharedMemoryRegionImpl(region_.Map());
}
base::ReadOnlySharedMemoryRegion CefProcessMessageSMRImpl::TakeRegion() {
return std::move(region_);
};
// static
CefRefPtr<CefSharedProcessMessageBuilder>
CefSharedProcessMessageBuilder::Create(const CefString& name,
size_t byte_size) {
return new CefSharedProcessMessageBuilderImpl(name, byte_size);
}
CefSharedProcessMessageBuilderImpl::CefSharedProcessMessageBuilderImpl(
const CefString& name,
size_t byte_size)
: name_(name),
region_(base::ReadOnlySharedMemoryRegion::Create(byte_size)) {}
bool CefSharedProcessMessageBuilderImpl::IsValid() {
return region_.region.IsValid() && region_.mapping.IsValid();
}
size_t CefSharedProcessMessageBuilderImpl::Size() {
return !IsValid() ? 0 : region_.mapping.size();
}
void* CefSharedProcessMessageBuilderImpl::Memory() {
return !IsValid() ? nullptr : region_.mapping.memory();
}
CefRefPtr<CefProcessMessage> CefSharedProcessMessageBuilderImpl::Build() {
if (!IsValid()) {
return nullptr;
}
return new CefProcessMessageSMRImpl(name_, std::move(region_.region));
}

View File

@ -0,0 +1,58 @@
// 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.
#ifndef CEF_LIBCEF_COMMON_PROCESS_MESSAGE_SMR_IMPL_H_
#define CEF_LIBCEF_COMMON_PROCESS_MESSAGE_SMR_IMPL_H_
#pragma once
#include "include/cef_process_message.h"
#include "include/cef_shared_process_message_builder.h"
#include "base/memory/read_only_shared_memory_region.h"
class CefProcessMessageSMRImpl final : public CefProcessMessage {
public:
CefProcessMessageSMRImpl(const CefString& name,
base::ReadOnlySharedMemoryRegion&& region);
CefProcessMessageSMRImpl(const CefProcessMessageSMRImpl&) = delete;
CefProcessMessageSMRImpl& operator=(const CefProcessMessageSMRImpl&) = delete;
~CefProcessMessageSMRImpl() override;
// CefProcessMessage methods.
bool IsValid() override;
bool IsReadOnly() override { return true; }
CefRefPtr<CefProcessMessage> Copy() override { return nullptr; }
CefString GetName() override;
CefRefPtr<CefListValue> GetArgumentList() override { return nullptr; };
CefRefPtr<CefSharedMemoryRegion> GetSharedMemoryRegion() override;
[[nodiscard]] base::ReadOnlySharedMemoryRegion TakeRegion();
private:
const CefString name_;
base::ReadOnlySharedMemoryRegion region_;
IMPLEMENT_REFCOUNTING(CefProcessMessageSMRImpl);
};
class CefSharedProcessMessageBuilderImpl final
: public CefSharedProcessMessageBuilder {
public:
CefSharedProcessMessageBuilderImpl(const CefString& name, size_t byte_size);
CefSharedProcessMessageBuilderImpl(const CefProcessMessageSMRImpl&) = delete;
CefSharedProcessMessageBuilderImpl& operator=(
const CefSharedProcessMessageBuilderImpl&) = delete;
bool IsValid() override;
size_t Size() override;
void* Memory() override;
CefRefPtr<CefProcessMessage> Build() override;
private:
const CefString name_;
base::MappedReadOnlyRegion region_;
IMPLEMENT_REFCOUNTING(CefSharedProcessMessageBuilderImpl);
};
#endif // CEF_LIBCEF_COMMON_PROCESS_MESSAGE_SMR_IMPL_H_

View File

@ -21,6 +21,7 @@
#include "libcef/common/frame_util.h"
#include "libcef/common/net/http_header_utils.h"
#include "libcef/common/process_message_impl.h"
#include "libcef/common/process_message_smr_impl.h"
#include "libcef/common/request_impl.h"
#include "libcef/common/string_util.h"
#include "libcef/renderer/blink_glue.h"
@ -270,16 +271,30 @@ void CefFrameImpl::SendProcessMessage(CefProcessId target_process,
if (!message || !message->IsValid())
return;
SendToBrowserFrame(
__FUNCTION__,
base::BindOnce(
[](CefRefPtr<CefProcessMessage> message,
const BrowserFrameType& browser_frame) {
auto impl = static_cast<CefProcessMessageImpl*>(message.get());
browser_frame->SendMessage(impl->GetName(),
impl->TakeArgumentList());
},
message));
if (message->GetArgumentList() != nullptr) {
// Invalidate the message object immediately by taking the argument list.
auto argument_list =
static_cast<CefProcessMessageImpl*>(message.get())->TakeArgumentList();
SendToBrowserFrame(
__FUNCTION__,
base::BindOnce(
[](const CefString& name, base::ListValue argument_list,
const BrowserFrameType& render_frame) {
render_frame->SendMessage(name, std::move(argument_list));
},
message->GetName(), std::move(argument_list)));
} else {
auto region =
static_cast<CefProcessMessageSMRImpl*>(message.get())->TakeRegion();
SendToBrowserFrame(
__FUNCTION__,
base::BindOnce(
[](const CefString& name, base::ReadOnlySharedMemoryRegion region,
const BrowserFrameType& render_frame) {
render_frame->SendSharedMemoryRegion(name, std::move(region));
},
message->GetName(), std::move(region)));
}
}
std::unique_ptr<blink::WebURLLoader> CefFrameImpl::CreateURLLoader() {
@ -654,6 +669,18 @@ void CefFrameImpl::SendMessage(const std::string& name, base::Value arguments) {
}
}
void CefFrameImpl::SendSharedMemoryRegion(
const std::string& name,
base::ReadOnlySharedMemoryRegion region) {
if (auto app = CefAppManager::Get()->GetApplication()) {
if (auto handler = app->GetRenderProcessHandler()) {
CefRefPtr<CefProcessMessage> message(
new CefProcessMessageSMRImpl(name, std::move(region)));
handler->OnProcessMessageReceived(browser_, this, PID_BROWSER, message);
}
}
}
void CefFrameImpl::SendCommand(const std::string& command) {
ExecuteOnLocalFrame(
__FUNCTION__,

View File

@ -135,6 +135,8 @@ class CefFrameImpl
// cef::mojom::RenderFrame methods:
void FrameAttachedAck() override;
void SendMessage(const std::string& name, base::Value arguments) override;
void SendSharedMemoryRegion(const std::string& name,
base::ReadOnlySharedMemoryRegion region) override;
void SendCommand(const std::string& command) override;
void SendCommandWithResponse(
const std::string& command,

View File

@ -9,11 +9,12 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=b63f665e68e4dc6269c3e88b81068190ea90abb3$
// $hash=5d331596c0425f145a19d8de6a866841d9ed8a87$
//
#include "libcef_dll/cpptoc/process_message_cpptoc.h"
#include "libcef_dll/cpptoc/list_value_cpptoc.h"
#include "libcef_dll/cpptoc/shared_memory_region_cpptoc.h"
#include "libcef_dll/shutdown_checker.h"
// GLOBAL FUNCTIONS - Body may be edited by hand.
@ -127,6 +128,24 @@ process_message_get_argument_list(struct _cef_process_message_t* self) {
return CefListValueCppToC::Wrap(_retval);
}
struct _cef_shared_memory_region_t* CEF_CALLBACK
process_message_get_shared_memory_region(struct _cef_process_message_t* self) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return NULL;
// Execute
CefRefPtr<CefSharedMemoryRegion> _retval =
CefProcessMessageCppToC::Get(self)->GetSharedMemoryRegion();
// Return type: refptr_same
return CefSharedMemoryRegionCppToC::Wrap(_retval);
}
} // namespace
// CONSTRUCTOR - Do not edit by hand.
@ -137,6 +156,8 @@ CefProcessMessageCppToC::CefProcessMessageCppToC() {
GetStruct()->copy = process_message_copy;
GetStruct()->get_name = process_message_get_name;
GetStruct()->get_argument_list = process_message_get_argument_list;
GetStruct()->get_shared_memory_region =
process_message_get_shared_memory_region;
}
// DESTRUCTOR - Do not edit by hand.

View File

@ -0,0 +1,105 @@
// 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=b8c6bf91bf16cb696121e7d304c21fbcdc927ffd$
//
#include "libcef_dll/cpptoc/shared_memory_region_cpptoc.h"
#include "libcef_dll/shutdown_checker.h"
namespace {
// MEMBER FUNCTIONS - Body may be edited by hand.
int CEF_CALLBACK
shared_memory_region_is_valid(struct _cef_shared_memory_region_t* self) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return 0;
// Execute
bool _retval = CefSharedMemoryRegionCppToC::Get(self)->IsValid();
// Return type: bool
return _retval;
}
size_t CEF_CALLBACK
shared_memory_region_size(struct _cef_shared_memory_region_t* self) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return 0;
// Execute
size_t _retval = CefSharedMemoryRegionCppToC::Get(self)->Size();
// Return type: simple
return _retval;
}
const void* CEF_CALLBACK
shared_memory_region_memory(struct _cef_shared_memory_region_t* self) {
shutdown_checker::AssertNotShutdown();
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return 0;
// Execute
const void* _retval = CefSharedMemoryRegionCppToC::Get(self)->Memory();
// Return type: simple
return _retval;
}
} // namespace
// CONSTRUCTOR - Do not edit by hand.
CefSharedMemoryRegionCppToC::CefSharedMemoryRegionCppToC() {
GetStruct()->is_valid = shared_memory_region_is_valid;
GetStruct()->size = shared_memory_region_size;
GetStruct()->memory = shared_memory_region_memory;
}
// DESTRUCTOR - Do not edit by hand.
CefSharedMemoryRegionCppToC::~CefSharedMemoryRegionCppToC() {
shutdown_checker::AssertNotShutdown();
}
template <>
CefRefPtr<CefSharedMemoryRegion> CefCppToCRefCounted<
CefSharedMemoryRegionCppToC,
CefSharedMemoryRegion,
cef_shared_memory_region_t>::UnwrapDerived(CefWrapperType type,
cef_shared_memory_region_t* s) {
NOTREACHED() << "Unexpected class type: " << type;
return nullptr;
}
template <>
CefWrapperType CefCppToCRefCounted<CefSharedMemoryRegionCppToC,
CefSharedMemoryRegion,
cef_shared_memory_region_t>::kWrapperType =
WT_SHARED_MEMORY_REGION;

View File

@ -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=77de7d9d9e62fa5075ff6e76e4647639c51a7f05$
//
#ifndef CEF_LIBCEF_DLL_CPPTOC_SHARED_MEMORY_REGION_CPPTOC_H_
#define CEF_LIBCEF_DLL_CPPTOC_SHARED_MEMORY_REGION_CPPTOC_H_
#pragma once
#if !defined(BUILDING_CEF_SHARED)
#error This file can be included DLL-side only
#endif
#include "include/capi/cef_shared_memory_region_capi.h"
#include "include/cef_shared_memory_region.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 CefSharedMemoryRegionCppToC
: public CefCppToCRefCounted<CefSharedMemoryRegionCppToC,
CefSharedMemoryRegion,
cef_shared_memory_region_t> {
public:
CefSharedMemoryRegionCppToC();
virtual ~CefSharedMemoryRegionCppToC();
};
#endif // CEF_LIBCEF_DLL_CPPTOC_SHARED_MEMORY_REGION_CPPTOC_H_

View File

@ -0,0 +1,145 @@
// 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=b3c3551c52baf66ab3cd4ee485313ef41a29313b$
//
#include "libcef_dll/cpptoc/shared_process_message_builder_cpptoc.h"
#include "libcef_dll/cpptoc/process_message_cpptoc.h"
#include "libcef_dll/shutdown_checker.h"
// GLOBAL FUNCTIONS - Body may be edited by hand.
CEF_EXPORT cef_shared_process_message_builder_t*
cef_shared_process_message_builder_create(const cef_string_t* name,
size_t byte_size) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: name; type: string_byref_const
DCHECK(name);
if (!name)
return NULL;
// Execute
CefRefPtr<CefSharedProcessMessageBuilder> _retval =
CefSharedProcessMessageBuilder::Create(CefString(name), byte_size);
// Return type: refptr_same
return CefSharedProcessMessageBuilderCppToC::Wrap(_retval);
}
namespace {
// MEMBER FUNCTIONS - Body may be edited by hand.
int CEF_CALLBACK shared_process_message_builder_is_valid(
struct _cef_shared_process_message_builder_t* self) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return 0;
// Execute
bool _retval = CefSharedProcessMessageBuilderCppToC::Get(self)->IsValid();
// Return type: bool
return _retval;
}
size_t CEF_CALLBACK shared_process_message_builder_size(
struct _cef_shared_process_message_builder_t* self) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return 0;
// Execute
size_t _retval = CefSharedProcessMessageBuilderCppToC::Get(self)->Size();
// Return type: simple
return _retval;
}
void* CEF_CALLBACK shared_process_message_builder_memory(
struct _cef_shared_process_message_builder_t* self) {
shutdown_checker::AssertNotShutdown();
DCHECK(self);
if (!self)
return 0;
// Execute
void* _retval = CefSharedProcessMessageBuilderCppToC::Get(self)->Memory();
// Return type: simple
return _retval;
}
cef_process_message_t* CEF_CALLBACK shared_process_message_builder_build(
struct _cef_shared_process_message_builder_t* self) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return NULL;
// Execute
CefRefPtr<CefProcessMessage> _retval =
CefSharedProcessMessageBuilderCppToC::Get(self)->Build();
// Return type: refptr_same
return CefProcessMessageCppToC::Wrap(_retval);
}
} // namespace
// CONSTRUCTOR - Do not edit by hand.
CefSharedProcessMessageBuilderCppToC::CefSharedProcessMessageBuilderCppToC() {
GetStruct()->is_valid = shared_process_message_builder_is_valid;
GetStruct()->size = shared_process_message_builder_size;
GetStruct()->memory = shared_process_message_builder_memory;
GetStruct()->build = shared_process_message_builder_build;
}
// DESTRUCTOR - Do not edit by hand.
CefSharedProcessMessageBuilderCppToC::~CefSharedProcessMessageBuilderCppToC() {
shutdown_checker::AssertNotShutdown();
}
template <>
CefRefPtr<CefSharedProcessMessageBuilder>
CefCppToCRefCounted<CefSharedProcessMessageBuilderCppToC,
CefSharedProcessMessageBuilder,
cef_shared_process_message_builder_t>::
UnwrapDerived(CefWrapperType type,
cef_shared_process_message_builder_t* s) {
NOTREACHED() << "Unexpected class type: " << type;
return nullptr;
}
template <>
CefWrapperType
CefCppToCRefCounted<CefSharedProcessMessageBuilderCppToC,
CefSharedProcessMessageBuilder,
cef_shared_process_message_builder_t>::kWrapperType =
WT_SHARED_PROCESS_MESSAGE_BUILDER;

View File

@ -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=a3ec0a78d1e092d99780b6ec87d345a20c081be6$
//
#ifndef CEF_LIBCEF_DLL_CPPTOC_SHARED_PROCESS_MESSAGE_BUILDER_CPPTOC_H_
#define CEF_LIBCEF_DLL_CPPTOC_SHARED_PROCESS_MESSAGE_BUILDER_CPPTOC_H_
#pragma once
#if !defined(BUILDING_CEF_SHARED)
#error This file can be included DLL-side only
#endif
#include "include/capi/cef_shared_process_message_builder_capi.h"
#include "include/cef_shared_process_message_builder.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 CefSharedProcessMessageBuilderCppToC
: public CefCppToCRefCounted<CefSharedProcessMessageBuilderCppToC,
CefSharedProcessMessageBuilder,
cef_shared_process_message_builder_t> {
public:
CefSharedProcessMessageBuilderCppToC();
virtual ~CefSharedProcessMessageBuilderCppToC();
};
#endif // CEF_LIBCEF_DLL_CPPTOC_SHARED_PROCESS_MESSAGE_BUILDER_CPPTOC_H_

View File

@ -9,11 +9,12 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=7ab779c6c98a1bd2385f14d514304a28ef58717f$
// $hash=0f16a0f4711caf0b3761bf6480622de2e75b72ef$
//
#include "libcef_dll/ctocpp/process_message_ctocpp.h"
#include "libcef_dll/ctocpp/list_value_ctocpp.h"
#include "libcef_dll/ctocpp/shared_memory_region_ctocpp.h"
#include "libcef_dll/shutdown_checker.h"
// STATIC METHODS - Body may be edited by hand.
@ -122,6 +123,25 @@ CefRefPtr<CefListValue> CefProcessMessageCToCpp::GetArgumentList() {
return CefListValueCToCpp::Wrap(_retval);
}
NO_SANITIZE("cfi-icall")
CefRefPtr<CefSharedMemoryRegion>
CefProcessMessageCToCpp::GetSharedMemoryRegion() {
shutdown_checker::AssertNotShutdown();
cef_process_message_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, get_shared_memory_region))
return nullptr;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Execute
cef_shared_memory_region_t* _retval =
_struct->get_shared_memory_region(_struct);
// Return type: refptr_same
return CefSharedMemoryRegionCToCpp::Wrap(_retval);
}
// CONSTRUCTOR - Do not edit by hand.
CefProcessMessageCToCpp::CefProcessMessageCToCpp() {}

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=39bf2321370b32cf02bf502529568e935b303550$
// $hash=f52b0dfcee0d432a334166fce348234d0d239b85$
//
#ifndef CEF_LIBCEF_DLL_CTOCPP_PROCESS_MESSAGE_CTOCPP_H_
@ -40,6 +40,7 @@ class CefProcessMessageCToCpp
CefRefPtr<CefProcessMessage> Copy() override;
CefString GetName() override;
CefRefPtr<CefListValue> GetArgumentList() override;
CefRefPtr<CefSharedMemoryRegion> GetSharedMemoryRegion() override;
};
#endif // CEF_LIBCEF_DLL_CTOCPP_PROCESS_MESSAGE_CTOCPP_H_

View File

@ -0,0 +1,90 @@
// 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=fd0a4c1bd80a53778d6e40e4ebcd9d4484f91269$
//
#include "libcef_dll/ctocpp/shared_memory_region_ctocpp.h"
#include "libcef_dll/shutdown_checker.h"
// VIRTUAL METHODS - Body may be edited by hand.
NO_SANITIZE("cfi-icall") bool CefSharedMemoryRegionCToCpp::IsValid() {
shutdown_checker::AssertNotShutdown();
cef_shared_memory_region_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, is_valid))
return false;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Execute
int _retval = _struct->is_valid(_struct);
// Return type: bool
return _retval ? true : false;
}
NO_SANITIZE("cfi-icall") size_t CefSharedMemoryRegionCToCpp::Size() {
shutdown_checker::AssertNotShutdown();
cef_shared_memory_region_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, size))
return 0;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Execute
size_t _retval = _struct->size(_struct);
// Return type: simple
return _retval;
}
NO_SANITIZE("cfi-icall") const void* CefSharedMemoryRegionCToCpp::Memory() {
shutdown_checker::AssertNotShutdown();
cef_shared_memory_region_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, memory))
return nullptr;
// Execute
const void* _retval = _struct->memory(_struct);
// Return type: simple
return _retval;
}
// CONSTRUCTOR - Do not edit by hand.
CefSharedMemoryRegionCToCpp::CefSharedMemoryRegionCToCpp() {}
// DESTRUCTOR - Do not edit by hand.
CefSharedMemoryRegionCToCpp::~CefSharedMemoryRegionCToCpp() {
shutdown_checker::AssertNotShutdown();
}
template <>
cef_shared_memory_region_t* CefCToCppRefCounted<
CefSharedMemoryRegionCToCpp,
CefSharedMemoryRegion,
cef_shared_memory_region_t>::UnwrapDerived(CefWrapperType type,
CefSharedMemoryRegion* c) {
NOTREACHED() << "Unexpected class type: " << type;
return nullptr;
}
template <>
CefWrapperType CefCToCppRefCounted<CefSharedMemoryRegionCToCpp,
CefSharedMemoryRegion,
cef_shared_memory_region_t>::kWrapperType =
WT_SHARED_MEMORY_REGION;

View File

@ -0,0 +1,43 @@
// 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=d477b2660e271bbdc4d6e8f866f006b84f13e50d$
//
#ifndef CEF_LIBCEF_DLL_CTOCPP_SHARED_MEMORY_REGION_CTOCPP_H_
#define CEF_LIBCEF_DLL_CTOCPP_SHARED_MEMORY_REGION_CTOCPP_H_
#pragma once
#if !defined(WRAPPING_CEF_SHARED)
#error This file can be included wrapper-side only
#endif
#include "include/capi/cef_shared_memory_region_capi.h"
#include "include/cef_shared_memory_region.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 CefSharedMemoryRegionCToCpp
: public CefCToCppRefCounted<CefSharedMemoryRegionCToCpp,
CefSharedMemoryRegion,
cef_shared_memory_region_t> {
public:
CefSharedMemoryRegionCToCpp();
virtual ~CefSharedMemoryRegionCToCpp();
// CefSharedMemoryRegion methods.
bool IsValid() override;
size_t Size() override;
const void* Memory() override;
};
#endif // CEF_LIBCEF_DLL_CTOCPP_SHARED_MEMORY_REGION_CTOCPP_H_

View File

@ -0,0 +1,132 @@
// 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=a20e093efb07503fe9e64c97c14091e3921544cd$
//
#include "libcef_dll/ctocpp/shared_process_message_builder_ctocpp.h"
#include "libcef_dll/ctocpp/process_message_ctocpp.h"
#include "libcef_dll/shutdown_checker.h"
// STATIC METHODS - Body may be edited by hand.
NO_SANITIZE("cfi-icall")
CefRefPtr<CefSharedProcessMessageBuilder>
CefSharedProcessMessageBuilder::Create(const CefString& name,
size_t byte_size) {
shutdown_checker::AssertNotShutdown();
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: name; type: string_byref_const
DCHECK(!name.empty());
if (name.empty())
return nullptr;
// Execute
cef_shared_process_message_builder_t* _retval =
cef_shared_process_message_builder_create(name.GetStruct(), byte_size);
// Return type: refptr_same
return CefSharedProcessMessageBuilderCToCpp::Wrap(_retval);
}
// VIRTUAL METHODS - Body may be edited by hand.
NO_SANITIZE("cfi-icall") bool CefSharedProcessMessageBuilderCToCpp::IsValid() {
shutdown_checker::AssertNotShutdown();
cef_shared_process_message_builder_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, is_valid))
return false;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Execute
int _retval = _struct->is_valid(_struct);
// Return type: bool
return _retval ? true : false;
}
NO_SANITIZE("cfi-icall") size_t CefSharedProcessMessageBuilderCToCpp::Size() {
shutdown_checker::AssertNotShutdown();
cef_shared_process_message_builder_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, size))
return 0;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Execute
size_t _retval = _struct->size(_struct);
// Return type: simple
return _retval;
}
NO_SANITIZE("cfi-icall") void* CefSharedProcessMessageBuilderCToCpp::Memory() {
shutdown_checker::AssertNotShutdown();
cef_shared_process_message_builder_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, memory))
return nullptr;
// Execute
void* _retval = _struct->memory(_struct);
// Return type: simple
return _retval;
}
NO_SANITIZE("cfi-icall")
CefRefPtr<CefProcessMessage> CefSharedProcessMessageBuilderCToCpp::Build() {
shutdown_checker::AssertNotShutdown();
cef_shared_process_message_builder_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, build))
return nullptr;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Execute
cef_process_message_t* _retval = _struct->build(_struct);
// Return type: refptr_same
return CefProcessMessageCToCpp::Wrap(_retval);
}
// CONSTRUCTOR - Do not edit by hand.
CefSharedProcessMessageBuilderCToCpp::CefSharedProcessMessageBuilderCToCpp() {}
// DESTRUCTOR - Do not edit by hand.
CefSharedProcessMessageBuilderCToCpp::~CefSharedProcessMessageBuilderCToCpp() {
shutdown_checker::AssertNotShutdown();
}
template <>
cef_shared_process_message_builder_t*
CefCToCppRefCounted<CefSharedProcessMessageBuilderCToCpp,
CefSharedProcessMessageBuilder,
cef_shared_process_message_builder_t>::
UnwrapDerived(CefWrapperType type, CefSharedProcessMessageBuilder* c) {
NOTREACHED() << "Unexpected class type: " << type;
return nullptr;
}
template <>
CefWrapperType
CefCToCppRefCounted<CefSharedProcessMessageBuilderCToCpp,
CefSharedProcessMessageBuilder,
cef_shared_process_message_builder_t>::kWrapperType =
WT_SHARED_PROCESS_MESSAGE_BUILDER;

View File

@ -0,0 +1,44 @@
// 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=0a8bf76a2347d7afc98e1cbeb61b88be4bc7e789$
//
#ifndef CEF_LIBCEF_DLL_CTOCPP_SHARED_PROCESS_MESSAGE_BUILDER_CTOCPP_H_
#define CEF_LIBCEF_DLL_CTOCPP_SHARED_PROCESS_MESSAGE_BUILDER_CTOCPP_H_
#pragma once
#if !defined(WRAPPING_CEF_SHARED)
#error This file can be included wrapper-side only
#endif
#include "include/capi/cef_shared_process_message_builder_capi.h"
#include "include/cef_shared_process_message_builder.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 CefSharedProcessMessageBuilderCToCpp
: public CefCToCppRefCounted<CefSharedProcessMessageBuilderCToCpp,
CefSharedProcessMessageBuilder,
cef_shared_process_message_builder_t> {
public:
CefSharedProcessMessageBuilderCToCpp();
virtual ~CefSharedProcessMessageBuilderCToCpp();
// CefSharedProcessMessageBuilder methods.
bool IsValid() override;
size_t Size() override;
void* Memory() override;
CefRefPtr<CefProcessMessage> Build() override;
};
#endif // CEF_LIBCEF_DLL_CTOCPP_SHARED_PROCESS_MESSAGE_BUILDER_CTOCPP_H_

View File

@ -8,6 +8,7 @@
#include <set>
#include "include/base/cef_callback.h"
#include "include/cef_shared_process_message_builder.h"
#include "include/cef_task.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
@ -31,6 +32,10 @@ const char kMemberPersistent[] = "persistent";
const int kCanceledErrorCode = -1;
const char kCanceledErrorMessage[] = "The query has been canceled";
// Value of 16KB is chosen as a result of performance tests available at
// http://tests/ipc_performance
constexpr size_t kResponseSizeThreshold = 16384;
// Validate configuration settings.
bool ValidateConfig(CefMessageRouterConfig& config) {
// Must specify function names.
@ -41,6 +46,124 @@ bool ValidateConfig(CefMessageRouterConfig& config) {
return true;
}
struct MessageHeader {
int context_id;
int request_id;
bool is_success;
};
struct ParsedMessage {
int context_id;
int request_id;
bool success;
int error_code;
CefString message;
};
size_t GetMessageSize(const CefString& response) {
return sizeof(MessageHeader) +
(response.size() * sizeof(CefString::char_type));
}
void CopyResponseIntoMemory(void* memory, const CefString& response) {
const size_t bytes = response.size() * sizeof(CefString::char_type);
void* dest = static_cast<uint8_t*>(memory) + sizeof(MessageHeader);
memcpy(dest, response.c_str(), bytes);
}
CefString GetStringFromMemory(const void* memory, size_t size) {
const size_t bytes = size - sizeof(MessageHeader);
const size_t string_len = bytes / sizeof(CefString::char_type);
const CefString::char_type* src =
reinterpret_cast<const CefString::char_type*>(
static_cast<const uint8_t*>(memory) + sizeof(MessageHeader));
constexpr bool copy = true;
CefString result;
result.FromString(src, string_len, copy);
return result;
}
CefRefPtr<CefProcessMessage> BuildListMessage(const std::string& message_name,
int context_id,
int request_id,
const CefString& response) {
auto message = CefProcessMessage::Create(message_name);
CefRefPtr<CefListValue> args = message->GetArgumentList();
args->SetInt(0, context_id);
args->SetInt(1, request_id);
args->SetBool(2, true); // Indicates a success result.
args->SetString(3, response);
return message;
}
CefRefPtr<CefProcessMessage> BuildBinaryMessage(const std::string& message_name,
int context_id,
int request_id,
const CefString& response) {
const size_t message_size = GetMessageSize(response);
auto builder =
CefSharedProcessMessageBuilder::Create(message_name, message_size);
if (!builder->IsValid()) {
LOG(ERROR) << "Failed to allocate shared memory region of size "
<< message_size;
// Use list message as a fallback
return BuildListMessage(message_name, context_id, request_id, response);
}
auto header = static_cast<MessageHeader*>(builder->Memory());
header->context_id = context_id;
header->request_id = request_id;
header->is_success = true;
CopyResponseIntoMemory(builder->Memory(), response);
return builder->Build();
}
CefRefPtr<CefProcessMessage> BuildMessage(size_t threshold,
const std::string& message_name,
int context_id,
int request_id,
const CefString& response) {
if (response.size() <= threshold) {
return BuildListMessage(message_name, context_id, request_id, response);
} else {
return BuildBinaryMessage(message_name, context_id, request_id, response);
}
}
ParsedMessage ParseMessage(const CefRefPtr<CefProcessMessage>& message) {
if (auto args = message->GetArgumentList()) {
DCHECK_GT(args->GetSize(), 3U);
const int context_id = args->GetInt(0);
const int request_id = args->GetInt(1);
const bool is_success = args->GetBool(2);
if (is_success) {
return ParsedMessage{context_id, request_id, is_success, 0,
args->GetString(3)};
}
DCHECK_EQ(args->GetSize(), 5U);
return ParsedMessage{context_id, request_id, is_success, args->GetInt(3),
args->GetString(4)};
}
if (const auto region = message->GetSharedMemoryRegion()) {
if (region->IsValid()) {
DCHECK_GE(region->Size(), sizeof(MessageHeader));
auto header = static_cast<const MessageHeader*>(region->Memory());
DCHECK(header->is_success);
return ParsedMessage{
header->context_id, header->request_id, header->is_success, 0,
GetStringFromMemory(region->Memory(), region->Size())};
}
}
return ParsedMessage{};
}
// Helper template for generated ID values.
template <typename T>
class IdGenerator {
@ -428,14 +551,11 @@ class CefMessageRouterBrowserSideImpl : public CefMessageRouterBrowserSide {
int context_id,
int request_id,
const CefString& response) {
CefRefPtr<CefProcessMessage> message =
CefProcessMessage::Create(query_message_name_);
CefRefPtr<CefListValue> args = message->GetArgumentList();
args->SetInt(0, context_id);
args->SetInt(1, request_id);
args->SetBool(2, true); // Indicates a success result.
args->SetString(3, response);
frame->SendProcessMessage(PID_RENDERER, message);
if (auto message =
BuildMessage(config_.message_size_threshold, query_message_name_,
context_id, request_id, response)) {
frame->SendProcessMessage(PID_RENDERER, message);
}
}
void SendQueryFailure(QueryInfo* info,
@ -817,31 +937,21 @@ class CefMessageRouterRendererSideImpl : public CefMessageRouterRendererSide {
const std::string& message_name = message->GetName();
if (message_name == query_message_name_) {
CefRefPtr<CefListValue> args = message->GetArgumentList();
DCHECK_GT(args->GetSize(), 3U);
const int context_id = args->GetInt(0);
const int request_id = args->GetInt(1);
bool is_success = args->GetBool(2);
if (is_success) {
DCHECK_EQ(args->GetSize(), 4U);
const CefString& response = args->GetString(3);
auto content = ParseMessage(message);
if (content.success) {
CefPostTask(
TID_RENDERER,
base::BindOnce(
&CefMessageRouterRendererSideImpl::ExecuteSuccessCallback, this,
browser->GetIdentifier(), context_id, request_id, response));
browser->GetIdentifier(), content.context_id,
content.request_id, content.message));
} else {
DCHECK_EQ(args->GetSize(), 5U);
int error_code = args->GetInt(3);
const CefString& error_message = args->GetString(4);
CefPostTask(
TID_RENDERER,
base::BindOnce(
&CefMessageRouterRendererSideImpl::ExecuteFailureCallback, this,
browser->GetIdentifier(), context_id, request_id, error_code,
error_message));
browser->GetIdentifier(), content.context_id,
content.request_id, content.error_code, content.message));
}
return true;
@ -913,10 +1023,9 @@ class CefMessageRouterRendererSideImpl : public CefMessageRouterRendererSide {
const int request_id = request_id_generator_.GetNextId();
RequestInfo* info = new RequestInfo;
info->persistent = persistent;
info->success_callback = success_callback;
info->failure_callback = failure_callback;
auto* info =
new RequestInfo{persistent, success_callback, failure_callback};
browser_request_info_map_.Add(browser->GetIdentifier(),
std::make_pair(context_id, request_id), info);
@ -1117,7 +1226,9 @@ class CefMessageRouterRendererSideImpl : public CefMessageRouterRendererSide {
} // namespace
CefMessageRouterConfig::CefMessageRouterConfig()
: js_query_function("cefQuery"), js_cancel_function("cefQueryCancel") {}
: js_query_function("cefQuery"),
js_cancel_function("cefQueryCancel"),
message_size_threshold(kResponseSizeThreshold) {}
// static
CefRefPtr<CefMessageRouterBrowserSide> CefMessageRouterBrowserSide::Create(

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=5bf495a6015a7c0937225685bfbe8e0163e67583$
// $hash=9c3fcd2a053f20a9a09b9938996f5df9eb2053ef$
//
#include <dlfcn.h>
@ -39,6 +39,7 @@
#include "include/capi/cef_response_capi.h"
#include "include/capi/cef_scheme_capi.h"
#include "include/capi/cef_server_capi.h"
#include "include/capi/cef_shared_process_message_builder_capi.h"
#include "include/capi/cef_ssl_info_capi.h"
#include "include/capi/cef_stream_capi.h"
#include "include/capi/cef_task_capi.h"
@ -219,6 +220,9 @@ typedef void (*cef_server_create_ptr)(const cef_string_t*,
uint16,
int,
struct _cef_server_handler_t*);
typedef struct _cef_shared_process_message_builder_t* (
*cef_shared_process_message_builder_create_ptr)(const cef_string_t*,
size_t);
typedef struct _cef_stream_reader_t* (*cef_stream_reader_create_for_file_ptr)(
const cef_string_t*);
typedef struct _cef_stream_reader_t* (
@ -580,6 +584,8 @@ struct libcef_pointers {
cef_resource_bundle_get_global_ptr cef_resource_bundle_get_global;
cef_response_create_ptr cef_response_create;
cef_server_create_ptr cef_server_create;
cef_shared_process_message_builder_create_ptr
cef_shared_process_message_builder_create;
cef_stream_reader_create_for_file_ptr cef_stream_reader_create_for_file;
cef_stream_reader_create_for_data_ptr cef_stream_reader_create_for_data;
cef_stream_reader_create_for_handler_ptr cef_stream_reader_create_for_handler;
@ -789,6 +795,7 @@ int libcef_init_pointers(const char* path) {
INIT_ENTRY(cef_resource_bundle_get_global);
INIT_ENTRY(cef_response_create);
INIT_ENTRY(cef_server_create);
INIT_ENTRY(cef_shared_process_message_builder_create);
INIT_ENTRY(cef_stream_reader_create_for_file);
INIT_ENTRY(cef_stream_reader_create_for_data);
INIT_ENTRY(cef_stream_reader_create_for_handler);
@ -1341,6 +1348,14 @@ void cef_server_create(const cef_string_t* address,
g_libcef_pointers.cef_server_create(address, port, backlog, handler);
}
NO_SANITIZE("cfi-icall")
struct _cef_shared_process_message_builder_t*
cef_shared_process_message_builder_create(const cef_string_t* name,
size_t byte_size) {
return g_libcef_pointers.cef_shared_process_message_builder_create(name,
byte_size);
}
NO_SANITIZE("cfi-icall")
struct _cef_stream_reader_t* cef_stream_reader_create_for_file(
const cef_string_t* fileName) {

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=54ed016f3b2bbffebc96ac9cadd3c3986d240342$
// $hash=27716558f027af36498437317407384392a34b71$
//
#ifndef CEF_LIBCEF_DLL_WRAPPER_TYPES_H_
@ -133,6 +133,8 @@ enum CefWrapperType {
WT_SERVER,
WT_SERVER_HANDLER,
WT_SET_COOKIE_CALLBACK,
WT_SHARED_MEMORY_REGION,
WT_SHARED_PROCESS_MESSAGE_BUILDER,
WT_STREAM_READER,
WT_STREAM_WRITER,
WT_STRING_VISITOR,

View File

@ -16,6 +16,7 @@
#include "include/cef_command_ids.h"
#include "include/cef_frame.h"
#include "include/cef_parser.h"
#include "include/cef_shared_process_message_builder.h"
#include "include/cef_ssl_status.h"
#include "include/cef_x509_certificate.h"
#include "include/wrapper/cef_closure_task.h"
@ -24,6 +25,7 @@
#include "tests/cefclient/browser/test_runner.h"
#include "tests/shared/browser/extension_util.h"
#include "tests/shared/browser/resource_util.h"
#include "tests/shared/common/binary_value_utils.h"
#include "tests/shared/common/client_switches.h"
namespace client {
@ -52,7 +54,7 @@ enum client_menu_ids {
CLIENT_ID_TESTMENU_RADIOITEM3,
};
// Musr match the value in client_renderer.cc.
// Must match the value in client_renderer.cc.
const char kFocusedNodeChangedMessage[] = "ClientRenderer.FocusedNodeChanged";
std::string GetTimeString(const CefTime& value) {
@ -224,6 +226,66 @@ std::string GetCertificateInformation(CefRefPtr<CefX509Certificate> cert,
return ss.str();
}
void OnTestProcessMessageReceived(
const CefRefPtr<CefFrame>& frame,
const CefRefPtr<CefProcessMessage>& process_message,
const bv_utils::TimePoint& finish_time) {
DCHECK(process_message->IsValid());
CefRefPtr<CefListValue> input_args = process_message->GetArgumentList();
DCHECK_EQ(input_args->GetSize(), 1U);
const auto renderer_msg =
bv_utils::GetRendererMsgFromBinary(input_args->GetBinary(0));
CefRefPtr<CefProcessMessage> response =
CefProcessMessage::Create(bv_utils::kTestSendProcessMessage);
CefRefPtr<CefListValue> args = response->GetArgumentList();
const auto message_size = std::max(input_args->GetBinary(0)->GetSize(),
sizeof(bv_utils::BrowserMessage));
std::vector<uint8_t> data(message_size);
const auto browser_msg =
reinterpret_cast<bv_utils::BrowserMessage*>(data.data());
browser_msg->test_id = renderer_msg.test_id;
browser_msg->duration = finish_time - renderer_msg.start_time;
browser_msg->start_time = bv_utils::Now();
args->SetBinary(0, bv_utils::CreateCefBinaryValue(data));
frame->SendProcessMessage(PID_RENDERER, response);
}
void OnTestSMRProcessMessageReceived(
const CefRefPtr<CefFrame>& frame,
const CefRefPtr<CefProcessMessage>& process_message,
const bv_utils::TimePoint& finish_time) {
DCHECK(process_message->IsValid());
CefRefPtr<CefSharedMemoryRegion> region =
process_message->GetSharedMemoryRegion();
DCHECK_GE(region->Size(), sizeof(bv_utils::RendererMessage));
const auto renderer_msg =
static_cast<const bv_utils::RendererMessage*>(region->Memory());
const auto message_size =
std::max(region->Size(), sizeof(bv_utils::BrowserMessage));
const auto renderer_time = renderer_msg->start_time;
const auto duration = finish_time - renderer_time;
const auto start_time = bv_utils::Now();
auto builder = CefSharedProcessMessageBuilder::Create(
bv_utils::kTestSendSMRProcessMessage, message_size);
const auto browser_msg =
static_cast<bv_utils::BrowserMessage*>(builder->Memory());
browser_msg->test_id = renderer_msg->test_id;
browser_msg->duration = duration;
browser_msg->start_time = start_time;
frame->SendProcessMessage(PID_RENDERER, builder->Build());
}
} // namespace
class ClientDownloadImageCallback : public CefDownloadImageCallback {
@ -332,13 +394,15 @@ bool ClientHandler::OnProcessMessageReceived(
CefRefPtr<CefProcessMessage> message) {
CEF_REQUIRE_UI_THREAD();
const auto finish_time = bv_utils::Now();
if (message_router_->OnProcessMessageReceived(browser, frame, source_process,
message)) {
return true;
}
// Check for messages from the client renderer.
std::string message_name = message->GetName();
const std::string& message_name = message->GetName();
if (message_name == kFocusedNodeChangedMessage) {
// A message is sent from ClientRenderDelegate to tell us whether the
// currently focused DOM node is editable. Use of |focus_on_editable_field_|
@ -348,6 +412,16 @@ bool ClientHandler::OnProcessMessageReceived(
return true;
}
if (message_name == bv_utils::kTestSendProcessMessage) {
OnTestProcessMessageReceived(frame, message, finish_time);
return true;
}
if (message_name == bv_utils::kTestSendSMRProcessMessage) {
OnTestSMRProcessMessageReceived(frame, message, finish_time);
return true;
}
return false;
}

View File

@ -46,27 +46,28 @@
#define IDS_BINDING_HTML 1000
#define IDS_DIALOGS_HTML 1001
#define IDS_DRAGGABLE_HTML 1002
#define IDS_LOCALSTORAGE_HTML 1003
#define IDS_LOGO_PNG 1004
#define IDS_MEDIA_ROUTER_HTML 1005
#define IDS_MENU_ICON_1X_PNG 1006
#define IDS_MENU_ICON_2X_PNG 1007
#define IDS_OSRTEST_HTML 1008
#define IDS_OTHER_TESTS_HTML 1009
#define IDS_PDF_HTML 1010
#define IDS_PDF_PDF 1011
#define IDS_PERFORMANCE_HTML 1012
#define IDS_PERFORMANCE2_HTML 1013
#define IDS_PREFERENCES_HTML 1014
#define IDS_RESPONSE_FILTER_HTML 1015
#define IDS_SERVER_HTML 1016
#define IDS_TRANSPARENCY_HTML 1017
#define IDS_URLREQUEST_HTML 1018
#define IDS_WEBSOCKET_HTML 1019
#define IDS_WINDOW_HTML 1020
#define IDS_WINDOW_ICON_1X_PNG 1021
#define IDS_WINDOW_ICON_2X_PNG 1022
#define IDS_XMLHTTPREQUEST_HTML 1023
#define IDS_IPC_PERFORMANCE_HTML 1003
#define IDS_LOCALSTORAGE_HTML 1004
#define IDS_LOGO_PNG 1005
#define IDS_MEDIA_ROUTER_HTML 1006
#define IDS_MENU_ICON_1X_PNG 1007
#define IDS_MENU_ICON_2X_PNG 1008
#define IDS_OSRTEST_HTML 1009
#define IDS_OTHER_TESTS_HTML 1010
#define IDS_PDF_HTML 1011
#define IDS_PDF_PDF 1012
#define IDS_PERFORMANCE_HTML 1013
#define IDS_PERFORMANCE2_HTML 1014
#define IDS_PREFERENCES_HTML 1015
#define IDS_RESPONSE_FILTER_HTML 1016
#define IDS_SERVER_HTML 1017
#define IDS_TRANSPARENCY_HTML 1018
#define IDS_URLREQUEST_HTML 1019
#define IDS_WEBSOCKET_HTML 1020
#define IDS_WINDOW_HTML 1021
#define IDS_WINDOW_ICON_1X_PNG 1022
#define IDS_WINDOW_ICON_2X_PNG 1023
#define IDS_XMLHTTPREQUEST_HTML 1024
#define IDS_EXTENSIONS_SET_PAGE_COLOR_ICON_PNG 1030
#define IDS_EXTENSIONS_SET_PAGE_COLOR_MANIFEST_JSON 1031

View File

@ -13,40 +13,40 @@ int GetResourceId(const char* resource_name) {
static struct _resource_map {
const char* name;
int id;
} resource_map[] = {
{"binding.html", IDS_BINDING_HTML},
{"dialogs.html", IDS_DIALOGS_HTML},
{"draggable.html", IDS_DRAGGABLE_HTML},
{"extensions/set_page_color/icon.png",
IDS_EXTENSIONS_SET_PAGE_COLOR_ICON_PNG},
{"extensions/set_page_color/manifest.json",
IDS_EXTENSIONS_SET_PAGE_COLOR_MANIFEST_JSON},
{"extensions/set_page_color/popup.html",
IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_HTML},
{"extensions/set_page_color/popup.js",
IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_JS},
{"localstorage.html", IDS_LOCALSTORAGE_HTML},
{"logo.png", IDS_LOGO_PNG},
{"media_router.html", IDS_MEDIA_ROUTER_HTML},
{"menu_icon.1x.png", IDS_MENU_ICON_1X_PNG},
{"menu_icon.2x.png", IDS_MENU_ICON_2X_PNG},
{"osr_test.html", IDS_OSRTEST_HTML},
{"other_tests.html", IDS_OTHER_TESTS_HTML},
{"pdf.html", IDS_PDF_HTML},
{"pdf.pdf", IDS_PDF_PDF},
{"performance.html", IDS_PERFORMANCE_HTML},
{"performance2.html", IDS_PERFORMANCE2_HTML},
{"preferences.html", IDS_PREFERENCES_HTML},
{"response_filter.html", IDS_RESPONSE_FILTER_HTML},
{"server.html", IDS_SERVER_HTML},
{"transparency.html", IDS_TRANSPARENCY_HTML},
{"urlrequest.html", IDS_URLREQUEST_HTML},
{"websocket.html", IDS_WEBSOCKET_HTML},
{"window.html", IDS_WINDOW_HTML},
{"window_icon.1x.png", IDS_WINDOW_ICON_1X_PNG},
{"window_icon.2x.png", IDS_WINDOW_ICON_2X_PNG},
{"xmlhttprequest.html", IDS_XMLHTTPREQUEST_HTML},
};
} resource_map[] = {{"binding.html", IDS_BINDING_HTML},
{"dialogs.html", IDS_DIALOGS_HTML},
{"draggable.html", IDS_DRAGGABLE_HTML},
{"extensions/set_page_color/icon.png",
IDS_EXTENSIONS_SET_PAGE_COLOR_ICON_PNG},
{"extensions/set_page_color/manifest.json",
IDS_EXTENSIONS_SET_PAGE_COLOR_MANIFEST_JSON},
{"extensions/set_page_color/popup.html",
IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_HTML},
{"extensions/set_page_color/popup.js",
IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_JS},
{"ipc_performance.html", IDS_IPC_PERFORMANCE_HTML},
{"localstorage.html", IDS_LOCALSTORAGE_HTML},
{"logo.png", IDS_LOGO_PNG},
{"media_router.html", IDS_MEDIA_ROUTER_HTML},
{"menu_icon.1x.png", IDS_MENU_ICON_1X_PNG},
{"menu_icon.2x.png", IDS_MENU_ICON_2X_PNG},
{"osr_test.html", IDS_OSRTEST_HTML},
{"other_tests.html", IDS_OTHER_TESTS_HTML},
{"pdf.html", IDS_PDF_HTML},
{"pdf.pdf", IDS_PDF_PDF},
{"performance.html", IDS_PERFORMANCE_HTML},
{"performance2.html", IDS_PERFORMANCE2_HTML},
{"preferences.html", IDS_PREFERENCES_HTML},
{"response_filter.html", IDS_RESPONSE_FILTER_HTML},
{"server.html", IDS_SERVER_HTML},
{"transparency.html", IDS_TRANSPARENCY_HTML},
{"urlrequest.html", IDS_URLREQUEST_HTML},
{"websocket.html", IDS_WEBSOCKET_HTML},
{"window.html", IDS_WINDOW_HTML},
{"window_icon.1x.png", IDS_WINDOW_ICON_1X_PNG},
{"window_icon.2x.png", IDS_WINDOW_ICON_2X_PNG},
{"xmlhttprequest.html", IDS_XMLHTTPREQUEST_HTML},
{"xmlhttprequest.html", IDS_XMLHTTPREQUEST_HTML}};
for (size_t i = 0; i < sizeof(resource_map) / sizeof(_resource_map); ++i) {
if (!strcmp(resource_map[i].name, resource_name))

View File

@ -3,6 +3,7 @@
// can be found in the LICENSE file.
#include "tests/cefclient/renderer/client_renderer.h"
#include "tests/cefclient/renderer/ipc_performance_test.h"
#include "tests/cefclient/renderer/performance_test.h"
#include "tests/shared/renderer/client_app_renderer.h"
@ -12,6 +13,7 @@ namespace client {
void ClientAppRenderer::CreateDelegates(DelegateSet& delegates) {
renderer::CreateDelegates(delegates);
performance_test::CreateDelegates(delegates);
ipc_performance_test::CreateDelegates(delegates);
}
} // namespace client

View File

@ -0,0 +1,249 @@
// 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.
#include "tests/cefclient/renderer/ipc_performance_test.h"
#include "include/cef_shared_process_message_builder.h"
#include "include/wrapper/cef_helpers.h"
#include "tests/shared/common/binary_value_utils.h"
namespace {
// JS object member names.
constexpr char kMessageSize[] = "size";
constexpr char kTestId[] = "testId";
constexpr CefV8Value::PropertyAttribute kAttributes =
static_cast<CefV8Value::PropertyAttribute>(
V8_PROPERTY_ATTRIBUTE_READONLY | V8_PROPERTY_ATTRIBUTE_DONTENUM |
V8_PROPERTY_ATTRIBUTE_DONTDELETE);
struct TestInfo {
size_t message_size = 0;
int id = 0;
bool is_valid = false;
};
TestInfo GetTest(const CefV8ValueList& arguments, CefString& exception) {
TestInfo info{};
if (arguments.size() != 1 || !arguments[0]->IsObject()) {
exception = "Invalid arguments; expecting a single object";
return info;
}
CefRefPtr<CefV8Value> arg = arguments[0];
CefRefPtr<CefV8Value> message_size = arg->GetValue(kMessageSize);
if (!message_size.get() || !message_size->IsInt()) {
exception =
"Invalid arguments; object member 'size' is required and must have "
"integer type";
return info;
}
if (message_size->GetIntValue() < 1) {
exception =
"Invalid arguments; object member 'size' must be "
"positive";
return info;
}
CefRefPtr<CefV8Value> test_id = arg->GetValue(kTestId);
if (!message_size.get() || !message_size->IsInt()) {
exception =
"Invalid arguments; object member 'testId' is required and must "
"have integer type";
return info;
}
info.message_size = static_cast<size_t>(message_size->GetIntValue());
info.id = test_id->GetIntValue();
info.is_valid = true;
return info;
}
// Handle bindings in the render process.
class IpcDelegate final : public client::ClientAppRenderer::Delegate {
public:
class V8HandlerImpl final : public CefV8Handler {
public:
explicit V8HandlerImpl(const CefRefPtr<IpcDelegate>& delegate)
: delegate_(delegate) {}
V8HandlerImpl(const V8HandlerImpl&) = delete;
V8HandlerImpl& operator=(const V8HandlerImpl&) = delete;
bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) override {
if (name == bv_utils::kTestSendProcessMessage) {
const auto test = GetTest(arguments, exception);
if (test.is_valid) {
SendTestProcessMessage(test.message_size, test.id);
}
return true;
}
if (name == bv_utils::kTestSendSMRProcessMessage) {
const auto test = GetTest(arguments, exception);
if (test.is_valid) {
SendTestSMRProcessMessage(test.message_size, test.id);
}
return true;
}
return false;
}
private:
void SendTestProcessMessage(int message_size, int test_id) {
auto context = CefV8Context::GetCurrentContext();
delegate_->SendTestProcessMessage(context->GetFrame(), message_size,
test_id);
}
void SendTestSMRProcessMessage(int message_size, int test_id) {
auto context = CefV8Context::GetCurrentContext();
delegate_->SendTestSMRProcessMessage(context->GetFrame(), message_size,
test_id);
}
CefRefPtr<IpcDelegate> delegate_;
IMPLEMENT_REFCOUNTING(V8HandlerImpl);
};
IpcDelegate() = default;
IpcDelegate(const IpcDelegate&) = delete;
IpcDelegate& operator=(const IpcDelegate&) = delete;
void OnContextCreated(CefRefPtr<client::ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) override {
CEF_REQUIRE_RENDERER_THREAD();
CefRefPtr<CefV8Handler> handler = new V8HandlerImpl(this);
// Register function handlers with the 'window' object.
auto window = context->GetGlobal();
window->SetValue(
bv_utils::kTestSendProcessMessage,
CefV8Value::CreateFunction(bv_utils::kTestSendProcessMessage, handler),
kAttributes);
window->SetValue(bv_utils::kTestSendSMRProcessMessage,
CefV8Value::CreateFunction(
bv_utils::kTestSendSMRProcessMessage, handler),
kAttributes);
}
bool OnProcessMessageReceived(CefRefPtr<client::ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) override {
CEF_REQUIRE_RENDERER_THREAD();
const auto finish_time = bv_utils::Now();
if (message->GetName() == bv_utils::kTestSendProcessMessage) {
auto args = message->GetArgumentList();
DCHECK_EQ(args->GetSize(), 1U);
const auto browser_msg =
bv_utils::GetBrowserMsgFromBinary(args->GetBinary(0));
PassTestResultToJs(frame, finish_time, browser_msg);
return true;
}
if (message->GetName() == bv_utils::kTestSendSMRProcessMessage) {
const auto region = message->GetSharedMemoryRegion();
DCHECK(region->IsValid());
DCHECK_GE(region->Size(), sizeof(bv_utils::BrowserMessage));
const auto browser_msg =
static_cast<const bv_utils::BrowserMessage*>(region->Memory());
PassTestResultToJs(frame, finish_time, *browser_msg);
return true;
}
return false;
}
private:
IMPLEMENT_REFCOUNTING(IpcDelegate);
void SendTestProcessMessage(CefRefPtr<CefFrame> frame,
size_t message_size,
int test_id) {
CEF_REQUIRE_RENDERER_THREAD();
auto process_message =
CefProcessMessage::Create(bv_utils::kTestSendProcessMessage);
auto args = process_message->GetArgumentList();
const auto buffer_size =
std::max(message_size, sizeof(bv_utils::RendererMessage));
std::vector<uint8_t> buffer(buffer_size);
const auto renderer_msg =
reinterpret_cast<bv_utils::RendererMessage*>(buffer.data());
renderer_msg->test_id = test_id;
renderer_msg->start_time = bv_utils::Now();
args->SetBinary(0, bv_utils::CreateCefBinaryValue(buffer));
frame->SendProcessMessage(PID_BROWSER, process_message);
}
void SendTestSMRProcessMessage(CefRefPtr<CefFrame> frame,
size_t message_size,
int test_id) {
CEF_REQUIRE_RENDERER_THREAD();
const auto buffer_size =
std::max(message_size, sizeof(bv_utils::RendererMessage));
const auto start_time = bv_utils::Now();
auto builder = CefSharedProcessMessageBuilder::Create(
bv_utils::kTestSendSMRProcessMessage, buffer_size);
auto renderer_msg =
static_cast<bv_utils::RendererMessage*>(builder->Memory());
renderer_msg->test_id = test_id;
renderer_msg->start_time = start_time;
frame->SendProcessMessage(PID_BROWSER, builder->Build());
}
// Execute the onSuccess JavaScript callback.
void PassTestResultToJs(CefRefPtr<CefFrame> frame,
const bv_utils::TimePoint& finish_time,
const bv_utils::BrowserMessage& msg) {
const auto rendered_to_browser = msg.duration;
const auto browser_to_rendered = finish_time - msg.start_time;
CefString code = "testSendProcessMessageResult(" +
std::to_string(msg.test_id) + ", " +
bv_utils::ToMilliString(rendered_to_browser) + ", " +
bv_utils::ToMilliString(browser_to_rendered) + ");";
frame->ExecuteJavaScript(code, frame->GetURL(), 0);
}
};
} // namespace
namespace client {
namespace ipc_performance_test {
void CreateDelegates(ClientAppRenderer::DelegateSet& delegates) {
delegates.insert(new IpcDelegate());
}
} // namespace ipc_performance_test
} // namespace client

View File

@ -0,0 +1,19 @@
// 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.
#ifndef CEF_TESTS_CEFCLIENT_RENDERER_IPC_PERFORMANCE_TEST_H_
#define CEF_TESTS_CEFCLIENT_RENDERER_IPC_PERFORMANCE_TEST_H_
#pragma once
#include "tests/shared/renderer/client_app_renderer.h"
namespace client {
namespace ipc_performance_test {
void CreateDelegates(ClientAppRenderer::DelegateSet& delegates);
} // namespace ipc_performance_test
} // namespace client
#endif // CEF_TESTS_CEFCLIENT_RENDERER_IPC_PERFORMANCE_TEST_H_

View File

@ -0,0 +1,448 @@
<!DOCTYPE html>
<html>
<head>
<title>IPC Performance Tests</title>
<script src="https://cdn.plot.ly/plotly-2.12.1.min.js"></script>
<style>
body {
font-family: Tahoma, Serif;
font-size: 10pt;
}
.left {
text-align: left;
}
.right {
text-align: right;
}
.positive {
color: green;
font-weight: bold;
}
.negative {
color: red;
font-weight: bold;
}
.center {
text-align: center;
}
table.resultTable {
border: 1px solid black;
border-collapse: collapse;
empty-cells: show;
width: 100%;
}
table.resultTable td {
padding: 2px 4px;
border: 1px solid black;
}
table.resultTable > thead > tr {
font-weight: bold;
background: lightblue;
}
table.resultTable > tbody > tr:nth-child(odd) {
background: white;
}
table.resultTable > tbody > tr:nth-child(even) {
background: lightgray;
}
.hide {
display: none;
}
</style>
</head>
<body background-color="white">
<h1>IPC Performance Tests</h1>
<table>
<tr>
<td>
<p>
There is no progress indication of the tests because it
significantly influences measurements. <br />It usually takes 30
seconds (for 100 samples) to complete the tests. <br /><b>AL</b> -
ArgumentList-based process messages. <b>SM</b> -
SharedMemoryRegion-based process messages.
</p>
</td>
</tr>
<tr>
<td>
Samples:
<input
id="sSamples"
type="text"
value="100"
required
pattern="[0-9]+"
/>
<button id="sRun" autofocus onclick="runTestSuite()">Run</button>
</td>
</tr>
</table>
<div style="padding-top: 10px; padding-bottom: 10px">
<table id="resultTable" class="resultTable">
<thead>
<tr>
<td class="center" style="width: 8%">Message Size</td>
<td class="center" style="width: 8%">AL Round Trip Avg,&nbsp;ms</td>
<td class="center" style="width: 8%">SM Round Trip Avg,&nbsp;ms</td>
<td class="center" style="width: 10%">Relative Trip Difference</td>
<td class="center" style="width: 8%">AL Speed,&nbsp;MB/s</td>
<td class="center" style="width: 8%">SM Speed,&nbsp;MB/s</td>
<td class="center" style="width: 10%">Relative Speed Difference</td>
<td class="center" style="width: 8%">AL Standard Deviation</td>
<td class="center" style="width: 8%">SM Standard Deviation</td>
</tr>
</thead>
<tbody>
<!-- result rows here -->
</tbody>
</table>
</div>
<div id="round_trip_avg_chart">
<!-- Average round trip linear chart will be drawn inside this DIV -->
</div>
<div id="box_plot_chart">
<!-- Box plot of round trip time will be drawn inside this DIV -->
</div>
<script type="text/javascript">
let tests = [];
let box_plot_test_data = [];
let round_trip_avg_plot_data = [];
function nextTest(test) {
setTimeout(() => {
execNextTest(test.index);
}, 0);
}
function testSendProcessMessageResult(
testIndex,
fromRendererToBrowser,
fromBrowserToRenderer
) {
const test = tests[testIndex];
const roundTrip = fromRendererToBrowser + fromBrowserToRenderer;
test.totalRoundTrip += roundTrip;
test.sample++;
box_plot_test_data[testIndex].x.push(roundTrip);
setTimeout(() => {
execTest(testIndex);
}, 10);
}
function sendRequest(size, testIndex) {
window.testSendProcessMessage({
size: size,
testId: testIndex,
});
}
function sendSMRRequest(size, testIndex) {
window.testSendSMRProcessMessage({
size: size,
testId: testIndex,
});
}
function getStandardDeviation(array, mean) {
const n = array.length;
if (n < 5) return null;
return Math.sqrt(
array.map((x) => Math.pow(x - mean, 2)).reduce((a, b) => a + b) /
(n - 1)
);
}
function execTest(testIndex) {
const test = tests[testIndex];
if (test.sample >= test.totalSamples) {
return nextTest(test);
}
test.func(test.index);
}
function column(prepared, value) {
return (
"<td class='right'>" + (!prepared ? "-" : value.toFixed(2)) + "</td>"
);
}
function relativeDiffColumn(prepared, value, isBiggerBetter) {
if (!prepared) return "<td class='right'>-</td>";
const isPositive = value > 0 == isBiggerBetter;
return [
"<td class='right ",
isPositive ? "positive" : "negative",
"'>",
value > 0 ? "+" : "",
value.toFixed(2),
"%</td>",
].join("");
}
function displayResult(test) {
const id = "testResultRow_" + test.index;
const markup = [
"<tr id='",
id,
"'>",
"<td class='left'>",
test.name,
"</td>",
column(test.prepared, test.avgRoundTrip),
column(test.prepared, test.avgRoundTripSMR),
relativeDiffColumn(test.prepared, test.relativeTripDiff, false),
column(test.prepared, test.speed),
column(test.prepared, test.speedSMR),
relativeDiffColumn(test.prepared, test.relativeSpeedDiff, true),
"<td class='right'>",
!test.prepared || test.stdDeviation == null
? "-"
: test.stdDeviation.toFixed(2),
"</td>",
"<td class='right'>",
!test.prepared || test.stdDeviationSMR == null
? "-"
: test.stdDeviationSMR.toFixed(2),
"</td>",
"</tr>",
].join("");
const row = document.getElementById(id);
if (row) {
row.outerHTML = markup;
} else {
const tbody = document.getElementById("resultTable").tBodies[0];
tbody.insertAdjacentHTML("beforeEnd", markup);
}
}
function buildTestResults(tests) {
testResults = [];
let oldRoundTrip = {
x: [],
y: [],
type: "scatter",
name: "ArgumentList",
};
let newRoundTrip = {
x: [],
y: [],
type: "scatter",
name: "SharedMemoryRegion",
};
for (let i = 0; i < tests.length / 2; i++) {
const index = testResults.length;
const test = tests[i * 2];
const testSMR = tests[i * 2 + 1];
const avgRoundTrip = test.totalRoundTrip / test.totalSamples;
const avgRoundTripSMR = testSMR.totalRoundTrip / testSMR.totalSamples;
const relativeTripDiff =
((avgRoundTripSMR - avgRoundTrip) / avgRoundTrip) * 100;
// In MB/s
const speed = test.messageSize / (avgRoundTrip * 1000);
const speedSMR = testSMR.messageSize / (avgRoundTripSMR * 1000);
const relativeSpeedDiff = ((speedSMR - speed) / speed) * 100;
const stdDeviation = getStandardDeviation(
box_plot_test_data[test.index].x,
avgRoundTrip
);
const stdDeviationSMR = getStandardDeviation(
box_plot_test_data[testSMR.index].x,
avgRoundTripSMR
);
testResults.push({
name: humanFileSize(test.messageSize),
index: index,
prepared: true,
avgRoundTrip: avgRoundTrip,
avgRoundTripSMR: avgRoundTripSMR,
relativeTripDiff: relativeTripDiff,
speed: speed,
speedSMR: speedSMR,
relativeSpeedDiff: relativeSpeedDiff,
stdDeviation: stdDeviation,
stdDeviationSMR: stdDeviationSMR,
});
oldRoundTrip.x.push(test.messageSize);
newRoundTrip.x.push(test.messageSize);
oldRoundTrip.y.push(avgRoundTrip);
newRoundTrip.y.push(avgRoundTripSMR);
}
round_trip_avg_plot_data = [oldRoundTrip, newRoundTrip];
return testResults;
}
function buildEmptyTestResults(tests) {
testResults = [];
for (let i = 0; i < tests.length / 2; i++) {
const index = testResults.length;
const test = tests[i * 2];
testResults.push({
name: humanFileSize(test.messageSize),
index: index,
prepared: false,
});
}
return testResults;
}
function prepareQueuedTests(totalSamples) {
if (totalSamples <= 0) totalSamples = 1;
tests.forEach((test) => {
test.sample = 0;
test.totalRoundTrip = 0;
test.totalSamples = totalSamples;
});
testResults = buildEmptyTestResults(tests);
testResults.forEach((result) => displayResult(result));
round_trip_avg_plot_data = [];
box_plot_test_data.forEach((data) => {
data.x = [];
});
}
function queueTest(name, messageSize, testFunc) {
const testIndex = tests.length;
test = {
name: name,
messageSize: messageSize,
index: testIndex,
func: testFunc,
};
tests.push(test);
box_plot_test_data.push({
x: [],
type: "box",
boxpoints: "all",
name: name,
jitter: 0.3,
pointpos: -1.8,
});
}
function execNextTest(testIndex) {
testIndex++;
if (tests.length <= testIndex) {
return testSuiteFinished();
} else {
return execTest(testIndex);
}
}
function execQueuedTests(totalSamples) {
prepareQueuedTests(totalSamples);
// Let the updated table render before starting the tests
setTimeout(() => execNextTest(-1), 200);
}
function setSettingsState(disabled) {
document.getElementById("sSamples").disabled = disabled;
document.getElementById("sRun").disabled = disabled;
}
function testSuiteFinished() {
testResults = buildTestResults(tests);
testResults.forEach((result) => displayResult(result));
const round_trip_layout = {
title: "Average round trip, ms (Smaller Better)",
};
Plotly.newPlot(
"round_trip_avg_chart",
round_trip_avg_plot_data,
round_trip_layout
);
const box_plot_layout = {
title: "Round Trip Time, ms",
};
Plotly.newPlot("box_plot_chart", box_plot_test_data, box_plot_layout);
setSettingsState(false);
}
function humanFileSize(bytes) {
const step = 1024;
const originalBytes = bytes;
if (Math.abs(bytes) < step) {
return bytes + " B";
}
const units = [" KB", " MB", " GB"];
let u = -1;
let count = 0;
do {
bytes /= step;
u += 1;
count += 1;
} while (Math.abs(bytes) >= step && u < units.length - 1);
return bytes.toString() + units[u];
}
window.runTestSuite = () => {
Plotly.purge("round_trip_avg_chart");
Plotly.purge("box_plot_chart");
setSettingsState(true);
const totalSamples = parseInt(
document.getElementById("sSamples").value
);
execQueuedTests(totalSamples);
return false;
};
for (let size = 512; size <= 512 * 1024; size = size * 2) {
queueTest(humanFileSize(size) + " AL", size, (testIndex) =>
sendRequest(size, testIndex)
);
queueTest(humanFileSize(size) + " SM", size, (testIndex) =>
sendSMRRequest(size, testIndex)
);
}
const totalSamples = parseInt(document.getElementById("sSamples").value);
prepareQueuedTests(totalSamples);
</script>
</body>
</html>

View File

@ -17,6 +17,7 @@
<li><a href="http://www.html5test.com">HTML5 Feature Test</a></li>
<li><a href="http://html5-demos.appspot.com/static/filesystem/filer.js/demos/index.html">HTML5 Filesystem</a> - requires "cache-path" flag</li>
<li><a href="http://www.youtube.com/watch?v=siOHh0uzcuY&html5=True">HTML5 Video</a></li>
<li><a href="ipc_performance">IPC Performance Tests</a></li>
<li><a href="binding">JavaScript Binding</a></li>
<li><a href="performance">JavaScript Performance Tests</a></li>
<li><a href="performance2">JavaScript Performance (2) Tests</a></li>

View File

@ -32,6 +32,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
IDS_BINDING_HTML BINARY "..\\binding.html"
IDS_DIALOGS_HTML BINARY "..\\dialogs.html"
IDS_DRAGGABLE_HTML BINARY "..\\draggable.html"
IDS_IPC_PERFORMANCE_HTML BINARY "..\\ipc_performance.html"
IDS_LOCALSTORAGE_HTML BINARY "..\\localstorage.html"
IDS_LOGO_PNG BINARY "..\\logo.png"
IDS_MEDIA_ROUTER_HTML BINARY "..\\media_router.html"

View File

@ -59,6 +59,11 @@ void CreateRenderDelegates(ClientAppRenderer::DelegateSet& delegates) {
delegates);
CreateProcessMessageRendererTests(delegates);
// Bring in the shared process message tests.
extern void CreateSharedProcessMessageTests(ClientAppRenderer::DelegateSet &
delegates);
CreateSharedProcessMessageTests(delegates);
// Bring in the RequestHandler tests.
extern void CreateRequestHandlerRendererTests(ClientAppRenderer::DelegateSet &
delegates);

View File

@ -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.
#include "tests/ceftests/message_router_unittest_utils.h"
namespace {
// Used to verify that the test harness (bound functions) behave correctly.
class HarnessTestHandler : public SingleLoadTestHandler {
public:
HarnessTestHandler(bool test_success) : test_success_(test_success) {}
std::string GetMainHTML() override {
std::string html;
if (test_success_) {
// All assertions should pass.
html =
"<html><body><script>\n"
"var fail_ct = 0;\n"
"try { window.mrtAssertTotalCount(" LINESTR
",0); } catch (e) { fail_ct++; }\n"
"try { window.mrtAssertBrowserCount(" LINESTR
",0); } catch (e) { fail_ct++; }\n"
"try { window.mrtAssertContextCount(" LINESTR
",0); } catch (e) { fail_ct++; }\n"
"window.mrtNotify('' + (fail_ct == 0));"
"</script></body></html>";
} else {
// All assertions should fail.
html =
"<html><body><script>\n"
"var fail_ct = 0;\n"
"try { window.mrtAssertTotalCount(" LINESTR
",1); } catch (e) { fail_ct++; }\n"
"try { window.mrtAssertBrowserCount(" LINESTR
",1); } catch (e) { fail_ct++; }\n"
"try { window.mrtAssertContextCount(" LINESTR
",1); } catch (e) { fail_ct++; }\n"
"window.mrtNotify('' + (fail_ct == 3));"
"</script></body></html>";
}
return html;
}
void OnNotify(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const std::string& message) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
got_done_.yes();
EXPECT_STREQ("true", message.c_str());
DestroyTest();
}
void DestroyTest() override {
EXPECT_TRUE(got_done_);
TestHandler::DestroyTest();
}
private:
const bool test_success_;
TrackCallback got_done_;
};
} // namespace
// Verify that the test harness works with successful assertions.
TEST(MessageRouterTest, HarnessSuccess) {
CefRefPtr<HarnessTestHandler> handler = new HarnessTestHandler(true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Verify that the test harness works with failed assertions.
TEST(MessageRouterTest, HarnessFailure) {
CefRefPtr<HarnessTestHandler> handler = new HarnessTestHandler(false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}

View File

@ -0,0 +1,623 @@
// 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.
#include "tests/ceftests/message_router_unittest_utils.h"
namespace {
const char kSingleQueryRequest[] = "request_context";
const char kSingleQueryResponse[] = "success_response";
const int kSingleQueryErrorCode = 5;
const char kSingleQueryErrorMessage[] = "error_message";
// Test a single query in a single page load.
class SingleQueryTestHandler : public SingleLoadTestHandler {
public:
enum TestType {
SUCCESS,
FAILURE,
CANCEL,
};
SingleQueryTestHandler(TestType type, bool sync_callback)
: test_type_(type), sync_callback_(sync_callback), query_id_(0) {}
std::string GetMainHTML() override {
std::string html;
std::stringstream ss;
ss << kSingleQueryErrorCode;
const std::string& errorCodeStr = ss.str();
html =
"<html><body><script>\n"
// No requests should exist.
"window.mrtAssertTotalCount(" LINESTR
",0);\n"
"window.mrtAssertBrowserCount(" LINESTR
",0);\n"
"window.mrtAssertContextCount(" LINESTR
",0);\n"
// Send the query.
"var request_id = window.mrtQuery({\n"
" request: '" +
std::string(kSingleQueryRequest) +
"',\n"
" persistent: false,\n"
" onSuccess: function(response) {\n"
// Request should be removed before callback is executed.
" window.mrtAssertTotalCount(" LINESTR
",0);\n"
" window.mrtAssertBrowserCount(" LINESTR
",0);\n"
" window.mrtAssertContextCount(" LINESTR
",0);\n"
" if (response == '" +
std::string(kSingleQueryResponse) +
"')\n"
" window.mrtNotify('success');\n"
" else\n"
" window.mrtNotify('error-onSuccess');\n"
" },\n"
" onFailure: function(error_code, error_message) {\n"
// Request should be removed before callback is executed.
" window.mrtAssertTotalCount(" LINESTR
",0);\n"
" window.mrtAssertBrowserCount(" LINESTR
",0);\n"
" window.mrtAssertContextCount(" LINESTR
",0);\n"
" if (error_code == " +
errorCodeStr + " && error_message == '" +
std::string(kSingleQueryErrorMessage) +
"')\n"
" window.mrtNotify('failure');\n"
" else\n"
" window.mrtNotify('error-onFailure');\n"
" }\n"
"});\n"
// Request should exist.
"window.mrtAssertTotalCount(" LINESTR
",1);\n"
"window.mrtAssertBrowserCount(" LINESTR
",1);\n"
"window.mrtAssertContextCount(" LINESTR ",1);\n";
if (test_type_ == CANCEL) {
html +=
"window.mrtQueryCancel(request_id);\n"
// Request should be removed immediately.
"window.mrtAssertTotalCount(" LINESTR
",0);\n"
"window.mrtAssertBrowserCount(" LINESTR
",0);\n"
"window.mrtAssertContextCount(" LINESTR
",0);\n"
"window.mrtNotify('cancel');\n";
}
html += "</script></body></html>";
return html;
}
void OnNotify(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const std::string& message) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
// OnNotify only be called once.
EXPECT_FALSE(got_notify_);
got_notify_.yes();
if (test_type_ == SUCCESS) {
EXPECT_STREQ("success", message.c_str());
} else if (test_type_ == FAILURE) {
EXPECT_STREQ("failure", message.c_str());
} else if (test_type_ == CANCEL) {
EXPECT_STREQ("cancel", message.c_str());
}
DestroyTestIfDone();
}
void ExecuteCallback() {
EXPECT_TRUE(callback_.get());
if (test_type_ == SUCCESS) {
callback_->Success(kSingleQueryResponse);
} else if (test_type_ == FAILURE) {
callback_->Failure(kSingleQueryErrorCode, kSingleQueryErrorMessage);
} else {
ADD_FAILURE(); // Not reached.
}
callback_ = nullptr;
}
bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
EXPECT_NE(0, query_id);
EXPECT_FALSE(persistent);
EXPECT_STREQ(kSingleQueryRequest, request.ToString().c_str());
got_on_query_.yes();
query_id_ = query_id;
callback_ = callback;
if (test_type_ == SUCCESS || test_type_ == FAILURE) {
if (sync_callback_) {
ExecuteCallback();
} else {
CefPostTask(
TID_UI,
base::BindOnce(&SingleQueryTestHandler::ExecuteCallback, this));
}
}
return true;
}
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
EXPECT_EQ(test_type_, CANCEL);
EXPECT_EQ(query_id_, query_id);
EXPECT_TRUE(got_on_query_);
EXPECT_TRUE(callback_.get());
got_on_query_canceled_.yes();
callback_ = nullptr;
DestroyTestIfDone();
}
void DestroyTestIfDone() {
bool destroy_test = false;
if (test_type_ == CANCEL)
destroy_test = got_notify_ && got_on_query_canceled_;
else
destroy_test = got_notify_;
if (destroy_test)
DestroyTest();
}
void DestroyTest() override {
EXPECT_TRUE(got_notify_);
EXPECT_TRUE(got_on_query_);
EXPECT_FALSE(callback_.get());
if (test_type_ == CANCEL)
EXPECT_TRUE(got_on_query_canceled_);
else
EXPECT_FALSE(got_on_query_canceled_);
TestHandler::DestroyTest();
}
private:
const TestType test_type_;
const bool sync_callback_;
int64 query_id_;
CefRefPtr<Callback> callback_;
TrackCallback got_on_query_;
TrackCallback got_on_query_canceled_;
TrackCallback got_notify_;
};
} // namespace
// Test that a single query with successful result delivered synchronously.
TEST(MessageRouterTest, SingleQuerySuccessSyncCallback) {
CefRefPtr<SingleQueryTestHandler> handler =
new SingleQueryTestHandler(SingleQueryTestHandler::SUCCESS, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that a single query with successful result delivered asynchronously.
TEST(MessageRouterTest, SingleQuerySuccessAsyncCallback) {
CefRefPtr<SingleQueryTestHandler> handler =
new SingleQueryTestHandler(SingleQueryTestHandler::SUCCESS, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that a single query with failure result delivered synchronously.
TEST(MessageRouterTest, SingleQueryFailureSyncCallback) {
CefRefPtr<SingleQueryTestHandler> handler =
new SingleQueryTestHandler(SingleQueryTestHandler::FAILURE, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that a single query with failure result delivered asynchronously.
TEST(MessageRouterTest, SingleQueryFailureAsyncCallback) {
CefRefPtr<SingleQueryTestHandler> handler =
new SingleQueryTestHandler(SingleQueryTestHandler::FAILURE, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that a single query with cancellation.
TEST(MessageRouterTest, SingleQueryCancel) {
CefRefPtr<SingleQueryTestHandler> handler =
new SingleQueryTestHandler(SingleQueryTestHandler::CANCEL, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
namespace {
const int kSinglePersistentQueryResponseCount = 10;
// Test a single persistent query in a single page load.
class SinglePersistentQueryTestHandler : public SingleLoadTestHandler {
public:
enum TestType {
SUCCESS,
FAILURE,
};
SinglePersistentQueryTestHandler(TestType test_type, bool sync_callback)
: test_type_(test_type), sync_callback_(sync_callback), query_id_(0) {}
std::string GetMainHTML() override {
std::string html;
std::stringstream ss;
ss << kSinglePersistentQueryResponseCount;
const std::string& responseCountStr = ss.str();
ss.str("");
ss << kSingleQueryErrorCode;
const std::string& errorCodeStr = ss.str();
html =
"<html><body><script>\n"
// No requests should exist.
"window.mrtAssertTotalCount(" LINESTR
",0);\n"
"window.mrtAssertBrowserCount(" LINESTR
",0);\n"
"window.mrtAssertContextCount(" LINESTR
",0);\n"
// Keep track of the number of responses.
"var count = 0;\n"
// Send the query.
"var request_id = window.mrtQuery({\n"
" request: '" +
std::string(kSingleQueryRequest) +
"',\n"
" persistent: true,\n"
" onSuccess: function(response) {\n"
// Request should not be removed.
" window.mrtAssertTotalCount(" LINESTR
",1);\n"
" window.mrtAssertBrowserCount(" LINESTR
",1);\n"
" window.mrtAssertContextCount(" LINESTR
",1);\n"
" if (response == '" +
std::string(kSingleQueryResponse) +
"') {\n"
" if (++count == " +
responseCountStr +
") {\n"
" window.mrtNotify('success');\n"
" window.mrtQueryCancel(request_id);\n"
// Request should be removed immediately.
" window.mrtAssertTotalCount(" LINESTR
",0);\n"
" window.mrtAssertBrowserCount(" LINESTR
",0);\n"
" window.mrtAssertContextCount(" LINESTR
",0);\n"
" }\n"
" } else {\n"
" window.mrtNotify('error-onSuccess');\n"
" }\n"
" },\n"
" onFailure: function(error_code, error_message) {\n"
// Request should be removed before callback is executed.
" window.mrtAssertTotalCount(" LINESTR
",0);\n"
" window.mrtAssertBrowserCount(" LINESTR
",0);\n"
" window.mrtAssertContextCount(" LINESTR
",0);\n"
" if (error_code == " +
errorCodeStr + " && error_message == '" +
std::string(kSingleQueryErrorMessage) +
"') {\n"
" window.mrtNotify('failure');\n"
" } else {\n"
" window.mrtNotify('error-onFailure');\n"
" }\n"
" }\n"
"});\n"
// Request should exist.
"window.mrtAssertTotalCount(" LINESTR
",1);\n"
"window.mrtAssertBrowserCount(" LINESTR
",1);\n"
"window.mrtAssertContextCount(" LINESTR ",1);\n";
html += "</script></body></html>";
return html;
}
void OnNotify(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const std::string& message) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
if (test_type_ == SUCCESS) {
EXPECT_STREQ("success", message.c_str());
} else if (test_type_ == FAILURE) {
EXPECT_STREQ("failure", message.c_str());
}
got_notify_.yes();
DestroyTestIfDone();
}
void ExecuteCallback() {
EXPECT_TRUE(callback_.get());
if (test_type_ == SUCCESS) {
callback_->Success(kSingleQueryResponse);
} else {
callback_->Failure(kSingleQueryErrorCode, kSingleQueryErrorMessage);
callback_ = nullptr;
}
}
bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
EXPECT_NE(0, query_id);
EXPECT_TRUE(persistent);
EXPECT_STREQ(kSingleQueryRequest, request.ToString().c_str());
got_on_query_.yes();
query_id_ = query_id;
callback_ = callback;
int repeat =
(test_type_ == SUCCESS ? kSinglePersistentQueryResponseCount : 1);
for (int i = 0; i < repeat; ++i) {
if (sync_callback_) {
ExecuteCallback();
} else {
CefPostTask(
TID_UI,
base::BindOnce(&SinglePersistentQueryTestHandler::ExecuteCallback,
this));
}
}
return true;
}
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
EXPECT_EQ(query_id_, query_id);
EXPECT_TRUE(got_on_query_);
EXPECT_TRUE(callback_.get());
got_on_query_canceled_.yes();
callback_ = nullptr;
DestroyTestIfDone();
}
void DestroyTestIfDone() {
bool destroy_test = false;
if (test_type_ == SUCCESS) {
if (got_on_query_ && got_on_query_canceled_ && got_notify_)
destroy_test = true;
} else if (got_on_query_ && got_notify_) {
destroy_test = true;
}
if (destroy_test)
DestroyTest();
}
void DestroyTest() override {
EXPECT_TRUE(got_notify_);
EXPECT_TRUE(got_on_query_);
EXPECT_FALSE(callback_.get());
if (test_type_ == SUCCESS)
EXPECT_TRUE(got_on_query_canceled_);
else
EXPECT_FALSE(got_on_query_canceled_);
TestHandler::DestroyTest();
}
private:
const TestType test_type_;
const bool sync_callback_;
int64 query_id_;
CefRefPtr<Callback> callback_;
TrackCallback got_on_query_;
TrackCallback got_on_query_canceled_;
TrackCallback got_notify_;
};
} // namespace
// Test that a single query with successful result delivered synchronously.
TEST(MessageRouterTest, SinglePersistentQuerySuccessSyncCallback) {
CefRefPtr<SinglePersistentQueryTestHandler> handler =
new SinglePersistentQueryTestHandler(
SinglePersistentQueryTestHandler::SUCCESS, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that a single query with successful result delivered asynchronously.
TEST(MessageRouterTest, SinglePersistentQuerySuccessAsyncCallback) {
CefRefPtr<SinglePersistentQueryTestHandler> handler =
new SinglePersistentQueryTestHandler(
SinglePersistentQueryTestHandler::SUCCESS, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that a single query with failure result delivered synchronously.
TEST(MessageRouterTest, SinglePersistentQueryFailureSyncCallback) {
CefRefPtr<SinglePersistentQueryTestHandler> handler =
new SinglePersistentQueryTestHandler(
SinglePersistentQueryTestHandler::FAILURE, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Test that a single query with failure result delivered asynchronously.
TEST(MessageRouterTest, SinglePersistentQueryFailureAsyncCallback) {
CefRefPtr<SinglePersistentQueryTestHandler> handler =
new SinglePersistentQueryTestHandler(
SinglePersistentQueryTestHandler::FAILURE, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
namespace {
// Test a single unhandled query in a single page load.
class SingleUnhandledQueryTestHandler : public SingleLoadTestHandler {
public:
SingleUnhandledQueryTestHandler() {}
std::string GetMainHTML() override {
std::string html;
html =
"<html><body><script>\n"
// No requests should exist.
"window.mrtAssertTotalCount(" LINESTR
",0);\n"
"window.mrtAssertBrowserCount(" LINESTR
",0);\n"
"window.mrtAssertContextCount(" LINESTR
",0);\n"
// Keep track of the number of responses.
"var count = 0;\n"
// Send the query.
"var request_id = window.mrtQuery({\n"
" request: '" +
std::string(kSingleQueryRequest) +
"',\n"
" persistent: false,\n"
" onSuccess: function(response) {\n"
" window.mrtNotify('error-onSuccess');\n"
" },\n"
" onFailure: function(error_code, error_message) {\n"
// Request should be removed before callback is executed.
" window.mrtAssertTotalCount(" LINESTR
",0);\n"
" window.mrtAssertBrowserCount(" LINESTR
",0);\n"
" window.mrtAssertContextCount(" LINESTR
",0);\n"
" if (error_code == -1 && "
"error_message == 'The query has been canceled') {\n"
" window.mrtNotify('failure');\n"
" } else {\n"
" window.mrtNotify('error-onFailure');\n"
" }\n"
" }\n"
"});\n"
// Request should exist.
"window.mrtAssertTotalCount(" LINESTR
",1);\n"
"window.mrtAssertBrowserCount(" LINESTR
",1);\n"
"window.mrtAssertContextCount(" LINESTR ",1);\n";
html += "</script></body></html>";
return html;
}
void OnNotify(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const std::string& message) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
EXPECT_STREQ("failure", message.c_str());
got_notify_.yes();
DestroyTest();
}
bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
EXPECT_NE(0, query_id);
EXPECT_FALSE(persistent);
EXPECT_STREQ(kSingleQueryRequest, request.ToString().c_str());
got_on_query_.yes();
return false;
}
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id) override {
EXPECT_FALSE(true); // Not reached.
}
void DestroyTest() override {
EXPECT_TRUE(got_on_query_);
EXPECT_TRUE(got_notify_);
TestHandler::DestroyTest();
}
private:
TrackCallback got_on_query_;
TrackCallback got_notify_;
};
} // namespace
// Test that a single unhandled query results in a call to onFailure.
TEST(MessageRouterTest, SingleUnhandledQuery) {
CefRefPtr<SingleUnhandledQueryTestHandler> handler =
new SingleUnhandledQueryTestHandler();
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}

View File

@ -0,0 +1,222 @@
// 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.
#include "tests/ceftests/message_router_unittest_utils.h"
namespace {
constexpr int kSingleQueryErrorCode = 5;
constexpr size_t kMessageSizeThreshold = 16000;
enum class TestType { SUCCESS, FAILURE };
CefString GenerateResponse(size_t size, char ch) {
return CefString(std::string(size, ch));
}
CefString GenerateResponse(size_t size, wchar_t ch) {
return CefString(std::wstring(size, ch));
}
template <class CharType>
class ThresholdTestHandler final : public SingleLoadTestHandler {
public:
ThresholdTestHandler(TestType type, size_t message_size, CharType symbol)
: test_type_(type),
message_size_(message_size),
message_size_str_(std::to_string(message_size)),
symbol_(symbol) {}
std::string GetMainHTML() override {
const std::string& errorCodeStr = std::to_string(kSingleQueryErrorCode);
std::string html =
"<html><body><script>\n"
// Send the query.
"var request_id = window." +
std::string(kJSQueryFunc) + "({\n request: '" + message_size_str_ +
"',\n persistent: false,\n"
" onSuccess: function(response) {\n"
" window.mrtNotify(response);\n"
" },\n"
" onFailure: function(error_code, error_message) {\n"
" if (error_code == " +
errorCodeStr +
")\n"
" window.mrtNotify(error_message);\n"
" else\n"
" window.mrtNotify('error-onFailure');\n"
" }\n"
"});\n</script></body></html>";
return html;
}
void OnNotify(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const std::string& message) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
// OnNotify only be called once.
EXPECT_FALSE(got_notify_);
got_notify_.yes();
auto expected = GenerateResponse(message_size_, symbol_);
switch (test_type_) {
case TestType::SUCCESS:
EXPECT_EQ(expected, message);
break;
case TestType::FAILURE:
EXPECT_EQ(expected, message);
break;
default:
ADD_FAILURE();
break;
}
DestroyTest();
}
void ExecuteCallback(size_t response_size) {
auto response = GenerateResponse(response_size, symbol_);
EXPECT_TRUE(callback_.get());
switch (test_type_) {
case TestType::SUCCESS:
callback_->Success(response);
break;
case TestType::FAILURE:
callback_->Failure(kSingleQueryErrorCode, response);
break;
default:
ADD_FAILURE();
break;
}
callback_ = nullptr;
}
bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) override {
AssertMainBrowser(browser);
AssertMainFrame(frame);
EXPECT_NE(0, query_id);
EXPECT_FALSE(persistent);
EXPECT_EQ(message_size_str_, request.ToString());
const size_t message_size =
static_cast<size_t>(std::stoi(request.ToString()));
got_on_query_.yes();
callback_ = callback;
ExecuteCallback(message_size);
return true;
}
void DestroyTest() override {
EXPECT_TRUE(got_notify_);
EXPECT_TRUE(got_on_query_);
EXPECT_FALSE(callback_.get());
TestHandler::DestroyTest();
}
private:
const TestType test_type_;
const size_t message_size_;
const std::string message_size_str_;
const CharType symbol_;
CefRefPtr<Callback> callback_;
TrackCallback got_on_query_;
TrackCallback got_notify_;
};
using CharTestHandler = CefRefPtr<ThresholdTestHandler<char>>;
using WCharTestHandler = CefRefPtr<ThresholdTestHandler<wchar_t>>;
} // namespace
TEST(MessageRouterTest, ThresholdMessageUnderSuccessCallback) {
const auto UnderThreshold = kMessageSizeThreshold - 1;
CharTestHandler handler =
new ThresholdTestHandler(TestType::SUCCESS, UnderThreshold, 'A');
handler->SetMessageSizeThreshold(kMessageSizeThreshold);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
TEST(MessageRouterTest, ThresholMessageExactdSuccessCallback) {
CharTestHandler handler =
new ThresholdTestHandler(TestType::SUCCESS, kMessageSizeThreshold, 'A');
handler->SetMessageSizeThreshold(kMessageSizeThreshold);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
TEST(MessageRouterTest, ThresholdMessageOverSuccessCallback) {
const auto OverThreshold = kMessageSizeThreshold + 1;
CharTestHandler handler =
new ThresholdTestHandler(TestType::SUCCESS, OverThreshold, 'A');
handler->SetMessageSizeThreshold(kMessageSizeThreshold);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
TEST(MessageRouterTest, ThresholdMessageUnderFailureCallback) {
const auto UnderThreshold = kMessageSizeThreshold - 1;
CharTestHandler handler =
new ThresholdTestHandler(TestType::FAILURE, UnderThreshold, 'A');
handler->SetMessageSizeThreshold(kMessageSizeThreshold);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
TEST(MessageRouterTest, ThresholMessageOverdFailureCallback) {
const auto OverThreshold = kMessageSizeThreshold + 1;
CharTestHandler handler =
new ThresholdTestHandler(TestType::FAILURE, OverThreshold, 'A');
handler->SetMessageSizeThreshold(kMessageSizeThreshold);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
TEST(MessageRouterTest, ThresholdUtf8MessageUnderSuccessCallback) {
const auto UnderThreshold = kMessageSizeThreshold - 1;
WCharTestHandler handler =
new ThresholdTestHandler(TestType::SUCCESS, UnderThreshold, L'\u304B');
handler->SetMessageSizeThreshold(kMessageSizeThreshold);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
TEST(MessageRouterTest, ThresholdUtf8MessageOverSuccessCallback) {
const auto OverThreshold = kMessageSizeThreshold + 1;
WCharTestHandler handler =
new ThresholdTestHandler(TestType::SUCCESS, OverThreshold, L'\u304B');
handler->SetMessageSizeThreshold(kMessageSizeThreshold);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}

View File

@ -0,0 +1,256 @@
// 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.
#include "tests/ceftests/message_router_unittest_utils.h"
extern const char kJSQueryFunc[] = "mrtQuery";
extern const char kJSQueryCancelFunc[] = "mrtQueryCancel";
namespace {
const char kTestDomainRoot[] = "http://tests-mr";
const char kDoneMessageName[] = "mrtNotifyMsg";
const char kJSNotifyFunc[] = "mrtNotify";
const char kJSAssertTotalCountFunc[] = "mrtAssertTotalCount";
const char kJSAssertBrowserCountFunc[] = "mrtAssertBrowserCount";
const char kJSAssertContextCountFunc[] = "mrtAssertContextCount";
void SetRouterConfig(CefMessageRouterConfig& config) {
config.js_query_function = kJSQueryFunc;
config.js_cancel_function = kJSQueryCancelFunc;
}
} // namespace
// Entry point for creating the test delegate.
// Called from client_app_delegates.cc.
void CreateMessageRouterRendererTests(
ClientAppRenderer::DelegateSet& delegates) {
delegates.insert(new MRRenderDelegate);
}
bool MRRenderDelegate::V8HandlerImpl::Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) {
const std::string& message_name = name;
if (message_name == kJSNotifyFunc) {
EXPECT_EQ(1U, arguments.size());
EXPECT_TRUE(arguments[0]->IsString());
const CefString& msg = arguments[0]->GetStringValue();
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
CefRefPtr<CefFrame> frame = context->GetFrame();
CefRefPtr<CefProcessMessage> message =
CefProcessMessage::Create(kDoneMessageName);
CefRefPtr<CefListValue> args = message->GetArgumentList();
args->SetString(0, msg);
frame->SendProcessMessage(PID_BROWSER, message);
return true;
} else {
EXPECT_EQ(2U, arguments.size());
EXPECT_TRUE(arguments[0]->IsInt());
EXPECT_TRUE(arguments[1]->IsInt());
const int line_no = arguments[0]->GetIntValue();
const int expected_count = arguments[1]->GetIntValue();
int actual_count = -1;
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
CefRefPtr<CefBrowser> browser = context->GetBrowser();
if (name == kJSAssertTotalCountFunc) {
actual_count =
delegate_->message_router_->GetPendingCount(nullptr, nullptr);
} else if (name == kJSAssertBrowserCountFunc) {
actual_count =
delegate_->message_router_->GetPendingCount(browser, nullptr);
} else if (name == kJSAssertContextCountFunc) {
actual_count =
delegate_->message_router_->GetPendingCount(browser, context);
}
if (expected_count != actual_count) {
std::stringstream ss;
ss << message_name << " failed (line " << line_no << "); expected "
<< expected_count << ", got " << actual_count;
exception = ss.str();
}
}
return true;
}
void MRRenderDelegate::OnWebKitInitialized(CefRefPtr<ClientAppRenderer> app) {
// Create the renderer-side router for query handling.
CefMessageRouterConfig config;
SetRouterConfig(config);
message_router_ = CefMessageRouterRendererSide::Create(config);
}
void MRRenderDelegate::OnContextCreated(CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
const std::string& url = frame->GetURL();
if (url.find(kTestDomainRoot) != 0)
return;
message_router_->OnContextCreated(browser, frame, context);
// Register function handlers with the 'window' object.
CefRefPtr<CefV8Value> window = context->GetGlobal();
CefRefPtr<V8HandlerImpl> handler = new V8HandlerImpl(this);
CefV8Value::PropertyAttribute attributes =
static_cast<CefV8Value::PropertyAttribute>(
V8_PROPERTY_ATTRIBUTE_READONLY | V8_PROPERTY_ATTRIBUTE_DONTENUM |
V8_PROPERTY_ATTRIBUTE_DONTDELETE);
CefRefPtr<CefV8Value> notify_func =
CefV8Value::CreateFunction(kJSNotifyFunc, handler.get());
window->SetValue(kJSNotifyFunc, notify_func, attributes);
CefRefPtr<CefV8Value> total_count_func =
CefV8Value::CreateFunction(kJSAssertTotalCountFunc, handler.get());
window->SetValue(kJSAssertTotalCountFunc, total_count_func, attributes);
CefRefPtr<CefV8Value> browser_count_func =
CefV8Value::CreateFunction(kJSAssertBrowserCountFunc, handler.get());
window->SetValue(kJSAssertBrowserCountFunc, browser_count_func, attributes);
CefRefPtr<CefV8Value> context_count_func =
CefV8Value::CreateFunction(kJSAssertContextCountFunc, handler.get());
window->SetValue(kJSAssertContextCountFunc, context_count_func, attributes);
}
void MRRenderDelegate::OnContextReleased(CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
const std::string& url = frame->GetURL();
if (url.find(kTestDomainRoot) != 0)
return;
message_router_->OnContextReleased(browser, frame, context);
}
bool MRRenderDelegate::OnProcessMessageReceived(
CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) {
const std::string& url = frame->GetURL();
if (url.find(kTestDomainRoot) != 0)
return false;
return message_router_->OnProcessMessageReceived(browser, frame,
source_process, message);
}
void MRTestHandler::RunTest() {
RunMRTest();
// Time out the test after a reasonable period of time.
SetTestTimeout(10000);
}
void MRTestHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
if (!message_router_.get()) {
// Create the browser-side router for query handling.
CefMessageRouterConfig config;
SetRouterConfig(config);
if (message_size_threshold_)
config.message_size_threshold = message_size_threshold_;
message_router_ = CefMessageRouterBrowserSide::Create(config);
AddHandlers(message_router_);
}
TestHandler::OnAfterCreated(browser);
}
void MRTestHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
message_router_->OnBeforeClose(browser);
TestHandler::OnBeforeClose(browser);
}
void MRTestHandler::OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
TerminationStatus status) {
message_router_->OnRenderProcessTerminated(browser);
}
bool MRTestHandler::OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool user_gesture,
bool is_redirect) {
message_router_->OnBeforeBrowse(browser, frame);
return false;
}
// Returns true if the router handled the navigation.
bool MRTestHandler::OnProcessMessageReceived(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) {
const std::string& message_name = message->GetName();
if (message_name == kDoneMessageName) {
CefRefPtr<CefListValue> args = message->GetArgumentList();
EXPECT_EQ(1U, args->GetSize());
EXPECT_EQ(VTYPE_STRING, args->GetType(0));
OnNotify(browser, frame, args->GetString(0));
return true;
}
return message_router_->OnProcessMessageReceived(browser, frame,
source_process, message);
}
CefRefPtr<CefMessageRouterBrowserSide> MRTestHandler::GetRouter() const {
return message_router_;
}
void MRTestHandler::SetMessageSizeThreshold(size_t message_size_threshold) {
message_size_threshold_ = message_size_threshold;
}
bool MRTestHandler::AssertQueryCount(
CefRefPtr<CefBrowser> browser,
CefMessageRouterBrowserSide::Handler* handler,
int expected_count) {
int actual_count = message_router_->GetPendingCount(browser, handler);
EXPECT_EQ(expected_count, actual_count);
return (expected_count == actual_count);
}
void MRTestHandler::AssertMainBrowser(CefRefPtr<CefBrowser> browser) {
EXPECT_TRUE(browser.get());
EXPECT_EQ(GetBrowserId(), browser->GetIdentifier());
}
SingleLoadTestHandler::SingleLoadTestHandler()
: main_url_("http://tests-mr.com/main.html") {}
void SingleLoadTestHandler::RunMRTest() {
AddOtherResources();
AddResource(main_url_, GetMainHTML(), "text/html");
CreateBrowser(main_url_, nullptr);
}
void SingleLoadTestHandler::AddHandlers(
CefRefPtr<CefMessageRouterBrowserSide> message_router) {
message_router->AddHandler(this, false);
}
void SingleLoadTestHandler::AssertMainFrame(CefRefPtr<CefFrame> frame) {
EXPECT_TRUE(frame.get());
EXPECT_TRUE(frame->IsMain());
EXPECT_STREQ(main_url_.c_str(), frame->GetURL().ToString().c_str());
}

View File

@ -0,0 +1,137 @@
// 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.
#ifndef CEF_TESTS_CEFTESTS_MESSAGE_ROUTER_UNITTEST_UTILS_H_
#define CEF_TESTS_CEFTESTS_MESSAGE_ROUTER_UNITTEST_UTILS_H_
#pragma once
#include <cstdlib>
#include "include/base/cef_callback.h"
#include "include/base/cef_weak_ptr.h"
#include "include/cef_v8.h"
#include "include/wrapper/cef_closure_task.h"
#include "tests/ceftests/routing_test_handler.h"
#include "tests/ceftests/test_util.h"
#include "tests/gtest/include/gtest/gtest.h"
#include "tests/shared/renderer/client_app_renderer.h"
using client::ClientAppRenderer;
extern const char kJSQueryFunc[];
extern const char kJSQueryCancelFunc[];
#define S1(N) #N
#define S2(N) S1(N)
#define LINESTR S2(__LINE__)
// Handle the renderer side of the routing implementation.
class MRRenderDelegate : public ClientAppRenderer::Delegate {
public:
class V8HandlerImpl : public CefV8Handler {
public:
explicit V8HandlerImpl(CefRefPtr<MRRenderDelegate> delegate)
: delegate_(delegate) {}
bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) override;
private:
CefRefPtr<MRRenderDelegate> delegate_;
IMPLEMENT_REFCOUNTING(V8HandlerImpl);
};
void OnWebKitInitialized(CefRefPtr<ClientAppRenderer> app) override;
void OnContextCreated(CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) override;
void OnContextReleased(CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) override;
bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) override;
private:
CefRefPtr<CefMessageRouterRendererSide> message_router_;
IMPLEMENT_REFCOUNTING(MRRenderDelegate);
};
class MRTestHandler : public TestHandler {
public:
void RunTest() override;
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;
void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
TerminationStatus status) override;
// Only call this method if the navigation isn't canceled.
bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool user_gesture,
bool is_redirect) override;
// Returns true if the router handled the navigation.
bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) override;
CefRefPtr<CefMessageRouterBrowserSide> GetRouter() const;
void SetMessageSizeThreshold(size_t message_size_treshold);
protected:
virtual void RunMRTest() = 0;
virtual void AddHandlers(
CefRefPtr<CefMessageRouterBrowserSide> message_router) = 0;
virtual void OnNotify(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const std::string& message) = 0;
bool AssertQueryCount(CefRefPtr<CefBrowser> browser,
CefMessageRouterBrowserSide::Handler* handler,
int expected_count);
void AssertMainBrowser(CefRefPtr<CefBrowser> browser);
private:
CefRefPtr<CefMessageRouterBrowserSide> message_router_;
size_t message_size_threshold_ = 0;
IMPLEMENT_REFCOUNTING(MRTestHandler);
};
// Implementation of MRTestHandler that loads a single page.
class SingleLoadTestHandler : public MRTestHandler,
public CefMessageRouterBrowserSide::Handler {
public:
SingleLoadTestHandler();
const std::string& GetMainURL() { return main_url_; }
protected:
void RunMRTest() override;
void AddHandlers(
CefRefPtr<CefMessageRouterBrowserSide> message_router) override;
virtual void AddOtherResources() {}
virtual std::string GetMainHTML() = 0;
void AssertMainFrame(CefRefPtr<CefFrame> frame);
private:
const std::string main_url_;
};
#endif // CEF_TESTS_CEFTESTS_MESSAGE_ROUTER_UNITTEST_UTILS_H_

View File

@ -0,0 +1,172 @@
// 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.
#include "include/base/cef_callback.h"
#include "include/cef_shared_process_message_builder.h"
#include "include/cef_task.h"
#include "include/wrapper/cef_closure_task.h"
#include "tests/ceftests/test_handler.h"
#include "tests/gtest/include/gtest/gtest.h"
#include "tests/shared/renderer/client_app_renderer.h"
#include <array>
using client::ClientAppRenderer;
namespace {
struct TestData {
bool flag = true;
int value = 1;
double d_value = 77.77;
std::array<size_t, 50> buffer{};
};
const char kSharedMessageUrl[] = "http://tests/SendSharedProcessMessageTest";
const char kSharedMessageName[] = "SendSharedProcessMessageTest";
CefRefPtr<CefProcessMessage> CreateTestMessage(const TestData& data) {
auto builder = CefSharedProcessMessageBuilder::Create(kSharedMessageName, sizeof(data));
std::memcpy(builder->Memory(), reinterpret_cast<const void*>(&data), sizeof(data));
return builder->Build();
}
// Renderer side.
class SharedMessageRendererTest final : public ClientAppRenderer::Delegate {
public:
bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) override {
if (message->GetName() == kSharedMessageName) {
EXPECT_TRUE(browser.get());
EXPECT_TRUE(frame.get());
EXPECT_EQ(PID_BROWSER, source_process);
EXPECT_TRUE(message.get());
EXPECT_TRUE(message->IsValid());
EXPECT_TRUE(message->IsReadOnly());
EXPECT_EQ(message->GetArgumentList(), nullptr);
const std::string& url = frame->GetURL();
if (url == kSharedMessageUrl) {
// Echo the message back to the sender natively.
frame->SendProcessMessage(PID_BROWSER, message);
EXPECT_FALSE(message->IsValid());
return true;
}
}
return false;
}
IMPLEMENT_REFCOUNTING(SharedMessageRendererTest);
};
// Browser side.
class SharedMessageTestHandler final : public TestHandler {
public:
explicit SharedMessageTestHandler(cef_thread_id_t send_thread)
: send_thread_(send_thread) {}
void RunTest() override {
AddResource(kSharedMessageUrl, "<html><body>TEST</body></html>",
"text/html");
CreateBrowser(kSharedMessageUrl);
// Time out the test after a reasonable period of time.
SetTestTimeout();
}
void OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int httpStatusCode) override {
EXPECT_TRUE(CefCurrentlyOn(TID_UI));
// Send the message to the renderer process.
if (!CefCurrentlyOn(send_thread_)) {
CefPostTask(send_thread_,
base::BindOnce(&SharedMessageTestHandler::SendProcessMessage,
this, browser, frame));
} else {
SendProcessMessage(browser, frame);
}
}
bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) override {
EXPECT_TRUE(CefCurrentlyOn(TID_UI));
EXPECT_TRUE(browser.get());
EXPECT_TRUE(frame.get());
EXPECT_EQ(PID_RENDERER, source_process);
EXPECT_TRUE(message.get());
EXPECT_TRUE(message->IsValid());
EXPECT_TRUE(message->IsReadOnly());
EXPECT_EQ(message->GetArgumentList(), nullptr);
// Verify that the recieved message is the same as the sent message.
auto region = message->GetSharedMemoryRegion();
const TestData* received = static_cast<const TestData*>(region->Memory());
EXPECT_EQ(data_.flag, received->flag);
EXPECT_EQ(data_.value, received->value);
EXPECT_EQ(data_.d_value, received->d_value);
got_message_.yes();
// Test is complete.
DestroyTest();
return true;
}
protected:
void DestroyTest() override {
EXPECT_TRUE(got_message_);
TestHandler::DestroyTest();
}
private:
void SendProcessMessage(const CefRefPtr<CefBrowser>& browser,
const CefRefPtr<CefFrame>& frame) {
EXPECT_TRUE(CefCurrentlyOn(send_thread_));
auto message = CreateTestMessage(data_);
frame->SendProcessMessage(PID_RENDERER, message);
// The message is invalidated immediately
EXPECT_FALSE(message->IsValid());
}
cef_thread_id_t send_thread_;
TrackCallback got_message_;
const TestData data_;
IMPLEMENT_REFCOUNTING(SharedMessageTestHandler);
};
} // namespace
TEST(SendSharedProcessMessageTest, CanSendAndReceiveFromUiThread) {
CefRefPtr<SharedMessageTestHandler> handler =
new SharedMessageTestHandler(TID_UI);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
TEST(SendSharedProcessMessageTest, CanSendAndReceiveFromIoThread) {
CefRefPtr<SharedMessageTestHandler> handler =
new SharedMessageTestHandler(TID_IO);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Entry point for creating shared process message renderer test objects.
// Called from client_app_delegates.cc.
void CreateSharedProcessMessageTests(
ClientAppRenderer::DelegateSet& delegates) {
delegates.insert(new SharedMessageRendererTest());
}

View File

@ -0,0 +1,94 @@
// 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.
#include "include/cef_shared_process_message_builder.h"
#include "testing/gtest/include/gtest/gtest.h"
#include <array>
namespace {
constexpr bool kTestFlag = true;
constexpr int kTestValue = 42;
constexpr double kTestDoubleValue = 123.456;
struct TestData {
bool flag = kTestFlag;
int value = kTestValue;
double doubleValue = kTestDoubleValue;
std::array<size_t, 50> buffer{};
};
const char kSharedMessageName[] = "SharedProcessMessageTest";
CefRefPtr<CefSharedProcessMessageBuilder> CreateTestBuilder() {
auto builder = CefSharedProcessMessageBuilder::Create(kSharedMessageName,
sizeof(TestData));
EXPECT_NE(builder, nullptr);
EXPECT_TRUE(builder->IsValid());
auto data = static_cast<TestData*>(builder->Memory());
EXPECT_NE(data, nullptr);
data->value = kTestValue;
data->doubleValue = kTestDoubleValue;
data->flag = kTestFlag;
for (size_t i = 0; i < data->buffer.size(); ++i) {
data->buffer[i] = i;
}
return builder;
}
} // namespace
TEST(SharedProcessMessageTest, CanBuildSharedMessageUsingBuilder) {
auto builder = CreateTestBuilder();
auto message = builder->Build();
EXPECT_FALSE(builder->IsValid());
EXPECT_NE(message, nullptr);
EXPECT_TRUE(message->IsValid());
EXPECT_TRUE(message->IsReadOnly());
auto region = message->GetSharedMemoryRegion();
EXPECT_TRUE(region->IsValid());
auto read_data = static_cast<const TestData*>(region->Memory());
EXPECT_EQ(read_data->flag, kTestFlag);
EXPECT_EQ(read_data->value, kTestValue);
EXPECT_EQ(read_data->doubleValue, kTestDoubleValue);
for (size_t i = 0; i < read_data->buffer.size(); ++i) {
EXPECT_EQ(read_data->buffer[i], i);
}
}
TEST(SharedProcessMessageTest, CopyingIsNotSupportedBySharedMessage) {
auto builder = CefSharedProcessMessageBuilder::Create(kSharedMessageName,
sizeof(TestData));
CefRefPtr<CefProcessMessage> message = builder->Build();
CefRefPtr<CefProcessMessage> message_copy = message->Copy();
EXPECT_EQ(message_copy, nullptr);
}
TEST(SharedProcessMessageTest,
RegionRemainsValidAfterSharedMessageDestruction) {
CefRefPtr<CefSharedMemoryRegion> region;
{
auto builder = CreateTestBuilder();
auto message = builder->Build();
region = message->GetSharedMemoryRegion();
}
EXPECT_TRUE(region->IsValid());
auto read_data = static_cast<const TestData*>(region->Memory());
EXPECT_EQ(read_data->flag, kTestFlag);
EXPECT_EQ(read_data->value, kTestValue);
EXPECT_EQ(read_data->doubleValue, kTestDoubleValue);
for (size_t i = 0; i < read_data->buffer.size(); ++i) {
EXPECT_EQ(read_data->buffer[i], i);
}
}

View File

@ -0,0 +1,44 @@
// 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.
#include "tests/shared/common/binary_value_utils.h"
namespace bv_utils {
const char kTestSendProcessMessage[] = "testSendProcessMessage";
const char kTestSendSMRProcessMessage[] = "testSendSMRProcessMessage";
TimePoint Now() {
return std::chrono::high_resolution_clock::now();
}
CefRefPtr<CefBinaryValue> CreateCefBinaryValue(
const std::vector<uint8_t>& data) {
return CefBinaryValue::Create(data.data(), data.size());
}
RendererMessage GetRendererMsgFromBinary(
const CefRefPtr<CefBinaryValue>& value) {
DCHECK_GE(value->GetSize(), sizeof(RendererMessage));
std::vector<uint8_t> data(value->GetSize());
value->GetData(data.data(), data.size(), 0);
return *reinterpret_cast<const RendererMessage*>(data.data());
}
BrowserMessage GetBrowserMsgFromBinary(const CefRefPtr<CefBinaryValue>& value) {
DCHECK_GE(value->GetSize(), sizeof(BrowserMessage));
std::vector<uint8_t> data(value->GetSize());
value->GetData(data.data(), data.size(), 0);
return *reinterpret_cast<const BrowserMessage*>(data.data());
}
std::string ToMilliString(const Duration& duration) {
const auto ms =
std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(
duration);
return std::to_string(ms.count());
}
} // namespace bv_utils

View File

@ -0,0 +1,47 @@
// 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.
#ifndef CEF_TESTS_SHARED_COMMON_BINARY_VALUE_UTILS
#define CEF_TESTS_SHARED_COMMON_BINARY_VALUE_UTILS
#pragma once
#include <chrono>
#include <cstdint>
#include <vector>
#include "include/cef_values.h"
namespace bv_utils {
extern const char kTestSendProcessMessage[];
extern const char kTestSendSMRProcessMessage[];
using TimePoint = std::chrono::high_resolution_clock::time_point;
using Duration = std::chrono::high_resolution_clock::duration;
struct RendererMessage {
int test_id;
TimePoint start_time;
};
struct BrowserMessage {
int test_id;
Duration duration;
TimePoint start_time;
};
TimePoint Now();
CefRefPtr<CefBinaryValue> CreateCefBinaryValue(
const std::vector<uint8_t>& data);
RendererMessage GetRendererMsgFromBinary(
const CefRefPtr<CefBinaryValue>& value);
BrowserMessage GetBrowserMsgFromBinary(const CefRefPtr<CefBinaryValue>& value);
std::string ToMilliString(const Duration& duration);
} // namespace bv_utils
#endif // CEF_TESTS_SHARED_COMMON_BINARY_VALUE_UTILS