From fa643b269e133888d850d9bc5f95f2878aa6049a Mon Sep 17 00:00:00 2001 From: VodBox Date: Tue, 11 Oct 2022 14:54:32 -0400 Subject: [PATCH] Add support for native creation and resolution of Promises (fixes issue #3305) --- include/capi/cef_v8_capi.h | 36 +++- include/cef_api_hash.h | 8 +- include/cef_v8.h | 38 ++++ libcef/renderer/v8_impl.cc | 106 +++++++++ libcef/renderer/v8_impl.h | 4 + libcef_dll/cpptoc/v8value_cpptoc.cc | 66 +++++- libcef_dll/ctocpp/v8value_ctocpp.cc | 64 +++++- libcef_dll/ctocpp/v8value_ctocpp.h | 5 +- libcef_dll/wrapper/libcef_dll_dylib.cc | 8 +- tests/ceftests/v8_unittest.cc | 283 +++++++++++++++++++++++++ 10 files changed, 609 insertions(+), 9 deletions(-) diff --git a/include/capi/cef_v8_capi.h b/include/capi/cef_v8_capi.h index da3119a0b..cd57020e4 100644 --- a/include/capi/cef_v8_capi.h +++ b/include/capi/cef_v8_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=98f6d1c93609958fa457c15d7f6fef56fac7e3f6$ +// $hash=b8af0d090bcb54f99d98804f7e3aaa0eab24449a$ // #ifndef CEF_INCLUDE_CAPI_CEF_V8_CAPI_H_ @@ -454,6 +454,11 @@ typedef struct _cef_v8value_t { /// int(CEF_CALLBACK* is_function)(struct _cef_v8value_t* self); + /// + /// True if the value type is a Promise. + /// + int(CEF_CALLBACK* is_promise)(struct _cef_v8value_t* self); + /// /// Returns true (1) if this object is pointing to the same handle as |that| /// object. @@ -718,6 +723,27 @@ typedef struct _cef_v8value_t { struct _cef_v8value_t* object, size_t argumentsCount, struct _cef_v8value_t* const* arguments); + + /// + /// Resolve the Promise using the current V8 context. This function should + /// only be called from within the scope of a cef_v8handler_t or + /// cef_v8accessor_t callback, or in combination with calling enter() and + /// exit() on a stored cef_v8context_t reference. |arg| is the argument passed + /// to the resolved promise. Returns true (1) on success. Returns false (0) if + /// this function is called incorrectly or an exception is thrown. + /// + int(CEF_CALLBACK* resolve_promise)(struct _cef_v8value_t* self, + struct _cef_v8value_t* arg); + + /// + /// Reject the Promise using the current V8 context. This function should only + /// be called from within the scope of a cef_v8handler_t or cef_v8accessor_t + /// callback, or in combination with calling enter() and exit() on a stored + /// cef_v8context_t reference. Returns true (1) on success. Returns false (0) + /// if this function is called incorrectly or an exception is thrown. + /// + int(CEF_CALLBACK* reject_promise)(struct _cef_v8value_t* self, + const cef_string_t* errorMsg); } cef_v8value_t; /// @@ -808,6 +834,14 @@ CEF_EXPORT cef_v8value_t* cef_v8value_create_array_buffer( CEF_EXPORT cef_v8value_t* cef_v8value_create_function(const cef_string_t* name, cef_v8handler_t* handler); +/// +/// Create a new cef_v8value_t object of type Promise. 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_promise(void); + /// /// Structure representing a V8 stack trace handle. V8 handles can only be /// accessed from the thread on which they are created. Valid threads for diff --git a/include/cef_api_hash.h b/include/cef_api_hash.h index 0d00b405d..c977e0c60 100644 --- a/include/cef_api_hash.h +++ b/include/cef_api_hash.h @@ -42,13 +42,13 @@ // way that may cause binary incompatibility with other builds. The universal // hash value will change if any platform is affected whereas the platform hash // values will change only if that particular platform is affected. -#define CEF_API_HASH_UNIVERSAL "c06406b23dc4a845177dcd306541ce527d061364" +#define CEF_API_HASH_UNIVERSAL "dfec58669a2c79108d61246f4e523570c3a21f63" #if defined(OS_WIN) -#define CEF_API_HASH_PLATFORM "edd206c50f636a935872c9cc251ccb9448b9050e" +#define CEF_API_HASH_PLATFORM "bd5a6f54227d8bd35d33580342cd78f81d0cee2e" #elif defined(OS_MAC) -#define CEF_API_HASH_PLATFORM "a74e6429302d2d947cc614688e83cd3b29e74c17" +#define CEF_API_HASH_PLATFORM "11902ab39267c1048acfa5c41053c6cfe0bd6227" #elif defined(OS_LINUX) -#define CEF_API_HASH_PLATFORM "e119b68ec7e406ca74ddea5e244af7150eef7118" +#define CEF_API_HASH_PLATFORM "213fb79db129c756661e7cfbe6749c15276bbebf" #endif #ifdef __cplusplus diff --git a/include/cef_v8.h b/include/cef_v8.h index b2418f2a7..a8534da28 100644 --- a/include/cef_v8.h +++ b/include/cef_v8.h @@ -536,6 +536,15 @@ class CefV8Value : public virtual CefBaseRefCounted { static CefRefPtr CreateFunction(const CefString& name, CefRefPtr handler); + /// + /// Create a new CefV8Value object of type Promise. 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()--*/ + static CefRefPtr CreatePromise(); + /// /// Returns true if the underlying handle is valid and it can be accessed on /// the current thread. Do not call any other methods if this method returns @@ -616,6 +625,12 @@ class CefV8Value : public virtual CefBaseRefCounted { /*--cef()--*/ virtual bool IsFunction() = 0; + /// + /// True if the value type is a Promise. + /// + /*--cef()--*/ + virtual bool IsPromise() = 0; + /// /// Returns true if this object is pointing to the same handle as |that| /// object. @@ -893,6 +908,29 @@ class CefV8Value : public virtual CefBaseRefCounted { CefRefPtr context, CefRefPtr object, const CefV8ValueList& arguments) = 0; + + // PROMISE METHODS - These methods are only available on Promises. + + /// + /// Resolve the Promise using the current V8 context. This method should only + /// be called from within the scope of a CefV8Handler or CefV8Accessor + /// callback, or in combination with calling Enter() and Exit() on a stored + /// CefV8Context reference. |arg| is the argument passed to the resolved + /// promise. Returns true on success. Returns false if this method is called + /// incorrectly or an exception is thrown. + /// + /*--cef(optional_param=arg)--*/ + virtual bool ResolvePromise(CefRefPtr arg) = 0; + + /// + /// Reject the Promise using the current V8 context. This method should only + /// be called from within the scope of a CefV8Handler or CefV8Accessor + /// callback, or in combination with calling Enter() and Exit() on a stored + /// CefV8Context reference. Returns true on success. Returns false if this + /// method is called incorrectly or an exception is thrown. + /// + /*--cef()--*/ + virtual bool RejectPromise(const CefString& errorMsg) = 0; }; /// diff --git a/libcef/renderer/v8_impl.cc b/libcef/renderer/v8_impl.cc index eb579e086..e91a9133f 100644 --- a/libcef/renderer/v8_impl.cc +++ b/libcef/renderer/v8_impl.cc @@ -1480,6 +1480,33 @@ CefRefPtr CefV8Value::CreateFunction( return impl.get(); } +// static +CefRefPtr CefV8Value::CreatePromise() { + CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); + v8::Isolate* isolate = GetIsolateManager()->isolate(); + v8::HandleScope handle_scope(isolate); + + v8::Local context = isolate->GetCurrentContext(); + if (context.IsEmpty()) { + NOTREACHED() << "not currently in a V8 context"; + return nullptr; + } + + v8::Local promise_resolver = + v8::Promise::Resolver::New(context).ToLocalChecked(); + + // 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); + + // Attach the tracker object. + tracker->AttachTo(context, promise_resolver); + + CefRefPtr impl = new CefV8ValueImpl(isolate); + impl->InitObject(promise_resolver, tracker); + return impl.get(); +} + // CefV8ValueImpl CefV8ValueImpl::CefV8ValueImpl(v8::Isolate* isolate) @@ -1713,6 +1740,16 @@ bool CefV8ValueImpl::IsFunction() { } } +bool CefV8ValueImpl::IsPromise() { + CEF_V8_REQUIRE_MLT_RETURN(false); + if (type_ == TYPE_OBJECT) { + v8::HandleScope handle_scope(handle_->isolate()); + return handle_->GetNewV8Handle(false)->IsPromise(); + } else { + return false; + } +} + bool CefV8ValueImpl::IsSame(CefRefPtr that) { CEF_V8_REQUIRE_MLT_RETURN(false); @@ -2444,6 +2481,75 @@ CefRefPtr CefV8ValueImpl::ExecuteFunctionWithContext( return retval; } +bool CefV8ValueImpl::ResolvePromise(CefRefPtr arg) { + CEF_V8_REQUIRE_OBJECT_RETURN(false); + + v8::Isolate* isolate = handle_->isolate(); + v8::HandleScope handle_scope(isolate); + v8::Local value = handle_->GetNewV8Handle(false); + if (!value->IsPromise()) { + NOTREACHED() << "V8 value is not a Promise"; + return false; + } + + if (arg.get() && !arg->IsValid()) { + NOTREACHED() << "invalid V8 arg parameter"; + return false; + } + + v8::Local context_local = isolate->GetCurrentContext(); + + v8::Context::Scope context_scope(context_local); + + v8::Local obj = value->ToObject(context_local).ToLocalChecked(); + v8::Local promise = + v8::Local::Cast(obj); + + v8::TryCatch try_catch(isolate); + try_catch.SetVerbose(true); + + if (arg.get()) { + promise + ->Resolve(context_local, + static_cast(arg.get())->GetV8Value(true)) + .ToChecked(); + } else { + promise->Resolve(context_local, v8::Undefined(isolate)).ToChecked(); + } + + return !HasCaught(context_local, try_catch); +} + +bool CefV8ValueImpl::RejectPromise(const CefString& errorMsg) { + CEF_V8_REQUIRE_OBJECT_RETURN(false); + + v8::Isolate* isolate = handle_->isolate(); + v8::HandleScope handle_scope(isolate); + v8::Local value = handle_->GetNewV8Handle(false); + if (!value->IsPromise()) { + NOTREACHED() << "V8 value is not a Promise"; + return false; + } + + v8::Local context_local = isolate->GetCurrentContext(); + + v8::Context::Scope context_scope(context_local); + + v8::Local obj = value->ToObject(context_local).ToLocalChecked(); + v8::Local promise = + v8::Local::Cast(obj); + + v8::TryCatch try_catch(isolate); + try_catch.SetVerbose(true); + + promise + ->Reject(context_local, + v8::Exception::Error(GetV8String(isolate, errorMsg))) + .ToChecked(); + + return !HasCaught(context_local, try_catch); +} + bool CefV8ValueImpl::HasCaught(v8::Local context, v8::TryCatch& try_catch) { if (try_catch.HasCaught()) { diff --git a/libcef/renderer/v8_impl.h b/libcef/renderer/v8_impl.h index d2cf67435..63e62a77e 100644 --- a/libcef/renderer/v8_impl.h +++ b/libcef/renderer/v8_impl.h @@ -237,6 +237,7 @@ class CefV8ValueImpl : public CefV8Value { bool IsArray() override; bool IsArrayBuffer() override; bool IsFunction() override; + bool IsPromise() override; bool IsSame(CefRefPtr value) override; bool GetBoolValue() override; int32 GetIntValue() override; @@ -282,6 +283,9 @@ class CefV8ValueImpl : public CefV8Value { CefRefPtr object, const CefV8ValueList& arguments) override; + bool ResolvePromise(CefRefPtr arg) override; + bool RejectPromise(const CefString& errorMsg) override; + private: // Test for and record any exception. bool HasCaught(v8::Local context, v8::TryCatch& try_catch); diff --git a/libcef_dll/cpptoc/v8value_cpptoc.cc b/libcef_dll/cpptoc/v8value_cpptoc.cc index 2835130d6..a6fc84dbc 100644 --- a/libcef_dll/cpptoc/v8value_cpptoc.cc +++ b/libcef_dll/cpptoc/v8value_cpptoc.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=46a80d60441e386e6a8999ecb5fd338f3f6b4319$ +// $hash=5dd413b62070b7d80292faf8396d3b795dd4035e$ // #include "libcef_dll/cpptoc/v8value_cpptoc.h" @@ -178,6 +178,16 @@ CEF_EXPORT cef_v8value_t* cef_v8value_create_function( return CefV8ValueCppToC::Wrap(_retval); } +CEF_EXPORT cef_v8value_t* cef_v8value_create_promise() { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + CefRefPtr _retval = CefV8Value::CreatePromise(); + + // Return type: refptr_same + return CefV8ValueCppToC::Wrap(_retval); +} + namespace { // MEMBER FUNCTIONS - Body may be edited by hand. @@ -364,6 +374,20 @@ int CEF_CALLBACK v8value_is_function(struct _cef_v8value_t* self) { return _retval; } +int CEF_CALLBACK v8value_is_promise(struct _cef_v8value_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + + // Execute + bool _retval = CefV8ValueCppToC::Get(self)->IsPromise(); + + // Return type: bool + return _retval; +} + int CEF_CALLBACK v8value_is_same(struct _cef_v8value_t* self, struct _cef_v8value_t* that) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -974,6 +998,43 @@ v8value_execute_function_with_context(struct _cef_v8value_t* self, return CefV8ValueCppToC::Wrap(_retval); } +int CEF_CALLBACK v8value_resolve_promise(struct _cef_v8value_t* self, + struct _cef_v8value_t* arg) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + // Unverified params: arg + + // Execute + bool _retval = CefV8ValueCppToC::Get(self)->ResolvePromise( + CefV8ValueCppToC::Unwrap(arg)); + + // Return type: bool + return _retval; +} + +int CEF_CALLBACK v8value_reject_promise(struct _cef_v8value_t* self, + const cef_string_t* errorMsg) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + // Verify param: errorMsg; type: string_byref_const + DCHECK(errorMsg); + if (!errorMsg) + return 0; + + // Execute + bool _retval = + CefV8ValueCppToC::Get(self)->RejectPromise(CefString(errorMsg)); + + // Return type: bool + return _retval; +} + } // namespace // CONSTRUCTOR - Do not edit by hand. @@ -992,6 +1053,7 @@ CefV8ValueCppToC::CefV8ValueCppToC() { GetStruct()->is_array = v8value_is_array; GetStruct()->is_array_buffer = v8value_is_array_buffer; GetStruct()->is_function = v8value_is_function; + GetStruct()->is_promise = v8value_is_promise; GetStruct()->is_same = v8value_is_same; GetStruct()->get_bool_value = v8value_get_bool_value; GetStruct()->get_int_value = v8value_get_int_value; @@ -1030,6 +1092,8 @@ CefV8ValueCppToC::CefV8ValueCppToC() { GetStruct()->execute_function = v8value_execute_function; GetStruct()->execute_function_with_context = v8value_execute_function_with_context; + GetStruct()->resolve_promise = v8value_resolve_promise; + GetStruct()->reject_promise = v8value_reject_promise; } // DESTRUCTOR - Do not edit by hand. diff --git a/libcef_dll/ctocpp/v8value_ctocpp.cc b/libcef_dll/ctocpp/v8value_ctocpp.cc index aeeda972d..01a367208 100644 --- a/libcef_dll/ctocpp/v8value_ctocpp.cc +++ b/libcef_dll/ctocpp/v8value_ctocpp.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=1dd04012b442aa3ceae8d56653dc499aed1e181f$ +// $hash=17fe0420a1c56309ff2156c9d7f7a4def484bd15$ // #include "libcef_dll/ctocpp/v8value_ctocpp.h" @@ -188,6 +188,16 @@ CefRefPtr CefV8Value::CreateFunction( return CefV8ValueCToCpp::Wrap(_retval); } +NO_SANITIZE("cfi-icall") CefRefPtr CefV8Value::CreatePromise() { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + cef_v8value_t* _retval = cef_v8value_create_promise(); + + // Return type: refptr_same + return CefV8ValueCToCpp::Wrap(_retval); +} + // VIRTUAL METHODS - Body may be edited by hand. NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsValid() { @@ -372,6 +382,20 @@ NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsFunction() { return _retval ? true : false; } +NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsPromise() { + cef_v8value_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, is_promise)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = _struct->is_promise(_struct); + + // Return type: bool + return _retval ? true : false; +} + NO_SANITIZE("cfi-icall") bool CefV8ValueCToCpp::IsSame(CefRefPtr that) { cef_v8value_t* _struct = GetStruct(); @@ -988,6 +1012,44 @@ CefRefPtr CefV8ValueCToCpp::ExecuteFunctionWithContext( return CefV8ValueCToCpp::Wrap(_retval); } +NO_SANITIZE("cfi-icall") +bool CefV8ValueCToCpp::ResolvePromise(CefRefPtr arg) { + cef_v8value_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, resolve_promise)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Unverified params: arg + + // Execute + int _retval = + _struct->resolve_promise(_struct, CefV8ValueCToCpp::Unwrap(arg)); + + // Return type: bool + return _retval ? true : false; +} + +NO_SANITIZE("cfi-icall") +bool CefV8ValueCToCpp::RejectPromise(const CefString& errorMsg) { + cef_v8value_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, reject_promise)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: errorMsg; type: string_byref_const + DCHECK(!errorMsg.empty()); + if (errorMsg.empty()) + return false; + + // Execute + int _retval = _struct->reject_promise(_struct, errorMsg.GetStruct()); + + // Return type: bool + return _retval ? true : false; +} + // CONSTRUCTOR - Do not edit by hand. CefV8ValueCToCpp::CefV8ValueCToCpp() {} diff --git a/libcef_dll/ctocpp/v8value_ctocpp.h b/libcef_dll/ctocpp/v8value_ctocpp.h index b24d617a7..06e499008 100644 --- a/libcef_dll/ctocpp/v8value_ctocpp.h +++ b/libcef_dll/ctocpp/v8value_ctocpp.h @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=012bb9108d28ddefb6ebc58963be24469b79146f$ +// $hash=ee45dceec69f21be7a17e0c10ed15b29f28e63ec$ // #ifndef CEF_LIBCEF_DLL_CTOCPP_V8VALUE_CTOCPP_H_ @@ -47,6 +47,7 @@ class CefV8ValueCToCpp bool IsArray() override; bool IsArrayBuffer() override; bool IsFunction() override; + bool IsPromise() override; bool IsSame(CefRefPtr that) override; bool GetBoolValue() override; int32 GetIntValue() override; @@ -91,6 +92,8 @@ class CefV8ValueCToCpp CefRefPtr context, CefRefPtr object, const CefV8ValueList& arguments) override; + bool ResolvePromise(CefRefPtr arg) override; + bool RejectPromise(const CefString& errorMsg) override; }; #endif // CEF_LIBCEF_DLL_CTOCPP_V8VALUE_CTOCPP_H_ diff --git a/libcef_dll/wrapper/libcef_dll_dylib.cc b/libcef_dll/wrapper/libcef_dll_dylib.cc index 3e6e9e9ac..c8845a22b 100644 --- a/libcef_dll/wrapper/libcef_dll_dylib.cc +++ b/libcef_dll/wrapper/libcef_dll_dylib.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=6c8f094d04b36c879f379e87ebf45dc4698eb41e$ +// $hash=a753ff43760df4f3c1126d248b4128ca14a8cd68$ // #include @@ -204,6 +204,7 @@ struct libcef_pointers { decltype(&cef_v8value_create_array) cef_v8value_create_array; decltype(&cef_v8value_create_array_buffer) cef_v8value_create_array_buffer; decltype(&cef_v8value_create_function) cef_v8value_create_function; + decltype(&cef_v8value_create_promise) cef_v8value_create_promise; decltype(&cef_v8stack_trace_get_current) cef_v8stack_trace_get_current; decltype(&cef_value_create) cef_value_create; decltype(&cef_binary_value_create) cef_binary_value_create; @@ -420,6 +421,7 @@ int libcef_init_pointers(const char* path) { INIT_ENTRY(cef_v8value_create_array); INIT_ENTRY(cef_v8value_create_array_buffer); INIT_ENTRY(cef_v8value_create_function); + INIT_ENTRY(cef_v8value_create_promise); INIT_ENTRY(cef_v8stack_trace_get_current); INIT_ENTRY(cef_value_create); INIT_ENTRY(cef_binary_value_create); @@ -1114,6 +1116,10 @@ struct _cef_v8value_t* cef_v8value_create_function( return g_libcef_pointers.cef_v8value_create_function(name, handler); } +NO_SANITIZE("cfi-icall") struct _cef_v8value_t* cef_v8value_create_promise() { + return g_libcef_pointers.cef_v8value_create_promise(); +} + NO_SANITIZE("cfi-icall") struct _cef_v8stack_trace_t* cef_v8stack_trace_get_current(int frame_limit) { return g_libcef_pointers.cef_v8stack_trace_get_current(frame_limit); diff --git a/tests/ceftests/v8_unittest.cc b/tests/ceftests/v8_unittest.cc index 6605c96c9..ff2cd012b 100644 --- a/tests/ceftests/v8_unittest.cc +++ b/tests/ceftests/v8_unittest.cc @@ -84,6 +84,12 @@ enum V8TestMode { V8TEST_FUNCTION_HANDLER_NO_OBJECT, V8TEST_FUNCTION_HANDLER_WITH_CONTEXT, V8TEST_FUNCTION_HANDLER_EMPTY_STRING, + V8TEST_PROMISE_CREATE, + V8TEST_PROMISE_RESOLVE, + V8TEST_PROMISE_RESOLVE_NO_ARGUMENT, + V8TEST_PROMISE_RESOLVE_HANDLER, + V8TEST_PROMISE_REJECT, + V8TEST_PROMISE_REJECT_HANDLER, V8TEST_CONTEXT_EVAL, V8TEST_CONTEXT_EVAL_EXCEPTION, V8TEST_CONTEXT_EVAL_CSP_BYPASS_UNSAFE_EVAL, @@ -214,6 +220,24 @@ class V8RendererTest : public ClientAppRenderer::Delegate, case V8TEST_FUNCTION_HANDLER_EMPTY_STRING: RunFunctionHandlerEmptyStringTest(); break; + case V8TEST_PROMISE_CREATE: + RunPromiseCreateTest(); + break; + case V8TEST_PROMISE_RESOLVE: + RunPromiseResolveTest(); + break; + case V8TEST_PROMISE_RESOLVE_NO_ARGUMENT: + RunPromiseResolveNoArgumentTest(); + break; + case V8TEST_PROMISE_RESOLVE_HANDLER: + RunPromiseResolveHandlerTest(); + break; + case V8TEST_PROMISE_REJECT: + RunPromiseRejectTest(); + break; + case V8TEST_PROMISE_REJECT_HANDLER: + RunPromiseRejectHandlerTest(); + break; case V8TEST_CONTEXT_EVAL: RunContextEvalTest(); break; @@ -273,6 +297,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, EXPECT_FALSE(value->IsInt()); EXPECT_FALSE(value->IsUInt()); EXPECT_FALSE(value->IsObject()); + EXPECT_FALSE(value->IsPromise()); EXPECT_FALSE(value->IsString()); DestroyTest(); @@ -293,6 +318,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, EXPECT_FALSE(value->IsUInt()); EXPECT_FALSE(value->IsNull()); EXPECT_FALSE(value->IsObject()); + EXPECT_FALSE(value->IsPromise()); EXPECT_FALSE(value->IsString()); DestroyTest(); @@ -315,6 +341,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, EXPECT_FALSE(value->IsFunction()); EXPECT_FALSE(value->IsNull()); EXPECT_FALSE(value->IsObject()); + EXPECT_FALSE(value->IsPromise()); EXPECT_FALSE(value->IsString()); DestroyTest(); @@ -337,6 +364,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, EXPECT_FALSE(value->IsFunction()); EXPECT_FALSE(value->IsNull()); EXPECT_FALSE(value->IsObject()); + EXPECT_FALSE(value->IsPromise()); EXPECT_FALSE(value->IsString()); DestroyTest(); @@ -357,6 +385,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, EXPECT_FALSE(value->IsUInt()); EXPECT_FALSE(value->IsNull()); EXPECT_FALSE(value->IsObject()); + EXPECT_FALSE(value->IsPromise()); EXPECT_FALSE(value->IsString()); DestroyTest(); @@ -396,6 +425,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, EXPECT_FALSE(value->IsUInt()); EXPECT_FALSE(value->IsObject()); EXPECT_FALSE(value->IsNull()); + EXPECT_FALSE(value->IsPromise()); EXPECT_FALSE(value->IsString()); DestroyTest(); @@ -416,6 +446,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, EXPECT_FALSE(value->IsInt()); EXPECT_FALSE(value->IsUInt()); EXPECT_FALSE(value->IsNull()); + EXPECT_FALSE(value->IsPromise()); EXPECT_FALSE(value->IsObject()); DestroyTest(); @@ -437,6 +468,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, EXPECT_FALSE(value->IsUInt()); EXPECT_FALSE(value->IsNull()); EXPECT_FALSE(value->IsObject()); + EXPECT_FALSE(value->IsPromise()); DestroyTest(); } @@ -466,6 +498,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, EXPECT_FALSE(value->IsInt()); EXPECT_FALSE(value->IsUInt()); EXPECT_FALSE(value->IsNull()); + EXPECT_FALSE(value->IsPromise()); EXPECT_FALSE(value->IsString()); DestroyTest(); @@ -645,6 +678,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, EXPECT_FALSE(value->IsInt()); EXPECT_FALSE(value->IsUInt()); EXPECT_FALSE(value->IsNull()); + EXPECT_FALSE(value->IsPromise()); EXPECT_FALSE(value->IsString()); // Exit the V8 context. @@ -1900,6 +1934,7 @@ class V8RendererTest : public ClientAppRenderer::Delegate, EXPECT_FALSE(value->IsInt()); EXPECT_FALSE(value->IsUInt()); EXPECT_FALSE(value->IsNull()); + EXPECT_FALSE(value->IsPromise()); EXPECT_FALSE(value->IsString()); DestroyTest(); @@ -2228,6 +2263,248 @@ class V8RendererTest : public ClientAppRenderer::Delegate, DestroyTest(); } + void RunPromiseCreateTest() { + CefRefPtr context = GetContext(); + + // Enter the V8 context. + EXPECT_TRUE(context->Enter()); + + CefRefPtr value = CefV8Value::CreatePromise(); + + // Exit the V8 context. + EXPECT_TRUE(context->Exit()); + + EXPECT_TRUE(value.get()); + EXPECT_TRUE(value->IsPromise()); + EXPECT_TRUE(value->IsObject()); + + EXPECT_FALSE(value->IsUndefined()); + EXPECT_FALSE(value->IsArray()); + EXPECT_FALSE(value->IsBool()); + EXPECT_FALSE(value->IsDate()); + EXPECT_FALSE(value->IsDouble()); + EXPECT_FALSE(value->IsFunction()); + EXPECT_FALSE(value->IsInt()); + EXPECT_FALSE(value->IsUInt()); + EXPECT_FALSE(value->IsNull()); + EXPECT_FALSE(value->IsString()); + + DestroyTest(); + } + + void RunPromiseResolveTest() { + CefRefPtr context = GetContext(); + + // Enter the V8 context. + EXPECT_TRUE(context->Enter()); + + CefRefPtr value = CefV8Value::CreatePromise(); + CefRefPtr obj = CefV8Value::CreateObject(nullptr, nullptr); + + EXPECT_TRUE(value.get()); + EXPECT_TRUE(value->ResolvePromise(obj)); + + // Exit the V8 context. + EXPECT_TRUE(context->Exit()); + + DestroyTest(); + } + + void RunPromiseResolveNoArgumentTest() { + CefRefPtr context = GetContext(); + + // Enter the V8 context. + EXPECT_TRUE(context->Enter()); + + CefRefPtr value = CefV8Value::CreatePromise(); + + EXPECT_TRUE(value.get()); + EXPECT_TRUE(value->ResolvePromise(nullptr)); + + // Exit the V8 context. + EXPECT_TRUE(context->Exit()); + + DestroyTest(); + } + + void RunPromiseResolveHandlerTest() { + CefRefPtr context = GetContext(); + + static const char* kPromiseName = "myprom"; + static const char* kResolveName = "myresolve"; + static const char* kRejectName = "myreject"; + static const int kVal1 = 32; + + class Handler : public CefV8Handler { + public: + Handler() {} + bool Execute(const CefString& name, + CefRefPtr object, + const CefV8ValueList& arguments, + CefRefPtr& retval, + CefString& exception) override { + EXPECT_STREQ(kResolveName, name.ToString().c_str()); + EXPECT_EQ((size_t)1, arguments.size()); + EXPECT_TRUE(arguments[0]->IsInt()); + EXPECT_EQ(kVal1, arguments[0]->GetIntValue()); + + got_execute_.yes(); + + return false; + } + + TrackCallback got_execute_; + + IMPLEMENT_REFCOUNTING(Handler); + }; + + // Enter the V8 context. + EXPECT_TRUE(context->Enter()); + + Handler* resolveHandler = new Handler; + Handler* rejectHandler = new Handler; + + CefRefPtr resolveHandlerPtr(resolveHandler); + CefRefPtr rejectHandlerPtr(rejectHandler); + + CefRefPtr resolveFunc = + CefV8Value::CreateFunction(kResolveName, resolveHandler); + EXPECT_TRUE(resolveFunc.get()); + EXPECT_TRUE(context->GetGlobal()->SetValue(kResolveName, resolveFunc, + V8_PROPERTY_ATTRIBUTE_NONE)); + + CefRefPtr rejectFunc = + CefV8Value::CreateFunction(kRejectName, rejectHandler); + EXPECT_TRUE(rejectFunc.get()); + EXPECT_TRUE(context->GetGlobal()->SetValue(kRejectName, rejectFunc, + V8_PROPERTY_ATTRIBUTE_NONE)); + + CefRefPtr value = CefV8Value::CreatePromise(); + + EXPECT_TRUE(value.get()); + EXPECT_TRUE(context->GetGlobal()->SetValue(kPromiseName, value, + V8_PROPERTY_ATTRIBUTE_NONE)); + + CefRefPtr retval; + CefRefPtr exception; + + std::stringstream test; + test << "window." << kPromiseName << ".then(" << kResolveName << ").catch(" + << kRejectName << ")"; + + EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception)); + EXPECT_TRUE(retval.get()); + EXPECT_TRUE(retval->IsPromise()); + EXPECT_FALSE(exception.get()); + + CefRefPtr arg = CefV8Value::CreateInt(kVal1); + EXPECT_TRUE(value->ResolvePromise(arg)); + + // Exit the V8 context. + EXPECT_TRUE(context->Exit()); + + EXPECT_TRUE(resolveHandler->got_execute_); + EXPECT_FALSE(rejectHandler->got_execute_); + + DestroyTest(); + } + + void RunPromiseRejectTest() { + CefRefPtr context = GetContext(); + + // Enter the V8 context. + EXPECT_TRUE(context->Enter()); + + CefRefPtr value = CefV8Value::CreatePromise(); + + EXPECT_TRUE(value.get()); + EXPECT_TRUE(value->RejectPromise("Error: Unknown")); + + // Exit the V8 context. + EXPECT_TRUE(context->Exit()); + + DestroyTest(); + } + + void RunPromiseRejectHandlerTest() { + CefRefPtr context = GetContext(); + + static const char* kPromiseName = "myprom"; + static const char* kResolveName = "myresolve"; + static const char* kRejectName = "myreject"; + + class Handler : public CefV8Handler { + public: + Handler() {} + bool Execute(const CefString& name, + CefRefPtr object, + const CefV8ValueList& arguments, + CefRefPtr& retval, + CefString& exception) override { + EXPECT_STREQ(kRejectName, name.ToString().c_str()); + EXPECT_EQ((size_t)1, arguments.size()); + EXPECT_TRUE(arguments[0]->IsObject()); + + got_execute_.yes(); + + return false; + } + + TrackCallback got_execute_; + + IMPLEMENT_REFCOUNTING(Handler); + }; + + // Enter the V8 context. + EXPECT_TRUE(context->Enter()); + + Handler* resolveHandler = new Handler; + Handler* rejectHandler = new Handler; + + CefRefPtr resolveHandlerPtr(resolveHandler); + CefRefPtr rejectHandlerPtr(rejectHandler); + + CefRefPtr resolveFunc = + CefV8Value::CreateFunction(kResolveName, resolveHandler); + EXPECT_TRUE(resolveFunc.get()); + EXPECT_TRUE(context->GetGlobal()->SetValue(kResolveName, resolveFunc, + V8_PROPERTY_ATTRIBUTE_NONE)); + + CefRefPtr rejectFunc = + CefV8Value::CreateFunction(kRejectName, rejectHandler); + EXPECT_TRUE(rejectFunc.get()); + EXPECT_TRUE(context->GetGlobal()->SetValue(kRejectName, rejectFunc, + V8_PROPERTY_ATTRIBUTE_NONE)); + + CefRefPtr value = CefV8Value::CreatePromise(); + + EXPECT_TRUE(value.get()); + EXPECT_TRUE(context->GetGlobal()->SetValue(kPromiseName, value, + V8_PROPERTY_ATTRIBUTE_NONE)); + + CefRefPtr retval; + CefRefPtr exception; + + std::stringstream test; + test << "window." << kPromiseName << ".then(" << kResolveName << ").catch(" + << kRejectName << ")"; + + EXPECT_TRUE(context->Eval(test.str(), CefString(), 0, retval, exception)); + EXPECT_TRUE(retval.get()); + EXPECT_TRUE(retval->IsPromise()); + EXPECT_FALSE(exception.get()); + + EXPECT_TRUE(value->RejectPromise("Error: Unknown")); + + // Exit the V8 context. + EXPECT_TRUE(context->Exit()); + + EXPECT_FALSE(resolveHandler->got_execute_); + EXPECT_TRUE(rejectHandler->got_execute_); + + DestroyTest(); + } + void RunContextEvalTest() { CefRefPtr context = GetContext(); @@ -3055,6 +3332,12 @@ V8_TEST(FunctionHandlerFail, V8TEST_FUNCTION_HANDLER_FAIL) V8_TEST(FunctionHandlerNoObject, V8TEST_FUNCTION_HANDLER_NO_OBJECT) V8_TEST(FunctionHandlerWithContext, V8TEST_FUNCTION_HANDLER_WITH_CONTEXT) V8_TEST(FunctionHandlerEmptyString, V8TEST_FUNCTION_HANDLER_EMPTY_STRING) +V8_TEST(PromiseCreate, V8TEST_PROMISE_CREATE) +V8_TEST(PromiseResolve, V8TEST_PROMISE_RESOLVE) +V8_TEST(PromiseResolveNoArgument, V8TEST_PROMISE_RESOLVE_NO_ARGUMENT) +V8_TEST(PromiseResolveHandler, V8TEST_PROMISE_RESOLVE_HANDLER) +V8_TEST(PromiseReject, V8TEST_PROMISE_REJECT) +V8_TEST(PromiseRejectHandler, V8TEST_PROMISE_REJECT_HANDLER) V8_TEST(ContextEval, V8TEST_CONTEXT_EVAL) V8_TEST(ContextEvalException, V8TEST_CONTEXT_EVAL_EXCEPTION) V8_TEST_EX(ContextEvalCspBypassUnsafeEval,