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