555 lines
16 KiB
C++
555 lines
16 KiB
C++
#pragma once
|
|
#include <AppCore/Defines.h>
|
|
#include <JavaScriptCore/JavaScript.h>
|
|
#include <JavaScriptCore/JSStringRef.h>
|
|
#include <Ultralight/String.h>
|
|
#include <functional>
|
|
#include <memory>
|
|
|
|
namespace ultralight {
|
|
|
|
///
|
|
/// Set the current JSContext.
|
|
///
|
|
/// Most JavaScriptCore C API calls require an active JavaScript execution
|
|
/// context (JSContextRef). You can get the JSContextRef for a page via
|
|
/// `View::LockJSContext()`. This context changes with each page navigation.
|
|
///
|
|
/// **Note**:
|
|
/// You MUST set a JSContext before using most of the C++ API below.
|
|
///
|
|
void AExport SetJSContext(JSContextRef ctx);
|
|
|
|
///
|
|
/// Get the current JSContext.
|
|
///
|
|
JSContextRef AExport GetJSContext();
|
|
|
|
///
|
|
/// JavaScript String wrapper that automatically manages JSStringRef lifetime
|
|
/// and provides helpful conversions.
|
|
///
|
|
class AExport JSString {
|
|
public:
|
|
/// Create empty string
|
|
JSString();
|
|
|
|
/// Create from C-string
|
|
JSString(const char* str);
|
|
|
|
/// Create from Ultralight String
|
|
JSString(const String& str);
|
|
|
|
/// Take ownership of existing JSStringRef (will not increase ref-count)
|
|
JSString(JSStringRef str);
|
|
|
|
/// Copy constructor (will increase ref-count)
|
|
JSString(const JSString& other);
|
|
|
|
/// Destructor
|
|
~JSString();
|
|
|
|
/// Assignment operator (will increase ref-count)
|
|
JSString& operator=(const JSString& other);
|
|
|
|
/// Cast to String
|
|
operator String();
|
|
|
|
/// Cast to underlying JSStringRef
|
|
operator JSStringRef() const { return instance_; }
|
|
|
|
protected:
|
|
JSStringRef instance_;
|
|
};
|
|
|
|
class JSArray;
|
|
class JSObject;
|
|
class JSFunction;
|
|
|
|
/// Tag type used with the JSValue constructor to create "Null" types
|
|
struct AExport JSValueNullTag {};
|
|
|
|
/// Tag type used with the JSValue constructor to create "Undefined" types
|
|
struct AExport JSValueUndefinedTag {};
|
|
|
|
///
|
|
/// JavaScript variant value wrapper that automatically manages JSValueRef
|
|
/// lifetime and provides helpful conversions.
|
|
///
|
|
class AExport JSValue {
|
|
public:
|
|
/// Create null (empty) JSValue
|
|
JSValue();
|
|
|
|
/// Create null JSValue explicitly
|
|
JSValue(JSValueNullTag);
|
|
|
|
/// Create undefined JSValue
|
|
JSValue(JSValueUndefinedTag);
|
|
|
|
/// Create boolean JSValue
|
|
JSValue(bool val);
|
|
|
|
/// Create unsigned integer JSValue (aka, Number) [will be cast to double]
|
|
JSValue(uint32_t val);
|
|
|
|
/// Create integer JSValue (aka, Number) [will be cast to double]
|
|
JSValue(int32_t val);
|
|
|
|
/// Create unsigned integer JSValue (aka, Number) [will be cast to double]
|
|
JSValue(uint64_t val);
|
|
|
|
/// Create integer JSValue (aka, Number) [will be cast to double]
|
|
JSValue(int64_t val);
|
|
|
|
/// Create double JSValue (aka, Number)
|
|
JSValue(double val);
|
|
|
|
/// Create string JSValue
|
|
JSValue(const char* val);
|
|
|
|
/// Create string JSValue
|
|
JSValue(const String& val);
|
|
|
|
/// Create string JSValue
|
|
JSValue(JSString val);
|
|
|
|
/// Create from existing JSValueRef
|
|
JSValue(JSValueRef val);
|
|
|
|
/// Create object JSValue
|
|
JSValue(JSObjectRef obj);
|
|
|
|
/// Copy constructor, a shallow copy is made, the constructed JSValue will
|
|
/// point to the same JSValueRef.
|
|
JSValue(const JSValue& other);
|
|
|
|
/// Destructor
|
|
virtual ~JSValue();
|
|
|
|
/// A shallow copy is made, this JSValue will point to the same JSValueRef
|
|
virtual JSValue& operator=(const JSValue& other);
|
|
|
|
/// Whether or not the value is a JavaScript Null type.
|
|
bool IsNull() const;
|
|
|
|
/// Whether or not the value is a JavaScript Undefined type.
|
|
bool IsUndefined() const;
|
|
|
|
/// Whether or not the value is a JavaScript Boolean type.
|
|
bool IsBoolean() const;
|
|
|
|
/// Whether or not the value is a JavaScript Number type.
|
|
bool IsNumber() const;
|
|
|
|
/// Whether or not the value is a JavaScript String type.
|
|
bool IsString() const;
|
|
|
|
/// Whether or not the value is a JavaScript Object type.
|
|
bool IsObject() const;
|
|
|
|
/// Whether or not the value is a JavaScript Array type.
|
|
bool IsArray() const;
|
|
|
|
/// Whether or not the value is a JavaScript Function type.
|
|
bool IsFunction() const;
|
|
|
|
/// Get the value as a Boolean
|
|
bool ToBoolean() const;
|
|
|
|
/// Get the value as a Number (Double)
|
|
double ToNumber() const;
|
|
|
|
/// Get the value as a Number (Integer)
|
|
int64_t ToInteger() const { return static_cast<int64_t>(ToNumber()); }
|
|
|
|
/// Get the value as a String
|
|
JSString ToString() const;
|
|
|
|
/// Get the value as an Object (will debug assert if not an Object)
|
|
JSObject ToObject() const;
|
|
|
|
/// Get the value as an Array (will debug asset if not an Array)
|
|
JSArray ToArray() const;
|
|
|
|
/// Get the value as a Function (will debug asset if not a Function)
|
|
JSFunction ToFunction() const;
|
|
|
|
operator bool() const { return ToBoolean(); }
|
|
|
|
operator double() const { return ToNumber(); }
|
|
|
|
operator uint32_t() const { return static_cast<uint32_t>(ToNumber()); }
|
|
|
|
operator int32_t() const { return static_cast<uint32_t>(ToNumber()); }
|
|
|
|
operator uint64_t() const { return static_cast<uint64_t>(ToNumber()); }
|
|
|
|
operator int64_t() const { return ToInteger(); }
|
|
|
|
operator String() const { return ToString(); }
|
|
|
|
operator JSString() const { return ToString(); }
|
|
|
|
operator JSObject() const;
|
|
|
|
operator JSObjectRef() const;
|
|
|
|
operator JSArray() const;
|
|
|
|
operator JSFunction() const;
|
|
|
|
/// Get the underlying JSValueRef
|
|
operator JSValueRef() const { return instance(); }
|
|
|
|
///
|
|
/// Get the bound context for this JSValue (it is cached at creation).
|
|
///
|
|
JSContextRef context() const { return ctx_; }
|
|
|
|
///
|
|
/// Set the JSContext for this JSValue.
|
|
///
|
|
/// **Note**:
|
|
/// JSValues created from within a JSCallback have a temporary JSContext
|
|
/// that is destroyed when the callback returns. You will need to "move"
|
|
/// any JSValues created within these callbacks to the View's main context
|
|
/// (call set_context() with the main context) before using them outside
|
|
/// the callback.
|
|
///
|
|
void set_context(JSContextRef context) { ctx_ = context; }
|
|
|
|
protected:
|
|
JSValue(JSContextRef ctx);
|
|
JSValue(JSContextRef ctx, JSValueRef val);
|
|
virtual JSValueRef instance() const;
|
|
|
|
JSContextRef ctx_;
|
|
JSValueRef instance_ = nullptr;
|
|
friend class JSFunction;
|
|
};
|
|
|
|
///
|
|
/// A vector of JSValues, used for passing around arguments in JSCallback.
|
|
///
|
|
class AExport JSArgs {
|
|
public:
|
|
/// Create an empty list of JavaScript arguments
|
|
JSArgs();
|
|
|
|
/// Create a list of JavaScript arguments using a C++ initializer list
|
|
JSArgs(const std::initializer_list<JSValue>& values);
|
|
|
|
/// Copy-constructor
|
|
JSArgs(const JSArgs& other);
|
|
|
|
/// Destructor
|
|
~JSArgs();
|
|
|
|
/// Assignment operator
|
|
JSArgs& operator=(const JSArgs& other);
|
|
|
|
///
|
|
/// Access an element of the argument list by index.
|
|
///
|
|
/// **Note**:
|
|
/// All JSValues are actually wrappers of JSValueRef instances so even
|
|
/// though this function doesn't return a JSValue& you are still operating
|
|
/// directly on the underlying JavaScript value instance.
|
|
///
|
|
JSValue operator[](size_t pos);
|
|
|
|
///
|
|
/// Access an element of the argument list by index. (const overload)
|
|
///
|
|
/// **Note**:
|
|
/// All JSValues are actually wrappers of JSValueRef instances so even
|
|
/// though this function doesn't return a JSValue& you are still operating
|
|
/// directly on the underlying JavaScript value instance.
|
|
///
|
|
const JSValue operator[](size_t pos) const;
|
|
|
|
/// Whether or not the argument list is empty.
|
|
bool empty() const;
|
|
|
|
/// The number of elements in the argument list.
|
|
size_t size() const;
|
|
|
|
/// Clear the argument list.
|
|
void clear();
|
|
|
|
/// Add a new argument to the end of the list.
|
|
void push_back(const JSValue& val);
|
|
|
|
/// Remove the last item from the end of the list.
|
|
void pop_back();
|
|
|
|
/// Get the argument list as a C-array of JSValues
|
|
JSValue* data();
|
|
|
|
/// Get the argument list as a C-array of JSValues (const overload)
|
|
const JSValue* data() const;
|
|
protected:
|
|
void* instance_;
|
|
};
|
|
|
|
///
|
|
/// JSCallback typedef used for binding C++ callbacks to JavaScript functions.
|
|
///
|
|
/// Takes two arguments (const JSObject& thisObj, const JSArgs& args) and
|
|
/// returns nothing (void).
|
|
///
|
|
typedef std::function<void(const JSObject&, const JSArgs&)> JSCallback;
|
|
|
|
///
|
|
/// JSCallbackWithRetval typedef used for binding C++ callbacks to JavaScript
|
|
/// functions with an optional return value.
|
|
///
|
|
/// Takes two arguments (const JSObject& thisObj, const JSArgs& args) and
|
|
/// returns a JSValue back to JavaScript.
|
|
///
|
|
typedef std::function<JSValue(const JSObject&, const JSArgs&)> JSCallbackWithRetval;
|
|
|
|
///
|
|
/// Macro to help bind C++ member functions to a JSCallback
|
|
///
|
|
/// Usage: JSCallback callback = BindJSCallback(&MyClass::MyMemberFunction);
|
|
///
|
|
/// **Note**: Expected to run from within an instance of 'MyClass', note the
|
|
/// 'this' keyword in the macro.
|
|
///
|
|
#define BindJSCallback(fn) (JSCallback)std::bind(fn, this, std::placeholders::_1, std::placeholders::_2)
|
|
|
|
///
|
|
/// Macro to help bind C++ member functions to a JSCallbackWithRetval
|
|
///
|
|
/// Usage: JSCallback callback = BindJSCallback(&MyClass::MyMemberFunction);
|
|
///
|
|
/// **Note**: Expected to run from within an instance of 'MyClass', note the
|
|
/// 'this' keyword in the macro.
|
|
///
|
|
#define BindJSCallbackWithRetval(fn) (JSCallbackWithRetval)std::bind(fn, this, std::placeholders::_1, std::placeholders::_2)
|
|
|
|
///
|
|
/// Wrapper for JSObject property value (JSValue subclass). Allows new value assignment
|
|
/// to object property, binding C++ callbacks to object properties via function objects,
|
|
/// as well as value query via the JSValue interface.
|
|
///
|
|
class AExport JSPropertyValue : public JSValue {
|
|
public:
|
|
virtual ~JSPropertyValue();
|
|
|
|
/// Assign a new value to the property (internally calls JSObjectSetProperty)
|
|
virtual JSPropertyValue& operator=(const JSValue& value);
|
|
|
|
/// Bind to native C++ callback (creates a Function object that can be called from JS)
|
|
JSPropertyValue& operator=(const JSCallback& callback);
|
|
|
|
/// Bind to native C++ callback with return value (creates a Function object that can be called from JS)
|
|
JSPropertyValue& operator=(const JSCallbackWithRetval& callback);
|
|
|
|
protected:
|
|
virtual JSValueRef instance() const;
|
|
JSPropertyValue(JSContextRef ctx, JSObjectRef proxy_obj, unsigned idx);
|
|
JSPropertyValue(JSContextRef ctx, JSObjectRef proxy_obj, JSString idx);
|
|
JSPropertyValue(const JSPropertyValue&) = default;
|
|
JSPropertyValue& operator=(const JSPropertyValue&) = delete;
|
|
|
|
JSObject* proxyObj_;
|
|
bool using_numeric_idx_;
|
|
unsigned numeric_idx_;
|
|
JSString string_idx_;
|
|
friend class JSArray;
|
|
friend class JSObject;
|
|
};
|
|
|
|
///
|
|
/// JSArray wrapper that automatically manages lifetime and provides
|
|
/// convenient access to indices and Array functions.
|
|
///
|
|
class AExport JSArray {
|
|
public:
|
|
/// Create empty Array
|
|
JSArray();
|
|
|
|
/// Create Array from list of JSValues
|
|
JSArray(const std::initializer_list<JSValue>& values);
|
|
|
|
/// Create Array from existing JSObjectRef (JavaScriptCore C API)
|
|
JSArray(JSObjectRef array_obj);
|
|
|
|
/// Copy constructor (shallow copy, will point to same instance)
|
|
JSArray(const JSArray& other);
|
|
|
|
~JSArray();
|
|
|
|
/// Assignment (shallow assignment, will point to same instance)
|
|
JSArray& operator=(const JSArray& other);
|
|
|
|
/// Get number of elements in the Array
|
|
unsigned length();
|
|
|
|
/// Push an element to back of Array
|
|
void push(const JSValue& val);
|
|
|
|
/// Find the index (location) of a certain value, will return -1 if not found
|
|
int indexOf(const JSValue& val, int start = 0) const;
|
|
|
|
/// Get a property by array index (numbering starts at 0)
|
|
JSPropertyValue operator[](unsigned idx) const;
|
|
|
|
/// Get the underlying JSObjectRef (JavaScriptCore C API)
|
|
operator JSObjectRef() const { return instance_; }
|
|
|
|
///
|
|
/// Get the bound context for this JSArray (it is cached at creation).
|
|
///
|
|
JSContextRef context() const { return ctx_; }
|
|
|
|
///
|
|
/// Set the JSContext for this JSArray.
|
|
///
|
|
/// **Note**:
|
|
/// JSArrays created from within a JSCallback have a temporary JSContext
|
|
/// that is destroyed when the callback returns. You will need to "move"
|
|
/// any JSArrays created within these callbacks to the View's main context
|
|
/// (call set_context() with the main context) before using them outside
|
|
/// the callback.
|
|
///
|
|
void set_context(JSContextRef context) { ctx_ = context; }
|
|
|
|
protected:
|
|
JSArray(JSContextRef ctx, JSValueRef val);
|
|
|
|
JSContextRef ctx_;
|
|
JSObjectRef instance_;
|
|
friend class JSValue;
|
|
};
|
|
|
|
///
|
|
/// JSObject wrapper that automatically manages lifetime and provides
|
|
/// convenient access to properties.
|
|
///
|
|
class AExport JSObject {
|
|
public:
|
|
/// Create empty Object
|
|
JSObject();
|
|
|
|
/// Create from existing JSObjectRef from JavaScriptCore C API
|
|
JSObject(JSObjectRef obj);
|
|
|
|
/// Copy constructor (shallow copy, will point to same instance)
|
|
JSObject(const JSObject& other);
|
|
|
|
~JSObject();
|
|
|
|
/// Assignment (shallow assignment, will point to same instance)
|
|
JSObject& operator=(const JSObject& other);
|
|
|
|
/// Get a property by name
|
|
JSPropertyValue operator[](JSString propertyName) const;
|
|
|
|
/// Check if a property exists
|
|
bool HasProperty(JSString propertyName) const;
|
|
|
|
/// Remove a property
|
|
bool DeleteProperty(JSString propertyName);
|
|
|
|
/// Get the underlying JSObjectRef (JavaScriptCore C API)
|
|
operator JSObjectRef() const { return instance_; }
|
|
|
|
///
|
|
/// Get the bound context for this JSObject (it is cached at creation).
|
|
///
|
|
JSContextRef context() const { return ctx_; }
|
|
|
|
///
|
|
/// Set the JSContext for this JSObject.
|
|
///
|
|
/// **Note**:
|
|
/// JSObjects created from within a JSCallback have a temporary JSContext
|
|
/// that is destroyed when the callback returns. You will need to "move"
|
|
/// any JSObjects created within these callbacks to the View's main context
|
|
/// (call set_context() with the main context) before using them outside
|
|
/// the callback.
|
|
///
|
|
void set_context(JSContextRef context) { ctx_ = context; }
|
|
|
|
protected:
|
|
JSObject(JSContextRef ctx, JSValueRef val);
|
|
JSObject(JSContextRef ctx, JSObjectRef obj);
|
|
|
|
JSContextRef ctx_;
|
|
JSObjectRef instance_;
|
|
friend class JSValue;
|
|
friend class JSPropertyValue;
|
|
};
|
|
|
|
///
|
|
/// JSFunction wrapper that automatically manages lifetime and provides
|
|
/// convenient function invocation operators.
|
|
///
|
|
class AExport JSFunction {
|
|
public:
|
|
/// Create an empty Function.
|
|
/// NOTE: It is OKAY to create this without calling SetJSContext() first.
|
|
JSFunction();
|
|
|
|
/// Copy constructor (shallow copy, will point to same instance)
|
|
JSFunction(const JSFunction& other);
|
|
|
|
~JSFunction();
|
|
|
|
/// Assignment (shallow assignment, will point to same instance)
|
|
JSFunction& operator=(const JSFunction& other);
|
|
|
|
/// Whether or not this is a valid, callable Function object.
|
|
bool IsValid() const;
|
|
|
|
/// Call function (using Global Object for 'this') and return the result.
|
|
JSValue operator()(const JSArgs& args);
|
|
|
|
/// Call function (with explicit object for 'this') and return the result
|
|
JSValue operator()(const JSObject& thisObject, const JSArgs& args);
|
|
|
|
/// Get the underlying JSObjectRef (JavaScriptCore C API)
|
|
operator JSObjectRef() const { return instance_; }
|
|
|
|
///
|
|
/// Get the bound context for this JSFunction (it is cached at creation).
|
|
///
|
|
JSContextRef context() const { return ctx_; }
|
|
|
|
///
|
|
/// Set the JSContext for this JSFunction.
|
|
///
|
|
/// **Note**:
|
|
/// JSFunctions created from within a JSCallback have a temporary JSContext
|
|
/// that is destroyed when the callback returns. You will need to "move"
|
|
/// any JSFunctions created within these callbacks to the View's main context
|
|
/// (call set_context() with the main context) before using them outside
|
|
/// the callback.
|
|
///
|
|
void set_context(JSContextRef context) { ctx_ = context; }
|
|
|
|
protected:
|
|
JSFunction(JSContextRef ctx, JSValueRef val);
|
|
|
|
JSContextRef ctx_;
|
|
JSObjectRef instance_;
|
|
friend class JSValue;
|
|
};
|
|
|
|
///
|
|
/// Get the Global Object for the current JSContext.
|
|
/// In JavaScript, this would be equivalent to the "window" object.
|
|
///
|
|
JSObject AExport JSGlobalObject();
|
|
|
|
///
|
|
/// Evaluate a string of JavaScript and return a result.
|
|
///
|
|
JSValue AExport JSEval(const JSString& str);
|
|
|
|
} // namespace ultralight
|