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