Home | History | Annotate | Download | only in javascript
      1 // Copyright 2014 PDFium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #ifndef FPDFSDK_JAVASCRIPT_JS_DEFINE_H_
      8 #define FPDFSDK_JAVASCRIPT_JS_DEFINE_H_
      9 
     10 #include <vector>
     11 
     12 #include "fpdfsdk/javascript/JS_Object.h"
     13 #include "fpdfsdk/javascript/JS_Value.h"
     14 #include "fpdfsdk/javascript/resource.h"
     15 #include "fxjs/fxjs_v8.h"
     16 
     17 struct JSConstSpec {
     18   enum Type { Number = 0, String = 1 };
     19 
     20   const char* pName;
     21   Type eType;
     22   double number;
     23   const char* pStr;
     24 };
     25 
     26 struct JSPropertySpec {
     27   const char* pName;
     28   v8::AccessorGetterCallback pPropGet;
     29   v8::AccessorSetterCallback pPropPut;
     30 };
     31 
     32 struct JSMethodSpec {
     33   const char* pName;
     34   v8::FunctionCallback pMethodCall;
     35 };
     36 
     37 template <class C, bool (C::*M)(CJS_Runtime*, CJS_PropValue&, CFX_WideString&)>
     38 void JSPropGetter(const char* prop_name_string,
     39                   const char* class_name_string,
     40                   v8::Local<v8::String> property,
     41                   const v8::PropertyCallbackInfo<v8::Value>& info) {
     42   CJS_Runtime* pRuntime =
     43       CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
     44   if (!pRuntime)
     45     return;
     46   CJS_Object* pJSObj =
     47       static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
     48   if (!pJSObj)
     49     return;
     50   C* pObj = reinterpret_cast<C*>(pJSObj->GetEmbedObject());
     51   CFX_WideString sError;
     52   CJS_PropValue value(pRuntime);
     53   value.StartGetting();
     54   if (!(pObj->*M)(pRuntime, value, sError)) {
     55     pRuntime->Error(
     56         JSFormatErrorString(class_name_string, prop_name_string, sError));
     57     return;
     58   }
     59   info.GetReturnValue().Set(value.GetJSValue()->ToV8Value(pRuntime));
     60 }
     61 
     62 template <class C, bool (C::*M)(CJS_Runtime*, CJS_PropValue&, CFX_WideString&)>
     63 void JSPropSetter(const char* prop_name_string,
     64                   const char* class_name_string,
     65                   v8::Local<v8::String> property,
     66                   v8::Local<v8::Value> value,
     67                   const v8::PropertyCallbackInfo<void>& info) {
     68   CJS_Runtime* pRuntime =
     69       CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
     70   if (!pRuntime)
     71     return;
     72   CJS_Object* pJSObj =
     73       static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
     74   if (!pJSObj)
     75     return;
     76   C* pObj = reinterpret_cast<C*>(pJSObj->GetEmbedObject());
     77   CFX_WideString sError;
     78   CJS_PropValue propValue(pRuntime, CJS_Value(pRuntime, value));
     79   propValue.StartSetting();
     80   if (!(pObj->*M)(pRuntime, propValue, sError)) {
     81     pRuntime->Error(
     82         JSFormatErrorString(class_name_string, prop_name_string, sError));
     83   }
     84 }
     85 
     86 #define JS_STATIC_PROP(prop_name, class_name)                                 \
     87   static void get_##prop_name##_static(                                       \
     88       v8::Local<v8::String> property,                                         \
     89       const v8::PropertyCallbackInfo<v8::Value>& info) {                      \
     90     JSPropGetter<class_name, &class_name::prop_name>(#prop_name, #class_name, \
     91                                                      property, info);         \
     92   }                                                                           \
     93   static void set_##prop_name##_static(                                       \
     94       v8::Local<v8::String> property, v8::Local<v8::Value> value,             \
     95       const v8::PropertyCallbackInfo<void>& info) {                           \
     96     JSPropSetter<class_name, &class_name::prop_name>(#prop_name, #class_name, \
     97                                                      property, value, info);  \
     98   }
     99 
    100 template <class C,
    101           bool (C::*M)(CJS_Runtime*,
    102                        const std::vector<CJS_Value>&,
    103                        CJS_Value&,
    104                        CFX_WideString&)>
    105 void JSMethod(const char* method_name_string,
    106               const char* class_name_string,
    107               const v8::FunctionCallbackInfo<v8::Value>& info) {
    108   CJS_Runtime* pRuntime =
    109       CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
    110   if (!pRuntime)
    111     return;
    112   std::vector<CJS_Value> parameters;
    113   for (unsigned int i = 0; i < (unsigned int)info.Length(); i++) {
    114     parameters.push_back(CJS_Value(pRuntime, info[i]));
    115   }
    116   CJS_Object* pJSObj =
    117       static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
    118   if (!pJSObj)
    119     return;
    120   C* pObj = reinterpret_cast<C*>(pJSObj->GetEmbedObject());
    121   CFX_WideString sError;
    122   CJS_Value valueRes(pRuntime);
    123   if (!(pObj->*M)(pRuntime, parameters, valueRes, sError)) {
    124     pRuntime->Error(
    125         JSFormatErrorString(class_name_string, method_name_string, sError));
    126     return;
    127   }
    128   info.GetReturnValue().Set(valueRes.ToV8Value(pRuntime));
    129 }
    130 
    131 #define JS_STATIC_METHOD(method_name, class_name)                             \
    132   static void method_name##_static(                                           \
    133       const v8::FunctionCallbackInfo<v8::Value>& info) {                      \
    134     JSMethod<class_name, &class_name::method_name>(#method_name, #class_name, \
    135                                                    info);                     \
    136   }
    137 
    138 #define JS_SPECIAL_STATIC_METHOD(method_name, class_alternate, class_name) \
    139   static void method_name##_static(                                        \
    140       const v8::FunctionCallbackInfo<v8::Value>& info) {                   \
    141     JSMethod<class_alternate, &class_alternate::method_name>(              \
    142         #method_name, #class_name, info);                                  \
    143   }
    144 
    145 // All JS classes have a name, an object defintion ID, and the ability to
    146 // register themselves with FXJS_V8. We never make a BASE class on its own
    147 // because it can't really do anything.
    148 #define DECLARE_JS_CLASS_BASE_PART() \
    149   static const char* g_pClassName;   \
    150   static int g_nObjDefnID;           \
    151   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
    152 
    153 #define IMPLEMENT_JS_CLASS_BASE_PART(js_class_name, class_name) \
    154   const char* js_class_name::g_pClassName = #class_name;        \
    155   int js_class_name::g_nObjDefnID = -1;
    156 
    157 // CONST classes provide constants, but not constructors, methods, or props.
    158 #define DECLARE_JS_CLASS_CONST() \
    159   DECLARE_JS_CLASS_BASE_PART()   \
    160   DECLARE_JS_CLASS_CONST_PART()
    161 
    162 #define IMPLEMENT_JS_CLASS_CONST(js_class_name, class_name)                  \
    163   IMPLEMENT_JS_CLASS_BASE_PART(js_class_name, class_name)                    \
    164   IMPLEMENT_JS_CLASS_CONST_PART(js_class_name, class_name)                   \
    165   void js_class_name::DefineJSObjects(CFXJS_Engine* pEngine,                 \
    166                                       FXJSOBJTYPE eObjType) {                \
    167     g_nObjDefnID = pEngine->DefineObj(js_class_name::g_pClassName, eObjType, \
    168                                       nullptr, nullptr);                     \
    169     DefineConsts(pEngine);                                                   \
    170   }
    171 
    172 #define DECLARE_JS_CLASS_CONST_PART() \
    173   static JSConstSpec ConstSpecs[];    \
    174   static void DefineConsts(CFXJS_Engine* pEngine);
    175 
    176 #define IMPLEMENT_JS_CLASS_CONST_PART(js_class_name, class_name)             \
    177   void js_class_name::DefineConsts(CFXJS_Engine* pEngine) {                  \
    178     for (size_t i = 0; i < FX_ArraySize(ConstSpecs) - 1; ++i) {              \
    179       pEngine->DefineObjConst(g_nObjDefnID, ConstSpecs[i].pName,             \
    180                               ConstSpecs[i].eType == JSConstSpec::Number     \
    181                                   ? pEngine->NewNumber(ConstSpecs[i].number) \
    182                                   : pEngine->NewString(ConstSpecs[i].pStr)); \
    183     }                                                                        \
    184   }
    185 
    186 // Convenience macros for declaring classes without an alternate.
    187 #define DECLARE_JS_CLASS() DECLARE_JS_CLASS_RICH()
    188 #define IMPLEMENT_JS_CLASS(js_class_name, class_name) \
    189   IMPLEMENT_JS_CLASS_RICH(js_class_name, class_name, class_name)
    190 
    191 // Rich JS classes provide constants, methods, properties, and the ability
    192 // to construct native object state.
    193 #define DECLARE_JS_CLASS_RICH() \
    194   DECLARE_JS_CLASS_BASE_PART()  \
    195   DECLARE_JS_CLASS_CONST_PART() \
    196   DECLARE_JS_CLASS_RICH_PART()
    197 
    198 #define IMPLEMENT_JS_CLASS_RICH(js_class_name, class_alternate, class_name)  \
    199   IMPLEMENT_JS_CLASS_BASE_PART(js_class_name, class_name)                    \
    200   IMPLEMENT_JS_CLASS_CONST_PART(js_class_name, class_name)                   \
    201   IMPLEMENT_JS_CLASS_RICH_PART(js_class_name, class_alternate, class_name)   \
    202   void js_class_name::DefineJSObjects(CFXJS_Engine* pEngine,                 \
    203                                       FXJSOBJTYPE eObjType) {                \
    204     g_nObjDefnID = pEngine->DefineObj(js_class_name::g_pClassName, eObjType, \
    205                                       JSConstructor, JSDestructor);          \
    206     DefineConsts(pEngine);                                                   \
    207     DefineProps(pEngine);                                                    \
    208     DefineMethods(pEngine);                                                  \
    209   }
    210 
    211 #define DECLARE_JS_CLASS_RICH_PART()                                           \
    212   static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj); \
    213   static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);  \
    214   static void DefineProps(CFXJS_Engine* pEngine);                              \
    215   static void DefineMethods(CFXJS_Engine* pEngine);                            \
    216   static JSPropertySpec PropertySpecs[];                                       \
    217   static JSMethodSpec MethodSpecs[];
    218 
    219 #define IMPLEMENT_JS_CLASS_RICH_PART(js_class_name, class_alternate,    \
    220                                      class_name)                        \
    221   void js_class_name::JSConstructor(CFXJS_Engine* pEngine,              \
    222                                     v8::Local<v8::Object> obj) {        \
    223     CJS_Object* pObj = new js_class_name(obj);                          \
    224     pObj->SetEmbedObject(new class_alternate(pObj));                    \
    225     pEngine->SetObjectPrivate(obj, (void*)pObj);                        \
    226     pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));             \
    227   }                                                                     \
    228   void js_class_name::JSDestructor(CFXJS_Engine* pEngine,               \
    229                                    v8::Local<v8::Object> obj) {         \
    230     delete static_cast<js_class_name*>(pEngine->GetObjectPrivate(obj)); \
    231   }                                                                     \
    232   void js_class_name::DefineProps(CFXJS_Engine* pEngine) {              \
    233     for (size_t i = 0; i < FX_ArraySize(PropertySpecs) - 1; ++i) {      \
    234       pEngine->DefineObjProperty(g_nObjDefnID, PropertySpecs[i].pName,  \
    235                                  PropertySpecs[i].pPropGet,             \
    236                                  PropertySpecs[i].pPropPut);            \
    237     }                                                                   \
    238   }                                                                     \
    239   void js_class_name::DefineMethods(CFXJS_Engine* pEngine) {            \
    240     for (size_t i = 0; i < FX_ArraySize(MethodSpecs) - 1; ++i) {        \
    241       pEngine->DefineObjMethod(g_nObjDefnID, MethodSpecs[i].pName,      \
    242                                MethodSpecs[i].pMethodCall);             \
    243     }                                                                   \
    244   }
    245 
    246 // Special JS classes implement methods, props, and queries, but not consts.
    247 #define DECLARE_SPECIAL_JS_CLASS() \
    248   DECLARE_JS_CLASS_BASE_PART()     \
    249   DECLARE_JS_CLASS_CONST_PART()    \
    250   DECLARE_JS_CLASS_RICH_PART()     \
    251   DECLARE_SPECIAL_JS_CLASS_PART()
    252 
    253 #define IMPLEMENT_SPECIAL_JS_CLASS(js_class_name, class_alternate, class_name) \
    254   IMPLEMENT_JS_CLASS_BASE_PART(js_class_name, class_name)                      \
    255   IMPLEMENT_JS_CLASS_CONST_PART(js_class_name, class_name)                     \
    256   IMPLEMENT_JS_CLASS_RICH_PART(js_class_name, class_alternate, class_name)     \
    257   IMPLEMENT_SPECIAL_JS_CLASS_PART(js_class_name, class_alternate, class_name)  \
    258   void js_class_name::DefineJSObjects(CFXJS_Engine* pEngine,                   \
    259                                       FXJSOBJTYPE eObjType) {                  \
    260     g_nObjDefnID = pEngine->DefineObj(js_class_name::g_pClassName, eObjType,   \
    261                                       JSConstructor, JSDestructor);            \
    262     DefineConsts(pEngine);                                                     \
    263     DefineProps(pEngine);                                                      \
    264     DefineMethods(pEngine);                                                    \
    265     DefineAllProperties(pEngine);                                              \
    266   }
    267 
    268 #define DECLARE_SPECIAL_JS_CLASS_PART()                                        \
    269   static void queryprop_static(                                                \
    270       v8::Local<v8::String> property,                                          \
    271       const v8::PropertyCallbackInfo<v8::Integer>& info);                      \
    272   static void getprop_static(v8::Local<v8::String> property,                   \
    273                              const v8::PropertyCallbackInfo<v8::Value>& info); \
    274   static void putprop_static(v8::Local<v8::String> property,                   \
    275                              v8::Local<v8::Value> value,                       \
    276                              const v8::PropertyCallbackInfo<v8::Value>& info); \
    277   static void delprop_static(                                                  \
    278       v8::Local<v8::String> property,                                          \
    279       const v8::PropertyCallbackInfo<v8::Boolean>& info);                      \
    280   static void DefineAllProperties(CFXJS_Engine* pEngine);
    281 
    282 #define IMPLEMENT_SPECIAL_JS_CLASS_PART(js_class_name, class_alternate,    \
    283                                         class_name)                        \
    284   void js_class_name::queryprop_static(                                    \
    285       v8::Local<v8::String> property,                                      \
    286       const v8::PropertyCallbackInfo<v8::Integer>& info) {                 \
    287     JSSpecialPropQuery<class_alternate>(#class_name, property, info);      \
    288   }                                                                        \
    289   void js_class_name::getprop_static(                                      \
    290       v8::Local<v8::String> property,                                      \
    291       const v8::PropertyCallbackInfo<v8::Value>& info) {                   \
    292     JSSpecialPropGet<class_alternate>(#class_name, property, info);        \
    293   }                                                                        \
    294   void js_class_name::putprop_static(                                      \
    295       v8::Local<v8::String> property, v8::Local<v8::Value> value,          \
    296       const v8::PropertyCallbackInfo<v8::Value>& info) {                   \
    297     JSSpecialPropPut<class_alternate>(#class_name, property, value, info); \
    298   }                                                                        \
    299   void js_class_name::delprop_static(                                      \
    300       v8::Local<v8::String> property,                                      \
    301       const v8::PropertyCallbackInfo<v8::Boolean>& info) {                 \
    302     JSSpecialPropDel<class_alternate>(#class_name, property, info);        \
    303   }                                                                        \
    304   void js_class_name::DefineAllProperties(CFXJS_Engine* pEngine) {         \
    305     pEngine->DefineObjAllProperties(                                       \
    306         g_nObjDefnID, js_class_name::queryprop_static,                     \
    307         js_class_name::getprop_static, js_class_name::putprop_static,      \
    308         js_class_name::delprop_static);                                    \
    309   }
    310 
    311 template <class Alt>
    312 void JSSpecialPropQuery(const char*,
    313                         v8::Local<v8::String> property,
    314                         const v8::PropertyCallbackInfo<v8::Integer>& info) {
    315   CJS_Runtime* pRuntime =
    316       CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
    317   if (!pRuntime)
    318     return;
    319 
    320   CJS_Object* pJSObj =
    321       static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
    322   if (!pJSObj)
    323     return;
    324 
    325   Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
    326   v8::String::Utf8Value utf8_value(property);
    327   CFX_WideString propname = CFX_WideString::FromUTF8(
    328       CFX_ByteStringC(*utf8_value, utf8_value.length()));
    329   bool bRet = pObj->QueryProperty(propname.c_str());
    330   info.GetReturnValue().Set(bRet ? 4 : 0);
    331 }
    332 
    333 template <class Alt>
    334 void JSSpecialPropGet(const char* class_name,
    335                       v8::Local<v8::String> property,
    336                       const v8::PropertyCallbackInfo<v8::Value>& info) {
    337   CJS_Runtime* pRuntime =
    338       CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
    339   if (!pRuntime)
    340     return;
    341 
    342   CJS_Object* pJSObj =
    343       static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
    344   if (!pJSObj)
    345     return;
    346 
    347   Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
    348   v8::String::Utf8Value utf8_value(property);
    349   CFX_WideString propname = CFX_WideString::FromUTF8(
    350       CFX_ByteStringC(*utf8_value, utf8_value.length()));
    351   CFX_WideString sError;
    352   CJS_PropValue value(pRuntime);
    353   value.StartGetting();
    354   if (!pObj->DoProperty(pRuntime, propname.c_str(), value, sError)) {
    355     pRuntime->Error(JSFormatErrorString(class_name, "GetProperty", sError));
    356     return;
    357   }
    358   info.GetReturnValue().Set(value.GetJSValue()->ToV8Value(pRuntime));
    359 }
    360 
    361 template <class Alt>
    362 void JSSpecialPropPut(const char* class_name,
    363                       v8::Local<v8::String> property,
    364                       v8::Local<v8::Value> value,
    365                       const v8::PropertyCallbackInfo<v8::Value>& info) {
    366   CJS_Runtime* pRuntime =
    367       CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
    368   if (!pRuntime)
    369     return;
    370 
    371   CJS_Object* pJSObj =
    372       static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
    373   if (!pJSObj)
    374     return;
    375 
    376   Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
    377   v8::String::Utf8Value utf8_value(property);
    378   CFX_WideString propname = CFX_WideString::FromUTF8(
    379       CFX_ByteStringC(*utf8_value, utf8_value.length()));
    380   CFX_WideString sError;
    381   CJS_PropValue PropValue(pRuntime, CJS_Value(pRuntime, value));
    382   PropValue.StartSetting();
    383   if (!pObj->DoProperty(pRuntime, propname.c_str(), PropValue, sError)) {
    384     pRuntime->Error(JSFormatErrorString(class_name, "PutProperty", sError));
    385   }
    386 }
    387 
    388 template <class Alt>
    389 void JSSpecialPropDel(const char* class_name,
    390                       v8::Local<v8::String> property,
    391                       const v8::PropertyCallbackInfo<v8::Boolean>& info) {
    392   CJS_Runtime* pRuntime =
    393       CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
    394   if (!pRuntime)
    395     return;
    396 
    397   CJS_Object* pJSObj =
    398       static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
    399   if (!pJSObj)
    400     return;
    401 
    402   Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
    403   v8::String::Utf8Value utf8_value(property);
    404   CFX_WideString propname = CFX_WideString::FromUTF8(
    405       CFX_ByteStringC(*utf8_value, utf8_value.length()));
    406   CFX_WideString sError;
    407   if (!pObj->DelProperty(pRuntime, propname.c_str(), sError)) {
    408     CFX_ByteString cbName;
    409     cbName.Format("%s.%s", class_name, "DelProperty");
    410     // Probably a missing call to JSFX_Error().
    411   }
    412 }
    413 
    414 template <bool (*F)(CJS_Runtime*,
    415                     const std::vector<CJS_Value>&,
    416                     CJS_Value&,
    417                     CFX_WideString&)>
    418 void JSGlobalFunc(const char* func_name_string,
    419                   const v8::FunctionCallbackInfo<v8::Value>& info) {
    420   CJS_Runtime* pRuntime =
    421       CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
    422   if (!pRuntime)
    423     return;
    424   std::vector<CJS_Value> parameters;
    425   for (unsigned int i = 0; i < (unsigned int)info.Length(); i++) {
    426     parameters.push_back(CJS_Value(pRuntime, info[i]));
    427   }
    428   CJS_Value valueRes(pRuntime);
    429   CFX_WideString sError;
    430   if (!(*F)(pRuntime, parameters, valueRes, sError)) {
    431     pRuntime->Error(JSFormatErrorString(func_name_string, nullptr, sError));
    432     return;
    433   }
    434   info.GetReturnValue().Set(valueRes.ToV8Value(pRuntime));
    435 }
    436 
    437 #define JS_STATIC_GLOBAL_FUN(fun_name)                   \
    438   static void fun_name##_static(                         \
    439       const v8::FunctionCallbackInfo<v8::Value>& info) { \
    440     JSGlobalFunc<fun_name>(#fun_name, info);             \
    441   }
    442 
    443 #define JS_STATIC_DECLARE_GLOBAL_FUN()       \
    444   static JSMethodSpec GlobalFunctionSpecs[]; \
    445   static void DefineJSObjects(CFXJS_Engine* pEngine)
    446 
    447 #define IMPLEMENT_JS_STATIC_GLOBAL_FUN(js_class_name)                    \
    448   void js_class_name::DefineJSObjects(CFXJS_Engine* pEngine) {           \
    449     for (size_t i = 0; i < FX_ArraySize(GlobalFunctionSpecs) - 1; ++i) { \
    450       pEngine->DefineGlobalMethod(                                       \
    451           js_class_name::GlobalFunctionSpecs[i].pName,                   \
    452           js_class_name::GlobalFunctionSpecs[i].pMethodCall);            \
    453     }                                                                    \
    454   }
    455 
    456 #endif  // FPDFSDK_JAVASCRIPT_JS_DEFINE_H_
    457