Enable V8 sandbox by default (fixes #3332)

When the V8 sandbox is enabled, ArrayBuffer backing stores must be
allocated inside the sandbox address space. This change introduces a new
CefV8Value::CreateArrayBufferWithCopy method that copies the memory
contents into the sandbox address space.

Enabling the V8 sandbox can have a performance impact, especially when
passing large ArrayBuffers from C++ code to the JS side. We have therefore
retained the old CefV8Value::CreateArrayBuffer method that references
external memory. However, this method can only be used if the V8 sandbox is
disabled at CEF/Chromium build time.

To disable the V8 sandbox add `v8_enable_sandbox=false` to
`GN_DEFINES` when building CEF/Chromium.
This commit is contained in:
Nik Pavlov 2024-08-05 16:00:58 +00:00 committed by Marshall Greenblatt
parent 08ae3a44a6
commit 295ea1f715
15 changed files with 264 additions and 72 deletions

View File

@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for // by hand. See the translator.README.txt file in the tools directory for
// more information. // more information.
// //
// $hash=5dd4948a92af2ad69e2171f2dffb8f2c23e5c147$ // $hash=0b56c483bee6489e591c54c9dbb0940cd3098eaa$
// //
#ifndef CEF_INCLUDE_CAPI_CEF_V8_CAPI_H_ #ifndef CEF_INCLUDE_CAPI_CEF_V8_CAPI_H_
@ -832,11 +832,24 @@ CEF_EXPORT cef_v8value_t* cef_v8value_create_array(int length);
/// cef_v8handler_t or cef_v8accessor_t callback, or in combination with calling /// cef_v8handler_t or cef_v8accessor_t callback, or in combination with calling
/// enter() and exit() on a stored cef_v8context_t reference. /// enter() and exit() on a stored cef_v8context_t reference.
/// ///
/// NOTE: Always returns nullptr when V8 sandbox is enabled.
///
CEF_EXPORT cef_v8value_t* cef_v8value_create_array_buffer( CEF_EXPORT cef_v8value_t* cef_v8value_create_array_buffer(
void* buffer, void* buffer,
size_t length, size_t length,
cef_v8array_buffer_release_callback_t* release_callback); cef_v8array_buffer_release_callback_t* release_callback);
///
/// Create a new cef_v8value_t object of type ArrayBuffer which copies the
/// provided |buffer| of size |length| bytes. This function should only be
/// called from within the scope of a cef_render_process_handler_t,
/// cef_v8handler_t or cef_v8accessor_t callback, or in combination with calling
/// enter() and exit() on a stored cef_v8context_t reference.
///
CEF_EXPORT cef_v8value_t* cef_v8value_create_array_buffer_with_copy(
void* buffer,
size_t length);
/// ///
/// Create a new cef_v8value_t object of type function. This function should /// Create a new cef_v8value_t object of type function. This function should
/// only be called from within the scope of a cef_render_process_handler_t, /// only be called from within the scope of a cef_render_process_handler_t,

View File

@ -42,13 +42,13 @@
// way that may cause binary incompatibility with other builds. The universal // way that may cause binary incompatibility with other builds. The universal
// hash value will change if any platform is affected whereas the platform hash // hash value will change if any platform is affected whereas the platform hash
// values will change only if that particular platform is affected. // values will change only if that particular platform is affected.
#define CEF_API_HASH_UNIVERSAL "316cc23ff49e0d0962090cbfb0a0279ce3dc3c50" #define CEF_API_HASH_UNIVERSAL "80fd8337eb375cb48d617cd7cf01b531ea577efc"
#if defined(OS_WIN) #if defined(OS_WIN)
#define CEF_API_HASH_PLATFORM "66c126d91698670af3835a707a84ce4dbb4a16fa" #define CEF_API_HASH_PLATFORM "81d92ace6598de3106b212d70454022678fb2e2b"
#elif defined(OS_MAC) #elif defined(OS_MAC)
#define CEF_API_HASH_PLATFORM "c1d8d20920c3a3e13a6a6efef51b2b775f69d2c7" #define CEF_API_HASH_PLATFORM "c7b1f631ac53d876ea88f60a7f768b8f1ad1f0d8"
#elif defined(OS_LINUX) #elif defined(OS_LINUX)
#define CEF_API_HASH_PLATFORM "7ccfa4c608c16a4f8bedc97a2bdf50729784c5ee" #define CEF_API_HASH_PLATFORM "8dc9b5f33e800f9ac83253d6c7d714952ba8fa85"
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -520,12 +520,26 @@ class CefV8Value : public virtual CefBaseRefCounted {
/// or CefV8Accessor callback, or in combination with calling Enter() and /// or CefV8Accessor callback, or in combination with calling Enter() and
/// Exit() on a stored CefV8Context reference. /// Exit() on a stored CefV8Context reference.
/// ///
/// NOTE: Always returns nullptr when V8 sandbox is enabled.
///
/*--cef(optional_param=buffer)--*/ /*--cef(optional_param=buffer)--*/
static CefRefPtr<CefV8Value> CreateArrayBuffer( static CefRefPtr<CefV8Value> CreateArrayBuffer(
void* buffer, void* buffer,
size_t length, size_t length,
CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback); CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback);
///
/// Create a new CefV8Value object of type ArrayBuffer which copies the
/// provided |buffer| of size |length| bytes.
/// This method should only be called from within the scope of a
/// CefRenderProcessHandler, CefV8Handler or CefV8Accessor callback, or in
/// combination with calling Enter() and Exit() on a stored CefV8Context
/// reference.
///
/*--cef(optional_param=buffer)--*/
static CefRefPtr<CefV8Value> CreateArrayBufferWithCopy(void* buffer,
size_t length);
/// ///
/// Create a new CefV8Value object of type function. This method should only /// Create a new CefV8Value object of type function. This method should only
/// be called from within the scope of a CefRenderProcessHandler, CefV8Handler /// be called from within the scope of a CefRenderProcessHandler, CefV8Handler

View File

@ -1412,6 +1412,7 @@ CefRefPtr<CefV8Value> CefV8Value::CreateArrayBuffer(
void* buffer, void* buffer,
size_t length, size_t length,
CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback) { CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback) {
#ifndef V8_ENABLE_SANDBOX
CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
v8::Isolate* isolate = CefV8IsolateManager::Get()->isolate(); v8::Isolate* isolate = CefV8IsolateManager::Get()->isolate();
@ -1451,6 +1452,46 @@ CefRefPtr<CefV8Value> CefV8Value::CreateArrayBuffer(
CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate); CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate);
impl->InitObject(ab, tracker); impl->InitObject(ab, tracker);
return impl.get(); return impl.get();
#else
LOG(ERROR)
<< "CefV8Value::CreateArrayBuffer is not supported with the V8 "
"sandbox enabled, use CefV8Value::CreateArrayBufferWithCopy instead";
return nullptr;
#endif // V8_ENABLE_SANDBOX
}
// static
CefRefPtr<CefV8Value> CefV8Value::CreateArrayBufferWithCopy(void* buffer,
size_t length) {
CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr);
v8::Isolate* isolate = CefV8IsolateManager::Get()->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 nullptr;
}
// Create a tracker object that will cause the user data reference to be
// released when the V8 object is destroyed.
V8TrackObject* tracker = new V8TrackObject(isolate);
v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(
isolate, length, v8::BackingStoreInitializationMode::kUninitialized);
if (length > 0) {
DCHECK(ab->Data());
DCHECK(buffer);
memcpy(ab->Data(), buffer, length);
}
// Attach the tracker object.
tracker->AttachTo(context, ab);
CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate);
impl->InitObject(ab, tracker);
return impl;
} }
// static // static

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=c59808566c2a9f2d204b6724bb5a905aeb0e7620$ // $hash=befb2f29af8a0e8eabf745fad126ebad5a741c74$
// //
#include "libcef_dll/cpptoc/v8value_cpptoc.h" #include "libcef_dll/cpptoc/v8value_cpptoc.h"
@ -155,6 +155,21 @@ CEF_EXPORT cef_v8value_t* cef_v8value_create_array_buffer(
return CefV8ValueCppToC::Wrap(_retval); return CefV8ValueCppToC::Wrap(_retval);
} }
CEF_EXPORT cef_v8value_t* cef_v8value_create_array_buffer_with_copy(
void* buffer,
size_t length) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Unverified params: buffer
// Execute
CefRefPtr<CefV8Value> _retval =
CefV8Value::CreateArrayBufferWithCopy(buffer, length);
// Return type: refptr_same
return CefV8ValueCppToC::Wrap(_retval);
}
CEF_EXPORT cef_v8value_t* cef_v8value_create_function( CEF_EXPORT cef_v8value_t* cef_v8value_create_function(
const cef_string_t* name, const cef_string_t* name,
cef_v8handler_t* handler) { cef_v8handler_t* handler) {

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=856fc6c190d0e3376824564155618e468764e841$ // $hash=df9571f843ed0e55e581dc6282079803e3641d2c$
// //
#include "libcef_dll/ctocpp/v8value_ctocpp.h" #include "libcef_dll/ctocpp/v8value_ctocpp.h"
@ -164,6 +164,21 @@ CefRefPtr<CefV8Value> CefV8Value::CreateArrayBuffer(
return CefV8ValueCToCpp::Wrap(_retval); return CefV8ValueCToCpp::Wrap(_retval);
} }
NO_SANITIZE("cfi-icall")
CefRefPtr<CefV8Value> CefV8Value::CreateArrayBufferWithCopy(void* buffer,
size_t length) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Unverified params: buffer
// Execute
cef_v8value_t* _retval =
cef_v8value_create_array_buffer_with_copy(buffer, length);
// Return type: refptr_same
return CefV8ValueCToCpp::Wrap(_retval);
}
NO_SANITIZE("cfi-icall") NO_SANITIZE("cfi-icall")
CefRefPtr<CefV8Value> CefV8Value::CreateFunction( CefRefPtr<CefV8Value> CefV8Value::CreateFunction(
const CefString& name, const CefString& name,

View File

@ -1070,11 +1070,15 @@ class CefMessageRouterRendererSideImpl : public CefMessageRouterRendererSide {
CefRefPtr<CefV8Context> context = GetContextByID(context_id); CefRefPtr<CefV8Context> context = GetContextByID(context_id);
if (context && info->success_callback && context->Enter()) { if (context && info->success_callback && context->Enter()) {
CefRefPtr<cmru::BinaryValueABRCallback> release_callback = CefRefPtr<CefV8Value> value;
new cmru::BinaryValueABRCallback(response); #ifdef CEF_V8_ENABLE_SANDBOX
value = CefV8Value::CreateArrayBufferWithCopy(response->GetData(),
CefRefPtr<CefV8Value> value = CefV8Value::CreateArrayBuffer( response->GetSize());
response->GetData(), response->GetSize(), release_callback); #else
value = CefV8Value::CreateArrayBuffer(
response->GetData(), response->GetSize(),
new cmru::BinaryValueABRCallback(response));
#endif
context->Exit(); context->Exit();

View File

@ -48,6 +48,7 @@ struct RendererMessage {
std::variant<CefString, CefRefPtr<const CefBinaryBuffer>> payload; std::variant<CefString, CefRefPtr<const CefBinaryBuffer>> payload;
}; };
#ifndef CEF_V8_ENABLE_SANDBOX
class BinaryValueABRCallback final : public CefV8ArrayBufferReleaseCallback { class BinaryValueABRCallback final : public CefV8ArrayBufferReleaseCallback {
public: public:
explicit BinaryValueABRCallback(CefRefPtr<CefBinaryBuffer> value) explicit BinaryValueABRCallback(CefRefPtr<CefBinaryBuffer> value)
@ -62,6 +63,7 @@ class BinaryValueABRCallback final : public CefV8ArrayBufferReleaseCallback {
IMPLEMENT_REFCOUNTING(BinaryValueABRCallback); IMPLEMENT_REFCOUNTING(BinaryValueABRCallback);
}; };
#endif
CefRefPtr<BrowserResponseBuilder> CreateBrowserResponseBuilder( CefRefPtr<BrowserResponseBuilder> CreateBrowserResponseBuilder(
size_t threshold, size_t threshold,

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory // implementations. See the translator.README.txt file in the tools directory
// for more information. // for more information.
// //
// $hash=fd61a77bd549fb94bba963f9c0737ebceac324ac$ // $hash=85864cf7616899c4d51fbaf995d8e0db55249bd7$
// //
#include <dlfcn.h> #include <dlfcn.h>
@ -209,6 +209,8 @@ struct libcef_pointers {
decltype(&cef_v8value_create_object) cef_v8value_create_object; decltype(&cef_v8value_create_object) cef_v8value_create_object;
decltype(&cef_v8value_create_array) cef_v8value_create_array; decltype(&cef_v8value_create_array) cef_v8value_create_array;
decltype(&cef_v8value_create_array_buffer) cef_v8value_create_array_buffer; decltype(&cef_v8value_create_array_buffer) cef_v8value_create_array_buffer;
decltype(&cef_v8value_create_array_buffer_with_copy)
cef_v8value_create_array_buffer_with_copy;
decltype(&cef_v8value_create_function) cef_v8value_create_function; decltype(&cef_v8value_create_function) cef_v8value_create_function;
decltype(&cef_v8value_create_promise) cef_v8value_create_promise; decltype(&cef_v8value_create_promise) cef_v8value_create_promise;
decltype(&cef_v8stack_trace_get_current) cef_v8stack_trace_get_current; decltype(&cef_v8stack_trace_get_current) cef_v8stack_trace_get_current;
@ -439,6 +441,7 @@ int libcef_init_pointers(const char* path) {
INIT_ENTRY(cef_v8value_create_object); INIT_ENTRY(cef_v8value_create_object);
INIT_ENTRY(cef_v8value_create_array); INIT_ENTRY(cef_v8value_create_array);
INIT_ENTRY(cef_v8value_create_array_buffer); INIT_ENTRY(cef_v8value_create_array_buffer);
INIT_ENTRY(cef_v8value_create_array_buffer_with_copy);
INIT_ENTRY(cef_v8value_create_function); INIT_ENTRY(cef_v8value_create_function);
INIT_ENTRY(cef_v8value_create_promise); INIT_ENTRY(cef_v8value_create_promise);
INIT_ENTRY(cef_v8stack_trace_get_current); INIT_ENTRY(cef_v8stack_trace_get_current);
@ -1145,6 +1148,14 @@ struct _cef_v8value_t* cef_v8value_create_array_buffer(
release_callback); release_callback);
} }
NO_SANITIZE("cfi-icall")
struct _cef_v8value_t* cef_v8value_create_array_buffer_with_copy(
void* buffer,
size_t length) {
return g_libcef_pointers.cef_v8value_create_array_buffer_with_copy(buffer,
length);
}
NO_SANITIZE("cfi-icall") NO_SANITIZE("cfi-icall")
struct _cef_v8value_t* cef_v8value_create_function( struct _cef_v8value_t* cef_v8value_create_function(
const cef_string_t* name, const cef_string_t* name,

View File

@ -322,6 +322,7 @@ PERF_TEST_FUNC(V8ObjectGetValueWithAccessor) {
PERF_ITERATIONS_END() PERF_ITERATIONS_END()
} }
#ifndef CEF_V8_ENABLE_SANDBOX
PERF_TEST_FUNC(V8ArrayBufferCreate) { PERF_TEST_FUNC(V8ArrayBufferCreate) {
class ReleaseCallback : public CefV8ArrayBufferReleaseCallback { class ReleaseCallback : public CefV8ArrayBufferReleaseCallback {
public: public:
@ -339,6 +340,17 @@ PERF_TEST_FUNC(V8ArrayBufferCreate) {
CefV8Value::CreateArrayBuffer(buffer, byte_len, callback); CefV8Value::CreateArrayBuffer(buffer, byte_len, callback);
PERF_ITERATIONS_END() PERF_ITERATIONS_END()
} }
#endif // CEF_V8_ENABLE_SANDBOX
PERF_TEST_FUNC(V8ArrayBufferCopy) {
constexpr size_t len = 1;
constexpr size_t byte_len = len * sizeof(float);
std::array<float, len> buffer = {0};
PERF_ITERATIONS_START()
CefRefPtr<CefV8Value> ret =
CefV8Value::CreateArrayBufferWithCopy(buffer.data(), byte_len);
PERF_ITERATIONS_END()
}
PERF_TEST_FUNC(V8ContextEnterExit) { PERF_TEST_FUNC(V8ContextEnterExit) {
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext(); CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
@ -385,7 +397,10 @@ const PerfTestEntry kPerfTests[] = {
PERF_TEST_ENTRY(V8ObjectGetValue), PERF_TEST_ENTRY(V8ObjectGetValue),
PERF_TEST_ENTRY(V8ObjectSetValueWithAccessor), PERF_TEST_ENTRY(V8ObjectSetValueWithAccessor),
PERF_TEST_ENTRY(V8ObjectGetValueWithAccessor), PERF_TEST_ENTRY(V8ObjectGetValueWithAccessor),
#ifndef CEF_V8_ENABLE_SANDBOX
PERF_TEST_ENTRY(V8ArrayBufferCreate), PERF_TEST_ENTRY(V8ArrayBufferCreate),
#endif // CEF_V8_ENABLE_SANDBOX
PERF_TEST_ENTRY(V8ArrayBufferCopy),
PERF_TEST_ENTRY(V8ContextEnterExit), PERF_TEST_ENTRY(V8ContextEnterExit),
PERF_TEST_ENTRY(V8ContextEval), PERF_TEST_ENTRY(V8ContextEval),
}; };

View File

@ -3,11 +3,12 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Binary vs String Transfer Benchmark</title> <title>Binary vs String Transfer Benchmark</title>
<script src="https://cdn.plot.ly/plotly-2.26.0.min.js"></script> <script src="https://cdn.plot.ly/plotly-2.34.0.min.js"></script>
<style> <style>
body { body {
font-family: Tahoma, Serif; font-family: Tahoma, Serif;
font-size: 10pt; font-size: 10pt;
background-color: white;
} }
.info { .info {
font-size: 12pt; font-size: 12pt;
@ -116,7 +117,9 @@
<div id="round_trip_avg_chart"> <div id="round_trip_avg_chart">
<!-- Average round trip linear chart will be drawn inside this DIV --> <!-- Average round trip linear chart will be drawn inside this DIV -->
</div> </div>
<div id="round_trip_chart">
<!-- Round trip linear chart will be drawn inside this DIV -->
</div>
<div id="box_plot_chart"> <div id="box_plot_chart">
<!-- Box plot of round trip time will be drawn inside this DIV --> <!-- Box plot of round trip time will be drawn inside this DIV -->
</div> </div>
@ -125,13 +128,14 @@
let tests = []; let tests = [];
let box_plot_test_data = []; let box_plot_test_data = [];
let round_trip_avg_plot_data = []; let round_trip_avg_plot_data = [];
let round_trip_plot_data = [];
function nextTestSuite(testIndex) { function nextTestSuite(testIndex) {
const nextTestIndex = testIndex + 1; const nextTestIndex = testIndex + 1;
setTimeout(execTestSuite, 0, nextTestIndex); setTimeout(execTestSuite, 0, nextTestIndex);
} }
function generateRandomString(size) { function generateString(size) {
// Symbols that will be encoded as two bytes in UTF-8 // Symbols that will be encoded as two bytes in UTF-8
// so we compare transfer of the same amount of bytes // so we compare transfer of the same amount of bytes
const characters = const characters =
@ -144,7 +148,7 @@
return randomString; return randomString;
} }
function generateRandomArrayBuffer(size) { function generateArrayBuffer(size) {
const buffer = new ArrayBuffer(size); const buffer = new ArrayBuffer(size);
const uint8Array = new Uint8Array(buffer); const uint8Array = new Uint8Array(buffer);
for (let i = 0; i < uint8Array.length; i++) { for (let i = 0; i < uint8Array.length; i++) {
@ -157,8 +161,7 @@
console.error(`ErrorCode:${errorCode} Message:${errorMessage}`); console.error(`ErrorCode:${errorCode} Message:${errorMessage}`);
} }
function sendString(size, testIndex) { function sendString(request, testIndex) {
const request = generateRandomString(size);
const startTime = performance.now(); const startTime = performance.now();
const onSuccess = (response) => { const onSuccess = (response) => {
const roundTrip = performance.now() - startTime; const roundTrip = performance.now() - startTime;
@ -166,6 +169,8 @@
test.totalRoundTrip += roundTrip; test.totalRoundTrip += roundTrip;
test.sample++; test.sample++;
box_plot_test_data[testIndex].x.push(roundTrip); box_plot_test_data[testIndex].x.push(roundTrip);
round_trip_plot_data[testIndex].x.push(test.sample);
round_trip_plot_data[testIndex].y.push(roundTrip);
setTimeout(execTest, 0, testIndex); setTimeout(execTest, 0, testIndex);
}; };
@ -176,8 +181,7 @@
}); });
} }
function sendArrayBuffer(size, testIndex) { function sendArrayBuffer(request, testIndex) {
const request = generateRandomArrayBuffer(size);
const startTime = performance.now(); const startTime = performance.now();
const onSuccess = (response) => { const onSuccess = (response) => {
const roundTrip = performance.now() - startTime; const roundTrip = performance.now() - startTime;
@ -185,6 +189,8 @@
test.totalRoundTrip += roundTrip; test.totalRoundTrip += roundTrip;
test.sample++; test.sample++;
box_plot_test_data[testIndex].x.push(roundTrip); box_plot_test_data[testIndex].x.push(roundTrip);
round_trip_plot_data[testIndex].x.push(test.sample);
round_trip_plot_data[testIndex].y.push(roundTrip);
setTimeout(execTest, 0, testIndex); setTimeout(execTest, 0, testIndex);
}; };
@ -209,7 +215,7 @@
if (test.sample >= test.totalSamples) { if (test.sample >= test.totalSamples) {
return nextTestSuite(testIndex); return nextTestSuite(testIndex);
} }
test.func(test.length, test.index); test.func(test.request, test.index);
} }
function column(prepared, value) { function column(prepared, value) {
@ -279,14 +285,14 @@
function buildTestResults(tests) { function buildTestResults(tests) {
testResults = []; testResults = [];
let oldRoundTrip = { let stringRoundTrip = {
x: [], x: [],
y: [], y: [],
type: "scatter", type: "scatter",
name: "String", name: "String",
}; };
let newRoundTrip = { let binaryRoundTrip = {
x: [], x: [],
y: [], y: [],
type: "scatter", type: "scatter",
@ -332,13 +338,13 @@
stdDeviationBinary: stdDeviationBinary, stdDeviationBinary: stdDeviationBinary,
}); });
oldRoundTrip.x.push(test.byteSize); stringRoundTrip.x.push(test.byteSize);
newRoundTrip.x.push(test.byteSize); binaryRoundTrip.x.push(test.byteSize);
oldRoundTrip.y.push(avgRoundTrip); stringRoundTrip.y.push(avgRoundTrip);
newRoundTrip.y.push(avgRoundTripBin); binaryRoundTrip.y.push(avgRoundTripBin);
} }
round_trip_avg_plot_data = [oldRoundTrip, newRoundTrip]; round_trip_avg_plot_data = [stringRoundTrip, binaryRoundTrip];
return testResults; return testResults;
} }
@ -374,17 +380,21 @@
box_plot_test_data.forEach((data) => { box_plot_test_data.forEach((data) => {
data.x = []; data.x = [];
}); });
round_trip_plot_data.forEach((data) => {
data.x = [];
data.y = [];
});
} }
function queueTest(name, byteSize, length, testFunc) { function queueTest(name, byteSize, request, testFunc) {
const testIndex = tests.length; const testIndex = tests.length;
test = { test = {
name: name, name: name,
byteSize: byteSize, byteSize: byteSize,
length: length,
index: testIndex, index: testIndex,
sample: 0, sample: 0,
totalRoundTrip: 0, totalRoundTrip: 0,
request: request,
func: testFunc, func: testFunc,
}; };
tests.push(test); tests.push(test);
@ -397,11 +407,18 @@
jitter: 0.3, jitter: 0.3,
pointpos: -1.8, pointpos: -1.8,
}); });
round_trip_plot_data.push({
x: [],
y: [],
type: "scatter",
name: name,
});
} }
function execTestSuite(testIndex) { function execTestSuite(testIndex) {
if (testIndex < tests.length) { if (testIndex < tests.length) {
execTest(testIndex); setTimeout(execTest, 0, testIndex);
} else { } else {
testsRunFinished(); testsRunFinished();
} }
@ -426,19 +443,16 @@
testResults = buildTestResults(tests); testResults = buildTestResults(tests);
testResults.forEach((result) => displayResult(result)); testResults.forEach((result) => displayResult(result));
const round_trip_layout = { Plotly.newPlot("round_trip_avg_chart", round_trip_avg_plot_data, {
title: "Average round trip, μs (Smaller Better)", title: "Average round trip, μs (Smaller Better)",
}; });
Plotly.newPlot( Plotly.newPlot("round_trip_chart", round_trip_plot_data, {
"round_trip_avg_chart", title: "Linear: Round Trip Time, μs",
round_trip_avg_plot_data, });
round_trip_layout Plotly.newPlot("box_plot_chart", box_plot_test_data, {
); title: "Box plot: Round Trip Time, μs",
});
const box_plot_layout = {
title: "Round Trip Time, μs",
};
Plotly.newPlot("box_plot_chart", box_plot_test_data, box_plot_layout);
setSettingsState(false); setSettingsState(false);
} }
@ -466,6 +480,7 @@
window.runTestSuite = () => { window.runTestSuite = () => {
Plotly.purge("round_trip_avg_chart"); Plotly.purge("round_trip_avg_chart");
Plotly.purge("box_plot_chart"); Plotly.purge("box_plot_chart");
Plotly.purge("round_trip_chart");
setSettingsState(true); setSettingsState(true);
const totalSamples = parseInt( const totalSamples = parseInt(
document.getElementById("sSamples").value document.getElementById("sSamples").value
@ -474,21 +489,22 @@
}; };
const totalSamples = parseInt(document.getElementById("sSamples").value); const totalSamples = parseInt(document.getElementById("sSamples").value);
queueTest("Empty String", 0, 0, sendString);
queueTest("Empty Binary", 0, 0, sendArrayBuffer); queueTest("Empty String", 0, generateString(0), sendString);
for (let byteSize = 8; byteSize <= 512 * 1024; byteSize *= 2) { queueTest("Empty Binary", 0, generateArrayBuffer(0), sendArrayBuffer);
for (let byteSize = 8; byteSize <= 512 * 1024; byteSize *= 4) {
// Byte size of a string is twice its length because of UTF-16 encoding // Byte size of a string is twice its length because of UTF-16 encoding
const stringLen = byteSize / 2; const stringLen = byteSize / 2;
queueTest( queueTest(
humanFileSize(byteSize) + " String", humanFileSize(byteSize) + " String",
byteSize, byteSize,
stringLen, generateString(stringLen),
sendString sendString
); );
queueTest( queueTest(
humanFileSize(byteSize) + " Binary", humanFileSize(byteSize) + " Binary",
byteSize, byteSize,
byteSize, generateArrayBuffer(byteSize),
sendArrayBuffer sendArrayBuffer
); );
} }

View File

@ -8,6 +8,7 @@
body { body {
font-family: Tahoma, Serif; font-family: Tahoma, Serif;
font-size: 10pt; font-size: 10pt;
background-color: white;
} }
.left { .left {

View File

@ -58,9 +58,12 @@ enum V8TestMode {
V8TEST_EMPTY_STRING_CREATE, V8TEST_EMPTY_STRING_CREATE,
V8TEST_ARRAY_CREATE, V8TEST_ARRAY_CREATE,
V8TEST_ARRAY_VALUE, V8TEST_ARRAY_VALUE,
#ifndef CEF_V8_ENABLE_SANDBOX
V8TEST_ARRAY_BUFFER, V8TEST_ARRAY_BUFFER,
V8TEST_ARRAY_BUFFER_CREATE_EMPTY,
V8TEST_ARRAY_BUFFER_VALUE, V8TEST_ARRAY_BUFFER_VALUE,
#endif // CEF_V8_ENABLE_SANDBOX
V8TEST_ARRAY_BUFFER_CREATE_EMPTY,
V8TEST_ARRAY_BUFFER_COPY,
V8TEST_OBJECT_CREATE, V8TEST_OBJECT_CREATE,
V8TEST_OBJECT_USERDATA, V8TEST_OBJECT_USERDATA,
V8TEST_OBJECT_ACCESSOR, V8TEST_OBJECT_ACCESSOR,
@ -149,14 +152,19 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
case V8TEST_ARRAY_VALUE: case V8TEST_ARRAY_VALUE:
RunArrayValueTest(); RunArrayValueTest();
break; break;
#ifndef CEF_V8_ENABLE_SANDBOX
case V8TEST_ARRAY_BUFFER: case V8TEST_ARRAY_BUFFER:
RunArrayBufferTest(); RunArrayBufferTest();
break; break;
case V8TEST_ARRAY_BUFFER_VALUE:
RunArrayBufferValueTest();
break;
#endif // CEF_V8_ENABLE_SANDBOX
case V8TEST_ARRAY_BUFFER_CREATE_EMPTY: case V8TEST_ARRAY_BUFFER_CREATE_EMPTY:
RunArrayBufferCreateEmptyTest(); RunArrayBufferCreateEmptyTest();
break; break;
case V8TEST_ARRAY_BUFFER_VALUE: case V8TEST_ARRAY_BUFFER_COPY:
RunArrayBufferValueTest(); RunArrayBufferCopyTest();
break; break;
case V8TEST_OBJECT_CREATE: case V8TEST_OBJECT_CREATE:
RunObjectCreateTest(); RunObjectCreateTest();
@ -612,6 +620,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
DestroyTest(); DestroyTest();
} }
#ifndef CEF_V8_ENABLE_SANDBOX
void RunArrayBufferTest() { void RunArrayBufferTest() {
class TestArrayBufferReleaseCallback class TestArrayBufferReleaseCallback
: public CefV8ArrayBufferReleaseCallback { : public CefV8ArrayBufferReleaseCallback {
@ -654,10 +663,11 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
void* data = value->GetArrayBufferData(); void* data = value->GetArrayBufferData();
EXPECT_EQ(static_cast<int*>(data), static_data); EXPECT_EQ(static_cast<int*>(data), static_data);
EXPECT_FALSE(value->HasValue(0)); EXPECT_FALSE(value->HasValue(0));
EXPECT_TRUE(value->GetArrayBufferReleaseCallback().get() != nullptr); EXPECT_NE(value->GetArrayBufferReleaseCallback().get(), nullptr);
EXPECT_TRUE(((TestArrayBufferReleaseCallback*)value EXPECT_EQ((TestArrayBufferReleaseCallback*)value
->GetArrayBufferReleaseCallback() ->GetArrayBufferReleaseCallback()
.get()) == release_callback); .get(),
release_callback);
// |Value| buffer is explicitly freed by NeuterArrayBuffer(). // |Value| buffer is explicitly freed by NeuterArrayBuffer().
EXPECT_FALSE(destructorCalled); EXPECT_FALSE(destructorCalled);
@ -670,7 +680,33 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
EXPECT_TRUE(context->Exit()); EXPECT_TRUE(context->Exit());
DestroyTest(); DestroyTest();
} }
#endif // CEF_V8_ENABLE_SANDBOX
void RunArrayBufferCopyTest() {
CefRefPtr<CefV8Context> context = GetContext();
// Enter the V8 context.
EXPECT_TRUE(context->Enter());
{
int static_data[16] = {1, 2, 3, 4};
CefRefPtr<CefV8Value> value = CefV8Value::CreateArrayBufferWithCopy(
static_data, sizeof(static_data));
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)[0], static_data[0]);
EXPECT_EQ(static_cast<int*>(data)[1], static_data[1]);
EXPECT_FALSE(value->HasValue(0));
}
// Exit the V8 context.
EXPECT_TRUE(context->Exit());
DestroyTest();
}
#ifndef CEF_V8_ENABLE_SANDBOX
void RunArrayBufferValueTest() { void RunArrayBufferValueTest() {
class TestArrayBufferReleaseCallback class TestArrayBufferReleaseCallback
: public CefV8ArrayBufferReleaseCallback { : public CefV8ArrayBufferReleaseCallback {
@ -720,8 +756,17 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
EXPECT_TRUE(context->Exit()); EXPECT_TRUE(context->Exit());
DestroyTest(); DestroyTest();
} }
#endif // CEF_V8_ENABLE_SANDBOX
void RunArrayBufferCreateEmptyTest() { void RunArrayBufferCreateEmptyTest() {
// 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;
#ifndef CEF_V8_ENABLE_SANDBOX
class TestArrayBufferReleaseCallback class TestArrayBufferReleaseCallback
: public CefV8ArrayBufferReleaseCallback { : public CefV8ArrayBufferReleaseCallback {
public: public:
@ -733,22 +778,17 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
CefRefPtr<TestArrayBufferReleaseCallback> owner = CefRefPtr<TestArrayBufferReleaseCallback> owner =
new TestArrayBufferReleaseCallback(); new TestArrayBufferReleaseCallback();
// Enter the V8 context value = CefV8Value::CreateArrayBuffer(null_data, zero_size, owner);
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); EXPECT_EQ(value->GetArrayBufferData(), null_data);
EXPECT_NE(value->GetArrayBufferReleaseCallback().get(), nullptr);
#else
value = CefV8Value::CreateArrayBufferWithCopy(null_data, zero_size);
#endif
EXPECT_EQ(value->GetArrayBufferByteLength(), zero_size);
CefRefPtr<CefV8Value> object = context->GetGlobal(); CefRefPtr<CefV8Value> object = context->GetGlobal();
EXPECT_TRUE(object.get()); EXPECT_TRUE(object.get());
EXPECT_TRUE(object->SetValue("arr", value, V8_PROPERTY_ATTRIBUTE_NONE)); EXPECT_TRUE(object->SetValue("arr", value, V8_PROPERTY_ATTRIBUTE_NONE));
EXPECT_NE(value->GetArrayBufferReleaseCallback().get(), nullptr);
EXPECT_TRUE(value->NeuterArrayBuffer()); EXPECT_TRUE(value->NeuterArrayBuffer());
// Exit the V8 context. // Exit the V8 context.
@ -3413,9 +3453,12 @@ V8_TEST(StringCreate, V8TEST_STRING_CREATE)
V8_TEST(EmptyStringCreate, V8TEST_EMPTY_STRING_CREATE) V8_TEST(EmptyStringCreate, V8TEST_EMPTY_STRING_CREATE)
V8_TEST(ArrayCreate, V8TEST_ARRAY_CREATE) V8_TEST(ArrayCreate, V8TEST_ARRAY_CREATE)
V8_TEST(ArrayValue, V8TEST_ARRAY_VALUE) V8_TEST(ArrayValue, V8TEST_ARRAY_VALUE)
#ifndef CEF_V8_ENABLE_SANDBOX
V8_TEST(ArrayBuffer, V8TEST_ARRAY_BUFFER) V8_TEST(ArrayBuffer, V8TEST_ARRAY_BUFFER)
V8_TEST(ArrayBufferCreateEmpty, V8TEST_ARRAY_BUFFER_CREATE_EMPTY)
V8_TEST(ArrayBufferValue, V8TEST_ARRAY_BUFFER_VALUE) V8_TEST(ArrayBufferValue, V8TEST_ARRAY_BUFFER_VALUE)
#endif // CEF_V8_ENABLE_SANDBOX
V8_TEST(ArrayBufferCreateEmpty, V8TEST_ARRAY_BUFFER_CREATE_EMPTY)
V8_TEST(ArrayBufferCopy, V8TEST_ARRAY_BUFFER_COPY)
V8_TEST(ObjectCreate, V8TEST_OBJECT_CREATE) V8_TEST(ObjectCreate, V8TEST_OBJECT_CREATE)
V8_TEST(ObjectUserData, V8TEST_OBJECT_USERDATA) V8_TEST(ObjectUserData, V8TEST_OBJECT_USERDATA)
V8_TEST(ObjectAccessor, V8TEST_OBJECT_ACCESSOR) V8_TEST(ObjectAccessor, V8TEST_OBJECT_ACCESSOR)

View File

@ -228,11 +228,6 @@ def GetRecommendedDefaultArgs():
# "//chrome:chrome_dll" target, which will fail to build with CEF. # "//chrome:chrome_dll" target, which will fail to build with CEF.
'enable_resource_allowlist_generation': False, 'enable_resource_allowlist_generation': False,
# Disable V8 sandboxed pointers to avoid crashing when using
# CefV8Value::CreateArrayBuffer with memory allocated outside of the V8
# sandbox. See https://github.com/chromiumembedded/cef/issues/3332.
'v8_enable_sandbox': False,
# Disable downgrade processing/restart with the Chrome runtime. # Disable downgrade processing/restart with the Chrome runtime.
# https://github.com/chromiumembedded/cef/issues/3608 # https://github.com/chromiumembedded/cef/issues/3608
'enable_downgrade_processing': False, 'enable_downgrade_processing': False,

View File

@ -28,6 +28,13 @@ def make_config_header(gn_config):
not 'ozone_platform_x11=false' in lines: not 'ozone_platform_x11=false' in lines:
defines.append('#define CEF_X11 1') defines.append('#define CEF_X11 1')
# If the V8 sandbox is not disabled explicitly and
# the target_cpu is arm64 or x64 (see v8_enable_pointer_compression)
# add CEF_V8_ENABLE_SANDBOX define used in cef_message_router.h
if not 'v8_enable_sandbox=false' in lines and \
('target_cpu="arm64"' in lines or 'target_cpu="x64"' in lines):
defines.append('#define CEF_V8_ENABLE_SANDBOX 1')
result = get_copyright(full=True, translator=False) + \ result = get_copyright(full=True, translator=False) + \
"""// """//
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------