mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-02-25 00:17:41 +01:00
Add binary format support to CefMessageRouter (fixes #3502)
This commit is contained in:
parent
1b74ac5124
commit
44323082b1
@ -147,6 +147,8 @@
|
||||
'libcef_dll/wrapper/cef_byte_read_handler.cc',
|
||||
'libcef_dll/wrapper/cef_closure_task.cc',
|
||||
'libcef_dll/wrapper/cef_message_router.cc',
|
||||
'libcef_dll/wrapper/cef_message_router_utils.cc',
|
||||
'libcef_dll/wrapper/cef_message_router_utils.h',
|
||||
'libcef_dll/wrapper/cef_resource_manager.cc',
|
||||
'libcef_dll/wrapper/cef_scoped_temp_dir.cc',
|
||||
'libcef_dll/wrapper/cef_stream_resource_handler.cc',
|
||||
@ -218,6 +220,8 @@
|
||||
'tests/shared/browser/util_win.h',
|
||||
],
|
||||
'cefclient_sources_browser': [
|
||||
'tests/cefclient/browser/binary_transfer_test.cc',
|
||||
'tests/cefclient/browser/binary_transfer_test.h',
|
||||
'tests/cefclient/browser/binding_test.cc',
|
||||
'tests/cefclient/browser/binding_test.h',
|
||||
'tests/cefclient/browser/browser_window.cc',
|
||||
@ -309,6 +313,7 @@
|
||||
'tests/cefclient/resources/dialogs.html',
|
||||
'tests/cefclient/resources/draggable.html',
|
||||
'tests/cefclient/resources/ipc_performance.html',
|
||||
'tests/cefclient/resources/binary_transfer.html',
|
||||
'tests/cefclient/resources/localstorage.html',
|
||||
'tests/cefclient/resources/logo.png',
|
||||
'tests/cefclient/resources/media_router.html',
|
||||
@ -496,6 +501,7 @@
|
||||
'tests/ceftests/jsdialog_unittest.cc',
|
||||
'tests/ceftests/life_span_unittest.cc',
|
||||
'tests/ceftests/media_access_unittest.cc',
|
||||
'tests/ceftests/message_router_binary_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',
|
||||
@ -603,6 +609,7 @@
|
||||
'tests/ceftests/dom_unittest.cc',
|
||||
'tests/ceftests/frame_unittest.cc',
|
||||
'tests/ceftests/media_access_unittest.cc',
|
||||
'tests/ceftests/message_router_binary_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',
|
||||
|
@ -33,7 +33,7 @@
|
||||
// by hand. See the translator.README.txt file in the tools directory for
|
||||
// more information.
|
||||
//
|
||||
// $hash=42de7c0e6f5ec529d9182fe4cbf2c1edfacd7392$
|
||||
// $hash=865ca5bff4a0867d0c25cb41bd2aa808cf3fddbd$
|
||||
//
|
||||
|
||||
#ifndef CEF_INCLUDE_CAPI_CEF_V8_CAPI_H_
|
||||
@ -679,6 +679,19 @@ typedef struct _cef_v8value_t {
|
||||
///
|
||||
int(CEF_CALLBACK* neuter_array_buffer)(struct _cef_v8value_t* self);
|
||||
|
||||
///
|
||||
/// Returns the length (in bytes) of the ArrayBuffer.
|
||||
///
|
||||
size_t(CEF_CALLBACK* get_array_buffer_byte_length)(
|
||||
struct _cef_v8value_t* self);
|
||||
|
||||
///
|
||||
/// Returns a pointer to the beginning of the memory block for this
|
||||
/// ArrayBuffer backing store. The returned pointer is valid as long as the
|
||||
/// cef_v8value_t is alive.
|
||||
///
|
||||
void*(CEF_CALLBACK* get_array_buffer_data)(struct _cef_v8value_t* self);
|
||||
|
||||
///
|
||||
/// Returns the function name.
|
||||
///
|
||||
|
@ -33,7 +33,7 @@
|
||||
// by hand. See the translator.README.txt file in the tools directory for
|
||||
// more information.
|
||||
//
|
||||
// $hash=1b8f7f620685c30b91c8fa656e1a01d182684ae6$
|
||||
// $hash=7b8fee9d4a0530782ed62f5741820708f110e24e$
|
||||
//
|
||||
|
||||
#ifndef CEF_INCLUDE_CAPI_CEF_VALUES_CAPI_H_
|
||||
@ -265,6 +265,12 @@ typedef struct _cef_binary_value_t {
|
||||
struct _cef_binary_value_t*(CEF_CALLBACK* copy)(
|
||||
struct _cef_binary_value_t* self);
|
||||
|
||||
///
|
||||
/// Returns a pointer to the beginning of the memory block. The returned
|
||||
/// pointer is valid as long as the cef_binary_value_t is alive.
|
||||
///
|
||||
const void*(CEF_CALLBACK* get_raw_data)(struct _cef_binary_value_t* self);
|
||||
|
||||
///
|
||||
/// Returns the data size.
|
||||
///
|
||||
|
@ -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 "647a2df6b870951e70487997f30e75960d609632"
|
||||
#define CEF_API_HASH_UNIVERSAL "1920c69ce75671bf41d947677d76cc92e9b0ca3b"
|
||||
#if defined(OS_WIN)
|
||||
#define CEF_API_HASH_PLATFORM "4a22d727f3f1f625844191f0f5adf297a2f17fa7"
|
||||
#define CEF_API_HASH_PLATFORM "c1c5f6ed3f032d525d36e737388b0b850f5e0c59"
|
||||
#elif defined(OS_MAC)
|
||||
#define CEF_API_HASH_PLATFORM "84a450a85c81d6cb16dda55d7f07047264d1e205"
|
||||
#define CEF_API_HASH_PLATFORM "5a278846114ac057af673f522377552f41e09179"
|
||||
#elif defined(OS_LINUX)
|
||||
#define CEF_API_HASH_PLATFORM "b7cbd7d044e02cb1854184cc1d5d621605b8476b"
|
||||
#define CEF_API_HASH_PLATFORM "e94defd6a38fb3d2933f5f417ec0e727d6d35abf"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -520,7 +520,7 @@ class CefV8Value : public virtual CefBaseRefCounted {
|
||||
/// or CefV8Accessor callback, or in combination with calling Enter() and
|
||||
/// Exit() on a stored CefV8Context reference.
|
||||
///
|
||||
/*--cef()--*/
|
||||
/*--cef(optional_param=buffer)--*/
|
||||
static CefRefPtr<CefV8Value> CreateArrayBuffer(
|
||||
void* buffer,
|
||||
size_t length,
|
||||
@ -866,6 +866,20 @@ class CefV8Value : public virtual CefBaseRefCounted {
|
||||
/*--cef()--*/
|
||||
virtual bool NeuterArrayBuffer() = 0;
|
||||
|
||||
///
|
||||
/// Returns the length (in bytes) of the ArrayBuffer.
|
||||
///
|
||||
/*--cef()--*/
|
||||
virtual size_t GetArrayBufferByteLength() = 0;
|
||||
|
||||
///
|
||||
/// Returns a pointer to the beginning of the memory block for this
|
||||
/// ArrayBuffer backing store. The returned pointer is valid as long as the
|
||||
/// CefV8Value is alive.
|
||||
///
|
||||
/*--cef()--*/
|
||||
virtual void* GetArrayBufferData() = 0;
|
||||
|
||||
// FUNCTION METHODS - These methods are only available on functions.
|
||||
|
||||
///
|
||||
|
@ -278,6 +278,13 @@ class CefBinaryValue : public virtual CefBaseRefCounted {
|
||||
/*--cef()--*/
|
||||
virtual CefRefPtr<CefBinaryValue> Copy() = 0;
|
||||
|
||||
///
|
||||
/// Returns a pointer to the beginning of the memory block.
|
||||
/// The returned pointer is valid as long as the CefBinaryValue is alive.
|
||||
///
|
||||
/*--cef()--*/
|
||||
virtual const void* GetRawData() = 0;
|
||||
|
||||
///
|
||||
/// Returns the data size.
|
||||
///
|
||||
|
@ -219,6 +219,38 @@ struct CefMessageRouterConfig {
|
||||
size_t message_size_threshold;
|
||||
};
|
||||
|
||||
///
|
||||
/// This class acts as a container for managing binary data. It retains
|
||||
/// references to the underlying backing store, ensuring it is valid as long as
|
||||
/// the CefBinaryBuffer exists. This allows efficient, zero-copy access to data
|
||||
/// received from another process.
|
||||
///
|
||||
/// This class is not designed to be thread-safe, and it is the user's
|
||||
/// responsibility to synchronize access from multiple threads to ensure data
|
||||
/// integrity.
|
||||
///
|
||||
class CefBinaryBuffer : public CefBaseRefCounted {
|
||||
public:
|
||||
///
|
||||
/// Returns the read-only pointer to the memory. Returns nullptr if
|
||||
/// |GetSize()| returns zero. The returned pointer is only valid for the life
|
||||
/// span of this object.
|
||||
///
|
||||
virtual const void* GetData() const = 0;
|
||||
|
||||
///
|
||||
/// Returns the writable pointer to the memory. Returns nullptr if
|
||||
/// |GetSize()| returns zero. The returned pointer is only valid for the life
|
||||
/// span of this object.
|
||||
///
|
||||
virtual void* GetData() = 0;
|
||||
|
||||
///
|
||||
/// Returns the size of the data.
|
||||
///
|
||||
virtual size_t GetSize() const = 0;
|
||||
};
|
||||
|
||||
///
|
||||
/// Implements the browser side of query routing. The methods of this class may
|
||||
/// be called on any browser process thread unless otherwise indicated.
|
||||
@ -238,10 +270,17 @@ class CefMessageRouterBrowserSide
|
||||
public:
|
||||
///
|
||||
/// Notify the associated JavaScript onSuccess callback that the query has
|
||||
/// completed successfully with the specified |response|.
|
||||
/// completed successfully with the specified string |response|.
|
||||
///
|
||||
virtual void Success(const CefString& response) = 0;
|
||||
|
||||
///
|
||||
/// Notify the associated JavaScript onSuccess callback that the query has
|
||||
/// completed successfully with binary data. A |data| pointer to the binary
|
||||
/// data can be nullptr only if the |size| is 0.
|
||||
///
|
||||
virtual void Success(const void* data, size_t size) = 0;
|
||||
|
||||
///
|
||||
/// Notify the associated JavaScript onFailure callback that the query has
|
||||
/// failed with the specified |error_code| and |error_message|.
|
||||
@ -276,6 +315,25 @@ class CefMessageRouterBrowserSide
|
||||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
/// Executed when a new query is received. |query_id| uniquely identifies
|
||||
/// the query for the life span of the router. Return true to handle the
|
||||
/// query or false to propagate the query to other registered handlers, if
|
||||
/// any. If no handlers return true from this method then the query will be
|
||||
/// automatically canceled with an error code of -1 delivered to the
|
||||
/// JavaScript onFailure callback. If this method returns true then a
|
||||
/// Callback method must be executed either in this method or asynchronously
|
||||
/// to complete the query.
|
||||
///
|
||||
virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64_t query_id,
|
||||
CefRefPtr<const CefBinaryBuffer> request,
|
||||
bool persistent,
|
||||
CefRefPtr<Callback> callback) {
|
||||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
/// Executed when a query has been canceled either explicitly using the
|
||||
/// JavaScript cancel function or implicitly due to browser destruction,
|
||||
|
@ -606,6 +606,11 @@ CefRefPtr<CefBinaryValue> CefBinaryValueImpl::Copy() {
|
||||
return new CefBinaryValueImpl(const_value().Clone());
|
||||
}
|
||||
|
||||
const void* CefBinaryValueImpl::GetRawData() {
|
||||
CEF_VALUE_VERIFY_RETURN(false, nullptr);
|
||||
return const_value().GetBlob().data();
|
||||
}
|
||||
|
||||
size_t CefBinaryValueImpl::GetSize() {
|
||||
CEF_VALUE_VERIFY_RETURN(false, 0);
|
||||
return const_value().GetBlob().size();
|
||||
|
@ -177,6 +177,7 @@ class CefBinaryValueImpl : public CefValueBase<CefBinaryValue, base::Value> {
|
||||
bool IsSame(CefRefPtr<CefBinaryValue> that) override;
|
||||
bool IsEqual(CefRefPtr<CefBinaryValue> that) override;
|
||||
CefRefPtr<CefBinaryValue> Copy() override;
|
||||
const void* GetRawData() override;
|
||||
size_t GetSize() override;
|
||||
size_t GetData(void* buffer, size_t buffer_size, size_t data_offset) override;
|
||||
|
||||
|
@ -2368,6 +2368,61 @@ bool CefV8ValueImpl::NeuterArrayBuffer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t CefV8ValueImpl::GetArrayBufferByteLength() {
|
||||
size_t rv = 0;
|
||||
CEF_V8_REQUIRE_ISOLATE_RETURN(rv);
|
||||
if (type_ != TYPE_OBJECT) {
|
||||
DCHECK(false) << "V8 value is not an object";
|
||||
return rv;
|
||||
}
|
||||
|
||||
v8::Isolate* isolate = handle_->isolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
if (context.IsEmpty()) {
|
||||
DCHECK(false) << "not currently in a V8 context";
|
||||
return rv;
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
|
||||
if (!value->IsArrayBuffer()) {
|
||||
DCHECK(false) << "V8 value is not an array buffer";
|
||||
return rv;
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
|
||||
return v8::Local<v8::ArrayBuffer>::Cast(obj)->ByteLength();
|
||||
}
|
||||
|
||||
void* CefV8ValueImpl::GetArrayBufferData() {
|
||||
void* rv = nullptr;
|
||||
CEF_V8_REQUIRE_ISOLATE_RETURN(rv);
|
||||
if (type_ != TYPE_OBJECT) {
|
||||
DCHECK(false) << "V8 value is not an object";
|
||||
return rv;
|
||||
}
|
||||
|
||||
v8::Isolate* isolate = handle_->isolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
if (context.IsEmpty()) {
|
||||
DCHECK(false) << "not currently in a V8 context";
|
||||
return rv;
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> value = handle_->GetNewV8Handle(false);
|
||||
if (!value->IsArrayBuffer()) {
|
||||
DCHECK(false) << "V8 value is not an array buffer";
|
||||
return rv;
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked();
|
||||
v8::Local<v8::ArrayBuffer> arr = v8::Local<v8::ArrayBuffer>::Cast(obj);
|
||||
return arr->Data();
|
||||
}
|
||||
|
||||
CefString CefV8ValueImpl::GetFunctionName() {
|
||||
CefString rv;
|
||||
CEF_V8_REQUIRE_OBJECT_RETURN(rv);
|
||||
|
@ -274,6 +274,8 @@ class CefV8ValueImpl : public CefV8Value {
|
||||
CefRefPtr<CefV8ArrayBufferReleaseCallback> GetArrayBufferReleaseCallback()
|
||||
override;
|
||||
bool NeuterArrayBuffer() override;
|
||||
size_t GetArrayBufferByteLength() override;
|
||||
void* GetArrayBufferData() override;
|
||||
CefString GetFunctionName() override;
|
||||
CefRefPtr<CefV8Handler> GetFunctionHandler() override;
|
||||
CefRefPtr<CefV8Value> ExecuteFunction(
|
||||
|
@ -9,7 +9,7 @@
|
||||
// implementations. See the translator.README.txt file in the tools directory
|
||||
// for more information.
|
||||
//
|
||||
// $hash=905b24443e08e2d3e2464d5f4138e97904be3e9e$
|
||||
// $hash=8da36dc268f2f9beb26abc105656f3b04b1d46ed$
|
||||
//
|
||||
|
||||
#include "libcef_dll/cpptoc/binary_value_cpptoc.h"
|
||||
@ -140,6 +140,25 @@ binary_value_copy(struct _cef_binary_value_t* self) {
|
||||
return CefBinaryValueCppToC::Wrap(_retval);
|
||||
}
|
||||
|
||||
const void* CEF_CALLBACK
|
||||
binary_value_get_raw_data(struct _cef_binary_value_t* self) {
|
||||
shutdown_checker::AssertNotShutdown();
|
||||
|
||||
DCHECK(self);
|
||||
if (!self) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// This manual implementation can be removed once support for 'const void*'
|
||||
// is integrated into the CEF translator tool (issue #3591).
|
||||
|
||||
// Execute
|
||||
const void* _retval = CefBinaryValueCppToC::Get(self)->GetRawData();
|
||||
|
||||
// Return type: simple_byaddr
|
||||
return _retval;
|
||||
}
|
||||
|
||||
size_t CEF_CALLBACK binary_value_get_size(struct _cef_binary_value_t* self) {
|
||||
shutdown_checker::AssertNotShutdown();
|
||||
|
||||
@ -193,6 +212,7 @@ CefBinaryValueCppToC::CefBinaryValueCppToC() {
|
||||
GetStruct()->is_same = binary_value_is_same;
|
||||
GetStruct()->is_equal = binary_value_is_equal;
|
||||
GetStruct()->copy = binary_value_copy;
|
||||
GetStruct()->get_raw_data = binary_value_get_raw_data;
|
||||
GetStruct()->get_size = binary_value_get_size;
|
||||
GetStruct()->get_data = binary_value_get_data;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
// implementations. See the translator.README.txt file in the tools directory
|
||||
// for more information.
|
||||
//
|
||||
// $hash=3bc6db85e54dc87c1e592291be01820547e0989f$
|
||||
// $hash=4356ad718a385149741e4c8bbbe5d5290466ed40$
|
||||
//
|
||||
|
||||
#include "libcef_dll/cpptoc/shared_memory_region_cpptoc.h"
|
||||
@ -64,6 +64,9 @@ shared_memory_region_memory(struct _cef_shared_memory_region_t* self) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// This manual implementation can be removed once support for 'void*'
|
||||
// is integrated into the CEF translator tool (issue #3591).
|
||||
|
||||
// Execute
|
||||
void* _retval = CefSharedMemoryRegionCppToC::Get(self)->Memory();
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
// implementations. See the translator.README.txt file in the tools directory
|
||||
// for more information.
|
||||
//
|
||||
// $hash=ecd6caa0c415b57e93bc66f3c7a4cfb547f022c1$
|
||||
// $hash=21d8fb47eb282f40fb9d602f44b8c1fd4ff44dea$
|
||||
//
|
||||
|
||||
#include "libcef_dll/cpptoc/v8value_cpptoc.h"
|
||||
@ -138,16 +138,12 @@ CEF_EXPORT cef_v8value_t* cef_v8value_create_array_buffer(
|
||||
cef_v8array_buffer_release_callback_t* release_callback) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
// Verify param: buffer; type: simple_byaddr
|
||||
DCHECK(buffer);
|
||||
if (!buffer) {
|
||||
return NULL;
|
||||
}
|
||||
// Verify param: release_callback; type: refptr_diff
|
||||
DCHECK(release_callback);
|
||||
if (!release_callback) {
|
||||
return NULL;
|
||||
}
|
||||
// Unverified params: buffer
|
||||
|
||||
// Execute
|
||||
CefRefPtr<CefV8Value> _retval = CefV8Value::CreateArrayBuffer(
|
||||
@ -948,6 +944,38 @@ int CEF_CALLBACK v8value_neuter_array_buffer(struct _cef_v8value_t* self) {
|
||||
return _retval;
|
||||
}
|
||||
|
||||
size_t CEF_CALLBACK
|
||||
v8value_get_array_buffer_byte_length(struct _cef_v8value_t* self) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
DCHECK(self);
|
||||
if (!self) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Execute
|
||||
size_t _retval = CefV8ValueCppToC::Get(self)->GetArrayBufferByteLength();
|
||||
|
||||
// Return type: simple
|
||||
return _retval;
|
||||
}
|
||||
|
||||
void* CEF_CALLBACK v8value_get_array_buffer_data(struct _cef_v8value_t* self) {
|
||||
DCHECK(self);
|
||||
if (!self) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// This manual implementation can be removed once support for 'void*'
|
||||
// is integrated into the CEF translator tool (issue #3591).
|
||||
|
||||
// Execute
|
||||
void* _retval = CefV8ValueCppToC::Get(self)->GetArrayBufferData();
|
||||
|
||||
// Return type: simple_byaddr
|
||||
return _retval;
|
||||
}
|
||||
|
||||
cef_string_userfree_t CEF_CALLBACK
|
||||
v8value_get_function_name(struct _cef_v8value_t* self) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
@ -1153,6 +1181,9 @@ CefV8ValueCppToC::CefV8ValueCppToC() {
|
||||
GetStruct()->get_array_buffer_release_callback =
|
||||
v8value_get_array_buffer_release_callback;
|
||||
GetStruct()->neuter_array_buffer = v8value_neuter_array_buffer;
|
||||
GetStruct()->get_array_buffer_byte_length =
|
||||
v8value_get_array_buffer_byte_length;
|
||||
GetStruct()->get_array_buffer_data = v8value_get_array_buffer_data;
|
||||
GetStruct()->get_function_name = v8value_get_function_name;
|
||||
GetStruct()->get_function_handler = v8value_get_function_handler;
|
||||
GetStruct()->execute_function = v8value_execute_function;
|
||||
|
@ -9,7 +9,7 @@
|
||||
// implementations. See the translator.README.txt file in the tools directory
|
||||
// for more information.
|
||||
//
|
||||
// $hash=caaf5dfe8d56c784e213b1590c21194f08084b70$
|
||||
// $hash=f42bab3d9e4ff45faf1fd8071646a8e5bed64177$
|
||||
//
|
||||
|
||||
#include "libcef_dll/ctocpp/binary_value_ctocpp.h"
|
||||
@ -139,6 +139,24 @@ CefRefPtr<CefBinaryValue> CefBinaryValueCToCpp::Copy() {
|
||||
return CefBinaryValueCToCpp::Wrap(_retval);
|
||||
}
|
||||
|
||||
NO_SANITIZE("cfi-icall") const void* CefBinaryValueCToCpp::GetRawData() {
|
||||
shutdown_checker::AssertNotShutdown();
|
||||
|
||||
cef_binary_value_t* _struct = GetStruct();
|
||||
if (CEF_MEMBER_MISSING(_struct, get_raw_data)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// This manual implementation can be removed once support for 'const void*'
|
||||
// is integrated into the CEF translator tool (issue #3591).
|
||||
|
||||
// Execute
|
||||
const void* _retval = _struct->get_raw_data(_struct);
|
||||
|
||||
// Return type: simple_byaddr
|
||||
return _retval;
|
||||
}
|
||||
|
||||
NO_SANITIZE("cfi-icall") size_t CefBinaryValueCToCpp::GetSize() {
|
||||
shutdown_checker::AssertNotShutdown();
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
// implementations. See the translator.README.txt file in the tools directory
|
||||
// for more information.
|
||||
//
|
||||
// $hash=b6f011a6c26b4264084eb68dae0d63032c07013c$
|
||||
// $hash=2e0ac9b73ba6bdb4b07ee0f8c445974359c5862f$
|
||||
//
|
||||
|
||||
#ifndef CEF_LIBCEF_DLL_CTOCPP_BINARY_VALUE_CTOCPP_H_
|
||||
@ -39,6 +39,7 @@ class CefBinaryValueCToCpp : public CefCToCppRefCounted<CefBinaryValueCToCpp,
|
||||
bool IsSame(CefRefPtr<CefBinaryValue> that) override;
|
||||
bool IsEqual(CefRefPtr<CefBinaryValue> that) override;
|
||||
CefRefPtr<CefBinaryValue> Copy() override;
|
||||
const void* GetRawData() override;
|
||||
size_t GetSize() override;
|
||||
size_t GetData(void* buffer, size_t buffer_size, size_t data_offset) override;
|
||||
};
|
||||
|
@ -9,7 +9,7 @@
|
||||
// implementations. See the translator.README.txt file in the tools directory
|
||||
// for more information.
|
||||
//
|
||||
// $hash=31516110398f9fe682988645d74ac8789b712181$
|
||||
// $hash=a396f422ed18fe4aae90d4fef3750b4726279c7e$
|
||||
//
|
||||
|
||||
#include "libcef_dll/ctocpp/shared_memory_region_ctocpp.h"
|
||||
@ -59,6 +59,9 @@ NO_SANITIZE("cfi-icall") void* CefSharedMemoryRegionCToCpp::Memory() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// This manual implementation can be removed once support for 'void*'
|
||||
// is integrated into the CEF translator tool (issue #3591).
|
||||
|
||||
// Execute
|
||||
void* _retval = _struct->memory(_struct);
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
// implementations. See the translator.README.txt file in the tools directory
|
||||
// for more information.
|
||||
//
|
||||
// $hash=459c331b0c02f55c4b700761ad2132d7320fd467$
|
||||
// $hash=f67b0996d7e2133f3f28f2d8ba5446c5ff40aaba$
|
||||
//
|
||||
|
||||
#include "libcef_dll/ctocpp/v8value_ctocpp.h"
|
||||
@ -147,16 +147,12 @@ CefRefPtr<CefV8Value> CefV8Value::CreateArrayBuffer(
|
||||
CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
// Verify param: buffer; type: simple_byaddr
|
||||
DCHECK(buffer);
|
||||
if (!buffer) {
|
||||
return nullptr;
|
||||
}
|
||||
// Verify param: release_callback; type: refptr_diff
|
||||
DCHECK(release_callback.get());
|
||||
if (!release_callback.get()) {
|
||||
return nullptr;
|
||||
}
|
||||
// Unverified params: buffer
|
||||
|
||||
// Execute
|
||||
cef_v8value_t* _retval = cef_v8value_create_array_buffer(
|
||||
@ -957,6 +953,37 @@ NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::NeuterArrayBuffer() {
|
||||
return _retval ? true : false;
|
||||
}
|
||||
|
||||
NO_SANITIZE("cfi-icall") size_t CefV8ValueCToCpp::GetArrayBufferByteLength() {
|
||||
cef_v8value_t* _struct = GetStruct();
|
||||
if (CEF_MEMBER_MISSING(_struct, get_array_buffer_byte_length)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
// Execute
|
||||
size_t _retval = _struct->get_array_buffer_byte_length(_struct);
|
||||
|
||||
// Return type: simple
|
||||
return _retval;
|
||||
}
|
||||
|
||||
NO_SANITIZE("cfi-icall") void* CefV8ValueCToCpp::GetArrayBufferData() {
|
||||
cef_v8value_t* _struct = GetStruct();
|
||||
if (CEF_MEMBER_MISSING(_struct, get_array_buffer_data)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// This manual implementation can be removed once support for 'void*'
|
||||
// is integrated into the CEF translator tool (issue #3591).
|
||||
|
||||
// Execute
|
||||
void* _retval = _struct->get_array_buffer_data(_struct);
|
||||
|
||||
// Return type: simple_byaddr
|
||||
return _retval;
|
||||
}
|
||||
|
||||
NO_SANITIZE("cfi-icall") CefString CefV8ValueCToCpp::GetFunctionName() {
|
||||
cef_v8value_t* _struct = GetStruct();
|
||||
if (CEF_MEMBER_MISSING(_struct, get_function_name)) {
|
||||
|
@ -9,7 +9,7 @@
|
||||
// implementations. See the translator.README.txt file in the tools directory
|
||||
// for more information.
|
||||
//
|
||||
// $hash=cab5b018f6706a3c8496865e0c9f30fcbc94cdd8$
|
||||
// $hash=c81cc0910be6678c0512c5423b8fc5dc1df42743$
|
||||
//
|
||||
|
||||
#ifndef CEF_LIBCEF_DLL_CTOCPP_V8VALUE_CTOCPP_H_
|
||||
@ -83,6 +83,8 @@ class CefV8ValueCToCpp
|
||||
CefRefPtr<CefV8ArrayBufferReleaseCallback> GetArrayBufferReleaseCallback()
|
||||
override;
|
||||
bool NeuterArrayBuffer() override;
|
||||
size_t GetArrayBufferByteLength() override;
|
||||
void* GetArrayBufferData() override;
|
||||
CefString GetFunctionName() override;
|
||||
CefRefPtr<CefV8Handler> GetFunctionHandler() override;
|
||||
CefRefPtr<CefV8Value> ExecuteFunction(
|
||||
|
@ -9,11 +9,11 @@
|
||||
#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"
|
||||
#include "libcef_dll/wrapper/cef_browser_info_map.h"
|
||||
#include "libcef_dll/wrapper/cef_message_router_utils.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -47,126 +47,7 @@ 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 GetByteLength(const CefString& value) {
|
||||
return value.size() * sizeof(CefString::char_type);
|
||||
}
|
||||
|
||||
size_t GetMessageSize(const CefString& response) {
|
||||
return sizeof(MessageHeader) + GetByteLength(response);
|
||||
}
|
||||
|
||||
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 (GetByteLength(response) <= 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{};
|
||||
}
|
||||
namespace cmru = cef_message_router_utils;
|
||||
|
||||
/**
|
||||
* @brief A helper template for generating ID values.
|
||||
@ -204,11 +85,15 @@ class CefMessageRouterBrowserSideImpl : public CefMessageRouterBrowserSide {
|
||||
CallbackImpl(CefRefPtr<CefMessageRouterBrowserSideImpl> router,
|
||||
int browser_id,
|
||||
int64_t query_id,
|
||||
bool persistent)
|
||||
bool persistent,
|
||||
size_t message_size_threshold,
|
||||
const std::string& query_message_name)
|
||||
: router_(router),
|
||||
browser_id_(browser_id),
|
||||
query_id_(query_id),
|
||||
persistent_(persistent) {}
|
||||
persistent_(persistent),
|
||||
message_size_threshold_(message_size_threshold),
|
||||
query_message_name_(query_message_name) {}
|
||||
|
||||
CallbackImpl(const CallbackImpl&) = delete;
|
||||
CallbackImpl& operator=(const CallbackImpl&) = delete;
|
||||
@ -221,44 +106,36 @@ class CefMessageRouterBrowserSideImpl : public CefMessageRouterBrowserSide {
|
||||
}
|
||||
|
||||
void Success(const CefString& response) override {
|
||||
if (!CefCurrentlyOn(TID_UI)) {
|
||||
// Must execute on the UI thread to access member variables.
|
||||
auto builder = cmru::CreateBrowserResponseBuilder(
|
||||
message_size_threshold_, query_message_name_, response);
|
||||
|
||||
// We need to post task here for two reasons:
|
||||
// 1) To safely access member variables.
|
||||
// 2) To let the router to persist the query information before
|
||||
// the Success callback is executed.
|
||||
CefPostTask(TID_UI,
|
||||
base::BindOnce(&CallbackImpl::Success, this, response));
|
||||
return;
|
||||
base::BindOnce(&CallbackImpl::SuccessImpl, this, builder));
|
||||
}
|
||||
|
||||
if (router_) {
|
||||
CefPostTask(
|
||||
TID_UI,
|
||||
base::BindOnce(&CefMessageRouterBrowserSideImpl::OnCallbackSuccess,
|
||||
router_.get(), browser_id_, query_id_, response));
|
||||
void Success(const void* data, size_t size) override {
|
||||
auto builder = cmru::CreateBrowserResponseBuilder(
|
||||
message_size_threshold_, query_message_name_, data, size);
|
||||
|
||||
if (!persistent_) {
|
||||
// Non-persistent callbacks are only good for a single use.
|
||||
router_ = nullptr;
|
||||
}
|
||||
}
|
||||
// We need to post task here for two reasons:
|
||||
// 1) To safely access member variables.
|
||||
// 2) To let the router to persist the query information before
|
||||
// the Success callback is executed.
|
||||
CefPostTask(TID_UI,
|
||||
base::BindOnce(&CallbackImpl::SuccessImpl, this, builder));
|
||||
}
|
||||
|
||||
void Failure(int error_code, const CefString& error_message) override {
|
||||
if (!CefCurrentlyOn(TID_UI)) {
|
||||
// Must execute on the UI thread to access member variables.
|
||||
CefPostTask(TID_UI, base::BindOnce(&CallbackImpl::Failure, this,
|
||||
// We need to post task here for two reasons:
|
||||
// 1) To safely access member variables.
|
||||
// 2) To give previosly submitted tasks by the Success calls to execute
|
||||
// before we invalidate the callback.
|
||||
CefPostTask(TID_UI, base::BindOnce(&CallbackImpl::FailureImpl, this,
|
||||
error_code, error_message));
|
||||
return;
|
||||
}
|
||||
|
||||
if (router_) {
|
||||
CefPostTask(
|
||||
TID_UI,
|
||||
base::BindOnce(&CefMessageRouterBrowserSideImpl::OnCallbackFailure,
|
||||
router_.get(), browser_id_, query_id_, error_code,
|
||||
error_message));
|
||||
|
||||
// Failure always invalidates the callback.
|
||||
router_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Detach() {
|
||||
@ -267,10 +144,37 @@ class CefMessageRouterBrowserSideImpl : public CefMessageRouterBrowserSide {
|
||||
}
|
||||
|
||||
private:
|
||||
void SuccessImpl(const CefRefPtr<cmru::BrowserResponseBuilder>& builder) {
|
||||
if (!router_) {
|
||||
return;
|
||||
}
|
||||
|
||||
router_->OnCallbackSuccess(browser_id_, query_id_, builder);
|
||||
|
||||
if (!persistent_) {
|
||||
// Non-persistent callbacks are only good for a single use.
|
||||
router_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void FailureImpl(int error_code, const CefString& error_message) {
|
||||
if (!router_) {
|
||||
return;
|
||||
}
|
||||
|
||||
router_->OnCallbackFailure(browser_id_, query_id_, error_code,
|
||||
error_message);
|
||||
|
||||
// Failure always invalidates the callback.
|
||||
router_ = nullptr;
|
||||
}
|
||||
|
||||
CefRefPtr<CefMessageRouterBrowserSideImpl> router_;
|
||||
const int browser_id_;
|
||||
const int64_t query_id_;
|
||||
const bool persistent_;
|
||||
const size_t message_size_threshold_;
|
||||
const std::string query_message_name_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(CallbackImpl);
|
||||
};
|
||||
@ -388,13 +292,10 @@ class CefMessageRouterBrowserSideImpl : public CefMessageRouterBrowserSide {
|
||||
|
||||
const std::string& message_name = message->GetName();
|
||||
if (message_name == query_message_name_) {
|
||||
CefRefPtr<CefListValue> args = message->GetArgumentList();
|
||||
DCHECK_EQ(args->GetSize(), 4U);
|
||||
|
||||
const int context_id = args->GetInt(0);
|
||||
const int request_id = args->GetInt(1);
|
||||
const CefString& request = args->GetString(2);
|
||||
const bool persistent = args->GetBool(3);
|
||||
cmru::RendererMessage content = cmru::ParseRendererMessage(message);
|
||||
const int context_id = content.context_id;
|
||||
const int request_id = content.request_id;
|
||||
const bool persistent = content.is_persistent;
|
||||
|
||||
if (handler_set_.empty()) {
|
||||
// No handlers so cancel the query.
|
||||
@ -405,40 +306,38 @@ class CefMessageRouterBrowserSideImpl : public CefMessageRouterBrowserSide {
|
||||
const int browser_id = browser->GetIdentifier();
|
||||
const int64_t query_id = query_id_generator_.GetNextId();
|
||||
|
||||
CefRefPtr<CallbackImpl> callback(
|
||||
new CallbackImpl(this, browser_id, query_id, persistent));
|
||||
CefRefPtr<CallbackImpl> callback =
|
||||
new CallbackImpl(this, browser_id, query_id, persistent,
|
||||
config_.message_size_threshold, query_message_name_);
|
||||
|
||||
// Make a copy of the handler list in case the user adds or removes a
|
||||
// handler while we're iterating.
|
||||
HandlerSet handler_set = handler_set_;
|
||||
const HandlerSet handlers = handler_set_;
|
||||
|
||||
bool handled = false;
|
||||
HandlerSet::const_iterator it_handler = handler_set.begin();
|
||||
for (; it_handler != handler_set.end(); ++it_handler) {
|
||||
handled = (*it_handler)
|
||||
->OnQuery(browser, frame, query_id, request, persistent,
|
||||
callback.get());
|
||||
Handler* handler = std::visit(
|
||||
[&](const auto& arg) -> CefMessageRouterBrowserSide::Handler* {
|
||||
for (auto handler : handlers) {
|
||||
bool handled = handler->OnQuery(browser, frame, query_id, arg,
|
||||
persistent, callback.get());
|
||||
if (handled) {
|
||||
break;
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
},
|
||||
content.payload);
|
||||
|
||||
// If the query isn't handled nothing should be keeping a reference to
|
||||
// the callback.
|
||||
DCHECK(handled || callback->HasOneRef());
|
||||
DCHECK(handler != nullptr || callback->HasOneRef());
|
||||
|
||||
if (handled) {
|
||||
if (handler) {
|
||||
// Persist the query information until the callback executes.
|
||||
// It's safe to do this here because the callback will execute
|
||||
// asynchronously.
|
||||
QueryInfo* info = new QueryInfo;
|
||||
info->browser = browser;
|
||||
info->frame = frame;
|
||||
info->context_id = context_id;
|
||||
info->request_id = request_id;
|
||||
info->persistent = persistent;
|
||||
info->callback = callback;
|
||||
info->handler = *(it_handler);
|
||||
QueryInfo* info =
|
||||
new QueryInfo{browser, frame, context_id, request_id,
|
||||
persistent, callback, handler};
|
||||
browser_query_info_map_.Add(browser_id, query_id, info);
|
||||
} else {
|
||||
// Invalidate the callback.
|
||||
@ -527,15 +426,17 @@ class CefMessageRouterBrowserSideImpl : public CefMessageRouterBrowserSide {
|
||||
}
|
||||
|
||||
// Called by CallbackImpl on success.
|
||||
void OnCallbackSuccess(int browser_id,
|
||||
void OnCallbackSuccess(
|
||||
int browser_id,
|
||||
int64_t query_id,
|
||||
const CefString& response) {
|
||||
const CefRefPtr<cmru::BrowserResponseBuilder>& builder) {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
|
||||
bool removed;
|
||||
QueryInfo* info = GetQueryInfo(browser_id, query_id, false, &removed);
|
||||
if (info) {
|
||||
SendQuerySuccess(info, response);
|
||||
SendQuerySuccess(info->browser, info->frame, info->context_id,
|
||||
info->request_id, builder);
|
||||
if (removed) {
|
||||
delete info;
|
||||
}
|
||||
@ -558,19 +459,13 @@ class CefMessageRouterBrowserSideImpl : public CefMessageRouterBrowserSide {
|
||||
}
|
||||
}
|
||||
|
||||
void SendQuerySuccess(QueryInfo* info, const CefString& response) {
|
||||
SendQuerySuccess(info->browser, info->frame, info->context_id,
|
||||
info->request_id, response);
|
||||
}
|
||||
|
||||
void SendQuerySuccess(CefRefPtr<CefBrowser> browser,
|
||||
void SendQuerySuccess(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int context_id,
|
||||
int request_id,
|
||||
const CefString& response) {
|
||||
if (auto message =
|
||||
BuildMessage(config_.message_size_threshold, query_message_name_,
|
||||
context_id, request_id, response)) {
|
||||
const CefRefPtr<cmru::BrowserResponseBuilder>& builder) {
|
||||
if (auto message = builder->Build(context_id, request_id)) {
|
||||
frame->SendProcessMessage(PID_RENDERER, message);
|
||||
}
|
||||
}
|
||||
@ -758,10 +653,16 @@ class CefMessageRouterRendererSideImpl : public CefMessageRouterRendererSide {
|
||||
CefRefPtr<CefV8Value> arg = arguments[0];
|
||||
|
||||
CefRefPtr<CefV8Value> requestVal = arg->GetValue(kMemberRequest);
|
||||
if (!requestVal.get() || !requestVal->IsString()) {
|
||||
if (!requestVal.get()) {
|
||||
exception = "Invalid arguments; object member '" +
|
||||
std::string(kMemberRequest) + "' is required";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!requestVal->IsString() && !requestVal->IsArrayBuffer()) {
|
||||
exception = "Invalid arguments; object member '" +
|
||||
std::string(kMemberRequest) +
|
||||
"' is required and must have type string";
|
||||
"' must have type string or ArrayBuffer";
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -804,9 +705,11 @@ class CefMessageRouterRendererSideImpl : public CefMessageRouterRendererSide {
|
||||
(persistentVal.get() && persistentVal->GetBoolValue());
|
||||
|
||||
const int request_id = router_->SendQuery(
|
||||
context->GetBrowser(), context->GetFrame(), context_id,
|
||||
requestVal->GetStringValue(), persistent, successVal, failureVal);
|
||||
context->GetBrowser(), context->GetFrame(), context_id, requestVal,
|
||||
persistent, successVal, failureVal);
|
||||
|
||||
retval = CefV8Value::CreateInt(request_id);
|
||||
|
||||
return true;
|
||||
} else if (name == config_.js_cancel_function) {
|
||||
if (arguments.size() != 1 || !arguments[0]->IsInt()) {
|
||||
@ -958,31 +861,28 @@ class CefMessageRouterRendererSideImpl : public CefMessageRouterRendererSide {
|
||||
CefRefPtr<CefProcessMessage> message) override {
|
||||
CEF_REQUIRE_RENDERER_THREAD();
|
||||
|
||||
const std::string& message_name = message->GetName();
|
||||
if (message_name == query_message_name_) {
|
||||
auto content = ParseMessage(message);
|
||||
if (content.success) {
|
||||
CefPostTask(
|
||||
TID_RENDERER,
|
||||
base::BindOnce(
|
||||
&CefMessageRouterRendererSideImpl::ExecuteSuccessCallback, this,
|
||||
browser->GetIdentifier(), content.context_id,
|
||||
content.request_id, content.message));
|
||||
if (message->GetName() != query_message_name_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cmru::BrowserMessage content = cmru::ParseBrowserMessage(message);
|
||||
if (content.is_success) {
|
||||
std::visit(
|
||||
[&](const auto& arg) {
|
||||
ExecuteSuccessCallback(browser->GetIdentifier(), content.context_id,
|
||||
content.request_id, arg);
|
||||
},
|
||||
content.payload);
|
||||
|
||||
} else {
|
||||
CefPostTask(
|
||||
TID_RENDERER,
|
||||
base::BindOnce(
|
||||
&CefMessageRouterRendererSideImpl::ExecuteFailureCallback, this,
|
||||
browser->GetIdentifier(), content.context_id,
|
||||
content.request_id, content.error_code, content.message));
|
||||
ExecuteFailureCallback(browser->GetIdentifier(), content.context_id,
|
||||
content.request_id, content.error_code,
|
||||
std::get<CefString>(content.payload));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
// Structure representing a pending request.
|
||||
struct RequestInfo {
|
||||
@ -1039,7 +939,7 @@ class CefMessageRouterRendererSideImpl : public CefMessageRouterRendererSide {
|
||||
int SendQuery(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int context_id,
|
||||
const CefString& request,
|
||||
CefRefPtr<CefV8Value> request,
|
||||
bool persistent,
|
||||
CefRefPtr<CefV8Value> success_callback,
|
||||
CefRefPtr<CefV8Value> failure_callback) {
|
||||
@ -1053,14 +953,9 @@ class CefMessageRouterRendererSideImpl : public CefMessageRouterRendererSide {
|
||||
browser_request_info_map_.Add(browser->GetIdentifier(),
|
||||
std::make_pair(context_id, request_id), info);
|
||||
|
||||
CefRefPtr<CefProcessMessage> message =
|
||||
CefProcessMessage::Create(query_message_name_);
|
||||
|
||||
CefRefPtr<CefListValue> args = message->GetArgumentList();
|
||||
args->SetInt(0, context_id);
|
||||
args->SetInt(1, request_id);
|
||||
args->SetString(2, request);
|
||||
args->SetBool(3, persistent);
|
||||
CefRefPtr<CefProcessMessage> message = cmru::BuildRendererMsg(
|
||||
config_.message_size_threshold, query_message_name_, context_id,
|
||||
request_id, request, persistent);
|
||||
|
||||
frame->SendProcessMessage(PID_BROWSER, message);
|
||||
|
||||
@ -1162,6 +1057,41 @@ class CefMessageRouterRendererSideImpl : public CefMessageRouterRendererSide {
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the onSuccess JavaScript callback.
|
||||
void ExecuteSuccessCallback(int browser_id,
|
||||
int context_id,
|
||||
int request_id,
|
||||
const CefRefPtr<CefBinaryBuffer>& response) {
|
||||
CEF_REQUIRE_RENDERER_THREAD();
|
||||
|
||||
bool removed;
|
||||
RequestInfo* info =
|
||||
GetRequestInfo(browser_id, context_id, request_id, false, &removed);
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
|
||||
CefRefPtr<CefV8Context> context = GetContextByID(context_id);
|
||||
if (context && info->success_callback && context->Enter()) {
|
||||
CefRefPtr<cmru::BinaryValueABRCallback> release_callback =
|
||||
new cmru::BinaryValueABRCallback(response);
|
||||
|
||||
CefRefPtr<CefV8Value> value = CefV8Value::CreateArrayBuffer(
|
||||
response->GetData(), response->GetSize(), release_callback);
|
||||
|
||||
context->Exit();
|
||||
|
||||
CefV8ValueList args;
|
||||
args.push_back(value);
|
||||
info->success_callback->ExecuteFunctionWithContext(context, nullptr,
|
||||
args);
|
||||
}
|
||||
|
||||
if (removed) {
|
||||
delete info;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the onFailure JavaScript callback.
|
||||
void ExecuteFailureCallback(int browser_id,
|
||||
int context_id,
|
||||
|
525
libcef_dll/wrapper/cef_message_router_utils.cc
Normal file
525
libcef_dll/wrapper/cef_message_router_utils.cc
Normal file
@ -0,0 +1,525 @@
|
||||
// Copyright (c) 2023 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_dll/wrapper/cef_message_router_utils.h"
|
||||
|
||||
#include "include/cef_shared_process_message_builder.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace cef_message_router_utils {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kNoError = 0;
|
||||
|
||||
constexpr size_t kContextId = 0;
|
||||
constexpr size_t kRequestId = 1;
|
||||
constexpr size_t kRendererPayload = 2;
|
||||
constexpr size_t kIsSuccess = 2;
|
||||
constexpr size_t kBrowserPayload = 3;
|
||||
constexpr size_t kIsPersistent = 3;
|
||||
|
||||
struct BrowserMsgHeader {
|
||||
int context_id;
|
||||
int request_id;
|
||||
bool is_binary;
|
||||
};
|
||||
|
||||
static_assert(
|
||||
std::is_trivially_copyable_v<BrowserMsgHeader>,
|
||||
"Copying non-trivially-copyable object across memory spaces is dangerous");
|
||||
|
||||
struct RendererMsgHeader {
|
||||
int context_id;
|
||||
int request_id;
|
||||
bool is_persistent;
|
||||
bool is_binary;
|
||||
};
|
||||
|
||||
static_assert(
|
||||
std::is_trivially_copyable_v<RendererMsgHeader>,
|
||||
"Copying non-trivially-copyable object across memory spaces is dangerous");
|
||||
|
||||
//
|
||||
// This is a workaround for handling empty CefBinaryValues, as it's not possible
|
||||
// to create an empty one directly. We use this empty struct as a tag to invoke
|
||||
// the SetNull function within the BuildBrowserListMsg and BuildRendererListMsg
|
||||
// functions.
|
||||
//
|
||||
struct Empty {};
|
||||
|
||||
size_t GetByteLength(const CefString& value) {
|
||||
return value.size() * sizeof(CefString::char_type);
|
||||
}
|
||||
|
||||
size_t GetByteLength(const CefRefPtr<CefV8Value>& value) {
|
||||
return value->GetArrayBufferByteLength();
|
||||
}
|
||||
|
||||
const CefString& GetListRepresentation(const CefString& value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
CefRefPtr<CefBinaryValue> GetListRepresentation(
|
||||
const CefRefPtr<CefV8Value>& value) {
|
||||
return CefBinaryValue::Create(value->GetArrayBufferData(),
|
||||
value->GetArrayBufferByteLength());
|
||||
}
|
||||
|
||||
template <class Header, class T>
|
||||
size_t GetMessageSize(const T& value) {
|
||||
return sizeof(Header) + GetByteLength(value);
|
||||
}
|
||||
|
||||
template <class Header>
|
||||
void CopyIntoMemory(void* memory, const void* data, size_t bytes) {
|
||||
if (bytes > 0) {
|
||||
void* dest = static_cast<uint8_t*>(memory) + sizeof(Header);
|
||||
memcpy(dest, data, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Header>
|
||||
void CopyIntoMemory(void* memory, const CefRefPtr<CefV8Value>& value) {
|
||||
CopyIntoMemory<Header>(memory, value->GetArrayBufferData(),
|
||||
value->GetArrayBufferByteLength());
|
||||
}
|
||||
|
||||
template <class Header>
|
||||
void CopyIntoMemory(void* memory, const CefString& value) {
|
||||
const size_t bytes = GetByteLength(value);
|
||||
CopyIntoMemory<Header>(memory, value.c_str(), bytes);
|
||||
}
|
||||
|
||||
template <class Header>
|
||||
CefString GetStringFromMemory(const void* memory, size_t size) {
|
||||
const size_t bytes = size - sizeof(Header);
|
||||
const size_t string_len = bytes / sizeof(CefString::char_type);
|
||||
const void* string_data =
|
||||
static_cast<const uint8_t*>(memory) + sizeof(Header);
|
||||
const CefString::char_type* src =
|
||||
static_cast<const CefString::char_type*>(string_data);
|
||||
CefString result;
|
||||
result.FromString(src, string_len, /*copy=*/true);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool IsCefString() {
|
||||
return std::is_same_v<std::remove_cv_t<T>, CefString>;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool IsEmpty() {
|
||||
return std::is_same_v<std::remove_cv_t<T>, Empty>;
|
||||
}
|
||||
|
||||
template <class ResponseType>
|
||||
CefRefPtr<CefProcessMessage> BuildBrowserListMsg(const CefString& name,
|
||||
int context_id,
|
||||
int request_id,
|
||||
const ResponseType& response) {
|
||||
auto message = CefProcessMessage::Create(name);
|
||||
CefRefPtr<CefListValue> args = message->GetArgumentList();
|
||||
args->SetInt(kContextId, context_id);
|
||||
args->SetInt(kRequestId, request_id);
|
||||
args->SetBool(kIsSuccess, true);
|
||||
|
||||
if constexpr (IsCefString<ResponseType>()) {
|
||||
args->SetString(kBrowserPayload, response);
|
||||
} else if constexpr (IsEmpty<ResponseType>()) {
|
||||
args->SetNull(kBrowserPayload);
|
||||
} else {
|
||||
args->SetBinary(kBrowserPayload, response);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
class EmptyResponseBuilder final : public BrowserResponseBuilder {
|
||||
public:
|
||||
explicit EmptyResponseBuilder(const std::string& name) : name_(name) {}
|
||||
EmptyResponseBuilder(const EmptyResponseBuilder&) = delete;
|
||||
EmptyResponseBuilder& operator=(const EmptyResponseBuilder&) = delete;
|
||||
|
||||
CefRefPtr<CefProcessMessage> Build(int context_id, int request_id) override {
|
||||
return BuildBrowserListMsg(name_, context_id, request_id, Empty{});
|
||||
}
|
||||
|
||||
private:
|
||||
const CefString name_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(EmptyResponseBuilder);
|
||||
};
|
||||
|
||||
class BinaryResponseBuilder final : public BrowserResponseBuilder {
|
||||
public:
|
||||
BinaryResponseBuilder(const std::string& name, const void* data, size_t size)
|
||||
: name_(name), value_(CefBinaryValue::Create(data, size)) {}
|
||||
BinaryResponseBuilder(const BinaryResponseBuilder&) = delete;
|
||||
BinaryResponseBuilder& operator=(const BinaryResponseBuilder&) = delete;
|
||||
|
||||
CefRefPtr<CefProcessMessage> Build(int context_id, int request_id) override {
|
||||
return BuildBrowserListMsg(name_, context_id, request_id, value_);
|
||||
}
|
||||
|
||||
private:
|
||||
const CefString name_;
|
||||
const CefRefPtr<CefBinaryValue> value_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(BinaryResponseBuilder);
|
||||
};
|
||||
|
||||
class StringResponseBuilder final : public BrowserResponseBuilder {
|
||||
public:
|
||||
StringResponseBuilder(const std::string& name, const CefString& value)
|
||||
: name_(name), value_(value) {}
|
||||
StringResponseBuilder(const StringResponseBuilder&) = delete;
|
||||
StringResponseBuilder& operator=(const StringResponseBuilder&) = delete;
|
||||
|
||||
CefRefPtr<CefProcessMessage> Build(int context_id, int request_id) override {
|
||||
return BuildBrowserListMsg(name_, context_id, request_id, value_);
|
||||
}
|
||||
|
||||
private:
|
||||
const CefString name_;
|
||||
const CefString value_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(StringResponseBuilder);
|
||||
};
|
||||
|
||||
// SharedProcessMessageResponseBuilder
|
||||
class SPMResponseBuilder final : public BrowserResponseBuilder {
|
||||
public:
|
||||
SPMResponseBuilder(const SPMResponseBuilder&) = delete;
|
||||
SPMResponseBuilder& operator=(const SPMResponseBuilder&) = delete;
|
||||
|
||||
static CefRefPtr<BrowserResponseBuilder> Create(const std::string& name,
|
||||
const void* data,
|
||||
size_t size) {
|
||||
const size_t message_size = sizeof(BrowserMsgHeader) + size;
|
||||
auto builder = CefSharedProcessMessageBuilder::Create(name, message_size);
|
||||
if (!builder->IsValid()) {
|
||||
LOG(ERROR) << "Failed to allocate shared memory region of size "
|
||||
<< message_size;
|
||||
return new BinaryResponseBuilder(name, data, size);
|
||||
}
|
||||
|
||||
CopyIntoMemory<BrowserMsgHeader>(builder->Memory(), data, size);
|
||||
return new SPMResponseBuilder(builder, /*is_binary=*/true);
|
||||
}
|
||||
|
||||
static CefRefPtr<BrowserResponseBuilder> Create(const std::string& name,
|
||||
const CefString& value) {
|
||||
const size_t message_size = GetMessageSize<BrowserMsgHeader>(value);
|
||||
auto builder = CefSharedProcessMessageBuilder::Create(name, message_size);
|
||||
if (!builder->IsValid()) {
|
||||
LOG(ERROR) << "Failed to allocate shared memory region of size "
|
||||
<< message_size;
|
||||
return new StringResponseBuilder(name, value);
|
||||
}
|
||||
|
||||
CopyIntoMemory<BrowserMsgHeader>(builder->Memory(), value);
|
||||
return new SPMResponseBuilder(builder, /*is_binary=*/false);
|
||||
}
|
||||
|
||||
CefRefPtr<CefProcessMessage> Build(int context_id, int request_id) override {
|
||||
auto header = static_cast<BrowserMsgHeader*>(builder_->Memory());
|
||||
header->context_id = context_id;
|
||||
header->request_id = request_id;
|
||||
header->is_binary = is_binary_;
|
||||
return builder_->Build();
|
||||
}
|
||||
|
||||
private:
|
||||
explicit SPMResponseBuilder(
|
||||
const CefRefPtr<CefSharedProcessMessageBuilder>& builder,
|
||||
bool is_binary)
|
||||
: builder_(builder), is_binary_(is_binary) {}
|
||||
|
||||
CefRefPtr<CefSharedProcessMessageBuilder> builder_;
|
||||
const bool is_binary_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(SPMResponseBuilder);
|
||||
};
|
||||
|
||||
class EmptyBinaryBuffer final : public CefBinaryBuffer {
|
||||
public:
|
||||
EmptyBinaryBuffer() = default;
|
||||
EmptyBinaryBuffer(const EmptyBinaryBuffer&) = delete;
|
||||
EmptyBinaryBuffer& operator=(const EmptyBinaryBuffer&) = delete;
|
||||
|
||||
const void* GetData() const override { return nullptr; }
|
||||
void* GetData() override { return nullptr; }
|
||||
size_t GetSize() const override { return 0; }
|
||||
|
||||
private:
|
||||
IMPLEMENT_REFCOUNTING(EmptyBinaryBuffer);
|
||||
};
|
||||
|
||||
class BinaryValueBuffer final : public CefBinaryBuffer {
|
||||
public:
|
||||
BinaryValueBuffer(CefRefPtr<CefProcessMessage> message,
|
||||
CefRefPtr<CefBinaryValue> value)
|
||||
: message_(std::move(message)), value_(std::move(value)) {}
|
||||
BinaryValueBuffer(const BinaryValueBuffer&) = delete;
|
||||
BinaryValueBuffer& operator=(const BinaryValueBuffer&) = delete;
|
||||
|
||||
const void* GetData() const override { return value_->GetRawData(); }
|
||||
void* GetData() override {
|
||||
// This is not UB as long as underlying storage is vector<uint8_t>
|
||||
return const_cast<void*>(value_->GetRawData());
|
||||
}
|
||||
size_t GetSize() const override { return value_->GetSize(); }
|
||||
|
||||
private:
|
||||
const CefRefPtr<CefProcessMessage> message_;
|
||||
const CefRefPtr<CefBinaryValue> value_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(BinaryValueBuffer);
|
||||
};
|
||||
|
||||
class SharedMemoryRegionBuffer final : public CefBinaryBuffer {
|
||||
public:
|
||||
SharedMemoryRegionBuffer(const CefRefPtr<CefSharedMemoryRegion>& region,
|
||||
size_t offset)
|
||||
: region_(region),
|
||||
data_(static_cast<uint8_t*>(region->Memory()) + offset),
|
||||
size_(region->Size() - offset) {}
|
||||
SharedMemoryRegionBuffer(const SharedMemoryRegionBuffer&) = delete;
|
||||
SharedMemoryRegionBuffer& operator=(const SharedMemoryRegionBuffer&) = delete;
|
||||
|
||||
const void* GetData() const override { return data_; }
|
||||
void* GetData() override { return data_; }
|
||||
size_t GetSize() const override { return size_; }
|
||||
|
||||
private:
|
||||
const CefRefPtr<CefSharedMemoryRegion> region_;
|
||||
void* const data_;
|
||||
const size_t size_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(SharedMemoryRegionBuffer);
|
||||
};
|
||||
|
||||
template <class RequestType>
|
||||
CefRefPtr<CefProcessMessage> BuildRendererListMsg(const std::string& name,
|
||||
int context_id,
|
||||
int request_id,
|
||||
const RequestType& request,
|
||||
bool persistent) {
|
||||
auto message = CefProcessMessage::Create(name);
|
||||
CefRefPtr<CefListValue> args = message->GetArgumentList();
|
||||
args->SetInt(kContextId, context_id);
|
||||
args->SetInt(kRequestId, request_id);
|
||||
|
||||
if constexpr (IsCefString<RequestType>()) {
|
||||
args->SetString(kRendererPayload, request);
|
||||
} else if constexpr (IsEmpty<RequestType>()) {
|
||||
args->SetNull(kRendererPayload);
|
||||
} else {
|
||||
args->SetBinary(kRendererPayload, request);
|
||||
}
|
||||
|
||||
args->SetBool(kIsPersistent, persistent);
|
||||
return message;
|
||||
}
|
||||
|
||||
template <class RequestType>
|
||||
CefRefPtr<CefProcessMessage> BuildRendererSharedMsg(const std::string& name,
|
||||
int context_id,
|
||||
int request_id,
|
||||
const RequestType& request,
|
||||
bool persistent) {
|
||||
const size_t message_size = GetMessageSize<RendererMsgHeader>(request);
|
||||
auto builder = CefSharedProcessMessageBuilder::Create(name, message_size);
|
||||
if (!builder->IsValid()) {
|
||||
LOG(ERROR) << "Failed to allocate shared memory region of size "
|
||||
<< message_size;
|
||||
return BuildRendererListMsg(name, context_id, request_id,
|
||||
GetListRepresentation(request), persistent);
|
||||
}
|
||||
|
||||
auto header = static_cast<RendererMsgHeader*>(builder->Memory());
|
||||
header->context_id = context_id;
|
||||
header->request_id = request_id;
|
||||
header->is_persistent = persistent;
|
||||
header->is_binary = !IsCefString<RequestType>();
|
||||
|
||||
CopyIntoMemory<RendererMsgHeader>(builder->Memory(), request);
|
||||
|
||||
return builder->Build();
|
||||
}
|
||||
|
||||
CefRefPtr<CefProcessMessage> BuildRendererMsg(size_t threshold,
|
||||
const std::string& name,
|
||||
int context_id,
|
||||
int request_id,
|
||||
const CefString& request,
|
||||
bool persistent) {
|
||||
if (GetByteLength(request) < threshold) {
|
||||
return BuildRendererListMsg(name, context_id, request_id, request,
|
||||
persistent);
|
||||
}
|
||||
|
||||
return BuildRendererSharedMsg(name, context_id, request_id, request,
|
||||
persistent);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CefRefPtr<BrowserResponseBuilder> CreateBrowserResponseBuilder(
|
||||
size_t threshold,
|
||||
const std::string& name,
|
||||
const CefString& response) {
|
||||
if (GetByteLength(response) < threshold) {
|
||||
return new StringResponseBuilder(name, response);
|
||||
}
|
||||
|
||||
return SPMResponseBuilder::Create(name, response);
|
||||
}
|
||||
|
||||
CefRefPtr<BrowserResponseBuilder> CreateBrowserResponseBuilder(
|
||||
size_t threshold,
|
||||
const std::string& name,
|
||||
const void* data,
|
||||
size_t size) {
|
||||
if (size == 0) {
|
||||
return new EmptyResponseBuilder(name);
|
||||
}
|
||||
|
||||
if (size < threshold) {
|
||||
return new BinaryResponseBuilder(name, data, size);
|
||||
}
|
||||
|
||||
return SPMResponseBuilder::Create(name, data, size);
|
||||
}
|
||||
|
||||
CefRefPtr<CefProcessMessage> BuildRendererMsg(
|
||||
size_t threshold,
|
||||
const std::string& name,
|
||||
int context_id,
|
||||
int request_id,
|
||||
const CefRefPtr<CefV8Value>& request,
|
||||
bool persistent) {
|
||||
if (request->IsString()) {
|
||||
return BuildRendererMsg(threshold, name, context_id, request_id,
|
||||
request->GetStringValue(), persistent);
|
||||
}
|
||||
|
||||
const auto size = request->GetArrayBufferByteLength();
|
||||
if (size == 0) {
|
||||
return BuildRendererListMsg(name, context_id, request_id, Empty{},
|
||||
persistent);
|
||||
}
|
||||
|
||||
if (size < threshold) {
|
||||
return BuildRendererListMsg(name, context_id, request_id,
|
||||
GetListRepresentation(request), persistent);
|
||||
}
|
||||
|
||||
return BuildRendererSharedMsg(name, context_id, request_id, request,
|
||||
persistent);
|
||||
}
|
||||
|
||||
BrowserMessage ParseBrowserMessage(
|
||||
const CefRefPtr<CefProcessMessage>& message) {
|
||||
if (auto args = message->GetArgumentList()) {
|
||||
DCHECK_GT(args->GetSize(), 3U);
|
||||
|
||||
const int context_id = args->GetInt(kContextId);
|
||||
const int request_id = args->GetInt(kRequestId);
|
||||
const bool is_success = args->GetBool(kIsSuccess);
|
||||
|
||||
if (is_success) {
|
||||
DCHECK_EQ(args->GetSize(), 4U);
|
||||
const auto payload_type = args->GetType(kBrowserPayload);
|
||||
if (payload_type == CefValueType::VTYPE_STRING) {
|
||||
return {context_id, request_id, is_success, kNoError,
|
||||
args->GetString(kBrowserPayload)};
|
||||
}
|
||||
|
||||
if (payload_type == CefValueType::VTYPE_BINARY) {
|
||||
return {
|
||||
context_id, request_id, is_success, kNoError,
|
||||
new BinaryValueBuffer(message, args->GetBinary(kBrowserPayload))};
|
||||
}
|
||||
|
||||
DCHECK(payload_type == CefValueType::VTYPE_NULL);
|
||||
return {context_id, request_id, is_success, kNoError,
|
||||
new EmptyBinaryBuffer()};
|
||||
}
|
||||
|
||||
DCHECK_EQ(args->GetSize(), 5U);
|
||||
return {context_id, request_id, is_success, args->GetInt(3),
|
||||
args->GetString(4)};
|
||||
}
|
||||
|
||||
const auto region = message->GetSharedMemoryRegion();
|
||||
if (region && region->IsValid()) {
|
||||
DCHECK_GE(region->Size(), sizeof(BrowserMsgHeader));
|
||||
auto header = static_cast<const BrowserMsgHeader*>(region->Memory());
|
||||
|
||||
if (header->is_binary) {
|
||||
return {header->context_id, header->request_id, true, kNoError,
|
||||
new SharedMemoryRegionBuffer(region, sizeof(BrowserMsgHeader))};
|
||||
}
|
||||
return {header->context_id, header->request_id, true, kNoError,
|
||||
GetStringFromMemory<BrowserMsgHeader>(region->Memory(),
|
||||
region->Size())};
|
||||
}
|
||||
|
||||
NOTREACHED();
|
||||
return {};
|
||||
}
|
||||
|
||||
RendererMessage ParseRendererMessage(
|
||||
const CefRefPtr<CefProcessMessage>& message) {
|
||||
if (auto args = message->GetArgumentList()) {
|
||||
DCHECK_EQ(args->GetSize(), 4U);
|
||||
|
||||
const int context_id = args->GetInt(kContextId);
|
||||
const int request_id = args->GetInt(kRequestId);
|
||||
const auto payload_type = args->GetType(kRendererPayload);
|
||||
const bool persistent = args->GetBool(kIsPersistent);
|
||||
|
||||
if (payload_type == CefValueType::VTYPE_STRING) {
|
||||
return {context_id, request_id, persistent,
|
||||
args->GetString(kRendererPayload)};
|
||||
}
|
||||
|
||||
if (payload_type == CefValueType::VTYPE_BINARY) {
|
||||
return {
|
||||
context_id, request_id, persistent,
|
||||
new BinaryValueBuffer(message, args->GetBinary(kRendererPayload))};
|
||||
}
|
||||
|
||||
DCHECK(payload_type == CefValueType::VTYPE_NULL);
|
||||
return {context_id, request_id, persistent, new EmptyBinaryBuffer()};
|
||||
}
|
||||
|
||||
const auto region = message->GetSharedMemoryRegion();
|
||||
if (region && region->IsValid()) {
|
||||
DCHECK_GE(region->Size(), sizeof(RendererMsgHeader));
|
||||
auto header = static_cast<const RendererMsgHeader*>(region->Memory());
|
||||
|
||||
if (header->is_binary) {
|
||||
return {header->context_id, header->request_id, header->is_persistent,
|
||||
new SharedMemoryRegionBuffer(region, sizeof(RendererMsgHeader))};
|
||||
}
|
||||
|
||||
return {
|
||||
header->context_id,
|
||||
header->request_id,
|
||||
header->is_persistent,
|
||||
GetStringFromMemory<RendererMsgHeader>(region->Memory(),
|
||||
region->Size()),
|
||||
};
|
||||
}
|
||||
|
||||
NOTREACHED();
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace cef_message_router_utils
|
92
libcef_dll/wrapper/cef_message_router_utils.h
Normal file
92
libcef_dll/wrapper/cef_message_router_utils.h
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2023 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_DLL_WRAPPER_CEF_MESSAGE_ROUTER_UTILS_H_
|
||||
#define CEF_LIBCEF_DLL_WRAPPER_CEF_MESSAGE_ROUTER_UTILS_H_
|
||||
#pragma once
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include "include/wrapper/cef_message_router.h"
|
||||
|
||||
namespace cef_message_router_utils {
|
||||
|
||||
///
|
||||
/// This class handles the task of copying user data, such as CefString or
|
||||
/// binary values like (void*, size_t), directly to an appropriate buffer based
|
||||
/// on the user data type and size.
|
||||
///
|
||||
/// There are four specializations of this abstract class. The appropriate
|
||||
/// specialization is chosen by the `CreateBrowserResponseBuilder` function,
|
||||
/// based on the provided data type and size. For instance, for a "short"
|
||||
/// CefString, a StringResponseBuilder specialization is used, and for an empty
|
||||
/// binary value - EmptyResponseBuilder.
|
||||
///
|
||||
class BrowserResponseBuilder : public CefBaseRefCounted {
|
||||
public:
|
||||
///
|
||||
/// Creates a new CefProcessMessage from the data provided to the builder.
|
||||
/// Returns nullptr for invalid instances. Invalidates this builder instance.
|
||||
///
|
||||
virtual CefRefPtr<CefProcessMessage> Build(int context_id,
|
||||
int request_id) = 0;
|
||||
};
|
||||
|
||||
struct BrowserMessage {
|
||||
int context_id;
|
||||
int request_id;
|
||||
bool is_success;
|
||||
int error_code;
|
||||
std::variant<CefString, CefRefPtr<CefBinaryBuffer>> payload;
|
||||
};
|
||||
|
||||
struct RendererMessage {
|
||||
int context_id;
|
||||
int request_id;
|
||||
bool is_persistent;
|
||||
std::variant<CefString, CefRefPtr<const CefBinaryBuffer>> payload;
|
||||
};
|
||||
|
||||
class BinaryValueABRCallback final : public CefV8ArrayBufferReleaseCallback {
|
||||
public:
|
||||
explicit BinaryValueABRCallback(CefRefPtr<CefBinaryBuffer> value)
|
||||
: value_(std::move(value)) {}
|
||||
BinaryValueABRCallback(const BinaryValueABRCallback&) = delete;
|
||||
BinaryValueABRCallback& operator=(const BinaryValueABRCallback&) = delete;
|
||||
|
||||
void ReleaseBuffer(void* buffer) override {}
|
||||
|
||||
private:
|
||||
const CefRefPtr<CefBinaryBuffer> value_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(BinaryValueABRCallback);
|
||||
};
|
||||
|
||||
CefRefPtr<BrowserResponseBuilder> CreateBrowserResponseBuilder(
|
||||
size_t threshold,
|
||||
const std::string& name,
|
||||
const CefString& response);
|
||||
|
||||
CefRefPtr<BrowserResponseBuilder> CreateBrowserResponseBuilder(
|
||||
size_t threshold,
|
||||
const std::string& name,
|
||||
const void* data,
|
||||
size_t size);
|
||||
|
||||
CefRefPtr<CefProcessMessage> BuildRendererMsg(
|
||||
size_t threshold,
|
||||
const std::string& name,
|
||||
int context_id,
|
||||
int request_id,
|
||||
const CefRefPtr<CefV8Value>& request,
|
||||
bool persistent);
|
||||
|
||||
BrowserMessage ParseBrowserMessage(const CefRefPtr<CefProcessMessage>& message);
|
||||
|
||||
RendererMessage ParseRendererMessage(
|
||||
const CefRefPtr<CefProcessMessage>& message);
|
||||
|
||||
} // namespace cef_message_router_utils
|
||||
|
||||
#endif // CEF_LIBCEF_DLL_WRAPPER_CEF_MESSAGE_ROUTER_UTILS_H_
|
60
tests/cefclient/browser/binary_transfer_test.cc
Normal file
60
tests/cefclient/browser/binary_transfer_test.cc
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2023 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/browser/binary_transfer_test.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
const char kTestUrlPath[] = "/binary_transfer";
|
||||
|
||||
// Handle messages in the browser process.
|
||||
class Handler final : public CefMessageRouterBrowserSide::Handler {
|
||||
public:
|
||||
// Called due to cefQuery execution in binary_transfer.html.
|
||||
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64_t query_id,
|
||||
const CefString& request,
|
||||
bool persistent,
|
||||
CefRefPtr<Callback> callback) override {
|
||||
// Only handle messages from the test URL.
|
||||
const std::string& url = frame->GetURL();
|
||||
if (!client::test_runner::IsTestURL(url, kTestUrlPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
callback->Success(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called due to cefQuery execution in binary_transfer.html.
|
||||
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64_t query_id,
|
||||
CefRefPtr<const CefBinaryBuffer> request,
|
||||
bool persistent,
|
||||
CefRefPtr<Callback> callback) override {
|
||||
// Only handle messages from the test URL.
|
||||
const std::string& url = frame->GetURL();
|
||||
if (!client::test_runner::IsTestURL(url, kTestUrlPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
callback->Success(request->GetData(), request->GetSize());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace client::binary_transfer_test {
|
||||
|
||||
void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
|
||||
handlers.insert(new Handler());
|
||||
}
|
||||
|
||||
} // namespace client::binary_transfer_test
|
17
tests/cefclient/browser/binary_transfer_test.h
Normal file
17
tests/cefclient/browser/binary_transfer_test.h
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2023 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_BROWSER_BINARY_TRANSFER_TEST_H_
|
||||
#define CEF_TESTS_CEFCLIENT_BROWSER_BINARY_TRANSFER_TEST_H_
|
||||
#pragma once
|
||||
|
||||
#include "tests/cefclient/browser/test_runner.h"
|
||||
|
||||
namespace client::binary_transfer_test {
|
||||
|
||||
void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers);
|
||||
|
||||
} // namespace client::binary_transfer_test
|
||||
|
||||
#endif // CEF_TESTS_CEFCLIENT_BROWSER_BINARY_TRANSFER_TEST_H_
|
@ -44,31 +44,32 @@
|
||||
#define ID_TESTS_UNMUTE_AUDIO 32717
|
||||
#define ID_TESTS_LAST 32717
|
||||
#define IDC_STATIC -1
|
||||
#define IDS_BINDING_HTML 1000
|
||||
#define IDS_DIALOGS_HTML 1001
|
||||
#define IDS_DRAGGABLE_HTML 1002
|
||||
#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_BINARY_TRANSFER_HTML 1000
|
||||
#define IDS_BINDING_HTML 1001
|
||||
#define IDS_DIALOGS_HTML 1002
|
||||
#define IDS_DRAGGABLE_HTML 1003
|
||||
#define IDS_IPC_PERFORMANCE_HTML 1004
|
||||
#define IDS_LOCALSTORAGE_HTML 1005
|
||||
#define IDS_LOGO_PNG 1006
|
||||
#define IDS_MEDIA_ROUTER_HTML 1007
|
||||
#define IDS_MENU_ICON_1X_PNG 1008
|
||||
#define IDS_MENU_ICON_2X_PNG 1009
|
||||
#define IDS_OSRTEST_HTML 1010
|
||||
#define IDS_OTHER_TESTS_HTML 1011
|
||||
#define IDS_PDF_HTML 1012
|
||||
#define IDS_PDF_PDF 1013
|
||||
#define IDS_PERFORMANCE_HTML 1014
|
||||
#define IDS_PERFORMANCE2_HTML 1015
|
||||
#define IDS_PREFERENCES_HTML 1016
|
||||
#define IDS_RESPONSE_FILTER_HTML 1017
|
||||
#define IDS_SERVER_HTML 1018
|
||||
#define IDS_TRANSPARENCY_HTML 1019
|
||||
#define IDS_URLREQUEST_HTML 1020
|
||||
#define IDS_WEBSOCKET_HTML 1021
|
||||
#define IDS_WINDOW_HTML 1022
|
||||
#define IDS_WINDOW_ICON_1X_PNG 1023
|
||||
#define IDS_WINDOW_ICON_2X_PNG 1024
|
||||
#define IDS_XMLHTTPREQUEST_HTML 1025
|
||||
|
||||
#define IDS_EXTENSIONS_SET_PAGE_COLOR_ICON_PNG 1030
|
||||
#define IDS_EXTENSIONS_SET_PAGE_COLOR_MANIFEST_JSON 1031
|
||||
|
@ -13,7 +13,8 @@ int GetResourceId(const char* resource_name) {
|
||||
static struct _resource_map {
|
||||
const char* name;
|
||||
int id;
|
||||
} resource_map[] = {{"binding.html", IDS_BINDING_HTML},
|
||||
} resource_map[] = {{"binary_transfer.html", IDS_BINARY_TRANSFER_HTML},
|
||||
{"binding.html", IDS_BINDING_HTML},
|
||||
{"dialogs.html", IDS_DIALOGS_HTML},
|
||||
{"draggable.html", IDS_DRAGGABLE_HTML},
|
||||
{"extensions/set_page_color/icon.png",
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "include/views/cef_browser_view.h"
|
||||
#include "include/wrapper/cef_closure_task.h"
|
||||
#include "include/wrapper/cef_stream_resource_handler.h"
|
||||
#include "tests/cefclient/browser/binary_transfer_test.h"
|
||||
#include "tests/cefclient/browser/binding_test.h"
|
||||
#include "tests/cefclient/browser/client_handler.h"
|
||||
#include "tests/cefclient/browser/dialog_test.h"
|
||||
@ -842,6 +843,9 @@ bool IsTestURL(const std::string& url, const std::string& path) {
|
||||
void CreateMessageHandlers(MessageHandlerSet& handlers) {
|
||||
handlers.insert(new PromptHandler);
|
||||
|
||||
// Create the binary trasfer test handlers.
|
||||
binary_transfer_test::CreateMessageHandlers(handlers);
|
||||
|
||||
// Create the binding test handlers.
|
||||
binding_test::CreateMessageHandlers(handlers);
|
||||
|
||||
|
498
tests/cefclient/resources/binary_transfer.html
Normal file
498
tests/cefclient/resources/binary_transfer.html
Normal file
@ -0,0 +1,498 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Binary vs String Transfer Benchmark</title>
|
||||
<script src="https://cdn.plot.ly/plotly-2.26.0.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: Tahoma, Serif;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.info {
|
||||
font-size: 12pt;
|
||||
}
|
||||
.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>Binary vs String Transfer Benchmark</h1>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<p class="info">
|
||||
This benchmark evaluates the message transfer speed between the
|
||||
renderer process and the browser process. <br />It compares the
|
||||
performance of binary and string message transfer.
|
||||
</p>
|
||||
<p class="info">
|
||||
<b>Note:</b> There is no progress indication of the tests because it
|
||||
significantly influences measurements. <br />It usually takes 30
|
||||
seconds (for 300 samples) to complete the tests.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Samples:
|
||||
<input
|
||||
id="sSamples"
|
||||
type="text"
|
||||
value="300"
|
||||
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%">
|
||||
String Round Trip Avg, ms
|
||||
</td>
|
||||
<td class="center" style="width: 8%">
|
||||
Binary Round Trip Avg, ms
|
||||
</td>
|
||||
<td class="center" style="width: 10%">Relative Trip Difference</td>
|
||||
<td class="center" style="width: 8%">String Speed, MB/s</td>
|
||||
<td class="center" style="width: 8%">Binary Speed, MB/s</td>
|
||||
<td class="center" style="width: 10%">Relative Speed Difference</td>
|
||||
<td class="center" style="width: 8%">String Standard Deviation</td>
|
||||
<td class="center" style="width: 8%">Binary 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 nextTestSuite(testIndex) {
|
||||
const nextTestIndex = testIndex + 1;
|
||||
setTimeout(execTestSuite, 0, nextTestIndex);
|
||||
}
|
||||
|
||||
function generateRandomString(size) {
|
||||
// Symbols that will be encoded as two bytes in UTF-8
|
||||
// so we compare transfer of the same amount of bytes
|
||||
const characters =
|
||||
"АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя";
|
||||
let randomString = "";
|
||||
for (let i = 0; i < size; i++) {
|
||||
const randomIndex = Math.floor(Math.random() * characters.length);
|
||||
randomString += characters.charAt(randomIndex);
|
||||
}
|
||||
return randomString;
|
||||
}
|
||||
|
||||
function generateRandomArrayBuffer(size) {
|
||||
const buffer = new ArrayBuffer(size);
|
||||
const uint8Array = new Uint8Array(buffer);
|
||||
for (let i = 0; i < uint8Array.length; i++) {
|
||||
uint8Array[i] = Math.floor(Math.random() * 256);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
function reportError(errorCode, errorMessage) {
|
||||
console.error(`ErrorCode:${errorCode} Message:${errorMessage}`);
|
||||
}
|
||||
|
||||
function sendString(size, testIndex) {
|
||||
const request = generateRandomString(size);
|
||||
const startTime = performance.now();
|
||||
const onSuccess = (response) => {
|
||||
const roundTrip = performance.now() - startTime;
|
||||
const test = tests[testIndex];
|
||||
test.totalRoundTrip += roundTrip;
|
||||
test.sample++;
|
||||
box_plot_test_data[testIndex].x.push(roundTrip);
|
||||
setTimeout(execTest, 0, testIndex);
|
||||
};
|
||||
|
||||
window.cefQuery({
|
||||
request: request,
|
||||
onSuccess: onSuccess,
|
||||
onFailure: reportError,
|
||||
});
|
||||
}
|
||||
|
||||
function sendArrayBuffer(size, testIndex) {
|
||||
const request = generateRandomArrayBuffer(size);
|
||||
const startTime = performance.now();
|
||||
const onSuccess = (response) => {
|
||||
const roundTrip = performance.now() - startTime;
|
||||
const test = tests[testIndex];
|
||||
test.totalRoundTrip += roundTrip;
|
||||
test.sample++;
|
||||
box_plot_test_data[testIndex].x.push(roundTrip);
|
||||
setTimeout(execTest, 0, testIndex);
|
||||
};
|
||||
|
||||
window.cefQuery({
|
||||
request: request,
|
||||
onSuccess: onSuccess,
|
||||
onFailure: reportError,
|
||||
});
|
||||
}
|
||||
|
||||
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 nextTestSuite(testIndex);
|
||||
}
|
||||
test.func(test.length, test.index);
|
||||
}
|
||||
|
||||
function column(prepared, value) {
|
||||
return (
|
||||
"<td class='right'>" + (!prepared ? "-" : value.toFixed(3)) + "</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.avgRoundTripBin),
|
||||
relativeDiffColumn(test.prepared, test.relativeTripDiff, false),
|
||||
column(test.prepared, test.speed),
|
||||
column(test.prepared, test.speedBinary),
|
||||
relativeDiffColumn(test.prepared, test.relativeSpeedDiff, true),
|
||||
"<td class='right'>",
|
||||
!test.prepared || test.stdDeviation == null
|
||||
? "-"
|
||||
: test.stdDeviation.toFixed(3),
|
||||
"</td>",
|
||||
"<td class='right'>",
|
||||
!test.prepared || test.stdDeviationBinary == null
|
||||
? "-"
|
||||
: test.stdDeviationBinary.toFixed(3),
|
||||
"</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 relativeDiff(left, right) {
|
||||
if (right != 0) {
|
||||
return ((left - right) / right) * 100;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function buildTestResults(tests) {
|
||||
testResults = [];
|
||||
|
||||
let oldRoundTrip = {
|
||||
x: [],
|
||||
y: [],
|
||||
type: "scatter",
|
||||
name: "String",
|
||||
};
|
||||
|
||||
let newRoundTrip = {
|
||||
x: [],
|
||||
y: [],
|
||||
type: "scatter",
|
||||
name: "Binary",
|
||||
};
|
||||
|
||||
for (let i = 0; i < tests.length / 2; i++) {
|
||||
const index = testResults.length;
|
||||
|
||||
// Tests are in pairs - String and Binary
|
||||
const test = tests[i * 2];
|
||||
const testBin = tests[i * 2 + 1];
|
||||
|
||||
const avgRoundTrip = test.totalRoundTrip / test.totalSamples;
|
||||
const avgRoundTripBin = testBin.totalRoundTrip / testBin.totalSamples;
|
||||
const relativeTripDiff = relativeDiff(avgRoundTripBin, avgRoundTrip);
|
||||
|
||||
// In MB/s
|
||||
const speed = test.byteSize / (avgRoundTrip * 1000);
|
||||
const speedBinary = testBin.byteSize / (avgRoundTripBin * 1000);
|
||||
const relativeSpeedDiff = relativeDiff(speedBinary, speed);
|
||||
|
||||
const stdDeviation = getStandardDeviation(
|
||||
box_plot_test_data[test.index].x,
|
||||
avgRoundTrip
|
||||
);
|
||||
const stdDeviationBinary = getStandardDeviation(
|
||||
box_plot_test_data[testBin.index].x,
|
||||
avgRoundTripBin
|
||||
);
|
||||
|
||||
testResults.push({
|
||||
name: humanFileSize(test.byteSize),
|
||||
index: index,
|
||||
prepared: true,
|
||||
avgRoundTrip: avgRoundTrip,
|
||||
avgRoundTripBin: avgRoundTripBin,
|
||||
relativeTripDiff: relativeTripDiff,
|
||||
speed: speed,
|
||||
speedBinary: speedBinary,
|
||||
relativeSpeedDiff: relativeSpeedDiff,
|
||||
stdDeviation: stdDeviation,
|
||||
stdDeviationBinary: stdDeviationBinary,
|
||||
});
|
||||
|
||||
oldRoundTrip.x.push(test.byteSize);
|
||||
newRoundTrip.x.push(test.byteSize);
|
||||
oldRoundTrip.y.push(avgRoundTrip);
|
||||
newRoundTrip.y.push(avgRoundTripBin);
|
||||
}
|
||||
|
||||
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.byteSize),
|
||||
index: index,
|
||||
prepared: false,
|
||||
});
|
||||
}
|
||||
return testResults;
|
||||
}
|
||||
|
||||
function resetTestsResults(totalSamples) {
|
||||
if (totalSamples <= 0) totalSamples = 1;
|
||||
|
||||
// Reset tests results
|
||||
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, byteSize, length, testFunc) {
|
||||
const testIndex = tests.length;
|
||||
test = {
|
||||
name: name,
|
||||
byteSize: byteSize,
|
||||
length: length,
|
||||
index: testIndex,
|
||||
sample: 0,
|
||||
totalRoundTrip: 0,
|
||||
func: testFunc,
|
||||
};
|
||||
tests.push(test);
|
||||
|
||||
box_plot_test_data.push({
|
||||
x: [],
|
||||
type: "box",
|
||||
boxpoints: "all",
|
||||
name: name,
|
||||
jitter: 0.3,
|
||||
pointpos: -1.8,
|
||||
});
|
||||
}
|
||||
|
||||
function execTestSuite(testIndex) {
|
||||
if (testIndex < tests.length) {
|
||||
execTest(testIndex);
|
||||
} else {
|
||||
testsRunFinished();
|
||||
}
|
||||
}
|
||||
|
||||
function startTests() {
|
||||
// Let the updated table render before starting the tests
|
||||
setTimeout(execTestSuite, 200, 0);
|
||||
}
|
||||
|
||||
function execQueuedTests(totalSamples) {
|
||||
resetTestsResults(totalSamples);
|
||||
startTests();
|
||||
}
|
||||
|
||||
function setSettingsState(disabled) {
|
||||
document.getElementById("sSamples").disabled = disabled;
|
||||
document.getElementById("sRun").disabled = disabled;
|
||||
}
|
||||
|
||||
function testsRunFinished() {
|
||||
testResults = buildTestResults(tests);
|
||||
testResults.forEach((result) => displayResult(result));
|
||||
|
||||
const round_trip_layout = {
|
||||
title: "Average round trip, μs (Smaller Better)",
|
||||
};
|
||||
Plotly.newPlot(
|
||||
"round_trip_avg_chart",
|
||||
round_trip_avg_plot_data,
|
||||
round_trip_layout
|
||||
);
|
||||
|
||||
const box_plot_layout = {
|
||||
title: "Round Trip Time, μs",
|
||||
};
|
||||
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);
|
||||
};
|
||||
|
||||
const totalSamples = parseInt(document.getElementById("sSamples").value);
|
||||
queueTest("Empty String", 0, 0, sendString);
|
||||
queueTest("Empty Binary", 0, 0, sendArrayBuffer);
|
||||
for (let byteSize = 8; byteSize <= 512 * 1024; byteSize *= 2) {
|
||||
// Byte size of a string is twice its length because of UTF-16 encoding
|
||||
const stringLen = byteSize / 2;
|
||||
queueTest(
|
||||
humanFileSize(byteSize) + " String",
|
||||
byteSize,
|
||||
stringLen,
|
||||
sendString
|
||||
);
|
||||
queueTest(
|
||||
humanFileSize(byteSize) + " Binary",
|
||||
byteSize,
|
||||
byteSize,
|
||||
sendArrayBuffer
|
||||
);
|
||||
}
|
||||
resetTestsResults(totalSamples);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -9,6 +9,7 @@
|
||||
<li><a href="http://webkit.org/blog-files/3d-transforms/poster-circle.html">Accelerated Layers</a></li>
|
||||
<li><a href="https://jigsaw.w3.org/HTTP/Basic/">Authentication (Basic)</a> - credentials returned via GetAuthCredentials</li>
|
||||
<li><a href="https://jigsaw.w3.org/HTTP/Digest/">Authentication (Digest)</a> - credentials returned via GetAuthCredentials</li>
|
||||
<li><a href="binary_transfer">Binary vs String Transfer Benchmark</a></li>
|
||||
<li><a href="http://html5advent2011.digitpaint.nl/3/index.html">Cursors</a></li>
|
||||
<li><a href="dialogs">Dialogs</a></li>
|
||||
<li><a href="http://html5demos.com/drag">Drag & Drop</a></li>
|
||||
|
@ -29,6 +29,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
// Binary
|
||||
//
|
||||
|
||||
IDS_BINARY_TRANSFER_HTML BINARY "..\\binary_transfer.html"
|
||||
IDS_BINDING_HTML BINARY "..\\binding.html"
|
||||
IDS_DIALOGS_HTML BINARY "..\\dialogs.html"
|
||||
IDS_DRAGGABLE_HTML BINARY "..\\draggable.html"
|
||||
@ -228,4 +229,3 @@ END
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
|
136
tests/ceftests/message_router_binary_unittest.cc
Normal file
136
tests/ceftests/message_router_binary_unittest.cc
Normal file
@ -0,0 +1,136 @@
|
||||
// Copyright (c) 2023 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 size_t kMsgSizeThresholdInBytes = 16000;
|
||||
|
||||
class BinaryTestHandler final : public SingleLoadTestHandler {
|
||||
public:
|
||||
explicit BinaryTestHandler(size_t message_size)
|
||||
: message_size_(message_size) {}
|
||||
|
||||
std::string GetMainHTML() override {
|
||||
return "<html><body><script>\n"
|
||||
"function generateRandomArrayBuffer(size) {\n"
|
||||
" const buffer = new ArrayBuffer(size);\n"
|
||||
" const uint8Array = new Uint8Array(buffer);\n"
|
||||
" for (let i = 0; i < uint8Array.length; i++) {\n"
|
||||
" uint8Array[i] = Math.floor(Math.random() * 256);\n"
|
||||
" }\n"
|
||||
" return buffer;\n"
|
||||
"}\n"
|
||||
"function isEqualArrayBuffers(left, right) {\n"
|
||||
" if (left.byteLength !== right.byteLength) { return false; }\n"
|
||||
" const leftView = new DataView(left);\n"
|
||||
" const rightView = new DataView(right);\n"
|
||||
" for (let i = 0; i < left.byteLength; i++) {\n"
|
||||
" if (leftView.getUint8(i) !== rightView.getUint8(i)) {\n"
|
||||
" return false;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" return true;\n"
|
||||
"}\n"
|
||||
"const request = generateRandomArrayBuffer(" +
|
||||
std::to_string(message_size_) +
|
||||
")\n"
|
||||
"window." +
|
||||
std::string(kJSQueryFunc) +
|
||||
"({\n request: request,\n"
|
||||
" persistent: false,\n"
|
||||
" onSuccess: (response) => {\n"
|
||||
" if (!isEqualArrayBuffers(request, response)) {\n"
|
||||
" window.mrtNotify('error-ArrayBuffersDiffer');\n"
|
||||
" } else {\n"
|
||||
" window.mrtNotify('success');\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" onFailure: (errorCode, errorMessage) => {\n"
|
||||
" window.mrtNotify('error-onFailureCalled');\n"
|
||||
" }\n"
|
||||
"});\n</script></body></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();
|
||||
|
||||
EXPECT_EQ("success", message);
|
||||
|
||||
DestroyTest();
|
||||
}
|
||||
|
||||
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64_t query_id,
|
||||
CefRefPtr<const CefBinaryBuffer> request,
|
||||
bool persistent,
|
||||
CefRefPtr<Callback> callback) override {
|
||||
AssertMainBrowser(browser);
|
||||
AssertMainFrame(frame);
|
||||
EXPECT_NE(0, query_id);
|
||||
EXPECT_FALSE(persistent);
|
||||
EXPECT_EQ(message_size_, request->GetSize());
|
||||
|
||||
got_on_query_.yes();
|
||||
|
||||
callback->Success(request->GetData(), request->GetSize());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DestroyTest() override {
|
||||
EXPECT_TRUE(got_notify_);
|
||||
EXPECT_TRUE(got_on_query_);
|
||||
|
||||
TestHandler::DestroyTest();
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t message_size_;
|
||||
TrackCallback got_on_query_;
|
||||
TrackCallback got_notify_;
|
||||
};
|
||||
|
||||
using TestHandlerPtr = CefRefPtr<BinaryTestHandler>;
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(MessageRouterTest, BinaryMessageEmpty) {
|
||||
const auto message_size = 0;
|
||||
TestHandlerPtr handler = new BinaryTestHandler(message_size);
|
||||
handler->SetMessageSizeThreshold(kMsgSizeThresholdInBytes);
|
||||
|
||||
handler->ExecuteTest();
|
||||
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
|
||||
TEST(MessageRouterTest, BinaryMessageUnderThresholdSize) {
|
||||
const auto under_threshold = kMsgSizeThresholdInBytes - 1;
|
||||
TestHandlerPtr handler = new BinaryTestHandler(under_threshold);
|
||||
handler->SetMessageSizeThreshold(kMsgSizeThresholdInBytes);
|
||||
|
||||
handler->ExecuteTest();
|
||||
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
|
||||
TEST(MessageRouterTest, BinaryMessageOverThresholdSize) {
|
||||
const auto over_threshold = kMsgSizeThresholdInBytes + 1;
|
||||
TestHandlerPtr handler = new BinaryTestHandler(over_threshold);
|
||||
handler->SetMessageSizeThreshold(kMsgSizeThresholdInBytes);
|
||||
|
||||
handler->ExecuteTest();
|
||||
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
@ -19,8 +19,18 @@ const char kMultiQueryError[] = "error";
|
||||
const char kMultiQueryErrorMessage[] = "errormsg";
|
||||
const int kMultiQueryPersistentResponseCount = 5;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool IsCefString() {
|
||||
return std::is_same_v<std::remove_cv_t<T>, CefString>;
|
||||
}
|
||||
|
||||
enum class TransferType {
|
||||
STRING,
|
||||
BINARY,
|
||||
};
|
||||
|
||||
// Generates HTML and verifies results for multiple simultanious queries.
|
||||
class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
|
||||
class MultiQueryManager {
|
||||
public:
|
||||
enum TestType {
|
||||
// Initiates a non-persistent query with a successful response.
|
||||
@ -70,10 +80,12 @@ class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
|
||||
|
||||
MultiQueryManager(const std::string& label,
|
||||
bool synchronous,
|
||||
int id_offset = 0)
|
||||
int id_offset = 0,
|
||||
TransferType transfer_type = TransferType::STRING)
|
||||
: label_(label),
|
||||
synchronous_(synchronous),
|
||||
id_offset_(id_offset),
|
||||
transfer_type_(transfer_type),
|
||||
finalized_(false),
|
||||
running_(false),
|
||||
manual_total_(0),
|
||||
@ -83,8 +95,6 @@ class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
|
||||
will_cancel_by_removing_handler_(false),
|
||||
weak_ptr_factory_(this) {}
|
||||
|
||||
virtual ~MultiQueryManager() {}
|
||||
|
||||
std::string label() const { return label_; }
|
||||
|
||||
void AddObserver(Observer* observer) {
|
||||
@ -282,12 +292,13 @@ class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
|
||||
}
|
||||
}
|
||||
|
||||
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
||||
template <class RequestType>
|
||||
bool OnQueryImpl(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64_t query_id,
|
||||
const CefString& request,
|
||||
const RequestType& request,
|
||||
bool persistent,
|
||||
CefRefPtr<Callback> callback) override {
|
||||
CefRefPtr<CefMessageRouterBrowserSide::Callback> callback) {
|
||||
EXPECT_TRUE(finalized_);
|
||||
EXPECT_UI_THREAD();
|
||||
|
||||
@ -328,13 +339,26 @@ class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
|
||||
|
||||
if (query.type == SUCCESS) {
|
||||
// Send the single success response.
|
||||
callback->Success(GetIDString(kMultiQueryResponse, index));
|
||||
if constexpr (IsCefString<RequestType>()) {
|
||||
const auto response = GetIDString(kMultiQueryResponse, index);
|
||||
callback->Success(response);
|
||||
} else {
|
||||
const auto response = GetIDBinary(kMultiQueryResponse, index);
|
||||
callback->Success(response.data(), response.size());
|
||||
}
|
||||
} else if (IsPersistent(query.type)) {
|
||||
// Send the required number of successful responses.
|
||||
const std::string& response = GetIDString(kMultiQueryResponse, index);
|
||||
if constexpr (IsCefString<RequestType>()) {
|
||||
const auto response = GetIDString(kMultiQueryResponse, index);
|
||||
for (int i = 0; i < kMultiQueryPersistentResponseCount; ++i) {
|
||||
callback->Success(response);
|
||||
}
|
||||
} else {
|
||||
const auto response = GetIDBinary(kMultiQueryResponse, index);
|
||||
for (int i = 0; i < kMultiQueryPersistentResponseCount; ++i) {
|
||||
callback->Success(response.data(), response.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (WillFail(query.type)) {
|
||||
@ -359,7 +383,7 @@ class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
|
||||
|
||||
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64_t query_id) override {
|
||||
int64_t query_id) {
|
||||
EXPECT_TRUE(finalized_);
|
||||
EXPECT_UI_THREAD();
|
||||
|
||||
@ -495,7 +519,7 @@ class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
|
||||
|
||||
// Used when a query is canceled.
|
||||
int64_t query_id;
|
||||
CefRefPtr<Callback> callback;
|
||||
CefRefPtr<CefMessageRouterBrowserSide::Callback> callback;
|
||||
|
||||
TrackCallback got_query;
|
||||
TrackCallback got_query_canceled;
|
||||
@ -610,13 +634,24 @@ class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
|
||||
const std::string& request_id_var =
|
||||
GetIDString(kMultiQueryRequestId, index);
|
||||
const std::string& repeat_ct_var = GetIDString(kMultiQueryRepeatCt, index);
|
||||
const std::string& request_val =
|
||||
const std::string& request_str =
|
||||
GetIDString(std::string(kMultiQueryRequest) + ":", index);
|
||||
const std::string& success_val =
|
||||
GetIDString(std::string(kMultiQuerySuccess) + ":", index);
|
||||
const std::string& error_val =
|
||||
GetIDString(std::string(kMultiQueryError) + ":", index);
|
||||
|
||||
const std::string request_val =
|
||||
transfer_type_ == TransferType::BINARY
|
||||
? ("new TextEncoder().encode('" + request_str + "').buffer")
|
||||
: "'" + request_str + "'";
|
||||
|
||||
const std::string response_conversion =
|
||||
transfer_type_ == TransferType::BINARY
|
||||
? " const decoder = new TextDecoder('utf-8');\n"
|
||||
" const message = decoder.decode(response);\n"
|
||||
: " const message = response;\n";
|
||||
|
||||
std::string html;
|
||||
|
||||
const bool persistent = IsPersistent(query.type);
|
||||
@ -627,19 +662,15 @@ class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
|
||||
|
||||
html += "var " + request_id_var +
|
||||
" = window.mrtQuery({\n"
|
||||
" request: '" +
|
||||
" request: " +
|
||||
request_val +
|
||||
"',\n"
|
||||
" persistent: " +
|
||||
(persistent ? "true" : "false") + ",\n";
|
||||
",\n persistent: " + (persistent ? "true" : "false") + ",\n";
|
||||
|
||||
if (query.type == SUCCESS) {
|
||||
const std::string& response_val = GetIDString(kMultiQueryResponse, index);
|
||||
|
||||
html +=
|
||||
" onSuccess: function(response) {\n"
|
||||
" if (response == '" +
|
||||
response_val +
|
||||
html += " onSuccess: function(response) {\n" + response_conversion +
|
||||
" if (message == '" + response_val +
|
||||
"')\n"
|
||||
" window.mrtNotify('" +
|
||||
success_val +
|
||||
@ -683,10 +714,8 @@ class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
|
||||
const std::string& repeat_ct =
|
||||
GetIntString(kMultiQueryPersistentResponseCount);
|
||||
|
||||
html +=
|
||||
" onSuccess: function(response) {\n"
|
||||
" if (response == '" +
|
||||
response_val +
|
||||
html += " onSuccess: function(response) {\n" + response_conversion +
|
||||
" if (message == '" + response_val +
|
||||
"') {\n"
|
||||
// Should get repeat_ct number of successful responses.
|
||||
" if (++" +
|
||||
@ -773,9 +802,12 @@ class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
|
||||
|
||||
std::string GetIDString(const std::string& prefix, int index) const {
|
||||
EXPECT_TRUE(!prefix.empty());
|
||||
std::stringstream ss;
|
||||
ss << prefix << GetIDFromIndex(index);
|
||||
return ss.str();
|
||||
return prefix + std::to_string(GetIDFromIndex(index));
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetIDBinary(const std::string& prefix, int index) const {
|
||||
auto str = GetIDString(prefix, index);
|
||||
return std::vector<uint8_t>(str.begin(), str.end());
|
||||
}
|
||||
|
||||
bool SplitIDString(const std::string& str,
|
||||
@ -792,6 +824,19 @@ class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SplitIDString(const CefRefPtr<const CefBinaryBuffer>& request,
|
||||
std::string* value,
|
||||
int* index) const {
|
||||
const size_t string_len =
|
||||
request->GetSize() / sizeof(std::string::value_type);
|
||||
const auto* src =
|
||||
static_cast<const std::string::value_type*>(request->GetData());
|
||||
CefString result;
|
||||
result.FromString(src, string_len);
|
||||
|
||||
return SplitIDString(result, value, index);
|
||||
}
|
||||
|
||||
std::string GetIntString(int val) const {
|
||||
std::stringstream ss;
|
||||
ss << val;
|
||||
@ -804,6 +849,7 @@ class MultiQueryManager : public CefMessageRouterBrowserSide::Handler {
|
||||
const std::string label_;
|
||||
const bool synchronous_;
|
||||
const int id_offset_;
|
||||
const TransferType transfer_type_;
|
||||
|
||||
typedef std::vector<TestQuery> TestQueryVector;
|
||||
TestQueryVector test_query_vector_;
|
||||
@ -904,8 +950,10 @@ class MultiQuerySingleFrameTestHandler : public SingleLoadTestHandler,
|
||||
|
||||
MultiQuerySingleFrameTestHandler(
|
||||
bool synchronous,
|
||||
TransferType transfer_type,
|
||||
CancelType cancel_type = CANCEL_BY_NAVIGATION)
|
||||
: manager_(std::string(), synchronous), cancel_type_(cancel_type) {
|
||||
: manager_(std::string(), synchronous, 0, transfer_type),
|
||||
cancel_type_(cancel_type) {
|
||||
manager_.AddObserver(this);
|
||||
}
|
||||
|
||||
@ -929,7 +977,20 @@ class MultiQuerySingleFrameTestHandler : public SingleLoadTestHandler,
|
||||
AssertMainBrowser(browser);
|
||||
AssertMainFrame(frame);
|
||||
|
||||
return manager_.OnQuery(browser, frame, query_id, request, persistent,
|
||||
return manager_.OnQueryImpl(browser, frame, query_id, request, persistent,
|
||||
callback);
|
||||
}
|
||||
|
||||
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64_t query_id,
|
||||
CefRefPtr<const CefBinaryBuffer> request,
|
||||
bool persistent,
|
||||
CefRefPtr<Callback> callback) override {
|
||||
AssertMainBrowser(browser);
|
||||
AssertMainFrame(frame);
|
||||
|
||||
return manager_.OnQueryImpl(browser, frame, query_id, request, persistent,
|
||||
callback);
|
||||
}
|
||||
|
||||
@ -994,10 +1055,22 @@ class MultiQuerySingleFrameTestHandler : public SingleLoadTestHandler,
|
||||
|
||||
} // namespace
|
||||
|
||||
#define MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(name, type, synchronous) \
|
||||
TEST(MessageRouterTest, name) { \
|
||||
#define MQSF_TYPE_TEST(name, type, synchronous) \
|
||||
TEST(MessageRouterTest, MultiQuerySingleFrame##name##String) { \
|
||||
CefRefPtr<MultiQuerySingleFrameTestHandler> handler = \
|
||||
new MultiQuerySingleFrameTestHandler(synchronous); \
|
||||
new MultiQuerySingleFrameTestHandler(synchronous, \
|
||||
TransferType::STRING); \
|
||||
MultiQueryManager* manager = handler->GetManager(); \
|
||||
manager->AddTestQuery(MultiQueryManager::type); \
|
||||
manager->Finalize(); \
|
||||
handler->ExecuteTest(); \
|
||||
ReleaseAndWaitForDestructor(handler); \
|
||||
} \
|
||||
\
|
||||
TEST(MessageRouterTest, MultiQuerySingleFrame##name##Binary) { \
|
||||
CefRefPtr<MultiQuerySingleFrameTestHandler> handler = \
|
||||
new MultiQuerySingleFrameTestHandler(synchronous, \
|
||||
TransferType::BINARY); \
|
||||
MultiQueryManager* manager = handler->GetManager(); \
|
||||
manager->AddTestQuery(MultiQueryManager::type); \
|
||||
manager->Finalize(); \
|
||||
@ -1006,97 +1079,80 @@ class MultiQuerySingleFrameTestHandler : public SingleLoadTestHandler,
|
||||
}
|
||||
|
||||
// Test the query types individually.
|
||||
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncSuccess,
|
||||
SUCCESS,
|
||||
true)
|
||||
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncSuccess,
|
||||
SUCCESS,
|
||||
false)
|
||||
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncFailure,
|
||||
FAILURE,
|
||||
true)
|
||||
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncFailure,
|
||||
FAILURE,
|
||||
false)
|
||||
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncPersistentSuccess,
|
||||
PERSISTENT_SUCCESS,
|
||||
true)
|
||||
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncPersistentSuccess,
|
||||
PERSISTENT_SUCCESS,
|
||||
false)
|
||||
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameSyncPersistentFailure,
|
||||
PERSISTENT_FAILURE,
|
||||
true)
|
||||
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAsyncPersistentFailure,
|
||||
PERSISTENT_FAILURE,
|
||||
false)
|
||||
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameCancel, CANCEL, true)
|
||||
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFrameAutoCancel,
|
||||
AUTOCANCEL,
|
||||
true)
|
||||
MULTI_QUERY_SINGLE_FRAME_TYPE_TEST(MultiQuerySingleFramePersistentAutoCancel,
|
||||
PERSISTENT_AUTOCANCEL,
|
||||
true)
|
||||
MQSF_TYPE_TEST(SyncSuccess, SUCCESS, true)
|
||||
MQSF_TYPE_TEST(AsyncSuccess, SUCCESS, false)
|
||||
MQSF_TYPE_TEST(SyncFailure, FAILURE, true)
|
||||
MQSF_TYPE_TEST(AsyncFailure, FAILURE, false)
|
||||
MQSF_TYPE_TEST(SyncPersistentSuccess, PERSISTENT_SUCCESS, true)
|
||||
MQSF_TYPE_TEST(AsyncPersistentSuccess, PERSISTENT_SUCCESS, false)
|
||||
MQSF_TYPE_TEST(SyncPersistentFailure, PERSISTENT_FAILURE, true)
|
||||
MQSF_TYPE_TEST(AsyncPersistentFailure, PERSISTENT_FAILURE, false)
|
||||
MQSF_TYPE_TEST(Cancel, CANCEL, true)
|
||||
MQSF_TYPE_TEST(AutoCancel, AUTOCANCEL, true)
|
||||
MQSF_TYPE_TEST(PersistentAutoCancel, PERSISTENT_AUTOCANCEL, true)
|
||||
|
||||
#define MQSF_QUERY_RANGE_TEST(name, some, synchronous) \
|
||||
TEST(MessageRouterTest, MultiQuerySingleFrame##name##String) { \
|
||||
CefRefPtr<MultiQuerySingleFrameTestHandler> handler = \
|
||||
new MultiQuerySingleFrameTestHandler(synchronous, \
|
||||
TransferType::STRING); \
|
||||
MakeTestQueries(handler->GetManager(), some); \
|
||||
handler->ExecuteTest(); \
|
||||
ReleaseAndWaitForDestructor(handler); \
|
||||
} \
|
||||
\
|
||||
TEST(MessageRouterTest, MultiQuerySingleFrame##name##Binary) { \
|
||||
CefRefPtr<MultiQuerySingleFrameTestHandler> handler = \
|
||||
new MultiQuerySingleFrameTestHandler(synchronous, \
|
||||
TransferType::BINARY); \
|
||||
MakeTestQueries(handler->GetManager(), some); \
|
||||
handler->ExecuteTest(); \
|
||||
ReleaseAndWaitForDestructor(handler); \
|
||||
}
|
||||
|
||||
// Test that one frame can run some queries successfully in a synchronous
|
||||
// manner.
|
||||
TEST(MessageRouterTest, MultiQuerySingleFrameSyncSome) {
|
||||
CefRefPtr<MultiQuerySingleFrameTestHandler> handler =
|
||||
new MultiQuerySingleFrameTestHandler(true);
|
||||
MakeTestQueries(handler->GetManager(), true);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
// manner
|
||||
MQSF_QUERY_RANGE_TEST(SyncSome, true, true)
|
||||
|
||||
// Test that one frame can run some queries successfully in an asynchronous
|
||||
// manner.
|
||||
TEST(MessageRouterTest, MultiQuerySingleFrameAsyncSome) {
|
||||
CefRefPtr<MultiQuerySingleFrameTestHandler> handler =
|
||||
new MultiQuerySingleFrameTestHandler(false);
|
||||
MakeTestQueries(handler->GetManager(), true);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQSF_QUERY_RANGE_TEST(AsyncSome, true, false)
|
||||
|
||||
// Test that one frame can run many queries successfully in a synchronous
|
||||
// manner.
|
||||
TEST(MessageRouterTest, MultiQuerySingleFrameSyncMany) {
|
||||
CefRefPtr<MultiQuerySingleFrameTestHandler> handler =
|
||||
new MultiQuerySingleFrameTestHandler(true);
|
||||
MakeTestQueries(handler->GetManager(), false);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQSF_QUERY_RANGE_TEST(SyncMany, false, true)
|
||||
|
||||
// Test that one frame can run many queries successfully in an asynchronous
|
||||
// manner.
|
||||
TEST(MessageRouterTest, MultiQuerySingleFrameAsyncMany) {
|
||||
CefRefPtr<MultiQuerySingleFrameTestHandler> handler =
|
||||
new MultiQuerySingleFrameTestHandler(false);
|
||||
MakeTestQueries(handler->GetManager(), false);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQSF_QUERY_RANGE_TEST(AsyncMany, false, false)
|
||||
|
||||
#define MQSF_QUERY_RANGE_CANCEL_TEST(name, cancelType) \
|
||||
TEST(MessageRouterTest, MultiQuerySingleFrame##name##String) { \
|
||||
CefRefPtr<MultiQuerySingleFrameTestHandler> handler = \
|
||||
new MultiQuerySingleFrameTestHandler( \
|
||||
false, TransferType::STRING, \
|
||||
MultiQuerySingleFrameTestHandler::cancelType); \
|
||||
MakeTestQueries(handler->GetManager(), false); \
|
||||
handler->ExecuteTest(); \
|
||||
ReleaseAndWaitForDestructor(handler); \
|
||||
} \
|
||||
\
|
||||
TEST(MessageRouterTest, MultiQuerySingleFrame##name##Binary) { \
|
||||
CefRefPtr<MultiQuerySingleFrameTestHandler> handler = \
|
||||
new MultiQuerySingleFrameTestHandler( \
|
||||
false, TransferType::BINARY, \
|
||||
MultiQuerySingleFrameTestHandler::cancelType); \
|
||||
MakeTestQueries(handler->GetManager(), false); \
|
||||
handler->ExecuteTest(); \
|
||||
ReleaseAndWaitForDestructor(handler); \
|
||||
}
|
||||
|
||||
// Test that pending queries can be canceled by removing the handler.
|
||||
TEST(MessageRouterTest, MultiQuerySingleFrameCancelByRemovingHandler) {
|
||||
CefRefPtr<MultiQuerySingleFrameTestHandler> handler =
|
||||
new MultiQuerySingleFrameTestHandler(
|
||||
false, MultiQuerySingleFrameTestHandler::CANCEL_BY_REMOVING_HANDLER);
|
||||
MakeTestQueries(handler->GetManager(), false);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQSF_QUERY_RANGE_CANCEL_TEST(CancelByRemovingHandler,
|
||||
CANCEL_BY_REMOVING_HANDLER)
|
||||
|
||||
// Test that pending queries can be canceled by closing the browser.
|
||||
TEST(MessageRouterTest, MultiQuerySingleFrameCancelByClosingBrowser) {
|
||||
CefRefPtr<MultiQuerySingleFrameTestHandler> handler =
|
||||
new MultiQuerySingleFrameTestHandler(
|
||||
false, MultiQuerySingleFrameTestHandler::CANCEL_BY_CLOSING_BROWSER);
|
||||
MakeTestQueries(handler->GetManager(), false);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQSF_QUERY_RANGE_CANCEL_TEST(CancelByClosingBrowser, CANCEL_BY_CLOSING_BROWSER)
|
||||
|
||||
namespace {
|
||||
|
||||
@ -1211,7 +1267,20 @@ class MultiQueryMultiHandlerTestHandler : public SingleLoadTestHandler,
|
||||
AssertMainBrowser(browser);
|
||||
AssertMainFrame(frame);
|
||||
|
||||
return manager_.OnQuery(browser, frame, query_id, request, persistent,
|
||||
return manager_.OnQueryImpl(browser, frame, query_id, request, persistent,
|
||||
callback);
|
||||
}
|
||||
|
||||
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64_t query_id,
|
||||
CefRefPtr<const CefBinaryBuffer> request,
|
||||
bool persistent,
|
||||
CefRefPtr<Callback> callback) override {
|
||||
AssertMainBrowser(browser);
|
||||
AssertMainFrame(frame);
|
||||
|
||||
return manager_.OnQueryImpl(browser, frame, query_id, request, persistent,
|
||||
callback);
|
||||
}
|
||||
|
||||
@ -1334,8 +1403,7 @@ TEST(MessageRouterTest, MultiQueryMultiHandlerCancelByRemovingHandler) {
|
||||
namespace {
|
||||
|
||||
// Map of managers on a per-URL basis.
|
||||
class MultiQueryManagerMap : public CefMessageRouterBrowserSide::Handler,
|
||||
public MultiQueryManager::Observer {
|
||||
class MultiQueryManagerMap : public MultiQueryManager::Observer {
|
||||
public:
|
||||
class Observer {
|
||||
public:
|
||||
@ -1367,11 +1435,14 @@ class MultiQueryManagerMap : public CefMessageRouterBrowserSide::Handler,
|
||||
EXPECT_TRUE(observer_set_.erase(observer));
|
||||
}
|
||||
|
||||
MultiQueryManager* CreateManager(const std::string& url, bool synchronous) {
|
||||
MultiQueryManager* CreateManager(const std::string& url,
|
||||
bool synchronous,
|
||||
TransferType transfer_type) {
|
||||
EXPECT_FALSE(finalized_);
|
||||
|
||||
MultiQueryManager* manager = new MultiQueryManager(
|
||||
url, synchronous, static_cast<int>(manager_map_.size()) * 1000);
|
||||
url, synchronous, static_cast<int>(manager_map_.size()) * 1000,
|
||||
transfer_type);
|
||||
manager->AddObserver(this);
|
||||
all_managers_.push_back(manager);
|
||||
pending_managers_.push_back(manager);
|
||||
@ -1412,25 +1483,26 @@ class MultiQueryManagerMap : public CefMessageRouterBrowserSide::Handler,
|
||||
manager->OnNotify(browser, frame, message);
|
||||
}
|
||||
|
||||
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
||||
template <class RequestType>
|
||||
bool OnQueryImpl(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64_t query_id,
|
||||
const CefString& request,
|
||||
const RequestType& request,
|
||||
bool persistent,
|
||||
CefRefPtr<Callback> callback) override {
|
||||
CefRefPtr<CefMessageRouterBrowserSide::Callback> callback) {
|
||||
EXPECT_TRUE(finalized_);
|
||||
if (!running_) {
|
||||
running_ = true;
|
||||
}
|
||||
|
||||
MultiQueryManager* manager = GetManager(browser, frame);
|
||||
return manager->OnQuery(browser, frame, query_id, request, persistent,
|
||||
return manager->OnQueryImpl(browser, frame, query_id, request, persistent,
|
||||
callback);
|
||||
}
|
||||
|
||||
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64_t query_id) override {
|
||||
int64_t query_id) {
|
||||
EXPECT_TRUE(finalized_);
|
||||
if (!running_) {
|
||||
running_ = true;
|
||||
@ -1611,8 +1683,12 @@ class MultiQueryManagerMap : public CefMessageRouterBrowserSide::Handler,
|
||||
class MultiQueryMultiFrameTestHandler : public SingleLoadTestHandler,
|
||||
public MultiQueryManagerMap::Observer {
|
||||
public:
|
||||
MultiQueryMultiFrameTestHandler(bool synchronous, bool cancel_with_subnav)
|
||||
: synchronous_(synchronous), cancel_with_subnav_(cancel_with_subnav) {
|
||||
MultiQueryMultiFrameTestHandler(bool synchronous,
|
||||
bool cancel_with_subnav,
|
||||
TransferType transfer_type)
|
||||
: synchronous_(synchronous),
|
||||
cancel_with_subnav_(cancel_with_subnav),
|
||||
transfer_type_(transfer_type) {
|
||||
manager_map_.AddObserver(this);
|
||||
}
|
||||
|
||||
@ -1657,8 +1733,21 @@ class MultiQueryMultiFrameTestHandler : public SingleLoadTestHandler,
|
||||
AssertMainBrowser(browser);
|
||||
EXPECT_FALSE(frame->IsMain());
|
||||
|
||||
return manager_map_.OnQuery(browser, frame, query_id, request, persistent,
|
||||
callback);
|
||||
return manager_map_.OnQueryImpl(browser, frame, query_id, request,
|
||||
persistent, callback);
|
||||
}
|
||||
|
||||
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64_t query_id,
|
||||
CefRefPtr<const CefBinaryBuffer> request,
|
||||
bool persistent,
|
||||
CefRefPtr<Callback> callback) override {
|
||||
AssertMainBrowser(browser);
|
||||
EXPECT_FALSE(frame->IsMain());
|
||||
|
||||
return manager_map_.OnQueryImpl(browser, frame, query_id, request,
|
||||
persistent, callback);
|
||||
}
|
||||
|
||||
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
|
||||
@ -1709,7 +1798,8 @@ class MultiQueryMultiFrameTestHandler : public SingleLoadTestHandler,
|
||||
void AddSubFrameResource(const std::string& name) {
|
||||
const std::string& url = std::string(kTestDomain1) + name + ".html";
|
||||
|
||||
MultiQueryManager* manager = manager_map_.CreateManager(url, synchronous_);
|
||||
MultiQueryManager* manager =
|
||||
manager_map_.CreateManager(url, synchronous_, transfer_type_);
|
||||
MakeTestQueries(manager, false, 100);
|
||||
|
||||
const std::string& html = manager->GetHTML(false, false);
|
||||
@ -1718,6 +1808,7 @@ class MultiQueryMultiFrameTestHandler : public SingleLoadTestHandler,
|
||||
|
||||
const bool synchronous_;
|
||||
const bool cancel_with_subnav_;
|
||||
const TransferType transfer_type_;
|
||||
|
||||
MultiQueryManagerMap manager_map_;
|
||||
|
||||
@ -1726,41 +1817,38 @@ class MultiQueryMultiFrameTestHandler : public SingleLoadTestHandler,
|
||||
|
||||
} // namespace
|
||||
|
||||
#define MQMF_TEST(name, sync, cancel_with_subnav) \
|
||||
TEST(MessageRouterTest, MultiQueryMultiFrame##name##String) { \
|
||||
CefRefPtr<MultiQueryMultiFrameTestHandler> handler = \
|
||||
new MultiQueryMultiFrameTestHandler(sync, cancel_with_subnav, \
|
||||
TransferType::STRING); \
|
||||
handler->ExecuteTest(); \
|
||||
ReleaseAndWaitForDestructor(handler); \
|
||||
} \
|
||||
\
|
||||
TEST(MessageRouterTest, MultiQueryMultiFrame##name##Binary) { \
|
||||
CefRefPtr<MultiQueryMultiFrameTestHandler> handler = \
|
||||
new MultiQueryMultiFrameTestHandler(sync, cancel_with_subnav, \
|
||||
TransferType::BINARY); \
|
||||
handler->ExecuteTest(); \
|
||||
ReleaseAndWaitForDestructor(handler); \
|
||||
}
|
||||
|
||||
// Test that multiple frames can run many queries successfully in a synchronous
|
||||
// manner.
|
||||
TEST(MessageRouterTest, MultiQueryMultiFrameSync) {
|
||||
CefRefPtr<MultiQueryMultiFrameTestHandler> handler =
|
||||
new MultiQueryMultiFrameTestHandler(true, false);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQMF_TEST(Sync, true, false)
|
||||
|
||||
// Test that multiple frames can run many queries successfully in an
|
||||
// asynchronous manner.
|
||||
TEST(MessageRouterTest, MultiQueryMultiFrameAsync) {
|
||||
CefRefPtr<MultiQueryMultiFrameTestHandler> handler =
|
||||
new MultiQueryMultiFrameTestHandler(false, false);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQMF_TEST(Async, false, false)
|
||||
|
||||
// Test that multiple frames can run many queries successfully in a synchronous
|
||||
// manner. Cancel auto queries with sub-frame navigation.
|
||||
TEST(MessageRouterTest, MultiQueryMultiFrameSyncSubnavCancel) {
|
||||
CefRefPtr<MultiQueryMultiFrameTestHandler> handler =
|
||||
new MultiQueryMultiFrameTestHandler(true, true);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQMF_TEST(SyncSubnavCancel, true, true)
|
||||
|
||||
// Test that multiple frames can run many queries successfully in an
|
||||
// asynchronous manner. Cancel auto queries with sub-frame navigation.
|
||||
TEST(MessageRouterTest, MultiQueryMultiFrameAsyncSubnavCancel) {
|
||||
CefRefPtr<MultiQueryMultiFrameTestHandler> handler =
|
||||
new MultiQueryMultiFrameTestHandler(false, true);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQMF_TEST(AsyncSubnavCancel, false, true)
|
||||
|
||||
namespace {
|
||||
|
||||
@ -1772,8 +1860,10 @@ class MultiQueryMultiLoadTestHandler
|
||||
public MultiQueryManagerMap::Observer,
|
||||
public MultiQueryManager::Observer {
|
||||
public:
|
||||
MultiQueryMultiLoadTestHandler(bool some, bool synchronous)
|
||||
: some_(some), synchronous_(synchronous) {
|
||||
MultiQueryMultiLoadTestHandler(bool some,
|
||||
bool synchronous,
|
||||
TransferType transfer_type)
|
||||
: some_(some), synchronous_(synchronous), transfer_type_(transfer_type) {
|
||||
manager_map_.AddObserver(this);
|
||||
}
|
||||
|
||||
@ -1795,8 +1885,18 @@ class MultiQueryMultiLoadTestHandler
|
||||
const CefString& request,
|
||||
bool persistent,
|
||||
CefRefPtr<Callback> callback) override {
|
||||
return manager_map_.OnQuery(browser, frame, query_id, request, persistent,
|
||||
callback);
|
||||
return manager_map_.OnQueryImpl(browser, frame, query_id, request,
|
||||
persistent, callback);
|
||||
}
|
||||
|
||||
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64_t query_id,
|
||||
CefRefPtr<const CefBinaryBuffer> request,
|
||||
bool persistent,
|
||||
CefRefPtr<Callback> callback) override {
|
||||
return manager_map_.OnQueryImpl(browser, frame, query_id, request,
|
||||
persistent, callback);
|
||||
}
|
||||
|
||||
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
|
||||
@ -1838,7 +1938,8 @@ class MultiQueryMultiLoadTestHandler
|
||||
void AddManagedResource(const std::string& url,
|
||||
bool assert_total,
|
||||
bool assert_browser) {
|
||||
MultiQueryManager* manager = manager_map_.CreateManager(url, synchronous_);
|
||||
MultiQueryManager* manager =
|
||||
manager_map_.CreateManager(url, synchronous_, transfer_type_);
|
||||
manager->AddObserver(this);
|
||||
MakeTestQueries(manager, some_, 75);
|
||||
|
||||
@ -1860,6 +1961,7 @@ class MultiQueryMultiLoadTestHandler
|
||||
private:
|
||||
const bool some_;
|
||||
const bool synchronous_;
|
||||
const TransferType transfer_type_;
|
||||
|
||||
std::string cancel_url_;
|
||||
};
|
||||
@ -1868,8 +1970,10 @@ class MultiQueryMultiLoadTestHandler
|
||||
class MultiQueryMultiBrowserTestHandler
|
||||
: public MultiQueryMultiLoadTestHandler {
|
||||
public:
|
||||
MultiQueryMultiBrowserTestHandler(bool synchronous, bool same_origin)
|
||||
: MultiQueryMultiLoadTestHandler(false, synchronous),
|
||||
MultiQueryMultiBrowserTestHandler(bool synchronous,
|
||||
bool same_origin,
|
||||
TransferType transfer_type)
|
||||
: MultiQueryMultiLoadTestHandler(false, synchronous, transfer_type),
|
||||
same_origin_(same_origin) {}
|
||||
|
||||
protected:
|
||||
@ -1899,37 +2003,34 @@ class MultiQueryMultiBrowserTestHandler
|
||||
|
||||
} // namespace
|
||||
|
||||
// Test that multiple browsers can query simultaniously from the same origin.
|
||||
TEST(MessageRouterTest, MultiQueryMultiBrowserSameOriginSync) {
|
||||
CefRefPtr<MultiQueryMultiBrowserTestHandler> handler =
|
||||
new MultiQueryMultiBrowserTestHandler(true, true);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
#define MQMB_TEST(name, sync, same_origin) \
|
||||
TEST(MessageRouterTest, MultiQueryMultiBrowser##name##String) { \
|
||||
CefRefPtr<MultiQueryMultiBrowserTestHandler> handler = \
|
||||
new MultiQueryMultiBrowserTestHandler(sync, same_origin, \
|
||||
TransferType::STRING); \
|
||||
handler->ExecuteTest(); \
|
||||
ReleaseAndWaitForDestructor(handler); \
|
||||
} \
|
||||
\
|
||||
TEST(MessageRouterTest, MultiQueryMultiBrowser##name##Binary) { \
|
||||
CefRefPtr<MultiQueryMultiBrowserTestHandler> handler = \
|
||||
new MultiQueryMultiBrowserTestHandler(sync, same_origin, \
|
||||
TransferType::BINARY); \
|
||||
handler->ExecuteTest(); \
|
||||
ReleaseAndWaitForDestructor(handler); \
|
||||
}
|
||||
|
||||
// Test that multiple browsers can query simultaniously from the same origin.
|
||||
TEST(MessageRouterTest, MultiQueryMultiBrowserSameOriginAsync) {
|
||||
CefRefPtr<MultiQueryMultiBrowserTestHandler> handler =
|
||||
new MultiQueryMultiBrowserTestHandler(false, true);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQMB_TEST(SameOriginSync, true, true)
|
||||
|
||||
// Test that multiple browsers can query simultaniously from the same origin.
|
||||
MQMB_TEST(SameOriginAsync, false, true)
|
||||
|
||||
// Test that multiple browsers can query simultaniously from different origins.
|
||||
TEST(MessageRouterTest, MultiQueryMultiBrowserDifferentOriginSync) {
|
||||
CefRefPtr<MultiQueryMultiBrowserTestHandler> handler =
|
||||
new MultiQueryMultiBrowserTestHandler(true, false);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQMB_TEST(DifferentOriginSync, true, false)
|
||||
|
||||
// Test that multiple browsers can query simultaniously from different origins.
|
||||
TEST(MessageRouterTest, MultiQueryMultiBrowserDifferentOriginAsync) {
|
||||
CefRefPtr<MultiQueryMultiBrowserTestHandler> handler =
|
||||
new MultiQueryMultiBrowserTestHandler(false, false);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQMB_TEST(DifferentOriginAsync, false, false)
|
||||
|
||||
namespace {
|
||||
|
||||
@ -1937,8 +2038,10 @@ namespace {
|
||||
class MultiQueryMultiNavigateTestHandler
|
||||
: public MultiQueryMultiLoadTestHandler {
|
||||
public:
|
||||
MultiQueryMultiNavigateTestHandler(bool synchronous, bool same_origin)
|
||||
: MultiQueryMultiLoadTestHandler(false, synchronous),
|
||||
MultiQueryMultiNavigateTestHandler(bool synchronous,
|
||||
bool same_origin,
|
||||
TransferType transfer_type)
|
||||
: MultiQueryMultiLoadTestHandler(false, synchronous, transfer_type),
|
||||
same_origin_(same_origin) {}
|
||||
|
||||
void OnManualQueriesCompleted(MultiQueryManager* manager) override {
|
||||
@ -1981,34 +2084,31 @@ class MultiQueryMultiNavigateTestHandler
|
||||
|
||||
} // namespace
|
||||
|
||||
// Test that multiple navigations can query from the same origin.
|
||||
TEST(MessageRouterTest, MultiQueryMultiNavigateSameOriginSync) {
|
||||
CefRefPtr<MultiQueryMultiNavigateTestHandler> handler =
|
||||
new MultiQueryMultiNavigateTestHandler(true, true);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
#define MQMN_TEST(name, sync, same_origin) \
|
||||
TEST(MessageRouterTest, MultiQueryMultiNavigate##name##String) { \
|
||||
CefRefPtr<MultiQueryMultiNavigateTestHandler> handler = \
|
||||
new MultiQueryMultiNavigateTestHandler(sync, same_origin, \
|
||||
TransferType::STRING); \
|
||||
handler->ExecuteTest(); \
|
||||
ReleaseAndWaitForDestructor(handler); \
|
||||
} \
|
||||
\
|
||||
TEST(MessageRouterTest, MultiQueryMultiNavigate##name##Binary) { \
|
||||
CefRefPtr<MultiQueryMultiNavigateTestHandler> handler = \
|
||||
new MultiQueryMultiNavigateTestHandler(sync, same_origin, \
|
||||
TransferType::BINARY); \
|
||||
handler->ExecuteTest(); \
|
||||
ReleaseAndWaitForDestructor(handler); \
|
||||
}
|
||||
|
||||
// Test that multiple navigations can query from the same origin.
|
||||
TEST(MessageRouterTest, MultiQueryMultiNavigateSameOriginAsync) {
|
||||
CefRefPtr<MultiQueryMultiNavigateTestHandler> handler =
|
||||
new MultiQueryMultiNavigateTestHandler(false, true);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQMN_TEST(SameOriginSync, true, true)
|
||||
|
||||
// Test that multiple navigations can query from the same origin.
|
||||
MQMN_TEST(SameOriginAsync, false, true)
|
||||
|
||||
// Test that multiple navigations can query from different origins.
|
||||
TEST(MessageRouterTest, MultiQueryMultiNavigateDifferentOriginSync) {
|
||||
CefRefPtr<MultiQueryMultiNavigateTestHandler> handler =
|
||||
new MultiQueryMultiNavigateTestHandler(true, false);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQMN_TEST(DifferentOriginSync, true, false)
|
||||
|
||||
// Test that multiple navigations can query from different origins.
|
||||
TEST(MessageRouterTest, MultiQueryMultiNavigateDifferentOriginAsync) {
|
||||
CefRefPtr<MultiQueryMultiNavigateTestHandler> handler =
|
||||
new MultiQueryMultiNavigateTestHandler(false, false);
|
||||
handler->ExecuteTest();
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
MQMN_TEST(DifferentOriginAsync, false, false)
|
||||
|
@ -59,6 +59,7 @@ enum V8TestMode {
|
||||
V8TEST_ARRAY_CREATE,
|
||||
V8TEST_ARRAY_VALUE,
|
||||
V8TEST_ARRAY_BUFFER,
|
||||
V8TEST_ARRAY_BUFFER_CREATE_EMPTY,
|
||||
V8TEST_ARRAY_BUFFER_VALUE,
|
||||
V8TEST_OBJECT_CREATE,
|
||||
V8TEST_OBJECT_USERDATA,
|
||||
@ -145,6 +146,9 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
||||
case V8TEST_ARRAY_BUFFER:
|
||||
RunArrayBufferTest();
|
||||
break;
|
||||
case V8TEST_ARRAY_BUFFER_CREATE_EMPTY:
|
||||
RunArrayBufferCreateEmptyTest();
|
||||
break;
|
||||
case V8TEST_ARRAY_BUFFER_VALUE:
|
||||
RunArrayBufferValueTest();
|
||||
break;
|
||||
@ -593,6 +597,9 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
||||
EXPECT_TRUE(value.get());
|
||||
EXPECT_TRUE(value->IsArrayBuffer());
|
||||
EXPECT_TRUE(value->IsObject());
|
||||
EXPECT_EQ(value->GetArrayBufferByteLength(), sizeof(static_data));
|
||||
void* data = value->GetArrayBufferData();
|
||||
EXPECT_EQ(static_cast<int*>(data), static_data);
|
||||
EXPECT_FALSE(value->HasValue(0));
|
||||
EXPECT_TRUE(value->GetArrayBufferReleaseCallback().get() != nullptr);
|
||||
EXPECT_TRUE(((TestArrayBufferReleaseCallback*)value
|
||||
@ -636,6 +643,9 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
||||
static_data[0] = 3;
|
||||
value =
|
||||
CefV8Value::CreateArrayBuffer(static_data, sizeof(static_data), owner);
|
||||
EXPECT_EQ(value->GetArrayBufferByteLength(), sizeof(static_data));
|
||||
void* data = value->GetArrayBufferData();
|
||||
EXPECT_EQ(static_cast<int*>(data), static_data);
|
||||
|
||||
CefRefPtr<CefV8Value> object = context->GetGlobal();
|
||||
EXPECT_TRUE(object.get());
|
||||
@ -649,8 +659,43 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
|
||||
ADD_FAILURE() << exception->GetMessage().c_str();
|
||||
}
|
||||
|
||||
EXPECT_TRUE(static_data[0] == 19);
|
||||
EXPECT_TRUE(value->GetArrayBufferReleaseCallback().get() != nullptr);
|
||||
EXPECT_EQ(static_data[0], 19);
|
||||
EXPECT_NE(value->GetArrayBufferReleaseCallback().get(), nullptr);
|
||||
EXPECT_TRUE(value->NeuterArrayBuffer());
|
||||
|
||||
// Exit the V8 context.
|
||||
EXPECT_TRUE(context->Exit());
|
||||
DestroyTest();
|
||||
}
|
||||
|
||||
void RunArrayBufferCreateEmptyTest() {
|
||||
class TestArrayBufferReleaseCallback
|
||||
: public CefV8ArrayBufferReleaseCallback {
|
||||
public:
|
||||
void ReleaseBuffer(void* buffer) override {}
|
||||
|
||||
IMPLEMENT_REFCOUNTING(TestArrayBufferReleaseCallback);
|
||||
};
|
||||
|
||||
CefRefPtr<TestArrayBufferReleaseCallback> owner =
|
||||
new TestArrayBufferReleaseCallback();
|
||||
|
||||
// Enter the V8 context
|
||||
CefRefPtr<CefV8Context> context = GetContext();
|
||||
EXPECT_TRUE(context->Enter());
|
||||
|
||||
const size_t zero_size = 0;
|
||||
void* null_data = nullptr;
|
||||
|
||||
CefRefPtr<CefV8Value> value =
|
||||
CefV8Value::CreateArrayBuffer(null_data, zero_size, owner);
|
||||
EXPECT_EQ(value->GetArrayBufferByteLength(), zero_size);
|
||||
EXPECT_EQ(value->GetArrayBufferData(), null_data);
|
||||
|
||||
CefRefPtr<CefV8Value> object = context->GetGlobal();
|
||||
EXPECT_TRUE(object.get());
|
||||
EXPECT_TRUE(object->SetValue("arr", value, V8_PROPERTY_ATTRIBUTE_NONE));
|
||||
EXPECT_NE(value->GetArrayBufferReleaseCallback().get(), nullptr);
|
||||
EXPECT_TRUE(value->NeuterArrayBuffer());
|
||||
|
||||
// Exit the V8 context.
|
||||
@ -3321,6 +3366,7 @@ V8_TEST(EmptyStringCreate, V8TEST_EMPTY_STRING_CREATE)
|
||||
V8_TEST(ArrayCreate, V8TEST_ARRAY_CREATE)
|
||||
V8_TEST(ArrayValue, V8TEST_ARRAY_VALUE)
|
||||
V8_TEST(ArrayBuffer, V8TEST_ARRAY_BUFFER)
|
||||
V8_TEST(ArrayBufferCreateEmpty, V8TEST_ARRAY_BUFFER_CREATE_EMPTY)
|
||||
V8_TEST(ArrayBufferValue, V8TEST_ARRAY_BUFFER_VALUE)
|
||||
V8_TEST(ObjectCreate, V8TEST_OBJECT_CREATE)
|
||||
V8_TEST(ObjectUserData, V8TEST_OBJECT_USERDATA)
|
||||
|
@ -44,12 +44,12 @@ const char* kStringValue = "My string value";
|
||||
void TestBinary(CefRefPtr<CefBinaryValue> value, char* data, size_t data_size) {
|
||||
// Testing requires strings longer than 15 characters.
|
||||
EXPECT_GT(data_size, 15U);
|
||||
|
||||
EXPECT_EQ(data_size, value->GetSize());
|
||||
|
||||
char* buff = new char[data_size + 1];
|
||||
char old_char;
|
||||
// Test direct access.
|
||||
EXPECT_EQ(memcmp(value->GetRawData(), data, data_size), 0);
|
||||
|
||||
char* buff = new char[data_size + 1];
|
||||
// Test full read.
|
||||
memset(buff, 0, data_size + 1);
|
||||
EXPECT_EQ(data_size, value->GetData(buff, data_size, 0));
|
||||
@ -57,7 +57,7 @@ void TestBinary(CefRefPtr<CefBinaryValue> value, char* data, size_t data_size) {
|
||||
|
||||
// Test partial read with offset.
|
||||
memset(buff, 0, data_size + 1);
|
||||
old_char = data[15];
|
||||
char old_char = data[15];
|
||||
data[15] = 0;
|
||||
EXPECT_EQ(10U, value->GetData(buff, 10, 5));
|
||||
EXPECT_TRUE(!strcmp(buff, data + 5));
|
||||
|
Loading…
x
Reference in New Issue
Block a user